Skip to content
This repository has been archived by the owner on Jun 16, 2022. It is now read-only.

Commit

Permalink
more cache fuckery
Browse files Browse the repository at this point in the history
Squashing and rebasing
  • Loading branch information
ggilchrist-ledger committed Apr 14, 2022
1 parent 70569c2 commit f7444ec
Show file tree
Hide file tree
Showing 13 changed files with 1,254 additions and 524 deletions.
13 changes: 11 additions & 2 deletions .detoxrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"build": "cd android && ENVFILE=.env.mock ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Nexus_5X_API_29_x86"
"avdName": "Nexus_5_API_30"
}
},
"android.staging": {
Expand All @@ -33,7 +33,16 @@
"build": "cd android && ENVFILE=.env.mock ./gradlew assembleStagingRelease assembleAndroidTest -DtestBuildType=stagingRelease && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Nexus_5X_API_29_x86"
"avdName": "Nexus_5_API_30"
}
},
"android.staging-ci": {
"binaryPath": "android/app/build/outputs/apk/stagingRelease/app-x86-stagingRelease.apk",
"testBinaryPath": "android/app/build/outputs/apk/androidTest/stagingRelease/app-stagingRelease-androidTest.apk",
"build": "cd android && ENVFILE=.env.mock ./gradlew assembleStagingRelease assembleAndroidTest -DtestBuildType=stagingRelease -PwithTestButler --warning-mode all && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Nexus_5_API_30"
}
}
}
Expand Down
190 changes: 190 additions & 0 deletions .github/workflows/detox-ci.yml
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
2 changes: 1 addition & 1 deletion e2e/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"testEnvironment": "./environment",
"testRunner": "jest-circus/runner",
"testTimeout": 120000,
"testTimeout": 300000,
"testRegex": "\\.spec\\.js$",
"reporters": ["detox/runners/jest/streamlineReporter"],
"verbose": true,
Expand Down
8 changes: 8 additions & 0 deletions e2e/global-setup.js
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;
8 changes: 8 additions & 0 deletions e2e/global-teardown.js
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;
116 changes: 116 additions & 0 deletions e2e/helpers.js
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!`,
);
}
}
Loading

0 comments on commit f7444ec

Please sign in to comment.