diff --git a/.github/actions/composite/buildAndroidAPKDelta/action.yml b/.github/actions/composite/buildAndroidAPKDelta/action.yml
new file mode 100644
index 000000000000..f466bb2a061a
--- /dev/null
+++ b/.github/actions/composite/buildAndroidAPKDelta/action.yml
@@ -0,0 +1,29 @@
+name: Build an Android apk
+description: Build an Android apk for an E2E test build and upload it as an artifact
+
+inputs:
+ ARTIFACT_NAME:
+ description: The name of the workflow artifact where the APK should be uploaded
+ required: true
+
+runs:
+ using: composite
+ steps:
+ - uses: Expensify/App/.github/actions/composite/setupNode@main
+
+ - uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011
+ with:
+ ruby-version: "2.7"
+ bundler-cache: true
+
+ - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef
+
+ - name: Build APK
+ run: npm run android-build-e2edelta
+ shell: bash
+
+ - name: Upload APK
+ uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05
+ with:
+ name: ${{ inputs.ARTIFACT_NAME }}
+ path: android/app/build/outputs/apk/e2e/release/app-e2edelta-release.apk
diff --git a/.github/workflows/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml
index e1bb286179cf..874ff87f68d8 100644
--- a/.github/workflows/e2ePerformanceTests.yml
+++ b/.github/workflows/e2ePerformanceTests.yml
@@ -46,14 +46,28 @@ jobs:
git fetch origin tag ${{ steps.getMostRecentRelease.outputs.VERSION }} --no-tags --depth=1
git switch --detach ${{ steps.getMostRecentRelease.outputs.VERSION }}
+ - uses: Expensify/App/.github/actions/composite/setupNode@main
+
+ - uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011
+ with:
+ ruby-version: "2.7"
+ bundler-cache: true
+
+ - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef
+
- name: Configure MapBox SDK
run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }}
- name: Build APK
- if: ${{ !fromJSON(steps.checkForExistingArtifact.outputs.exists) }}
- uses: Expensify/App/.github/actions/composite/buildAndroidAPK@main
+ run: npm run android-build-e2e
+ shell: bash
+
+ - name: Upload APK
+ uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05
with:
- ARTIFACT_NAME: baseline-apk-${{ steps.getMostRecentRelease.outputs.VERSION }}
+ name: baseline-apk-${{ steps.getMostRecentRelease.outputs.VERSION }}
+ path: android/app/build/outputs/apk/e2e/release/app-e2e-release.apk
+
buildDelta:
runs-on: ubuntu-latest-xl
@@ -113,10 +127,24 @@ jobs:
- name: Configure MapBox SDK
run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }}
+ - uses: Expensify/App/.github/actions/composite/setupNode@main
+
+ - uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011
+ with:
+ ruby-version: "2.7"
+ bundler-cache: true
+
+ - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef
+
- name: Build APK
- uses: Expensify/App/.github/actions/composite/buildAndroidAPK@main
+ run: npm run android-build-e2edelta
+ shell: bash
+
+ - name: Upload APK
+ uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05
with:
- ARTIFACT_NAME: delta-apk-${{ steps.getDeltaRef.outputs.DELTA_REF }}
+ name: delta-apk-${{ steps.getDeltaRef.outputs.DELTA_REF }}
+ path: android/app/build/outputs/apk/e2edelta/release/app-e2edelta-release.apk
runTestsInAWS:
runs-on: ubuntu-latest
@@ -140,7 +168,7 @@ jobs:
# The downloaded artifact will be a file named "app-e2e-release.apk" so we have to rename it
- name: Rename baseline APK
- run: mv "${{steps.downloadBaselineAPK.outputs.download-path}}/app-e2e-release.apk" "${{steps.downloadBaselineAPK.outputs.download-path}}/app-e2eRelease-main.apk"
+ run: mv "${{steps.downloadBaselineAPK.outputs.download-path}}/app-e2e-release.apk" "${{steps.downloadBaselineAPK.outputs.download-path}}/app-e2eRelease.apk"
- name: Download delta APK
uses: actions/download-artifact@e9ef242655d12993efdcda9058dee2db83a2cb9b
@@ -150,7 +178,7 @@ jobs:
path: zip
- name: Rename delta APK
- run: mv "${{steps.downloadDeltaAPK.outputs.download-path}}/app-e2e-release.apk" "${{steps.downloadDeltaAPK.outputs.download-path}}/app-e2eRelease-delta.apk"
+ run: mv "${{steps.downloadDeltaAPK.outputs.download-path}}/app-e2edelta-release.apk" "${{steps.downloadDeltaAPK.outputs.download-path}}/app-e2edeltaRelease.apk"
- name: Copy e2e code into zip folder
run: cp -r tests/e2e zip
@@ -172,12 +200,12 @@ jobs:
name: App E2E Performance Regression Tests
project_arn: ${{ secrets.AWS_PROJECT_ARN }}
device_pool_arn: ${{ secrets.AWS_DEVICE_POOL_ARN }}
- app_file: zip/app-e2eRelease-main.apk
+ app_file: zip/app-e2eRelease.apk
app_type: ANDROID_APP
test_type: APPIUM_NODE
test_package_file: App.zip
test_package_type: APPIUM_NODE_TEST_PACKAGE
- test_spec_file: tests/e2e/TestSpecMain.yml
+ test_spec_file: tests/e2e/TestSpec.yml
test_spec_type: APPIUM_NODE_TEST_SPEC
remote_src: false
file_artifacts: Customer Artifacts.zip
@@ -192,38 +220,13 @@ jobs:
unzip "Customer Artifacts.zip" -d mainResults
cat ./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/debug.log
- - name: Unzip AWS Device Farm main results
- run: unzip "Customer Artifacts.zip" -d mainResults
-
- - name: Delete Customer Artifacts.zip
- run: rm "Customer Artifacts.zip"
-
- - name: Schedule AWS Device Farm test run on delta branch
- uses: realm/aws-devicefarm/test-application@7b9a91236c456c97e28d384c9e476035d5ea686b
- with:
- name: App E2E Performance Regression Tests
- project_arn: ${{ secrets.AWS_PROJECT_ARN }}
- device_pool_arn: ${{ secrets.AWS_DEVICE_POOL_ARN }}
- app_file: zip/app-e2eRelease-delta.apk
- app_type: ANDROID_APP
- test_type: APPIUM_NODE
- test_package_file: App.zip
- test_package_type: APPIUM_NODE_TEST_PACKAGE
- test_spec_file: tests/e2e/TestSpecDelta.yml
- test_spec_type: APPIUM_NODE_TEST_SPEC
- remote_src: false
- file_artifacts: Customer Artifacts.zip
- cleanup: true
- timeout: 5400
-
- - name: Unzip AWS Device Farm delta results
- run: unzip "Customer Artifacts.zip" -d deltaResults
-
- - name: Compare results
- run: node tests/e2e/merge.js --mainPath ./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/main.json --deltaPath ./deltaResults//Host_Machine_Files/\$WORKING_DIRECTORY/delta.json --outputPath ./output.md
+ - name: Unzip AWS Device Farm results
+ if: ${{ always() }}
+ run: unzip "Customer Artifacts.zip"
- - name: Print results
- run: cat "./output.md"
+ - name: Print AWS Device Farm run results
+ if: ${{ always() }}
+ run: cat "./Host_Machine_Files/\$WORKING_DIRECTORY/output.md"
- name: Check if test failed, if so post the results and add the DeployBlocker label
run: |
diff --git a/android/app/build.gradle b/android/app/build.gradle
index d99b2f762163..25516308afca 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -58,7 +58,8 @@ project.ext.envConfigFiles = [
adhocRelease: ".env.adhoc",
developmentRelease: ".env",
developmentDebug: ".env",
- e2eRelease: "tests/e2e/.env.e2e"
+ e2eRelease: "tests/e2e/.env.e2e",
+ e2edeltaRelease: "tests/e2e/.env.e2edelta"
]
/**
@@ -101,7 +102,14 @@ android {
e2e {
// If are building a version that won't be uploaded to the play store, we don't have to use production keys
// applies all non-production flavors
- applicationIdSuffix ".adhoc"
+ applicationIdSuffix ".e2e"
+ signingConfig signingConfigs.debug
+ resValue "string", "build_config_package", "com.expensify.chat"
+ }
+ e2edelta {
+ // If are building a version that won't be uploaded to the play store, we don't have to use production keys
+ // applies all non-production flavors
+ applicationIdSuffix ".e2edelta"
signingConfig signingConfigs.debug
resValue "string", "build_config_package", "com.expensify.chat"
}
@@ -150,12 +158,13 @@ android {
}
// ... except for the e2e flavor, which we maybe want to build locally:
productFlavors.e2e.signingConfig signingConfigs.debug
+ productFlavors.e2edelta.signingConfig signingConfigs.debug
}
}
// since we don't need variants adhocDebug and e2eDebug, we can force gradle to ignore them
variantFilter { variant ->
- if (variant.name == "adhocDebug" || variant.name == "e2eDebug") {
+ if (variant.name == "adhocDebug" || variant.name == "e2eDebug" || variant.name == "e2edeltaDebug") {
setIgnore(true)
}
}
diff --git a/android/app/google-services.json b/android/app/google-services.json
index 35f7f5b68921..c0dcb51310ef 100644
--- a/android/app/google-services.json
+++ b/android/app/google-services.json
@@ -1,143 +1,231 @@
{
- "project_info": {
- "project_number": "921154746561",
- "firebase_url": "https://expensify-chat.firebaseio.com",
- "project_id": "expensify-chat",
- "storage_bucket": "expensify-chat.appspot.com"
- },
- "client": [
- {
- "client_info": {
- "mobilesdk_app_id": "1:921154746561:android:4f04268f25f84eaf027c40",
- "android_client_info": {
- "package_name": "com.expensify.chat"
+ "project_info": {
+ "project_number": "921154746561",
+ "firebase_url": "https://expensify-chat.firebaseio.com",
+ "project_id": "expensify-chat",
+ "storage_bucket": "expensify-chat.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:921154746561:android:4f04268f25f84eaf027c40",
+ "android_client_info": {
+ "package_name": "com.expensify.chat"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "921154746561-o0pgqgc84e3e97s9iljlmimcb5nesqad.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.expensify.chat",
+ "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
}
},
- "oauth_client": [
- {
- "client_id": "921154746561-o0pgqgc84e3e97s9iljlmimcb5nesqad.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "com.expensify.chat",
- "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
- }
- },
- {
- "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "com.expensify.chat.adhoc"
- }
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.expensify.chat.adhoc"
}
- ]
- }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:921154746561:android:333e293a7fef83a8027c40",
+ "android_client_info": {
+ "package_name": "com.expensify.chat.adhoc"
}
},
- {
- "client_info": {
- "mobilesdk_app_id": "1:921154746561:android:333e293a7fef83a8027c40",
- "android_client_info": {
- "package_name": "com.expensify.chat.adhoc"
+ "oauth_client": [
+ {
+ "client_id": "921154746561-cbegir0tnc2gan6k1gre5vtn75p60hom.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.expensify.chat.adhoc",
+ "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
}
},
- "oauth_client": [
- {
- "client_id": "921154746561-cbegir0tnc2gan6k1gre5vtn75p60hom.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "com.expensify.chat.adhoc",
- "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.expensify.chat.adhoc"
+ }
}
- },
- {
- "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ ]
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:921154746561:android:333e293a7fef83a8027c40",
+ "android_client_info": {
+ "package_name": "com.expensify.chat.e2e"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "921154746561-cbegir0tnc2gan6k1gre5vtn75p60hom.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.expensify.chat.e2e",
+ "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
}
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "com.expensify.chat.adhoc"
- }
+ },
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.expensify.chat.e2e"
}
- ]
- }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:921154746561:android:333e293a7fef83a8027c40",
+ "android_client_info": {
+ "package_name": "com.expensify.chat.e2edelta"
}
},
- {
- "client_info": {
- "mobilesdk_app_id": "1:921154746561:android:3b19fdbaedb5b586027c40",
- "android_client_info": {
- "package_name": "com.expensify.chat.dev"
+ "oauth_client": [
+ {
+ "client_id": "921154746561-cbegir0tnc2gan6k1gre5vtn75p60hom.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.expensify.chat.e2edelta",
+ "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
}
},
- "oauth_client": [
- {
- "client_id": "921154746561-svjnccrcn6vet45kn9o7sibb3jemipa6.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "com.expensify.chat.dev",
- "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.expensify.chat.e2edelta"
+ }
}
- },
- {
- "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ ]
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:921154746561:android:3b19fdbaedb5b586027c40",
+ "android_client_info": {
+ "package_name": "com.expensify.chat.dev"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "921154746561-svjnccrcn6vet45kn9o7sibb3jemipa6.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.expensify.chat.dev",
+ "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
}
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "com.expensify.chat.adhoc"
- }
+ },
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "921154746561-080fav7kvk6s70k6nd70mt50isubgff4.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.expensify.chat.adhoc"
}
- ]
- }
+ }
+ ]
}
}
- ],
- "configuration_version": "1"
- }
+ }
+ ],
+ "configuration_version": "1"
+}
diff --git a/android/app/src/e2edelta/AndroidManifest.xml b/android/app/src/e2edelta/AndroidManifest.xml
new file mode 100644
index 000000000000..201d730f5211
--- /dev/null
+++ b/android/app/src/e2edelta/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 78abf8074155..98366cd3b967 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -15,6 +15,7 @@ opt_out_usage
platform :android do
desc "Generate a new local APK for e2e testing"
+
lane :build_e2e do
ENV["ENVFILE"]="tests/e2e/.env.e2e"
ENV["ENTRY_FILE"]="src/libs/E2E/reactNativeLaunchingTest.js"
@@ -28,6 +29,19 @@ platform :android do
)
end
+ lane :build_e2edelta do
+ ENV["ENVFILE"]="tests/e2e/.env.e2edelta"
+ ENV["ENTRY_FILE"]="src/libs/E2E/reactNativeLaunchingTest.js"
+ ENV["E2E_TESTING"]="true"
+
+ gradle(
+ project_dir: './android',
+ task: ':app:assemble',
+ flavor: 'e2edelta',
+ build_type: 'Release',
+ )
+ end
+
desc "Generate a new local APK"
lane :build do
ENV["ENVFILE"]=".env.production"
diff --git a/package.json b/package.json
index 6cb4dd115272..91cc7c62bda8 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"ios-build": "fastlane ios build",
"android-build": "fastlane android build",
"android-build-e2e": "bundle exec fastlane android build_e2e",
+ "android-build-e2edelta": "bundle exec fastlane android build_e2edelta",
"test": "TZ=utc jest",
"typecheck": "tsc",
"lint": "eslint . --max-warnings=0 --cache --cache-location=node_modules/.cache/eslint",
@@ -49,10 +50,7 @@
"analyze-packages": "ANALYZE_BUNDLE=true webpack --config config/webpack/webpack.common.js --env envFile=.env.production",
"symbolicate:android": "npx metro-symbolicate android/app/build/generated/sourcemaps/react/release/index.android.bundle.map",
"symbolicate:ios": "npx metro-symbolicate main.jsbundle.map",
- "test:e2e:dev": "node tests/e2e/testRunner.js --development --skipCheckout --config ./config.dev.js --buildMode skip --skipInstallDeps",
- "test:e2e:main": "node tests/e2e/testRunner.js --development --skipCheckout",
- "test:e2e:delta": "node tests/e2e/testRunner.js --development --label delta --skipCheckout --skipInstallDeps",
- "test:e2e:compare": "node tests/e2e/merge.js",
+ "test:e2e": "node tests/e2e/testRunner.js --development --skipCheckout --skipInstallDeps --buildMode none",
"gh-actions-unused-styles": "./.github/scripts/findUnusedKeys.sh",
"workflow-test": "./workflow_tests/scripts/runWorkflowTests.sh",
"workflow-test:generate": "node workflow_tests/utils/preGenerateTest.js",
diff --git a/src/libs/E2E/tests/appStartTimeTest.e2e.js b/src/libs/E2E/tests/appStartTimeTest.e2e.js
index 311b891fcd4c..0abbf3323f2a 100644
--- a/src/libs/E2E/tests/appStartTimeTest.e2e.js
+++ b/src/libs/E2E/tests/appStartTimeTest.e2e.js
@@ -1,3 +1,4 @@
+import Config from 'react-native-config';
import _ from 'underscore';
import E2ELogin from '@libs/E2E/actions/e2eLogin';
import E2EClient from '@libs/E2E/client';
@@ -20,6 +21,7 @@ const test = () => {
Promise.all(
_.map(metrics, (metric) =>
E2EClient.submitTestResults({
+ branch: Config.E2E_BRANCH,
name: `App start ${metric.name}`,
duration: metric.duration,
}),
diff --git a/src/libs/E2E/tests/openSearchPageTest.e2e.js b/src/libs/E2E/tests/openSearchPageTest.e2e.js
index 1101a620f413..aad816766f9e 100644
--- a/src/libs/E2E/tests/openSearchPageTest.e2e.js
+++ b/src/libs/E2E/tests/openSearchPageTest.e2e.js
@@ -1,3 +1,4 @@
+import Config from 'react-native-config';
import E2ELogin from '@libs/E2E/actions/e2eLogin';
import E2EClient from '@libs/E2E/client';
import Navigation from '@libs/Navigation/Navigation';
@@ -31,6 +32,7 @@ const test = () => {
console.debug(`[E2E] Submitting!`);
E2EClient.submitTestResults({
+ branch: Config.E2E_BRANCH,
name: 'Open Search Page TTI',
duration: entry.duration,
})
diff --git a/src/libs/E2E/tests/reportTypingTest.e2e.js b/src/libs/E2E/tests/reportTypingTest.e2e.js
index b79166063b4f..90d0dc9e0bb6 100644
--- a/src/libs/E2E/tests/reportTypingTest.e2e.js
+++ b/src/libs/E2E/tests/reportTypingTest.e2e.js
@@ -1,3 +1,4 @@
+import Config from 'react-native-config';
import E2ELogin from '@libs/E2E/actions/e2eLogin';
import waitForKeyboard from '@libs/E2E/actions/waitForKeyboard';
import E2EClient from '@libs/E2E/client';
@@ -42,6 +43,7 @@ const test = () => {
const rerenderCount = getRerenderCount();
E2EClient.submitTestResults({
+ branch: Config.E2E_BRANCH,
name: 'Composer typing rerender count',
renderCount: rerenderCount,
}).then(E2EClient.submitTestDone);
diff --git a/tests/e2e/.env.e2e b/tests/e2e/.env.e2e
index 7c2afd5a820b..a6611ad2ff9d 100644
--- a/tests/e2e/.env.e2e
+++ b/tests/e2e/.env.e2e
@@ -1,2 +1,3 @@
+E2E_BRANCH=main
E2E_TESTING=true
CAPTURE_METRICS=true
diff --git a/tests/e2e/.env.e2edelta b/tests/e2e/.env.e2edelta
new file mode 100644
index 000000000000..a5d7a96d0788
--- /dev/null
+++ b/tests/e2e/.env.e2edelta
@@ -0,0 +1,3 @@
+E2E_BRANCH=delta
+E2E_TESTING=true
+CAPTURE_METRICS=true
diff --git a/tests/e2e/TestSpecDelta.yml b/tests/e2e/TestSpec.yml
similarity index 89%
rename from tests/e2e/TestSpecDelta.yml
rename to tests/e2e/TestSpec.yml
index 2d4906855ca8..4a5be0a5fcdd 100644
--- a/tests/e2e/TestSpecDelta.yml
+++ b/tests/e2e/TestSpec.yml
@@ -20,7 +20,7 @@ phases:
commands:
- cd zip
- npm install underscore
- - node e2e/testRunner.js -- --skipInstallDeps --buildMode "skip" --skipCheckout --label delta --appPath app-e2eRelease-delta.apk
+ - node e2e/testRunner.js -- --skipInstallDeps --buildMode "skip" --skipCheckout --mainAppPath app-e2eRelease.apk --deltaAppPath app-e2edeltaRelease.apk
artifacts:
- $WORKING_DIRECTORY
diff --git a/tests/e2e/TestSpecMain.yml b/tests/e2e/TestSpecMain.yml
deleted file mode 100644
index 6cf1c5d0b273..000000000000
--- a/tests/e2e/TestSpecMain.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-version: 0.1
-
-phases:
- install:
- commands:
- # Install correct version of node
- - export NVM_DIR=$HOME/.nvm
- - . $NVM_DIR/nvm.sh
- - nvm install 16.15.1
- - nvm use 16.15.1
-
- # Reverse ports using AWS magic
- - PORT=4723
- - IP_ADDRESS=$(ip -4 addr show eth0 | grep -Po "(?<=inet\s)\d+(\.\d+){3}")
- - reverse_values="{\"ip_address\":\"$IP_ADDRESS\",\"local_port\":\"$PORT\",\"remote_port\":\"$PORT\"}"
- - "curl -H \"Content-Type: application/json\" -X POST -d \"$reverse_values\" http://localhost:31007/reverse_forward_tcp"
- - adb reverse tcp:$PORT tcp:$PORT
-
- test:
- commands:
- - cd zip
- - npm install underscore
- - node e2e/testRunner.js -- --skipInstallDeps --buildMode "skip" --skipCheckout --branch main --appPath app-e2eRelease-main.apk
-
-artifacts:
-- $WORKING_DIRECTORY
diff --git a/tests/e2e/compare/compare.js b/tests/e2e/compare/compare.js
index 63d8697e4053..3be7abc91188 100644
--- a/tests/e2e/compare/compare.js
+++ b/tests/e2e/compare/compare.js
@@ -1,7 +1,6 @@
-const fs = require('fs/promises');
-const fsSync = require('fs');
const _ = require('underscore');
const {computeProbability, computeZ} = require('./math');
+const {getStats} = require('../measure/math');
const printToConsole = require('./output/console');
const writeToMarkdown = require('./output/markdown');
@@ -26,18 +25,6 @@ const PROBABILITY_CONSIDERED_SIGNIFICANCE = 0.02;
*/
const DURATION_DIFF_THRESHOLD_SIGNIFICANCE = 100;
-const loadFile = (path) =>
- fs.readFile(path, 'utf8').then((data) => {
- const entries = JSON.parse(data);
-
- const result = {};
- entries.forEach((entry) => {
- result[entry.name] = entry;
- });
-
- return result;
- });
-
/**
*
* @param {string} name
@@ -83,8 +70,11 @@ function compareResults(compareEntries, baselineEntries) {
const current = compareEntries[name];
const baseline = baselineEntries[name];
+ const currentStats = getStats(baseline);
+ const deltaStats = getStats(current);
+
if (baseline && current) {
- compared.push(buildCompareEntry(name, current, baseline));
+ compared.push(buildCompareEntry(name, deltaStats, currentStats));
} else if (current) {
added.push({
name,
@@ -100,11 +90,9 @@ function compareResults(compareEntries, baselineEntries) {
const significance = _.chain(compared)
.filter((item) => item.isDurationDiffOfSignificance)
- .sort((a, b) => b.diff - a.diff)
.value();
const meaningless = _.chain(compared)
.filter((item) => !item.isDurationDiffOfSignificance)
- .sort((a, b) => b.diff - a.diff)
.value();
added.sort((a, b) => b.current.mean - a.current.mean);
@@ -118,25 +106,14 @@ function compareResults(compareEntries, baselineEntries) {
};
}
-module.exports = (baselineFile, compareFile, outputFile, outputFormat = 'all') => {
- const hasBaselineFile = fsSync.existsSync(baselineFile);
- if (!hasBaselineFile) {
- throw new Error(`Baseline results files "${baselineFile}" does not exists.`);
+module.exports = (main, delta, outputFile, outputFormat = 'all') => {
+ const outputData = compareResults(main, delta);
+
+ if (outputFormat === 'console' || outputFormat === 'all') {
+ printToConsole(outputData);
+ }
+
+ if (outputFormat === 'markdown' || outputFormat === 'all') {
+ return writeToMarkdown(outputFile, outputData);
}
- return loadFile(baselineFile).then((baseline) => {
- const hasCompareFile = fsSync.existsSync(compareFile);
- if (!hasCompareFile) {
- throw new Error(`Compare results files "${compareFile}" does not exists.`);
- }
- return loadFile(compareFile).then((compare) => {
- const outputData = compareResults(compare, baseline);
-
- if (outputFormat === 'console' || outputFormat === 'all') {
- printToConsole(outputData);
- }
- if (outputFormat === 'markdown' || outputFormat === 'all') {
- return writeToMarkdown(outputFile, outputData);
- }
- });
- });
};
diff --git a/tests/e2e/config.js b/tests/e2e/config.js
index c466000d0b53..4f7677cad706 100644
--- a/tests/e2e/config.js
+++ b/tests/e2e/config.js
@@ -22,9 +22,11 @@ const TEST_NAMES = {
* ```
*/
module.exports = {
- APP_PACKAGE: 'com.expensify.chat.adhoc',
+ MAIN_APP_PACKAGE: 'com.expensify.chat.e2e',
+ DELTA_APP_PACKAGE: 'com.expensify.chat.e2edelta',
- APP_PATH: './app-e2eRelease-main.apk',
+ MAIN_APP_PATH: './app-e2eRelease.apk',
+ DELTA_APP_PATH: './app-e2edeltaRelease.apk',
ENTRY_FILE: 'src/libs/E2E/reactNativeLaunchingTest.js',
diff --git a/tests/e2e/config.local.js b/tests/e2e/config.local.js
index 8cdfc50ac625..45b946b91aeb 100644
--- a/tests/e2e/config.local.js
+++ b/tests/e2e/config.local.js
@@ -1,5 +1,9 @@
module.exports = {
- APP_PACKAGE: 'com.expensify.chat.adhoc',
- APP_PATH: './android/app/build/outputs/apk/e2e/release/app-e2e-release.apk',
- RUNS: 4,
+ MAIN_APP_PACKAGE: 'com.expensify.chat.e2e',
+ DELTA_APP_PACKAGE: 'com.expensify.chat.e2edelta',
+ MAIN_APP_PATH: './android/app/build/outputs/apk/e2e/release/app-e2e-release.apk',
+ DELTA_APP_PATH: './android/app/build/outputs/apk/e2edelta/release/app-e2edelta-release.apk',
+
+ BOOT_COOL_DOWN: 1 * 1000,
+ RUNS: 8,
};
diff --git a/tests/e2e/testRunner.js b/tests/e2e/testRunner.js
index 54cde8f5b336..1e9a9a89caf0 100644
--- a/tests/e2e/testRunner.js
+++ b/tests/e2e/testRunner.js
@@ -23,11 +23,10 @@ const killApp = require('./utils/killApp');
const launchApp = require('./utils/launchApp');
const createServerInstance = require('./server');
const installApp = require('./utils/installApp');
-const math = require('./measure/math');
-const writeTestStats = require('./measure/writeTestStats');
const withFailTimeout = require('./utils/withFailTimeout');
const reversePort = require('./utils/androidReversePort');
const sleep = require('./utils/sleep');
+const compare = require('./compare/compare');
// VARIABLE CONFIGURATION
const args = process.argv.slice(2);
@@ -81,10 +80,8 @@ if (args.includes('--config')) {
}
// Important set app path after correct config file has been set
-let appPath = config.APP_PATH;
-if (args.includes('--appPath')) {
- appPath = args[args.indexOf('--appPath') + 1];
-}
+let mainAppPath = config.MAIN_APP_PATH;
+let deltaAppPath = config.DELTA_APP_PATH;
// Create some variables after the correct config file has been loaded
const OUTPUT_FILE = `${config.OUTPUT_DIR}/${label}.json`;
@@ -113,57 +110,103 @@ if (isDevMode) {
// START OF TEST CODE
-const restartApp = async () => {
- Logger.log('Killing app …');
- await killApp('android', config.APP_PACKAGE);
- Logger.log('Launching app …');
- await launchApp('android', config.APP_PACKAGE);
-};
-
const runTests = async () => {
// check if using buildMode "js-only" or "none" is possible
if (buildMode !== 'full') {
- const appExists = fs.existsSync(appPath);
- if (!appExists) {
+ const mainAppExists = fs.existsSync(mainAppPath);
+ const deltaAppExists = fs.existsSync(deltaAppPath);
+ if (!mainAppExists || !deltaAppExists) {
Logger.warn(`Build mode "${buildMode}" is not possible, because the app does not exist. Falling back to build mode "full".`);
- Logger.note(`App path: ${appPath}`);
+ Logger.note(`App path: ${mainAppPath}`);
buildMode = 'full';
}
}
- if (branch != null && !skipCheckout) {
- // Switch branch
- Logger.log(`Preparing tests on branch '${branch}' - git checkout`);
- await execAsync(`git checkout ${branch}`);
- }
-
- if (!skipInstallDeps) {
- Logger.log(`Preparing tests on branch '${branch}' - npm install`);
- await execAsync('npm i');
- }
-
// Build app
if (buildMode === 'full') {
- Logger.log(`Preparing tests on branch '${branch}' - building app`);
+ Logger.log(`Test setup - building main branch`);
+
+ if (!skipCheckout) {
+ // Switch branch
+ Logger.log(`Test setup - checkout main`);
+ await execAsync(`git checkout main`);
+ }
+
+ if (!skipInstallDeps) {
+ Logger.log(`Test setup - npm install`);
+ await execAsync('npm i');
+ }
+
await execAsync('npm run android-build-e2e');
+
+ if (branch != null && !skipCheckout) {
+ // Switch branch
+ Logger.log(`Test setup - checkout branch '${branch}'`);
+ await execAsync(`git checkout ${branch}`);
+ }
+
+ if (!skipInstallDeps) {
+ Logger.log(`Test setup - npm install`);
+ await execAsync('npm i');
+ }
+
+ Logger.log(`Test setup '${branch}' - building delta branch`);
+ await execAsync('npm run android-build-e2edelta');
} else if (buildMode === 'js-only') {
- Logger.log(`Preparing tests on branch '${branch}' - building js bundle`);
+ Logger.log(`Test setup '${branch}' - building js bundle`);
+
+ if (!skipInstallDeps) {
+ Logger.log(`Test setup '${branch}' - npm install`);
+ await execAsync('npm i');
+ }
// Build a new JS bundle
+ if (!skipCheckout) {
+ // Switch branch
+ Logger.log(`Test setup - checkout main`);
+ await execAsync(`git checkout main`);
+ }
+
+ if (!skipInstallDeps) {
+ Logger.log(`Test setup - npm install`);
+ await execAsync('npm i');
+ }
+
const tempDir = `${config.OUTPUT_DIR}/temp`;
- const tempBundlePath = `${tempDir}/index.android.bundle`;
+ let tempBundlePath = `${tempDir}/index.android.bundle`;
+ await execAsync(`rm -rf ${tempDir} && mkdir ${tempDir}`);
+ await execAsync(`npx react-native bundle --platform android --dev false --entry-file ${config.ENTRY_FILE} --bundle-output ${tempBundlePath}`, {E2E_TESTING: 'true'});
+ // Repackage the existing native app with the new bundle
+ let tempApkPath = `${tempDir}/app-release.apk`;
+ await execAsync(`./scripts/android-repackage-app-bundle-and-sign.sh ${mainAppPath} ${tempBundlePath} ${tempApkPath}`);
+ mainAppPath = tempApkPath;
+
+ // Build a new JS bundle
+ if (!skipCheckout) {
+ // Switch branch
+ Logger.log(`Test setup - checkout main`);
+ await execAsync(`git checkout ${branch}`);
+ }
+
+ if (!skipInstallDeps) {
+ Logger.log(`Test setup - npm install`);
+ await execAsync('npm i');
+ }
+
+ tempBundlePath = `${tempDir}/index.android.bundle`;
await execAsync(`rm -rf ${tempDir} && mkdir ${tempDir}`);
await execAsync(`npx react-native bundle --platform android --dev false --entry-file ${config.ENTRY_FILE} --bundle-output ${tempBundlePath}`, {E2E_TESTING: 'true'});
// Repackage the existing native app with the new bundle
- const tempApkPath = `${tempDir}/app-release.apk`;
- await execAsync(`./scripts/android-repackage-app-bundle-and-sign.sh ${appPath} ${tempBundlePath} ${tempApkPath}`);
- appPath = tempApkPath;
+ tempApkPath = `${tempDir}/app-release.apk`;
+ await execAsync(`./scripts/android-repackage-app-bundle-and-sign.sh ${deltaAppPath} ${tempBundlePath} ${tempApkPath}`);
+ deltaAppPath = tempApkPath;
}
- // Install app and reverse port
- let progressLog = Logger.progressInfo('Installing app and reversing port');
- await installApp('android', config.APP_PACKAGE, appPath);
+ let progressLog = Logger.progressInfo('Installing apps and reversing port');
+
+ await installApp('android', config.MAIN_APP_PACKAGE, defaultConfig.MAIN_APP_PATH);
+ await installApp('android', config.DELTA_APP_PACKAGE, defaultConfig.DELTA_APP_PATH);
await reversePort();
progressLog.done();
@@ -171,8 +214,8 @@ const runTests = async () => {
const server = createServerInstance();
await server.start();
- // Create a dict in which we will store the collected metrics for all tests
- const resultsByTestName = {};
+ // Create a dict in which we will store the run durations for all tests
+ const results = {};
// Collect results while tests are being executed
server.addTestResultListener((testResult) => {
@@ -191,28 +234,33 @@ const runTests = async () => {
result = testResult.renderCount;
}
- Logger.log(`[LISTENER] Test '${testResult.name}' measured ${result}`);
- resultsByTestName[testResult.name] = (resultsByTestName[testResult.name] || []).concat(result);
+ Logger.log(`[LISTENER] Test '${testResult.name}' on '${testResult.branch}' measured ${result}`);
+
+ if (!results[testResult.branch]) {
+ results[testResult.branch] = {};
+ }
+
+ results[testResult.branch][testResult.name] = (results[testResult.branch][testResult.name] || []).concat(result);
});
// Run the tests
- const numOfTests = _.values(config.TESTS_CONFIG).length;
- for (let testIndex = 0; testIndex < numOfTests; testIndex++) {
- const testConfig = _.values(config.TESTS_CONFIG)[testIndex];
+ const suites = _.values(config.TESTS_CONFIG);
+ for (let suiteIndex = 0; suiteIndex < suites.length; suiteIndex++) {
+ const suite = _.values(config.TESTS_CONFIG)[suiteIndex];
// check if we want to skip the test
if (args.includes('--includes')) {
const includes = args[args.indexOf('--includes') + 1];
// assume that "includes" is a regexp
- if (!testConfig.name.match(includes)) {
+ if (!suite.name.match(includes)) {
// eslint-disable-next-line no-continue
continue;
}
}
- const coolDownLogs = Logger.progressInfo(`Cooling down for ${config.COOL_DOWN / 1000}s`);
- coolDownLogs.updateText(`Cooling down for ${config.COOL_DOWN / 1000}s`);
+ const coolDownLogs = Logger.progressInfo(`Cooling down for ${config.BOOT_COOL_DOWN / 1000}s`);
+ coolDownLogs.updateText(`Cooling down for ${config.BOOT_COOL_DOWN / 1000}s`);
// Having the cooldown right at the beginning should hopefully lower the chances of heat
// throttling from the previous run (which we have no control over and will be a
@@ -220,18 +268,22 @@ const runTests = async () => {
await sleep(config.BOOT_COOL_DOWN);
coolDownLogs.done();
- server.setTestConfig(testConfig);
+ server.setTestConfig(suite);
- const warmupLogs = Logger.progressInfo(`Running warmup '${testConfig.name}'`);
+ const warmupLogs = Logger.progressInfo(`Running warmup '${suite.name}'`);
- let progressText = `Warmup for suite '${testConfig.name}' [${testIndex + 1}/${numOfTests}]\n`;
+ let progressText = `Warmup for suite '${suite.name}' [${suiteIndex + 1}/${suites.length}]\n`;
warmupLogs.updateText(progressText);
- await restartApp();
+ Logger.log('Killing main app');
+ await killApp('android', config.MAIN_APP_PACKAGE);
+ Logger.log('Launching main app');
+ await launchApp('android', config.MAIN_APP_PACKAGE);
await withFailTimeout(
new Promise((resolve) => {
const cleanup = server.addTestDoneListener(() => {
+ Logger.log('Main warm up ready ✅');
cleanup();
resolve();
});
@@ -239,25 +291,68 @@ const runTests = async () => {
progressText,
);
+ Logger.log('Killing main app');
+ await killApp('android', config.MAIN_APP_PACKAGE);
+
+ Logger.log('Killing delta app');
+ await killApp('android', config.DELTA_APP_PACKAGE);
+ Logger.log('Launching delta app');
+ await launchApp('android', config.DELTA_APP_PACKAGE);
+
+ await withFailTimeout(
+ new Promise((resolve) => {
+ const cleanup = server.addTestDoneListener(() => {
+ Logger.log('Delta warm up ready ✅');
+ cleanup();
+ resolve();
+ });
+ }),
+ progressText,
+ );
+
+ Logger.log('Killing delta app');
+ await killApp('android', config.DELTA_APP_PACKAGE);
+
warmupLogs.done();
// We run each test multiple time to average out the results
const testLog = Logger.progressInfo('');
for (let i = 0; i < config.RUNS; i++) {
- progressText = `Suite '${testConfig.name}' [${testIndex + 1}/${numOfTests}], iteration [${i + 1}/${config.RUNS}]\n`;
+ progressText = `Suite '${suite.name}' [${suiteIndex + 1}/${suites.length}], iteration [${i + 1}/${config.RUNS}]\n`;
testLog.updateText(progressText);
- Logger.log('Killing app...');
- await killApp('android', config.APP_PACKAGE);
+ Logger.log('Killing delta app');
+ await killApp('android', config.DELTA_APP_PACKAGE);
- testLog.updateText(`Coolin down phone 🧊 ${config.SUITE_COOL_DOWN / 1000}s\n`);
+ Logger.log('Killing main app');
+ await killApp('android', config.MAIN_APP_PACKAGE);
- // Adding the cool down between booting the app again, had the side-effect of actually causing a cold boot,
- // which increased TTI/bundle load JS times significantly but also stabilized standard deviation.
- await sleep(config.SUITE_COOL_DOWN);
+ Logger.log('Starting main app');
+ await launchApp('android', config.MAIN_APP_PACKAGE);
- Logger.log('Starting app...');
- await launchApp('android', config.APP_PACKAGE);
+ // Wait for a test to finish by waiting on its done call to the http server
+ try {
+ await withFailTimeout(
+ new Promise((resolve) => {
+ const cleanup = server.addTestDoneListener(() => {
+ Logger.log(`Test iteration ${i + 1} done!`);
+ cleanup();
+ resolve();
+ });
+ }),
+ progressText,
+ );
+ } catch (e) {
+ // When we fail due to a timeout it's interesting to take a screenshot of the emulator to see whats going on
+ testLog.done();
+ throw e; // Rethrow to abort execution
+ }
+
+ Logger.log('Killing main app');
+ await killApp('android', config.MAIN_APP_PACKAGE);
+
+ Logger.log('Starting delta app');
+ await launchApp('android', config.DELTA_APP_PACKAGE);
// Wait for a test to finish by waiting on its done call to the http server
try {
@@ -283,16 +378,8 @@ const runTests = async () => {
// Calculate statistics and write them to our work file
progressLog = Logger.progressInfo('Calculating statics and writing results');
- for (const testName of _.keys(resultsByTestName)) {
- const stats = math.getStats(resultsByTestName[testName]);
- await writeTestStats(
- {
- name: testName,
- ...stats,
- },
- OUTPUT_FILE,
- );
- }
+ compare(results.main, results.delta, `${config.OUTPUT_DIR}/output.md`);
+
progressLog.done();
await server.stop();
diff --git a/tests/e2e/utils/installApp.js b/tests/e2e/utils/installApp.js
index ff961940826a..3741e459ea83 100644
--- a/tests/e2e/utils/installApp.js
+++ b/tests/e2e/utils/installApp.js
@@ -1,4 +1,3 @@
-const {APP_PACKAGE} = require('../config');
const execAsync = require('./execAsync');
const Logger = require('./logger');
@@ -11,7 +10,7 @@ const Logger = require('./logger');
* @param {String} path
* @returns {Promise}
*/
-module.exports = function (platform = 'android', packageName = APP_PACKAGE, path) {
+module.exports = function (platform = 'android', packageName, path) {
if (platform !== 'android') {
throw new Error(`installApp() missing implementation for platform: ${platform}`);
}