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"
|
||||
required: false
|
||||
type: string
|
||||
default: "33"
|
||||
default: "34"
|
||||
outputs:
|
||||
STATUS:
|
||||
value: ${{ jobs.generate-report.outputs.STATUS }}
|
||||
|
|
@ -130,7 +130,7 @@ jobs:
|
|||
name: android-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
continue-on-error: true
|
||||
timeout-minutes: 150
|
||||
timeout-minutes: 240
|
||||
env:
|
||||
ANDROID_HOME: /usr/local/lib/android/sdk
|
||||
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
|
||||
|
|
@ -170,6 +170,12 @@ jobs:
|
|||
cd detox
|
||||
npm install
|
||||
|
||||
- name: Set .env with RUNNING_E2E=true
|
||||
run: |
|
||||
cat > .env <<EOF
|
||||
RUNNING_E2E=true
|
||||
EOF
|
||||
|
||||
- name: Create destination path
|
||||
run: mkdir -p android/app/build
|
||||
|
||||
|
|
@ -194,18 +200,29 @@ jobs:
|
|||
|
||||
- name: Accept Android licenses
|
||||
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: |
|
||||
sdkmanager "system-images;android-34;default;x86_64"
|
||||
sdkmanager "platform-tools" "emulator"
|
||||
|
||||
yes | sdkmanager --install "platform-tools" "emulator" "platforms;android-34" "system-images;android-34;default;x86_64" "system-images;android-34;google_apis;x86_64"
|
||||
env:
|
||||
JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }}
|
||||
|
||||
- name: Create and run Android Emulator
|
||||
run: |
|
||||
cd detox
|
||||
chmod +x ./create_android_emulator.sh
|
||||
./create_android_emulator.sh ${{ env.SDK_VERSION }} ${{ env.AVD_NAME }} ${{ matrix.specs }}
|
||||
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
|
||||
|
||||
- name: Upload Android Test Report
|
||||
|
|
@ -293,3 +310,36 @@ jobs:
|
|||
echo "" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "You can check the full report [here](${{ steps.set-url.outputs.TARGET_URL }})" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "There was **${{ steps.summary.outputs.PERCENTAGE }}%** success rate." >> ${GITHUB_STEP_SUMMARY}
|
||||
|
||||
- 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:
|
||||
branches:
|
||||
- main
|
||||
- feature_schedule_posts
|
||||
types:
|
||||
- labeled
|
||||
|
||||
|
|
@ -104,6 +105,7 @@ jobs:
|
|||
cd detox
|
||||
npm install
|
||||
npm install -g detox-cli
|
||||
npm run e2e:android-inject-settings
|
||||
npm run e2e: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"
|
||||
required: false
|
||||
type: string
|
||||
default: "iPhone 16 Pro"
|
||||
default: "iPhone 15 Pro"
|
||||
ios_device_os_name:
|
||||
description: "iPhone simulator OS version"
|
||||
required: false
|
||||
type: string
|
||||
default: "iOS 18.1"
|
||||
default: "iOS 17.2"
|
||||
low_bandwidth_mode:
|
||||
description: "Enable low bandwidth mode"
|
||||
required: false
|
||||
|
|
@ -81,7 +81,7 @@ env:
|
|||
WEBHOOK_URL: ${{ secrets.MM_MOBILE_E2E_WEBHOOK_URL }}
|
||||
FAILURE_MESSAGE: "Something has failed"
|
||||
IOS: "true"
|
||||
RUNNING_E2E: true
|
||||
RUNNING_E2E: "true"
|
||||
|
||||
jobs:
|
||||
generate-specs:
|
||||
|
|
@ -124,7 +124,7 @@ jobs:
|
|||
name: ios-detox-e2e-${{ matrix.runId }}-${{ matrix.deviceName }}-${{ matrix.deviceOsVersion }}
|
||||
runs-on: macos-14
|
||||
continue-on-error: true
|
||||
timeout-minutes: ${{ inputs.low_bandwidth_mode && 140 || 70 }}
|
||||
timeout-minutes: ${{ inputs.low_bandwidth_mode && 240 || 180 }}
|
||||
env:
|
||||
IOS: true
|
||||
needs:
|
||||
|
|
@ -153,7 +153,10 @@ jobs:
|
|||
path: mobile-artifacts
|
||||
|
||||
- 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
|
||||
id: prepare-low-bandwidth
|
||||
|
|
@ -167,9 +170,6 @@ jobs:
|
|||
upload_speed: "3300"
|
||||
latency: "500"
|
||||
|
||||
- name: Start React Native Metro Server
|
||||
run: npm run start &
|
||||
|
||||
- name: Install Detox Dependencies
|
||||
run: cd detox && npm i
|
||||
|
||||
|
|
@ -180,13 +180,25 @@ jobs:
|
|||
with:
|
||||
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
|
||||
continue-on-error: true # We want to run all the tests
|
||||
run: |
|
||||
# Start the server
|
||||
npm run start &
|
||||
sleep 120 # Wait for watchman to finish querying the files
|
||||
cd detox
|
||||
npm run clean-detox
|
||||
npm run detox:config-gen
|
||||
npm run e2e:ios-test -- ${{ matrix.specs }}
|
||||
env:
|
||||
DETOX_DISABLE_HIERARCHY_DUMP: "YES"
|
||||
DETOX_DISABLE_SCREENSHOT_TRACKING: "YES"
|
||||
DETOX_LOGLEVEL: "debug"
|
||||
DETOX_DEVICE_TYPE: ${{ env.DEVICE_NAME }}
|
||||
DETOX_OS_VERSION: ${{ env.DEVICE_OS_VERSION }}
|
||||
LOW_BANDWIDTH_MODE: ${{ inputs.low_bandwidth_mode }}
|
||||
|
|
@ -295,3 +307,36 @@ jobs:
|
|||
echo "" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "You can check the full report [here](${{ steps.set-url.outputs.TARGET_URL }})" >> ${GITHUB_STEP_SUMMARY}
|
||||
echo "There was **${{ steps.summary.outputs.PERCENTAGE }}%** success rate." >> ${GITHUB_STEP_SUMMARY}
|
||||
|
||||
- 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 (
|
||||
<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(
|
||||
{id: 'channel_info.scheduled', defaultMessage: 'Send on {time}'},
|
||||
{time: scheduledTime},
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@ function ScheduledPostIndicator({
|
|||
name='clock-send-outline'
|
||||
size={18}
|
||||
/>
|
||||
<Text style={styles.text}>
|
||||
<Text
|
||||
style={styles.text}
|
||||
testID='scheduled_post_header.scheduled_post_indicator'
|
||||
>
|
||||
{scheduledPostText}
|
||||
{' '}
|
||||
<Text
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ module.exports = {
|
|||
},
|
||||
}],
|
||||
['module:react-native-dotenv', {
|
||||
moduleName: 'react-native-dotenv',
|
||||
moduleName: '@env',
|
||||
path: '.env',
|
||||
blacklist: null,
|
||||
whitelist: null,
|
||||
|
|
|
|||
|
|
@ -60,11 +60,35 @@
|
|||
}
|
||||
},
|
||||
"artifacts": {
|
||||
"pathBuilder": "./e2e/path_builder.js"
|
||||
"pathBuilder": "./e2e/path_builder.js",
|
||||
"plugins": {
|
||||
"screenshot": {
|
||||
"shouldTakeAutomaticSnapshots": true,
|
||||
"keepOnlyFailedTestsArtifacts": true
|
||||
},
|
||||
"video": {
|
||||
"enabled": false
|
||||
},
|
||||
"log": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"behavior": {
|
||||
"init": {
|
||||
"reinstallApp": true,
|
||||
"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:
|
||||
|
||||
```sh
|
||||
npm run e2e:android-inject-settings
|
||||
|
||||
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
|
||||
|
||||
#### 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:
|
||||
|
||||
```sh
|
||||
npm run e2e:android-test
|
||||
|
||||
# To run a particular tests
|
||||
|
||||
npm run e2e:android-test <path to test file>
|
||||
```
|
||||
|
||||
## iOS
|
||||
|
|
@ -40,10 +58,30 @@ To build the iOS simulator for Detox, navigate to the `detox` folder and run:
|
|||
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
|
||||
|
||||
To execute the Detox tests on iOS, navigate to the `detox` folder and run:
|
||||
|
||||
```sh
|
||||
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
|
||||
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/"
|
||||
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() {
|
||||
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
|
||||
emulator $emulator_opts -gpu host -accel on -qemu -m 4096 &
|
||||
|
|
@ -82,6 +82,7 @@ start_server() {
|
|||
cd ..
|
||||
RUNNING_E2E=true npm run start &
|
||||
local timeout=120 interval=5 elapsed=0
|
||||
sleep $timeout
|
||||
|
||||
until nc -z localhost 8081; do
|
||||
if [[ $elapsed -ge $timeout ]]; then
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module.exports = {
|
|||
setupFilesAfterEnv: ['./test/setup.ts'],
|
||||
maxWorkers: 1,
|
||||
testSequencer: './custom_sequencer.js',
|
||||
testTimeout: 120000,
|
||||
testTimeout: 180000,
|
||||
rootDir: '.',
|
||||
testMatch: ['<rootDir>/test/**/*.e2e.ts'],
|
||||
transform: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
|
||||
import client from './client';
|
||||
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
|
||||
*/
|
||||
export const apiGetLastPostInChannel = async (baseUrl: string, channelId: string): Promise<any> => {
|
||||
await wait(timeouts.TWO_SEC);
|
||||
const {posts} = await apiGetPostsInChannel(baseUrl, channelId);
|
||||
return {post: posts[0]};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class Alert {
|
|||
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).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);
|
||||
deleteScheduledMessageButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(1);
|
||||
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);
|
||||
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',
|
||||
channelDropdownTextShared: 'browse_channels.channel_dropdown.text.shared',
|
||||
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));
|
||||
closeButton = element(by.id(this.testID.closeButton));
|
||||
createButton = element(by.id(this.testID.createButton));
|
||||
|
|
@ -60,6 +62,15 @@ class BrowseChannelsScreen {
|
|||
await this.closeButton.tap();
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
PostOptionsScreen,
|
||||
ThreadScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
import {isIos, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
class ChannelScreen {
|
||||
|
|
@ -47,8 +47,31 @@ class ChannelScreen {
|
|||
postPriorityUrgentMessage: 'post_priority_picker_item.urgent',
|
||||
postPriorityRequestAck: 'post_priority_picker_item.requested_ack.toggled.false.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));
|
||||
postPriorityUrgentMessage = element(by.id(this.testID.postPriorityUrgentMessage));
|
||||
postPriorityRequestAck = element(by.id(this.testID.postPriorityRequestAck));
|
||||
|
|
@ -132,7 +155,12 @@ class ChannelScreen {
|
|||
open = async (categoryKey: string, channelName: string) => {
|
||||
// # Open channel screen
|
||||
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();
|
||||
};
|
||||
|
||||
|
|
@ -169,7 +197,7 @@ class ChannelScreen {
|
|||
await expect(postListPostItem).toBeVisible();
|
||||
|
||||
// # Open post options
|
||||
await postListPostItem.longPress();
|
||||
await postListPostItem.longPress(timeouts.TWO_SEC);
|
||||
await PostOptionsScreen.toBeVisible();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
};
|
||||
|
|
@ -190,6 +218,12 @@ class ChannelScreen {
|
|||
await this.tapSendButton();
|
||||
};
|
||||
|
||||
enterMessageToSchedule = async (message: string) => {
|
||||
await this.postInput.tap();
|
||||
await this.postInput.clearText();
|
||||
await this.postInput.replaceText(message);
|
||||
};
|
||||
|
||||
tapSendButton = async () => {
|
||||
// # Tap send button
|
||||
await this.sendButton.tap();
|
||||
|
|
@ -197,6 +231,11 @@ class ChannelScreen {
|
|||
await expect(this.sendButtonDisabled).toBeVisible();
|
||||
};
|
||||
|
||||
longPressSendButton = async () => {
|
||||
// # Long press send button
|
||||
await this.sendButton.longPress();
|
||||
};
|
||||
|
||||
hasPostMessage = async (postId: string, postMessage: string) => {
|
||||
const {postListPostItem} = this.getPostListPostItem(postId, postMessage);
|
||||
await expect(postListPostItem).toBeVisible();
|
||||
|
|
@ -231,6 +270,47 @@ class ChannelScreen {
|
|||
applyPostPrioritySettings = async () => {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ class ChannelInfoScreen {
|
|||
noButton,
|
||||
yesButton,
|
||||
} = Alert;
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await expect(alertArchiveChannelTitle).toBeVisible();
|
||||
await expect(noButton).toBeVisible();
|
||||
await expect(yesButton).toBeVisible();
|
||||
|
|
@ -141,7 +142,7 @@ class ChannelInfoScreen {
|
|||
convertToPrivateChannel = async (channelDisplayName: string, {confirm = true} = {}) => {
|
||||
await this.scrollView.tap({x: 1, y: 1});
|
||||
await this.scrollView.scroll(100, 'down');
|
||||
await waitFor(this.convertPrivateOption).toExist().withTimeout(timeouts.TWO_SEC);
|
||||
await waitFor(this.convertPrivateOption).toBeVisible().whileElement(by.id(this.testID.scrollView)).scroll(50, 'down');
|
||||
await this.convertPrivateOption.tap({x: 1, y: 1});
|
||||
const {
|
||||
channelNowPrivateTitle,
|
||||
|
|
@ -205,6 +206,7 @@ class ChannelInfoScreen {
|
|||
|
||||
unarchiveChannel = async (alertUnarchiveChannelTitle: Detox.NativeElement, {confirm = true} = {}) => {
|
||||
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});
|
||||
const {
|
||||
noButton,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class ChannelListScreen {
|
|||
draftChannelInfo: 'draft_post.channel_info',
|
||||
draftbuttonListScreen: 'channel_list.drafts.button',
|
||||
draftCountListScreen: 'channel_list.drafts.count',
|
||||
scheduledMessageCountListScreen: 'channel_list.scheduled_post.count',
|
||||
teamItemPrefix: 'team_sidebar.team_list.team_item.',
|
||||
channelListScreen: 'channel_list.screen',
|
||||
serverIcon: 'channel_list.servers.server_icon',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {expect} from 'detox';
|
|||
|
||||
class CreateDirectMessageScreen {
|
||||
testID = {
|
||||
selectedDMUserPrefix: 'create_direct_message.',
|
||||
selectedUserPrefix: 'create_direct_message.selected_user.',
|
||||
userItemPrefix: 'create_direct_message.user_list.user_item.',
|
||||
createDirectMessageScreen: 'create_direct_message.screen',
|
||||
|
|
@ -20,8 +21,10 @@ class CreateDirectMessageScreen {
|
|||
sectionUserList: 'create_direct_message.user_list.section_list',
|
||||
tutorialHighlight: 'tutorial_highlight',
|
||||
tutorialSwipeLeft: 'tutorial_swipe_left',
|
||||
scheduledPostTooltipCloseButton: 'scheduled_post.tooltip.close.button',
|
||||
};
|
||||
|
||||
scheduledPostTooltipCloseButton = element(by.id(this.testID.scheduledPostTooltipCloseButton));
|
||||
createDirectMessageScreen = element(by.id(this.testID.createDirectMessageScreen));
|
||||
closeButton = element(by.id(this.testID.closeButton));
|
||||
startButton = element(by.id(this.testID.startButton));
|
||||
|
|
@ -41,6 +44,10 @@ class CreateDirectMessageScreen {
|
|||
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) => {
|
||||
return element(by.id(`${this.testID.selectedUserPrefix}${userId}.remove.button`));
|
||||
};
|
||||
|
|
@ -70,7 +77,7 @@ class CreateDirectMessageScreen {
|
|||
await ChannelListScreen.headerPlusButton.tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await ChannelListScreen.openDirectMessageItem.tap();
|
||||
|
||||
await wait(timeouts.TEN_SEC);
|
||||
return this.toBeVisible();
|
||||
};
|
||||
|
||||
|
|
@ -80,14 +87,19 @@ class CreateDirectMessageScreen {
|
|||
};
|
||||
|
||||
closeTutorial = async () => {
|
||||
if (isIos()) {
|
||||
await waitFor(this.tutorialHighlight).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await this.tutorialSwipeLeft.tap();
|
||||
await expect(this.tutorialHighlight).not.toExist();
|
||||
} else {
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await device.pressBack();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
try {
|
||||
if (isIos()) {
|
||||
await waitFor(this.tutorialHighlight).toExist().withTimeout(timeouts.HALF_MIN);
|
||||
await this.tutorialSwipeLeft.tap();
|
||||
await expect(this.tutorialHighlight).not.toExist();
|
||||
} else {
|
||||
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 () => {
|
||||
// # Open edit channel screen
|
||||
await ChannelInfoScreen.scrollView.tap({x: 1, y: 1});
|
||||
await ChannelInfoScreen.scrollView.scroll(100, 'down');
|
||||
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||
await ChannelInfoScreen.editChannelOption.tap();
|
||||
|
||||
return this.toBeVisible();
|
||||
|
|
@ -96,6 +96,16 @@ class CreateOrEditChannelScreen {
|
|||
await this.makePrivateToggledOn.tap();
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class DraftScreen {
|
|||
draftMessageContent: 'draft_message',
|
||||
draftScreen: 'global_drafts_list',
|
||||
draftTooltipCloseButton: 'draft.tooltip.close.button',
|
||||
draftPost: 'draft_post',
|
||||
draftPost: 'draft_message',
|
||||
draftSendButton: 'send_draft_button',
|
||||
draftEmptyTitle: 'drafts.empty.title',
|
||||
requestACKIcon: 'drafts.requested_ack.icon',
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ class EditProfileScreen {
|
|||
closeButton: 'close.edit_profile.button',
|
||||
saveButton: 'edit_profile.save.button',
|
||||
scrollView: 'edit_profile.scroll_view',
|
||||
firstNameInput: 'edit_profile_form.first_name.input',
|
||||
firstNameInputDisabled: 'edit_profile_form.first_name.input.disabled',
|
||||
lastNameInput: 'edit_profile_form.last_name.input',
|
||||
lastNameInputDisabled: 'edit_profile_form.last_name.input.disabled',
|
||||
firstNameInput: 'edit_profile_form.firstName.input',
|
||||
firstNameInputDisabled: 'edit_profile_form.firstName.input.disabled',
|
||||
lastNameInput: 'edit_profile_form.lastName.input',
|
||||
lastNameInputDisabled: 'edit_profile_form.lastName.input.disabled',
|
||||
usernameInput: 'edit_profile_form.username.input',
|
||||
usernameInputDisabled: 'edit_profile_form.username.input.disabled',
|
||||
emailInput: 'edit_profile_form.email.input',
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import PushNotificationSettingsScreen from './push_notification_settings';
|
|||
import ReactionsScreen from './reactions';
|
||||
import RecentMentionsScreen from './recent_mentions';
|
||||
import SavedMessagesScreen from './saved_messages';
|
||||
import ScheduleMessageScreen from './scheduled_message_screen';
|
||||
import SearchMessagesScreen from './search_messages';
|
||||
import SelectTimezoneScreen from './select_timezone';
|
||||
import ServerScreen from './server';
|
||||
|
|
@ -95,4 +96,5 @@ export {
|
|||
TimezoneDisplaySettingsScreen,
|
||||
UserProfileScreen,
|
||||
DraftScreen,
|
||||
ScheduleMessageScreen,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -79,6 +79,10 @@ class InviteScreen {
|
|||
return element(by.id(`${this.testID.selectedItemPrefix}.${id}`));
|
||||
};
|
||||
|
||||
getSelectedChip = () => {
|
||||
return element(by.id('invite.selected_item.display_name'));
|
||||
};
|
||||
|
||||
getSummaryReportSent = () => {
|
||||
return element(by.id(`${this.testID.summaryReportPrefix}.sent`));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
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';
|
||||
|
||||
class LoginScreen {
|
||||
|
|
@ -21,8 +21,10 @@ class LoginScreen {
|
|||
forgotPasswordButton: 'login_form.forgot_password.button',
|
||||
signinButton: 'login_form.signin.button',
|
||||
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));
|
||||
backButton = element(by.id(this.testID.backButton));
|
||||
titleLoginToAccount = element(by.id(this.testID.titleLoginToAccount));
|
||||
|
|
@ -58,17 +60,20 @@ class LoginScreen {
|
|||
await expect(this.loginScreen).not.toBeVisible();
|
||||
};
|
||||
|
||||
login = async (user: any = {}) => {
|
||||
loginWithRetryIfStuck = async (user: any = {}) => {
|
||||
await this.toBeVisible();
|
||||
|
||||
await this.usernameInput.tap({x: 150, y: 10});
|
||||
await this.usernameInput.replaceText(user.newUser.email);
|
||||
await this.passwordInput.tap();
|
||||
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 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 = {}) => {
|
||||
|
|
@ -78,9 +83,9 @@ class LoginScreen {
|
|||
await this.usernameInput.replaceText(user.username);
|
||||
await this.passwordInput.tap();
|
||||
await this.passwordInput.replaceText(user.password);
|
||||
await this.loginFormInfoText.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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
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) => {
|
||||
return element(by.id(`search.recent_item.${searchTerm}.remove.button`));
|
||||
return element(by.id(`search.recent_item.${searchTerm}.remove.button`)).atIndex(0);
|
||||
};
|
||||
|
||||
toBeVisible = async () => {
|
||||
|
|
@ -87,6 +87,7 @@ class SearchMessagesScreen {
|
|||
|
||||
openPostOptionsFor = async (postId: string, text: string) => {
|
||||
const {postListPostItem} = this.getPostListPostItem(postId, text);
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await expect(postListPostItem).toBeVisible();
|
||||
|
||||
// # 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 () => {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class ThreadScreen {
|
|||
|
||||
back = async () => {
|
||||
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) => {
|
||||
|
|
@ -112,6 +112,17 @@ class ThreadScreen {
|
|||
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 () => {
|
||||
// # Tap send button
|
||||
await this.sendButton.tap();
|
||||
|
|
|
|||
|
|
@ -77,3 +77,28 @@ export const timeouts = {
|
|||
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.saveButton).toBeVisible();
|
||||
await expect(EditProfileScreen.getEditProfilePicture(testUser.id)).toBeVisible();
|
||||
await waitFor(EditProfileScreen.firstNameInput).toExist().withTimeout(2000);
|
||||
if (isIos()) {
|
||||
await expect(EditProfileScreen.firstNameInput).toHaveValue(testUser.first_name);
|
||||
await expect(EditProfileScreen.lastNameInput).toHaveValue(testUser.last_name);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
ServerScreen,
|
||||
SettingsScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId, isIos} from '@support/utils';
|
||||
import {getRandomId, isIos, timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Account - Settings - Mention Notification Settings', () => {
|
||||
|
|
@ -84,7 +84,7 @@ describe('Account - Settings - Mention Notification Settings', () => {
|
|||
const triggerMentionKeyword = await MentionNotificationSettingsScreen.getKeywordTriggerElement(keywords);
|
||||
await expect(triggerMentionKeyword).toHaveText(keywords.replace(/ /g, '').toLowerCase());
|
||||
} 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
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
// # Tap on a sapphire option and tap on back button
|
||||
await ThemeDisplaySettingsScreen.sapphireOption.tap();
|
||||
await ThemeDisplaySettingsScreen.back();
|
||||
|
||||
// * Verify on display settings screen and sapphire is set
|
||||
await DisplaySettingsScreen.toBeVisible();
|
||||
|
|
@ -80,7 +79,6 @@ describe('Account - Settings - Theme Display Settings', () => {
|
|||
|
||||
// # Tap on denim option and tap on back button
|
||||
await ThemeDisplaySettingsScreen.denimOption.tap();
|
||||
await ThemeDisplaySettingsScreen.back();
|
||||
|
||||
// * Verify on display settings screen and denim is set
|
||||
await DisplaySettingsScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ describe('Autocomplete - At-Mention', () => {
|
|||
|
||||
// # Log in to server
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
await LoginScreen.login(testUser);
|
||||
await LoginScreen.login(user);
|
||||
|
||||
// * Verify on channel list screen
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ describe('Autocomplete - Slash Suggestion', () => {
|
|||
await slashSuggestionAutocomplete.tap();
|
||||
|
||||
// * 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
|
||||
await ChannelScreen.postInput.typeText('/');
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ describe('Channels - Browse Channels', () => {
|
|||
|
||||
// # Tap on the new public channel item
|
||||
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
||||
await BrowseChannelsScreen.dismissScheduledPostTooltip();
|
||||
|
||||
// * Verify on newly joined public channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ describe('Channels - Channel Info', () => {
|
|||
await expect(ChannelInfoScreen.muteAction).toBeVisible();
|
||||
await expect(ChannelInfoScreen.joinStartCallAction).toBeVisible();
|
||||
await expect(ChannelInfoScreen.ignoreMentionsOptionToggledOff).toBeVisible();
|
||||
await ChannelInfoScreen.scrollView.scrollTo('bottom');
|
||||
await expect(ChannelInfoScreen.pinnedMessagesOption).toBeVisible();
|
||||
await expect(ChannelInfoScreen.copyChannelLinkOption).toBeVisible();
|
||||
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 () => {
|
||||
// # Tap on a first channel
|
||||
await ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name).tap();
|
||||
await ChannelScreen.closeScheduledMessageTooltip();
|
||||
|
||||
// * Verify on first channel
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -17,16 +17,18 @@ import {
|
|||
ServerScreen,
|
||||
ChannelInfoScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getAdminAccount, getRandomId, timeouts} from '@support/utils';
|
||||
import {getAdminAccount, getRandomId, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Convert to Private Channel', () => {
|
||||
const siteOneDisplayName = 'Server 1';
|
||||
|
||||
beforeAll(async () => {
|
||||
|
||||
// # Log in to server as admin
|
||||
await ServerScreen.connectToServer(siteOneUrl, siteOneDisplayName);
|
||||
await LoginScreen.loginAsAdmin(getAdminAccount());
|
||||
await wait(timeouts.TWO_SEC);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
@ -45,6 +47,8 @@ describe('Channels - Convert to Private Channel', () => {
|
|||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount.tap();
|
||||
await ChannelInfoScreen.open();
|
||||
await ChannelInfoScreen.convertToPrivateChannel(channelDisplayName, {confirm: true});
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId, isIos} from '@support/utils';
|
||||
import {getRandomId, isIos, timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
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.headerInput.replaceText(header);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
await waitFor(ChannelScreen.scheduledPostTooltipCloseButton).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||
await ChannelScreen.scheduledPostTooltipCloseButton.tap();
|
||||
|
||||
// * Verify on newly created public channel
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Create Direct Message', () => {
|
||||
|
|
@ -87,10 +88,12 @@ describe('Channels - Create Direct Message', () => {
|
|||
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
||||
|
||||
// * Verify the new user is selected
|
||||
await expect(CreateDirectMessageScreen.getSelectedUserDisplayName(newUser.id)).toBeVisible();
|
||||
await expect(CreateDirectMessageScreen.getSelectedDMUserDisplayName(newUser.id)).toBeVisible();
|
||||
|
||||
// # Tap on start button
|
||||
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
|
||||
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
|
||||
await CreateDirectMessageScreen.open();
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(firstNewUser.username);
|
||||
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await CreateDirectMessageScreen.getUserItem(firstNewUser.id).tap();
|
||||
|
||||
// * 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
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(secondNewUser.username);
|
||||
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await CreateDirectMessageScreen.getUserItem(secondNewUser.id).tap();
|
||||
|
||||
// * 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
|
||||
await CreateDirectMessageScreen.startButton.tap();
|
||||
|
|
@ -154,6 +161,8 @@ describe('Channels - Create Direct Message', () => {
|
|||
const searchTerm = 'blahblahblahblah';
|
||||
await CreateDirectMessageScreen.open();
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(searchTerm);
|
||||
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
|
||||
// * Verify empty search state for create direct message
|
||||
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {isIos} from '@support/utils';
|
||||
import {isAndroid, isIos, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Edit Channel', () => {
|
||||
|
|
@ -112,6 +112,10 @@ describe('Channels - Edit Channel', () => {
|
|||
await ChannelInfoScreen.toBeVisible();
|
||||
await expect(ChannelInfoScreen.publicPrivateTitleDisplayName).toHaveText(`${testChannel.display_name} name`);
|
||||
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();
|
||||
|
||||
// # Go back to channel screen
|
||||
|
|
@ -154,8 +158,11 @@ describe('Channels - Edit Channel', () => {
|
|||
await CreateDirectMessageScreen.searchInput.replaceText(testOtherUser1.username);
|
||||
await CreateDirectMessageScreen.getUserItem(testOtherUser1.id).tap();
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(testOtherUser2.username);
|
||||
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await CreateDirectMessageScreen.getUserItem(testOtherUser2.id).tap();
|
||||
await CreateDirectMessageScreen.startButton.tap();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
// * Verify on group message channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
import {isIos, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Find Channels', () => {
|
||||
|
|
@ -68,7 +68,8 @@ describe('Channels - Find Channels', () => {
|
|||
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
|
||||
await FindChannelsScreen.open();
|
||||
await FindChannelsScreen.searchInput.replaceText(testChannel.display_name);
|
||||
|
|
@ -98,7 +99,7 @@ describe('Channels - Find Channels', () => {
|
|||
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
|
||||
const {user: testOtherUser1} = await User.apiCreateUser(siteOneUrl, {prefix: 'a'});
|
||||
await Team.apiAddUserToTeam(siteOneUrl, testOtherUser1.id, testTeam.id);
|
||||
|
|
@ -127,7 +128,7 @@ describe('Channels - Find Channels', () => {
|
|||
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
|
||||
const {channel: archivedChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
||||
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, archivedChannel.id);
|
||||
|
|
@ -171,6 +172,12 @@ describe('Channels - Find Channels', () => {
|
|||
});
|
||||
|
||||
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 expect(ChannelScreen.headerTitle).toHaveText(display_name);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(display_name);
|
||||
|
|
|
|||
|
|
@ -17,16 +17,18 @@ import {
|
|||
ServerScreen,
|
||||
ChannelInfoScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getAdminAccount, getRandomId, timeouts} from '@support/utils';
|
||||
import {getAdminAccount, getRandomId, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Unarchive Channel', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
|
||||
beforeAll(async () => {
|
||||
|
||||
// # Log in to server as admin
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
await LoginScreen.loginAsAdmin(getAdminAccount());
|
||||
await wait(timeouts.TWO_SEC);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
@ -45,6 +47,9 @@ describe('Channels - Unarchive Channel', () => {
|
|||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await expect(ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount).toBeVisible();
|
||||
await ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount.tap();
|
||||
await ChannelInfoScreen.open();
|
||||
await ChannelInfoScreen.archivePublicChannel({confirm: true});
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ describe('Messaging - At-Mention', () => {
|
|||
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
|
||||
[...Array(3).keys()].forEach(async (key) => {
|
||||
const {user} = await User.apiCreateUser(siteOneUrl, {prefix: `a-${key}-`});
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ describe('Messaging - Emojis and Reactions', () => {
|
|||
await EmojiPickerScreen.open();
|
||||
|
||||
// * 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();
|
||||
|
||||
|
|
@ -175,5 +175,9 @@ describe('Messaging - Emojis and Reactions', () => {
|
|||
// * Verify empty search state for emoji picker
|
||||
await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible();
|
||||
await expect(element(by.text('Check the spelling or try another search.'))).toBeVisible();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await EmojiPickerScreen.close();
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {
|
|||
PostOptionsScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId, timeouts} from '@support/utils';
|
||||
import {getRandomId, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Messaging - Follow and Unfollow Message', () => {
|
||||
|
|
@ -62,19 +62,21 @@ describe('Messaging - Follow and Unfollow Message', () => {
|
|||
await expect(postListPostItem).toBeVisible();
|
||||
|
||||
// # 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();
|
||||
|
||||
// * Verify message is followed by user via post footer
|
||||
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
|
||||
await ChannelScreen.openPostOptionsFor(post.id, message);
|
||||
await PostOptionsScreen.followingThreadOption.tap();
|
||||
|
||||
// * 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
|
||||
await ChannelScreen.back();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ describe('Messaging - Markdown Latex', () => {
|
|||
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
|
||||
// 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]';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {isAndroid, isIos} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Messaging - Markdown List', () => {
|
||||
|
|
@ -48,7 +49,7 @@ describe('Messaging - Markdown List', () => {
|
|||
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
|
||||
const item1 = 'item one';
|
||||
const item2 = 'item two';
|
||||
|
|
@ -61,15 +62,24 @@ describe('Messaging - Markdown List', () => {
|
|||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
const {postListPostItemListItem, postListPostItemListItemBullet} = ChannelScreen.getPostListPostItem(post.id);
|
||||
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
||||
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('•');
|
||||
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(postListPostItemListItem.atIndex(2)).toBeVisible();
|
||||
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('◦');
|
||||
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
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
|
@ -89,13 +99,20 @@ describe('Messaging - Markdown List', () => {
|
|||
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
|
||||
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('1.');
|
||||
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(postListPostItemListItem.atIndex(2)).toBeVisible();
|
||||
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('3.');
|
||||
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
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
ServerScreen,
|
||||
TableScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {isIos} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Messaging - Markdown Table', () => {
|
||||
|
|
@ -56,11 +57,11 @@ describe('Messaging - Markdown Table', () => {
|
|||
'|:---|:---|:---|\n' +
|
||||
'| 1 | Name | Toast |\n' +
|
||||
'| 2 | Name | Server |\n';
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await Post.apiCreatePost(siteOneUrl, {
|
||||
channelId: testChannel.id,
|
||||
message: markdownTable,
|
||||
});
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
|
||||
// * Verify markdown table is displayed
|
||||
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' +
|
||||
'| :-- | :-: | --: |\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, {
|
||||
channelId: testChannel.id,
|
||||
message: markdownTable,
|
||||
});
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
|
||||
// * Verify table is displayed with long text wrapped properly
|
||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
|
|
@ -119,11 +120,11 @@ describe('Messaging - Markdown Table', () => {
|
|||
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
|
||||
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(7) +
|
||||
'| Left | Center | Right | Right | Left | Center | Right | Right HS last |\n';
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await Post.apiCreatePost(siteOneUrl, {
|
||||
channelId: testChannel.id,
|
||||
message: markdownTable,
|
||||
});
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
|
||||
// * Verify table is displayed with some right columns not visible
|
||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
|
|
@ -156,11 +157,11 @@ describe('Messaging - Markdown Table', () => {
|
|||
'| :-- | :-: | --: |\n' +
|
||||
'| Left | Center | Right |\n'.repeat(30) +
|
||||
'| Left | Center | Right VS last |\n';
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await Post.apiCreatePost(siteOneUrl, {
|
||||
channelId: testChannel.id,
|
||||
message: markdownTable,
|
||||
});
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
|
||||
// * Verify table is displayed with some bottom rows not visible
|
||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
|
|
@ -178,9 +179,13 @@ describe('Messaging - Markdown Table', () => {
|
|||
|
||||
// * Verify table screen is scrollable to the bottom
|
||||
const expectedElement = element(by.text('Right VS last'));
|
||||
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(50, 'down');
|
||||
await expect(element(by.text('Header VS last'))).not.toBeVisible();
|
||||
await expect(expectedElement).toBeVisible();
|
||||
if (isIos()) {
|
||||
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(150, 'down');
|
||||
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
|
||||
await TableScreen.back();
|
||||
|
|
@ -194,11 +199,11 @@ describe('Messaging - Markdown Table', () => {
|
|||
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
|
||||
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(30) +
|
||||
'| Left | Center | Right | Right | Left | Center | Right | Right last |\n';
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await Post.apiCreatePost(siteOneUrl, {
|
||||
channelId: testChannel.id,
|
||||
message: markdownTable,
|
||||
});
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
|
||||
// * Verify table is displayed with some right columns and bottom rows not visible
|
||||
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();
|
||||
|
||||
// * Verify table screen is scrollable to the right and scrollable to the bottom
|
||||
await TableScreen.tableScrollView.scrollTo('right');
|
||||
await expect(element(by.text('Header last'))).toBeVisible();
|
||||
await waitFor(element(by.text('Header last'))).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(150, 'right');
|
||||
await expect(element(by.text('Right last'))).not.toBeVisible();
|
||||
const expectedElement = element(by.text('Right last'));
|
||||
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(50, 'down');
|
||||
await expect(element(by.text('Header last'))).not.toBeVisible();
|
||||
await expect(expectedElement).toBeVisible();
|
||||
if (isIos()) {
|
||||
await waitFor(expectedElement).toBeVisible().whileElement(by.id(TableScreen.testID.tableScrollView)).scroll(150, 'down');
|
||||
await expect(element(by.text('Header last'))).not.toBeVisible();
|
||||
await expect(expectedElement).toBeVisible();
|
||||
} else {
|
||||
await expect(expectedElement).toExist();
|
||||
}
|
||||
|
||||
// # Go back to channel list screen
|
||||
await TableScreen.back();
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
|||
await PostOptionsScreen.pinPostOption.tap();
|
||||
|
||||
// * 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);
|
||||
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
|||
await PostOptionsScreen.unpinPostOption.tap();
|
||||
|
||||
// * 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();
|
||||
|
||||
// # Go back to channel list screen
|
||||
|
|
@ -96,7 +96,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
|||
await PostOptionsScreen.pinPostOption.tap();
|
||||
|
||||
// * 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);
|
||||
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ describe('Messaging - Pin and Unpin Message', () => {
|
|||
await PostOptionsScreen.unpinPostOption.tap();
|
||||
|
||||
// * 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();
|
||||
|
||||
// # 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.searchInput.typeText(testUser.username);
|
||||
const {atMentionItem} = Autocomplete.getAtMentionItem(testUser.id);
|
||||
await waitFor(atMentionItem).toBeVisible().withTimeout(2000);
|
||||
await waitFor(atMentionItem).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await atMentionItem.tap();
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
|
||||
|
|
@ -126,6 +126,7 @@ describe('Search - Search Messages', () => {
|
|||
await SearchMessagesScreen.searchModifierIn.tap();
|
||||
await SearchMessagesScreen.searchInput.typeText(testChannel.name);
|
||||
const {channelMentionItem} = Autocomplete.getChannelMentionItem(testChannel.name);
|
||||
await waitFor(channelMentionItem).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await channelMentionItem.tap();
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
|
||||
|
|
@ -166,6 +167,7 @@ describe('Search - Search Messages', () => {
|
|||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
|
||||
// * Verify search results do not contain messages with excluded term
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await expect(nonExcludedPostListPostItem).toBeVisible();
|
||||
await expect(excludedPostListPostItem).not.toBeVisible();
|
||||
|
||||
|
|
@ -198,11 +200,15 @@ describe('Search - Search Messages', () => {
|
|||
await SearchMessagesScreen.searchModifierPhrases.tap();
|
||||
await SearchMessagesScreen.searchInput.tapBackspaceKey();
|
||||
await SearchMessagesScreen.searchInput.typeText(messageWithIncludedTerm);
|
||||
|
||||
// # Collapse the keyboard
|
||||
await element(by.id('search.modifier.header')).tap();
|
||||
await SearchMessagesScreen.searchModifierPhrases.tap();
|
||||
await SearchMessagesScreen.searchInput.tapBackspaceKey();
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
|
||||
// * Verify search results only contain messages with included term
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await expect(nonIncludedPostListPostItem).not.toBeVisible();
|
||||
await expect(includedPostListPostItem).toBeVisible();
|
||||
|
||||
|
|
@ -223,17 +229,10 @@ describe('Search - Search Messages', () => {
|
|||
// * Verify on search messages screen
|
||||
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
|
||||
await SearchMessagesScreen.searchModifierFrom.tap();
|
||||
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();
|
||||
// # Tap on from-search-modifier, type in username
|
||||
await SearchMessagesScreen.searchInput.typeText(`from: ${testUser.username} channel: ${testChannel.name}`);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
// * Verify search results only contain messages from user in channel
|
||||
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
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TEN_SEC);
|
||||
|
||||
// * Verify search results contain searched message
|
||||
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
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
// * Verify search results contain searched 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
|
||||
await SearchMessagesScreen.teamPickerButton.tap();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await TeamDropdownMenuScreen.getTeamIcon(testTeam.id).tap();
|
||||
|
||||
// * Verify search results do not contain searched message
|
||||
|
|
@ -347,6 +349,7 @@ describe('Search - Search Messages', () => {
|
|||
const searchTerm = getRandomId();
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
// * Verify empty search state for search messages
|
||||
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
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
||||
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
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
||||
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
|
||||
await SearchMessagesScreen.searchInput.typeText(searchTerm);
|
||||
await SearchMessagesScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
const {post: searchedPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, message);
|
||||
await PostOptionsScreen.pinPostOption.tap();
|
||||
|
|
|
|||
|
|
@ -78,7 +78,11 @@ describe('Server Login - Connect to Server', () => {
|
|||
|
||||
// * Verify invalid url error
|
||||
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 () => {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
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 () => {
|
||||
// # Log in with empty username and non-empty password
|
||||
await usernameInput.clearText();
|
||||
await passwordInput.typeText('password');
|
||||
await passwordInput.replaceText('password');
|
||||
await wait(timeouts.TWO_SEC);
|
||||
|
||||
// * Verify signin button is disabled
|
||||
await expect(signinButtonDisabled).toBeVisible();
|
||||
|
||||
// # Log in with non-empty username and empty password
|
||||
await usernameInput.typeText('username');
|
||||
await usernameInput.replaceText('username');
|
||||
await passwordInput.clearText();
|
||||
await LoginScreen.loginFormInfoText.tap();
|
||||
|
||||
// * Verify signin button is disabled
|
||||
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 () => {
|
||||
// # Log in with incorrect credentials
|
||||
await usernameInput.typeText('username');
|
||||
await passwordInput.typeText('password');
|
||||
await usernameInput.replaceText('username');
|
||||
await passwordInput.replaceText('password');
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await LoginScreen.loginFormInfoText.tap();
|
||||
await signinButton.tap();
|
||||
|
||||
// * 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 () => {
|
||||
// # Log in to server with correct credentials
|
||||
const {team, user} = await Setup.apiInit(siteOneUrl);
|
||||
await usernameInput.typeText(user.newUser.username);
|
||||
await passwordInput.typeText(user.newUser.password);
|
||||
await usernameInput.replaceText(user.newUser.username);
|
||||
await passwordInput.replaceText(user.newUser.password);
|
||||
await LoginScreen.loginFormInfoText.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
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -87,8 +87,9 @@ describe('Server Login - Server List', () => {
|
|||
// # Add a second server and log in to the second server
|
||||
await User.apiAdminLogin(siteTwoUrl);
|
||||
({user: serverTwoUser} = await Setup.apiInit(siteTwoUrl));
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
||||
await LoginScreen.login(serverTwoUser);
|
||||
|
||||
|
|
@ -112,8 +113,9 @@ describe('Server Login - Server List', () => {
|
|||
// # Add a third server and log in to the third server
|
||||
await User.apiAdminLogin(siteThreeUrl);
|
||||
({user: serverThreeUser} = await Setup.apiInit(siteThreeUrl));
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverThreeUrl, serverThreeDisplayName);
|
||||
await LoginScreen.login(serverThreeUser);
|
||||
|
||||
|
|
@ -123,6 +125,7 @@ describe('Server Login - Server List', () => {
|
|||
|
||||
// # Open server list screen
|
||||
await ServerListScreen.open();
|
||||
await wait(timeouts.TWO_SEC);
|
||||
if (isIos()) {
|
||||
await ServerListScreen.serverListTitle.swipe('up');
|
||||
} 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 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();
|
||||
|
||||
// * Verify on edit server screen
|
||||
|
|
@ -209,7 +212,7 @@ describe('Server Login - Server List', () => {
|
|||
await expect(ServerListScreen.getServerItemActive(newServerOneDisplayName)).toBeVisible();
|
||||
|
||||
// # 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 EditServerScreen.serverDisplayNameInput.replaceText(serverOneDisplayName);
|
||||
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 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();
|
||||
|
||||
// * 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
|
||||
await Alert.removeButton3.tap();
|
||||
await waitFor(Alert.removeButton1).toBeVisible().withTimeout(timeouts.HALF_MIN);
|
||||
await Alert.removeButton1.tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await ServerListScreen.open();
|
||||
if (isIos()) {
|
||||
await ServerListScreen.serverListTitle.swipe('up');
|
||||
} else if (isAndroid()) {
|
||||
await waitFor(ServerListScreen.serverListTitle).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
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
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
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 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();
|
||||
|
||||
// * 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
|
||||
await Alert.logoutButton3.tap();
|
||||
await waitFor(Alert.logoutButton).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.logoutButton.tap();
|
||||
|
||||
// * 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();
|
||||
|
||||
// # 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.addServerButton.tap();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.serverUrlInput.replaceText(serverTwoUrl);
|
||||
if (isAndroid()) {
|
||||
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 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 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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,23 +1,100 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable no-await-in-loop, no-console */
|
||||
|
||||
import {Plugin, System, User} from '@support/server_api';
|
||||
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 () => {
|
||||
// Login as sysadmin and reset server configuration
|
||||
await System.apiCheckSystemHealth(siteOneUrl);
|
||||
await User.apiAdminLogin(siteOneUrl);
|
||||
await Plugin.apiDisableNonPrepackagedPlugins(siteOneUrl);
|
||||
|
||||
await device.launchApp({
|
||||
newInstance: true,
|
||||
launchArgs: {detoxPrintBusyIdleResources: 'YES'},
|
||||
permissions: {
|
||||
notifications: 'YES',
|
||||
camera: 'YES',
|
||||
medialibrary: 'YES',
|
||||
photos: 'YES',
|
||||
},
|
||||
});
|
||||
await launchAppWithRetry();
|
||||
});
|
||||
|
||||
// Add this to speed up test cleanup
|
||||
afterAll(async () => {
|
||||
try {
|
||||
await device.terminateApp();
|
||||
} catch (error) {
|
||||
console.error('Error terminating app:', error);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -66,8 +66,10 @@ describe('Smoke Test - Channels', () => {
|
|||
const {channel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
||||
await BrowseChannelsScreen.open();
|
||||
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 ChannelScreen.closeScheduledMessageTooltip();
|
||||
|
||||
// * Verify on newly joined channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
@ -90,8 +92,9 @@ describe('Smoke Test - Channels', () => {
|
|||
// # Open create channel screen and create a new channel
|
||||
const displayName = `Channel ${getRandomId()}`;
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(displayName);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
await CreateOrEditChannelScreen.displayNameInput.typeText(displayName);
|
||||
await wait(timeouts.FOUR_SEC);
|
||||
await CreateOrEditChannelScreen.clickonCreateButton();
|
||||
|
||||
// * Verify on newly created public channel
|
||||
await ChannelScreen.toBeVisible();
|
||||
|
|
@ -106,8 +109,10 @@ describe('Smoke Test - Channels', () => {
|
|||
await CreateDirectMessageScreen.open();
|
||||
await CreateDirectMessageScreen.closeTutorial();
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(newUserDisplayName);
|
||||
await CreateDirectMessageScreen.searchInput.tapReturnKey();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await CreateDirectMessageScreen.startButton.tap();
|
||||
|
||||
// * Verify on direct message channel screen for the new user
|
||||
|
|
@ -134,11 +139,13 @@ describe('Smoke Test - Channels', () => {
|
|||
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
|
||||
await FindChannelsScreen.open();
|
||||
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();
|
||||
|
||||
// * Verify on target channel screen
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import {
|
|||
ServerListScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {isAndroid, isIos, timeouts} from '@support/utils';
|
||||
import {isAndroid, isIos, timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Smoke Test - Server Login', () => {
|
||||
|
|
@ -69,7 +69,7 @@ describe('Smoke Test - Server Login', () => {
|
|||
await User.apiAdminLogin(siteTwoUrl);
|
||||
const {user} = await Setup.apiInit(siteTwoUrl);
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
||||
await LoginScreen.login(user);
|
||||
|
||||
|
|
@ -102,6 +102,7 @@ describe('Smoke Test - Server Login', () => {
|
|||
|
||||
// # Tap on logout button
|
||||
await Alert.logoutButton.tap();
|
||||
await wait(timeouts.FOUR_SEC);
|
||||
|
||||
// * Verify second server is logged out
|
||||
await ServerListScreen.getServerItemInactive(serverTwoDisplayName).swipe('left');
|
||||
|
|
|
|||
|
|
@ -73,9 +73,6 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.teamDisplayName).toHaveText(testTeam.display_name);
|
||||
await expect(Invite.teamIcon).toBeVisible();
|
||||
|
||||
// * Verify default Selection
|
||||
await waitFor(Invite.screenSelection).toBeVisible().withTimeout(timeouts.FOUR_SEC);
|
||||
|
||||
// * Verify Server data
|
||||
await expect(Invite.serverDisplayName).toHaveText(serverOneDisplayName);
|
||||
|
||||
|
|
@ -139,7 +136,8 @@ describe('Teams - Invite', () => {
|
|||
await expect(Invite.getSearchListUserItem(testUser1.id)).not.toBeVisible();
|
||||
|
||||
// * 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
|
||||
await Invite.sendButton.tap();
|
||||
|
|
@ -163,7 +161,8 @@ describe('Teams - Invite', () => {
|
|||
await Invite.getSearchListUserItem(testUser2.id).tap();
|
||||
|
||||
// * 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
|
||||
await Invite.sendButton.tap();
|
||||
|
|
@ -190,7 +189,8 @@ describe('Teams - Invite', () => {
|
|||
await Invite.getSearchListUserItem(testUser3.id).tap();
|
||||
|
||||
// * 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
|
||||
await Invite.searchBarInput.replaceText(testUser.username);
|
||||
|
|
@ -201,9 +201,6 @@ describe('Teams - Invite', () => {
|
|||
// # Select user item
|
||||
await Invite.getSearchListUserItem(testUser.id).tap();
|
||||
|
||||
// * Validate user is added to selected items
|
||||
await expect(Invite.getSelectedItem(testUser.id)).toBeVisible();
|
||||
|
||||
// # Send invitation
|
||||
await Invite.sendButton.tap();
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,8 @@ describe('Threads - Follow and Unfollow Thread', () => {
|
|||
await wait(timeouts.ONE_SEC);
|
||||
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible();
|
||||
|
||||
await wait(timeouts.FOUR_SEC);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await GlobalThreadsScreen.back();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
ServerScreen,
|
||||
ThreadScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId} from '@support/utils';
|
||||
import {getRandomId, timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Threads - Global Threads', () => {
|
||||
|
|
@ -67,7 +67,7 @@ describe('Threads - Global Threads', () => {
|
|||
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
|
||||
const parentMessage = `Message ${getRandomId()}`;
|
||||
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
|
||||
await ThreadScreen.back();
|
||||
await ChannelScreen.back();
|
||||
await device.reloadReactNative();
|
||||
await GlobalThreadsScreen.open();
|
||||
await GlobalThreadsScreen.headerAllThreadsButton.tap();
|
||||
|
||||
|
|
@ -93,7 +92,13 @@ describe('Threads - Global Threads', () => {
|
|||
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible();
|
||||
await expect(GlobalThreadsScreen.getThreadItemThreadStarterUserDisplayName(parentPost.id)).toHaveText(testUser.username);
|
||||
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
|
||||
await GlobalThreadsScreen.getThreadItem(parentPost.id).tap();
|
||||
|
|
@ -133,7 +138,7 @@ describe('Threads - Global Threads', () => {
|
|||
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
|
||||
const parentMessage = `Message ${getRandomId()}`;
|
||||
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
|
||||
await ThreadScreen.back();
|
||||
await ChannelScreen.back();
|
||||
await device.reloadReactNative();
|
||||
await GlobalThreadsScreen.open();
|
||||
await GlobalThreadsScreen.headerAllThreadsButton.tap();
|
||||
|
||||
// * Verify the thread replied to by the current user is displayed
|
||||
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.getThreadItemFooterReplyCount(parentPost.id)).toHaveText('1 reply');
|
||||
|
||||
// # Tap on the thread
|
||||
await GlobalThreadsScreen.getThreadItem(parentPost.id).tap();
|
||||
|
|
|
|||
|
|
@ -119,9 +119,6 @@ describe('Threads - Save and Unsave Thread', () => {
|
|||
// # Tap on thread overview unsave button
|
||||
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
|
||||
await ThreadScreen.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",
|
||||
"client-oauth2": "4.3.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"detox": "20.33.0",
|
||||
"detox": "20.37.0",
|
||||
"form-data": "4.0.1",
|
||||
"jest": "29.7.0",
|
||||
"jest-circus": "29.7.0",
|
||||
|
|
@ -1098,14 +1098,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
|
||||
"integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
|
||||
"integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.3",
|
||||
"@babel/types": "^7.26.3",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
|
|
@ -1145,18 +1144,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
|
||||
"integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz",
|
||||
"integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
"@babel/helper-member-expression-to-functions": "^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/traverse": "^7.25.9",
|
||||
"@babel/traverse": "^7.27.0",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -1261,11 +1259,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
|
||||
"integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
|
||||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
|
||||
"integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
|
|
@ -1289,15 +1286,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-replace-supers": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
|
||||
"integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
|
||||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz",
|
||||
"integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-member-expression-to-functions": "^7.25.9",
|
||||
"@babel/helper-optimise-call-expression": "^7.25.9",
|
||||
"@babel/traverse": "^7.25.9"
|
||||
"@babel/traverse": "^7.26.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
|
@ -1380,13 +1376,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
|
||||
"integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.3"
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
|
|
@ -2753,32 +2748,30 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.25.9",
|
||||
"@babel/parser": "^7.25.9",
|
||||
"@babel/types": "^7.25.9"
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.26.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz",
|
||||
"integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
|
||||
"integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.3",
|
||||
"@babel/parser": "^7.26.3",
|
||||
"@babel/template": "^7.25.9",
|
||||
"@babel/types": "^7.26.3",
|
||||
"@babel/generator": "^7.27.0",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/template": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
|
|
@ -2787,11 +2780,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
|
||||
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.9",
|
||||
"@babel/helper-validator-identifier": "^7.25.9"
|
||||
|
|
@ -2807,6 +2799,26 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz",
|
||||
|
|
@ -4276,6 +4288,12 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
|
|
@ -4300,6 +4318,37 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
|
@ -4932,18 +4981,6 @@
|
|||
"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": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
||||
|
|
@ -5014,6 +5051,16 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
|
@ -5034,6 +5081,41 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
|
@ -5232,19 +5314,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detox": {
|
||||
"version": "20.33.0",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.33.0.tgz",
|
||||
"integrity": "sha512-9KZC3NgCav/IvuWm/EaKNIL5lV2bJf23PPK67OHS2dMhJsDsNBRWP9k3RN7hzLzzUwAXMMoOmk+S1OpOsazNFA==",
|
||||
"version": "20.37.0",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.37.0.tgz",
|
||||
"integrity": "sha512-OUI2p3z31Yku1USBia0jrlwoQoa6rANvVWuUSJyjBlRRKDSeOf/DG/ivk+R5FO+lYZZtA3pIbl6LSql/Bf6DpQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@wix-pilot/core": "^3.2.2",
|
||||
"@wix-pilot/detox": "^1.0.11",
|
||||
"ajv": "^8.6.3",
|
||||
"bunyan": "^1.8.12",
|
||||
"bunyan-debug-stream": "^3.1.0",
|
||||
"caf": "^15.0.1",
|
||||
"chalk": "^4.0.0",
|
||||
"child-process-promise": "^2.2.0",
|
||||
"detox-copilot": "^0.0.27",
|
||||
"execa": "^5.1.1",
|
||||
"find-up": "^5.0.0",
|
||||
"fs-extra": "^11.0.0",
|
||||
|
|
@ -5257,6 +5340,7 @@
|
|||
"multi-sort-stream": "^1.0.3",
|
||||
"multipipe": "^4.0.0",
|
||||
"node-ipc": "9.2.1",
|
||||
"promisify-child-process": "^4.1.2",
|
||||
"proper-lockfile": "^3.0.2",
|
||||
"resolve-from": "^5.0.0",
|
||||
"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": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
|
|
@ -5508,6 +5585,12 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
|
|
@ -5723,6 +5806,12 @@
|
|||
"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": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
|
|
@ -5793,6 +5882,12 @@
|
|||
"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": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
|
|
@ -6159,8 +6254,7 @@
|
|||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "1.4.0",
|
||||
|
|
@ -7408,6 +7502,12 @@
|
|||
"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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
|
|
@ -7466,6 +7566,23 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
|
|
@ -7789,16 +7906,6 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
||||
|
|
@ -7858,6 +7965,15 @@
|
|||
"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": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
|
|
@ -8159,6 +8275,15 @@
|
|||
"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": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||
|
|
@ -8322,12 +8447,14 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/promise-polyfill": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
|
||||
"integrity": "sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==",
|
||||
"node_modules/promisify-child-process": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/promisify-child-process/-/promisify-child-process-4.1.2.tgz",
|
||||
"integrity": "sha512-APnkIgmaHNJpkAn7k+CrJSi9WMuff5ctYFbD0CO2XIPkM8yO7d/ShouU2clywbpHV/DUsyc4bpJCsNgddNtx4g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/prompts": {
|
||||
"version": "2.4.2",
|
||||
|
|
@ -8816,6 +8943,15 @@
|
|||
"license": "MIT",
|
||||
"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": {
|
||||
"version": "1.6.3",
|
||||
"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",
|
||||
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
|
|
@ -8991,6 +9126,21 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
|
|
@ -9036,6 +9186,15 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
|
||||
|
|
@ -9327,6 +9486,12 @@
|
|||
"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": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/throwback/-/throwback-4.1.0.tgz",
|
||||
|
|
@ -9400,6 +9565,15 @@
|
|||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||
|
|
@ -9692,6 +9866,70 @@
|
|||
"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": {
|
||||
"version": "7.0.0",
|
||||
"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",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"babel-plugin-module-resolver": "5.0.2",
|
||||
"client-oauth2": "4.3.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"detox": "20.33.0",
|
||||
"detox": "20.37.0",
|
||||
"form-data": "4.0.1",
|
||||
"jest": "29.7.0",
|
||||
"jest-circus": "29.7.0",
|
||||
|
|
@ -42,13 +42,14 @@
|
|||
"xml2js": "0.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"e2e:android-inject-settings": "node inject-detox-settings.js",
|
||||
"e2e:android-create-emulator": "./create_android_emulator.sh",
|
||||
"e2e:android-build": "detox build -c android.emu.debug",
|
||||
"e2e:android-test": "detox test -c android.emu.debug",
|
||||
"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-test-release": "detox test -c android.emu.release --record-logs failing --take-screenshots failing",
|
||||
"e2e:ios-build": "IOS=true detox build -c ios.sim.debug",
|
||||
"e2e:ios-test": "IOS=true detox test -c ios.sim.debug",
|
||||
"e2e:ios-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-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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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) {
|
||||
// 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) {
|
||||
console.log('Successfully uploaded artifacts to S3:', result.reportLink);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,8 @@ const {
|
|||
DETOX_AWS_S3_BUCKET,
|
||||
DETOX_AWS_ACCESS_KEY_ID,
|
||||
DETOX_AWS_SECRET_ACCESS_KEY,
|
||||
IOS,
|
||||
REPORT_PATH,
|
||||
} = process.env;
|
||||
const platform = IOS === 'true' ? 'ios' : 'android';
|
||||
|
||||
const s3 = new S3({
|
||||
credentials: {
|
||||
|
|
@ -36,7 +34,7 @@ function getFiles(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) {
|
||||
console.log('No AWS credentials found. Test artifacts not uploaded to S3.');
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
/* eslint-disable no-process-env */
|
||||
/* eslint-disable no-console */
|
||||
const fs = require('fs');
|
||||
const deviceName = process.env.DEVICE_NAME || 'iPhone 16 Pro';
|
||||
const deviceOSVersion = process.env.DEVICE_OS_VERSION || 'iOS 18.1';
|
||||
const deviceName = process.env.DEVICE_NAME || 'iPhone 15 Pro';
|
||||
const deviceOSVersion = process.env.DEVICE_OS_VERSION || 'iOS 17.2';
|
||||
const detoxConfigTemplate = fs.readFileSync('../.detoxrc.json', 'utf8');
|
||||
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);
|
||||
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) {
|
||||
|
|
|
|||
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
|
||||
declare module '@env' {
|
||||
export const RUNNING_E2E: string;
|
||||
}
|
||||
|
||||
declare module 'react-native-dotenv' {
|
||||
|
|
|
|||
2
index.ts
2
index.ts
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {RUNNING_E2E} from '@env';
|
||||
import TurboLogger from '@mattermost/react-native-turbo-log';
|
||||
import {LogBox, Platform, UIManager} from 'react-native';
|
||||
import ViewReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
|
||||
import {RUNNING_E2E} from 'react-native-dotenv';
|
||||
import 'react-native-gesture-handler';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue