Add workflow to run detox iOS tests (#7878)
This commit is contained in:
parent
cf254f8a86
commit
f46ddb49d0
37 changed files with 1896 additions and 9238 deletions
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -27,6 +27,7 @@ Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fie
|
|||
- [ ] Has UI changes
|
||||
- [ ] Includes text changes and localization file updates
|
||||
- [ ] Have tested against the 5 core themes to ensure consistency between them.
|
||||
- [ ] Have run E2E tests by adding label `E2E iOS tests for PR`.
|
||||
|
||||
#### Device Information
|
||||
This PR was tested on: <!-- Device name(s), OS version(s) -->
|
||||
|
|
|
|||
39
.github/actions/generate-specs/action.yml
vendored
Normal file
39
.github/actions/generate-specs/action.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2024 Mattermost, Inc.
|
||||
name: "generate-specs"
|
||||
description: This action used to split Detox integration tests based on the parallelism provided
|
||||
|
||||
inputs:
|
||||
search_path:
|
||||
description: The path to look for from within the directory
|
||||
required: true
|
||||
parallelism:
|
||||
description: The parallelism for the tests
|
||||
required: true
|
||||
device_name:
|
||||
description: The name of Device used for the tests
|
||||
required: false
|
||||
default: "iPhone 15"
|
||||
device_os_version:
|
||||
description: The os of the device used for the tests
|
||||
required: false
|
||||
default: "iOS 17.1"
|
||||
|
||||
outputs:
|
||||
specs:
|
||||
description: The specs generated for the strategy
|
||||
value: ${{ steps.generate-specs.outputs.specs }}
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: ci/generate-specs
|
||||
id: generate-specs
|
||||
env:
|
||||
PARALLELISM: ${{ inputs.parallelism }}
|
||||
SEARCH_PATH: ${{ inputs.search_path }}
|
||||
DEVICE_NAME: ${{ inputs.device_name }}
|
||||
DEVICE_OS_VERSION: ${{ inputs.device_os_version }}
|
||||
run: |
|
||||
set -e
|
||||
node ${{ github.action_path }}/split-tests.js | tee output.json
|
||||
echo "specs=$(cat output.json)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
94
.github/actions/generate-specs/split-tests.js
vendored
Normal file
94
.github/actions/generate-specs/split-tests.js
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
class DeviceInfo {
|
||||
constructor(deviceName, deviceOsVersion) {
|
||||
this.deviceName = deviceName;
|
||||
this.deviceOsVersion = deviceOsVersion;
|
||||
}
|
||||
}
|
||||
|
||||
class SpecGroup {
|
||||
constructor(runId, specs, deviceInfo) {
|
||||
this.runId = runId;
|
||||
this.specs = specs;
|
||||
this.deviceName = deviceInfo.deviceName;
|
||||
this.deviceOsVersion = deviceInfo.deviceOsVersion;
|
||||
}
|
||||
}
|
||||
|
||||
class Specs {
|
||||
constructor(searchPath, parallelism, deviceInfo) {
|
||||
this.searchPath = searchPath;
|
||||
this.parallelism = parallelism;
|
||||
this.rawFiles = [];
|
||||
this.groupedFiles = [];
|
||||
this.deviceInfo = deviceInfo;
|
||||
}
|
||||
|
||||
findFiles() {
|
||||
const dirPath = path.join(this.searchPath);
|
||||
|
||||
const fileRegex = /\.e2e\.ts$/;
|
||||
|
||||
const walkSync = (currentPath) => {
|
||||
const files = fs.readdirSync(currentPath);
|
||||
|
||||
files.forEach((file) => {
|
||||
const filePath = path.join(currentPath, file);
|
||||
const stats = fs.statSync(filePath);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
walkSync(filePath);
|
||||
} else if (fileRegex.test(filePath)) {
|
||||
const relativeFilePath = filePath.replace(dirPath + '/', '');
|
||||
const fullPath = path.join(this.searchPath, relativeFilePath);
|
||||
this.rawFiles.push(fullPath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
walkSync(dirPath);
|
||||
}
|
||||
|
||||
generateSplits() {
|
||||
const chunkSize = Math.ceil(this.rawFiles.length / this.parallelism);
|
||||
let runNo = 1;
|
||||
|
||||
for (let i = 0; i < this.rawFiles.length; i += chunkSize) {
|
||||
const end = Math.min(i + chunkSize, this.rawFiles.length);
|
||||
const fileGroup = this.rawFiles.slice(i, end).join(' ');
|
||||
const specFileGroup = new SpecGroup(runNo.toString(), fileGroup, this.deviceInfo);
|
||||
this.groupedFiles.push(specFileGroup);
|
||||
|
||||
if (end === this.rawFiles.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
runNo++;
|
||||
}
|
||||
}
|
||||
|
||||
dumpSplits() {
|
||||
const output = {
|
||||
include: this.groupedFiles,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(output));
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const searchPath = process.env.SEARCH_PATH;
|
||||
const parallelism = parseInt(process.env.PARALLELISM, 10);
|
||||
const deviceName = process.env.DEVICE_NAME;
|
||||
const deviceOsVersion = process.env.DEVICE_OS_VERSION;
|
||||
const deviceInfo = new DeviceInfo(deviceName, deviceOsVersion);
|
||||
const specs = new Specs(searchPath, parallelism, deviceInfo);
|
||||
|
||||
specs.findFiles();
|
||||
specs.generateSplits();
|
||||
specs.dumpSplits();
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -14,3 +14,11 @@ runs:
|
|||
npm run ios-gems
|
||||
npm run pod-install
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Cache Pods
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: Pods
|
||||
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pods-
|
||||
|
|
|
|||
|
|
@ -15,5 +15,13 @@ runs:
|
|||
echo "::endgroup::"
|
||||
working-directory: ./fastlane
|
||||
|
||||
- name: Cache Ruby gems
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: vendor/bundle
|
||||
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gems-
|
||||
|
||||
- name: ci/prepare-node-deps
|
||||
uses: ./.github/actions/prepare-node-deps
|
||||
|
|
|
|||
10
.github/actions/prepare-node-deps/action.yaml
vendored
10
.github/actions/prepare-node-deps/action.yaml
vendored
|
|
@ -5,7 +5,7 @@ runs:
|
|||
using: composite
|
||||
steps:
|
||||
- name: ci/setup-node
|
||||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
|
@ -21,6 +21,14 @@ runs:
|
|||
node node_modules/\@sentry/cli/scripts/install.js
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Cache Node.js modules
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: ci/patch-npm-dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
|
|
|||
6
.github/workflows/build-android-beta.yml
vendored
6
.github/workflows/build-android-beta.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: ci/test
|
||||
uses: ./.github/actions/test
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-android-build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-android-beta-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-beta-${{ github.run_id }}
|
||||
path: "*.apk"
|
||||
|
|
|
|||
6
.github/workflows/build-android-release.yml
vendored
6
.github/workflows/build-android-release.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: ci/test
|
||||
uses: ./.github/actions/test
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-android-build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-android-release-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-release-${{ github.run_id }}
|
||||
path: "*.apk"
|
||||
|
|
|
|||
10
.github/workflows/build-ios-beta.yml
vendored
10
.github/workflows/build-ios-beta.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: ci/test
|
||||
uses: ./.github/actions/test
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-ios-build
|
||||
uses: ./.github/actions/prepare-ios-build
|
||||
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-ios-pr-simulator
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: Mattermost-simulator-x86_64.app.zip
|
||||
|
|
@ -56,7 +56,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/output-ssh-private-key
|
||||
shell: bash
|
||||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-ios-beta-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-build-beta-${{ github.run_id }}
|
||||
path: "*.ipa"
|
||||
|
|
|
|||
10
.github/workflows/build-ios-release.yml
vendored
10
.github/workflows/build-ios-release.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: ci/test
|
||||
uses: ./.github/actions/test
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-ios-build
|
||||
uses: ./.github/actions/prepare-ios-build
|
||||
|
|
@ -65,7 +65,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-ios-release-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-build-release-${{ github.run_id }}
|
||||
path: "*.ipa"
|
||||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-ios-build
|
||||
uses: ./.github/actions/prepare-ios-build
|
||||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-ios-pr-simulator
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: Mattermost-simulator-x86_64.app.zip
|
||||
|
|
|
|||
10
.github/workflows/build-pr.yml
vendored
10
.github/workflows/build-pr.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
if: ${{ github.event.label.name == 'Build Apps for PR' || github.event.label.name == 'Build App for iOS' || github.event.label.name == 'Build App for Android' }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: ci/test
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-ios-pr-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-build-pr-${{ github.run_id }}
|
||||
path: "*.ipa"
|
||||
|
|
@ -72,7 +72,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-android-pr-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-pr-${{ github.run_id }}
|
||||
path: "*.apk"
|
||||
|
|
|
|||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -16,6 +16,6 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: ci/test
|
||||
uses: ./.github/actions/test
|
||||
|
|
|
|||
300
.github/workflows/e2e-detox-ci-template.yml
vendored
Normal file
300
.github/workflows/e2e-detox-ci-template.yml
vendored
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
name: Detox E2E Tests Template
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run-ios-tests:
|
||||
description: 'Run iOS tests'
|
||||
required: true
|
||||
type: boolean
|
||||
run-android-tests:
|
||||
description: 'Run Android tests'
|
||||
required: true
|
||||
type: boolean
|
||||
remove-pr-label:
|
||||
description: 'Remove PR label'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
status_check_context:
|
||||
type: string
|
||||
required: true
|
||||
commit_sha:
|
||||
type: string
|
||||
required: true
|
||||
run-type:
|
||||
type: string
|
||||
required: false
|
||||
default: 'PR'
|
||||
testcase_failure_fatal:
|
||||
description: 'Should failures be considered fatal'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
AWS_REGION: 'us-east-1'
|
||||
ADMIN_EMAIL: ${{ secrets.MM_MOBILE_E2E_ADMIN_EMAIL }}
|
||||
ADMIN_USERNAME: ${{ secrets.MM_MOBILE_E2E_ADMIN_USERNAME }}
|
||||
ADMIN_PASSWORD: ${{ secrets.MM_MOBILE_E2E_ADMIN_PASSWORD }}
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
COMMIT_HASH: ${{ github.sha }}
|
||||
DETOX_AWS_S3_BUCKET: 'mattermost-detox-report'
|
||||
DETOX_AWS_ACCESS_KEY_ID: ${{ secrets.MM_MOBILE_DETOX_AWS_ACCESS_KEY_ID }}
|
||||
DETOX_AWS_SECRET_ACCESS_KEY: ${{ secrets.MM_MOBILE_DETOX_AWS_SECRET_ACCESS_KEY }}
|
||||
HEADLESS: 'true'
|
||||
TYPE: ${{ inputs.run-type }}
|
||||
PULL_REQUEST: 'https://github.com/mattermost/mattermost-mobile/pull/${{ github.event.number }}'
|
||||
SITE_1_URL: ${{ secrets.MM_MOBILE_E2E_SITE_1_URL }}
|
||||
SITE_2_URL: ${{ secrets.MM_MOBILE_E2E_SITE_2_URL }}
|
||||
SITE_3_URL: ${{ secrets.MM_MOBILE_E2E_SITE_3_URL }}
|
||||
ZEPHYR_ENABLE: false
|
||||
JIRA_PROJECT_KEY: 'MM'
|
||||
ZEPHYR_API_KEY: ${{ secrets.MM_MOBILE_E2E_ZEPHYR_API_KEY }}
|
||||
ZEPHYR_FOLDER_ID: '3233873'
|
||||
TEST_CYCLE_LINK_PREFIX: ${{ secrets.MM_MOBILE_E2E_TEST_CYCLE_LINK_PREFIX }}
|
||||
WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }}
|
||||
FAILURE_MESSAGE: "Something has failed"
|
||||
|
||||
jobs:
|
||||
update-initial-status:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ inputs.commit_sha }}
|
||||
context: ${{ inputs.status_check_context }}
|
||||
description: Detox tests for mattermost mobile app
|
||||
status: pending
|
||||
|
||||
generate-specs:
|
||||
needs: update-initial-status
|
||||
runs-on: ubuntu-22.04
|
||||
if: ${{ inputs.run-ios-tests || inputs.run-android-tests }}
|
||||
outputs:
|
||||
specs: ${{ steps.generate-specs.outputs.specs }}
|
||||
build_id: ${{ env.BUILD_ID }}
|
||||
device_name: ${{ env.DEVICE_NAME }}
|
||||
device_os_version: ${{ env.DEVICE_OS_VERSION }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: Set Build ID
|
||||
id: resolve-device
|
||||
run: |
|
||||
cd detox
|
||||
DEVICE_NAME=$(node -p "require('./.detoxrc').devices['ios.simulator'].device.type")
|
||||
DEVICE_OS_VERSION=$(node -p "require('./.detoxrc').devices['ios.simulator'].device.os")
|
||||
BUILD_ID="${{ github.run_id }}-$DEVICE_NAME-$DEVICE_OS_VERSION"
|
||||
|
||||
echo "BUILD_ID=$(echo ${BUILD_ID} | sed 's/ /_/g')" >> $GITHUB_ENV
|
||||
echo "DEVICE_NAME=$(echo ${DEVICE_NAME})" >> $GITHUB_ENV
|
||||
echo "DEVICE_OS_VERSION=$(echo ${DEVICE_OS_VERSION})" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate Test Specs
|
||||
id: generate-specs
|
||||
uses: ./.github/actions/generate-specs
|
||||
with:
|
||||
parallelism: 10
|
||||
search_path: detox/e2e/test
|
||||
device_name: ${{ env.DEVICE_NAME }}
|
||||
device_os_version: ${{env.DEVICE_OS_VERSION}}
|
||||
|
||||
build-ios-simulator:
|
||||
needs: update-initial-status
|
||||
runs-on: macos-14-large
|
||||
if: ${{ inputs.run-ios-tests }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: Prepare iOS Build
|
||||
uses: ./.github/actions/prepare-ios-build
|
||||
|
||||
- name: Build iOS Simulator
|
||||
env:
|
||||
TAG: "${{ github.ref_name }}"
|
||||
AWS_ACCESS_KEY_ID: "${{ secrets.MM_MOBILE_BETA_AWS_ACCESS_KEY_ID }}"
|
||||
AWS_SECRET_ACCESS_KEY: "${{ secrets.MM_MOBILE_BETA_AWS_SECRET_ACCESS_KEY }}"
|
||||
GITHUB_TOKEN: "${{ secrets.MM_MOBILE_GITHUB_TOKEN }}"
|
||||
run: bundle exec fastlane ios simulator --env ios.simulator
|
||||
working-directory: ./fastlane
|
||||
|
||||
- name: Upload iOS Simulator Build
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: Mattermost-simulator-x86_64.app.zip
|
||||
|
||||
e2e-ios:
|
||||
continue-on-error: true
|
||||
env:
|
||||
IOS: true
|
||||
runs-on: macos-14-large
|
||||
name: ios-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||
if: ${{ inputs.run-ios-tests }}
|
||||
timeout-minutes: 40
|
||||
needs:
|
||||
- generate-specs
|
||||
- build-ios-simulator
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.generate-specs.outputs.specs) }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-node-deps
|
||||
uses: ./.github/actions/prepare-node-deps
|
||||
|
||||
- name: Install Homebrew Dependencies
|
||||
run: |
|
||||
brew tap wix/brew
|
||||
brew install applesimutils
|
||||
|
||||
- name: Download iOS Simulator Build
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: mobile-artifacts
|
||||
|
||||
- name: Unzip iOS Simulator Build
|
||||
run: unzip -o mobile-artifacts/*.zip -d mobile-artifacts/
|
||||
|
||||
- name: Start React Native Metro Server
|
||||
run: npm run start &
|
||||
|
||||
- name: Install Detox Dependencies
|
||||
run: cd detox && npm i
|
||||
|
||||
- name: Run Detox E2E Tests
|
||||
continue-on-error: true # Label might have been removed manually
|
||||
run: cd detox && npm run e2e:ios-test -- ${{ matrix.specs }}
|
||||
|
||||
- name: Upload iOS Test Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: ios-results-${{ matrix.runId }}
|
||||
path: detox/artifacts/
|
||||
|
||||
download-e2e-results:
|
||||
runs-on: ubuntu-22.04
|
||||
if: ${{ inputs.run-ios-tests || inputs.run-android-tests }}
|
||||
needs:
|
||||
- generate-specs
|
||||
- e2e-ios
|
||||
outputs:
|
||||
failures: "${{ steps.calculate-failures.outputs.failures }}"
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-node-deps
|
||||
uses: ./.github/actions/prepare-node-deps
|
||||
|
||||
- name: Create artifacts directory
|
||||
run: mkdir -p detox/artifacts/
|
||||
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
path: detox/artifacts/
|
||||
pattern: ios-results-*
|
||||
|
||||
- name: Save report Detox Dependencies
|
||||
id: report-link
|
||||
run: |
|
||||
cd detox
|
||||
npm ci
|
||||
npm run e2e:save-report
|
||||
env:
|
||||
IOS: ${{ inputs.run-ios-tests }}
|
||||
BUILD_ID: ${{ needs.generate-specs.outputs.build_id }}
|
||||
DEVICE_NAME: ${{ needs.generate-specs.outputs.device_name }}
|
||||
DEVICE_OS_VERSION: ${{ needs.generate-specs.outputs.device_os_version }}
|
||||
|
||||
- name: Calculate failures
|
||||
id: calculate-failures
|
||||
run: |
|
||||
FAILURES=$(find detox/artifacts/ -name 'summary.json' | xargs -l jq -r '.stats.failures' | jq -s add)
|
||||
echo "failures=${FAILURES}" >> $GITHUB_OUTPUT
|
||||
echo "Detox run completed with $FAILURES failures"
|
||||
|
||||
update-final-status:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- generate-specs
|
||||
- download-e2e-results
|
||||
steps:
|
||||
- name: Set Target URL
|
||||
id: set-url
|
||||
run: |
|
||||
s3Folder="${{ env.BUILD_ID }}-${{ env.COMMIT_HASH }}-${{ env.BRANCH }}"
|
||||
s3Folder=$(echo "$s3Folder" | sed 's/\./-/g')
|
||||
|
||||
if ${{ inputs.run-ios-tests }}; then
|
||||
echo "TARGET_URL=https://${{ env.DETOX_AWS_S3_BUCKET }}.s3.amazonaws.com/${s3Folder}/jest-stare/ios-report.html" >> $GITHUB_ENV
|
||||
else
|
||||
echo "TARGET_URL=https://${{ env.DETOX_AWS_S3_BUCKET }}.s3.amazonaws.com/${s3Folder}/jest-stare/android-report.html" >> $GITHUB_ENV
|
||||
fi
|
||||
env:
|
||||
BUILD_ID: ${{ needs.generate-specs.outputs.build_id }}
|
||||
|
||||
- name: Determine Status
|
||||
id: determine-status
|
||||
run: |
|
||||
if [[ ${{ needs.download-e2e-results.outputs.failures }} -gt 0 && "${{ inputs.testcase_failure_fatal }}" == "true" ]]; then
|
||||
echo "status=failure" >> $GITHUB_ENV
|
||||
else
|
||||
echo "status=success" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ inputs.commit_sha }}
|
||||
context: ${{ inputs.status_check_context }}
|
||||
description: |
|
||||
Completed with ${{ needs.download-e2e-results.outputs.failures }} failures
|
||||
status: ${{ env.status }}
|
||||
target_url: ${{ env.TARGET_URL }}
|
||||
|
||||
e2e-remove-label:
|
||||
if: ${{ inputs.remove-pr-label }}
|
||||
needs:
|
||||
- download-e2e-results
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: e2e/remove-label-from-pr
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
continue-on-error: true # Label might have been removed manually
|
||||
with:
|
||||
script: |
|
||||
const iosLabel = 'E2E iOS tests for PR';
|
||||
const androidLabel = 'E2E Android tests for PR';
|
||||
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||
|
||||
if (labels.includes(iosLabel)) {
|
||||
github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: iosLabel,
|
||||
});
|
||||
}
|
||||
|
||||
if (labels.includes(androidLabel)) {
|
||||
github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: androidLabel,
|
||||
});
|
||||
}
|
||||
94
.github/workflows/e2e-detox.yml
vendored
Normal file
94
.github/workflows/e2e-detox.yml
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
name: Detox E2E Tests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # every day at midnight
|
||||
push:
|
||||
branches:
|
||||
- 'release-*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
types:
|
||||
- labeled
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ios_version:
|
||||
description: 'Enter iOS version'
|
||||
required: false
|
||||
default: "iOS 17.4"
|
||||
type: choice
|
||||
options:
|
||||
- "iOS 17.5"
|
||||
- "iOS 17.4"
|
||||
- "iOS 17.2"
|
||||
- "iOS 17.0"
|
||||
device_name:
|
||||
description: 'Enter iOS Device Name'
|
||||
required: false
|
||||
default: "iPhone 15 Pro"
|
||||
type: choice
|
||||
options:
|
||||
- "iPhone 15 Pro"
|
||||
- "iPhone 15"
|
||||
- "iPhone 14 Pro"
|
||||
- "iPhone 14"
|
||||
jobs:
|
||||
|
||||
run-ios-tests-on-pr:
|
||||
name: iOS Mobile Tests on PR
|
||||
uses: ./.github/workflows/e2e-detox-ci-template.yml
|
||||
if: ${{
|
||||
(
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.labels &&
|
||||
contains(github.event.pull_request.labels.*.name, 'E2E iOS tests for PR')
|
||||
)
|
||||
}}
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-android-tests: false
|
||||
remove-pr-label: true
|
||||
commit_sha: "${{ github.event.pull_request.head.sha || github.sha }}"
|
||||
status_check_context: "Detox iOS Mobile Tests on PR"
|
||||
run-type: 'PR'
|
||||
secrets: inherit
|
||||
|
||||
run-ios-tests-on-release:
|
||||
name: iOS Mobile Tests on Release
|
||||
uses: ./.github/workflows/e2e-detox-ci-template.yml
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release-') }}
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-android-tests: false
|
||||
remove-pr-label: false
|
||||
commit_sha: "${{ github.sha }}"
|
||||
status_check_context: "Detox iOS Mobile Tests on Release"
|
||||
run-type: 'RELEASE'
|
||||
secrets: inherit
|
||||
|
||||
run-ios-tests-on-main-scheduled:
|
||||
name: iOS Mobile Tests on Main (Scheduled)
|
||||
uses: ./.github/workflows/e2e-detox-ci-template.yml
|
||||
if: ${{ github.event_name == 'schedule' && github.ref == 'refs/heads/main' }}
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-android-tests: false
|
||||
remove-pr-label: false
|
||||
commit_sha: "${{ github.sha }}"
|
||||
status_check_context: "Detox iOS Mobile Tests on Main (Scheduled)"
|
||||
run-type: 'MAIN'
|
||||
secrets: inherit
|
||||
|
||||
run-ios-tests-on-manual-trigger:
|
||||
name: iOS Mobile Tests on Manual Trigger
|
||||
uses: ./.github/workflows/e2e-detox-ci-template.yml
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-android-tests: false
|
||||
remove-pr-label: false
|
||||
commit_sha: "${{ github.sha }}"
|
||||
status_check_context: "Detox iOS Mobile Tests on Manual Trigger"
|
||||
run-type: 'MANUAL'
|
||||
secrets: inherit
|
||||
12
.github/workflows/github-release.yml
vendored
12
.github/workflows/github-release.yml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: ci/test
|
||||
uses: ./.github/actions/test
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-ios-build
|
||||
uses: ./.github/actions/prepare-ios-build
|
||||
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-ios-unsigned
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
path: Mattermost-unsigned.ipa
|
||||
name: Mattermost-unsigned.ipa
|
||||
|
|
@ -53,7 +53,7 @@ jobs:
|
|||
- test
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: ci/prepare-android-build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
|
|
@ -68,7 +68,7 @@ jobs:
|
|||
working-directory: ./fastlane
|
||||
|
||||
- name: ci/upload-android-unsigned-build
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
path: Mattermost-unsigned.apk
|
||||
name: Mattermost-unsigned.apk
|
||||
|
|
@ -80,7 +80,7 @@ jobs:
|
|||
- build-android-unsigned
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- uses: ruby/setup-ruby@22fdc77bf4148f810455b226c90fb81b5cbc00a7 # v1.171.0
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"apps": {
|
||||
"ios.debug": {
|
||||
"type": "ios.app",
|
||||
"binaryPath": "../ios/Build/Products/Debug-iphonesimulator/Mattermost.app"
|
||||
"binaryPath": "../mobile-artifacts/Mattermost.app"
|
||||
},
|
||||
"ios.release": {
|
||||
"type": "ios.app",
|
||||
|
|
@ -30,7 +30,8 @@
|
|||
"ios.simulator": {
|
||||
"type": "ios.simulator",
|
||||
"device": {
|
||||
"type": "iPhone 14"
|
||||
"type": "iPhone 15 Pro",
|
||||
"os": "iOS 17.4"
|
||||
}
|
||||
},
|
||||
"android.emulator": {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export const apiCreateUser = async (baseUrl: string, {prefix = 'user', user = nu
|
|||
newUser,
|
||||
);
|
||||
|
||||
return {user: {...response.data, password: newUser.password}};
|
||||
return {user: {...response.data, newUser}};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export const serverOneUrl = process.env.SITE_1_URL || (process.env.IOS === 'true' ? 'http://127.0.0.1:8065' : 'http://10.0.2.2:8065');
|
||||
export const siteOneUrl = process.env.SITE_1_URL || 'http://127.0.0.1:8065';
|
||||
export const serverTwoUrl = process.env.SITE_2_URL || 'https://mobile02.test.mattermost.cloud';
|
||||
export const siteTwoUrl = process.env.SITE_2_URL || 'https://mobile02.test.mattermost.cloud';
|
||||
export const serverThreeUrl = process.env.SITE_3_URL || 'https://mobile03.test.mattermost.cloud';
|
||||
export const siteThreeUrl = process.env.SITE_3_URL || 'https://mobile03.test.mattermost.cloud';
|
||||
export const serverOneUrl = process.env.SITE_1_URL || (process.env.IOS === 'true' ? 'http://localhost:8065' : 'http://10.0.2.2:8065');
|
||||
export const siteOneUrl = process.env.SITE_1_URL || 'http://localhost:8065';
|
||||
export const serverTwoUrl = process.env.SITE_2_URL || 'http://localhost:8065';
|
||||
export const siteTwoUrl = process.env.SITE_2_URL || 'http://localhost:8065';
|
||||
export const serverThreeUrl = process.env.SITE_3_URL || 'http://localhost:8065';
|
||||
export const siteThreeUrl = process.env.SITE_3_URL || 'http://localhost:8065';
|
||||
export const smtpUrl = process.env.SMTP_URL || 'http://127.0.0.1:9001';
|
||||
export const adminEmail = process.env.ADMIN_EMAIL || 'sysadmin@sample.mattermost.com';
|
||||
export const adminUsername = process.env.ADMIN_USERNAME || 'sysadmin';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {expect} from 'detox';
|
|||
|
||||
class ChannelScreen {
|
||||
testID = {
|
||||
archievedCloseChannelButton: 'channel.post_draft.archived.close_channel.button',
|
||||
channelScreenPrefix: 'channel.',
|
||||
channelScreen: 'channel.screen',
|
||||
channelQuickActionsButton: 'channel_header.channel_quick_actions.button',
|
||||
|
|
@ -43,6 +44,7 @@ class ChannelScreen {
|
|||
toastMessage: 'toast.message',
|
||||
};
|
||||
|
||||
archievedCloseChannelButton = element(by.id(this.testID.archievedCloseChannelButton));
|
||||
channelScreen = element(by.id(this.testID.channelScreen));
|
||||
channelQuickActionsButton = element(by.id(this.testID.channelQuickActionsButton));
|
||||
favoriteQuickAction = element(by.id(this.testID.favoriteQuickAction));
|
||||
|
|
@ -172,6 +174,7 @@ class ChannelScreen {
|
|||
postMessage = async (message: string) => {
|
||||
// # Post message
|
||||
await this.postInput.tap();
|
||||
await this.postInput.clearText();
|
||||
await this.postInput.replaceText(message);
|
||||
await this.tapSendButton();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ class ChannelInfoScreen {
|
|||
};
|
||||
|
||||
convertToPrivateChannel = async (channelDisplayName: string, {confirm = true} = {}) => {
|
||||
await this.scrollView.tap({x: 1, y: 1});
|
||||
await this.scrollView.scroll(100, 'down');
|
||||
await waitFor(this.convertPrivateOption).toExist().withTimeout(timeouts.TWO_SEC);
|
||||
await this.convertPrivateOption.tap({x: 1, y: 1});
|
||||
const {
|
||||
|
|
@ -165,6 +167,8 @@ class ChannelInfoScreen {
|
|||
};
|
||||
|
||||
leaveChannel = async ({confirm = true} = {}) => {
|
||||
await this.scrollView.tap({x: 1, y: 1});
|
||||
await this.scrollView.scroll(200, 'down');
|
||||
await waitFor(this.leaveChannelOption).toExist().withTimeout(timeouts.TWO_SEC);
|
||||
if (isAndroid()) {
|
||||
await this.scrollView.scrollTo('bottom');
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class CreateDirectMessageScreen {
|
|||
};
|
||||
|
||||
getUserItem = (userId: string) => {
|
||||
return element(by.id(`${this.testID.userItemPrefix}${userId}`));
|
||||
return element(by.id(`${this.testID.userItemPrefix}${userId}.${userId}`));
|
||||
};
|
||||
|
||||
getUserItemProfilePicture = (userId: string) => {
|
||||
|
|
@ -54,7 +54,7 @@ class CreateDirectMessageScreen {
|
|||
};
|
||||
|
||||
getUserItemDisplayName = (userId: string) => {
|
||||
return element(by.id(`${this.testID.userItemPrefix}${userId}.display_name`));
|
||||
return element(by.id(`${this.testID.userItemPrefix}${userId}.${userId}.display_name`));
|
||||
};
|
||||
|
||||
toBeVisible = async () => {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ class CreateOrEditChannelScreen {
|
|||
|
||||
openEditChannel = async () => {
|
||||
// # Open edit channel screen
|
||||
await ChannelInfoScreen.scrollView.tap({x: 1, y: 1});
|
||||
await ChannelInfoScreen.scrollView.scroll(100, 'down');
|
||||
await ChannelInfoScreen.editChannelOption.tap();
|
||||
|
||||
return this.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class InviteScreen {
|
|||
};
|
||||
|
||||
getSummaryReportUserItemText = (id: string) => {
|
||||
return element(by.id(`${this.testID.summaryReportUserItemPrefix}.${id}.username`));
|
||||
return element(by.id(`${this.testID.summaryReportUserItemPrefix}.${id}.display_name`));
|
||||
};
|
||||
|
||||
toBeVisible = async () => {
|
||||
|
|
|
|||
|
|
@ -60,9 +60,17 @@ class LoginScreen {
|
|||
|
||||
login = async (user: any = {}) => {
|
||||
await this.toBeVisible();
|
||||
await this.usernameInput.replaceText(user.username);
|
||||
await this.passwordInput.replaceText(user.password);
|
||||
await this.signinButton.tap();
|
||||
await this.usernameInput.typeText(`${user.newUser.email}\n`);
|
||||
await this.passwordInput.typeText(`${user.newUser.password}\n`);
|
||||
|
||||
await wait(timeouts.FOUR_SEC);
|
||||
};
|
||||
|
||||
loginAsAdmin = async (user: any = {}) => {
|
||||
await this.toBeVisible();
|
||||
await this.usernameInput.typeText(user.username);
|
||||
await this.passwordInput.typeText(`${user.password}\n`);
|
||||
|
||||
await wait(timeouts.ONE_SEC);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ class ServerScreen {
|
|||
displayHelp: 'server_form.display_help',
|
||||
connectButton: 'server_form.connect.button',
|
||||
connectButtonDisabled: 'server_form.connect.button.disabled',
|
||||
usernameInput: 'login_form.username.input',
|
||||
usernameInputError: 'login_form.username.input.error',
|
||||
};
|
||||
|
||||
serverScreen = element(by.id(this.testID.serverScreen));
|
||||
|
|
@ -35,6 +37,7 @@ class ServerScreen {
|
|||
displayHelp = element(by.id(this.testID.displayHelp));
|
||||
connectButton = element(by.id(this.testID.connectButton));
|
||||
connectButtonDisabled = element(by.id(this.testID.connectButtonDisabled));
|
||||
usernameInput = element(by.id(this.testID.usernameInput));
|
||||
|
||||
toBeVisible = async () => {
|
||||
await waitFor(this.serverScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
|
|
@ -53,13 +56,18 @@ class ServerScreen {
|
|||
}
|
||||
if (isIos()) {
|
||||
await this.tapConnectButton();
|
||||
|
||||
if (serverUrl.includes('127.0.0.1')) {
|
||||
// # Tap alert okay button
|
||||
await waitFor(Alert.okayButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okayButton.tap();
|
||||
if (serverUrl.includes('127.0.0.1') || !process.env.CI) {
|
||||
try {
|
||||
// # Tap alert okay button
|
||||
await waitFor(Alert.okayButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okayButton.tap();
|
||||
} catch (error) {
|
||||
/* eslint-disable no-console */
|
||||
console.log('Alert button did not appear!');
|
||||
}
|
||||
}
|
||||
}
|
||||
await waitFor(this.usernameInput).toExist().withTimeout(timeouts.ONE_SEC);
|
||||
};
|
||||
|
||||
close = async () => {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ describe('Account - Settings - About', () => {
|
|||
await expect(AboutScreen.databaseSchemaVersionTitle).toHaveText('Database Schema Version:');
|
||||
await expect(AboutScreen.databaseSchemaVersionValue).toBeVisible();
|
||||
await expect(AboutScreen.copyInfoButton).toBeVisible();
|
||||
await expect(AboutScreen.copyInfoButton).toHaveText('Copy info');
|
||||
await expect(element(by.text(new RegExp('Copy info', 'i')))).toBeVisible();
|
||||
|
||||
if (isLicensed) {
|
||||
await expect(AboutScreen.licensee).toBeVisible();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe('Channels - Convert to Private Channel', () => {
|
|||
beforeAll(async () => {
|
||||
// # Log in to server as admin
|
||||
await ServerScreen.connectToServer(siteOneUrl, siteOneDisplayName);
|
||||
await LoginScreen.login(getAdminAccount());
|
||||
await LoginScreen.loginAsAdmin(getAdminAccount());
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
|
|||
|
|
@ -71,19 +71,13 @@ describe('Channels - Find Channels', () => {
|
|||
it('MM-T4907_2 - should be able to find and navigate to a public channel', async () => {
|
||||
// # Open find channels screen and search for a public channel to navigate to
|
||||
await FindChannelsScreen.open();
|
||||
await FindChannelsScreen.searchInput.replaceText(testChannel.name);
|
||||
|
||||
// * Verify search returns the target public channel item
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await expect(FindChannelsScreen.getFilteredChannelItemDisplayName(testChannel.name)).toHaveText(testChannel.display_name);
|
||||
await FindChannelsScreen.searchInput.replaceText(testChannel.display_name);
|
||||
|
||||
// # Tap on the target public channel item
|
||||
await FindChannelsScreen.getFilteredChannelItem(testChannel.name).tap();
|
||||
|
||||
// * Verify on target public channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(testChannel.display_name);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(testChannel.display_name);
|
||||
await verifyDetailsOnChannelScreen(testChannel.display_name);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ChannelScreen.back();
|
||||
|
|
@ -124,10 +118,13 @@ describe('Channels - Find Channels', () => {
|
|||
|
||||
// * Verify search returns the target group message channel item
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await expect(FindChannelsScreen.getFilteredChannelItemDisplayName(groupMessageChannel.name)).toHaveText(`${testOtherUser1.username}, ${testOtherUser2.username}, sysadmin`);
|
||||
await FindChannelsScreen.getFilteredChannelItem(groupMessageChannel.name).tap();
|
||||
|
||||
// * Verify on target GM screen
|
||||
await verifyDetailsOnChannelScreen(`${testOtherUser1.username}, admin, ${testOtherUser2.username}`);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await FindChannelsScreen.close();
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
||||
it('MM-T4907_5 - should be able to find an archived channel', async () => {
|
||||
|
|
@ -140,10 +137,14 @@ describe('Channels - Find Channels', () => {
|
|||
|
||||
// * Verify search returns the target archived channel item
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await expect(FindChannelsScreen.getFilteredChannelItemDisplayName(archivedChannel.name)).toHaveText(archivedChannel.display_name);
|
||||
await FindChannelsScreen.getFilteredChannelItem(archivedChannel.name).tap();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await FindChannelsScreen.close();
|
||||
// * Verify on archievd channel name
|
||||
await verifyDetailsOnChannelScreen(archivedChannel.display_name);
|
||||
|
||||
// # Go back to channel list screen by closing archived channel
|
||||
await expect(ChannelScreen.archievedCloseChannelButton).toBeVisible();
|
||||
await ChannelScreen.archievedCloseChannelButton.tap();
|
||||
});
|
||||
|
||||
it('MM-T4907_6 - should be able to find a joined private channel and not find an unjoined private channel', async () => {
|
||||
|
|
@ -156,7 +157,6 @@ describe('Channels - Find Channels', () => {
|
|||
|
||||
// * Verify search returns the target joined private channel item
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await expect(FindChannelsScreen.getFilteredChannelItemDisplayName(joinedPrivateChannel.name)).toHaveText(joinedPrivateChannel.display_name);
|
||||
|
||||
// # Search for an unjoined private channel
|
||||
await FindChannelsScreen.searchInput.replaceText(unjoinedPrivateChannel.name);
|
||||
|
|
@ -169,3 +169,9 @@ describe('Channels - Find Channels', () => {
|
|||
await FindChannelsScreen.close();
|
||||
});
|
||||
});
|
||||
|
||||
async function verifyDetailsOnChannelScreen(display_name: string) {
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(display_name);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(display_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,11 @@ import {expect} from 'detox';
|
|||
|
||||
describe('Channels - Unarchive Channel', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
const channelsCategory = 'channels';
|
||||
|
||||
beforeAll(async () => {
|
||||
// # Log in to server as admin
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
await LoginScreen.login(getAdminAccount());
|
||||
await LoginScreen.loginAsAdmin(getAdminAccount());
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
@ -43,7 +42,6 @@ describe('Channels - Unarchive Channel', () => {
|
|||
it('MM-T4944_1 - should be able to unarchive a public channel and confirm', async () => {
|
||||
// # Create a public channel screen, open channel info screen, and tap on archive channel option and confirm
|
||||
const channelDisplayName = `Channel ${getRandomId()}`;
|
||||
const channelName = channelDisplayName.replace(/ /, '-').toLowerCase();
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
|
|
@ -57,8 +55,6 @@ describe('Channels - Unarchive Channel', () => {
|
|||
// # Open channel info screen, tap on unarchive channel and confirm, close and re-open app to reload, and re-open unarchived public channel
|
||||
await ChannelInfoScreen.open();
|
||||
await ChannelInfoScreen.unarchivePublicChannel({confirm: true});
|
||||
await device.reloadReactNative();
|
||||
await ChannelScreen.open(channelsCategory, channelName);
|
||||
|
||||
// * Verify on unarchived public channel screen and active post draft is displayed
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
@ -71,7 +67,6 @@ describe('Channels - Unarchive Channel', () => {
|
|||
it('MM-T4944_2 - should be able to unarchive a private channel and confirm', async () => {
|
||||
// # Create a private channel screen, open channel info screen, and tap on archive channel option and confirm
|
||||
const channelDisplayName = `Channel ${getRandomId()}`;
|
||||
const channelName = channelDisplayName.replace(/ /, '-').toLowerCase();
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.toggleMakePrivateOn();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
||||
|
|
@ -86,8 +81,6 @@ describe('Channels - Unarchive Channel', () => {
|
|||
// # Open channel info screen, tap on unarchive channel and confirm, close and re-open app to reload, and re-open unarchived private channel
|
||||
await ChannelInfoScreen.open();
|
||||
await ChannelInfoScreen.unarchivePrivateChannel({confirm: true});
|
||||
await device.reloadReactNative();
|
||||
await ChannelScreen.open(channelsCategory, channelName);
|
||||
|
||||
// * Verify on unarchived private channel screen and active post draft is displayed
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
PostOptionsScreen,
|
||||
ReactionsScreen,
|
||||
ServerScreen,
|
||||
UserProfileScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
|
@ -115,14 +116,15 @@ describe('Messaging - Emojis and Reactions', () => {
|
|||
|
||||
// * Verify user who reacted with the emoji
|
||||
await ReactionsScreen.toBeVisible();
|
||||
const {reactorItemEmojiAliases, reactorItemUserProfilePicture, reactorItemUserDisplayName, reactorItemUsername} = ReactionsScreen.getReactorItem(testUser.id, 'fire');
|
||||
const {reactorItemEmojiAliases, reactorItemUserProfilePicture, reactorItemUser} = ReactionsScreen.getReactorItem(testUser.id, 'fire');
|
||||
await expect(reactorItemEmojiAliases).toHaveText(':fire:');
|
||||
await expect(reactorItemUserProfilePicture).toBeVisible();
|
||||
await expect(reactorItemUserDisplayName).toHaveText(`${testUser.first_name} ${testUser.last_name}`);
|
||||
await expect(reactorItemUsername).toHaveText(` @${testUser.username}`);
|
||||
await expect(reactorItemUser).toBeVisible();
|
||||
await reactorItemUser.tap();
|
||||
await expect(UserProfileScreen.userDisplayName).toHaveText(`@${testUser.username}`);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ReactionsScreen.close();
|
||||
await UserProfileScreen.close();
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ describe('Messaging - Message Draft', () => {
|
|||
const message = `Message ${getRandomId()}`;
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await ChannelScreen.postInput.tap();
|
||||
await ChannelScreen.postInput.clearText();
|
||||
await ChannelScreen.postInput.replaceText(message);
|
||||
|
||||
// * Verify message draft exists in post draft
|
||||
|
|
@ -122,6 +123,7 @@ describe('Messaging - Message Draft', () => {
|
|||
let message = '1234567890'.repeat(1638) + '1234';
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await ChannelScreen.postInput.tap();
|
||||
await ChannelScreen.postInput.clearText();
|
||||
await ChannelScreen.postInput.replaceText(message);
|
||||
|
||||
// * Verify warning message is displayed and send button is disabled
|
||||
|
|
|
|||
|
|
@ -60,15 +60,15 @@ describe('Server Login - Login by Email', () => {
|
|||
|
||||
it('MM-T4677_2 - should show disabled signin button on empty username or password', async () => {
|
||||
// # Log in with empty username and non-empty password
|
||||
await usernameInput.replaceText('');
|
||||
await passwordInput.replaceText('password');
|
||||
await usernameInput.clearText();
|
||||
await passwordInput.typeText('password');
|
||||
|
||||
// * Verify signin button is disabled
|
||||
await expect(signinButtonDisabled).toBeVisible();
|
||||
|
||||
// # Log in with non-empty username and empty password
|
||||
await usernameInput.replaceText('username');
|
||||
await passwordInput.replaceText('');
|
||||
await usernameInput.typeText('username');
|
||||
await passwordInput.clearText();
|
||||
|
||||
// * Verify signin button is disabled
|
||||
await expect(signinButtonDisabled).toBeVisible();
|
||||
|
|
@ -76,8 +76,8 @@ describe('Server Login - Login by Email', () => {
|
|||
|
||||
it('MM-T4677_3 - should show incorrect combination error on incorrect credentials', async () => {
|
||||
// # Log in with incorrect credentials
|
||||
await usernameInput.replaceText('username');
|
||||
await passwordInput.replaceText('password');
|
||||
await usernameInput.typeText('username');
|
||||
await passwordInput.typeText('password');
|
||||
await signinButton.tap();
|
||||
|
||||
// * Verify incorrect combination error
|
||||
|
|
@ -87,8 +87,8 @@ describe('Server Login - Login by Email', () => {
|
|||
it('MM-T4677_4 - should show channel list screen on successful login', async () => {
|
||||
// # Log in to server with correct credentials
|
||||
const {team, user} = await Setup.apiInit(siteOneUrl);
|
||||
await usernameInput.replaceText(user.username);
|
||||
await passwordInput.replaceText(user.password);
|
||||
await usernameInput.typeText(user.newUser.username);
|
||||
await passwordInput.typeText(user.newUser.password);
|
||||
await signinButton.tap();
|
||||
|
||||
// * Verify on channel list screen and channel list header shows team display name and server display name
|
||||
|
|
|
|||
|
|
@ -202,9 +202,6 @@ describe('Teams - Invite', () => {
|
|||
it('MM-T5365 - should handle both sent and not sent invites', async () => {
|
||||
const {user: testUser2} = await User.apiCreateUser(siteOneUrl, {prefix: 'i'});
|
||||
|
||||
const username1 = ` @${testUser1.username}`;
|
||||
const username2 = ` @${testUser2.username}`;
|
||||
|
||||
// # Search for an existent user
|
||||
await Invite.searchBarInput.replaceText(testUser2.username);
|
||||
|
||||
|
|
@ -218,31 +215,31 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.getSelectedItem(testUser2.id)).toBeVisible();
|
||||
|
||||
// # Search for a existent user already in team
|
||||
await Invite.searchBarInput.replaceText(testUser1.username);
|
||||
await Invite.searchBarInput.replaceText(testUser.username);
|
||||
|
||||
// # Wait for user item in search list
|
||||
await waitFor(Invite.getSearchListUserItem(testUser1.id)).toExist().withTimeout(timeouts.TWO_SEC);
|
||||
await waitFor(Invite.getSearchListUserItem(testUser.id)).toExist().withTimeout(timeouts.TWO_SEC);
|
||||
|
||||
// # Select user item
|
||||
await Invite.getSearchListUserItem(testUser1.id).tap();
|
||||
await Invite.getSearchListUserItem(testUser.id).tap();
|
||||
|
||||
// * Validate user is added to selected items
|
||||
await expect(Invite.getSelectedItem(testUser1.id)).toBeVisible();
|
||||
await expect(Invite.getSelectedItem(testUser.id)).toBeVisible();
|
||||
|
||||
// # Send invitation
|
||||
await Invite.sendButton.tap();
|
||||
|
||||
// * Validate summary
|
||||
await waitFor(Invite.screenSummary).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
waitFor(Invite.screenSummary).toBeVisible();
|
||||
|
||||
// * Validate summary report not sent
|
||||
await expect(Invite.getSummaryReportNotSent()).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItem(testUser1.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser1.id)).toHaveText(username1);
|
||||
await expect(Invite.getSummaryReportUserItem(testUser.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser.id)).toBeVisible(testUser.username1);
|
||||
|
||||
// * Validate summary report sent
|
||||
await waitFor(Invite.getSummaryReportSent()).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
waitFor(Invite.getSummaryReportSent()).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItem(testUser2.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser2.id)).toHaveText(username2);
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser2.id)).toBeVisible(testUser2.username1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
10324
detox/package-lock.json
generated
10324
detox/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -14,13 +14,14 @@
|
|||
"@types/jest": "29.5.5",
|
||||
"@types/tough-cookie": "4.0.3",
|
||||
"@types/uuid": "9.0.4",
|
||||
"async": "3.2.5",
|
||||
"axios": "1.5.0",
|
||||
"axios-cookiejar-support": "4.0.7",
|
||||
"babel-jest": "29.7.0",
|
||||
"babel-plugin-module-resolver": "5.0.0",
|
||||
"client-oauth2": "4.3.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"detox": "20.11.4",
|
||||
"detox": "20.19.3",
|
||||
"form-data": "4.0.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-circus": "29.7.0",
|
||||
|
|
@ -46,11 +47,14 @@
|
|||
"e2e:android-test": "detox test -c android.emu.debug",
|
||||
"e2e:android-build-release": "detox build -c android.emu.release",
|
||||
"e2e:android-test-release": "detox test -c android.emu.release --record-logs failing --take-screenshots failing",
|
||||
"e2e:ios-build": "IOS=true detox build -c ios.sim.debug",
|
||||
"e2e:ios-test": "IOS=true detox test -c ios.sim.debug",
|
||||
"e2e:ios-build-release": "detox build -c ios.sim.release",
|
||||
"e2e:ios-test-release": "IOS=true detox test -c ios.sim.release --record-logs failing --take-screenshots failing",
|
||||
"check": "npm run lint && npm run tsc",
|
||||
"clean-detox": "detox clean",
|
||||
"lint": "eslint --ignore-pattern node_modules --quiet .",
|
||||
"e2e:save-report": "node save_report.js",
|
||||
"start:webhook": "node webhook_server.js",
|
||||
"tsc": "NODE_OPTIONS=--max_old_space_size=12000 tsc --noEmit"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
* - TYPE=[type], e.g. "MASTER", "PR", "RELEASE", "GEKIDOU"
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
|
|
@ -74,17 +75,19 @@ const saveReport = async () => {
|
|||
removeOldGeneratedReports();
|
||||
|
||||
const detox_version = shell.exec('npm list detox').stdout.split('\n')[1].split('@')[1].trim();
|
||||
const headless = IOS === 'true' ? false : HEADLESS === 'true';
|
||||
const headless = IOS === 'true' ? 'false' : HEADLESS === 'true';
|
||||
const os_name = os.platform();
|
||||
const os_version = os.release();
|
||||
const node_version = process.version;
|
||||
const npm_version = shell.exec('npm --version').stdout.trim();
|
||||
const device_name = DEVICE_NAME;
|
||||
const device_os_version = DEVICE_OS_VERSION;
|
||||
|
||||
// Write environment details to file
|
||||
const environmentDetails = {
|
||||
detox_version,
|
||||
device_name: DEVICE_NAME,
|
||||
device_os_version: DEVICE_OS_VERSION,
|
||||
device_name,
|
||||
device_os_version,
|
||||
headless,
|
||||
os_name,
|
||||
os_version,
|
||||
|
|
@ -96,7 +99,7 @@ const saveReport = async () => {
|
|||
// Merge all XML reports into one single XML report
|
||||
const platform = process.env.IOS === 'true' ? 'ios' : 'android';
|
||||
const combinedFilePath = `${ARTIFACTS_DIR}/${platform}-combined.xml`;
|
||||
await mergeFiles(path.join(__dirname, combinedFilePath), [`${ARTIFACTS_DIR}/${platform}-junit*.xml`]);
|
||||
await mergeFiles(path.join(__dirname, combinedFilePath), [`${ARTIFACTS_DIR}/ios-results*/${platform}-junit*.xml`]);
|
||||
console.log(`Merged, check ${combinedFilePath}`);
|
||||
|
||||
// Read XML from a file
|
||||
|
|
@ -112,9 +115,23 @@ const saveReport = async () => {
|
|||
// Generate jest-stare report
|
||||
const jestStareOutputDir = path.join(__dirname, `${ARTIFACTS_DIR}/jest-stare`);
|
||||
const jestStareCombinedFilePath = `${jestStareOutputDir}/${platform}-combined.json`;
|
||||
await mergeJestStareJsonFiles(jestStareCombinedFilePath, [`${ARTIFACTS_DIR}/jest-stare/${platform}-data*.json`]);
|
||||
if (!fs.existsSync(jestStareOutputDir)) {
|
||||
fs.mkdirSync(jestStareOutputDir, {recursive: true});
|
||||
}
|
||||
|
||||
// 'ios-results-*' is the path in CI where the parallel detox jobs save artifacts
|
||||
await mergeJestStareJsonFiles(jestStareCombinedFilePath, [`${ARTIFACTS_DIR}/ios-results*/jest-stare/${platform}-data*.json`]);
|
||||
generateJestStareHtmlReport(jestStareOutputDir, `${platform}-report.html`, jestStareCombinedFilePath);
|
||||
|
||||
if (process.env.CI) {
|
||||
// Delete folders starting with "iOS-results-" only in CI environment
|
||||
const entries = fs.readdirSync(ARTIFACTS_DIR, {withFileTypes: true});
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name.startsWith('ios-results-')) {
|
||||
fs.rmSync(path.join(ARTIFACTS_DIR, entry.name), {recursive: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await saveArtifacts();
|
||||
if (result && result.success) {
|
||||
console.log('Successfully uploaded artifacts to S3:', result.reportLink);
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ function generateTestReport(summary, isUploadedToS3, reportLink, environment, te
|
|||
function generateTitle() {
|
||||
const {
|
||||
BRANCH,
|
||||
BUILD_AWS_S3_BUCKET,
|
||||
DETOX_AWS_S3_BUCKET,
|
||||
BUILD_ID,
|
||||
COMMIT_HASH,
|
||||
IOS,
|
||||
|
|
@ -332,7 +332,7 @@ function generateTitle() {
|
|||
|
||||
switch (TYPE) {
|
||||
case 'PR':
|
||||
buildLink = ` with [${lane}:${COMMIT_HASH}](https://${BUILD_AWS_S3_BUCKET}.s3.amazonaws.com/${s3Folder}/${appFilePath})`;
|
||||
buildLink = ` with [${lane}:${COMMIT_HASH}](https://${DETOX_AWS_S3_BUCKET}.s3.amazonaws.com/${s3Folder}/${appFilePath})`;
|
||||
title = `${platform} E2E for Pull Request Build: [${BRANCH}](${PULL_REQUEST})${buildLink}`;
|
||||
break;
|
||||
case 'RELEASE':
|
||||
|
|
|
|||
Loading…
Reference in a new issue