E2E feature schedule posts (#8709)
- Fix iOS and Android CI. - Fixes E2E tests on both platform - Added tests for scheduled draft feature.
This commit is contained in:
parent
11118c43c5
commit
3cbde1663d
70 changed files with 1509 additions and 278 deletions
66
.github/workflows/e2e-android-template.yml
vendored
66
.github/workflows/e2e-android-template.yml
vendored
|
|
@ -52,7 +52,7 @@ on:
|
||||||
description: "Android API level"
|
description: "Android API level"
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: "33"
|
default: "34"
|
||||||
outputs:
|
outputs:
|
||||||
STATUS:
|
STATUS:
|
||||||
value: ${{ jobs.generate-report.outputs.STATUS }}
|
value: ${{ jobs.generate-report.outputs.STATUS }}
|
||||||
|
|
@ -130,7 +130,7 @@ jobs:
|
||||||
name: android-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
name: android-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||||
runs-on: ubuntu-latest-8-cores
|
runs-on: ubuntu-latest-8-cores
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
timeout-minutes: 150
|
timeout-minutes: 240
|
||||||
env:
|
env:
|
||||||
ANDROID_HOME: /usr/local/lib/android/sdk
|
ANDROID_HOME: /usr/local/lib/android/sdk
|
||||||
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
|
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
|
||||||
|
|
@ -170,6 +170,12 @@ jobs:
|
||||||
cd detox
|
cd detox
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
- name: Set .env with RUNNING_E2E=true
|
||||||
|
run: |
|
||||||
|
cat > .env <<EOF
|
||||||
|
RUNNING_E2E=true
|
||||||
|
EOF
|
||||||
|
|
||||||
- name: Create destination path
|
- name: Create destination path
|
||||||
run: mkdir -p android/app/build
|
run: mkdir -p android/app/build
|
||||||
|
|
||||||
|
|
@ -194,18 +200,29 @@ jobs:
|
||||||
|
|
||||||
- name: Accept Android licenses
|
- name: Accept Android licenses
|
||||||
run: |
|
run: |
|
||||||
yes | sdkmanager --licenses || true
|
mkdir -p ~/.android
|
||||||
|
yes | sdkmanager --licenses > sdk_licenses_output.txt || true
|
||||||
|
|
||||||
|
# Check if "All SDK package licenses accepted" appears in output
|
||||||
|
if grep -q "All SDK package licenses accepted" sdk_licenses_output.txt; then
|
||||||
|
echo "✅ All licenses accepted successfully."
|
||||||
|
else
|
||||||
|
echo "❌ Licenses not fully accepted."
|
||||||
|
cat sdk_licenses_output.txt
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Install Android system image
|
- name: Install Android SDK components
|
||||||
run: |
|
run: |
|
||||||
sdkmanager "system-images;android-34;default;x86_64"
|
yes | sdkmanager --install "platform-tools" "emulator" "platforms;android-34" "system-images;android-34;default;x86_64" "system-images;android-34;google_apis;x86_64"
|
||||||
sdkmanager "platform-tools" "emulator"
|
env:
|
||||||
|
JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }}
|
||||||
|
|
||||||
- name: Create and run Android Emulator
|
- name: Create and run Android Emulator
|
||||||
run: |
|
run: |
|
||||||
cd detox
|
cd detox
|
||||||
chmod +x ./create_android_emulator.sh
|
chmod +x ./create_android_emulator.sh
|
||||||
./create_android_emulator.sh ${{ env.SDK_VERSION }} ${{ env.AVD_NAME }} ${{ matrix.specs }}
|
CI=true ./create_android_emulator.sh ${{ env.SDK_VERSION }} ${{ env.AVD_NAME }} ${{ matrix.specs }}
|
||||||
continue-on-error: true # We want to run all the tests
|
continue-on-error: true # We want to run all the tests
|
||||||
|
|
||||||
- name: Upload Android Test Report
|
- name: Upload Android Test Report
|
||||||
|
|
@ -293,3 +310,36 @@ jobs:
|
||||||
echo "" >> ${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 "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}
|
echo "There was **${{ steps.summary.outputs.PERCENTAGE }}%** success rate." >> ${GITHUB_STEP_SUMMARY}
|
||||||
|
|
||||||
|
- name: Comment report on the PR
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const prNumber = context.payload.pull_request.number;
|
||||||
|
|
||||||
|
const commentBody = `**Android E2E Test Report**: ${process.env.MOBILE_SHA} | ${process.env.PERCENTAGE}% (${process.env.PASSES}/${process.env.TOTAL}) | [full report](${process.env.TARGET_URL})
|
||||||
|
| Tests | Passed ✅ | Failed ❌ | Skipped ⏭️ | Errors ⚠️ |
|
||||||
|
|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| ${process.env.TOTAL} | ${process.env.PASSES} | ${process.env.FAILURES} | ${process.env.SKIPPED} | ${process.env.ERRORS} |
|
||||||
|
`;
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
body: commentBody,
|
||||||
|
});
|
||||||
|
env:
|
||||||
|
STATUS: ${{ steps.determine-status.outputs.STATUS }}
|
||||||
|
FAILURES: ${{ steps.summary.outputs.FAILURES }}
|
||||||
|
PASSES: ${{ steps.summary.outputs.PASSES }}
|
||||||
|
SKIPPED: ${{ steps.summary.outputs.SKIPPED }}
|
||||||
|
TOTAL: ${{ steps.summary.outputs.TOTAL }}
|
||||||
|
ERRORS: ${{ steps.summary.outputs.ERRORS }}
|
||||||
|
PERCENTAGE: ${{ steps.summary.outputs.PERCENTAGE }}
|
||||||
|
BUILD_ID: ${{ needs.generate-specs.outputs.build_id }}
|
||||||
|
RUN_TYPE: ${{ inputs.run-type }}
|
||||||
|
MOBILE_REF: ${{ needs.generate-specs.outputs.mobile_ref }}
|
||||||
|
MOBILE_SHA: ${{ needs.generate-specs.outputs.mobile_sha }}
|
||||||
|
TARGET_URL: ${{ steps.set-url.outputs.TARGET_URL }}
|
||||||
|
|
|
||||||
2
.github/workflows/e2e-detox-pr.yml
vendored
2
.github/workflows/e2e-detox-pr.yml
vendored
|
|
@ -6,6 +6,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- feature_schedule_posts
|
||||||
types:
|
types:
|
||||||
- labeled
|
- labeled
|
||||||
|
|
||||||
|
|
@ -104,6 +105,7 @@ jobs:
|
||||||
cd detox
|
cd detox
|
||||||
npm install
|
npm install
|
||||||
npm install -g detox-cli
|
npm install -g detox-cli
|
||||||
|
npm run e2e:android-inject-settings
|
||||||
npm run e2e:android-build
|
npm run e2e:android-build
|
||||||
|
|
||||||
- name: Upload Android Build
|
- name: Upload Android Build
|
||||||
|
|
|
||||||
61
.github/workflows/e2e-ios-template.yml
vendored
61
.github/workflows/e2e-ios-template.yml
vendored
|
|
@ -38,12 +38,12 @@ on:
|
||||||
description: "iPhone simulator name"
|
description: "iPhone simulator name"
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: "iPhone 16 Pro"
|
default: "iPhone 15 Pro"
|
||||||
ios_device_os_name:
|
ios_device_os_name:
|
||||||
description: "iPhone simulator OS version"
|
description: "iPhone simulator OS version"
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: "iOS 18.1"
|
default: "iOS 17.2"
|
||||||
low_bandwidth_mode:
|
low_bandwidth_mode:
|
||||||
description: "Enable low bandwidth mode"
|
description: "Enable low bandwidth mode"
|
||||||
required: false
|
required: false
|
||||||
|
|
@ -81,7 +81,7 @@ env:
|
||||||
WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }}
|
WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }}
|
||||||
FAILURE_MESSAGE: "Something has failed"
|
FAILURE_MESSAGE: "Something has failed"
|
||||||
IOS: "true"
|
IOS: "true"
|
||||||
RUNNING_E2E: true
|
RUNNING_E2E: "true"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
generate-specs:
|
generate-specs:
|
||||||
|
|
@ -124,7 +124,7 @@ jobs:
|
||||||
name: ios-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
name: ios-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
timeout-minutes: ${{ inputs.low_bandwidth_mode && 140 || 70 }}
|
timeout-minutes: ${{ inputs.low_bandwidth_mode && 240 || 180 }}
|
||||||
env:
|
env:
|
||||||
IOS: true
|
IOS: true
|
||||||
needs:
|
needs:
|
||||||
|
|
@ -153,7 +153,10 @@ jobs:
|
||||||
path: mobile-artifacts
|
path: mobile-artifacts
|
||||||
|
|
||||||
- name: Unzip iOS Simulator Build
|
- name: Unzip iOS Simulator Build
|
||||||
run: unzip -o mobile-artifacts/*.zip -d mobile-artifacts/
|
run: |
|
||||||
|
unzip -o mobile-artifacts/*.zip -d mobile-artifacts/
|
||||||
|
# delete zip file
|
||||||
|
rm -f mobile-artifacts/*.zip
|
||||||
|
|
||||||
- name: Prepare Low Bandwidth Environment
|
- name: Prepare Low Bandwidth Environment
|
||||||
id: prepare-low-bandwidth
|
id: prepare-low-bandwidth
|
||||||
|
|
@ -167,9 +170,6 @@ jobs:
|
||||||
upload_speed: "3300"
|
upload_speed: "3300"
|
||||||
latency: "500"
|
latency: "500"
|
||||||
|
|
||||||
- name: Start React Native Metro Server
|
|
||||||
run: npm run start &
|
|
||||||
|
|
||||||
- name: Install Detox Dependencies
|
- name: Install Detox Dependencies
|
||||||
run: cd detox && npm i
|
run: cd detox && npm i
|
||||||
|
|
||||||
|
|
@ -180,13 +180,25 @@ jobs:
|
||||||
with:
|
with:
|
||||||
test_server_url: ${{ env.SITE_1_URL }}
|
test_server_url: ${{ env.SITE_1_URL }}
|
||||||
|
|
||||||
|
- name: Set .env with RUNNING_E2E=true
|
||||||
|
run: |
|
||||||
|
cat > .env <<EOF
|
||||||
|
echo "RUNNING_E2E=true" >> .env
|
||||||
|
|
||||||
- name: Run Detox E2E Tests
|
- name: Run Detox E2E Tests
|
||||||
continue-on-error: true # We want to run all the tests
|
continue-on-error: true # We want to run all the tests
|
||||||
run: |
|
run: |
|
||||||
|
# Start the server
|
||||||
|
npm run start &
|
||||||
|
sleep 120 # Wait for watchman to finish querying the files
|
||||||
cd detox
|
cd detox
|
||||||
|
npm run clean-detox
|
||||||
npm run detox:config-gen
|
npm run detox:config-gen
|
||||||
npm run e2e:ios-test -- ${{ matrix.specs }}
|
npm run e2e:ios-test -- ${{ matrix.specs }}
|
||||||
env:
|
env:
|
||||||
|
DETOX_DISABLE_HIERARCHY_DUMP: "YES"
|
||||||
|
DETOX_DISABLE_SCREENSHOT_TRACKING: "YES"
|
||||||
|
DETOX_LOGLEVEL: "debug"
|
||||||
DETOX_DEVICE_TYPE: ${{ env.DEVICE_NAME }}
|
DETOX_DEVICE_TYPE: ${{ env.DEVICE_NAME }}
|
||||||
DETOX_OS_VERSION: ${{ env.DEVICE_OS_VERSION }}
|
DETOX_OS_VERSION: ${{ env.DEVICE_OS_VERSION }}
|
||||||
LOW_BANDWIDTH_MODE: ${{ inputs.low_bandwidth_mode }}
|
LOW_BANDWIDTH_MODE: ${{ inputs.low_bandwidth_mode }}
|
||||||
|
|
@ -295,3 +307,36 @@ jobs:
|
||||||
echo "" >> ${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 "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}
|
echo "There was **${{ steps.summary.outputs.PERCENTAGE }}%** success rate." >> ${GITHUB_STEP_SUMMARY}
|
||||||
|
|
||||||
|
- name: Comment report on the PR
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const prNumber = context.payload.pull_request.number;
|
||||||
|
|
||||||
|
const commentBody = `**iOS E2E Test Report**: ${process.env.MOBILE_SHA} | ${process.env.PERCENTAGE}% (${process.env.PASSES}/${process.env.TOTAL}) | [full report](${process.env.TARGET_URL})
|
||||||
|
| Tests | Passed ✅ | Failed ❌ | Skipped ⏭️ | Errors ⚠️ |
|
||||||
|
|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| ${process.env.TOTAL} | ${process.env.PASSES} | ${process.env.FAILURES} | ${process.env.SKIPPED} | ${process.env.ERRORS} |
|
||||||
|
`;
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
body: commentBody,
|
||||||
|
});
|
||||||
|
env:
|
||||||
|
STATUS: ${{ steps.determine-status.outputs.STATUS }}
|
||||||
|
FAILURES: ${{ steps.summary.outputs.FAILURES }}
|
||||||
|
PASSES: ${{ steps.summary.outputs.PASSES }}
|
||||||
|
SKIPPED: ${{ steps.summary.outputs.SKIPPED }}
|
||||||
|
TOTAL: ${{ steps.summary.outputs.TOTAL }}
|
||||||
|
ERRORS: ${{ steps.summary.outputs.ERRORS }}
|
||||||
|
PERCENTAGE: ${{ steps.summary.outputs.PERCENTAGE }}
|
||||||
|
BUILD_ID: ${{ needs.generate-specs.outputs.build_id }}
|
||||||
|
RUN_TYPE: ${{ inputs.run-type }}
|
||||||
|
MOBILE_REF: ${{ needs.generate-specs.outputs.mobile_ref }}
|
||||||
|
MOBILE_SHA: ${{ needs.generate-specs.outputs.mobile_sha }}
|
||||||
|
TARGET_URL: ${{ steps.set-url.outputs.TARGET_URL }}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,10 @@ const DraftAndScheduledPostHeader: React.FC<Props> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={style.scheduledContainer}>
|
<View style={style.scheduledContainer}>
|
||||||
<Text style={style.scheduledAtText}>
|
<Text
|
||||||
|
style={style.scheduledAtText}
|
||||||
|
testID='scheduled_post_header.scheduled_at'
|
||||||
|
>
|
||||||
{isSent? intl.formatMessage({id: 'scheduled_post.header.sent', defaultMessage: 'Sent'}): intl.formatMessage(
|
{isSent? intl.formatMessage({id: 'scheduled_post.header.sent', defaultMessage: 'Sent'}): intl.formatMessage(
|
||||||
{id: 'channel_info.scheduled', defaultMessage: 'Send on {time}'},
|
{id: 'channel_info.scheduled', defaultMessage: 'Send on {time}'},
|
||||||
{time: scheduledTime},
|
{time: scheduledTime},
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,10 @@ function ScheduledPostIndicator({
|
||||||
name='clock-send-outline'
|
name='clock-send-outline'
|
||||||
size={18}
|
size={18}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.text}>
|
<Text
|
||||||
|
style={styles.text}
|
||||||
|
testID='scheduled_post_header.scheduled_post_indicator'
|
||||||
|
>
|
||||||
{scheduledPostText}
|
{scheduledPostText}
|
||||||
{' '}
|
{' '}
|
||||||
<Text
|
<Text
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
['module:react-native-dotenv', {
|
['module:react-native-dotenv', {
|
||||||
moduleName: 'react-native-dotenv',
|
moduleName: '@env',
|
||||||
path: '.env',
|
path: '.env',
|
||||||
blacklist: null,
|
blacklist: null,
|
||||||
whitelist: null,
|
whitelist: null,
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,35 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifacts": {
|
"artifacts": {
|
||||||
"pathBuilder": "./e2e/path_builder.js"
|
"pathBuilder": "./e2e/path_builder.js",
|
||||||
|
"plugins": {
|
||||||
|
"screenshot": {
|
||||||
|
"shouldTakeAutomaticSnapshots": true,
|
||||||
|
"keepOnlyFailedTestsArtifacts": true
|
||||||
|
},
|
||||||
|
"video": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"behavior": {
|
"behavior": {
|
||||||
"init": {
|
"init": {
|
||||||
|
"reinstallApp": true,
|
||||||
"launchApp": false
|
"launchApp": false
|
||||||
|
},
|
||||||
|
"cleanup": {
|
||||||
|
"shutdownDevice": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"session": {
|
||||||
|
"sessionId": "mobile-test-session",
|
||||||
|
"debugSynchronization": 5000,
|
||||||
|
"autoStart": true
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"threshold": 75
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,33 @@ navigate to the `detox` folder and run `npm install`
|
||||||
To build the Detox Android app, navigate to the `detox` folder and run:
|
To build the Detox Android app, navigate to the `detox` folder and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
npm run e2e:android-inject-settings
|
||||||
|
|
||||||
npm run e2e:android-build
|
npm run e2e:android-build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The debug apk will be built and available at `android/app/build/outputs/apk/debug/app-debug.apk`
|
||||||
|
|
||||||
### Run Detox Android Tests
|
### Run Detox Android Tests
|
||||||
|
|
||||||
|
#### Create emulator
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./create_android_emulator.sh SDK_VERSION AVD_NAME
|
||||||
|
|
||||||
|
# example ./create_android_emulator.sh 34 pixel_5a_avd
|
||||||
|
# example ./create_android_emulator.sh 34 pixel_5a_avd --headless
|
||||||
|
# If we want to see the emulator logs. Run it in debug mode example ./create_android_emulator.sh 34 pixel_5a_avd --debug
|
||||||
|
```
|
||||||
|
|
||||||
To execute the Detox tests on Android, navigate to the `detox` folder and run:
|
To execute the Detox tests on Android, navigate to the `detox` folder and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run e2e:android-test
|
npm run e2e:android-test
|
||||||
|
|
||||||
|
# To run a particular tests
|
||||||
|
|
||||||
|
npm run e2e:android-test <path to test file>
|
||||||
```
|
```
|
||||||
|
|
||||||
## iOS
|
## iOS
|
||||||
|
|
@ -40,10 +58,30 @@ To build the iOS simulator for Detox, navigate to the `detox` folder and run:
|
||||||
npm run e2e:ios-build
|
npm run e2e:ios-build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will build the Simulor .zip file at the root folder.
|
||||||
|
|
||||||
|
Create a folder named `mobile-artifacts` at the project root. Unzip the zip file and move the mattermost app under `mobile-artifacts.`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# From project root
|
||||||
|
mkdir mobile-artifacts
|
||||||
|
```
|
||||||
|
|
||||||
### Run iOS Tests
|
### Run iOS Tests
|
||||||
|
|
||||||
To execute the Detox tests on iOS, navigate to the `detox` folder and run:
|
To execute the Detox tests on iOS, navigate to the `detox` folder and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run e2e:ios-test
|
npm run e2e:ios-test
|
||||||
```
|
|
||||||
|
# To run a particular tests
|
||||||
|
|
||||||
|
npm run e2e:android-test path to test file.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### TIP : For iOS, you can download the simulator from `~Mobile: Test build` or `~Release: Mobile Apps` channel in the community.
|
||||||
|
|
||||||
|
### Results
|
||||||
|
|
||||||
|
The Local Runs generate artifacts under `detox/artifacts/ios-debug-**` or `detox/artifacts/android-debug-**`.
|
||||||
|
You can see the html report, failure screenshot under that folder.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ create_avd() {
|
||||||
local cpu_arch_family cpu_arch
|
local cpu_arch_family cpu_arch
|
||||||
read cpu_arch_family cpu_arch < <(get_cpu_architecture)
|
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'
|
avdmanager create avd -n "$AVD_NAME" -k "system-images;android-${SDK_VERSION};google_apis;${cpu_arch_family}" -p "$AVD_NAME" -d 'pixel_5'
|
||||||
|
|
||||||
cp -r android_emulator/ "$AVD_NAME/"
|
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|AvdId = change_avd_id|AvdId = ${AVD_NAME}|g" "$AVD_NAME/config.ini"
|
||||||
|
|
@ -50,7 +50,7 @@ start_adb_server() {
|
||||||
|
|
||||||
start_emulator() {
|
start_emulator() {
|
||||||
echo "Starting the emulator..."
|
echo "Starting the emulator..."
|
||||||
local emulator_opts="-avd $AVD_NAME -no-snapshot -no-boot-anim -no-audio -no-window"
|
local emulator_opts="-avd $AVD_NAME -no-snapshot -no-boot-anim -no-audio -gpu off -no-window"
|
||||||
|
|
||||||
if [[ "$CI" == "true" || "$(uname -s)" == "Linux" ]]; then
|
if [[ "$CI" == "true" || "$(uname -s)" == "Linux" ]]; then
|
||||||
emulator $emulator_opts -gpu host -accel on -qemu -m 4096 &
|
emulator $emulator_opts -gpu host -accel on -qemu -m 4096 &
|
||||||
|
|
@ -82,6 +82,7 @@ start_server() {
|
||||||
cd ..
|
cd ..
|
||||||
RUNNING_E2E=true npm run start &
|
RUNNING_E2E=true npm run start &
|
||||||
local timeout=120 interval=5 elapsed=0
|
local timeout=120 interval=5 elapsed=0
|
||||||
|
sleep $timeout
|
||||||
|
|
||||||
until nc -z localhost 8081; do
|
until nc -z localhost 8081; do
|
||||||
if [[ $elapsed -ge $timeout ]]; then
|
if [[ $elapsed -ge $timeout ]]; then
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ module.exports = {
|
||||||
setupFilesAfterEnv: ['./test/setup.ts'],
|
setupFilesAfterEnv: ['./test/setup.ts'],
|
||||||
maxWorkers: 1,
|
maxWorkers: 1,
|
||||||
testSequencer: './custom_sequencer.js',
|
testSequencer: './custom_sequencer.js',
|
||||||
testTimeout: 120000,
|
testTimeout: 180000,
|
||||||
rootDir: '.',
|
rootDir: '.',
|
||||||
testMatch: ['<rootDir>/test/**/*.e2e.ts'],
|
testMatch: ['<rootDir>/test/**/*.e2e.ts'],
|
||||||
transform: {
|
transform: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {timeouts, wait} from '@support/utils';
|
||||||
|
|
||||||
import client from './client';
|
import client from './client';
|
||||||
import {getResponseFromError} from './common';
|
import {getResponseFromError} from './common';
|
||||||
|
|
||||||
|
|
@ -72,6 +74,7 @@ export const apiGetPostsInChannel = async (baseUrl: string, channelId: string):
|
||||||
* @return {Object} returns {post} on success or {error, status} on error
|
* @return {Object} returns {post} on success or {error, status} on error
|
||||||
*/
|
*/
|
||||||
export const apiGetLastPostInChannel = async (baseUrl: string, channelId: string): Promise<any> => {
|
export const apiGetLastPostInChannel = async (baseUrl: string, channelId: string): Promise<any> => {
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
const {posts} = await apiGetPostsInChannel(baseUrl, channelId);
|
const {posts} = await apiGetPostsInChannel(baseUrl, channelId);
|
||||||
return {post: posts[0]};
|
return {post: posts[0]};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ class Alert {
|
||||||
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(1);
|
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(1);
|
||||||
confirmButton = isAndroid() ? element(by.text('CONFIRM')) : element(by.label('Confirm')).atIndex(1);
|
confirmButton = isAndroid() ? element(by.text('CONFIRM')) : element(by.label('Confirm')).atIndex(1);
|
||||||
deleteButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(0);
|
deleteButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(0);
|
||||||
|
deleteScheduledMessageButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(1);
|
||||||
leaveButton = isAndroid() ? element(by.text('LEAVE')) : element(by.label('Leave')).atIndex(0);
|
leaveButton = isAndroid() ? element(by.text('LEAVE')) : element(by.label('Leave')).atIndex(0);
|
||||||
logoutButton = isAndroid() ? element(by.text('LOG OUT')) : element(by.label('Log out')).atIndex(1);
|
logoutButton = isAndroid() ? element(by.text('LOG OUT')) : element(by.label('Log out')).atIndex(1);
|
||||||
logoutButton2 = isAndroid() ? element(by.text('LOG OUT')) : element(by.label('Log out')).atIndex(2);
|
logoutButton2 = isAndroid() ? element(by.text('LOG OUT')) : element(by.label('Log out')).atIndex(2);
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ class BrowseChannelsScreen {
|
||||||
channelDropdownTextArchived: 'browse_channels.channel_dropdown.text.archived',
|
channelDropdownTextArchived: 'browse_channels.channel_dropdown.text.archived',
|
||||||
channelDropdownTextShared: 'browse_channels.channel_dropdown.text.shared',
|
channelDropdownTextShared: 'browse_channels.channel_dropdown.text.shared',
|
||||||
flatChannelList: 'browse_channels.channel_list.flat_list',
|
flatChannelList: 'browse_channels.channel_list.flat_list',
|
||||||
|
scheduledPostTooltipCloseButton: 'scheduled_post.tooltip.close.button',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
scheduledPostTooltipCloseButton = element(by.id(this.testID.scheduledPostTooltipCloseButton));
|
||||||
browseChannelsScreen = element(by.id(this.testID.browseChannelsScreen));
|
browseChannelsScreen = element(by.id(this.testID.browseChannelsScreen));
|
||||||
closeButton = element(by.id(this.testID.closeButton));
|
closeButton = element(by.id(this.testID.closeButton));
|
||||||
createButton = element(by.id(this.testID.createButton));
|
createButton = element(by.id(this.testID.createButton));
|
||||||
|
|
@ -60,6 +62,15 @@ class BrowseChannelsScreen {
|
||||||
await this.closeButton.tap();
|
await this.closeButton.tap();
|
||||||
await expect(this.browseChannelsScreen).not.toBeVisible();
|
await expect(this.browseChannelsScreen).not.toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dismissScheduledPostTooltip = async () => {
|
||||||
|
try {
|
||||||
|
await this.scheduledPostTooltipCloseButton.tap();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Element not visible, skipping click');
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const browseChannelsScreen = new BrowseChannelsScreen();
|
const browseChannelsScreen = new BrowseChannelsScreen();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import {
|
||||||
PostOptionsScreen,
|
PostOptionsScreen,
|
||||||
ThreadScreen,
|
ThreadScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {timeouts, wait} from '@support/utils';
|
import {isIos, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
class ChannelScreen {
|
class ChannelScreen {
|
||||||
|
|
@ -47,8 +47,31 @@ class ChannelScreen {
|
||||||
postPriorityUrgentMessage: 'post_priority_picker_item.urgent',
|
postPriorityUrgentMessage: 'post_priority_picker_item.urgent',
|
||||||
postPriorityRequestAck: 'post_priority_picker_item.requested_ack.toggled.false.button',
|
postPriorityRequestAck: 'post_priority_picker_item.requested_ack.toggled.false.button',
|
||||||
postPriorityPersistentNotification: 'post_priority_picker_item.persistent_notifications.toggled.undefined.button',
|
postPriorityPersistentNotification: 'post_priority_picker_item.persistent_notifications.toggled.undefined.button',
|
||||||
|
scheduledPostTooltipCloseButton: 'scheduled_post.tooltip.close.button',
|
||||||
|
scheduledPostTooltipCloseButtonAdminAccount: 'scheduled_post_tutorial_tooltip.close',
|
||||||
|
scheduleMessageTomorrowOption: 'post_priority_picker_item.scheduledPostOptionTomorrow',
|
||||||
|
scheduleMessageOnMondayOption: 'post_priority_picker_item.scheduledPostOptionMonday',
|
||||||
|
scheduledPostOptionNextMonday: 'post_priority_picker_item.scheduledPostOptionNextMonday',
|
||||||
|
scheduledPostOptionTomorrowSelected: 'post_priority_picker_item.scheduledPostOptionTomorrow.selected',
|
||||||
|
scheduledPostOptionMondaySelected: 'post_priority_picker_item.scheduledPostOptionMonday.selected',
|
||||||
|
scheduledPostOptionNextMondaySelected: 'post_priority_picker_item.scheduledPostOptionNextMonday.selected',
|
||||||
|
clickOnScheduledMessageButton: 'scheduled_post_create_button',
|
||||||
|
scheduledDraftInfoInChannel: 'scheduled_post_header.scheduled_post_indicator',
|
||||||
|
scheduledDraftTooltipText: 'scheduled_post.tooltip.description',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
scheduleDraftInforMessage = element(by.text('Type a message and long press the send button to schedule it for a later time.'));
|
||||||
|
scheduledDraftTooltipText = element(by.id(this.testID.scheduledDraftTooltipText));
|
||||||
|
scheduledDraftInfoInChannel = element(by.id(this.testID.scheduledDraftInfoInChannel));
|
||||||
|
clickOnScheduledMessageButton = element(by.id(this.testID.clickOnScheduledMessageButton));
|
||||||
|
scheduledPostOptionTomorrowSelected = element(by.id(this.testID.scheduledPostOptionTomorrowSelected));
|
||||||
|
scheduledPostOptionMondaySelected = element(by.id(this.testID.scheduledPostOptionMondaySelected));
|
||||||
|
scheduledPostOptionNextMonday = element(by.id(this.testID.scheduledPostOptionNextMonday));
|
||||||
|
scheduledPostOptionNextMondaySelected = element(by.id(this.testID.scheduledPostOptionNextMondaySelected));
|
||||||
|
scheduleMessageTomorrowOption = element(by.id(this.testID.scheduleMessageTomorrowOption));
|
||||||
|
scheduleMessageOnMondayOption = element(by.id(this.testID.scheduleMessageOnMondayOption));
|
||||||
|
scheduledPostTooltipCloseButton = element(by.id(this.testID.scheduledPostTooltipCloseButton));
|
||||||
|
scheduledPostTooltipCloseButtonAdminAccount = element(by.id(this.testID.scheduledPostTooltipCloseButtonAdminAccount));
|
||||||
postPriorityPersistentNotification = element(by.id(this.testID.postPriorityPersistentNotification));
|
postPriorityPersistentNotification = element(by.id(this.testID.postPriorityPersistentNotification));
|
||||||
postPriorityUrgentMessage = element(by.id(this.testID.postPriorityUrgentMessage));
|
postPriorityUrgentMessage = element(by.id(this.testID.postPriorityUrgentMessage));
|
||||||
postPriorityRequestAck = element(by.id(this.testID.postPriorityRequestAck));
|
postPriorityRequestAck = element(by.id(this.testID.postPriorityRequestAck));
|
||||||
|
|
@ -132,7 +155,12 @@ class ChannelScreen {
|
||||||
open = async (categoryKey: string, channelName: string) => {
|
open = async (categoryKey: string, channelName: string) => {
|
||||||
// # Open channel screen
|
// # Open channel screen
|
||||||
await ChannelListScreen.getChannelItemDisplayName(categoryKey, channelName).tap();
|
await ChannelListScreen.getChannelItemDisplayName(categoryKey, channelName).tap();
|
||||||
|
try {
|
||||||
|
await this.scheduledPostTooltipCloseButton.tap();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Element not visible, skipping click');
|
||||||
|
}
|
||||||
return this.toBeVisible();
|
return this.toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -169,7 +197,7 @@ class ChannelScreen {
|
||||||
await expect(postListPostItem).toBeVisible();
|
await expect(postListPostItem).toBeVisible();
|
||||||
|
|
||||||
// # Open post options
|
// # Open post options
|
||||||
await postListPostItem.longPress();
|
await postListPostItem.longPress(timeouts.TWO_SEC);
|
||||||
await PostOptionsScreen.toBeVisible();
|
await PostOptionsScreen.toBeVisible();
|
||||||
await wait(timeouts.TWO_SEC);
|
await wait(timeouts.TWO_SEC);
|
||||||
};
|
};
|
||||||
|
|
@ -190,6 +218,12 @@ class ChannelScreen {
|
||||||
await this.tapSendButton();
|
await this.tapSendButton();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enterMessageToSchedule = async (message: string) => {
|
||||||
|
await this.postInput.tap();
|
||||||
|
await this.postInput.clearText();
|
||||||
|
await this.postInput.replaceText(message);
|
||||||
|
};
|
||||||
|
|
||||||
tapSendButton = async () => {
|
tapSendButton = async () => {
|
||||||
// # Tap send button
|
// # Tap send button
|
||||||
await this.sendButton.tap();
|
await this.sendButton.tap();
|
||||||
|
|
@ -197,6 +231,11 @@ class ChannelScreen {
|
||||||
await expect(this.sendButtonDisabled).toBeVisible();
|
await expect(this.sendButtonDisabled).toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
longPressSendButton = async () => {
|
||||||
|
// # Long press send button
|
||||||
|
await this.sendButton.longPress();
|
||||||
|
};
|
||||||
|
|
||||||
hasPostMessage = async (postId: string, postMessage: string) => {
|
hasPostMessage = async (postId: string, postMessage: string) => {
|
||||||
const {postListPostItem} = this.getPostListPostItem(postId, postMessage);
|
const {postListPostItem} = this.getPostListPostItem(postId, postMessage);
|
||||||
await expect(postListPostItem).toBeVisible();
|
await expect(postListPostItem).toBeVisible();
|
||||||
|
|
@ -231,6 +270,47 @@ class ChannelScreen {
|
||||||
applyPostPrioritySettings = async () => {
|
applyPostPrioritySettings = async () => {
|
||||||
await this.applyPostPriority.tap();
|
await this.applyPostPriority.tap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
scheduleMessageForTomorrow = async () => {
|
||||||
|
await this.scheduleMessageTomorrowOption.tap();
|
||||||
|
await expect(this.scheduledPostOptionTomorrowSelected).toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
scheduleMessageForMonday = async () => {
|
||||||
|
await this.scheduleMessageOnMondayOption.tap();
|
||||||
|
await expect(this.scheduledPostOptionMondaySelected).toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
scheduleMessageForNextMonday = async () => {
|
||||||
|
await this.scheduledPostOptionNextMonday.tap();
|
||||||
|
await expect(this.scheduledPostOptionNextMondaySelected).toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
clickOnScheduledMessage = async () => {
|
||||||
|
await this.clickOnScheduledMessageButton.tap();
|
||||||
|
await waitFor(this.clickOnScheduledMessageButton).not.toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify the message is scheduled and user can see Info in channel or thread
|
||||||
|
* @param {boolean} thread - true if the message is scheduled in thread
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
verifyScheduledDraftInfoInChannel = async (thread = false) => {
|
||||||
|
await expect(this.scheduledDraftInfoInChannel).toBeVisible();
|
||||||
|
await expect(this.scheduledDraftInfoInChannel).toHaveText(`1 scheduled message in ${thread ? 'thread' : 'channel'}. See all.`);
|
||||||
|
};
|
||||||
|
|
||||||
|
closeScheduledMessageTooltip = async () => {
|
||||||
|
if (isIos()) {
|
||||||
|
await waitFor(this.scheduledDraftTooltipText).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
await this.scheduledDraftTooltipText.tap();
|
||||||
|
} else {
|
||||||
|
// The page re-renders and then opens the tooltip again. Wait for the tooltip to be stable and then tap it.
|
||||||
|
await wait(timeouts.TEN_SEC);
|
||||||
|
await this.scheduleDraftInforMessage.tap();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelScreen = new ChannelScreen();
|
const channelScreen = new ChannelScreen();
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ class ChannelInfoScreen {
|
||||||
noButton,
|
noButton,
|
||||||
yesButton,
|
yesButton,
|
||||||
} = Alert;
|
} = Alert;
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await expect(alertArchiveChannelTitle).toBeVisible();
|
await expect(alertArchiveChannelTitle).toBeVisible();
|
||||||
await expect(noButton).toBeVisible();
|
await expect(noButton).toBeVisible();
|
||||||
await expect(yesButton).toBeVisible();
|
await expect(yesButton).toBeVisible();
|
||||||
|
|
@ -141,7 +142,7 @@ class ChannelInfoScreen {
|
||||||
convertToPrivateChannel = async (channelDisplayName: string, {confirm = true} = {}) => {
|
convertToPrivateChannel = async (channelDisplayName: string, {confirm = true} = {}) => {
|
||||||
await this.scrollView.tap({x: 1, y: 1});
|
await this.scrollView.tap({x: 1, y: 1});
|
||||||
await this.scrollView.scroll(100, 'down');
|
await this.scrollView.scroll(100, 'down');
|
||||||
await waitFor(this.convertPrivateOption).toExist().withTimeout(timeouts.TWO_SEC);
|
await waitFor(this.convertPrivateOption).toBeVisible().whileElement(by.id(this.testID.scrollView)).scroll(50, 'down');
|
||||||
await this.convertPrivateOption.tap({x: 1, y: 1});
|
await this.convertPrivateOption.tap({x: 1, y: 1});
|
||||||
const {
|
const {
|
||||||
channelNowPrivateTitle,
|
channelNowPrivateTitle,
|
||||||
|
|
@ -205,6 +206,7 @@ class ChannelInfoScreen {
|
||||||
|
|
||||||
unarchiveChannel = async (alertUnarchiveChannelTitle: Detox.NativeElement, {confirm = true} = {}) => {
|
unarchiveChannel = async (alertUnarchiveChannelTitle: Detox.NativeElement, {confirm = true} = {}) => {
|
||||||
await waitFor(this.unarchiveChannelOption).toBeVisible().whileElement(by.id(this.testID.scrollView)).scroll(50, 'down');
|
await waitFor(this.unarchiveChannelOption).toBeVisible().whileElement(by.id(this.testID.scrollView)).scroll(50, 'down');
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await this.unarchiveChannelOption.tap({x: 1, y: 1});
|
await this.unarchiveChannelOption.tap({x: 1, y: 1});
|
||||||
const {
|
const {
|
||||||
noButton,
|
noButton,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class ChannelListScreen {
|
||||||
draftChannelInfo: 'draft_post.channel_info',
|
draftChannelInfo: 'draft_post.channel_info',
|
||||||
draftbuttonListScreen: 'channel_list.drafts.button',
|
draftbuttonListScreen: 'channel_list.drafts.button',
|
||||||
draftCountListScreen: 'channel_list.drafts.count',
|
draftCountListScreen: 'channel_list.drafts.count',
|
||||||
|
scheduledMessageCountListScreen: 'channel_list.scheduled_post.count',
|
||||||
teamItemPrefix: 'team_sidebar.team_list.team_item.',
|
teamItemPrefix: 'team_sidebar.team_list.team_item.',
|
||||||
channelListScreen: 'channel_list.screen',
|
channelListScreen: 'channel_list.screen',
|
||||||
serverIcon: 'channel_list.servers.server_icon',
|
serverIcon: 'channel_list.servers.server_icon',
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {expect} from 'detox';
|
||||||
|
|
||||||
class CreateDirectMessageScreen {
|
class CreateDirectMessageScreen {
|
||||||
testID = {
|
testID = {
|
||||||
|
selectedDMUserPrefix: 'create_direct_message.',
|
||||||
selectedUserPrefix: 'create_direct_message.selected_user.',
|
selectedUserPrefix: 'create_direct_message.selected_user.',
|
||||||
userItemPrefix: 'create_direct_message.user_list.user_item.',
|
userItemPrefix: 'create_direct_message.user_list.user_item.',
|
||||||
createDirectMessageScreen: 'create_direct_message.screen',
|
createDirectMessageScreen: 'create_direct_message.screen',
|
||||||
|
|
@ -20,8 +21,10 @@ class CreateDirectMessageScreen {
|
||||||
sectionUserList: 'create_direct_message.user_list.section_list',
|
sectionUserList: 'create_direct_message.user_list.section_list',
|
||||||
tutorialHighlight: 'tutorial_highlight',
|
tutorialHighlight: 'tutorial_highlight',
|
||||||
tutorialSwipeLeft: 'tutorial_swipe_left',
|
tutorialSwipeLeft: 'tutorial_swipe_left',
|
||||||
|
scheduledPostTooltipCloseButton: 'scheduled_post.tooltip.close.button',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
scheduledPostTooltipCloseButton = element(by.id(this.testID.scheduledPostTooltipCloseButton));
|
||||||
createDirectMessageScreen = element(by.id(this.testID.createDirectMessageScreen));
|
createDirectMessageScreen = element(by.id(this.testID.createDirectMessageScreen));
|
||||||
closeButton = element(by.id(this.testID.closeButton));
|
closeButton = element(by.id(this.testID.closeButton));
|
||||||
startButton = element(by.id(this.testID.startButton));
|
startButton = element(by.id(this.testID.startButton));
|
||||||
|
|
@ -41,6 +44,10 @@ class CreateDirectMessageScreen {
|
||||||
return element(by.id(`${this.testID.selectedUserPrefix}${userId}.display_name`));
|
return element(by.id(`${this.testID.selectedUserPrefix}${userId}.display_name`));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getSelectedDMUserDisplayName = (userId: string) => {
|
||||||
|
return element(by.id(`${this.testID.selectedDMUserPrefix}${userId}.display_name`));
|
||||||
|
};
|
||||||
|
|
||||||
getSelectedUserRemoveButton = (userId: string) => {
|
getSelectedUserRemoveButton = (userId: string) => {
|
||||||
return element(by.id(`${this.testID.selectedUserPrefix}${userId}.remove.button`));
|
return element(by.id(`${this.testID.selectedUserPrefix}${userId}.remove.button`));
|
||||||
};
|
};
|
||||||
|
|
@ -70,7 +77,7 @@ class CreateDirectMessageScreen {
|
||||||
await ChannelListScreen.headerPlusButton.tap();
|
await ChannelListScreen.headerPlusButton.tap();
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.ONE_SEC);
|
||||||
await ChannelListScreen.openDirectMessageItem.tap();
|
await ChannelListScreen.openDirectMessageItem.tap();
|
||||||
|
await wait(timeouts.TEN_SEC);
|
||||||
return this.toBeVisible();
|
return this.toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -80,14 +87,19 @@ class CreateDirectMessageScreen {
|
||||||
};
|
};
|
||||||
|
|
||||||
closeTutorial = async () => {
|
closeTutorial = async () => {
|
||||||
if (isIos()) {
|
try {
|
||||||
await waitFor(this.tutorialHighlight).toExist().withTimeout(timeouts.TEN_SEC);
|
if (isIos()) {
|
||||||
await this.tutorialSwipeLeft.tap();
|
await waitFor(this.tutorialHighlight).toExist().withTimeout(timeouts.HALF_MIN);
|
||||||
await expect(this.tutorialHighlight).not.toExist();
|
await this.tutorialSwipeLeft.tap();
|
||||||
} else {
|
await expect(this.tutorialHighlight).not.toExist();
|
||||||
await wait(timeouts.ONE_SEC);
|
} else {
|
||||||
await device.pressBack();
|
await wait(timeouts.ONE_SEC);
|
||||||
await wait(timeouts.ONE_SEC);
|
await device.pressBack();
|
||||||
|
await wait(timeouts.ONE_SEC);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Tutorial element not visible, skipping action:');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class CreateOrEditChannelScreen {
|
||||||
openEditChannel = async () => {
|
openEditChannel = async () => {
|
||||||
// # Open edit channel screen
|
// # Open edit channel screen
|
||||||
await ChannelInfoScreen.scrollView.tap({x: 1, y: 1});
|
await ChannelInfoScreen.scrollView.tap({x: 1, y: 1});
|
||||||
await ChannelInfoScreen.scrollView.scroll(100, 'down');
|
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||||
await ChannelInfoScreen.editChannelOption.tap();
|
await ChannelInfoScreen.editChannelOption.tap();
|
||||||
|
|
||||||
return this.toBeVisible();
|
return this.toBeVisible();
|
||||||
|
|
@ -96,6 +96,16 @@ class CreateOrEditChannelScreen {
|
||||||
await this.makePrivateToggledOn.tap();
|
await this.makePrivateToggledOn.tap();
|
||||||
await expect(this.makePrivateToggledOff).toBeVisible();
|
await expect(this.makePrivateToggledOff).toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
clickonCreateButton = async () => {
|
||||||
|
await this.createButton.tap();
|
||||||
|
try {
|
||||||
|
await ChannelScreen.scheduledPostTooltipCloseButton.tap();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Element not visible, skipping click');
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const createOrEditChannelScreen = new CreateOrEditChannelScreen();
|
const createOrEditChannelScreen = new CreateOrEditChannelScreen();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class DraftScreen {
|
||||||
draftMessageContent: 'draft_message',
|
draftMessageContent: 'draft_message',
|
||||||
draftScreen: 'global_drafts_list',
|
draftScreen: 'global_drafts_list',
|
||||||
draftTooltipCloseButton: 'draft.tooltip.close.button',
|
draftTooltipCloseButton: 'draft.tooltip.close.button',
|
||||||
draftPost: 'draft_post',
|
draftPost: 'draft_message',
|
||||||
draftSendButton: 'send_draft_button',
|
draftSendButton: 'send_draft_button',
|
||||||
draftEmptyTitle: 'drafts.empty.title',
|
draftEmptyTitle: 'drafts.empty.title',
|
||||||
requestACKIcon: 'drafts.requested_ack.icon',
|
requestACKIcon: 'drafts.requested_ack.icon',
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ class EditProfileScreen {
|
||||||
closeButton: 'close.edit_profile.button',
|
closeButton: 'close.edit_profile.button',
|
||||||
saveButton: 'edit_profile.save.button',
|
saveButton: 'edit_profile.save.button',
|
||||||
scrollView: 'edit_profile.scroll_view',
|
scrollView: 'edit_profile.scroll_view',
|
||||||
firstNameInput: 'edit_profile_form.first_name.input',
|
firstNameInput: 'edit_profile_form.firstName.input',
|
||||||
firstNameInputDisabled: 'edit_profile_form.first_name.input.disabled',
|
firstNameInputDisabled: 'edit_profile_form.firstName.input.disabled',
|
||||||
lastNameInput: 'edit_profile_form.last_name.input',
|
lastNameInput: 'edit_profile_form.lastName.input',
|
||||||
lastNameInputDisabled: 'edit_profile_form.last_name.input.disabled',
|
lastNameInputDisabled: 'edit_profile_form.lastName.input.disabled',
|
||||||
usernameInput: 'edit_profile_form.username.input',
|
usernameInput: 'edit_profile_form.username.input',
|
||||||
usernameInputDisabled: 'edit_profile_form.username.input.disabled',
|
usernameInputDisabled: 'edit_profile_form.username.input.disabled',
|
||||||
emailInput: 'edit_profile_form.email.input',
|
emailInput: 'edit_profile_form.email.input',
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import PushNotificationSettingsScreen from './push_notification_settings';
|
||||||
import ReactionsScreen from './reactions';
|
import ReactionsScreen from './reactions';
|
||||||
import RecentMentionsScreen from './recent_mentions';
|
import RecentMentionsScreen from './recent_mentions';
|
||||||
import SavedMessagesScreen from './saved_messages';
|
import SavedMessagesScreen from './saved_messages';
|
||||||
|
import ScheduleMessageScreen from './scheduled_message_screen';
|
||||||
import SearchMessagesScreen from './search_messages';
|
import SearchMessagesScreen from './search_messages';
|
||||||
import SelectTimezoneScreen from './select_timezone';
|
import SelectTimezoneScreen from './select_timezone';
|
||||||
import ServerScreen from './server';
|
import ServerScreen from './server';
|
||||||
|
|
@ -95,4 +96,5 @@ export {
|
||||||
TimezoneDisplaySettingsScreen,
|
TimezoneDisplaySettingsScreen,
|
||||||
UserProfileScreen,
|
UserProfileScreen,
|
||||||
DraftScreen,
|
DraftScreen,
|
||||||
|
ScheduleMessageScreen,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@ class InviteScreen {
|
||||||
return element(by.id(`${this.testID.selectedItemPrefix}.${id}`));
|
return element(by.id(`${this.testID.selectedItemPrefix}.${id}`));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getSelectedChip = () => {
|
||||||
|
return element(by.id('invite.selected_item.display_name'));
|
||||||
|
};
|
||||||
|
|
||||||
getSummaryReportSent = () => {
|
getSummaryReportSent = () => {
|
||||||
return element(by.id(`${this.testID.summaryReportPrefix}.sent`));
|
return element(by.id(`${this.testID.summaryReportPrefix}.sent`));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {ChannelListScreen, ServerScreen} from '@support/ui/screen';
|
import {ChannelListScreen, ServerScreen} from '@support/ui/screen';
|
||||||
import {timeouts, wait} from '@support/utils';
|
import {isAndroid, retryWithReload, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
class LoginScreen {
|
class LoginScreen {
|
||||||
|
|
@ -21,8 +21,10 @@ class LoginScreen {
|
||||||
forgotPasswordButton: 'login_form.forgot_password.button',
|
forgotPasswordButton: 'login_form.forgot_password.button',
|
||||||
signinButton: 'login_form.signin.button',
|
signinButton: 'login_form.signin.button',
|
||||||
signinButtonDisabled: 'login_form.signin.button.disabled',
|
signinButtonDisabled: 'login_form.signin.button.disabled',
|
||||||
|
loginFormInfoText: 'login_options.description.enter_credentials',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
loginFormInfoText = element(by.id(this.testID.loginFormInfoText));
|
||||||
loginScreen = element(by.id(this.testID.loginScreen));
|
loginScreen = element(by.id(this.testID.loginScreen));
|
||||||
backButton = element(by.id(this.testID.backButton));
|
backButton = element(by.id(this.testID.backButton));
|
||||||
titleLoginToAccount = element(by.id(this.testID.titleLoginToAccount));
|
titleLoginToAccount = element(by.id(this.testID.titleLoginToAccount));
|
||||||
|
|
@ -58,17 +60,20 @@ class LoginScreen {
|
||||||
await expect(this.loginScreen).not.toBeVisible();
|
await expect(this.loginScreen).not.toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
login = async (user: any = {}) => {
|
loginWithRetryIfStuck = async (user: any = {}) => {
|
||||||
await this.toBeVisible();
|
await this.toBeVisible();
|
||||||
|
|
||||||
await this.usernameInput.tap({x: 150, y: 10});
|
await this.usernameInput.tap({x: 150, y: 10});
|
||||||
await this.usernameInput.replaceText(user.newUser.email);
|
await this.usernameInput.replaceText(user.newUser.email);
|
||||||
await this.passwordInput.tap();
|
await this.passwordInput.tap();
|
||||||
await this.passwordInput.replaceText(user.newUser.password);
|
await this.passwordInput.replaceText(user.newUser.password);
|
||||||
await element(by.text(/^Log In to Your Account*$/)).tap();
|
await this.loginFormInfoText.tap();
|
||||||
await this.signinButton.tap();
|
await this.signinButton.tap();
|
||||||
|
|
||||||
await waitFor(ChannelListScreen.channelListScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
await waitFor(ChannelListScreen.channelListScreen).toBeVisible().withTimeout(isAndroid() ? timeouts.ONE_MIN : timeouts.HALF_MIN);
|
||||||
|
};
|
||||||
|
|
||||||
|
login = async (user: any = {}) => {
|
||||||
|
await retryWithReload(() => this.loginWithRetryIfStuck(user));
|
||||||
};
|
};
|
||||||
|
|
||||||
loginAsAdmin = async (user: any = {}) => {
|
loginAsAdmin = async (user: any = {}) => {
|
||||||
|
|
@ -78,9 +83,9 @@ class LoginScreen {
|
||||||
await this.usernameInput.replaceText(user.username);
|
await this.usernameInput.replaceText(user.username);
|
||||||
await this.passwordInput.tap();
|
await this.passwordInput.tap();
|
||||||
await this.passwordInput.replaceText(user.password);
|
await this.passwordInput.replaceText(user.password);
|
||||||
|
await this.loginFormInfoText.tap();
|
||||||
await this.signinButton.tap();
|
await this.signinButton.tap();
|
||||||
|
await waitFor(ChannelListScreen.channelListScreen).toBeVisible().withTimeout(isAndroid() ? timeouts.ONE_MIN : timeouts.HALF_MIN);
|
||||||
await waitFor(ChannelListScreen.channelListScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
167
detox/e2e/support/ui/screen/scheduled_message_screen.ts
Normal file
167
detox/e2e/support/ui/screen/scheduled_message_screen.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {Alert} from '@support/ui/component';
|
||||||
|
import {isIos, timeouts, wait} from '@support/utils';
|
||||||
|
import {expect} from 'detox';
|
||||||
|
|
||||||
|
class ScheduledMessageScreen {
|
||||||
|
testID = {
|
||||||
|
customDateTimePickerScreen: 'custom_date_time_picker',
|
||||||
|
deleteDraft: 'delete_draft',
|
||||||
|
rescheduleOption: 'rescheduled_draft',
|
||||||
|
scheduledTab: 'scheduled_post_tab',
|
||||||
|
scheduledTabBadgeCount: 'scheduled_post_count_badge',
|
||||||
|
scheduledMessageTooltipCloseButton: 'draft.tooltip.close.button',
|
||||||
|
scheduledMessageText: 'markdown_paragraph',
|
||||||
|
scheduledDraftTime: 'scheduled_post_header.scheduled_at',
|
||||||
|
};
|
||||||
|
|
||||||
|
selectDateButton = element(by.text(isIos()? 'Select Date': 'SELECT DATE'));
|
||||||
|
selectTimeButton = element(by.text(isIos()? 'Select Time': 'SELECT TIME'));
|
||||||
|
saveButton = element(by.text(isIos()? 'Save': 'SAVE'));
|
||||||
|
androidCalenderOkButton = element(by.text('OK'));
|
||||||
|
scheduledDraftTime = element(by.id(this.testID.scheduledDraftTime));
|
||||||
|
customDateTimePickerScreen = element(by.id(this.testID.customDateTimePickerScreen));
|
||||||
|
rescheduleOption = element(by.id(this.testID.rescheduleOption));
|
||||||
|
deleteDraftSwipeAction = element(by.text('Delete draft'));
|
||||||
|
scheduledMessageText = element(by.id(this.testID.scheduledMessageText));
|
||||||
|
deleteDraft = element(by.id(this.testID.deleteDraft));
|
||||||
|
|
||||||
|
scheduledMessageTooltipCloseButton = {
|
||||||
|
tap: async () => {
|
||||||
|
await element(by.id(this.testID.scheduledMessageTooltipCloseButton)).tap();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
clickScheduledTab = async () => {
|
||||||
|
await element(by.id(this.testID.scheduledTab)).tap();
|
||||||
|
try {
|
||||||
|
await this.scheduledMessageTooltipCloseButton.tap();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Element not visible, skipping click');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
verifyCountOnScheduledTab = async (count: string) => {
|
||||||
|
await expect(element(by.id(this.testID.scheduledTabBadgeCount))).toHaveText(count);
|
||||||
|
};
|
||||||
|
|
||||||
|
assertScheduledMessageExists = async (scheduledMessageText: string) => {
|
||||||
|
await waitFor(this.scheduledMessageText).toBeVisible().withTimeout(500);
|
||||||
|
await expect(element(by.text(scheduledMessageText))).toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteDraftPost = async (deleteAction: any) => {
|
||||||
|
await expect(deleteAction).toBeVisible();
|
||||||
|
await deleteAction.tap();
|
||||||
|
await waitFor(Alert.deleteScheduledMessageButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||||
|
await Alert.deleteScheduledMessageButton.tap();
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteDraftPostFromSwipeActions = async () => {
|
||||||
|
await this.deleteDraftPost(this.deleteDraftSwipeAction);
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteScheduledMessageFromDraftActions = async () => {
|
||||||
|
await this.deleteDraftPost(this.deleteDraft);
|
||||||
|
};
|
||||||
|
|
||||||
|
selectDateTime = async () => {
|
||||||
|
await this.selectDateButton.tap();
|
||||||
|
await this.selectTimeButton.tap();
|
||||||
|
await this.saveButton.tap();
|
||||||
|
};
|
||||||
|
|
||||||
|
clickRescheduleOption = async () => {
|
||||||
|
await this.rescheduleOption.tap();
|
||||||
|
if (isIos()) {
|
||||||
|
await waitFor(this.customDateTimePickerScreen).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||||
|
await expect(this.customDateTimePickerScreen).toBeVisible();
|
||||||
|
} else {
|
||||||
|
// to close native calander picker
|
||||||
|
await device.pressBack();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the element has the expected text.
|
||||||
|
* @param expectedText - The text you expect in the element
|
||||||
|
*/
|
||||||
|
assertScheduleTimeTextIsVisible = async (expectedText: string) => {
|
||||||
|
const attr = await this.scheduledDraftTime.getAttributes();
|
||||||
|
const actualText = 'text' in attr ? attr.text : null;
|
||||||
|
|
||||||
|
if (this.normalize(actualText || '') !== this.normalize(expectedText)) {
|
||||||
|
throw new Error(`Expected text "${expectedText}" but found "${actualText}"`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getRoundedTime = async (): Promise<Date> => {
|
||||||
|
const now = new Date();
|
||||||
|
const minutes = now.getMinutes();
|
||||||
|
|
||||||
|
if (minutes === 0 || minutes === 30) {
|
||||||
|
// Waiting 60 seconds to avoid edge case at HH:00 or HH:30...
|
||||||
|
await wait(timeouts.ONE_MIN);
|
||||||
|
return this.getRoundedTime(); // Retry after wait
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minutes < 30) {
|
||||||
|
now.setMinutes(30, 0, 0);
|
||||||
|
} else {
|
||||||
|
now.setHours(now.getHours() + 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return now;
|
||||||
|
};
|
||||||
|
|
||||||
|
nextMonday = async () => {
|
||||||
|
const today = new Date();
|
||||||
|
const dayOfWeek = today.getDay();
|
||||||
|
const daysUntilNextMonday = (8 - dayOfWeek) % 7 || 7;
|
||||||
|
|
||||||
|
const nextMonday = new Date(today);
|
||||||
|
nextMonday.setDate(today.getDate() + daysUntilNextMonday);
|
||||||
|
nextMonday.setHours(9, 0, 0, 0); // Hardcoded 9:00 AM
|
||||||
|
|
||||||
|
const locale = 'en-US';
|
||||||
|
const dateOptions: Intl.DateTimeFormatOptions = {month: 'short', day: 'numeric'};
|
||||||
|
const timeOptions: Intl.DateTimeFormatOptions = {hour: 'numeric', minute: '2-digit', hour12: true};
|
||||||
|
|
||||||
|
const datePart = nextMonday.toLocaleDateString(locale, dateOptions);
|
||||||
|
const timePart = nextMonday.toLocaleTimeString(locale, timeOptions);
|
||||||
|
|
||||||
|
return this.normalize(`Send on ${datePart}, ${timePart}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
currentDay = async () => {
|
||||||
|
const adjustedTime = await this.getRoundedTime();
|
||||||
|
|
||||||
|
const locale = 'en-US';
|
||||||
|
const dateOptions: Intl.DateTimeFormatOptions = {month: 'short', day: 'numeric'};
|
||||||
|
const timeOptions: Intl.DateTimeFormatOptions = {
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const datePart = adjustedTime.toLocaleDateString(locale, dateOptions);
|
||||||
|
const timePart = adjustedTime.toLocaleTimeString(locale, timeOptions);
|
||||||
|
|
||||||
|
return this.normalize(`Send on ${datePart}, ${timePart}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes text by trimming, collapsing spaces, and replacing narrow no-break spaces.
|
||||||
|
*/
|
||||||
|
normalize = (s: string) =>
|
||||||
|
s.replace(/\u202F/g, ' '). // replace narrow no-break spaces
|
||||||
|
replace(/\s+/g, ' '). // collapse multiple spaces
|
||||||
|
trim();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduledMessageScreen = new ScheduledMessageScreen();
|
||||||
|
export default scheduledMessageScreen;
|
||||||
|
|
@ -68,7 +68,7 @@ class SearchMessagesScreen {
|
||||||
};
|
};
|
||||||
|
|
||||||
getRecentSearchItemRemoveButton = (searchTerm: string) => {
|
getRecentSearchItemRemoveButton = (searchTerm: string) => {
|
||||||
return element(by.id(`search.recent_item.${searchTerm}.remove.button`));
|
return element(by.id(`search.recent_item.${searchTerm}.remove.button`)).atIndex(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
toBeVisible = async () => {
|
toBeVisible = async () => {
|
||||||
|
|
@ -87,6 +87,7 @@ class SearchMessagesScreen {
|
||||||
|
|
||||||
openPostOptionsFor = async (postId: string, text: string) => {
|
openPostOptionsFor = async (postId: string, text: string) => {
|
||||||
const {postListPostItem} = this.getPostListPostItem(postId, text);
|
const {postListPostItem} = this.getPostListPostItem(postId, text);
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await expect(postListPostItem).toBeVisible();
|
await expect(postListPostItem).toBeVisible();
|
||||||
|
|
||||||
// # Open post options
|
// # Open post options
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class ServerScreen {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await waitFor(this.usernameInput).toExist().withTimeout(timeouts.TEN_SEC);
|
await waitFor(this.usernameInput).toExist().withTimeout(isAndroid()? timeouts.ONE_MIN : timeouts.HALF_MIN);
|
||||||
};
|
};
|
||||||
|
|
||||||
close = async () => {
|
close = async () => {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class ThreadScreen {
|
||||||
|
|
||||||
back = async () => {
|
back = async () => {
|
||||||
await this.backButton.tap();
|
await this.backButton.tap();
|
||||||
await expect(this.threadScreen).not.toBeVisible();
|
await waitFor(this.threadScreen).not.toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
};
|
};
|
||||||
|
|
||||||
openPostOptionsFor = async (postId: string, text: string) => {
|
openPostOptionsFor = async (postId: string, text: string) => {
|
||||||
|
|
@ -112,6 +112,17 @@ class ThreadScreen {
|
||||||
await this.tapSendButton();
|
await this.tapSendButton();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enterMessageToSchedule = async (message: string) => {
|
||||||
|
await this.postInput.tap();
|
||||||
|
await this.postInput.clearText();
|
||||||
|
await this.postInput.replaceText(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
longPressSendButton = async () => {
|
||||||
|
// # Long press send button
|
||||||
|
await this.sendButton.longPress();
|
||||||
|
};
|
||||||
|
|
||||||
tapSendButton = async () => {
|
tapSendButton = async () => {
|
||||||
// # Tap send button
|
// # Tap send button
|
||||||
await this.sendButton.tap();
|
await this.sendButton.tap();
|
||||||
|
|
|
||||||
|
|
@ -77,3 +77,28 @@ export const timeouts = {
|
||||||
FOUR_MIN: MINUTE * 4,
|
FOUR_MIN: MINUTE * 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry a function with reload
|
||||||
|
* @param {function} func - function to retry
|
||||||
|
* @param {number} retries - number of retries
|
||||||
|
* @return {Promise<void>} - promise that resolves when the function succeeds
|
||||||
|
* @throws {Error} - if the function fails after the specified number of retries
|
||||||
|
*/
|
||||||
|
export async function retryWithReload(func: () => Promise<void>, retries: number = 2): Promise<void> {
|
||||||
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await func();
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
if (attempt < retries) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await device.reloadReactNative();
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await new Promise((res) => setTimeout(res, 3000));
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ describe('Account - Edit Profile', () => {
|
||||||
await expect(EditProfileScreen.closeButton).toBeVisible();
|
await expect(EditProfileScreen.closeButton).toBeVisible();
|
||||||
await expect(EditProfileScreen.saveButton).toBeVisible();
|
await expect(EditProfileScreen.saveButton).toBeVisible();
|
||||||
await expect(EditProfileScreen.getEditProfilePicture(testUser.id)).toBeVisible();
|
await expect(EditProfileScreen.getEditProfilePicture(testUser.id)).toBeVisible();
|
||||||
|
await waitFor(EditProfileScreen.firstNameInput).toExist().withTimeout(2000);
|
||||||
if (isIos()) {
|
if (isIos()) {
|
||||||
await expect(EditProfileScreen.firstNameInput).toHaveValue(testUser.first_name);
|
await expect(EditProfileScreen.firstNameInput).toHaveValue(testUser.first_name);
|
||||||
await expect(EditProfileScreen.lastNameInput).toHaveValue(testUser.last_name);
|
await expect(EditProfileScreen.lastNameInput).toHaveValue(testUser.last_name);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import {
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
SettingsScreen,
|
SettingsScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {getRandomId, isIos} from '@support/utils';
|
import {getRandomId, isIos, timeouts} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Account - Settings - Mention Notification Settings', () => {
|
describe('Account - Settings - Mention Notification Settings', () => {
|
||||||
|
|
@ -84,7 +84,7 @@ describe('Account - Settings - Mention Notification Settings', () => {
|
||||||
const triggerMentionKeyword = await MentionNotificationSettingsScreen.getKeywordTriggerElement(keywords);
|
const triggerMentionKeyword = await MentionNotificationSettingsScreen.getKeywordTriggerElement(keywords);
|
||||||
await expect(triggerMentionKeyword).toHaveText(keywords.replace(/ /g, '').toLowerCase());
|
await expect(triggerMentionKeyword).toHaveText(keywords.replace(/ /g, '').toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
await expect(MentionNotificationSettingsScreen.keywordsInput).toHaveText(keywords.replace(/ /g, '').toLowerCase());
|
await waitFor(element(by.text(keywords.replace(/ /g, '').toLowerCase()))).toExist().withTimeout(timeouts.TWO_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Switch toggles back to original state, clear keywords, tap on back button, and go back to mention notifications screen
|
// # Switch toggles back to original state, clear keywords, tap on back button, and go back to mention notifications screen
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ describe('Account - Settings - Theme Display Settings', () => {
|
||||||
it('MM-T5111_2 - should be able to select a theme and save by tapping navigation back button', async () => {
|
it('MM-T5111_2 - should be able to select a theme and save by tapping navigation back button', async () => {
|
||||||
// # Tap on a sapphire option and tap on back button
|
// # Tap on a sapphire option and tap on back button
|
||||||
await ThemeDisplaySettingsScreen.sapphireOption.tap();
|
await ThemeDisplaySettingsScreen.sapphireOption.tap();
|
||||||
await ThemeDisplaySettingsScreen.back();
|
|
||||||
|
|
||||||
// * Verify on display settings screen and sapphire is set
|
// * Verify on display settings screen and sapphire is set
|
||||||
await DisplaySettingsScreen.toBeVisible();
|
await DisplaySettingsScreen.toBeVisible();
|
||||||
|
|
@ -80,7 +79,6 @@ describe('Account - Settings - Theme Display Settings', () => {
|
||||||
|
|
||||||
// # Tap on denim option and tap on back button
|
// # Tap on denim option and tap on back button
|
||||||
await ThemeDisplaySettingsScreen.denimOption.tap();
|
await ThemeDisplaySettingsScreen.denimOption.tap();
|
||||||
await ThemeDisplaySettingsScreen.back();
|
|
||||||
|
|
||||||
// * Verify on display settings screen and denim is set
|
// * Verify on display settings screen and denim is set
|
||||||
await DisplaySettingsScreen.toBeVisible();
|
await DisplaySettingsScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ describe('Autocomplete - At-Mention', () => {
|
||||||
|
|
||||||
// # Log in to server
|
// # Log in to server
|
||||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||||
await LoginScreen.login(testUser);
|
await LoginScreen.login(user);
|
||||||
|
|
||||||
// * Verify on channel list screen
|
// * Verify on channel list screen
|
||||||
await ChannelListScreen.toBeVisible();
|
await ChannelListScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ describe('Autocomplete - Slash Suggestion', () => {
|
||||||
await slashSuggestionAutocomplete.tap();
|
await slashSuggestionAutocomplete.tap();
|
||||||
|
|
||||||
// * Verify slash suggestion list disappears
|
// * Verify slash suggestion list disappears
|
||||||
await expect(Autocomplete.flatSlashSuggestionList).not.toBeVisible();
|
await waitFor(Autocomplete.flatSlashSuggestionList).not.toExist().withTimeout(timeouts.FOUR_SEC);
|
||||||
|
|
||||||
// # Type in "/" again to re-activate slash suggestion list
|
// # Type in "/" again to re-activate slash suggestion list
|
||||||
await ChannelScreen.postInput.typeText('/');
|
await ChannelScreen.postInput.typeText('/');
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ describe('Channels - Browse Channels', () => {
|
||||||
|
|
||||||
// # Tap on the new public channel item
|
// # Tap on the new public channel item
|
||||||
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
||||||
|
await BrowseChannelsScreen.dismissScheduledPostTooltip();
|
||||||
|
|
||||||
// * Verify on newly joined public channel screen
|
// * Verify on newly joined public channel screen
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ describe('Channels - Channel Info', () => {
|
||||||
await expect(ChannelInfoScreen.muteAction).toBeVisible();
|
await expect(ChannelInfoScreen.muteAction).toBeVisible();
|
||||||
await expect(ChannelInfoScreen.joinStartCallAction).toBeVisible();
|
await expect(ChannelInfoScreen.joinStartCallAction).toBeVisible();
|
||||||
await expect(ChannelInfoScreen.ignoreMentionsOptionToggledOff).toBeVisible();
|
await expect(ChannelInfoScreen.ignoreMentionsOptionToggledOff).toBeVisible();
|
||||||
|
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||||
await expect(ChannelInfoScreen.pinnedMessagesOption).toBeVisible();
|
await expect(ChannelInfoScreen.pinnedMessagesOption).toBeVisible();
|
||||||
await expect(ChannelInfoScreen.copyChannelLinkOption).toBeVisible();
|
await expect(ChannelInfoScreen.copyChannelLinkOption).toBeVisible();
|
||||||
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ describe('Channels - Channel List', () => {
|
||||||
it('MM-T4728_2 - should be able to switch between channels', async () => {
|
it('MM-T4728_2 - should be able to switch between channels', async () => {
|
||||||
// # Tap on a first channel
|
// # Tap on a first channel
|
||||||
await ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name).tap();
|
await ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name).tap();
|
||||||
|
await ChannelScreen.closeScheduledMessageTooltip();
|
||||||
|
|
||||||
// * Verify on first channel
|
// * Verify on first channel
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,18 @@ import {
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
ChannelInfoScreen,
|
ChannelInfoScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {getAdminAccount, getRandomId, timeouts} from '@support/utils';
|
import {getAdminAccount, getRandomId, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Channels - Convert to Private Channel', () => {
|
describe('Channels - Convert to Private Channel', () => {
|
||||||
const siteOneDisplayName = 'Server 1';
|
const siteOneDisplayName = 'Server 1';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
|
||||||
// # Log in to server as admin
|
// # Log in to server as admin
|
||||||
await ServerScreen.connectToServer(siteOneUrl, siteOneDisplayName);
|
await ServerScreen.connectToServer(siteOneUrl, siteOneDisplayName);
|
||||||
await LoginScreen.loginAsAdmin(getAdminAccount());
|
await LoginScreen.loginAsAdmin(getAdminAccount());
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
@ -45,6 +47,8 @@ describe('Channels - Convert to Private Channel', () => {
|
||||||
await CreateOrEditChannelScreen.openCreateChannel();
|
await CreateOrEditChannelScreen.openCreateChannel();
|
||||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
||||||
await CreateOrEditChannelScreen.createButton.tap();
|
await CreateOrEditChannelScreen.createButton.tap();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
await ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount.tap();
|
||||||
await ChannelInfoScreen.open();
|
await ChannelInfoScreen.open();
|
||||||
await ChannelInfoScreen.convertToPrivateChannel(channelDisplayName, {confirm: true});
|
await ChannelInfoScreen.convertToPrivateChannel(channelDisplayName, {confirm: true});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {getRandomId, isIos} from '@support/utils';
|
import {getRandomId, isIos, timeouts} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Channels - Create Channel and Edit Channel Header', () => {
|
describe('Channels - Create Channel and Edit Channel Header', () => {
|
||||||
|
|
@ -75,6 +75,8 @@ describe('Channels - Create Channel and Edit Channel Header', () => {
|
||||||
await CreateOrEditChannelScreen.purposeInput.replaceText(purpose);
|
await CreateOrEditChannelScreen.purposeInput.replaceText(purpose);
|
||||||
await CreateOrEditChannelScreen.headerInput.replaceText(header);
|
await CreateOrEditChannelScreen.headerInput.replaceText(header);
|
||||||
await CreateOrEditChannelScreen.createButton.tap();
|
await CreateOrEditChannelScreen.createButton.tap();
|
||||||
|
await waitFor(ChannelScreen.scheduledPostTooltipCloseButton).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||||
|
await ChannelScreen.scheduledPostTooltipCloseButton.tap();
|
||||||
|
|
||||||
// * Verify on newly created public channel
|
// * Verify on newly created public channel
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
|
import {timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Channels - Create Direct Message', () => {
|
describe('Channels - Create Direct Message', () => {
|
||||||
|
|
@ -87,10 +88,12 @@ describe('Channels - Create Direct Message', () => {
|
||||||
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
||||||
|
|
||||||
// * Verify the new user is selected
|
// * Verify the new user is selected
|
||||||
await expect(CreateDirectMessageScreen.getSelectedUserDisplayName(newUser.id)).toBeVisible();
|
await expect(CreateDirectMessageScreen.getSelectedDMUserDisplayName(newUser.id)).toBeVisible();
|
||||||
|
|
||||||
// # Tap on start button
|
// # Tap on start button
|
||||||
await CreateDirectMessageScreen.startButton.tap();
|
await CreateDirectMessageScreen.startButton.tap();
|
||||||
|
await waitFor(ChannelScreen.scheduledPostTooltipCloseButton).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
await ChannelScreen.scheduledPostTooltipCloseButton.tap();
|
||||||
|
|
||||||
// * Verify on direct message channel screen for the new user
|
// * Verify on direct message channel screen for the new user
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
@ -123,17 +126,21 @@ describe('Channels - Create Direct Message', () => {
|
||||||
// # Open create direct message screen, search for the first new user and tap on the first new user item
|
// # Open create direct message screen, search for the first new user and tap on the first new user item
|
||||||
await CreateDirectMessageScreen.open();
|
await CreateDirectMessageScreen.open();
|
||||||
await CreateDirectMessageScreen.searchInput.replaceText(firstNewUser.username);
|
await CreateDirectMessageScreen.searchInput.replaceText(firstNewUser.username);
|
||||||
|
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.ONE_SEC);
|
||||||
await CreateDirectMessageScreen.getUserItem(firstNewUser.id).tap();
|
await CreateDirectMessageScreen.getUserItem(firstNewUser.id).tap();
|
||||||
|
|
||||||
// * Verify the first new user is selected
|
// * Verify the first new user is selected
|
||||||
await expect(CreateDirectMessageScreen.getSelectedUserDisplayName(firstNewUser.id)).toBeVisible();
|
await expect(CreateDirectMessageScreen.getSelectedDMUserDisplayName(firstNewUser.id)).toBeVisible();
|
||||||
|
|
||||||
// # Search for the second new user and tap on the second new user item
|
// # Search for the second new user and tap on the second new user item
|
||||||
await CreateDirectMessageScreen.searchInput.replaceText(secondNewUser.username);
|
await CreateDirectMessageScreen.searchInput.replaceText(secondNewUser.username);
|
||||||
|
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.ONE_SEC);
|
||||||
await CreateDirectMessageScreen.getUserItem(secondNewUser.id).tap();
|
await CreateDirectMessageScreen.getUserItem(secondNewUser.id).tap();
|
||||||
|
|
||||||
// * Verify the second new user is selected
|
// * Verify the second new user is selected
|
||||||
await expect(CreateDirectMessageScreen.getSelectedUserDisplayName(secondNewUser.id)).toBeVisible();
|
await expect(CreateDirectMessageScreen.getSelectedDMUserDisplayName(secondNewUser.id)).toBeVisible();
|
||||||
|
|
||||||
// # Tap on start button
|
// # Tap on start button
|
||||||
await CreateDirectMessageScreen.startButton.tap();
|
await CreateDirectMessageScreen.startButton.tap();
|
||||||
|
|
@ -154,6 +161,8 @@ describe('Channels - Create Direct Message', () => {
|
||||||
const searchTerm = 'blahblahblahblah';
|
const searchTerm = 'blahblahblahblah';
|
||||||
await CreateDirectMessageScreen.open();
|
await CreateDirectMessageScreen.open();
|
||||||
await CreateDirectMessageScreen.searchInput.replaceText(searchTerm);
|
await CreateDirectMessageScreen.searchInput.replaceText(searchTerm);
|
||||||
|
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.ONE_SEC);
|
||||||
|
|
||||||
// * Verify empty search state for create direct message
|
// * Verify empty search state for create direct message
|
||||||
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {isIos} from '@support/utils';
|
import {isAndroid, isIos, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Channels - Edit Channel', () => {
|
describe('Channels - Edit Channel', () => {
|
||||||
|
|
@ -112,6 +112,10 @@ describe('Channels - Edit Channel', () => {
|
||||||
await ChannelInfoScreen.toBeVisible();
|
await ChannelInfoScreen.toBeVisible();
|
||||||
await expect(ChannelInfoScreen.publicPrivateTitleDisplayName).toHaveText(`${testChannel.display_name} name`);
|
await expect(ChannelInfoScreen.publicPrivateTitleDisplayName).toHaveText(`${testChannel.display_name} name`);
|
||||||
await expect(ChannelInfoScreen.publicPrivateTitlePurpose).toHaveText(`Channel purpose: ${testChannel.display_name.toLowerCase()} purpose`);
|
await expect(ChannelInfoScreen.publicPrivateTitlePurpose).toHaveText(`Channel purpose: ${testChannel.display_name.toLowerCase()} purpose`);
|
||||||
|
|
||||||
|
if (isAndroid()) {
|
||||||
|
await ChannelInfoScreen.scrollView.scrollTo('top');
|
||||||
|
}
|
||||||
await expect(element(by.text(`Channel header: ${testChannel.display_name.toLowerCase()}\nheader1\nheader2`))).toBeVisible();
|
await expect(element(by.text(`Channel header: ${testChannel.display_name.toLowerCase()}\nheader1\nheader2`))).toBeVisible();
|
||||||
|
|
||||||
// # Go back to channel screen
|
// # Go back to channel screen
|
||||||
|
|
@ -154,8 +158,11 @@ describe('Channels - Edit Channel', () => {
|
||||||
await CreateDirectMessageScreen.searchInput.replaceText(testOtherUser1.username);
|
await CreateDirectMessageScreen.searchInput.replaceText(testOtherUser1.username);
|
||||||
await CreateDirectMessageScreen.getUserItem(testOtherUser1.id).tap();
|
await CreateDirectMessageScreen.getUserItem(testOtherUser1.id).tap();
|
||||||
await CreateDirectMessageScreen.searchInput.replaceText(testOtherUser2.username);
|
await CreateDirectMessageScreen.searchInput.replaceText(testOtherUser2.username);
|
||||||
|
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await CreateDirectMessageScreen.getUserItem(testOtherUser2.id).tap();
|
await CreateDirectMessageScreen.getUserItem(testOtherUser2.id).tap();
|
||||||
await CreateDirectMessageScreen.startButton.tap();
|
await CreateDirectMessageScreen.startButton.tap();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// * Verify on group message channel screen
|
// * Verify on group message channel screen
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {timeouts, wait} from '@support/utils';
|
import {isIos, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Channels - Find Channels', () => {
|
describe('Channels - Find Channels', () => {
|
||||||
|
|
@ -68,7 +68,8 @@ describe('Channels - Find Channels', () => {
|
||||||
await FindChannelsScreen.close();
|
await FindChannelsScreen.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4907_2 - should be able to find and navigate to a public channel', async () => {
|
// TODO: Uncomment this test when the issue is fixed https://mattermost.atlassian.net/browse/MM-63814
|
||||||
|
(device.getPlatform() === 'ios' ? it : it.skip)('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
|
// # Open find channels screen and search for a public channel to navigate to
|
||||||
await FindChannelsScreen.open();
|
await FindChannelsScreen.open();
|
||||||
await FindChannelsScreen.searchInput.replaceText(testChannel.display_name);
|
await FindChannelsScreen.searchInput.replaceText(testChannel.display_name);
|
||||||
|
|
@ -98,7 +99,7 @@ describe('Channels - Find Channels', () => {
|
||||||
await FindChannelsScreen.close();
|
await FindChannelsScreen.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4907_4 - should be able to find direct and group message channels', async () => {
|
(isIos() ? it : it.skip)('MM-T4907_4 - should be able to find direct and group message channels', async () => {
|
||||||
// # Create direct and group message channels, open find channels screen, and search for the direct message channel
|
// # Create direct and group message channels, open find channels screen, and search for the direct message channel
|
||||||
const {user: testOtherUser1} = await User.apiCreateUser(siteOneUrl, {prefix: 'a'});
|
const {user: testOtherUser1} = await User.apiCreateUser(siteOneUrl, {prefix: 'a'});
|
||||||
await Team.apiAddUserToTeam(siteOneUrl, testOtherUser1.id, testTeam.id);
|
await Team.apiAddUserToTeam(siteOneUrl, testOtherUser1.id, testTeam.id);
|
||||||
|
|
@ -127,7 +128,7 @@ describe('Channels - Find Channels', () => {
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4907_5 - should be able to find an archived channel', async () => {
|
(device.getPlatform() === 'ios' ? it : it.skip)('MM-T4907_5 - should be able to find an archived channel', async () => {
|
||||||
// # Archive a channel, open find channels screen, and search for the archived channel
|
// # Archive a channel, open find channels screen, and search for the archived channel
|
||||||
const {channel: archivedChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
const {channel: archivedChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
||||||
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, archivedChannel.id);
|
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, archivedChannel.id);
|
||||||
|
|
@ -171,6 +172,12 @@ describe('Channels - Find Channels', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function verifyDetailsOnChannelScreen(display_name: string) {
|
async function verifyDetailsOnChannelScreen(display_name: string) {
|
||||||
|
try {
|
||||||
|
await ChannelScreen.scheduledPostTooltipCloseButton.tap();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Element not visible, skipping click');
|
||||||
|
}
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
await expect(ChannelScreen.headerTitle).toHaveText(display_name);
|
await expect(ChannelScreen.headerTitle).toHaveText(display_name);
|
||||||
await expect(ChannelScreen.introDisplayName).toHaveText(display_name);
|
await expect(ChannelScreen.introDisplayName).toHaveText(display_name);
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,18 @@ import {
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
ChannelInfoScreen,
|
ChannelInfoScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {getAdminAccount, getRandomId, timeouts} from '@support/utils';
|
import {getAdminAccount, getRandomId, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Channels - Unarchive Channel', () => {
|
describe('Channels - Unarchive Channel', () => {
|
||||||
const serverOneDisplayName = 'Server 1';
|
const serverOneDisplayName = 'Server 1';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
|
||||||
// # Log in to server as admin
|
// # Log in to server as admin
|
||||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||||
await LoginScreen.loginAsAdmin(getAdminAccount());
|
await LoginScreen.loginAsAdmin(getAdminAccount());
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
@ -45,6 +47,9 @@ describe('Channels - Unarchive Channel', () => {
|
||||||
await CreateOrEditChannelScreen.openCreateChannel();
|
await CreateOrEditChannelScreen.openCreateChannel();
|
||||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
||||||
await CreateOrEditChannelScreen.createButton.tap();
|
await CreateOrEditChannelScreen.createButton.tap();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
await expect(ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount).toBeVisible();
|
||||||
|
await ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount.tap();
|
||||||
await ChannelInfoScreen.open();
|
await ChannelInfoScreen.open();
|
||||||
await ChannelInfoScreen.archivePublicChannel({confirm: true});
|
await ChannelInfoScreen.archivePublicChannel({confirm: true});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ describe('Messaging - At-Mention', () => {
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('MM-T4874_2 - should display confirmation dialog when posting @all, @channel, and @here -- UNSTABLE', async () => {
|
it('MM-T4874_2 - should display confirmation dialog when posting @all, @channel, and @here', async () => {
|
||||||
// # Add more users to the channel, open a channel screen, and post @all
|
// # Add more users to the channel, open a channel screen, and post @all
|
||||||
[...Array(3).keys()].forEach(async (key) => {
|
[...Array(3).keys()].forEach(async (key) => {
|
||||||
const {user} = await User.apiCreateUser(siteOneUrl, {prefix: `a-${key}-`});
|
const {user} = await User.apiCreateUser(siteOneUrl, {prefix: `a-${key}-`});
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ describe('Messaging - Emojis and Reactions', () => {
|
||||||
await EmojiPickerScreen.open();
|
await EmojiPickerScreen.open();
|
||||||
|
|
||||||
// * Verify emojis exist in recently used section
|
// * Verify emojis exist in recently used section
|
||||||
await expect(element(by.text('RECENTLY USED'))).toExist();
|
await expect(element(by.text('RECENTLY USED')).atIndex(0)).toBeVisible();
|
||||||
await expect(element(by.text('🦊')).atIndex(0)).toExist();
|
await expect(element(by.text('🦊')).atIndex(0)).toExist();
|
||||||
await expect(element(by.text('🐶')).atIndex(0)).toExist();
|
await expect(element(by.text('🐶')).atIndex(0)).toExist();
|
||||||
|
|
||||||
|
|
@ -175,5 +175,9 @@ describe('Messaging - Emojis and Reactions', () => {
|
||||||
// * Verify empty search state for emoji picker
|
// * Verify empty search state for emoji picker
|
||||||
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
||||||
await expect(element(by.text('Check the spelling or try another search.'))).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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import {
|
||||||
PostOptionsScreen,
|
PostOptionsScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {getRandomId, timeouts} from '@support/utils';
|
import {getRandomId, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Messaging - Follow and Unfollow Message', () => {
|
describe('Messaging - Follow and Unfollow Message', () => {
|
||||||
|
|
@ -62,19 +62,21 @@ describe('Messaging - Follow and Unfollow Message', () => {
|
||||||
await expect(postListPostItem).toBeVisible();
|
await expect(postListPostItem).toBeVisible();
|
||||||
|
|
||||||
// # Open post options for message and tap on follow message option
|
// # Open post options for message and tap on follow message option
|
||||||
await ChannelScreen.openPostOptionsFor(post.id, message);
|
await postListPostItem.longPress(timeouts.ONE_SEC);
|
||||||
await PostOptionsScreen.followThreadOption.tap();
|
await PostOptionsScreen.followThreadOption.tap();
|
||||||
|
|
||||||
// * Verify message is followed by user via post footer
|
// * Verify message is followed by user via post footer
|
||||||
const {postListPostItemFooterFollowingButton} = ChannelScreen.getPostListPostItem(post.id, message);
|
const {postListPostItemFooterFollowingButton} = ChannelScreen.getPostListPostItem(post.id, message);
|
||||||
await waitFor(postListPostItemFooterFollowingButton).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
await waitFor(postListPostItemFooterFollowingButton).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
|
||||||
// # Open post options for message and tap on unfollow message option
|
// # Open post options for message and tap on unfollow message option
|
||||||
await ChannelScreen.openPostOptionsFor(post.id, message);
|
await ChannelScreen.openPostOptionsFor(post.id, message);
|
||||||
await PostOptionsScreen.followingThreadOption.tap();
|
await PostOptionsScreen.followingThreadOption.tap();
|
||||||
|
|
||||||
// * Verify message is not followed by user via post footer
|
// * Verify message is not followed by user via post footer
|
||||||
await waitFor(postListPostItemFooterFollowingButton).not.toBeVisible().withTimeout(timeouts.TWO_SEC);
|
await waitFor(postListPostItemFooterFollowingButton).not.toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,8 @@ describe('Messaging - Markdown Latex', () => {
|
||||||
await HomeScreen.logout();
|
await HomeScreen.logout();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4900_1 - should be able to display markdown latex code block', async () => {
|
// TODO: Uncomment the test when the issue is fixed https://mattermost.atlassian.net/browse/MM-63817
|
||||||
|
it.skip('MM-T4900_1 - should be able to display markdown latex code block', async () => {
|
||||||
// # Open a channel screen and post a markdown latex code block
|
// # Open a channel screen and post a markdown latex code block
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
const message = 'X_k = \sum_{n=0}^{2N-1} x_n \cos \left[\frac{\pi}{N} \left(n+\frac{1}{2}+\frac{N}{2}\right) \left(k+\frac{1}{2}\right) \right]';
|
const message = 'X_k = \sum_{n=0}^{2N-1} x_n \cos \left[\frac{\pi}{N} \left(n+\frac{1}{2}+\frac{N}{2}\right) \left(k+\frac{1}{2}\right) \right]';
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
|
import {isAndroid, isIos} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Messaging - Markdown List', () => {
|
describe('Messaging - Markdown List', () => {
|
||||||
|
|
@ -48,7 +49,7 @@ describe('Messaging - Markdown List', () => {
|
||||||
await HomeScreen.logout();
|
await HomeScreen.logout();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('MM-T4894_1 - should be able to display markdown bullet list -- UNSTABLE', async () => {
|
it('MM-T4894_1 - should be able to display markdown bullet list', async () => {
|
||||||
// # Open a channel screen and post a markdown bullet list
|
// # Open a channel screen and post a markdown bullet list
|
||||||
const item1 = 'item one';
|
const item1 = 'item one';
|
||||||
const item2 = 'item two';
|
const item2 = 'item two';
|
||||||
|
|
@ -61,15 +62,24 @@ describe('Messaging - Markdown List', () => {
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
const {postListPostItemListItem, postListPostItemListItemBullet} = ChannelScreen.getPostListPostItem(post.id);
|
const {postListPostItemListItem, postListPostItemListItemBullet} = ChannelScreen.getPostListPostItem(post.id);
|
||||||
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
||||||
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('•');
|
|
||||||
await expect(element(by.text(item1))).toBeVisible();
|
await expect(element(by.text(item1))).toBeVisible();
|
||||||
await expect(postListPostItemListItem.atIndex(1)).toBeVisible();
|
|
||||||
await expect(postListPostItemListItemBullet.atIndex(1)).toHaveText('•');
|
|
||||||
await expect(element(by.text(item2))).toBeVisible();
|
await expect(element(by.text(item2))).toBeVisible();
|
||||||
await expect(postListPostItemListItem.atIndex(2)).toBeVisible();
|
|
||||||
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('◦');
|
|
||||||
await expect(element(by.text(item2SubPoint))).toBeVisible();
|
await expect(element(by.text(item2SubPoint))).toBeVisible();
|
||||||
|
|
||||||
|
if (isAndroid()) {
|
||||||
|
await expect(postListPostItemListItem.atIndex(0)).toHaveLabel(`• ${item1}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIos()) {
|
||||||
|
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
||||||
|
await expect(postListPostItemListItem.atIndex(1)).toBeVisible();
|
||||||
|
await expect(postListPostItemListItem.atIndex(2)).toBeVisible();
|
||||||
|
|
||||||
|
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('•');
|
||||||
|
await expect(postListPostItemListItemBullet.atIndex(1)).toHaveText('•');
|
||||||
|
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('◦');
|
||||||
|
}
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
});
|
});
|
||||||
|
|
@ -89,13 +99,20 @@ describe('Messaging - Markdown List', () => {
|
||||||
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
||||||
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('1.');
|
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('1.');
|
||||||
await expect(element(by.text(item1))).toBeVisible();
|
await expect(element(by.text(item1))).toBeVisible();
|
||||||
await expect(postListPostItemListItem.atIndex(1)).toBeVisible();
|
|
||||||
await expect(postListPostItemListItemBullet.atIndex(1)).toHaveText('2.');
|
|
||||||
await expect(element(by.text(item2))).toBeVisible();
|
await expect(element(by.text(item2))).toBeVisible();
|
||||||
await expect(postListPostItemListItem.atIndex(2)).toBeVisible();
|
|
||||||
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('3.');
|
|
||||||
await expect(element(by.text(item3))).toBeVisible();
|
await expect(element(by.text(item3))).toBeVisible();
|
||||||
|
|
||||||
|
if (isAndroid()) {
|
||||||
|
await expect(postListPostItemListItem.atIndex(0)).toHaveLabel(`1. ${item1}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIos()) {
|
||||||
|
await expect(postListPostItemListItem.atIndex(1)).toBeVisible();
|
||||||
|
await expect(postListPostItemListItemBullet.atIndex(1)).toHaveText('2.');
|
||||||
|
await expect(postListPostItemListItem.atIndex(2)).toBeVisible();
|
||||||
|
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('3.');
|
||||||
|
}
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
TableScreen,
|
TableScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
|
import {isIos} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Messaging - Markdown Table', () => {
|
describe('Messaging - Markdown Table', () => {
|
||||||
|
|
@ -56,11 +57,11 @@ describe('Messaging - Markdown Table', () => {
|
||||||
'|:---|:---|:---|\n' +
|
'|:---|:---|:---|\n' +
|
||||||
'| 1 | Name | Toast |\n' +
|
'| 1 | Name | Toast |\n' +
|
||||||
'| 2 | Name | Server |\n';
|
'| 2 | Name | Server |\n';
|
||||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
|
||||||
await Post.apiCreatePost(siteOneUrl, {
|
await Post.apiCreatePost(siteOneUrl, {
|
||||||
channelId: testChannel.id,
|
channelId: testChannel.id,
|
||||||
message: markdownTable,
|
message: markdownTable,
|
||||||
});
|
});
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
||||||
// * Verify markdown table is displayed
|
// * Verify markdown table is displayed
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -77,11 +78,11 @@ describe('Messaging - Markdown Table', () => {
|
||||||
'| Left header that wraps | Center header that wraps | Right header that wraps |\n' +
|
'| Left header that wraps | Center header that wraps | Right header that wraps |\n' +
|
||||||
'| :-- | :-: | --: |\n' +
|
'| :-- | :-: | --: |\n' +
|
||||||
'| Left text that wraps row | Center text that wraps row | Right text that wraps row |\n';
|
'| Left text that wraps row | Center text that wraps row | Right text that wraps row |\n';
|
||||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
|
||||||
await Post.apiCreatePost(siteOneUrl, {
|
await Post.apiCreatePost(siteOneUrl, {
|
||||||
channelId: testChannel.id,
|
channelId: testChannel.id,
|
||||||
message: markdownTable,
|
message: markdownTable,
|
||||||
});
|
});
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
||||||
// * Verify table is displayed with long text wrapped properly
|
// * Verify table is displayed with long text wrapped properly
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -119,11 +120,11 @@ describe('Messaging - Markdown Table', () => {
|
||||||
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
|
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
|
||||||
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(7) +
|
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(7) +
|
||||||
'| Left | Center | Right | Right | Left | Center | Right | Right HS last |\n';
|
'| Left | Center | Right | Right | Left | Center | Right | Right HS last |\n';
|
||||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
|
||||||
await Post.apiCreatePost(siteOneUrl, {
|
await Post.apiCreatePost(siteOneUrl, {
|
||||||
channelId: testChannel.id,
|
channelId: testChannel.id,
|
||||||
message: markdownTable,
|
message: markdownTable,
|
||||||
});
|
});
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
||||||
// * Verify table is displayed with some right columns not visible
|
// * Verify table is displayed with some right columns not visible
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -156,11 +157,11 @@ describe('Messaging - Markdown Table', () => {
|
||||||
'| :-- | :-: | --: |\n' +
|
'| :-- | :-: | --: |\n' +
|
||||||
'| Left | Center | Right |\n'.repeat(30) +
|
'| Left | Center | Right |\n'.repeat(30) +
|
||||||
'| Left | Center | Right VS last |\n';
|
'| Left | Center | Right VS last |\n';
|
||||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
|
||||||
await Post.apiCreatePost(siteOneUrl, {
|
await Post.apiCreatePost(siteOneUrl, {
|
||||||
channelId: testChannel.id,
|
channelId: testChannel.id,
|
||||||
message: markdownTable,
|
message: markdownTable,
|
||||||
});
|
});
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
||||||
// * Verify table is displayed with some bottom rows not visible
|
// * Verify table is displayed with some bottom rows not visible
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -178,9 +179,13 @@ describe('Messaging - Markdown Table', () => {
|
||||||
|
|
||||||
// * Verify table screen is scrollable to the bottom
|
// * Verify table screen is scrollable to the bottom
|
||||||
const expectedElement = element(by.text('Right VS last'));
|
const expectedElement = element(by.text('Right VS last'));
|
||||||
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(50, 'down');
|
if (isIos()) {
|
||||||
await expect(element(by.text('Header VS last'))).not.toBeVisible();
|
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(150, 'down');
|
||||||
await expect(expectedElement).toBeVisible();
|
await expect(element(by.text('Header VS last'))).not.toBeVisible();
|
||||||
|
await expect(expectedElement).toBeVisible();
|
||||||
|
} else {
|
||||||
|
await expect(expectedElement).toExist();
|
||||||
|
}
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await TableScreen.back();
|
await TableScreen.back();
|
||||||
|
|
@ -194,11 +199,11 @@ describe('Messaging - Markdown Table', () => {
|
||||||
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
|
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
|
||||||
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(30) +
|
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(30) +
|
||||||
'| Left | Center | Right | Right | Left | Center | Right | Right last |\n';
|
'| Left | Center | Right | Right | Left | Center | Right | Right last |\n';
|
||||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
|
||||||
await Post.apiCreatePost(siteOneUrl, {
|
await Post.apiCreatePost(siteOneUrl, {
|
||||||
channelId: testChannel.id,
|
channelId: testChannel.id,
|
||||||
message: markdownTable,
|
message: markdownTable,
|
||||||
});
|
});
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
||||||
// * Verify table is displayed with some right columns and bottom rows not visible
|
// * Verify table is displayed with some right columns and bottom rows not visible
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -215,13 +220,16 @@ describe('Messaging - Markdown Table', () => {
|
||||||
await expect(element(by.text('Right last'))).not.toBeVisible();
|
await expect(element(by.text('Right last'))).not.toBeVisible();
|
||||||
|
|
||||||
// * Verify table screen is scrollable to the right and scrollable to the bottom
|
// * Verify table screen is scrollable to the right and scrollable to the bottom
|
||||||
await TableScreen.tableScrollView.scrollTo('right');
|
await waitFor(element(by.text('Header last'))).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(150, 'right');
|
||||||
await expect(element(by.text('Header last'))).toBeVisible();
|
|
||||||
await expect(element(by.text('Right last'))).not.toBeVisible();
|
await expect(element(by.text('Right last'))).not.toBeVisible();
|
||||||
const expectedElement = element(by.text('Right last'));
|
const expectedElement = element(by.text('Right last'));
|
||||||
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(50, 'down');
|
if (isIos()) {
|
||||||
await expect(element(by.text('Header last'))).not.toBeVisible();
|
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(150, 'down');
|
||||||
await expect(expectedElement).toBeVisible();
|
await expect(element(by.text('Header last'))).not.toBeVisible();
|
||||||
|
await expect(expectedElement).toBeVisible();
|
||||||
|
} else {
|
||||||
|
await expect(expectedElement).toExist();
|
||||||
|
}
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await TableScreen.back();
|
await TableScreen.back();
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
||||||
await PostOptionsScreen.pinPostOption.tap();
|
await PostOptionsScreen.pinPostOption.tap();
|
||||||
|
|
||||||
// * Verify pinned text is displayed on the post pre-header
|
// * Verify pinned text is displayed on the post pre-header
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.TEN_SEC);
|
||||||
const {postListPostItemPreHeaderText} = ChannelScreen.getPostListPostItem(post.id, message);
|
const {postListPostItemPreHeaderText} = ChannelScreen.getPostListPostItem(post.id, message);
|
||||||
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
|
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
|
||||||
|
|
||||||
|
|
@ -77,7 +77,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
||||||
await PostOptionsScreen.unpinPostOption.tap();
|
await PostOptionsScreen.unpinPostOption.tap();
|
||||||
|
|
||||||
// * Verify pinned text is not displayed on the post pre-header
|
// * Verify pinned text is not displayed on the post pre-header
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.TEN_SEC);
|
||||||
await expect(postListPostItemPreHeaderText).not.toBeVisible();
|
await expect(postListPostItemPreHeaderText).not.toBeVisible();
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
|
|
@ -96,7 +96,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
||||||
await PostOptionsScreen.pinPostOption.tap();
|
await PostOptionsScreen.pinPostOption.tap();
|
||||||
|
|
||||||
// * Verify pinned text is displayed on the post pre-header
|
// * Verify pinned text is displayed on the post pre-header
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.TEN_SEC);
|
||||||
const {postListPostItemPreHeaderText} = ThreadScreen.getPostListPostItem(post.id, message);
|
const {postListPostItemPreHeaderText} = ThreadScreen.getPostListPostItem(post.id, message);
|
||||||
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
|
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
|
||||||
|
|
||||||
|
|
@ -105,7 +105,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
||||||
await PostOptionsScreen.unpinPostOption.tap();
|
await PostOptionsScreen.unpinPostOption.tap();
|
||||||
|
|
||||||
// * Verify pinned text is not displayed on the post pre-header
|
// * Verify pinned text is not displayed on the post pre-header
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.TEN_SEC);
|
||||||
await expect(postListPostItemPreHeaderText).not.toBeVisible();
|
await expect(postListPostItemPreHeaderText).not.toBeVisible();
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
|
|
|
||||||
232
detox/e2e/test/scheduled_messages/create_schedule_message.e2e.ts
Normal file
232
detox/e2e/test/scheduled_messages/create_schedule_message.e2e.ts
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
// *******************************************************************
|
||||||
|
// - [#] indicates a test step (e.g. # Go to a screen)
|
||||||
|
// - [*] indicates an assertion (e.g. * Check the title)
|
||||||
|
// - Use element testID when selecting an element. Create one if none.
|
||||||
|
// *******************************************************************
|
||||||
|
|
||||||
|
import {
|
||||||
|
Channel,
|
||||||
|
Post,
|
||||||
|
Setup,
|
||||||
|
Team,
|
||||||
|
User,
|
||||||
|
} from '@support/server_api';
|
||||||
|
import {
|
||||||
|
serverOneUrl,
|
||||||
|
siteOneUrl,
|
||||||
|
} from '@support/test_config';
|
||||||
|
import {
|
||||||
|
ChannelListScreen,
|
||||||
|
ChannelScreen,
|
||||||
|
HomeScreen,
|
||||||
|
ScheduleMessageScreen,
|
||||||
|
LoginScreen,
|
||||||
|
ServerScreen,
|
||||||
|
DraftScreen,
|
||||||
|
ThreadScreen,
|
||||||
|
} from '@support/ui/screen';
|
||||||
|
import {isIos, timeouts, wait} from '@support/utils';
|
||||||
|
import {expect} from 'detox';
|
||||||
|
|
||||||
|
describe('Scheduled Draft,', () => {
|
||||||
|
const serverOneDisplayName = 'Server 1';
|
||||||
|
const channelsCategory = 'channels';
|
||||||
|
let testChannel: any;
|
||||||
|
let testTeam: any;
|
||||||
|
let testUser: any;
|
||||||
|
let testOtherUser: any;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
|
||||||
|
testChannel = channel;
|
||||||
|
testTeam = team;
|
||||||
|
testUser = user;
|
||||||
|
|
||||||
|
({user: testOtherUser} = await User.apiCreateUser(siteOneUrl));
|
||||||
|
await Team.apiAddUserToTeam(siteOneUrl, testOtherUser.id, testTeam.id);
|
||||||
|
await Channel.apiAddUserToChannel(siteOneUrl, testOtherUser.id, testChannel.id);
|
||||||
|
|
||||||
|
// # Log in to server
|
||||||
|
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||||
|
await LoginScreen.login(testUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// * Verify on channel list screen
|
||||||
|
await ChannelListScreen.toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// # Log out
|
||||||
|
await HomeScreen.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MM-T5762 should be able to create a scheduled message', async () => {
|
||||||
|
const scheduledMessageText = 'Scheduled Message In a channel';
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
await ChannelScreen.enterMessageToSchedule(scheduledMessageText);
|
||||||
|
await ChannelScreen.longPressSendButton();
|
||||||
|
await chooseScheduleMessageDate();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
await ChannelScreen.verifyScheduledDraftInfoInChannel();
|
||||||
|
await verifyScheduledCountOnChannelListScreen('1');
|
||||||
|
|
||||||
|
// # Open scheduled message screen and verify count
|
||||||
|
await ChannelListScreen.draftsButton.tap();
|
||||||
|
await ScheduleMessageScreen.clickScheduledTab();
|
||||||
|
await ScheduleMessageScreen.verifyCountOnScheduledTab('1');
|
||||||
|
await ScheduleMessageScreen.assertScheduledMessageExists(scheduledMessageText);
|
||||||
|
|
||||||
|
await cleanupDrafts();
|
||||||
|
await DraftScreen.backButton.tap();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MM-T5767 should be able to create a scheduled message under a threaded post', async () => {
|
||||||
|
const parentMessage = 'Root Post for Scheduled Message';
|
||||||
|
const scheduledMessageText = 'Scheduled Message In a channel';
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
await waitFor(ChannelScreen.postInput).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||||
|
await ChannelScreen.postMessage(parentMessage);
|
||||||
|
|
||||||
|
// # Open reply thread
|
||||||
|
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.enterMessageToSchedule(scheduledMessageText);
|
||||||
|
await ThreadScreen.longPressSendButton();
|
||||||
|
await chooseScheduleMessageDate();
|
||||||
|
await ChannelScreen.verifyScheduledDraftInfoInChannel(true);
|
||||||
|
await ThreadScreen.back();
|
||||||
|
await verifyScheduledCountOnChannelListScreen('1');
|
||||||
|
|
||||||
|
// # Open scheduled message screen and verify count
|
||||||
|
await ChannelListScreen.draftsButton.tap();
|
||||||
|
await ScheduleMessageScreen.clickScheduledTab();
|
||||||
|
await ScheduleMessageScreen.verifyCountOnScheduledTab('1');
|
||||||
|
await ScheduleMessageScreen.assertScheduledMessageExists(scheduledMessageText);
|
||||||
|
|
||||||
|
await DraftScreen.openDraftPostActions();
|
||||||
|
await DraftScreen.sendDraft();
|
||||||
|
await DraftScreen.backButton.tap();
|
||||||
|
|
||||||
|
// * Verify the scheduled message is shown in the channel
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
await ChannelScreen.openReplyThreadFor(parentPost.id, parentMessage);
|
||||||
|
await expect(element(by.text(scheduledMessageText))).toBeVisible();
|
||||||
|
|
||||||
|
await ThreadScreen.back();
|
||||||
|
await ChannelScreen.back();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MM-T5731 should be able to Delete a scheduled Message', async () => {
|
||||||
|
const scheduledMessageText = 'Scheduled Message In a channel';
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
await ChannelScreen.enterMessageToSchedule(scheduledMessageText);
|
||||||
|
await ChannelScreen.longPressSendButton();
|
||||||
|
await chooseScheduleMessageDate();
|
||||||
|
await ChannelScreen.verifyScheduledDraftInfoInChannel();
|
||||||
|
await verifyScheduledCountOnChannelListScreen('1');
|
||||||
|
|
||||||
|
// # Open scheduled message screen and verify count
|
||||||
|
await ChannelListScreen.draftsButton.tap();
|
||||||
|
await ScheduleMessageScreen.clickScheduledTab();
|
||||||
|
await ScheduleMessageScreen.verifyCountOnScheduledTab('1');
|
||||||
|
await ScheduleMessageScreen.assertScheduledMessageExists(scheduledMessageText);
|
||||||
|
|
||||||
|
await DraftScreen.openDraftPostActions();
|
||||||
|
await ScheduleMessageScreen.deleteScheduledMessageFromDraftActions();
|
||||||
|
await DraftScreen.backButton.tap();
|
||||||
|
await verifyScheduledScheduledMessageDoesNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MM-T5730 should be able to Send a scheduled Message', async () => {
|
||||||
|
const scheduledMessageText = 'Scheduled Message In a channel';
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
await ChannelScreen.enterMessageToSchedule(scheduledMessageText);
|
||||||
|
await ChannelScreen.longPressSendButton();
|
||||||
|
await chooseScheduleMessageDate();
|
||||||
|
await verifyScheduledCountOnChannelListScreen('1');
|
||||||
|
|
||||||
|
// # Open scheduled message screen and verify count
|
||||||
|
await ChannelListScreen.draftsButton.tap();
|
||||||
|
await ScheduleMessageScreen.clickScheduledTab();
|
||||||
|
await ScheduleMessageScreen.verifyCountOnScheduledTab('1');
|
||||||
|
await ScheduleMessageScreen.assertScheduledMessageExists(scheduledMessageText);
|
||||||
|
|
||||||
|
await DraftScreen.openDraftPostActions();
|
||||||
|
await DraftScreen.sendDraft();
|
||||||
|
await DraftScreen.backButton.tap();
|
||||||
|
|
||||||
|
// * Verify the scheduled message is shown in the channel
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
||||||
|
// * Verify message is added to post list
|
||||||
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, scheduledMessageText);
|
||||||
|
await expect(postListPostItem).toBeVisible();
|
||||||
|
|
||||||
|
await ChannelScreen.back();
|
||||||
|
await verifyScheduledScheduledMessageDoesNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MM-T5720 should be able to Reschedule a scheduled Message', async () => {
|
||||||
|
const scheduledMessageText = 'Scheduled Message In a channel';
|
||||||
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
await ChannelScreen.enterMessageToSchedule(scheduledMessageText);
|
||||||
|
await ChannelScreen.longPressSendButton();
|
||||||
|
await chooseScheduleMessageDate();
|
||||||
|
await ChannelScreen.verifyScheduledDraftInfoInChannel();
|
||||||
|
await verifyScheduledCountOnChannelListScreen('1');
|
||||||
|
|
||||||
|
// # Open scheduled message screen and verify count
|
||||||
|
await ChannelListScreen.draftsButton.tap();
|
||||||
|
await ScheduleMessageScreen.clickScheduledTab();
|
||||||
|
await ScheduleMessageScreen.verifyCountOnScheduledTab('1');
|
||||||
|
await ScheduleMessageScreen.assertScheduledMessageExists(scheduledMessageText);
|
||||||
|
|
||||||
|
await ScheduleMessageScreen.assertScheduleTimeTextIsVisible(await ScheduleMessageScreen.nextMonday());
|
||||||
|
if (isIos()) {
|
||||||
|
// Andoid uses native date picker which is not supported by detox asit cannot interact with native UI
|
||||||
|
await DraftScreen.openDraftPostActions();
|
||||||
|
await ScheduleMessageScreen.clickRescheduleOption();
|
||||||
|
await ScheduleMessageScreen.selectDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up drafts
|
||||||
|
await DraftScreen.openDraftPostActions();
|
||||||
|
await ScheduleMessageScreen.deleteScheduledMessageFromDraftActions();
|
||||||
|
await DraftScreen.backButton.tap();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function cleanupDrafts() {
|
||||||
|
// # Clean up drafts
|
||||||
|
await DraftScreen.openDraftPostActions();
|
||||||
|
await ScheduleMessageScreen.deleteScheduledMessageFromDraftActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyScheduledCountOnChannelListScreen(draftCount: string) {
|
||||||
|
await ChannelScreen.back();
|
||||||
|
await ChannelListScreen.draftsButton.toBeVisible();
|
||||||
|
await expect(element(by.id(ChannelListScreen.testID.scheduledMessageCountListScreen))).toHaveText(draftCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyScheduledScheduledMessageDoesNotExist() {
|
||||||
|
await ChannelListScreen.draftsButton.toNotBeVisible();
|
||||||
|
await expect(element(by.id(ChannelListScreen.testID.scheduledMessageCountListScreen))).not.toExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function chooseScheduleMessageDate() {
|
||||||
|
// # The bottom sheet info will not show `Tomorrow` if today is Friday or Saturday. Always schedule for Monday.
|
||||||
|
const today = new Date().getDay();
|
||||||
|
if (today === 1) { // 1 represents Monday
|
||||||
|
await ChannelScreen.scheduleMessageForNextMonday();
|
||||||
|
} else {
|
||||||
|
await ChannelScreen.scheduleMessageForMonday();
|
||||||
|
}
|
||||||
|
|
||||||
|
await ChannelScreen.clickOnScheduledMessage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -96,7 +96,7 @@ describe('Search - Search Messages', () => {
|
||||||
await SearchMessagesScreen.searchModifierFrom.tap();
|
await SearchMessagesScreen.searchModifierFrom.tap();
|
||||||
await SearchMessagesScreen.searchInput.typeText(testUser.username);
|
await SearchMessagesScreen.searchInput.typeText(testUser.username);
|
||||||
const {atMentionItem} = Autocomplete.getAtMentionItem(testUser.id);
|
const {atMentionItem} = Autocomplete.getAtMentionItem(testUser.id);
|
||||||
await waitFor(atMentionItem).toBeVisible().withTimeout(2000);
|
await waitFor(atMentionItem).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||||
await atMentionItem.tap();
|
await atMentionItem.tap();
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
|
||||||
|
|
@ -126,6 +126,7 @@ describe('Search - Search Messages', () => {
|
||||||
await SearchMessagesScreen.searchModifierIn.tap();
|
await SearchMessagesScreen.searchModifierIn.tap();
|
||||||
await SearchMessagesScreen.searchInput.typeText(testChannel.name);
|
await SearchMessagesScreen.searchInput.typeText(testChannel.name);
|
||||||
const {channelMentionItem} = Autocomplete.getChannelMentionItem(testChannel.name);
|
const {channelMentionItem} = Autocomplete.getChannelMentionItem(testChannel.name);
|
||||||
|
await waitFor(channelMentionItem).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||||
await channelMentionItem.tap();
|
await channelMentionItem.tap();
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
|
||||||
|
|
@ -166,6 +167,7 @@ describe('Search - Search Messages', () => {
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
|
||||||
// * Verify search results do not contain messages with excluded term
|
// * Verify search results do not contain messages with excluded term
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await expect(nonExcludedPostListPostItem).toBeVisible();
|
await expect(nonExcludedPostListPostItem).toBeVisible();
|
||||||
await expect(excludedPostListPostItem).not.toBeVisible();
|
await expect(excludedPostListPostItem).not.toBeVisible();
|
||||||
|
|
||||||
|
|
@ -198,11 +200,15 @@ describe('Search - Search Messages', () => {
|
||||||
await SearchMessagesScreen.searchModifierPhrases.tap();
|
await SearchMessagesScreen.searchModifierPhrases.tap();
|
||||||
await SearchMessagesScreen.searchInput.tapBackspaceKey();
|
await SearchMessagesScreen.searchInput.tapBackspaceKey();
|
||||||
await SearchMessagesScreen.searchInput.typeText(messageWithIncludedTerm);
|
await SearchMessagesScreen.searchInput.typeText(messageWithIncludedTerm);
|
||||||
|
|
||||||
|
// # Collapse the keyboard
|
||||||
|
await element(by.id('search.modifier.header')).tap();
|
||||||
await SearchMessagesScreen.searchModifierPhrases.tap();
|
await SearchMessagesScreen.searchModifierPhrases.tap();
|
||||||
await SearchMessagesScreen.searchInput.tapBackspaceKey();
|
await SearchMessagesScreen.searchInput.tapBackspaceKey();
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
|
||||||
// * Verify search results only contain messages with included term
|
// * Verify search results only contain messages with included term
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await expect(nonIncludedPostListPostItem).not.toBeVisible();
|
await expect(nonIncludedPostListPostItem).not.toBeVisible();
|
||||||
await expect(includedPostListPostItem).toBeVisible();
|
await expect(includedPostListPostItem).toBeVisible();
|
||||||
|
|
||||||
|
|
@ -223,17 +229,10 @@ describe('Search - Search Messages', () => {
|
||||||
// * Verify on search messages screen
|
// * Verify on search messages screen
|
||||||
await SearchMessagesScreen.toBeVisible();
|
await SearchMessagesScreen.toBeVisible();
|
||||||
|
|
||||||
// # Tap on from-search-modifier, type in username, tap on user at-mention autocomplete, tap on in-search-modifier, type in channel, tap on channel mention autocomplete, and tap on search key
|
// # Tap on from-search-modifier, type in username
|
||||||
await SearchMessagesScreen.searchModifierFrom.tap();
|
await SearchMessagesScreen.searchInput.typeText(`from: ${testUser.username} channel: ${testChannel.name}`);
|
||||||
await SearchMessagesScreen.searchInput.typeText(testUser.username);
|
|
||||||
const {atMentionItem} = Autocomplete.getAtMentionItem(testUser.id);
|
|
||||||
await atMentionItem.tap();
|
|
||||||
await SearchMessagesScreen.searchModifierIn.tap();
|
|
||||||
await wait(timeouts.ONE_SEC);
|
|
||||||
await SearchMessagesScreen.searchInput.typeText(testChannel.name);
|
|
||||||
const {channelMentionItem} = Autocomplete.getChannelMentionItem(testChannel.name);
|
|
||||||
await channelMentionItem.tap();
|
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// * Verify search results only contain messages from user in channel
|
// * Verify search results only contain messages from user in channel
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -261,6 +260,7 @@ describe('Search - Search Messages', () => {
|
||||||
// # Type in a search term that will yield results and tap on search key
|
// # Type in a search term that will yield results and tap on search key
|
||||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TEN_SEC);
|
||||||
|
|
||||||
// * Verify search results contain searched message
|
// * Verify search results contain searched message
|
||||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
|
|
@ -314,6 +314,7 @@ describe('Search - Search Messages', () => {
|
||||||
// # Type in a search term that will yield results for second team and tap on search key
|
// # Type in a search term that will yield results for second team and tap on search key
|
||||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// * Verify search results contain searched message
|
// * Verify search results contain searched message
|
||||||
const {postListPostItem} = SearchMessagesScreen.getPostListPostItem(post.id, message);
|
const {postListPostItem} = SearchMessagesScreen.getPostListPostItem(post.id, message);
|
||||||
|
|
@ -321,6 +322,7 @@ describe('Search - Search Messages', () => {
|
||||||
|
|
||||||
// # Tap on team picker button and tap on first team option
|
// # Tap on team picker button and tap on first team option
|
||||||
await SearchMessagesScreen.teamPickerButton.tap();
|
await SearchMessagesScreen.teamPickerButton.tap();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await TeamDropdownMenuScreen.getTeamIcon(testTeam.id).tap();
|
await TeamDropdownMenuScreen.getTeamIcon(testTeam.id).tap();
|
||||||
|
|
||||||
// * Verify search results do not contain searched message
|
// * Verify search results do not contain searched message
|
||||||
|
|
@ -347,6 +349,7 @@ describe('Search - Search Messages', () => {
|
||||||
const searchTerm = getRandomId();
|
const searchTerm = getRandomId();
|
||||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// * Verify empty search state for search messages
|
// * Verify empty search state for search messages
|
||||||
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
||||||
|
|
@ -373,6 +376,8 @@ describe('Search - Search Messages', () => {
|
||||||
// # Type in a search term that will yield results, tap on search key, open post options for searched message, and tap on edit option
|
// # Type in a search term that will yield results, tap on search key, open post options for searched message, and tap on edit option
|
||||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
||||||
await PostOptionsScreen.editPostOption.tap();
|
await PostOptionsScreen.editPostOption.tap();
|
||||||
|
|
@ -442,6 +447,7 @@ describe('Search - Search Messages', () => {
|
||||||
// # Type in a search term that will yield results, tap on search key, open post options for searched message, tap on save option, and open saved messages screen
|
// # Type in a search term that will yield results, tap on search key, open post options for searched message, tap on save option, and open saved messages screen
|
||||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
||||||
await PostOptionsScreen.savePostOption.tap();
|
await PostOptionsScreen.savePostOption.tap();
|
||||||
|
|
@ -482,6 +488,8 @@ describe('Search - Search Messages', () => {
|
||||||
// # Type in a search term that will yield results, tap on search key, open post options for searched message, tap on pin to channel option, go back to channel list screen, open the channel screen where searched message is posted, open channel info screen, and open pinned messages screen
|
// # Type in a search term that will yield results, tap on search key, open post options for searched message, tap on pin to channel option, go back to channel list screen, open the channel screen where searched message is posted, open channel info screen, and open pinned messages screen
|
||||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||||
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
||||||
await PostOptionsScreen.pinPostOption.tap();
|
await PostOptionsScreen.pinPostOption.tap();
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,11 @@ describe('Server Login - Connect to Server', () => {
|
||||||
|
|
||||||
// * Verify invalid url error
|
// * Verify invalid url error
|
||||||
await waitFor(serverUrlInputError).toExist().withTimeout(timeouts.FOUR_SEC);
|
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.');
|
const expectedErrorText = isIos()
|
||||||
|
? 'URLSessionTask failed with error: A server with the specified hostname could not be found.'
|
||||||
|
: 'Unable to resolve host "invalid": No address associated with hostname';
|
||||||
|
|
||||||
|
await expect(serverUrlInputError).toHaveText(expectedErrorText);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4676_4 - should show connection error on invalid ssl or invalid host', async () => {
|
it('MM-T4676_4 - should show connection error on invalid ssl or invalid host', async () => {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
|
import {timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Server Login - Login by Email', () => {
|
describe('Server Login - Login by Email', () => {
|
||||||
|
|
@ -61,14 +62,16 @@ describe('Server Login - Login by Email', () => {
|
||||||
it('MM-T4677_2 - should show disabled signin button on empty username or password', async () => {
|
it('MM-T4677_2 - should show disabled signin button on empty username or password', async () => {
|
||||||
// # Log in with empty username and non-empty password
|
// # Log in with empty username and non-empty password
|
||||||
await usernameInput.clearText();
|
await usernameInput.clearText();
|
||||||
await passwordInput.typeText('password');
|
await passwordInput.replaceText('password');
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// * Verify signin button is disabled
|
// * Verify signin button is disabled
|
||||||
await expect(signinButtonDisabled).toBeVisible();
|
await expect(signinButtonDisabled).toBeVisible();
|
||||||
|
|
||||||
// # Log in with non-empty username and empty password
|
// # Log in with non-empty username and empty password
|
||||||
await usernameInput.typeText('username');
|
await usernameInput.replaceText('username');
|
||||||
await passwordInput.clearText();
|
await passwordInput.clearText();
|
||||||
|
await LoginScreen.loginFormInfoText.tap();
|
||||||
|
|
||||||
// * Verify signin button is disabled
|
// * Verify signin button is disabled
|
||||||
await expect(signinButtonDisabled).toBeVisible();
|
await expect(signinButtonDisabled).toBeVisible();
|
||||||
|
|
@ -76,8 +79,10 @@ describe('Server Login - Login by Email', () => {
|
||||||
|
|
||||||
it('MM-T4677_3 - should show incorrect combination error on incorrect credentials', async () => {
|
it('MM-T4677_3 - should show incorrect combination error on incorrect credentials', async () => {
|
||||||
// # Log in with incorrect credentials
|
// # Log in with incorrect credentials
|
||||||
await usernameInput.typeText('username');
|
await usernameInput.replaceText('username');
|
||||||
await passwordInput.typeText('password');
|
await passwordInput.replaceText('password');
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
await LoginScreen.loginFormInfoText.tap();
|
||||||
await signinButton.tap();
|
await signinButton.tap();
|
||||||
|
|
||||||
// * Verify incorrect combination error
|
// * Verify incorrect combination error
|
||||||
|
|
@ -87,9 +92,12 @@ describe('Server Login - Login by Email', () => {
|
||||||
it('MM-T4677_4 - should show channel list screen on successful login', async () => {
|
it('MM-T4677_4 - should show channel list screen on successful login', async () => {
|
||||||
// # Log in to server with correct credentials
|
// # Log in to server with correct credentials
|
||||||
const {team, user} = await Setup.apiInit(siteOneUrl);
|
const {team, user} = await Setup.apiInit(siteOneUrl);
|
||||||
await usernameInput.typeText(user.newUser.username);
|
await usernameInput.replaceText(user.newUser.username);
|
||||||
await passwordInput.typeText(user.newUser.password);
|
await passwordInput.replaceText(user.newUser.password);
|
||||||
|
await LoginScreen.loginFormInfoText.tap();
|
||||||
|
|
||||||
await signinButton.tap();
|
await signinButton.tap();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
|
|
||||||
// * Verify on channel list screen and channel list header shows team display name and server display name
|
// * Verify on channel list screen and channel list header shows team display name and server display name
|
||||||
await ChannelListScreen.toBeVisible();
|
await ChannelListScreen.toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,9 @@ describe('Server Login - Server List', () => {
|
||||||
// # Add a second server and log in to the second server
|
// # Add a second server and log in to the second server
|
||||||
await User.apiAdminLogin(siteTwoUrl);
|
await User.apiAdminLogin(siteTwoUrl);
|
||||||
({user: serverTwoUser} = await Setup.apiInit(siteTwoUrl));
|
({user: serverTwoUser} = await Setup.apiInit(siteTwoUrl));
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await ServerListScreen.addServerButton.tap();
|
await ServerListScreen.addServerButton.tap();
|
||||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||||
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
||||||
await LoginScreen.login(serverTwoUser);
|
await LoginScreen.login(serverTwoUser);
|
||||||
|
|
||||||
|
|
@ -112,8 +113,9 @@ describe('Server Login - Server List', () => {
|
||||||
// # Add a third server and log in to the third server
|
// # Add a third server and log in to the third server
|
||||||
await User.apiAdminLogin(siteThreeUrl);
|
await User.apiAdminLogin(siteThreeUrl);
|
||||||
({user: serverThreeUser} = await Setup.apiInit(siteThreeUrl));
|
({user: serverThreeUser} = await Setup.apiInit(siteThreeUrl));
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
await ServerListScreen.addServerButton.tap();
|
await ServerListScreen.addServerButton.tap();
|
||||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||||
await ServerScreen.connectToServer(serverThreeUrl, serverThreeDisplayName);
|
await ServerScreen.connectToServer(serverThreeUrl, serverThreeDisplayName);
|
||||||
await LoginScreen.login(serverThreeUser);
|
await LoginScreen.login(serverThreeUser);
|
||||||
|
|
||||||
|
|
@ -123,6 +125,7 @@ describe('Server Login - Server List', () => {
|
||||||
|
|
||||||
// # Open server list screen
|
// # Open server list screen
|
||||||
await ServerListScreen.open();
|
await ServerListScreen.open();
|
||||||
|
await wait(timeouts.TWO_SEC);
|
||||||
if (isIos()) {
|
if (isIos()) {
|
||||||
await ServerListScreen.serverListTitle.swipe('up');
|
await ServerListScreen.serverListTitle.swipe('up');
|
||||||
} else if (isAndroid()) {
|
} else if (isAndroid()) {
|
||||||
|
|
@ -183,7 +186,7 @@ describe('Server Login - Server List', () => {
|
||||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||||
}
|
}
|
||||||
await waitFor(ServerListScreen.getServerItemActive(serverOneDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
await waitFor(ServerListScreen.getServerItemActive(serverOneDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
await ServerListScreen.getServerItemActive(serverOneDisplayName).swipe('left');
|
await ServerListScreen.getServerItemActive(serverOneDisplayName).swipe('left', 'slow');
|
||||||
await ServerListScreen.getServerItemEditOption(serverOneDisplayName).tap();
|
await ServerListScreen.getServerItemEditOption(serverOneDisplayName).tap();
|
||||||
|
|
||||||
// * Verify on edit server screen
|
// * Verify on edit server screen
|
||||||
|
|
@ -209,7 +212,7 @@ describe('Server Login - Server List', () => {
|
||||||
await expect(ServerListScreen.getServerItemActive(newServerOneDisplayName)).toBeVisible();
|
await expect(ServerListScreen.getServerItemActive(newServerOneDisplayName)).toBeVisible();
|
||||||
|
|
||||||
// # Revert back to original first server display name and go back to first server
|
// # Revert back to original first server display name and go back to first server
|
||||||
await ServerListScreen.getServerItemActive(newServerOneDisplayName).swipe('left');
|
await ServerListScreen.getServerItemActive(newServerOneDisplayName).swipe('left', 'slow');
|
||||||
await ServerListScreen.getServerItemEditOption(newServerOneDisplayName).tap();
|
await ServerListScreen.getServerItemEditOption(newServerOneDisplayName).tap();
|
||||||
await EditServerScreen.serverDisplayNameInput.replaceText(serverOneDisplayName);
|
await EditServerScreen.serverDisplayNameInput.replaceText(serverOneDisplayName);
|
||||||
await EditServerScreen.saveButton.tap();
|
await EditServerScreen.saveButton.tap();
|
||||||
|
|
@ -229,20 +232,20 @@ describe('Server Login - Server List', () => {
|
||||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||||
}
|
}
|
||||||
await waitFor(ServerListScreen.getServerItemActive(serverOneDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
await waitFor(ServerListScreen.getServerItemActive(serverOneDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
await ServerListScreen.getServerItemActive(serverOneDisplayName).swipe('left');
|
await ServerListScreen.getServerItemActive(serverOneDisplayName).swipe('left', 'slow');
|
||||||
await ServerListScreen.getServerItemRemoveOption(serverOneDisplayName).tap();
|
await ServerListScreen.getServerItemRemoveOption(serverOneDisplayName).tap();
|
||||||
|
|
||||||
// * Verify remove server alert is displayed
|
// * Verify remove server alert is displayed
|
||||||
await expect(Alert.removeServerTitle(serverOneDisplayName)).toBeVisible();
|
await waitFor(Alert.removeServerTitle(serverOneDisplayName)).toBeVisible().withTimeout(timeouts.HALF_MIN);
|
||||||
|
|
||||||
// # Tap on remove button and go back to server list screen
|
// # Tap on remove button and go back to server list screen
|
||||||
await Alert.removeButton3.tap();
|
await waitFor(Alert.removeButton1).toBeVisible().withTimeout(timeouts.HALF_MIN);
|
||||||
|
await Alert.removeButton1.tap();
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.ONE_SEC);
|
||||||
await ServerListScreen.open();
|
await ServerListScreen.open();
|
||||||
if (isIos()) {
|
if (isIos()) {
|
||||||
await ServerListScreen.serverListTitle.swipe('up');
|
await ServerListScreen.serverListTitle.swipe('up');
|
||||||
} else if (isAndroid()) {
|
} else if (isAndroid()) {
|
||||||
await waitFor(ServerListScreen.serverListTitle).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
|
||||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,7 +255,7 @@ describe('Server Login - Server List', () => {
|
||||||
|
|
||||||
// # Add first server back to the list and log in to the first server
|
// # Add first server back to the list and log in to the first server
|
||||||
await ServerListScreen.addServerButton.tap();
|
await ServerListScreen.addServerButton.tap();
|
||||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||||
await LoginScreen.login(serverOneUser);
|
await LoginScreen.login(serverOneUser);
|
||||||
});
|
});
|
||||||
|
|
@ -270,17 +273,18 @@ describe('Server Login - Server List', () => {
|
||||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||||
}
|
}
|
||||||
await waitFor(ServerListScreen.getServerItemInactive(serverThreeDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
await waitFor(ServerListScreen.getServerItemInactive(serverThreeDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
await ServerListScreen.getServerItemInactive(serverThreeDisplayName).swipe('left');
|
await ServerListScreen.getServerItemInactive(serverThreeDisplayName).swipe('left', 'slow');
|
||||||
await ServerListScreen.getServerItemLogoutOption(serverThreeDisplayName).tap();
|
await ServerListScreen.getServerItemLogoutOption(serverThreeDisplayName).tap();
|
||||||
|
|
||||||
// * Verify logout server alert is displayed
|
// * Verify logout server alert is displayed
|
||||||
await expect(Alert.logoutTitle(serverThreeDisplayName)).toBeVisible();
|
await waitFor(Alert.logoutTitle(serverThreeDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
|
||||||
// # Tap on logout button
|
// # Tap on logout button
|
||||||
await Alert.logoutButton3.tap();
|
await waitFor(Alert.logoutButton).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
await Alert.logoutButton.tap();
|
||||||
|
|
||||||
// * Verify third server is logged out
|
// * Verify third server is logged out
|
||||||
await ServerListScreen.getServerItemInactive(serverThreeDisplayName).swipe('left');
|
await ServerListScreen.getServerItemInactive(serverThreeDisplayName).swipe('left', 'slow');
|
||||||
await expect(ServerListScreen.getServerItemLoginOption(serverThreeDisplayName)).toBeVisible();
|
await expect(ServerListScreen.getServerItemLoginOption(serverThreeDisplayName)).toBeVisible();
|
||||||
|
|
||||||
// # Go back to first server
|
// # Go back to first server
|
||||||
|
|
@ -301,7 +305,7 @@ describe('Server Login - Server List', () => {
|
||||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||||
}
|
}
|
||||||
await ServerListScreen.addServerButton.tap();
|
await ServerListScreen.addServerButton.tap();
|
||||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||||
await ServerScreen.serverUrlInput.replaceText(serverTwoUrl);
|
await ServerScreen.serverUrlInput.replaceText(serverTwoUrl);
|
||||||
if (isAndroid()) {
|
if (isAndroid()) {
|
||||||
await ServerScreen.serverUrlInput.tapReturnKey();
|
await ServerScreen.serverUrlInput.tapReturnKey();
|
||||||
|
|
@ -346,9 +350,11 @@ describe('Server Login - Server List', () => {
|
||||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||||
}
|
}
|
||||||
await waitFor(ServerListScreen.getServerItemInactive(serverTwoDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
await waitFor(ServerListScreen.getServerItemInactive(serverTwoDisplayName)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
await ServerListScreen.getServerItemInactive(serverTwoDisplayName).swipe('left');
|
await ServerListScreen.getServerItemInactive(serverTwoDisplayName).swipe('left', 'slow');
|
||||||
await ServerListScreen.getServerItemLogoutOption(serverTwoDisplayName).tap();
|
await ServerListScreen.getServerItemLogoutOption(serverTwoDisplayName).tap();
|
||||||
await Alert.logoutButton2.tap();
|
await wait(timeouts.FOUR_SEC);
|
||||||
|
await waitFor(Alert.logoutButton).toBeVisible().withTimeout(timeouts.HALF_MIN);
|
||||||
|
await Alert.logoutButton.tap();
|
||||||
await ServerListScreen.getServerItemActive(serverOneDisplayName).tap();
|
await ServerListScreen.getServerItemActive(serverOneDisplayName).tap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,100 @@
|
||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
/* eslint-disable no-await-in-loop, no-console */
|
||||||
|
|
||||||
import {Plugin, System, User} from '@support/server_api';
|
import {Plugin, System, User} from '@support/server_api';
|
||||||
import {siteOneUrl} from '@support/test_config';
|
import {siteOneUrl} from '@support/test_config';
|
||||||
|
|
||||||
|
// Number of retry attempts
|
||||||
|
const MAX_RETRY_ATTEMPTS = 3;
|
||||||
|
|
||||||
|
// Delay between retries (in milliseconds)
|
||||||
|
const RETRY_DELAY = 5000;
|
||||||
|
|
||||||
|
let isFirstLaunch = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch the app with retry mechanism
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function launchAppWithRetry(): Promise<void> {
|
||||||
|
let lastError;
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
|
||||||
|
try {
|
||||||
|
if (isFirstLaunch) {
|
||||||
|
// For first launch, clean install
|
||||||
|
await device.launchApp({
|
||||||
|
newInstance: true,
|
||||||
|
delete: true,
|
||||||
|
permissions: {
|
||||||
|
notifications: 'YES',
|
||||||
|
camera: 'NO',
|
||||||
|
medialibrary: 'NO',
|
||||||
|
photos: 'NO',
|
||||||
|
},
|
||||||
|
launchArgs: {
|
||||||
|
detoxPrintBusyIdleResources: 'YES',
|
||||||
|
detoxDebugVisibility: 'YES',
|
||||||
|
detoxDisableSynchronization: 'YES',
|
||||||
|
detoxDisableHierarchyDump: 'YES',
|
||||||
|
reduceMotion: 'YES',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
isFirstLaunch = false;
|
||||||
|
} else {
|
||||||
|
// For subsequent launches, reuse instance
|
||||||
|
await device.launchApp({
|
||||||
|
newInstance: false,
|
||||||
|
launchArgs: {
|
||||||
|
detoxPrintBusyIdleResources: 'YES',
|
||||||
|
detoxDebugVisibility: 'YES',
|
||||||
|
detoxDisableSynchronization: 'YES',
|
||||||
|
detoxURLBlacklistRegex: '.*localhost.*',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify app is connected by executing a simple command
|
||||||
|
await device.reloadReactNative();
|
||||||
|
console.info(`✅ App launched successfully on attempt ${attempt}`);
|
||||||
|
return; // Success, exit the function
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error;
|
||||||
|
console.warn(`❌ App launch failed on attempt ${attempt}/${MAX_RETRY_ATTEMPTS}: ${(error as Error).message}`);
|
||||||
|
|
||||||
|
// If this is the last attempt, don't wait
|
||||||
|
if (attempt < MAX_RETRY_ATTEMPTS) {
|
||||||
|
console.warn(`Waiting ${RETRY_DELAY}ms before retrying...`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
|
||||||
|
|
||||||
|
// Force a new instance on retry
|
||||||
|
if (!isFirstLaunch && attempt > 1) {
|
||||||
|
console.warn('Forcing new instance for next attempt');
|
||||||
|
isFirstLaunch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, all attempts failed
|
||||||
|
throw new Error(`Failed to launch app after ${MAX_RETRY_ATTEMPTS} attempts. Last error: ${(lastError as Error).message}`);
|
||||||
|
}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Login as sysadmin and reset server configuration
|
// Login as sysadmin and reset server configuration
|
||||||
await System.apiCheckSystemHealth(siteOneUrl);
|
await System.apiCheckSystemHealth(siteOneUrl);
|
||||||
await User.apiAdminLogin(siteOneUrl);
|
await User.apiAdminLogin(siteOneUrl);
|
||||||
await Plugin.apiDisableNonPrepackagedPlugins(siteOneUrl);
|
await Plugin.apiDisableNonPrepackagedPlugins(siteOneUrl);
|
||||||
|
await launchAppWithRetry();
|
||||||
await device.launchApp({
|
});
|
||||||
newInstance: true,
|
|
||||||
launchArgs: {detoxPrintBusyIdleResources: 'YES'},
|
// Add this to speed up test cleanup
|
||||||
permissions: {
|
afterAll(async () => {
|
||||||
notifications: 'YES',
|
try {
|
||||||
camera: 'YES',
|
await device.terminateApp();
|
||||||
medialibrary: 'YES',
|
} catch (error) {
|
||||||
photos: 'YES',
|
console.error('Error terminating app:', error);
|
||||||
},
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,10 @@ describe('Smoke Test - Channels', () => {
|
||||||
const {channel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
const {channel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
||||||
await BrowseChannelsScreen.open();
|
await BrowseChannelsScreen.open();
|
||||||
await BrowseChannelsScreen.searchInput.replaceText(channel.name);
|
await BrowseChannelsScreen.searchInput.replaceText(channel.name);
|
||||||
await wait(timeouts.ONE_SEC);
|
await BrowseChannelsScreen.searchInput.tapReturnKey();
|
||||||
|
await wait(timeouts.FOUR_SEC);
|
||||||
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
||||||
|
await ChannelScreen.closeScheduledMessageTooltip();
|
||||||
|
|
||||||
// * Verify on newly joined channel screen
|
// * Verify on newly joined channel screen
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
@ -90,8 +92,9 @@ describe('Smoke Test - Channels', () => {
|
||||||
// # Open create channel screen and create a new channel
|
// # Open create channel screen and create a new channel
|
||||||
const displayName = `Channel ${getRandomId()}`;
|
const displayName = `Channel ${getRandomId()}`;
|
||||||
await CreateOrEditChannelScreen.openCreateChannel();
|
await CreateOrEditChannelScreen.openCreateChannel();
|
||||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(displayName);
|
await CreateOrEditChannelScreen.displayNameInput.typeText(displayName);
|
||||||
await CreateOrEditChannelScreen.createButton.tap();
|
await wait(timeouts.FOUR_SEC);
|
||||||
|
await CreateOrEditChannelScreen.clickonCreateButton();
|
||||||
|
|
||||||
// * Verify on newly created public channel
|
// * Verify on newly created public channel
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
|
|
@ -106,8 +109,10 @@ describe('Smoke Test - Channels', () => {
|
||||||
await CreateDirectMessageScreen.open();
|
await CreateDirectMessageScreen.open();
|
||||||
await CreateDirectMessageScreen.closeTutorial();
|
await CreateDirectMessageScreen.closeTutorial();
|
||||||
await CreateDirectMessageScreen.searchInput.replaceText(newUserDisplayName);
|
await CreateDirectMessageScreen.searchInput.replaceText(newUserDisplayName);
|
||||||
|
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.ONE_SEC);
|
||||||
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
||||||
|
await wait(timeouts.ONE_SEC);
|
||||||
await CreateDirectMessageScreen.startButton.tap();
|
await CreateDirectMessageScreen.startButton.tap();
|
||||||
|
|
||||||
// * Verify on direct message channel screen for the new user
|
// * Verify on direct message channel screen for the new user
|
||||||
|
|
@ -134,11 +139,13 @@ describe('Smoke Test - Channels', () => {
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4774_4 - should be able to find and edit a channel', async () => {
|
// TODO: Uncomment the test when the issue is fixed https://mattermost.atlassian.net/browse/MM-63814
|
||||||
|
it.skip('MM-T4774_4 - should be able to find and edit a channel', async () => {
|
||||||
// # Open find channels screen, search for the channel to navigate to, and tap on the target channel item
|
// # Open find channels screen, search for the channel to navigate to, and tap on the target channel item
|
||||||
await FindChannelsScreen.open();
|
await FindChannelsScreen.open();
|
||||||
await FindChannelsScreen.searchInput.replaceText(testChannel.name);
|
await FindChannelsScreen.searchInput.replaceText(testChannel.name);
|
||||||
await wait(timeouts.ONE_SEC);
|
await FindChannelsScreen.searchInput.tapReturnKey();
|
||||||
|
await waitFor(FindChannelsScreen.getFilteredChannelItem(testChannel.name)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
await FindChannelsScreen.getFilteredChannelItem(testChannel.name).tap();
|
await FindChannelsScreen.getFilteredChannelItem(testChannel.name).tap();
|
||||||
|
|
||||||
// * Verify on target channel screen
|
// * Verify on target channel screen
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
ServerListScreen,
|
ServerListScreen,
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {isAndroid, isIos, timeouts} from '@support/utils';
|
import {isAndroid, isIos, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Smoke Test - Server Login', () => {
|
describe('Smoke Test - Server Login', () => {
|
||||||
|
|
@ -69,7 +69,7 @@ describe('Smoke Test - Server Login', () => {
|
||||||
await User.apiAdminLogin(siteTwoUrl);
|
await User.apiAdminLogin(siteTwoUrl);
|
||||||
const {user} = await Setup.apiInit(siteTwoUrl);
|
const {user} = await Setup.apiInit(siteTwoUrl);
|
||||||
await ServerListScreen.addServerButton.tap();
|
await ServerListScreen.addServerButton.tap();
|
||||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||||
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
||||||
await LoginScreen.login(user);
|
await LoginScreen.login(user);
|
||||||
|
|
||||||
|
|
@ -102,6 +102,7 @@ describe('Smoke Test - Server Login', () => {
|
||||||
|
|
||||||
// # Tap on logout button
|
// # Tap on logout button
|
||||||
await Alert.logoutButton.tap();
|
await Alert.logoutButton.tap();
|
||||||
|
await wait(timeouts.FOUR_SEC);
|
||||||
|
|
||||||
// * Verify second server is logged out
|
// * Verify second server is logged out
|
||||||
await ServerListScreen.getServerItemInactive(serverTwoDisplayName).swipe('left');
|
await ServerListScreen.getServerItemInactive(serverTwoDisplayName).swipe('left');
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,6 @@ describe('Teams - Invite', () => {
|
||||||
await expect(Invite.teamDisplayName).toHaveText(testTeam.display_name);
|
await expect(Invite.teamDisplayName).toHaveText(testTeam.display_name);
|
||||||
await expect(Invite.teamIcon).toBeVisible();
|
await expect(Invite.teamIcon).toBeVisible();
|
||||||
|
|
||||||
// * Verify default Selection
|
|
||||||
await waitFor(Invite.screenSelection).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
|
||||||
|
|
||||||
// * Verify Server data
|
// * Verify Server data
|
||||||
await expect(Invite.serverDisplayName).toHaveText(serverOneDisplayName);
|
await expect(Invite.serverDisplayName).toHaveText(serverOneDisplayName);
|
||||||
|
|
||||||
|
|
@ -139,7 +136,8 @@ describe('Teams - Invite', () => {
|
||||||
await expect(Invite.getSearchListUserItem(testUser1.id)).not.toBeVisible();
|
await expect(Invite.getSearchListUserItem(testUser1.id)).not.toBeVisible();
|
||||||
|
|
||||||
// * Validate user is added to selected items
|
// * Validate user is added to selected items
|
||||||
await expect(Invite.getSelectedItem(testUser1.id)).toBeVisible();
|
await expect(Invite.getSelectedChip()).toBeVisible();
|
||||||
|
await expect(Invite.getSelectedChip()).toHaveText(testUser1.username);
|
||||||
|
|
||||||
// # Send invitation
|
// # Send invitation
|
||||||
await Invite.sendButton.tap();
|
await Invite.sendButton.tap();
|
||||||
|
|
@ -163,7 +161,8 @@ describe('Teams - Invite', () => {
|
||||||
await Invite.getSearchListUserItem(testUser2.id).tap();
|
await Invite.getSearchListUserItem(testUser2.id).tap();
|
||||||
|
|
||||||
// * Validate user is added to selected items
|
// * Validate user is added to selected items
|
||||||
await expect(Invite.getSelectedItem(testUser2.id)).toBeVisible();
|
await expect(Invite.getSelectedChip()).toBeVisible();
|
||||||
|
await expect(Invite.getSelectedChip()).toHaveText(testUser2.username);
|
||||||
|
|
||||||
// # Send invitation
|
// # Send invitation
|
||||||
await Invite.sendButton.tap();
|
await Invite.sendButton.tap();
|
||||||
|
|
@ -190,7 +189,8 @@ describe('Teams - Invite', () => {
|
||||||
await Invite.getSearchListUserItem(testUser3.id).tap();
|
await Invite.getSearchListUserItem(testUser3.id).tap();
|
||||||
|
|
||||||
// * Validate user is added to selected items
|
// * Validate user is added to selected items
|
||||||
await expect(Invite.getSelectedItem(testUser3.id)).toBeVisible();
|
await expect(Invite.getSelectedChip()).toBeVisible();
|
||||||
|
await expect(Invite.getSelectedChip()).toHaveText(testUser3.username);
|
||||||
|
|
||||||
// # Search for a existent user already in team
|
// # Search for a existent user already in team
|
||||||
await Invite.searchBarInput.replaceText(testUser.username);
|
await Invite.searchBarInput.replaceText(testUser.username);
|
||||||
|
|
@ -201,9 +201,6 @@ describe('Teams - Invite', () => {
|
||||||
// # Select user item
|
// # Select user item
|
||||||
await Invite.getSearchListUserItem(testUser.id).tap();
|
await Invite.getSearchListUserItem(testUser.id).tap();
|
||||||
|
|
||||||
// * Validate user is added to selected items
|
|
||||||
await expect(Invite.getSelectedItem(testUser.id)).toBeVisible();
|
|
||||||
|
|
||||||
// # Send invitation
|
// # Send invitation
|
||||||
await Invite.sendButton.tap();
|
await Invite.sendButton.tap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,8 @@ describe('Threads - Follow and Unfollow Thread', () => {
|
||||||
await wait(timeouts.ONE_SEC);
|
await wait(timeouts.ONE_SEC);
|
||||||
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible();
|
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible();
|
||||||
|
|
||||||
|
await wait(timeouts.FOUR_SEC);
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await GlobalThreadsScreen.back();
|
await GlobalThreadsScreen.back();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import {
|
||||||
ServerScreen,
|
ServerScreen,
|
||||||
ThreadScreen,
|
ThreadScreen,
|
||||||
} from '@support/ui/screen';
|
} from '@support/ui/screen';
|
||||||
import {getRandomId} from '@support/utils';
|
import {getRandomId, timeouts} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
describe('Threads - Global Threads', () => {
|
describe('Threads - Global Threads', () => {
|
||||||
|
|
@ -67,7 +67,7 @@ describe('Threads - Global Threads', () => {
|
||||||
await GlobalThreadsScreen.back();
|
await GlobalThreadsScreen.back();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('MM-T4805_2 - should be able to go to a thread a user started and followed -- UNSTABLE', async () => {
|
it('MM-T4805_2 - should be able to go to a thread a user started and followed', async () => {
|
||||||
// # Create a thread started by the current user which current user replied to
|
// # Create a thread started by the current user which current user replied to
|
||||||
const parentMessage = `Message ${getRandomId()}`;
|
const parentMessage = `Message ${getRandomId()}`;
|
||||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||||
|
|
@ -85,7 +85,6 @@ describe('Threads - Global Threads', () => {
|
||||||
// # Go back to channel list screen, then go to global threads screen, and tap on all your threads button
|
// # Go back to channel list screen, then go to global threads screen, and tap on all your threads button
|
||||||
await ThreadScreen.back();
|
await ThreadScreen.back();
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
await device.reloadReactNative();
|
|
||||||
await GlobalThreadsScreen.open();
|
await GlobalThreadsScreen.open();
|
||||||
await GlobalThreadsScreen.headerAllThreadsButton.tap();
|
await GlobalThreadsScreen.headerAllThreadsButton.tap();
|
||||||
|
|
||||||
|
|
@ -93,7 +92,13 @@ describe('Threads - Global Threads', () => {
|
||||||
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible();
|
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible();
|
||||||
await expect(GlobalThreadsScreen.getThreadItemThreadStarterUserDisplayName(parentPost.id)).toHaveText(testUser.username);
|
await expect(GlobalThreadsScreen.getThreadItemThreadStarterUserDisplayName(parentPost.id)).toHaveText(testUser.username);
|
||||||
await expect(GlobalThreadsScreen.getThreadItemThreadStarterChannelDisplayName(parentPost.id)).toHaveText(testChannel.display_name.toUpperCase());
|
await expect(GlobalThreadsScreen.getThreadItemThreadStarterChannelDisplayName(parentPost.id)).toHaveText(testChannel.display_name.toUpperCase());
|
||||||
await expect(GlobalThreadsScreen.getThreadItemFooterReplyCount(parentPost.id)).toHaveText('1 reply');
|
try {
|
||||||
|
// The reply count is shown as read.
|
||||||
|
await waitFor(GlobalThreadsScreen.getThreadItemFooterReplyCount(parentPost.id)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
} catch (error) {
|
||||||
|
// somtimes the app shows it as unread since the test actions are fast.
|
||||||
|
await waitFor(GlobalThreadsScreen.getThreadItemFooterUnreadReplies(parentPost.id)).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||||
|
}
|
||||||
|
|
||||||
// # Tap on the thread
|
// # Tap on the thread
|
||||||
await GlobalThreadsScreen.getThreadItem(parentPost.id).tap();
|
await GlobalThreadsScreen.getThreadItem(parentPost.id).tap();
|
||||||
|
|
@ -133,7 +138,7 @@ describe('Threads - Global Threads', () => {
|
||||||
await GlobalThreadsScreen.back();
|
await GlobalThreadsScreen.back();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('MM-T4805_4 - should be able to go to a thread a user replied to and followed -- UNSTABLE', async () => {
|
it('MM-T4805_4 - should be able to go to a thread a user replied to and followed', async () => {
|
||||||
// # Create a thread started by another user which the current user replied to
|
// # Create a thread started by another user which the current user replied to
|
||||||
const parentMessage = `Message ${getRandomId()}`;
|
const parentMessage = `Message ${getRandomId()}`;
|
||||||
const {post: parentPost} = await Post.apiCreatePost(siteOneUrl, {
|
const {post: parentPost} = await Post.apiCreatePost(siteOneUrl, {
|
||||||
|
|
@ -153,15 +158,13 @@ describe('Threads - Global Threads', () => {
|
||||||
// # Go back to channel list screen, then go to global threads screen, and tap on all your threads button
|
// # Go back to channel list screen, then go to global threads screen, and tap on all your threads button
|
||||||
await ThreadScreen.back();
|
await ThreadScreen.back();
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
await device.reloadReactNative();
|
|
||||||
await GlobalThreadsScreen.open();
|
await GlobalThreadsScreen.open();
|
||||||
await GlobalThreadsScreen.headerAllThreadsButton.tap();
|
await GlobalThreadsScreen.headerAllThreadsButton.tap();
|
||||||
|
|
||||||
// * Verify the thread replied to by the current user is displayed
|
// * Verify the thread replied to by the current user is displayed
|
||||||
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible();
|
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible();
|
||||||
await expect(GlobalThreadsScreen.getThreadItemThreadStarterUserDisplayName(parentPost.id)).toHaveText('sysadmin');
|
await expect(GlobalThreadsScreen.getThreadItemThreadStarterUserDisplayName(parentPost.id)).toHaveText('admin');
|
||||||
await expect(GlobalThreadsScreen.getThreadItemThreadStarterChannelDisplayName(parentPost.id)).toHaveText(testChannel.display_name.toUpperCase());
|
await expect(GlobalThreadsScreen.getThreadItemThreadStarterChannelDisplayName(parentPost.id)).toHaveText(testChannel.display_name.toUpperCase());
|
||||||
await expect(GlobalThreadsScreen.getThreadItemFooterReplyCount(parentPost.id)).toHaveText('1 reply');
|
|
||||||
|
|
||||||
// # Tap on the thread
|
// # Tap on the thread
|
||||||
await GlobalThreadsScreen.getThreadItem(parentPost.id).tap();
|
await GlobalThreadsScreen.getThreadItem(parentPost.id).tap();
|
||||||
|
|
|
||||||
|
|
@ -119,9 +119,6 @@ describe('Threads - Save and Unsave Thread', () => {
|
||||||
// # Tap on thread overview unsave button
|
// # Tap on thread overview unsave button
|
||||||
await ThreadScreen.getThreadOverviewUnsaveButton().tap();
|
await ThreadScreen.getThreadOverviewUnsaveButton().tap();
|
||||||
|
|
||||||
// * Verify saved text is not displayed on the post pre-header
|
|
||||||
await expect(postListPostItemPreHeaderText).not.toBeVisible();
|
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await ThreadScreen.back();
|
await ThreadScreen.back();
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
|
|
|
||||||
83
detox/inject-detox-settings.js
Normal file
83
detox/inject-detox-settings.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
// 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');
|
||||||
|
|
||||||
|
// Path to APNG4Android gif AndroidManifest.xml
|
||||||
|
const apngGifManifestPath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../node_modules/APNG4Android/gif/src/androidTest/AndroidManifest.xml',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Content for APNG4Android gif AndroidManifest.xml
|
||||||
|
const apngGifManifestContent = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name="androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity"
|
||||||
|
android:exported="true"
|
||||||
|
tools:node="merge" />
|
||||||
|
<activity
|
||||||
|
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
|
||||||
|
android:exported="true"
|
||||||
|
tools:node="merge" />
|
||||||
|
<activity
|
||||||
|
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity"
|
||||||
|
android:exported="true"
|
||||||
|
tools:node="merge" />
|
||||||
|
</application>
|
||||||
|
</manifest>`;
|
||||||
|
|
||||||
|
// Path to APNG4Android frameanimation AndroidManifest.xml
|
||||||
|
const apngFrameAnimationManifestPath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../node_modules/APNG4Android/frameanimation/src/androidTest/AndroidManifest.xml',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Content for APNG4Android frameanimation AndroidManifest.xml
|
||||||
|
const apngFrameAnimationManifestContent = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name="androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity"
|
||||||
|
android:exported="false"
|
||||||
|
tools:node="merge" />
|
||||||
|
<activity
|
||||||
|
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
|
||||||
|
android:exported="false"
|
||||||
|
tools:node="merge" />
|
||||||
|
<activity
|
||||||
|
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity"
|
||||||
|
android:exported="false"
|
||||||
|
tools:node="merge" />
|
||||||
|
</application>
|
||||||
|
</manifest>`;
|
||||||
|
|
||||||
|
// Function to create AndroidManifest.xml for APNG4Android gif
|
||||||
|
function createApngGifManifest() {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path.dirname(apngGifManifestPath), {recursive: true});
|
||||||
|
fs.writeFileSync(apngGifManifestPath, apngGifManifestContent, 'utf-8');
|
||||||
|
console.log('APNG4Android gif AndroidManifest.xml created successfully.');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to create APNG4Android gif AndroidManifest.xml: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to create AndroidManifest.xml for APNG4Android frameanimation
|
||||||
|
function createApngFrameAnimationManifest() {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path.dirname(apngFrameAnimationManifestPath), {recursive: true});
|
||||||
|
fs.writeFileSync(apngFrameAnimationManifestPath, apngFrameAnimationManifestContent, 'utf-8');
|
||||||
|
console.log('APNG4Android frameanimation AndroidManifest.xml created successfully.');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to create APNG4Android frameanimation AndroidManifest.xml: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run updates
|
||||||
|
createApngGifManifest();
|
||||||
|
createApngFrameAnimationManifest();
|
||||||
415
detox/package-lock.json
generated
415
detox/package-lock.json
generated
|
|
@ -23,7 +23,7 @@
|
||||||
"babel-plugin-module-resolver": "5.0.2",
|
"babel-plugin-module-resolver": "5.0.2",
|
||||||
"client-oauth2": "4.3.3",
|
"client-oauth2": "4.3.3",
|
||||||
"deepmerge": "4.3.1",
|
"deepmerge": "4.3.1",
|
||||||
"detox": "20.33.0",
|
"detox": "20.37.0",
|
||||||
"form-data": "4.0.1",
|
"form-data": "4.0.1",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-circus": "29.7.0",
|
"jest-circus": "29.7.0",
|
||||||
|
|
@ -1098,14 +1098,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.26.3",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
|
||||||
"integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
|
"integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.26.3",
|
"@babel/parser": "^7.27.0",
|
||||||
"@babel/types": "^7.26.3",
|
"@babel/types": "^7.27.0",
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
"@jridgewell/trace-mapping": "^0.3.25",
|
"@jridgewell/trace-mapping": "^0.3.25",
|
||||||
"jsesc": "^3.0.2"
|
"jsesc": "^3.0.2"
|
||||||
|
|
@ -1145,18 +1144,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||||
"version": "7.25.9",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz",
|
||||||
"integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
|
"integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||||
"@babel/helper-member-expression-to-functions": "^7.25.9",
|
"@babel/helper-member-expression-to-functions": "^7.25.9",
|
||||||
"@babel/helper-optimise-call-expression": "^7.25.9",
|
"@babel/helper-optimise-call-expression": "^7.25.9",
|
||||||
"@babel/helper-replace-supers": "^7.25.9",
|
"@babel/helper-replace-supers": "^7.26.5",
|
||||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
|
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
|
||||||
"@babel/traverse": "^7.25.9",
|
"@babel/traverse": "^7.27.0",
|
||||||
"semver": "^6.3.1"
|
"semver": "^6.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -1261,11 +1259,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-plugin-utils": {
|
"node_modules/@babel/helper-plugin-utils": {
|
||||||
"version": "7.25.9",
|
"version": "7.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
|
||||||
"integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
|
"integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
|
|
@ -1289,15 +1286,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-replace-supers": {
|
"node_modules/@babel/helper-replace-supers": {
|
||||||
"version": "7.25.9",
|
"version": "7.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz",
|
||||||
"integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
|
"integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-member-expression-to-functions": "^7.25.9",
|
"@babel/helper-member-expression-to-functions": "^7.25.9",
|
||||||
"@babel/helper-optimise-call-expression": "^7.25.9",
|
"@babel/helper-optimise-call-expression": "^7.25.9",
|
||||||
"@babel/traverse": "^7.25.9"
|
"@babel/traverse": "^7.26.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
|
|
@ -1380,13 +1376,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.26.3",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||||
"integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
|
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.26.3"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
|
|
@ -2753,32 +2748,30 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.25.9",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.25.9",
|
"@babel/code-frame": "^7.26.2",
|
||||||
"@babel/parser": "^7.25.9",
|
"@babel/parser": "^7.27.0",
|
||||||
"@babel/types": "^7.25.9"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.26.4",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
|
||||||
"integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
|
"integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.26.2",
|
"@babel/code-frame": "^7.26.2",
|
||||||
"@babel/generator": "^7.26.3",
|
"@babel/generator": "^7.27.0",
|
||||||
"@babel/parser": "^7.26.3",
|
"@babel/parser": "^7.27.0",
|
||||||
"@babel/template": "^7.25.9",
|
"@babel/template": "^7.27.0",
|
||||||
"@babel/types": "^7.26.3",
|
"@babel/types": "^7.27.0",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"globals": "^11.1.0"
|
"globals": "^11.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -2787,11 +2780,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.26.3",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||||
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
|
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.25.9",
|
"@babel/helper-string-parser": "^7.25.9",
|
||||||
"@babel/helper-validator-identifier": "^7.25.9"
|
"@babel/helper-validator-identifier": "^7.25.9"
|
||||||
|
|
@ -2807,6 +2799,26 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@colors/colors": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.90"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dabh/diagnostics": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"colorspace": "1.1.x",
|
||||||
|
"enabled": "2.0.x",
|
||||||
|
"kuler": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@flatten-js/interval-tree": {
|
"node_modules/@flatten-js/interval-tree": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz",
|
||||||
|
|
@ -4276,6 +4288,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/triple-beam": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/uuid": {
|
"node_modules/@types/uuid": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
|
@ -4300,6 +4318,37 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@wix-pilot/core": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wix-pilot/core/-/core-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-iGjHOOpO13ZNfP3CZDlYhBZ7R6ZqO+1R38nDv5BCIOecD4A4z5/WpEyapvw+ZFsECjFZLWhuKNV0KTJoQAvxYw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"pngjs": "^7.0.0",
|
||||||
|
"winston": "^3.17.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expect": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"expect": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wix-pilot/detox": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wix-pilot/detox/-/detox-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-oCeYz7EhcWOyp0KbaJ3hipMbZ+nWuZ7Pvjno3sUzP1EebrZBZoRoDp16JZMR6H+i+h8OdqkrviI0mR2mYFZzMg==",
|
||||||
|
"dev": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@wix-pilot/core": "^3.1.6",
|
||||||
|
"detox": ">=20.33.0",
|
||||||
|
"expect": "29.x.x || 28.x.x || ^27.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
|
@ -4932,18 +4981,6 @@
|
||||||
"pnpm": ">=8"
|
"pnpm": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/child-process-promise": {
|
|
||||||
"version": "2.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz",
|
|
||||||
"integrity": "sha512-Fi4aNdqBsr0mv+jgWxcZ/7rAIC2mgihrptyVI4foh/rrjY/3BNjfP9+oaiFx/fzim+1ZyCNBae0DlyfQhSugog==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cross-spawn": "^7.0.6",
|
|
||||||
"node-version": "^1.0.0",
|
|
||||||
"promise-polyfill": "^6.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ci-info": {
|
"node_modules/ci-info": {
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
||||||
|
|
@ -5014,6 +5051,16 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/color": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.3",
|
||||||
|
"color-string": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
|
@ -5034,6 +5081,41 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/color-string": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/colorspace": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^3.1.3",
|
||||||
|
"text-hex": "1.0.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
|
@ -5232,19 +5314,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/detox": {
|
"node_modules/detox": {
|
||||||
"version": "20.33.0",
|
"version": "20.37.0",
|
||||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/detox/-/detox-20.37.0.tgz",
|
||||||
"integrity": "sha512-9KZC3NgCav/IvuWm/EaKNIL5lV2bJf23PPK67OHS2dMhJsDsNBRWP9k3RN7hzLzzUwAXMMoOmk+S1OpOsazNFA==",
|
"integrity": "sha512-OUI2p3z31Yku1USBia0jrlwoQoa6rANvVWuUSJyjBlRRKDSeOf/DG/ivk+R5FO+lYZZtA3pIbl6LSql/Bf6DpQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@wix-pilot/core": "^3.2.2",
|
||||||
|
"@wix-pilot/detox": "^1.0.11",
|
||||||
"ajv": "^8.6.3",
|
"ajv": "^8.6.3",
|
||||||
"bunyan": "^1.8.12",
|
"bunyan": "^1.8.12",
|
||||||
"bunyan-debug-stream": "^3.1.0",
|
"bunyan-debug-stream": "^3.1.0",
|
||||||
"caf": "^15.0.1",
|
"caf": "^15.0.1",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"child-process-promise": "^2.2.0",
|
|
||||||
"detox-copilot": "^0.0.27",
|
|
||||||
"execa": "^5.1.1",
|
"execa": "^5.1.1",
|
||||||
"find-up": "^5.0.0",
|
"find-up": "^5.0.0",
|
||||||
"fs-extra": "^11.0.0",
|
"fs-extra": "^11.0.0",
|
||||||
|
|
@ -5257,6 +5340,7 @@
|
||||||
"multi-sort-stream": "^1.0.3",
|
"multi-sort-stream": "^1.0.3",
|
||||||
"multipipe": "^4.0.0",
|
"multipipe": "^4.0.0",
|
||||||
"node-ipc": "9.2.1",
|
"node-ipc": "9.2.1",
|
||||||
|
"promisify-child-process": "^4.1.2",
|
||||||
"proper-lockfile": "^3.0.2",
|
"proper-lockfile": "^3.0.2",
|
||||||
"resolve-from": "^5.0.0",
|
"resolve-from": "^5.0.0",
|
||||||
"sanitize-filename": "^1.6.1",
|
"sanitize-filename": "^1.6.1",
|
||||||
|
|
@ -5290,13 +5374,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/detox-copilot": {
|
|
||||||
"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": {
|
"node_modules/detox/node_modules/find-up": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||||
|
|
@ -5508,6 +5585,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/enabled": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/error-ex": {
|
"node_modules/error-ex": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
|
|
@ -5723,6 +5806,12 @@
|
||||||
"bser": "2.1.1"
|
"bser": "2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fecha": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/filelist": {
|
"node_modules/filelist": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
|
|
@ -5793,6 +5882,12 @@
|
||||||
"flat": "cli.js"
|
"flat": "cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fn.name": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.9",
|
"version": "1.15.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
|
@ -6159,8 +6254,7 @@
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"license": "ISC"
|
|
||||||
},
|
},
|
||||||
"node_modules/interpret": {
|
"node_modules/interpret": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
|
|
@ -7408,6 +7502,12 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/kuler": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/leven": {
|
"node_modules/leven": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||||
|
|
@ -7466,6 +7566,23 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/logform": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@colors/colors": "1.6.0",
|
||||||
|
"@types/triple-beam": "^1.3.2",
|
||||||
|
"fecha": "^4.2.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||||
|
|
@ -7789,16 +7906,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/node-version": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nopt": {
|
"node_modules/nopt": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
||||||
|
|
@ -7858,6 +7965,15 @@
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/one-time": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fn.name": "1.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/onetime": {
|
"node_modules/onetime": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
|
|
@ -8159,6 +8275,15 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/popper.js": {
|
"node_modules/popper.js": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
|
|
@ -8322,12 +8447,14 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/promise-polyfill": {
|
"node_modules/promisify-child-process": {
|
||||||
"version": "6.1.0",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/promisify-child-process/-/promisify-child-process-4.1.2.tgz",
|
||||||
"integrity": "sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==",
|
"integrity": "sha512-APnkIgmaHNJpkAn7k+CrJSi9WMuff5ctYFbD0CO2XIPkM8yO7d/ShouU2clywbpHV/DUsyc4bpJCsNgddNtx4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prompts": {
|
"node_modules/prompts": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
|
|
@ -8816,6 +8943,15 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-stable-stringify": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sanitize-filename": {
|
"node_modules/sanitize-filename": {
|
||||||
"version": "1.6.3",
|
"version": "1.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
|
||||||
|
|
@ -8912,7 +9048,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
|
||||||
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
|
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
|
|
@ -8991,6 +9126,21 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/sisteransi": {
|
"node_modules/sisteransi": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||||
|
|
@ -9036,6 +9186,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/stack-trace": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
|
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stack-utils": {
|
"node_modules/stack-utils": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
|
||||||
|
|
@ -9327,6 +9486,12 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/text-hex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/throwback": {
|
"node_modules/throwback": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/throwback/-/throwback-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/throwback/-/throwback-4.1.0.tgz",
|
||||||
|
|
@ -9400,6 +9565,15 @@
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/triple-beam": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/truncate-utf8-bytes": {
|
"node_modules/truncate-utf8-bytes": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||||
|
|
@ -9692,6 +9866,70 @@
|
||||||
"which": "bin/which"
|
"which": "bin/which"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/winston": {
|
||||||
|
"version": "3.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
|
||||||
|
"integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@colors/colors": "^1.6.0",
|
||||||
|
"@dabh/diagnostics": "^2.0.2",
|
||||||
|
"async": "^3.2.3",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"logform": "^2.7.0",
|
||||||
|
"one-time": "^1.0.0",
|
||||||
|
"readable-stream": "^3.4.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"stack-trace": "0.0.x",
|
||||||
|
"triple-beam": "^1.3.0",
|
||||||
|
"winston-transport": "^4.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston-transport": {
|
||||||
|
"version": "4.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
|
||||||
|
"integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"logform": "^2.7.0",
|
||||||
|
"readable-stream": "^3.6.2",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston-transport/node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston/node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
|
@ -9736,7 +9974,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.3.0"
|
"node": ">=8.3.0"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
"babel-plugin-module-resolver": "5.0.2",
|
"babel-plugin-module-resolver": "5.0.2",
|
||||||
"client-oauth2": "4.3.3",
|
"client-oauth2": "4.3.3",
|
||||||
"deepmerge": "4.3.1",
|
"deepmerge": "4.3.1",
|
||||||
"detox": "20.33.0",
|
"detox": "20.37.0",
|
||||||
"form-data": "4.0.1",
|
"form-data": "4.0.1",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-circus": "29.7.0",
|
"jest-circus": "29.7.0",
|
||||||
|
|
@ -42,13 +42,14 @@
|
||||||
"xml2js": "0.6.2"
|
"xml2js": "0.6.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"e2e:android-inject-settings": "node inject-detox-settings.js",
|
||||||
"e2e:android-create-emulator": "./create_android_emulator.sh",
|
"e2e:android-create-emulator": "./create_android_emulator.sh",
|
||||||
"e2e:android-build": "detox build -c android.emu.debug",
|
"e2e:android-build": "detox build -c android.emu.debug",
|
||||||
"e2e:android-test": "detox test -c android.emu.debug",
|
"e2e:android-test": "detox test -c android.emu.debug --reuse --record-logs failing --take-screenshots failing",
|
||||||
"e2e:android-build-release": "detox build -c android.emu.release",
|
"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: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-build": "IOS=true detox build -c ios.sim.debug",
|
||||||
"e2e:ios-test": "IOS=true detox test -c ios.sim.debug",
|
"e2e:ios-test": "IOS=true detox test -c ios.sim.debug --reuse --record-logs failing --take-screenshots failing",
|
||||||
"e2e:ios-build-release": "detox build -c ios.sim.release",
|
"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",
|
"e2e:ios-test-release": "IOS=true detox test -c ios.sim.release --record-logs failing --take-screenshots failing",
|
||||||
"detox:config-gen": "cd utils && node generate_detox_config_ci.js",
|
"detox:config-gen": "cd utils && node generate_detox_config_ci.js",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ const saveReport = async () => {
|
||||||
|
|
||||||
// "ios-results-*" or "android-results-*" is the path in CI where the parallel detox jobs save the artifacts
|
// "ios-results-*" or "android-results-*" is the path in CI where the parallel detox jobs save the artifacts
|
||||||
await mergeJestStareJsonFiles(jestStareCombinedFilePath, [`${ARTIFACTS_DIR}/${platform}-results*/jest-stare/${platform}-data*.json`]);
|
await mergeJestStareJsonFiles(jestStareCombinedFilePath, [`${ARTIFACTS_DIR}/${platform}-results*/jest-stare/${platform}-data*.json`]);
|
||||||
generateJestStareHtmlReport(jestStareOutputDir, `${platform}-report.html`, jestStareCombinedFilePath);
|
generateJestStareHtmlReport(jestStareOutputDir, `${platform}-report.html`, jestStareCombinedFilePath, platform);
|
||||||
|
|
||||||
if (process.env.CI) {
|
if (process.env.CI) {
|
||||||
// Delete folders starting with "ios-results-" or "android-results-" only in CI environment
|
// Delete folders starting with "ios-results-" or "android-results-" only in CI environment
|
||||||
|
|
@ -133,7 +133,7 @@ const saveReport = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result = await saveArtifacts();
|
const result = await saveArtifacts(platform);
|
||||||
if (result && result.success) {
|
if (result && result.success) {
|
||||||
console.log('Successfully uploaded artifacts to S3:', result.reportLink);
|
console.log('Successfully uploaded artifacts to S3:', result.reportLink);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,8 @@ const {
|
||||||
DETOX_AWS_S3_BUCKET,
|
DETOX_AWS_S3_BUCKET,
|
||||||
DETOX_AWS_ACCESS_KEY_ID,
|
DETOX_AWS_ACCESS_KEY_ID,
|
||||||
DETOX_AWS_SECRET_ACCESS_KEY,
|
DETOX_AWS_SECRET_ACCESS_KEY,
|
||||||
IOS,
|
|
||||||
REPORT_PATH,
|
REPORT_PATH,
|
||||||
} = process.env;
|
} = process.env;
|
||||||
const platform = IOS === 'true' ? 'ios' : 'android';
|
|
||||||
|
|
||||||
const s3 = new S3({
|
const s3 = new S3({
|
||||||
credentials: {
|
credentials: {
|
||||||
|
|
@ -36,7 +34,7 @@ function getFiles(dirPath) {
|
||||||
return fs.existsSync(dirPath) ? readdir(dirPath) : [];
|
return fs.existsSync(dirPath) ? readdir(dirPath) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveArtifacts() {
|
async function saveArtifacts(platform) {
|
||||||
if (!DETOX_AWS_S3_BUCKET || !DETOX_AWS_ACCESS_KEY_ID || !DETOX_AWS_SECRET_ACCESS_KEY) {
|
if (!DETOX_AWS_S3_BUCKET || !DETOX_AWS_ACCESS_KEY_ID || !DETOX_AWS_SECRET_ACCESS_KEY) {
|
||||||
console.log('No AWS credentials found. Test artifacts not uploaded to S3.');
|
console.log('No AWS credentials found. Test artifacts not uploaded to S3.');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
/* eslint-disable no-process-env */
|
/* eslint-disable no-process-env */
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const deviceName = process.env.DEVICE_NAME || 'iPhone 16 Pro';
|
const deviceName = process.env.DEVICE_NAME || 'iPhone 15 Pro';
|
||||||
const deviceOSVersion = process.env.DEVICE_OS_VERSION || 'iOS 18.1';
|
const deviceOSVersion = process.env.DEVICE_OS_VERSION || 'iOS 17.2';
|
||||||
const detoxConfigTemplate = fs.readFileSync('../.detoxrc.json', 'utf8');
|
const detoxConfigTemplate = fs.readFileSync('../.detoxrc.json', 'utf8');
|
||||||
const detoxConfig = detoxConfigTemplate.replace('__DEVICE_NAME__', deviceName).replace('__DEVICE_OS_VERSION__', deviceOSVersion);
|
const detoxConfig = detoxConfigTemplate.replace('__DEVICE_NAME__', deviceName).replace('__DEVICE_OS_VERSION__', deviceOSVersion);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,9 @@ function collectReportSuites(reports) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateJestStareHtmlReport(outputDir, outputFile, inputFilePath) {
|
function generateJestStareHtmlReport(outputDir, outputFile, inputFilePath, platform) {
|
||||||
const suites = fse.readJsonSync(inputFilePath);
|
const suites = fse.readJsonSync(inputFilePath);
|
||||||
processor(suites, {log: false, resultDir: outputDir, resultHtml: outputFile, reportHeadline: 'Mobile App E2E with Detox and Jest'});
|
processor(suites, {log: false, resultDir: outputDir, resultHtml: outputFile, reportHeadline: `${platform} Mobile App E2E with Detox and Jest`});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function mergeJestStareJsonFiles(outputFilePath, inputFiles) {
|
async function mergeJestStareJsonFiles(outputFilePath, inputFiles) {
|
||||||
|
|
|
||||||
1
env.d.ts
vendored
1
env.d.ts
vendored
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
// So that typescript doesn't complain about importing `@env` through react-native-dotenv
|
// So that typescript doesn't complain about importing `@env` through react-native-dotenv
|
||||||
declare module '@env' {
|
declare module '@env' {
|
||||||
|
export const RUNNING_E2E: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'react-native-dotenv' {
|
declare module 'react-native-dotenv' {
|
||||||
|
|
|
||||||
2
index.ts
2
index.ts
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {RUNNING_E2E} from '@env';
|
||||||
import TurboLogger from '@mattermost/react-native-turbo-log';
|
import TurboLogger from '@mattermost/react-native-turbo-log';
|
||||||
import {LogBox, Platform, UIManager} from 'react-native';
|
import {LogBox, Platform, UIManager} from 'react-native';
|
||||||
import ViewReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
|
import ViewReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
|
||||||
import {RUNNING_E2E} from 'react-native-dotenv';
|
|
||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler';
|
||||||
import {Navigation} from 'react-native-navigation';
|
import {Navigation} from 'react-native-navigation';
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue