This repository has been archived by the owner on Jun 16, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
13 changed files
with
1,254 additions
and
524 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
name: Detox E2E CI | ||
|
||
on: | ||
pull_request: | ||
branches: | ||
- "*" | ||
workflow_dispatch: | ||
inputs: | ||
prNumber: | ||
description: pr number to trigger on | ||
required: true | ||
|
||
jobs: | ||
# prepare-test-run: | ||
# name: "Prepare test run" | ||
# runs-on: macos-latest | ||
# steps: | ||
# - name: prepare runner | ||
# run: | ||
|
||
ios: | ||
# needs: [prepare-test-run] | ||
runs-on: macos-latest | ||
steps: | ||
- name: Checkout PR | ||
uses: actions/checkout@v2 | ||
with: | ||
ref: ${{github.event.pull_request.head.ref}} | ||
repository: ${{github.event.pull_request.head.repo.full_name}} | ||
|
||
# - name: Setup tmate session | ||
# uses: mxschmitt/action-tmate@v3 | ||
|
||
- name: Setup Ruby | ||
uses: ruby/setup-ruby@v1 | ||
with: | ||
ruby-version: 2.6 # Not needed with a .ruby-version file | ||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically | ||
|
||
- name: Install applesimutils | ||
run: | | ||
brew tap wix/brew | ||
brew install applesimutils | ||
- name: List sims | ||
run: applesimutils --list | ||
|
||
- name: Setup Node | ||
uses: actions/setup-node@v2 | ||
with: | ||
# node-version: '14' | ||
node-version-file: '.nvmrc' | ||
cache: 'yarn' | ||
|
||
- name: Has hash commit deps | ||
uses: ledgerhq/actions/packages/has-hash-commit-deps@main | ||
id: has-hash-commit-deps | ||
with: | ||
workspace: ${{ github.workspace }} | ||
|
||
- name: Install dependencies | ||
if: ${{ steps.has-hash-commit-deps.outputs.has-hash-commit-deps == 'true' }} | ||
env: | ||
JOBS: max | ||
run: yarn --prefer-offline --frozen-lockfile --network-timeout 100000 --network-concurrency 1 | ||
|
||
- name: Install dependencies | ||
if: ${{ steps.has-hash-commit-deps.outputs.has-hash-commit-deps == 'false' }} | ||
env: | ||
JOBS: max | ||
run: yarn --prefer-offline --frozen-lockfile --network-timeout 100000 | ||
|
||
# - name: Rebuild detox | ||
# if: ${{ steps.yarn-cache.outputs.cache-hit == 'true' }} | ||
# run: yarn detox clean-framework-cache && yarn detox build-framework-cache | ||
|
||
- name: Build iOS app for detox test run | ||
run: yarn detox build -c ios.debug | ||
|
||
- name: Test iOS app | ||
timeout-minutes: 15 | ||
run: yarn detox test -c ios.debug --loglevel verbose --record-logs failing --record-timeline all --take-screenshots failing | ||
# --retries 1 --reuse | ||
|
||
- name: Upload test artifacts | ||
if: always() | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: test-ios-artifacts | ||
path: | | ||
e2e/artifacts | ||
android: | ||
runs-on: macos-latest | ||
steps: | ||
- name: Checkout PR | ||
uses: actions/checkout@v2 | ||
with: | ||
ref: ${{github.event.pull_request.head.ref}} | ||
repository: ${{github.event.pull_request.head.repo.full_name}} | ||
|
||
# - name: Setup tmate session | ||
# uses: mxschmitt/action-tmate@v3 | ||
|
||
- name: Setup Node | ||
uses: actions/setup-node@v2 | ||
with: | ||
node-version: '14' | ||
cache: 'yarn' | ||
|
||
- name: Check hash commit deps | ||
uses: ledgerhq/actions/packages/has-hash-commit-deps@main | ||
id: has-hash-commit-deps | ||
with: | ||
workspace: ${{ github.workspace }} | ||
|
||
- name: Install dependencies | ||
if: ${{ steps.has-hash-commit-deps.outputs.has-hash-commit-deps == 'true' }} | ||
env: | ||
JOBS: max | ||
run: yarn --prefer-offline --frozen-lockfile --network-timeout 100000 --network-concurrency 1 | ||
|
||
- name: install dependencies | ||
if: ${{ steps.has-hash-commit-deps.outputs.has-hash-commit-deps == 'false' }} | ||
env: | ||
JOBS: max | ||
run: yarn --prefer-offline --frozen-lockfile --network-timeout 100000 | ||
|
||
- name: Gradle cache | ||
uses: gradle/gradle-build-action@v2 | ||
|
||
- name: AVD cache | ||
uses: actions/cache@v2 | ||
id: avd-cache | ||
with: | ||
path: | | ||
~/.android/avd/* | ||
~/.android/adb* | ||
key: avd-api-29 | ||
|
||
- name: Build Android app for Detox test run | ||
run: yarn detox build -c android.debug | ||
|
||
- name: Create AVD and generate snapshot for caching | ||
timeout-minutes: 30 | ||
if: steps.avd-cache.outputs.cache-hit != 'true' | ||
uses: reactivecircus/android-emulator-runner@v2 | ||
with: | ||
api-level: 29 | ||
force-avd-creation: false | ||
avd-name: Nexus_5_API_30 | ||
emulator-options: -verbose -no-boot-anim -noaudio | ||
ram-size: 2048 | ||
# disable-animations: true | ||
script: | | ||
$ANDROID_HOME/platform-tools/adb devices | ||
adb logcat -c | ||
adb logcat *:E & | ||
mkdir ./temp | ||
curl -f -o ./temp/test-butler-app.apk https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/2.2.1/test-butler-app-2.2.1.apk | ||
adb install ./temp/test-butler-app.apk || adb install ./e2e/test-butler-app.apk | ||
yarn detox test -c android.debug --loglevel verbose --record-logs failing --record-timeline all --take-screenshots failing | ||
- name: Run Android tests (with Test Butler) | ||
if: steps.avd-cache.outputs.cache-hit == 'true' | ||
timeout-minutes: 30 | ||
uses: reactivecircus/android-emulator-runner@v2 | ||
with: | ||
api-level: 29 | ||
force-avd-creation: false | ||
avd-name: Nexus_5_API_30 | ||
emulator-options: -verbose -no-snapshot-save -no-boot-anim -noaudio | ||
# disable-animations: true | ||
# ./gradlew connectedCheck | ||
script: | | ||
adb logcat -c | ||
adb logcat *:E & | ||
mkdir ./temp | ||
curl -f -o ./temp/test-butler-app.apk https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/2.2.1/test-butler-app-2.2.1.apk | ||
adb install ./temp/test-butler-app.apk || adb install ./e2e/test-butler-app.apk | ||
yarn detox test -c android.debug --loglevel verbose --record-logs failing --record-timeline all --take-screenshots failing | ||
# --retries 1 --reuse | ||
|
||
- name: Upload test artifacts | ||
if: always() | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: test-android-artifacts | ||
path: | | ||
e2e/artifacts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const detox = require("detox"); | ||
|
||
async function globalSetup() { | ||
console.log("==============> STARTING GLOBAL SETUP"); | ||
await detox.globalInit(); | ||
} | ||
|
||
module.exports = globalSetup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const detox = require("detox"); | ||
|
||
async function globalTeardown() { | ||
console.log("==============> STARTING GLOBAL TEARDOWN"); | ||
await detox.globalCleanup(); | ||
} | ||
|
||
module.exports = globalTeardown; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { waitFor, element, by } from "detox"; | ||
import { readFileSync } from "fs"; | ||
|
||
const DEFAULT_TIMEOUT = 30000; | ||
|
||
export function waitAndTap(elementId, timeout) { | ||
console.log(`==================> Waiting for element with id: ${elementId}`); | ||
waitFor(element(by.id(elementId))) | ||
.toBeVisible() | ||
.withTimeout(timeout || DEFAULT_TIMEOUT); | ||
|
||
console.log(`==================> Tapping element with id: ${elementId}`); | ||
return element(by.id(elementId)).tap(); | ||
} | ||
|
||
export function waitForElement(elementId, timeout) { | ||
console.log(`==================> Waiting for element with id: ${elementId}`); | ||
return waitFor(element(by.id(elementId))) | ||
.toBeVisible() | ||
.withTimeout(timeout || DEFAULT_TIMEOUT); | ||
} | ||
|
||
export function tap(elementId) { | ||
console.log(`==================> Tapping element with id: ${elementId}`); | ||
return element(by.id(elementId)).tap(); | ||
} | ||
|
||
export function tapByText(text, index) { | ||
console.log(`==================> Tapping element with text: ${text}`); | ||
return element(by.text(text)) | ||
.atIndex(index || 0) | ||
.tap(); | ||
} | ||
|
||
export async function typeText(elementId, text, focus = true) { | ||
console.log( | ||
`==================> Typing text '${text}' into element with id ${elementId}`, | ||
); | ||
if (focus) { | ||
await tap(elementId); | ||
} | ||
return element(by.id(elementId)).typeText(text); | ||
} | ||
|
||
export function clearField(elementId) { | ||
console.log(`==================> Clearing field with id: ${elementId}`); | ||
element(by.id(elementId)).replaceText(""); | ||
} | ||
|
||
export async function scrollToElementById( | ||
elementToScrollToId, | ||
parentElementId, | ||
pixelsToScroll, | ||
direction = "down", | ||
startPositionXAxis = NaN, | ||
startPositionYAxis = 0.5, | ||
) { | ||
console.log( | ||
`==================> Scrolling to element with id: ${elementToScrollToId}`, | ||
); | ||
await waitFor(element(by.id(elementToScrollToId))) | ||
.toBeVisible() | ||
.whileElement(by.id(parentElementId)) | ||
.scroll(pixelsToScroll, direction, startPositionXAxis, startPositionYAxis); | ||
} | ||
|
||
export async function retryAction(action, timeout) { | ||
let shouldContinue = true; | ||
const startTime = Date.now(); | ||
|
||
while (shouldContinue) { | ||
shouldContinue = false; | ||
|
||
try { | ||
await action(); | ||
} catch { | ||
shouldContinue = true; | ||
} | ||
|
||
if (timeout && Date.now() - startTime > timeout) { | ||
throw new Error("Timed out when waiting for action"); | ||
} | ||
|
||
// eslint-disable-next-line no-console | ||
console.log("Trying again..."); | ||
} | ||
} | ||
|
||
// TODO: expects should be in test file | ||
export async function verifyIsVisible(elementId) { | ||
await delay(1000); | ||
await expect(element(by.id(elementId))).toBeVisible(); | ||
} | ||
|
||
export async function verifyTextIsVisible(text) { | ||
await delay(1000); | ||
await expect(element(by.text(text))).toBeVisible(); | ||
} | ||
|
||
export function delay(ms) { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
resolve(); | ||
}, ms); | ||
}); | ||
} | ||
|
||
export function expectBitmapsToBeEqual(imagePath, expectedImagePath) { | ||
const bitmapBuffer = readFileSync(imagePath); | ||
const expectedBitmapBuffer = readFileSync(expectedImagePath); | ||
if (!bitmapBuffer.equals(expectedBitmapBuffer)) { | ||
throw new Error( | ||
`Expected image at ${imagePath} to be equal to image at ${expectedImagePath}, but it was different!`, | ||
); | ||
} | ||
} |
Oops, something went wrong.