Add ability to run detox tests on Android (#8553)
This commit is contained in:
parent
985fa692a6
commit
9976cacec4
46 changed files with 907 additions and 394 deletions
18
.github/actions/generate-specs/split-tests.js
vendored
18
.github/actions/generate-specs/split-tests.js
vendored
|
|
@ -52,20 +52,22 @@ class Specs {
|
|||
}
|
||||
|
||||
generateSplits() {
|
||||
const chunkSize = Math.ceil(this.rawFiles.length / this.parallelism);
|
||||
const chunkSize = Math.floor(this.rawFiles.length / this.parallelism);
|
||||
let remainder = this.rawFiles.length % this.parallelism;
|
||||
let runNo = 1;
|
||||
let start = 0;
|
||||
|
||||
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(' ');
|
||||
for (let i = 0; i < this.parallelism; i++) {
|
||||
let end = start + chunkSize + (remainder > 0 ? 1 : 0);
|
||||
const fileGroup = this.rawFiles.slice(start, end).join(' ');
|
||||
const specFileGroup = new SpecGroup(runNo.toString(), fileGroup, this.deviceInfo);
|
||||
this.groupedFiles.push(specFileGroup);
|
||||
|
||||
if (end === this.rawFiles.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
start = end;
|
||||
runNo++;
|
||||
if (remainder > 0) {
|
||||
remainder--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
73
.github/workflows/e2e-android-detox.yml
vendored
73
.github/workflows/e2e-android-detox.yml
vendored
|
|
@ -1,73 +0,0 @@
|
|||
name: Detox E2E Android Tests PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
types:
|
||||
- labeled
|
||||
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.number }}-${{ github.event.label.name }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-android-apk:
|
||||
if: github.event.label.name == 'E2E Android tests for PR'
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_jvmargs: -Xmx8g
|
||||
steps:
|
||||
- name: Prune Docker to free up space
|
||||
run: docker system prune -af
|
||||
|
||||
- name: Remove npm Temporary Files
|
||||
run: |
|
||||
rm -rf ~/.npm/_cacache
|
||||
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: ci/prepare-android-build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
env:
|
||||
STORE_FILE: "${{ secrets.MM_MOBILE_STORE_FILE }}"
|
||||
STORE_ALIAS: "${{ secrets.MM_MOBILE_STORE_ALIAS }}"
|
||||
STORE_PASSWORD: "${{ secrets.MM_MOBILE_STORE_PASSWORD }}"
|
||||
MATTERMOST_BUILD_GH_TOKEN: "${{ secrets.MATTERMOST_BUILD_GH_TOKEN }}"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y default-jdk
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/caches/modules-2/
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/actions/wrapper-validation@v3
|
||||
|
||||
- name: Inject Detox settings
|
||||
run: cd detox && npm run e2e:android-inject-settings
|
||||
|
||||
- name: Update minSdkVersion for react-native-image-picker
|
||||
run: |
|
||||
sed -i 's/minSdkVersion 21/minSdkVersion 23/' ./node_modules/react-native-image-picker/android/build.gradle
|
||||
cat ./node_modules/react-native-image-picker/android/build.gradle | grep minSdkVersion
|
||||
|
||||
- name: Detox build
|
||||
run: |
|
||||
cd detox
|
||||
npm install
|
||||
npm install -g detox-cli
|
||||
npm run e2e:android-build
|
||||
|
||||
- name: ci/upload-android-pr-build
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-apk-${{ github.run_id }}
|
||||
path: "android/app/build/outputs/apk/**/app-*.apk"
|
||||
295
.github/workflows/e2e-android-template.yml
vendored
Normal file
295
.github/workflows/e2e-android-template.yml
vendored
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
name: Detox Android E2E Tests Template
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
MM_TEST_SERVER_URL:
|
||||
description: "The test server URL"
|
||||
required: false
|
||||
type: string
|
||||
MM_TEST_USER_NAME:
|
||||
description: "The admin username of the test instance"
|
||||
required: false
|
||||
type: string
|
||||
MM_TEST_PASSWORD:
|
||||
description: "The admin password of the test instance"
|
||||
required: false
|
||||
type: string
|
||||
MOBILE_VERSION:
|
||||
description: "The mobile version to test"
|
||||
required: false
|
||||
default: ${{ github.head_ref || github.ref }}
|
||||
type: string
|
||||
run-android-tests:
|
||||
description: "Run Android tests"
|
||||
required: true
|
||||
type: boolean
|
||||
run-type:
|
||||
type: string
|
||||
required: false
|
||||
default: "PR"
|
||||
testcase_failure_fatal:
|
||||
description: "Should failures be considered fatal"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
record_tests_in_zephyr:
|
||||
description: "Record test results in Zephyr, typically for nightly and release runs"
|
||||
required: false
|
||||
type: string
|
||||
default: 'false'
|
||||
low_bandwidth_mode:
|
||||
description: "Enable low bandwidth mode"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
android_avd_name:
|
||||
description: "Android Emulator name"
|
||||
required: false
|
||||
type: string
|
||||
default: "detox_pixel_4_xl"
|
||||
android_api_level:
|
||||
description: "Android API level"
|
||||
required: false
|
||||
type: string
|
||||
default: "33"
|
||||
outputs:
|
||||
STATUS:
|
||||
value: ${{ jobs.generate-report.outputs.STATUS }}
|
||||
TARGET_URL:
|
||||
value: ${{ jobs.generate-report.outputs.TARGET_URL }}
|
||||
FAILURES:
|
||||
value: ${{ jobs.generate-report.outputs.FAILURES }}
|
||||
|
||||
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.event_name == 'pull_request' && github.head_ref || github.ref_name }}
|
||||
COMMIT_HASH: ${{ github.sha }}
|
||||
DEVICE_NAME: ${{ inputs.android_avd_name }} # This is needed to split tests as same code is used in iOS job
|
||||
DEVICE_OS_VERSION: ${{ inputs.android_api_level }}
|
||||
DETOX_AWS_S3_BUCKET: "mattermost-detox-report"
|
||||
HEADLESS: "true"
|
||||
TYPE: ${{ inputs.run-type }}
|
||||
PULL_REQUEST: "https://github.com/mattermost/mattermost-mobile/pull/${{ github.event.number }}"
|
||||
SITE_1_URL: ${{ inputs.MM_TEST_SERVER_URL || 'https://mobile-e2e-site-1.test.mattermost.cloud' }}
|
||||
SITE_2_URL: "https://mobile-e2e-site-2.test.mattermost.cloud"
|
||||
SITE_3_URL: "https://mobile-e2e-site-3.test.mattermost.cloud"
|
||||
ZEPHYR_ENABLE: ${{ inputs.record_tests_in_zephyr }}
|
||||
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"
|
||||
IOS: "false"
|
||||
RUNNING_E2E: "true"
|
||||
AVD_NAME: ${{ inputs.android_avd_name }}
|
||||
SDK_VERSION: ${{ inputs.android_api_level }}
|
||||
|
||||
jobs:
|
||||
generate-specs:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
specs: ${{ steps.generate-specs.outputs.specs }}
|
||||
build_id: ${{ steps.resolve-device.outputs.BUILD_ID }}
|
||||
mobile_sha: ${{ steps.resolve-device.outputs.MOBILE_SHA }}
|
||||
mobile_ref: ${{ steps.resolve-device.outputs.MOBILE_REF }}
|
||||
workflow_hash: ${{ steps.resolve-device.outputs.WORKFLOW_HASH }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ inputs.MOBILE_VERSION }}
|
||||
|
||||
- name: Set Build ID
|
||||
id: resolve-device
|
||||
run: |
|
||||
BUILD_ID="${{ github.run_id }}-${{ env.AVD_NAME }}-${{ env.SDK_VERSION}}"
|
||||
WORKFLOW_HASH=$(tr -dc a-z0-9 </dev/urandom | head -c 10)
|
||||
|
||||
## We need that hash to separate the artifacts
|
||||
echo "WORKFLOW_HASH=${WORKFLOW_HASH}" >> ${GITHUB_OUTPUT}
|
||||
|
||||
echo "BUILD_ID=$(echo ${BUILD_ID} | sed 's/ /_/g')" >> ${GITHUB_OUTPUT}
|
||||
echo "MOBILE_SHA=$(git rev-parse HEAD)" >> ${GITHUB_OUTPUT}
|
||||
echo "MOBILE_REF=$(git rev-parse --abbrev-ref HEAD)" >> ${GITHUB_OUTPUT}
|
||||
|
||||
- name: Generate Test Specs
|
||||
id: generate-specs
|
||||
uses: ./.github/actions/generate-specs
|
||||
with:
|
||||
parallelism: 10
|
||||
search_path: detox/e2e/test
|
||||
device_name: ${{ env.AVD_NAME }}
|
||||
device_os_version: ${{ env.SDK_VERSION }}
|
||||
|
||||
e2e-android:
|
||||
name: android-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
continue-on-error: true
|
||||
timeout-minutes: 150
|
||||
env:
|
||||
ANDROID_HOME: /usr/local/lib/android/sdk
|
||||
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
|
||||
needs:
|
||||
- generate-specs
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.generate-specs.outputs.specs) }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ inputs.MOBILE_VERSION }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libpulse0
|
||||
sudo apt-get install -y scrot ffmpeg xvfb
|
||||
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Prepare Android Build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
env:
|
||||
STORE_FILE: "${{ secrets.MM_MOBILE_STORE_FILE }}"
|
||||
STORE_ALIAS: "${{ secrets.MM_MOBILE_STORE_ALIAS }}"
|
||||
STORE_PASSWORD: "${{ secrets.MM_MOBILE_STORE_PASSWORD }}"
|
||||
MATTERMOST_BUILD_GH_TOKEN: "${{ secrets.MATTERMOST_BUILD_GH_TOKEN }}"
|
||||
|
||||
- name: Install Detox Dependencies
|
||||
run: |
|
||||
cd detox
|
||||
npm install
|
||||
|
||||
- name: Create destination path
|
||||
run: mkdir -p android/app/build
|
||||
|
||||
- name: Download APK artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: android-build-files-${{ github.run_id }}
|
||||
path: android/app/build
|
||||
|
||||
- name: Set up Android SDK
|
||||
run: |
|
||||
export ANDROID_HOME=/usr/local/lib/android/sdk
|
||||
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools:$PATH
|
||||
echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV
|
||||
echo "PATH=$PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Start Xvfb
|
||||
run: |
|
||||
Xvfb :99 -screen 0 1920x1080x24 &
|
||||
export DISPLAY=:99
|
||||
echo "DISPLAY=:99" >> $GITHUB_ENV
|
||||
|
||||
- name: Accept Android licenses
|
||||
run: |
|
||||
yes | sdkmanager --licenses || true
|
||||
|
||||
- name: Install Android system image
|
||||
run: |
|
||||
sdkmanager "system-images;android-34;default;x86_64"
|
||||
sdkmanager "platform-tools" "emulator"
|
||||
|
||||
- name: Create and run Android Emulator
|
||||
run: |
|
||||
cd detox
|
||||
chmod +x ./create_android_emulator.sh
|
||||
./create_android_emulator.sh ${{ env.SDK_VERSION }} ${{ env.AVD_NAME }} ${{ matrix.specs }}
|
||||
continue-on-error: true # We want to run all the tests
|
||||
|
||||
- name: Upload Android Test Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-results-${{ needs.generate-specs.outputs.workflow_hash }}-${{ matrix.runId }}
|
||||
path: detox/artifacts/
|
||||
|
||||
generate-report:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- generate-specs
|
||||
- e2e-android
|
||||
outputs:
|
||||
TARGET_URL: ${{ steps.set-url.outputs.TARGET_URL }}
|
||||
STATUS: ${{ steps.determine-status.outputs.STATUS }}
|
||||
FAILURES: ${{ steps.summary.outputs.FAILURES }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ inputs.MOBILE_VERSION }}
|
||||
|
||||
- name: ci/prepare-node-deps
|
||||
uses: ./.github/actions/prepare-node-deps
|
||||
|
||||
- name: Download Android Artifacts
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
path: detox/artifacts/
|
||||
pattern: android-results-${{ needs.generate-specs.outputs.workflow_hash }}-*
|
||||
continue-on-error: true
|
||||
|
||||
- name: Generate Report Path
|
||||
id: s3
|
||||
run: |
|
||||
path="${{ needs.generate-specs.outputs.build_id }}-${{ needs.generate-specs.outputs.mobile_sha }}-${{ needs.generate-specs.outputs.mobile_ref }}"
|
||||
echo "path=$(echo "${path}" | sed 's/\./-/g')" >> ${GITHUB_OUTPUT}
|
||||
|
||||
- name: Save report Detox Dependencies
|
||||
id: report-link
|
||||
run: |
|
||||
cd detox
|
||||
npm ci
|
||||
npm run e2e:save-report
|
||||
env:
|
||||
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 }}
|
||||
BUILD_ID: ${{ needs.generate-specs.outputs.build_id }}
|
||||
REPORT_PATH: ${{ steps.s3.outputs.path }}
|
||||
## These are needed for the MM Webhook report
|
||||
COMMIT_HASH: ${{ needs.generate-specs.outputs.mobile_sha }}
|
||||
BRANCH: ${{ needs.generate-specs.outputs.mobile_ref }}
|
||||
|
||||
- name: Calculate failures
|
||||
id: summary
|
||||
run: |
|
||||
echo "FAILURES=$(cat detox/artifacts/summary.json | jq .stats.failures)" >> ${GITHUB_OUTPUT}
|
||||
echo "PASSES=$(cat detox/artifacts/summary.json | jq .stats.passes)" >> ${GITHUB_OUTPUT}
|
||||
echo "SKIPPED=$(cat detox/artifacts/summary.json | jq .stats.skipped)" >> ${GITHUB_OUTPUT}
|
||||
echo "TOTAL=$(cat detox/artifacts/summary.json | jq .stats.tests)" >> ${GITHUB_OUTPUT}
|
||||
echo "ERRORS=$(cat detox/artifacts/summary.json | jq .stats.errors)" >> ${GITHUB_OUTPUT}
|
||||
echo "PERCENTAGE=$(cat detox/artifacts/summary.json | jq .stats.passPercent)" >> ${GITHUB_OUTPUT}
|
||||
|
||||
- name: Set Target URL
|
||||
id: set-url
|
||||
run: |
|
||||
echo "TARGET_URL=https://${{ env.DETOX_AWS_S3_BUCKET }}.s3.amazonaws.com/${{ steps.s3.outputs.path }}/jest-stare/android-report.html" >> ${GITHUB_OUTPUT}
|
||||
|
||||
- name: Determine Status
|
||||
id: determine-status
|
||||
run: |
|
||||
if [[ ${{ steps.summary.outputs.failures }} -gt 0 && "${{ inputs.testcase_failure_fatal }}" == "true" ]]; then
|
||||
echo "STATUS=failure" >> ${GITHUB_OUTPUT}
|
||||
else
|
||||
echo "STATUS=success" >> ${GITHUB_OUTPUT}
|
||||
fi
|
||||
|
||||
- name: Generate Summary
|
||||
run: |
|
||||
echo "| Tests | Passed :white_check_mark: | Failed :x: | Skipped :fast_forward: | Errors :warning: | " >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "|:---:|:---:|:---:|:---:|:---:|" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "| ${{ steps.summary.outputs.TOTAL }} | ${{ steps.summary.outputs.PASSES }} | ${{ steps.summary.outputs.FAILURES }} | ${{ steps.summary.outputs.SKIPPED }} | ${{ steps.summary.outputs.ERRORS }} |" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "You can check the full report [here](${{ steps.set-url.outputs.TARGET_URL }})" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "There was **${{ steps.summary.outputs.PERCENTAGE }}%** success rate." >> ${GITHUB_STEP_SUMMARY}
|
||||
131
.github/workflows/e2e-detox-pr.yml
vendored
131
.github/workflows/e2e-detox-pr.yml
vendored
|
|
@ -14,9 +14,24 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-initial-status:
|
||||
runs-on: ubuntu-22.04
|
||||
update-initial-status-ios:
|
||||
if: contains(github.event.label.name, 'E2E iOS tests for PR')
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
if: contains(github.event.label.name, 'E2E iOS tests for PR')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.event.pull_request.head.sha }}
|
||||
context: e2e/detox-ios-tests
|
||||
description: Detox iOS tests for mattermost mobile app have started ...
|
||||
status: pending
|
||||
|
||||
update-initial-status-android:
|
||||
runs-on: ubuntu-22.04
|
||||
if: contains(github.event.label.name, 'E2E Android tests for PR')
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
env:
|
||||
|
|
@ -24,14 +39,15 @@ jobs:
|
|||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.event.pull_request.head.sha }}
|
||||
context: e2e/detox-tests
|
||||
description: Detox tests for mattermost mobile app have started ...
|
||||
context: e2e/detox-android-tests
|
||||
description: Detox Android tests for mattermost mobile app have started ...
|
||||
status: pending
|
||||
|
||||
build-ios-simulator:
|
||||
if: contains(github.event.label.name, 'E2E iOS tests for PR')
|
||||
runs-on: macos-14
|
||||
needs:
|
||||
- update-initial-status
|
||||
- update-initial-status-ios
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
|
@ -54,20 +70,75 @@ jobs:
|
|||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: Mattermost-simulator-x86_64.app.zip
|
||||
|
||||
build-android-apk:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
if: contains(github.event.label.name, 'E2E Android tests for PR')
|
||||
needs:
|
||||
- update-initial-status-android
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_jvmargs: -Xmx8g
|
||||
steps:
|
||||
- name: Prune Docker to free up space
|
||||
run: docker system prune -af
|
||||
|
||||
- name: Remove npm Temporary Files
|
||||
run: |
|
||||
rm -rf ~/.npm/_cacache
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: Prepare Android Build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
env:
|
||||
STORE_FILE: "${{ secrets.MM_MOBILE_STORE_FILE }}"
|
||||
STORE_ALIAS: "${{ secrets.MM_MOBILE_STORE_ALIAS }}"
|
||||
STORE_PASSWORD: "${{ secrets.MM_MOBILE_STORE_PASSWORD }}"
|
||||
MATTERMOST_BUILD_GH_TOKEN: "${{ secrets.MATTERMOST_BUILD_GH_TOKEN }}"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y default-jdk
|
||||
|
||||
- name: Detox build
|
||||
run: |
|
||||
cd detox
|
||||
npm install
|
||||
npm install -g detox-cli
|
||||
npm run e2e:android-build
|
||||
|
||||
- name: Upload Android Build
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-files-${{ github.run_id }}
|
||||
path: "android/app/build/**/*"
|
||||
|
||||
run-ios-tests-on-pr:
|
||||
if: contains(github.event.label.name, 'E2E iOS tests for PR')
|
||||
name: iOS Mobile Tests on PR
|
||||
uses: ./.github/workflows/e2e-detox-template.yml
|
||||
uses: ./.github/workflows/e2e-ios-template.yml
|
||||
needs:
|
||||
- build-ios-simulator
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-type: "PR"
|
||||
MOBILE_VERSION: ${{ github.event.pull_request.head.sha }}
|
||||
low_bandwidth_mode: ${{ contains(github.event.label.name,'LBW') && true || false }}
|
||||
secrets: inherit
|
||||
|
||||
update-final-status:
|
||||
run-android-tests-on-pr:
|
||||
if: contains(github.event.label.name, 'E2E Android tests for PR')
|
||||
name: Android Mobile Tests on PR
|
||||
uses: ./.github/workflows/e2e-android-template.yml
|
||||
needs:
|
||||
- build-android-apk
|
||||
with:
|
||||
run-android-tests: true
|
||||
run-type: "PR"
|
||||
MOBILE_VERSION: ${{ github.event.pull_request.head.sha }}
|
||||
secrets: inherit
|
||||
|
||||
update-final-status-ios:
|
||||
runs-on: ubuntu-22.04
|
||||
if: contains(github.event.label.name, 'E2E iOS tests for PR')
|
||||
needs:
|
||||
- run-ios-tests-on-pr
|
||||
steps:
|
||||
|
|
@ -77,12 +148,29 @@ jobs:
|
|||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.event.pull_request.head.sha }}
|
||||
context: e2e/detox-tests
|
||||
context: e2e/detox-ios-tests
|
||||
description: Completed with ${{ needs.run-ios-tests-on-pr.outputs.FAILURES }} failures
|
||||
status: ${{ needs.run-ios-tests-on-pr.outputs.STATUS }}
|
||||
target_url: ${{ needs.run-ios-tests-on-pr.outputs.TARGET_URL }}
|
||||
|
||||
e2e-remove-label:
|
||||
update-final-status-android:
|
||||
runs-on: ubuntu-22.04
|
||||
if: contains(github.event.label.name, 'E2E Android tests for PR')
|
||||
needs:
|
||||
- run-android-tests-on-pr
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.event.pull_request.head.sha }}
|
||||
context: e2e/detox-android-tests
|
||||
description: Completed with ${{ needs.run-android-tests-on-pr.outputs.FAILURES }} failures
|
||||
status: ${{ needs.run-android-tests-on-pr.outputs.STATUS }}
|
||||
target_url: ${{ needs.run-android-tests-on-pr.outputs.TARGET_URL }}
|
||||
|
||||
e2e-remove-ios-label:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- run-ios-tests-on-pr
|
||||
|
|
@ -93,7 +181,6 @@ jobs:
|
|||
with:
|
||||
script: |
|
||||
const iosLabel = 'E2E iOS tests for PR';
|
||||
const androidLabel = 'E2E Android tests for PR';
|
||||
context.payload.pull_request.labels.forEach(label => {
|
||||
if (label.name.includes(iosLabel)) {
|
||||
github.rest.issues.removeLabel({
|
||||
|
|
@ -104,3 +191,25 @@ jobs:
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
e2e-remove-android-label:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- run-android-tests-on-pr
|
||||
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 androidLabel = 'E2E Android tests for PR';
|
||||
context.payload.pull_request.labels.forEach(label => {
|
||||
if (label.name.includes(androidLabel)) {
|
||||
github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: label.name,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
106
.github/workflows/e2e-detox-release.yml
vendored
106
.github/workflows/e2e-detox-release.yml
vendored
|
|
@ -6,7 +6,7 @@ on:
|
|||
- release-*
|
||||
|
||||
jobs:
|
||||
update-initial-status:
|
||||
update-initial-status-ios:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
|
|
@ -15,14 +15,27 @@ jobs:
|
|||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
context: e2e/detox-tests
|
||||
description: Detox tests for mattermost mobile app have started ...
|
||||
context: e2e/detox-ios-tests
|
||||
description: Detox iOS tests for mattermost mobile app have started ...
|
||||
status: pending
|
||||
|
||||
update-initial-status-android:
|
||||
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: ${{ github.sha }}
|
||||
context: e2e/detox-android-tests
|
||||
description: Detox Android tests for mattermost mobile app have started ...
|
||||
status: pending
|
||||
|
||||
build-ios-simulator:
|
||||
runs-on: macos-14
|
||||
needs:
|
||||
- update-initial-status
|
||||
- update-initial-status-ios
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
|
@ -45,19 +58,80 @@ jobs:
|
|||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: Mattermost-simulator-x86_64.app.zip
|
||||
|
||||
build-android-apk:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
needs:
|
||||
- update-initial-status-android
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_jvmargs: -Xmx8g
|
||||
steps:
|
||||
- name: Prune Docker to free up space
|
||||
run: docker system prune -af
|
||||
|
||||
- name: Remove npm Temporary Files
|
||||
run: |
|
||||
rm -rf ~/.npm/_cacache
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ inputs.MOBILE_VERSION }}
|
||||
|
||||
- name: Prepare Android Build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
env:
|
||||
STORE_FILE: "${{ secrets.MM_MOBILE_STORE_FILE }}"
|
||||
STORE_ALIAS: "${{ secrets.MM_MOBILE_STORE_ALIAS }}"
|
||||
STORE_PASSWORD: "${{ secrets.MM_MOBILE_STORE_PASSWORD }}"
|
||||
MATTERMOST_BUILD_GH_TOKEN: "${{ secrets.MATTERMOST_BUILD_GH_TOKEN }}"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y default-jdk
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/caches/modules-2/
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
- name: Detox build
|
||||
run: |
|
||||
cd detox
|
||||
npm install
|
||||
npm install -g detox-cli
|
||||
npm run e2e:android-build
|
||||
|
||||
- name: Upload Android Build
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-files-${{ github.run_id }}
|
||||
path: "android/app/build/**/*"
|
||||
|
||||
run-ios-tests-on-release:
|
||||
name: iOS Mobile Tests on Release
|
||||
uses: ./.github/workflows/e2e-detox-template.yml
|
||||
uses: ./.github/workflows/e2e-ios-template.yml
|
||||
needs:
|
||||
- build-ios-simulator
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-type: "RELEASE"
|
||||
record_tests_in_zephyr: 'true'
|
||||
MOBILE_VERSION: ${{ github.ref }}
|
||||
secrets: inherit
|
||||
|
||||
update-final-status:
|
||||
run-android-tests-on-release:
|
||||
name: Android Mobile Tests on Release
|
||||
uses: ./.github/workflows/e2e-android-template.yml
|
||||
needs:
|
||||
- build-android-apk
|
||||
with:
|
||||
run-android-tests: true
|
||||
run-type: "RELEASE"
|
||||
record_tests_in_zephyr: 'true'
|
||||
MOBILE_VERSION: ${{ github.ref }}
|
||||
secrets: inherit
|
||||
|
||||
update-final-status-ios:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- run-ios-tests-on-release
|
||||
|
|
@ -68,7 +142,23 @@ jobs:
|
|||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
context: e2e/detox-tests
|
||||
context: e2e/detox-ios-tests
|
||||
description: Completed with ${{ needs.run-ios-tests-on-release.outputs.FAILURES }} failures
|
||||
status: ${{ needs.run-ios-tests-on-release.outputs.STATUS }}
|
||||
target_url: ${{ needs.run-ios-tests-on-release.outputs.TARGET_URL }}
|
||||
|
||||
update-final-status-android:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- run-android-tests-on-release
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
context: e2e/detox-android-tests
|
||||
description: Completed with ${{ needs.run-android-tests-on-release.outputs.FAILURES }} failures
|
||||
status: ${{ needs.run-android-tests-on-release.outputs.STATUS }}
|
||||
target_url: ${{ needs.run-android-tests-on-release.outputs.TARGET_URL }}
|
||||
|
|
|
|||
106
.github/workflows/e2e-detox-scheduled.yml
vendored
106
.github/workflows/e2e-detox-scheduled.yml
vendored
|
|
@ -5,7 +5,7 @@ on:
|
|||
- cron: "0 0 * * 4,5" # Wednesday and Thursday midnight
|
||||
|
||||
jobs:
|
||||
update-initial-status:
|
||||
update-initial-status-ios:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
|
|
@ -14,14 +14,27 @@ jobs:
|
|||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
context: e2e/detox-tests
|
||||
description: Detox tests for mattermost mobile app have started ...
|
||||
context: e2e/detox-ios-tests
|
||||
description: Detox iOS tests for mattermost mobile app have started ...
|
||||
status: pending
|
||||
|
||||
update-initial-status-android:
|
||||
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: ${{ github.sha }}
|
||||
context: e2e/detox-android-tests
|
||||
description: Detox Android tests for mattermost mobile app have started ...
|
||||
status: pending
|
||||
|
||||
build-ios-simulator:
|
||||
runs-on: macos-14
|
||||
needs:
|
||||
- update-initial-status
|
||||
- update-initial-status-ios
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
|
@ -44,19 +57,80 @@ jobs:
|
|||
name: ios-build-simulator-${{ github.run_id }}
|
||||
path: Mattermost-simulator-x86_64.app.zip
|
||||
|
||||
build-android-apk:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
needs:
|
||||
- update-initial-status-android
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_jvmargs: -Xmx8g
|
||||
steps:
|
||||
- name: Prune Docker to free up space
|
||||
run: docker system prune -af
|
||||
|
||||
- name: Remove npm Temporary Files
|
||||
run: |
|
||||
rm -rf ~/.npm/_cacache
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ inputs.MOBILE_VERSION }}
|
||||
|
||||
- name: Prepare Android Build
|
||||
uses: ./.github/actions/prepare-android-build
|
||||
env:
|
||||
STORE_FILE: "${{ secrets.MM_MOBILE_STORE_FILE }}"
|
||||
STORE_ALIAS: "${{ secrets.MM_MOBILE_STORE_ALIAS }}"
|
||||
STORE_PASSWORD: "${{ secrets.MM_MOBILE_STORE_PASSWORD }}"
|
||||
MATTERMOST_BUILD_GH_TOKEN: "${{ secrets.MATTERMOST_BUILD_GH_TOKEN }}"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y default-jdk
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/caches/modules-2/
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
- name: Detox build
|
||||
run: |
|
||||
cd detox
|
||||
npm install
|
||||
npm install -g detox-cli
|
||||
npm run e2e:android-build
|
||||
|
||||
- name: Upload Android Build
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: android-build-files-${{ github.run_id }}
|
||||
path: "android/app/build/**/*"
|
||||
|
||||
run-ios-tests-on-main-scheduled:
|
||||
name: iOS Mobile Tests on Main (Scheduled)
|
||||
uses: ./.github/workflows/e2e-detox-template.yml
|
||||
uses: ./.github/workflows/e2e-ios-template.yml
|
||||
needs:
|
||||
- build-ios-simulator
|
||||
with:
|
||||
run-ios-tests: true
|
||||
run-type: "MAIN"
|
||||
record_tests_in_zephyr: 'true'
|
||||
MOBILE_VERSION: ${{ github.ref }}
|
||||
secrets: inherit
|
||||
|
||||
update-final-status:
|
||||
run-android-tests-on-main-scheduled:
|
||||
name: Android Mobile Tests on Main (Scheduled)
|
||||
uses: ./.github/workflows/e2e-android-template.yml
|
||||
needs:
|
||||
- build-android-apk
|
||||
with:
|
||||
run-android-tests: true
|
||||
run-type: "MAIN"
|
||||
record_tests_in_zephyr: 'true'
|
||||
MOBILE_VERSION: ${{ github.ref }}
|
||||
secrets: inherit
|
||||
|
||||
update-final-status-ios:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- run-ios-tests-on-main-scheduled
|
||||
|
|
@ -67,7 +141,23 @@ jobs:
|
|||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
context: e2e/detox-tests
|
||||
context: e2e/detox-ios-tests
|
||||
description: Completed with ${{ needs.run-ios-tests-on-main-scheduled.outputs.FAILURES }} failures
|
||||
status: ${{ needs.run-ios-tests-on-main-scheduled.outputs.STATUS }}
|
||||
target_url: ${{ needs.run-ios-tests-on-main-scheduled.outputs.TARGET_URL }}
|
||||
|
||||
update-final-status-android:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- run-android-tests-on-main-scheduled
|
||||
steps:
|
||||
- uses: mattermost/actions/delivery/update-commit-status@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
repository_full_name: ${{ github.repository }}
|
||||
commit_sha: ${{ github.sha }}
|
||||
context: e2e/detox-android-tests
|
||||
description: Completed with ${{ needs.run-android-tests-on-main-scheduled.outputs.FAILURES }} failures
|
||||
status: ${{ needs.run-android-tests-on-main-scheduled.outputs.STATUS }}
|
||||
target_url: ${{ needs.run-android-tests-on-main-scheduled.outputs.TARGET_URL }}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
name: Detox E2E Tests Template
|
||||
name: Detox iOS E2E Tests Template
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
|
@ -20,10 +20,6 @@ on:
|
|||
required: false
|
||||
default: ${{ github.head_ref || github.ref }}
|
||||
type: string
|
||||
run-ios-tests:
|
||||
description: "Run iOS tests"
|
||||
required: true
|
||||
type: boolean
|
||||
run-type:
|
||||
type: string
|
||||
required: false
|
||||
|
|
@ -42,12 +38,12 @@ on:
|
|||
description: "iPhone simulator name"
|
||||
required: false
|
||||
type: string
|
||||
default: "iPhone 15 Pro"
|
||||
default: "iPhone 16 Pro"
|
||||
ios_device_os_name:
|
||||
description: "iPhone simulator OS version"
|
||||
required: false
|
||||
type: string
|
||||
default: "iOS 17.4"
|
||||
default: "iOS 18.1"
|
||||
low_bandwidth_mode:
|
||||
description: "Enable low bandwidth mode"
|
||||
required: false
|
||||
|
|
@ -84,11 +80,12 @@ env:
|
|||
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"
|
||||
IOS: "true"
|
||||
RUNNING_E2E: true
|
||||
|
||||
jobs:
|
||||
generate-specs:
|
||||
runs-on: ubuntu-22.04
|
||||
if: ${{ inputs.run-ios-tests }}
|
||||
outputs:
|
||||
specs: ${{ steps.generate-specs.outputs.specs }}
|
||||
build_id: ${{ steps.resolve-device.outputs.BUILD_ID }}
|
||||
|
|
@ -125,10 +122,9 @@ jobs:
|
|||
|
||||
e2e-ios:
|
||||
name: ios-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||
if: ${{ inputs.run-ios-tests }}
|
||||
runs-on: macos-14
|
||||
continue-on-error: true
|
||||
timeout-minutes: ${{ inputs.low_bandwidth_mode && 80 || 40 }}
|
||||
timeout-minutes: ${{ inputs.low_bandwidth_mode && 140 || 70 }}
|
||||
env:
|
||||
IOS: true
|
||||
needs:
|
||||
|
|
@ -185,7 +181,7 @@ jobs:
|
|||
test_server_url: ${{ env.SITE_1_URL }}
|
||||
|
||||
- name: Run Detox E2E Tests
|
||||
continue-on-error: true # Label might have been removed manually
|
||||
continue-on-error: true # We want to run all the tests
|
||||
run: |
|
||||
cd detox
|
||||
npm run detox:config-gen
|
||||
|
|
@ -224,7 +220,6 @@ jobs:
|
|||
|
||||
generate-report:
|
||||
runs-on: ubuntu-22.04
|
||||
if: ${{ inputs.run-ios-tests}}
|
||||
needs:
|
||||
- generate-specs
|
||||
- e2e-ios
|
||||
|
|
@ -241,7 +236,7 @@ jobs:
|
|||
- name: ci/prepare-node-deps
|
||||
uses: ./.github/actions/prepare-node-deps
|
||||
|
||||
- name: Download All Artifacts
|
||||
- name: Download iOS Artifacts
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
path: detox/artifacts/
|
||||
|
|
@ -262,7 +257,6 @@ jobs:
|
|||
env:
|
||||
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 }}
|
||||
IOS: ${{ inputs.run-ios-tests }}
|
||||
BUILD_ID: ${{ needs.generate-specs.outputs.build_id }}
|
||||
REPORT_PATH: ${{ steps.s3.outputs.path }}
|
||||
## These are needed for the MM Webhook report
|
||||
|
|
@ -280,6 +280,7 @@ const FloatingTextChipsInput = forwardRef<Ref, Props>(({
|
|||
key={chipValue}
|
||||
id={chipValue}
|
||||
text={chipValue}
|
||||
testID={`${testID}.${chipValue}`}
|
||||
onRemove={onChipRemove}
|
||||
containerStyle={styles.chipContainer}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ const OptionItem = ({
|
|||
value={selected}
|
||||
trackColor={trackColor}
|
||||
thumbColor={thumbColor}
|
||||
testID={`${testID}.toggled.${selected}.${value}`}
|
||||
testID={`${testID}.toggled.${selected}.button`}
|
||||
/>
|
||||
);
|
||||
} else if (type === OptionType.ARROW) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
"android.emulator": {
|
||||
"type": "android.emulator",
|
||||
"device": {
|
||||
"avdName": "detox_pixel_4_xl_api_31"
|
||||
"avdName": "detox_pixel_4_xl_api_34"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,36 +1,49 @@
|
|||
# How to Run Detox Tests
|
||||
|
||||
## Android
|
||||
This guide will help you set up and run Detox tests for your project.
|
||||
|
||||
### Install Dependencies
|
||||
## Install Dependencies
|
||||
|
||||
From the root directory, run the following command to install the necessary dependencies:
|
||||
First, navigate to the root directory of your project and install the necessary dependencies by running:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Inject Detox Settings
|
||||
navigate to the `detox` folder and run `npm install`
|
||||
|
||||
To inject the Detox settings into your project, navigate to the `detox` directory and run the following command:
|
||||
## Android
|
||||
|
||||
### Build Detox Android App
|
||||
|
||||
To build the Detox Android app, navigate to the `detox` folder and run:
|
||||
|
||||
```sh
|
||||
npm run inject-detox-settings
|
||||
```
|
||||
|
||||
### Update `minSdkVersion` for `react-native-image-picker`
|
||||
|
||||
On macOS machines, update the `minSdkVersion` of `react-native-image-picker` to 23 by running the following command from the root directory.
|
||||
This is required for Detox to build the test apk targeting android API 31 or higher.
|
||||
|
||||
```sh
|
||||
sed -i '' 's/minSdkVersion 21/minSdkVersion 23/' ./node_modules/react-native-image-picker/android/build.gradle
|
||||
```
|
||||
|
||||
### Build detox android app
|
||||
|
||||
From the `detox` folder run:
|
||||
|
||||
```
|
||||
npm run e2e:android-build
|
||||
```
|
||||
|
||||
### Run Detox Android Tests
|
||||
|
||||
To execute the Detox tests on Android, navigate to the `detox` folder and run:
|
||||
|
||||
```sh
|
||||
npm run e2e:android-test
|
||||
```
|
||||
|
||||
## iOS
|
||||
|
||||
### Build iOS Simulator
|
||||
|
||||
To build the iOS simulator for Detox, navigate to the `detox` folder and run:
|
||||
|
||||
```sh
|
||||
npm run e2e:ios-build
|
||||
```
|
||||
|
||||
### Run iOS Tests
|
||||
|
||||
To execute the Detox tests on iOS, navigate to the `detox` folder and run:
|
||||
|
||||
```sh
|
||||
npm run e2e:ios-test
|
||||
```
|
||||
|
|
@ -1,39 +1,132 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Reference: Download Android (AOSP) Emulators - https://github.com/wix/Detox/blob/master/docs/guide/android-dev-env.md#android-aosp-emulators
|
||||
# sdkmanager "system-images;android-31;default;arm64-v8a"
|
||||
# sdkmanager --licenses
|
||||
|
||||
set -ex
|
||||
set -o pipefail
|
||||
|
||||
SDK_VERSION=31
|
||||
NAME="detox_pixel_4_xl_api_${SDK_VERSION}"
|
||||
SDK_VERSION=${1:-34} # First argument is SDK version
|
||||
AVD_BASE_NAME=${2:-"detox_pixel_4_xl_api_34"} # Second argument is AVD base name
|
||||
AVD_NAME="${AVD_BASE_NAME}_api_${SDK_VERSION}"
|
||||
TEST_FILES=${@:3} # Capture all remaining arguments as Detox test files
|
||||
|
||||
if emulator -list-avds | grep -q $NAME; then
|
||||
echo "'${NAME}' Android virtual device already exists."
|
||||
else
|
||||
CPU_ARCH_FAMILY=''
|
||||
CPU_ARCH=''
|
||||
setup_avd_home() {
|
||||
if [[ "$CI" == "true" ]]; then
|
||||
export ANDROID_AVD_HOME=$(pwd)/.android/avd
|
||||
mkdir -p "$ANDROID_AVD_HOME"
|
||||
fi
|
||||
}
|
||||
|
||||
get_cpu_architecture() {
|
||||
if [[ $(uname -p) == 'arm' ]]; then
|
||||
CPU_ARCH_FAMILY=arm64-v8a
|
||||
CPU_ARCH=arm64
|
||||
echo "arm64-v8a arm64"
|
||||
else
|
||||
CPU_ARCH_FAMILY=x86_64
|
||||
CPU_ARCH=x86_64
|
||||
echo "x86_64 x86_64"
|
||||
fi
|
||||
}
|
||||
|
||||
create_avd() {
|
||||
local cpu_arch_family cpu_arch
|
||||
read cpu_arch_family cpu_arch < <(get_cpu_architecture)
|
||||
|
||||
avdmanager create avd -n "$AVD_NAME" -k "system-images;android-${SDK_VERSION};default;${cpu_arch_family}" -p "$AVD_NAME" -d 'pixel'
|
||||
|
||||
cp -r android_emulator/ "$AVD_NAME/"
|
||||
sed -i -e "s|AvdId = change_avd_id|AvdId = ${AVD_NAME}|g" "$AVD_NAME/config.ini"
|
||||
sed -i -e "s|avd.ini.displayname = change_avd_displayname|avd.ini.displayname = Detox Pixel 4 XL API ${SDK_VERSION}|g" "$AVD_NAME/config.ini"
|
||||
sed -i -e "s|abi.type = change_type|abi.type = ${cpu_arch_family}|g" "$AVD_NAME/config.ini"
|
||||
sed -i -e "s|hw.cpu.arch = change_cpu_arch|hw.cpu.arch = ${cpu_arch}|g" "$AVD_NAME/config.ini"
|
||||
sed -i -e "s|image.sysdir.1 = change_to_image_sysdir/|image.sysdir.1 = system-images/android-${SDK_VERSION}/default/${cpu_arch_family}/|g" "$AVD_NAME/config.ini"
|
||||
sed -i -e "s|skin.path = change_to_absolute_path/pixel_4_xl_skin|skin.path = $(pwd)/${AVD_NAME}/pixel_4_xl_skin|g" "$AVD_NAME/config.ini"
|
||||
|
||||
echo "hw.cpu.ncore=5" >> "$AVD_NAME/config.ini"
|
||||
echo "Android virtual device successfully created: ${AVD_NAME}"
|
||||
}
|
||||
|
||||
start_adb_server() {
|
||||
echo "Restarting ADB server..."
|
||||
adb kill-server
|
||||
adb start-server
|
||||
}
|
||||
|
||||
start_emulator() {
|
||||
echo "Starting the emulator..."
|
||||
local emulator_opts="-avd $AVD_NAME -no-snapshot -no-boot-anim -no-audio -no-window"
|
||||
|
||||
if [[ "$CI" == "true" || "$(uname -s)" == "Linux" ]]; then
|
||||
emulator $emulator_opts -gpu host -accel on -qemu -m 4096 &
|
||||
else
|
||||
emulator $emulator_opts -gpu guest -verbose -qemu -vnc :0
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_emulator() {
|
||||
if [[ "$CI" != "true" ]]; then return; fi
|
||||
|
||||
echo "Waiting for emulator to boot..."
|
||||
adb wait-for-device
|
||||
until [[ "$(adb shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do
|
||||
echo "Waiting for emulator to fully boot..."
|
||||
sleep 10
|
||||
done
|
||||
echo "Emulator is fully booted."
|
||||
}
|
||||
|
||||
install_app() {
|
||||
echo "Installing the app..."
|
||||
adb install -r ../android/app/build/outputs/apk/debug/app-debug.apk
|
||||
adb shell pm list packages | grep "com.mattermost.rnbeta" && echo "App is installed." || echo "App is not installed."
|
||||
}
|
||||
|
||||
start_server() {
|
||||
echo "Starting the server..."
|
||||
cd ..
|
||||
RUNNING_E2E=true npm run start &
|
||||
local timeout=120 interval=5 elapsed=0
|
||||
|
||||
until nc -z localhost 8081; do
|
||||
if [[ $elapsed -ge $timeout ]]; then
|
||||
echo "Server did not start within 3 minutes."
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for server to be ready..."
|
||||
sleep $interval
|
||||
elapsed=$((elapsed + interval))
|
||||
done
|
||||
echo "Server is ready."
|
||||
}
|
||||
|
||||
setup_adb_reverse() {
|
||||
echo "Setting up ADB reverse port forwarding..."
|
||||
adb reverse tcp:8081 tcp:8081
|
||||
}
|
||||
|
||||
run_detox_tests() {
|
||||
echo "Running Detox tests... $@"
|
||||
|
||||
cd detox
|
||||
npm run e2e:android-test -- "$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
setup_avd_home
|
||||
|
||||
if ! emulator -list-avds | grep -q "$AVD_NAME"; then
|
||||
create_avd
|
||||
else
|
||||
echo "'${AVD_NAME}' Android virtual device already exists."
|
||||
fi
|
||||
|
||||
# Create virtual device in a relative "detox_pixel_4_xl_api_${SDK_VERSION}" folder
|
||||
avdmanager create avd -n $NAME -k "system-images;android-${SDK_VERSION};default;${CPU_ARCH_FAMILY}" -p $NAME -d 'pixel'
|
||||
start_adb_server
|
||||
start_emulator
|
||||
wait_for_emulator
|
||||
|
||||
# Copy predefined config and skin
|
||||
cp -r android_emulator/ $NAME/
|
||||
sed -i -e "s|AvdId = change_avd_id|AvdId = ${NAME}|g" $NAME/config.ini
|
||||
sed -i -e "s|avd.ini.displayname = change_avd_displayname|avd.ini.displayname = Detox Pixel 4 XL API ${SDK_VERSION}|g" $NAME/config.ini
|
||||
sed -i -e "s|abi.type = change_type|abi.type = ${CPU_ARCH_FAMILY}|g" $NAME/config.ini
|
||||
sed -i -e "s|hw.cpu.arch = change_cpu_arch|hw.cpu.arch = ${CPU_ARCH}|g" $NAME/config.ini
|
||||
sed -i -e "s|image.sysdir.1 = change_to_image_sysdir/|image.sysdir.1 = system-images/android-${SDK_VERSION}/default/${CPU_ARCH_FAMILY}/|g" $NAME/config.ini
|
||||
sed -i -e "s|skin.path = change_to_absolute_path/pixel_4_xl_skin|skin.path = $(pwd)/${NAME}/pixel_4_xl_skin|g" $NAME/config.ini
|
||||
if [[ "$CI" == "true" ]]; then
|
||||
install_app
|
||||
start_server
|
||||
setup_adb_reverse
|
||||
fi
|
||||
|
||||
echo "Android virtual device successfully created: ${NAME}"
|
||||
fi
|
||||
run_detox_tests $TEST_FILES
|
||||
}
|
||||
|
||||
main
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ class AutoResponderNotificationSettingsScreen {
|
|||
autoResponderNotificationSettingsScreen: 'auto_responder_notification_settings.screen',
|
||||
backButton: 'screen.back.button',
|
||||
scrollView: 'auto_responder_notification_settings.scroll_view',
|
||||
enableAutomaticRepliesOptionToggledOff: 'auto_responder_notification_settings.enable_automatic_replies.option.toggled.false',
|
||||
enableAutomaticRepliesOptionToggledOn: 'auto_responder_notification_settings.enable_automatic_replies.option.toggled.true',
|
||||
enableAutomaticRepliesOptionToggledOff: 'auto_responder_notification_settings.enable_automatic_replies.option.toggled.false.button',
|
||||
enableAutomaticRepliesOptionToggledOn: 'auto_responder_notification_settings.enable_automatic_replies.option.toggled.true.button',
|
||||
messageInput: 'auto_responder_notification_settings.message.input',
|
||||
messageInputDescription: 'auto_responder_notification_settings.message.input.description',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ class ChannelScreen {
|
|||
postPriorityPicker: 'channel.post_draft.quick_actions.post_priority_action',
|
||||
postPriorityImportantMessage: 'post_priority_picker_item.important',
|
||||
postPriorityUrgentMessage: 'post_priority_picker_item.urgent',
|
||||
postPriorityRequestAck: 'post_priority_picker_item.requested_ack.toggled.false.requested_ack',
|
||||
postPriorityPersistentNotification: 'post_priority_picker_item.persistent_notifications.toggled.undefined.persistent_notifications',
|
||||
postPriorityRequestAck: 'post_priority_picker_item.requested_ack.toggled.false.button',
|
||||
postPriorityPersistentNotification: 'post_priority_picker_item.persistent_notifications.toggled.undefined.button',
|
||||
};
|
||||
|
||||
postPriorityPersistentNotification = element(by.id(this.testID.postPriorityPersistentNotification));
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ class CreateOrEditChannelScreen {
|
|||
createButton: 'create_or_edit_channel.create.button',
|
||||
saveButton: 'create_or_edit_channel.save.button',
|
||||
scrollView: 'create_or_edit_channel.scroll_view',
|
||||
makePrivateToggledOff: 'channel_info_form.make_private.toggled.false',
|
||||
makePrivateToggledOn: 'channel_info_form.make_private.toggled.true',
|
||||
makePrivateToggledOff: 'channel_info_form.make_private.toggled.false.button',
|
||||
makePrivateToggledOn: 'channel_info_form.make_private.toggled.true.button',
|
||||
makePrivateDescription: 'channel_info_form.make_private.description',
|
||||
displayNameInput: 'channel_info_form.display_name.input',
|
||||
purposeInput: 'channel_info_form.purpose.input',
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ class EmailNotificationSettingsScreen {
|
|||
everyHourOptionSelected: 'email_notification_settings.every_hour.option.selected',
|
||||
neverOption: 'email_notification_settings.never.option',
|
||||
neverOptionSelected: 'email_notification_settings.never.option.selected',
|
||||
emailThreadsOptionToggledOff: 'email_notification_settings.email_threads.option.toggled.false',
|
||||
emailThreadsOptionToggledOn: 'email_notification_settings.email_threads.option.toggled.true',
|
||||
emailThreadsOptionToggledOff: 'email_notification_settings.email_threads.option.toggled.false.button',
|
||||
emailThreadsOptionToggledOn: 'email_notification_settings.email_threads.option.toggled.true.button',
|
||||
};
|
||||
|
||||
emailNotificationSettingsScreen = element(by.id(this.testID.emailNotificationSettingsScreen));
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class InviteScreen {
|
|||
};
|
||||
|
||||
getSearchListUserItemText = (id: string) => {
|
||||
return element(by.id(`${this.testID.searchListUserItemPrefix}.${id}.username`));
|
||||
return element(by.id(`${this.testID.searchListUserItemPrefix}.${id}.display_name`));
|
||||
};
|
||||
|
||||
getSearchListNoResults = (id: string) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ServerScreen} from '@support/ui/screen';
|
||||
import {ChannelListScreen, ServerScreen} from '@support/ui/screen';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ class LoginScreen {
|
|||
await element(by.text(/^Log In to Your Account*$/)).tap();
|
||||
await this.signinButton.tap();
|
||||
|
||||
await wait(timeouts.FOUR_SEC);
|
||||
await waitFor(ChannelListScreen.channelListScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
};
|
||||
|
||||
loginAsAdmin = async (user: any = {}) => {
|
||||
|
|
@ -79,7 +79,8 @@ class LoginScreen {
|
|||
await this.passwordInput.tap();
|
||||
await this.passwordInput.replaceText(user.password);
|
||||
await this.signinButton.tap();
|
||||
await wait(timeouts.FOUR_SEC);
|
||||
|
||||
await waitFor(ChannelListScreen.channelListScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ class MentionNotificationSettingsScreen {
|
|||
mentionNotificationSettingsScreen: 'mention_notification_settings.screen',
|
||||
backButton: 'screen.back.button',
|
||||
scrollView: 'mention_notification_settings.scroll_view',
|
||||
caseSensitiveFirstNameOptionToggledOff: 'mention_notification_settings.case_sensitive_first_name.option.toggled.false',
|
||||
caseSensitiveFirstNameOptionToggledOn: 'mention_notification_settings.case_sensitive_first_name.option.toggled.true',
|
||||
nonCaseSensitiveUsernameOptionToggledOff: 'mention_notification_settings.non_case_sensitive_username.option.toggled.false',
|
||||
nonCaseSensitiveUsernameOptionToggledOn: 'mention_notification_settings.non_case_sensitive_username.option.toggled.true',
|
||||
channelWideMentionsOptionToggledOff: 'mention_notification_settings.channel_wide_mentions.option.toggled.false',
|
||||
channelWideMentionsOptionToggledOn: 'mention_notification_settings.channel_wide_mentions.option.toggled.true',
|
||||
caseSensitiveFirstNameOptionToggledOff: 'mention_notification_settings.case_sensitive_first_name.option.toggled.false.button',
|
||||
caseSensitiveFirstNameOptionToggledOn: 'mention_notification_settings.case_sensitive_first_name.option.toggled.true.button',
|
||||
nonCaseSensitiveUsernameOptionToggledOff: 'mention_notification_settings.non_case_sensitive_username.option.toggled.false.button',
|
||||
nonCaseSensitiveUsernameOptionToggledOn: 'mention_notification_settings.non_case_sensitive_username.option.toggled.true.button',
|
||||
channelWideMentionsOptionToggledOff: 'mention_notification_settings.channel_wide_mentions.option.toggled.false.button',
|
||||
channelWideMentionsOptionToggledOn: 'mention_notification_settings.channel_wide_mentions.option.toggled.true.button',
|
||||
keywordsInput: 'mention_notification_settings.keywords.input',
|
||||
keywordsInputDescription: 'mention_notification_settings.keywords.input.description',
|
||||
threadsStartParticipateOption: 'mention_notification_settings.threads_start_participate.option',
|
||||
|
|
@ -62,6 +62,10 @@ class MentionNotificationSettingsScreen {
|
|||
await expect(this.mentionNotificationSettingsScreen).not.toBeVisible();
|
||||
};
|
||||
|
||||
getKeywordTriggerElement = async (keyword: string) => {
|
||||
return element(by.text(`${keyword.replace(/ /g, '').toLowerCase()}`));
|
||||
};
|
||||
|
||||
toggleCaseSensitiveFirstNameOptionOn = async () => {
|
||||
await this.caseSensitiveFirstNameOptionToggledOff.tap();
|
||||
await expect(this.caseSensitiveFirstNameOptionToggledOn).toBeVisible();
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ class PushNotificationSettingsScreen {
|
|||
mentionsOnlyOptionSelected: 'push_notification_settings.mentions_only.option.selected',
|
||||
nothingOption: 'push_notification_settings.nothing.option',
|
||||
nothingOptionSelected: 'push_notification_settings.nothing.option.selected',
|
||||
pushThreadsFollowingOptionToggledOff: 'push_notification_settings.push_threads_following.option.toggled.false',
|
||||
pushThreadsFollowingOptionToggledOn: 'push_notification_settings.push_threads_following.option.toggled.true',
|
||||
pushThreadsFollowingOptionToggledOff: 'push_notification_settings.push_threads_following.option.toggled.false.button',
|
||||
pushThreadsFollowingOptionToggledOn: 'push_notification_settings.push_threads_following.option.toggled.true.button',
|
||||
mobileOnlineOption: 'push_notification_settings.mobile_online.option',
|
||||
mobileOnlineOptionSelected: 'push_notification_settings.mobile_online.option.selected',
|
||||
mobileAwayOption: 'push_notification_settings.mobile_away.option',
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class ServerScreen {
|
|||
}
|
||||
}
|
||||
}
|
||||
await waitFor(this.usernameInput).toExist().withTimeout(timeouts.FOUR_SEC);
|
||||
await waitFor(this.usernameInput).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
};
|
||||
|
||||
close = async () => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class TeamDropdownMenuScreen {
|
|||
teamDropdownMenuScreen = element(by.id(this.testID.teamDropdownMenuScreen));
|
||||
|
||||
getTeamIcon = (teamId: string) => {
|
||||
return element(by.id(`team_sidebar.team_list.team_list_item.${teamId}.team_icon`));
|
||||
return element(by.id(`team_sidebar.team_list.team_list_item.${teamId}.team_display_name`));
|
||||
};
|
||||
|
||||
getTeamDisplayName = (teamId: string) => {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ class TimezoneDisplaySettingsScreen {
|
|||
timezoneDisplaySettingsScreen: 'timezone_display_settings.screen',
|
||||
backButton: 'screen.back.button',
|
||||
scrollView: 'timezone_display_settings.scroll_view',
|
||||
automaticOptionToggledOff: 'timezone_display_settings.automatic.option.toggled.false',
|
||||
automaticOptionToggledOn: 'timezone_display_settings.automatic.option.toggled.true',
|
||||
automaticOptionToggledOff: 'timezone_display_settings.automatic.option.toggled.false.button',
|
||||
automaticOptionToggledOn: 'timezone_display_settings.automatic.option.toggled.true.button',
|
||||
manualOption: 'timezone_display_settings.manual.option',
|
||||
manualOptionInfo: 'timezone_display_settings.manual.option.info',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -172,13 +172,11 @@ describe('Account - Custom Status', () => {
|
|||
// # Open custom status screen
|
||||
await CustomStatusScreen.open();
|
||||
|
||||
await wait(timeouts.ONE_SEC);
|
||||
|
||||
// * Verify custom status is cleared from input field
|
||||
await expect(CustomStatusScreen.getCustomStatusEmoji('default')).toBeVisible();
|
||||
if (isIos()) {
|
||||
await expect(CustomStatusScreen.statusInput).toHaveValue(defaultStatusText);
|
||||
} else {
|
||||
await expect(CustomStatusScreen.statusInput).toHaveText('');
|
||||
}
|
||||
await expect(CustomStatusScreen.statusInput).toHaveText('');
|
||||
|
||||
// # Go back to account screen
|
||||
await CustomStatusScreen.close();
|
||||
|
|
|
|||
|
|
@ -66,11 +66,13 @@ describe('Account - Settings - Mention Notification Settings', () => {
|
|||
|
||||
it('MM-T5107_2 - should be able to change mention notification settings and save by tapping navigation back button', async () => {
|
||||
// # Switch toggles, type in keywords as camelcase with spaces, tap on back button, and go back to mention notifications screen
|
||||
const keywords = ` Keywords ${getRandomId()} `;
|
||||
const keywords = `Keywords ${getRandomId()}`;
|
||||
const commasSeparator = ',';
|
||||
await MentionNotificationSettingsScreen.toggleCaseSensitiveFirstNameOptionOn();
|
||||
await MentionNotificationSettingsScreen.toggleNonCaseSensitiveUsernameOptionOn();
|
||||
await MentionNotificationSettingsScreen.toggleChannelWideMentionsOptionOff();
|
||||
await MentionNotificationSettingsScreen.keywordsInput.replaceText(keywords);
|
||||
await MentionNotificationSettingsScreen.keywordsInput.typeText(keywords);
|
||||
await MentionNotificationSettingsScreen.keywordsInput.typeText(commasSeparator);
|
||||
await MentionNotificationSettingsScreen.back();
|
||||
await MentionNotificationSettingsScreen.open();
|
||||
|
||||
|
|
@ -79,7 +81,8 @@ describe('Account - Settings - Mention Notification Settings', () => {
|
|||
await expect(MentionNotificationSettingsScreen.nonCaseSensitiveUsernameOptionToggledOn).toBeVisible();
|
||||
await expect(MentionNotificationSettingsScreen.channelWideMentionsOptionToggledOff).toBeVisible();
|
||||
if (isIos()) {
|
||||
await expect(MentionNotificationSettingsScreen.keywordsInput).toHaveValue(keywords.replace(/ /g, '').toLowerCase());
|
||||
const triggerMentionKeyword = await MentionNotificationSettingsScreen.getKeywordTriggerElement(keywords);
|
||||
await expect(triggerMentionKeyword).toHaveText(keywords.replace(/ /g, '').toLowerCase());
|
||||
} else {
|
||||
await expect(MentionNotificationSettingsScreen.keywordsInput).toHaveText(keywords.replace(/ /g, '').toLowerCase());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,9 +316,9 @@ describe('Autocomplete - At-Mention', () => {
|
|||
await ChannelScreen.postInput.typeText(testUser.username);
|
||||
|
||||
// * Verify at-mention autocomplete contains current user
|
||||
const {atMentionItemUserDisplayName, atMentionItemCurrentUserIndicator, atMentionItemUsername} = Autocomplete.getAtMentionItem(testUser.id);
|
||||
await expect(atMentionItemUserDisplayName).toHaveText(`${testUser.first_name} ${testUser.last_name}`);
|
||||
await expect(atMentionItemCurrentUserIndicator).toHaveText(' (you)');
|
||||
await expect(atMentionItemUsername).toHaveText(` @${testUser.username}`);
|
||||
await wait(timeouts.TWO_SEC);
|
||||
const {atMentionItemUserDisplayName, atMentionItemProfilePicture} = Autocomplete.getAtMentionItem(testUser.id);
|
||||
await expect(atMentionItemUserDisplayName).toBeVisible();
|
||||
await expect(atMentionItemProfilePicture).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -253,6 +253,6 @@ describe('Autocomplete - Channel Mention', () => {
|
|||
|
||||
// * Verify channel mention autocomplete contains current channel
|
||||
const {channelMentionItemChannelDisplayName} = Autocomplete.getChannelMentionItem(testChannel.name);
|
||||
await expect(channelMentionItemChannelDisplayName).toHaveText(`${testChannel.display_name} ~${testChannel.name}`);
|
||||
await expect(channelMentionItemChannelDisplayName).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ describe('Channels - Channel Info', () => {
|
|||
await expect(ChannelInfoScreen.ignoreMentionsOptionToggledOff).toBeVisible();
|
||||
await expect(ChannelInfoScreen.pinnedMessagesOption).toBeVisible();
|
||||
await expect(ChannelInfoScreen.copyChannelLinkOption).toBeVisible();
|
||||
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||
await expect(ChannelInfoScreen.editChannelOption).toBeVisible();
|
||||
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||
await expect(ChannelInfoScreen.leaveChannelOption).toBeVisible();
|
||||
|
|
|
|||
|
|
@ -69,10 +69,14 @@ describe('Channels - Channel List', () => {
|
|||
await expect(ChannelListScreen.headerPlusButton).toBeVisible();
|
||||
await expect(ChannelListScreen.threadsButton).toBeVisible();
|
||||
await expect(ChannelListScreen.getCategoryHeaderDisplayName(channelsCategory)).toHaveText('CHANNELS');
|
||||
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name)).toHaveText(testChannel.display_name);
|
||||
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName)).toHaveText('Off-Topic');
|
||||
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName)).toHaveText('Town Square');
|
||||
await expect(ChannelListScreen.getCategoryHeaderDisplayName(directMessagesCategory)).toHaveText('DIRECT MESSAGES');
|
||||
await waitFor(ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name)).toBeVisible();
|
||||
await waitFor(ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName)).toBeVisible();
|
||||
await waitFor(ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName)).toBeVisible();
|
||||
await waitFor(ChannelListScreen.getCategoryHeaderDisplayName(directMessagesCategory)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await expect(ChannelListScreen.getCategoryHeaderDisplayName(directMessagesCategory)).toBeVisible();
|
||||
});
|
||||
|
||||
it('MM-T4728_2 - should be able to switch between channels', async () => {
|
||||
|
|
@ -108,6 +112,7 @@ describe('Channels - Channel List', () => {
|
|||
it('MM-T4728_3 - should be able to collapse and expand categories', async () => {
|
||||
// # Go to a channel to make it active and go back to channel list screen
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await ChannelScreen.postMessage('Test message');
|
||||
await ChannelScreen.back();
|
||||
|
||||
// * Verify on channel list screen
|
||||
|
|
@ -156,8 +161,14 @@ describe('Channels - Channel List', () => {
|
|||
// * Verify on create direct message screen
|
||||
await CreateDirectMessageScreen.toBeVisible();
|
||||
|
||||
try {
|
||||
await CreateDirectMessageScreen.closeTutorial();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to close tutorial:', error);
|
||||
}
|
||||
|
||||
// # Go back to channel list screen
|
||||
await CreateDirectMessageScreen.closeTutorial();
|
||||
await CreateDirectMessageScreen.close();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ describe('Channels - Create Direct Message', () => {
|
|||
// # Post a message and go back to channel list screen
|
||||
await ChannelScreen.postMessage('test');
|
||||
await ChannelScreen.back();
|
||||
await device.reloadReactNative();
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
||||
// * Verify direct message channel for the new user is added to direct message list
|
||||
|
|
@ -147,11 +146,7 @@ describe('Channels - Create Direct Message', () => {
|
|||
// # Post a message and go back to channel list screen
|
||||
await ChannelScreen.postMessage('test');
|
||||
await ChannelScreen.back();
|
||||
await device.reloadReactNative();
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
||||
// * Verify group message channel for the other two new users is added to direct message list
|
||||
await expect(element(by.text(groupDisplayName))).toBeVisible();
|
||||
});
|
||||
|
||||
it('MM-T4730_4 - should display empty search state for create direct message', async () => {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ describe('Channels - Favorite and Unfavorite Channel', () => {
|
|||
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
||||
await CreateDirectMessageScreen.startButton.tap();
|
||||
await ChannelScreen.postMessage('test');
|
||||
await device.reloadReactNative();
|
||||
await ChannelScreen.back();
|
||||
await ChannelListScreen.getChannelItemDisplayName(directMessagesCategory, directMessageChannel.name).tap();
|
||||
await ChannelScreen.introFavoriteAction.tap();
|
||||
await ChannelScreen.back();
|
||||
|
|
|
|||
|
|
@ -175,9 +175,5 @@ describe('Messaging - Emojis and Reactions', () => {
|
|||
// * Verify empty search state for emoji picker
|
||||
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
||||
await expect(element(by.text('Check the spelling or try another search.'))).toBeVisible();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await EmojiPickerScreen.close();
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ describe('Search - Saved Messages', () => {
|
|||
|
||||
// * Verify saved message is not displayed anymore
|
||||
const {postListPostItem} = SavedMessagesScreen.getPostListPostItem(savedPost.id, message);
|
||||
await waitFor(postListPostItem).not.toExist().withTimeout(3000);
|
||||
await expect(postListPostItem).not.toExist();
|
||||
|
||||
// # Go back to channel list screen
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ describe('Search - Search Messages', () => {
|
|||
await SearchMessagesScreen.searchModifierFrom.tap();
|
||||
await SearchMessagesScreen.searchInput.typeText(testUser.username);
|
||||
const {atMentionItem} = Autocomplete.getAtMentionItem(testUser.id);
|
||||
await waitFor(atMentionItem).toBeVisible().withTimeout(2000);
|
||||
await atMentionItem.tap();
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
|
||||
|
|
@ -274,6 +275,7 @@ describe('Search - Search Messages', () => {
|
|||
await expect(postListPostItem).toBeVisible();
|
||||
|
||||
// # Clear search input, remove recent search item, and go back to channel list screen
|
||||
await SearchMessagesScreen.searchInput.tap();
|
||||
await SearchMessagesScreen.searchClearButton.tap();
|
||||
await SearchMessagesScreen.getRecentSearchItemRemoveButton(searchTerm).tap();
|
||||
await ChannelListScreen.open();
|
||||
|
|
@ -306,8 +308,8 @@ describe('Search - Search Messages', () => {
|
|||
await SearchMessagesScreen.teamPickerButton.tap();
|
||||
await TeamDropdownMenuScreen.getTeamIcon(testTeamTwo.id).tap();
|
||||
|
||||
// * Verify team picker button displays second team icon
|
||||
await expect(SearchMessagesScreen.getTeamPickerIcon(testTeamTwo.id)).toBeVisible();
|
||||
// * Verify team picker button displays second team name
|
||||
await expect(element(by.text(testTeamTwo.display_name))).toBeVisible();
|
||||
|
||||
// # Type in a search term that will yield results for second team and tap on search key
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
|
|
@ -321,8 +323,7 @@ describe('Search - Search Messages', () => {
|
|||
await SearchMessagesScreen.teamPickerButton.tap();
|
||||
await TeamDropdownMenuScreen.getTeamIcon(testTeam.id).tap();
|
||||
|
||||
// * Verify team picker button displays first team icon and search results do not contain searched message
|
||||
await expect(SearchMessagesScreen.getTeamPickerIcon(testTeam.id)).toBeVisible();
|
||||
// * Verify search results do not contain searched message
|
||||
await expect(postListPostItem).not.toBeVisible();
|
||||
|
||||
// # Clear search input, remove recent search item, and go back to channel list screen
|
||||
|
|
|
|||
|
|
@ -77,13 +77,11 @@ describe('Server Login - Connect to Server', () => {
|
|||
await wait(timeouts.ONE_SEC);
|
||||
|
||||
// * Verify invalid url error
|
||||
await waitFor(serverUrlInputError).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await expect(serverUrlInputError).toHaveText('Cannot connect to the server.');
|
||||
await waitFor(serverUrlInputError).toExist().withTimeout(timeouts.FOUR_SEC);
|
||||
await expect(serverUrlInputError).toHaveText('URLSessionTask failed with error: A server with the specified hostname could not be found.');
|
||||
});
|
||||
|
||||
it('MM-T4676_4 - should show connection error on invalid ssl or invalid host', async () => {
|
||||
await device.reloadReactNative();
|
||||
|
||||
// # Connect with invalid ssl and non-empty server display name
|
||||
const expiredServerUrl = 'expired.badssl.com';
|
||||
const wrongHostServerUrl = 'wrong.host.badssl.com';
|
||||
|
|
@ -95,19 +93,8 @@ describe('Server Login - Connect to Server', () => {
|
|||
await wait(timeouts.ONE_SEC);
|
||||
|
||||
// * Verify invalid SSL cert error
|
||||
await waitFor(Alert.invalidSslCertTitle).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okButton.tap();
|
||||
|
||||
// # Connect with invalid host and valid server display name
|
||||
await device.reloadReactNative();
|
||||
await serverUrlInput.replaceText(wrongHostServerUrl);
|
||||
await serverDisplayNameInput.replaceText('Server 1');
|
||||
await connectButton.tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
|
||||
// * Verify invalid SSL cert error
|
||||
await waitFor(Alert.invalidSslCertTitle).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okButton.tap();
|
||||
await waitFor(serverUrlInputError).toExist().withTimeout(timeouts.FOUR_SEC);
|
||||
await expect(serverUrlInputError).toBeVisible();
|
||||
});
|
||||
|
||||
it('MM-T4676_5 - should show login screen on successful connection to server', async () => {
|
||||
|
|
@ -117,7 +104,7 @@ describe('Server Login - Connect to Server', () => {
|
|||
await connectButton.tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
|
||||
if (isIos()) {
|
||||
if (isIos() && !process.env.CI) {
|
||||
// # Tap alert okay button
|
||||
await waitFor(Alert.okayButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okayButton.tap();
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import {
|
|||
SettingsScreen,
|
||||
ThemeDisplaySettingsScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId, isIos, timeouts, wait} from '@support/utils';
|
||||
import {getRandomId, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Smoke Test - Account', () => {
|
||||
|
|
@ -131,20 +131,15 @@ describe('Smoke Test - Account', () => {
|
|||
|
||||
it('MM-T5114_3 - should be able to set notification settings', async () => {
|
||||
// # Open settings screen, open notification settings screen, open mention notification settings screen, type in keywords, tap on back button, and go back to mention notification settings screen
|
||||
const keywords = `${getRandomId()},${getRandomId()}`;
|
||||
const keywords = `${getRandomId()}`;
|
||||
await SettingsScreen.open();
|
||||
await NotificationSettingsScreen.open();
|
||||
await MentionNotificationSettingsScreen.open();
|
||||
await MentionNotificationSettingsScreen.keywordsInput.replaceText(keywords);
|
||||
await MentionNotificationSettingsScreen.keywordsInput.typeText(',');
|
||||
await MentionNotificationSettingsScreen.back();
|
||||
await MentionNotificationSettingsScreen.open();
|
||||
|
||||
// * Verify keywords are saved
|
||||
if (isIos()) {
|
||||
await expect(MentionNotificationSettingsScreen.keywordsInput).toHaveValue(keywords.toLowerCase());
|
||||
} else {
|
||||
await expect(MentionNotificationSettingsScreen.keywordsInput).toHaveText(keywords.toLowerCase());
|
||||
}
|
||||
await expect(element(by.text(keywords))).toBeVisible();
|
||||
|
||||
// # Go back to notification settings screen, open push notification settings screen, tap on mentions only option, tap on mobile away option, tap on back button, and go back to notification settings screen
|
||||
await MentionNotificationSettingsScreen.back();
|
||||
|
|
|
|||
|
|
@ -186,7 +186,8 @@ describe('Smoke Test - Messaging', () => {
|
|||
|
||||
// # Tap on post to open thread and tap on thread overview unsave button
|
||||
await postListPostItem.tap();
|
||||
await ThreadScreen.getThreadOverviewUnsaveButton().tap();
|
||||
await element(by.text(message)).longPress();
|
||||
await PostOptionsScreen.unsavePostOption.tap();
|
||||
|
||||
// * Verify saved text is not displayed on the post pre-header
|
||||
await expect(channelPostListPostItemPreHeaderText).not.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ describe('Smoke Test - Server Login', () => {
|
|||
await expect(Alert.logoutTitle(serverTwoDisplayName)).toBeVisible();
|
||||
|
||||
// # Tap on logout button
|
||||
await Alert.logoutButton2.tap();
|
||||
await Alert.logoutButton.tap();
|
||||
|
||||
// * Verify second server is logged out
|
||||
await ServerListScreen.getServerItemInactive(serverTwoDisplayName).swipe('left');
|
||||
|
|
|
|||
|
|
@ -58,9 +58,11 @@ describe('Smoke Test - Threads', () => {
|
|||
// # Create a thread and unfollow thread via thread navigation
|
||||
const parentMessage = `Message ${getRandomId()}`;
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await waitFor(ChannelScreen.postInput).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||
await ChannelScreen.postMessage(parentMessage);
|
||||
const {post: parentPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
await ChannelScreen.openReplyThreadFor(parentPost.id, parentMessage);
|
||||
await waitFor(ThreadScreen.postInput).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||
await ThreadScreen.postMessage(`${parentMessage} reply`);
|
||||
await ThreadScreen.followingButton.tap();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
// - Use element testID when selecting an element. Create one if none.
|
||||
// *******************************************************************
|
||||
|
||||
import {Setup, User} from '@support/server_api';
|
||||
import {Setup, Team, User} from '@support/server_api';
|
||||
import {
|
||||
serverOneUrl,
|
||||
siteOneUrl,
|
||||
|
|
@ -19,32 +19,27 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {isIos, timeouts} from '@support/utils';
|
||||
import {timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
function systemDialog(label: string) {
|
||||
if (isIos()) {
|
||||
return element(by.label(label)).atIndex(0);
|
||||
}
|
||||
return element(by.text(label));
|
||||
}
|
||||
|
||||
describe('Teams - Invite', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
|
||||
let testTeam: any;
|
||||
let testUser: any;
|
||||
let testUser1: any;
|
||||
let testUser2: any;
|
||||
let testUser3: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const {team, user} = await Setup.apiInit(siteOneUrl);
|
||||
|
||||
testTeam = team;
|
||||
testUser = user;
|
||||
|
||||
const {user: user1} = await User.apiCreateUser(siteOneUrl, {prefix: 'i'});
|
||||
|
||||
testUser1 = user1;
|
||||
const {user: user2} = await User.apiCreateUser(siteOneUrl);
|
||||
testUser2 = user2;
|
||||
await Team.apiAddUserToTeam(siteOneUrl, testUser2.id, testTeam.id);
|
||||
|
||||
// # Log in to server
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
|
|
@ -79,7 +74,7 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.teamIcon).toBeVisible();
|
||||
|
||||
// * Verify default Selection
|
||||
await waitFor(Invite.screenSelection).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await waitFor(Invite.screenSelection).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||
|
||||
// * Verify Server data
|
||||
await expect(Invite.serverDisplayName).toHaveText(serverOneDisplayName);
|
||||
|
|
@ -92,20 +87,6 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.searchBarInput).toBeVisible();
|
||||
});
|
||||
|
||||
it('MM-T5221 - should be able to share a URL invite to the team', async () => {
|
||||
if (isIos()) {
|
||||
// # Tap on Share link
|
||||
await Invite.shareLinkButton.tap();
|
||||
const dialog = systemDialog(`Join the ${testTeam.display_name} team`);
|
||||
|
||||
// * Verify share dialog is open
|
||||
await expect(dialog).toExist();
|
||||
|
||||
// # Close share dialog
|
||||
await dialog.swipe('down');
|
||||
} // no support for Android system dialogs by detox yet. See https://github.com/wix/Detox/issues/3227
|
||||
});
|
||||
|
||||
it('MM-T5361 - should show no results item in search list', async () => {
|
||||
const noUser = 'qwertyuiop';
|
||||
|
||||
|
|
@ -146,14 +127,12 @@ describe('Teams - Invite', () => {
|
|||
});
|
||||
|
||||
it('MM-T5363 - should be able to send user invite', async () => {
|
||||
const username = ` @${testUser1.username}`;
|
||||
|
||||
// # Search for an existent user
|
||||
await Invite.searchBarInput.replaceText(testUser1.username);
|
||||
|
||||
// * Validate user item in search list
|
||||
await waitFor(Invite.getSearchListUserItem(testUser1.id)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await expect(Invite.getSearchListUserItemText(testUser1.id)).toHaveText(username);
|
||||
await expect(Invite.getSearchListUserItemText(testUser1.id)).toHaveText(testUser1.username);
|
||||
|
||||
// # Select user item
|
||||
await Invite.getSearchListUserItem(testUser1.id).tap();
|
||||
|
|
@ -170,23 +149,21 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.getSummaryReportSent()).toBeVisible();
|
||||
await expect(Invite.getSummaryReportNotSent()).not.toExist();
|
||||
await expect(Invite.getSummaryReportUserItem(testUser1.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser1.id)).toHaveText(username);
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser1.id)).toHaveText(testUser1.username);
|
||||
});
|
||||
|
||||
it('MM-T5364 - should not be able to send user invite to user already in team', async () => {
|
||||
const username = ` @${testUser1.username}`;
|
||||
|
||||
// # Search for an existent user already in team
|
||||
await Invite.searchBarInput.replaceText(testUser1.username);
|
||||
await Invite.searchBarInput.replaceText(testUser2.username);
|
||||
|
||||
// * Validate user item in search list
|
||||
await waitFor(Invite.getSearchListUserItem(testUser1.id)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await waitFor(Invite.getSearchListUserItem(testUser2.id)).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
|
||||
// # Select user item
|
||||
await Invite.getSearchListUserItem(testUser1.id).tap();
|
||||
await Invite.getSearchListUserItem(testUser2.id).tap();
|
||||
|
||||
// * Validate user is added to selected items
|
||||
await expect(Invite.getSelectedItem(testUser1.id)).toBeVisible();
|
||||
await expect(Invite.getSelectedItem(testUser2.id)).toBeVisible();
|
||||
|
||||
// # Send invitation
|
||||
await Invite.sendButton.tap();
|
||||
|
|
@ -195,24 +172,25 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.screenSummary).toBeVisible();
|
||||
await expect(Invite.getSummaryReportSent()).not.toExist();
|
||||
await expect(Invite.getSummaryReportNotSent()).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItem(testUser1.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser1.id)).toHaveText(username);
|
||||
await expect(Invite.getSummaryReportUserItem(testUser2.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser2.id)).toHaveText(testUser2.username);
|
||||
});
|
||||
|
||||
it('MM-T5365 - should handle both sent and not sent invites', async () => {
|
||||
const {user: testUser2} = await User.apiCreateUser(siteOneUrl, {prefix: 'i'});
|
||||
const {user: user3} = await User.apiCreateUser(siteOneUrl, {prefix: 'i'});
|
||||
testUser3 = user3;
|
||||
|
||||
// # Search for an existent user
|
||||
await Invite.searchBarInput.replaceText(testUser2.username);
|
||||
await Invite.searchBarInput.replaceText(testUser3.username);
|
||||
|
||||
// * Validate user item in search list
|
||||
await waitFor(Invite.getSearchListUserItem(testUser2.id)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
await waitFor(Invite.getSearchListUserItem(testUser3.id)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
|
||||
// # Select user item
|
||||
await Invite.getSearchListUserItem(testUser2.id).tap();
|
||||
await Invite.getSearchListUserItem(testUser3.id).tap();
|
||||
|
||||
// * Validate user is added to selected items
|
||||
await expect(Invite.getSelectedItem(testUser2.id)).toBeVisible();
|
||||
await expect(Invite.getSelectedItem(testUser3.id)).toBeVisible();
|
||||
|
||||
// # Search for a existent user already in team
|
||||
await Invite.searchBarInput.replaceText(testUser.username);
|
||||
|
|
@ -239,7 +217,7 @@ describe('Teams - Invite', () => {
|
|||
|
||||
// * Validate summary report sent
|
||||
waitFor(Invite.getSummaryReportSent()).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItem(testUser2.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser2.id)).toBeVisible(testUser2.username1);
|
||||
await expect(Invite.getSummaryReportUserItem(testUser3.id)).toBeVisible();
|
||||
await expect(Invite.getSummaryReportUserItemText(testUser3.id)).toBeVisible(testUser3.username1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable no-console */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Paths to files
|
||||
const androidManifestPath = path.resolve(
|
||||
__dirname,
|
||||
'../android/app/src/debug/AndroidManifest.xml',
|
||||
);
|
||||
const settingsGradlePath = path.resolve(__dirname, '../android/settings.gradle');
|
||||
|
||||
// Detox code to add to settings.gradle
|
||||
const detoxSettings = `
|
||||
include ':detox'
|
||||
project(':detox').projectDir = new File(rootProject.projectDir, '../detox/node_modules/detox/android')
|
||||
`;
|
||||
|
||||
// Updated AndroidManifest.xml content
|
||||
const updatedManifest = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.mattermost.rnbeta">
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="28"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity
|
||||
android:name="androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity"
|
||||
android:exported="true"
|
||||
tools:node="replace"/>
|
||||
<activity
|
||||
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
|
||||
android:exported="true"
|
||||
tools:node="replace"/>
|
||||
<activity
|
||||
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity"
|
||||
android:exported="true"
|
||||
tools:node="replace"/>
|
||||
</application>
|
||||
</manifest>`;
|
||||
|
||||
// Update AndroidManifest.xml
|
||||
function updateAndroidManifest() {
|
||||
try {
|
||||
fs.writeFileSync(androidManifestPath, updatedManifest, 'utf-8');
|
||||
console.log('AndroidManifest.xml updated successfully.');
|
||||
} catch (err) {
|
||||
console.error(`Failed to update AndroidManifest.xml: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update settings.gradle
|
||||
function updateSettingsGradle() {
|
||||
try {
|
||||
const content = fs.readFileSync(settingsGradlePath, 'utf-8');
|
||||
if (content.includes("include ':detox'")) {
|
||||
console.log('Detox settings already present in settings.gradle.');
|
||||
return;
|
||||
}
|
||||
fs.writeFileSync(settingsGradlePath, content + detoxSettings, 'utf-8');
|
||||
console.log('settings.gradle updated successfully.');
|
||||
} catch (err) {
|
||||
console.error(`Failed to update settings.gradle: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Run updates
|
||||
updateAndroidManifest();
|
||||
updateSettingsGradle();
|
||||
21
detox/package-lock.json
generated
21
detox/package-lock.json
generated
|
|
@ -23,7 +23,7 @@
|
|||
"babel-plugin-module-resolver": "5.0.2",
|
||||
"client-oauth2": "4.3.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"detox": "20.28.0",
|
||||
"detox": "20.33.0",
|
||||
"form-data": "4.0.1",
|
||||
"jest": "29.7.0",
|
||||
"jest-circus": "29.7.0",
|
||||
|
|
@ -5232,12 +5232,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detox": {
|
||||
"version": "20.28.0",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.28.0.tgz",
|
||||
"integrity": "sha512-JeUkWNnYE7lqby3S9AeYJP3ttCBKH+qZWACjWXwvSbe3tm6JeXvecVUYkzSoNfC4IzTX5p+rWvG0IPsfOsZSFw==",
|
||||
"version": "20.33.0",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.33.0.tgz",
|
||||
"integrity": "sha512-9KZC3NgCav/IvuWm/EaKNIL5lV2bJf23PPK67OHS2dMhJsDsNBRWP9k3RN7hzLzzUwAXMMoOmk+S1OpOsazNFA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.6.3",
|
||||
"bunyan": "^1.8.12",
|
||||
|
|
@ -5245,7 +5244,7 @@
|
|||
"caf": "^15.0.1",
|
||||
"chalk": "^4.0.0",
|
||||
"child-process-promise": "^2.2.0",
|
||||
"detox-copilot": "^0.0.24",
|
||||
"detox-copilot": "^0.0.27",
|
||||
"execa": "^5.1.1",
|
||||
"find-up": "^5.0.0",
|
||||
"fs-extra": "^11.0.0",
|
||||
|
|
@ -5292,11 +5291,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detox-copilot": {
|
||||
"version": "0.0.24",
|
||||
"resolved": "https://registry.npmjs.org/detox-copilot/-/detox-copilot-0.0.24.tgz",
|
||||
"integrity": "sha512-42g0QyJS31URl28YRxc4hGozSXhbbB1sKwzxEjZR9WtLoSx6WYDsQkQD8+yP5t1NExiSCZAfvNmBw8PYQwDKwg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"version": "0.0.27",
|
||||
"resolved": "https://registry.npmjs.org/detox-copilot/-/detox-copilot-0.0.27.tgz",
|
||||
"integrity": "sha512-H2febTNp0arVx2A8rvM1C2BwDiBEP/2Ya8Hd1mVyV66rR5u8om1gdIypaRGm+plpTLCHhlefe4+7qLtHgVzpng==",
|
||||
"deprecated": "This package has been renamed to @wix-pilot/core. Please update your dependencies accordingly.",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/detox/node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"babel-plugin-module-resolver": "5.0.2",
|
||||
"client-oauth2": "4.3.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"detox": "20.28.0",
|
||||
"detox": "20.33.0",
|
||||
"form-data": "4.0.1",
|
||||
"jest": "29.7.0",
|
||||
"jest-circus": "29.7.0",
|
||||
|
|
@ -42,7 +42,6 @@
|
|||
"xml2js": "0.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"e2e:android-inject-settings": "node inject-detox-settings.js",
|
||||
"e2e:android-create-emulator": "./create_android_emulator.sh",
|
||||
"e2e:android-build": "detox build -c android.emu.debug",
|
||||
"e2e:android-test": "detox test -c android.emu.debug",
|
||||
|
|
|
|||
|
|
@ -99,12 +99,13 @@ 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}-results*/${platform}-junit*.xml`]);
|
||||
console.log(`Merged, check ${combinedFilePath}`);
|
||||
|
||||
// Read XML from a file
|
||||
const xml = fse.readFileSync(combinedFilePath);
|
||||
const {testsuites} = convertXmlToJson(xml);
|
||||
const {testsuites} = convertXmlToJson(xml, platform);
|
||||
|
||||
// Generate short summary, write to file and then send report via webhook
|
||||
const allTests = getAllTests(testsuites);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
/* eslint-disable no-process-env */
|
||||
/* eslint-disable no-console */
|
||||
const fs = require('fs');
|
||||
const deviceName = process.env.DEVICE_NAME || 'iPhone 14';
|
||||
const deviceOSVersion = process.env.DEVICE_OS_VERSION || 'iOS 17.2';
|
||||
const deviceName = process.env.DEVICE_NAME || 'iPhone 16 Pro';
|
||||
const deviceOSVersion = process.env.DEVICE_OS_VERSION || 'iOS 18.1';
|
||||
const detoxConfigTemplate = fs.readFileSync('../.detoxrc.json', 'utf8');
|
||||
const detoxConfig = detoxConfigTemplate.replace('__DEVICE_NAME__', deviceName).replace('__DEVICE_OS_VERSION__', deviceOSVersion);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ const {ARTIFACTS_DIR} = require('./constants');
|
|||
|
||||
const MAX_FAILED_TITLES = 5;
|
||||
|
||||
function convertXmlToJson(xml) {
|
||||
const platform = process.env.IOS === 'true' ? 'ios' : 'android';
|
||||
function convertXmlToJson(xml, platform) {
|
||||
const jsonFile = `${ARTIFACTS_DIR}/${platform}-junit.json`;
|
||||
|
||||
// Convert XML to JSON
|
||||
|
|
@ -27,7 +26,6 @@ function convertXmlToJson(xml) {
|
|||
// Save JSON in a file
|
||||
fse.writeFileSync(jsonFile, json);
|
||||
});
|
||||
|
||||
return readJsonFromFile(jsonFile);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue