From f69b82dae96774d3d1b972ab880ccc74cb426684 Mon Sep 17 00:00:00 2001 From: Anant Bhasin <38764067+aKn1ghtOut@users.noreply.github.com> Date: Thu, 2 Dec 2021 18:49:15 +0530 Subject: [PATCH] Tests: Make Detox work on Android (#3051) --- .../__snapshots__/Storyshots.test.js.snap | 3 + android/app/build.gradle | 18 +++ .../chat/rocket/reactnative/DetoxTest.java | 32 ++++ .../e2e/res/xml/network_security_config.xml | 10 ++ android/build.gradle | 4 + app/containers/ActionSheet/ActionSheet.tsx | 6 +- app/containers/Button/index.tsx | 1 + app/containers/message/Content.tsx | 6 +- app/definition/ITeam.js | 2 +- app/views/DirectoryView/Options.tsx | 8 +- app/views/RoomActionsView/index.js | 1 - app/views/SidebarView/SidebarItem.tsx | 2 +- e2e/helpers/app.js | 103 +++++++++--- e2e/tests/assorted/01-e2eencryption.spec.js | 69 +++++--- e2e/tests/assorted/02-broadcast.spec.js | 6 +- e2e/tests/assorted/03-profile.spec.js | 17 +- e2e/tests/assorted/04-setting.spec.js | 10 +- e2e/tests/assorted/05-joinpublicroom.spec.js | 38 ++--- e2e/tests/assorted/06-status.spec.js | 3 +- e2e/tests/assorted/07-changeserver.spec.js | 7 +- .../assorted/08-joinprotectedroom.spec.js | 22 ++- .../assorted/09-joinfromdirectory.spec.js | 12 +- e2e/tests/assorted/10-deleteserver.spec.js | 12 +- e2e/tests/assorted/11-deeplinking.spec.js | 43 ++--- e2e/tests/assorted/12-i18n.spec.js | 10 +- e2e/tests/assorted/13-display-pref.spec.js | 8 +- e2e/tests/init.js | 2 + e2e/tests/onboarding/01-onboarding.spec.js | 23 +-- .../onboarding/03-forgotpassword.spec.js | 9 +- e2e/tests/onboarding/04-createuser.spec.js | 13 +- e2e/tests/onboarding/05-login.spec.js | 9 +- e2e/tests/onboarding/06-roomslist.spec.js | 4 +- .../onboarding/07-server-history.spec.js | 4 +- e2e/tests/room/01-createroom.spec.js | 31 +++- e2e/tests/room/02-room.spec.js | 150 +++++++++++------- e2e/tests/room/03-roomactions.spec.js | 147 ++++++++--------- e2e/tests/room/04-discussion.spec.js | 15 +- e2e/tests/room/05-threads.spec.js | 86 +++++----- e2e/tests/room/06-createdmgroup.spec.js | 6 +- e2e/tests/room/07-markasunread.spec.js | 10 +- e2e/tests/room/08-roominfo.spec.js | 103 +++++------- e2e/tests/room/09-jumptomessage.spec.js | 145 ++++++++++------- e2e/tests/team/01-createteam.spec.js | 23 ++- e2e/tests/team/02-team.spec.js | 62 ++++++-- e2e/tests/team/03-moveconvert.spec.js | 37 +++-- package.json | 12 ++ 46 files changed, 837 insertions(+), 507 deletions(-) create mode 100644 android/app/src/androidTest/java/chat/rocket/reactnative/DetoxTest.java create mode 100644 android/app/src/e2e/res/xml/network_security_config.xml diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 62fa05a8..f85a69dd 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -1419,6 +1419,7 @@ Array [ @@ -294,6 +310,8 @@ dependencies { implementation "com.tencent:mmkv-static:1.2.1" implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation "com.squareup.okhttp3:okhttp-urlconnection:4.9.0" + androidTestImplementation('com.wix:detox:+') { transitive = true } + androidTestImplementation 'junit:junit:4.12' } // Run this once to be able to run the application with BUCK diff --git a/android/app/src/androidTest/java/chat/rocket/reactnative/DetoxTest.java b/android/app/src/androidTest/java/chat/rocket/reactnative/DetoxTest.java new file mode 100644 index 00000000..1ab897cd --- /dev/null +++ b/android/app/src/androidTest/java/chat/rocket/reactnative/DetoxTest.java @@ -0,0 +1,32 @@ +// Replace "com.example" here and below with your app's package name from the top of MainActivity.java +package chat.rocket.reactnative; + +import com.wix.detox.Detox; +import com.wix.detox.config.DetoxConfig; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class DetoxTest { + @Rule + // Replace 'MainActivity' with the value of android:name entry in + // in AndroidManifest.xml + public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); + + @Test + public void runDetoxTests() { + DetoxConfig detoxConfig = new DetoxConfig(); + detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; + detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; + detoxConfig.rnContextLoadTimeoutSec = (chat.rocket.reactnative.BuildConfig.DEBUG ? 180 : 60); + + Detox.runTests(mActivityRule, detoxConfig); + } +} diff --git a/android/app/src/e2e/res/xml/network_security_config.xml b/android/app/src/e2e/res/xml/network_security_config.xml new file mode 100644 index 00000000..95aaf3c1 --- /dev/null +++ b/android/app/src/e2e/res/xml/network_security_config.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 31650a07..626e7564 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -53,6 +53,10 @@ allprojects { url("$rootDir/../node_modules/jsc-android/dist") } + maven { + url "$rootDir/../node_modules/detox/Detox-android" + } + maven { url jitsi_url } diff --git a/app/containers/ActionSheet/ActionSheet.tsx b/app/containers/ActionSheet/ActionSheet.tsx index 35e69c70..11414150 100644 --- a/app/containers/ActionSheet/ActionSheet.tsx +++ b/app/containers/ActionSheet/ActionSheet.tsx @@ -124,7 +124,11 @@ const ActionSheet = React.memo( const renderFooter = () => data?.hasCancel ? ( - ) : null; diff --git a/app/containers/Button/index.tsx b/app/containers/Button/index.tsx index 9e475a67..8c99dcce 100644 --- a/app/containers/Button/index.tsx +++ b/app/containers/Button/index.tsx @@ -70,6 +70,7 @@ export default class Button extends React.PureComponent, a disabled && styles.disabled, style ]} + accessibilityLabel={title} {...otherProps}> {loading ? ( diff --git a/app/containers/message/Content.tsx b/app/containers/message/Content.tsx index b9aaf962..9d4d005e 100644 --- a/app/containers/message/Content.tsx +++ b/app/containers/message/Content.tsx @@ -43,7 +43,11 @@ const Content = React.memo( content = {I18n.t('Sent_an_attachment')}; } else if (props.isEncrypted) { content = ( - {I18n.t('Encrypted_message')} + + {I18n.t('Encrypted_message')} + ); } else { const { baseUrl, user, onLinkPress } = useContext(MessageContext); diff --git a/app/definition/ITeam.js b/app/definition/ITeam.js index 10919715..8cf8bddc 100644 --- a/app/definition/ITeam.js +++ b/app/definition/ITeam.js @@ -1,5 +1,5 @@ // https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts -export const TEAM_TYPE = { +exports.TEAM_TYPE = { PUBLIC: 0, PRIVATE: 1 }; diff --git a/app/views/DirectoryView/Options.tsx b/app/views/DirectoryView/Options.tsx index fcc0f7bf..11206806 100644 --- a/app/views/DirectoryView/Options.tsx +++ b/app/views/DirectoryView/Options.tsx @@ -63,7 +63,11 @@ export default class DirectoryOptions extends PureComponent changeType(itemType)} style={styles.dropdownItemButton} theme={theme}> + changeType(itemType)} + style={styles.dropdownItemButton} + theme={theme} + accessibilityLabel={I18n.t(text)}> {I18n.t(text)} @@ -90,7 +94,7 @@ export default class DirectoryOptions extends PureComponent - + } showActionIndicator /> diff --git a/app/views/SidebarView/SidebarItem.tsx b/app/views/SidebarView/SidebarItem.tsx index 7590e82c..bfbf2d2d 100644 --- a/app/views/SidebarView/SidebarItem.tsx +++ b/app/views/SidebarView/SidebarItem.tsx @@ -25,7 +25,7 @@ const Item = React.memo(({ left, right, text, onPress, testID, current, theme }: style={[styles.item, current && { backgroundColor: themes[theme].borderColor }]}> {left} - + {text} diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js index 6ade15c9..df696cff 100644 --- a/e2e/helpers/app.js +++ b/e2e/helpers/app.js @@ -1,10 +1,33 @@ +const { exec } = require('child_process'); const data = require('../data'); +const platformTypes = { + android: { + // Android types + alertButtonType: 'android.widget.Button', + scrollViewType: 'android.widget.ScrollView', + textInputType: 'android.widget.EditText', + textMatcher: 'text' + }, + ios: { + // iOS types + alertButtonType: '_UIAlertControllerActionView', + scrollViewType: 'UIScrollView', + textInputType: '_UIAlertControllerTextField', + textMatcher: 'label' + } +}; + +function sleep(ms) { + return new Promise(res => setTimeout(res, ms)); +} + async function navigateToWorkspace(server = data.server) { await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(60000); - await element(by.id('new-server-view-input')).typeText(`${server}\n`); + await element(by.id('new-server-view-input')).replaceText(`${server}`); + await element(by.id('new-server-view-input')).tapReturnKey(); await waitFor(element(by.id('workspace-view'))) .toBeVisible() .withTimeout(60000); @@ -41,6 +64,8 @@ async function login(username, password) { } async function logout() { + const deviceType = device.getPlatform(); + const { scrollViewType, textMatcher } = platformTypes[deviceType]; await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))) .toBeVisible() @@ -52,14 +77,14 @@ async function logout() { await waitFor(element(by.id('settings-view'))) .toBeVisible() .withTimeout(2000); - await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom'); + await element(by.type(scrollViewType)).atIndex(1).scrollTo('bottom'); await element(by.id('settings-logout')).tap(); const logoutAlertMessage = 'You will be logged out of this application.'; - await waitFor(element(by.text(logoutAlertMessage)).atIndex(0)) + await waitFor(element(by[textMatcher](logoutAlertMessage)).atIndex(0)) .toExist() .withTimeout(10000); - await expect(element(by.text(logoutAlertMessage)).atIndex(0)).toExist(); - await element(by.text('Logout')).tap(); + await expect(element(by[textMatcher](logoutAlertMessage)).atIndex(0)).toExist(); + await element(by[textMatcher]('Logout')).atIndex(0).tap(); await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(10000); @@ -67,66 +92,73 @@ async function logout() { } async function mockMessage(message, isThread = false) { + const deviceType = device.getPlatform(); + const { textMatcher } = platformTypes[deviceType]; const input = isThread ? 'messagebox-input-thread' : 'messagebox-input'; - await element(by.id(input)).tap(); - await element(by.id(input)).typeText(`${data.random}${message}`); + await element(by.id(input)).replaceText(`${data.random}${message}`); + await sleep(300); await element(by.id('messagebox-send-message')).tap(); - await waitFor(element(by.label(`${data.random}${message}`))) + await waitFor(element(by[textMatcher](`${data.random}${message}`))) .toExist() .withTimeout(60000); - await expect(element(by.label(`${data.random}${message}`))).toExist(); - await element(by.label(`${data.random}${message}`)) + await element(by[textMatcher](`${data.random}${message}`)) .atIndex(0) .tap(); } async function starMessage(message) { + const deviceType = device.getPlatform(); + const { textMatcher } = platformTypes[deviceType]; const messageLabel = `${data.random}${message}`; - await element(by.label(messageLabel)).atIndex(0).longPress(); + await element(by[textMatcher](messageLabel)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Star')).atIndex(0).tap(); + await element(by[textMatcher]('Star')).atIndex(0).tap(); await waitFor(element(by.id('action-sheet'))) .not.toExist() .withTimeout(5000); } async function pinMessage(message) { + const deviceType = device.getPlatform(); + const { textMatcher } = platformTypes[deviceType]; const messageLabel = `${data.random}${message}`; - await waitFor(element(by.label(messageLabel)).atIndex(0)).toExist(); - await element(by.label(messageLabel)).atIndex(0).longPress(); + await waitFor(element(by[textMatcher](messageLabel)).atIndex(0)).toExist(); + await element(by[textMatcher](messageLabel)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Pin')).atIndex(0).tap(); + await element(by[textMatcher]('Pin')).atIndex(0).tap(); await waitFor(element(by.id('action-sheet'))) .not.toExist() .withTimeout(5000); } async function dismissReviewNag() { - await waitFor(element(by.text('Are you enjoying this app?'))) + const deviceType = device.getPlatform(); + const { textMatcher } = platformTypes[deviceType]; + await waitFor(element(by[textMatcher]('Are you enjoying this app?'))) .toExist() .withTimeout(60000); - await element(by.label('No').and(by.type('_UIAlertControllerActionView'))).tap(); // Tap `no` on ask for review alert + await element(by[textMatcher]('No')).atIndex(0).tap(); // Tap `no` on ask for review alert } async function tapBack() { await element(by.id('header-back')).atIndex(0).tap(); } -function sleep(ms) { - return new Promise(res => setTimeout(res, ms)); -} - async function searchRoom(room) { + await waitFor(element(by.id('rooms-list-view'))) + .toBeVisible() + .withTimeout(30000); await element(by.id('rooms-list-view-search')).tap(); await expect(element(by.id('rooms-list-view-search-input'))).toExist(); await waitFor(element(by.id('rooms-list-view-search-input'))) .toExist() .withTimeout(5000); - await element(by.id('rooms-list-view-search-input')).typeText(room); + await sleep(300); + await element(by.id('rooms-list-view-search-input')).replaceText(room); await sleep(300); await waitFor(element(by.id(`rooms-list-view-item-${room}`))) .toBeVisible() @@ -162,6 +194,29 @@ const checkServer = async server => { await element(by.id('sidebar-close-drawer')).tap(); }; +function runCommand(command) { + return new Promise((resolve, reject) => { + exec(command, (error, stdout, stderr) => { + if (error) { + reject(new Error(`exec error: ${stderr}`)); + return; + } + resolve(); + }); + }); +} + +async function prepareAndroid() { + if (device.getPlatform() !== 'android') { + return; + } + await runCommand('adb shell settings put secure spell_checker_enabled 0'); + await runCommand('adb shell settings put secure autofill_service null'); + await runCommand('adb shell settings put global window_animation_scale 0.0'); + await runCommand('adb shell settings put global transition_animation_scale 0.0'); + await runCommand('adb shell settings put global animator_duration_scale 0.0'); +} + module.exports = { navigateToWorkspace, navigateToLogin, @@ -176,5 +231,7 @@ module.exports = { sleep, searchRoom, tryTapping, - checkServer + checkServer, + platformTypes, + prepareAndroid }; diff --git a/e2e/tests/assorted/01-e2eencryption.spec.js b/e2e/tests/assorted/01-e2eencryption.spec.js index 1a40335b..c8ba4c4d 100644 --- a/e2e/tests/assorted/01-e2eencryption.spec.js +++ b/e2e/tests/assorted/01-e2eencryption.spec.js @@ -1,4 +1,5 @@ -const { navigateToLogin, login, sleep, tapBack, mockMessage, searchRoom, logout } = require('../../helpers/app'); +const { navigateToLogin, login, sleep, tapBack, mockMessage, searchRoom, logout, platformTypes } = require('../../helpers/app'); + const data = require('../../data'); const testuser = data.users.regular; @@ -17,8 +18,9 @@ const checkServer = async server => { }; const checkBanner = async () => { - await waitFor(element(by.id('listheader-encryption').withDescendant(by.label('Save Your Encryption Password')))) - .toBeVisible() + // TODO: Assert 'Save Your Encryption Password' + await waitFor(element(by.id('listheader-encryption'))) + .toExist() .withTimeout(10000); }; @@ -58,9 +60,13 @@ async function navigateSecurityPrivacy() { describe('E2E Encryption', () => { const room = `encrypted${data.random}`; const newPassword = 'abc'; + let alertButtonType; + let scrollViewType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, scrollViewType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(testuser.username, testuser.password); }); @@ -187,11 +193,11 @@ describe('E2E Encryption', () => { it('should change password', async () => { await element(by.id('e2e-encryption-security-view-password')).typeText(newPassword); await element(by.id('e2e-encryption-security-view-change-password')).tap(); - await waitFor(element(by.text('Are you sure?'))) + await waitFor(element(by[textMatcher]('Are you sure?'))) .toExist() .withTimeout(2000); - await expect(element(by.text("Make sure you've saved it carefully somewhere else."))).toExist(); - await element(by.label('Yes, change it').and(by.type('_UIAlertControllerActionView'))).tap(); + await expect(element(by[textMatcher]("Make sure you've saved it carefully somewhere else."))).toExist(); + await element(by[textMatcher]('Yes, change it')).atIndex(0).tap(); await waitForToast(); }); @@ -216,7 +222,7 @@ describe('E2E Encryption', () => { .toBeVisible() .withTimeout(2000); await navigateToRoom(room); - await waitFor(element(by.label(`${data.random}message`)).atIndex(0)) + await waitFor(element(by[textMatcher](`${data.random}message`)).atIndex(0)) .toExist() .withTimeout(2000); }); @@ -230,7 +236,7 @@ describe('E2E Encryption', () => { await navigateToLogin(); await login(testuser.username, testuser.password); await navigateToRoom(room); - await waitFor(element(by.label(`${data.random}message`)).atIndex(0)) + await waitFor(element(by[textMatcher](`${data.random}message`)).atIndex(0)) .not.toExist() .withTimeout(2000); await expect(element(by.label('Encrypted message')).atIndex(0)).toExist(); @@ -241,10 +247,11 @@ describe('E2E Encryption', () => { await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(2000); - await waitFor(element(by.id('listheader-encryption').withDescendant(by.label('Enter Your E2E Password')))) + // TODO: assert 'Enter Your E2E Password' + await waitFor(element(by.id('listheader-encryption'))) .toBeVisible() .withTimeout(2000); - await element(by.id('listheader-encryption').withDescendant(by.label('Enter Your E2E Password'))).tap(); + await element(by.id('listheader-encryption')).tap(); await waitFor(element(by.id('e2e-enter-your-password-view'))) .toBeVisible() .withTimeout(2000); @@ -254,43 +261,52 @@ describe('E2E Encryption', () => { .not.toExist() .withTimeout(10000); await navigateToRoom(room); - await waitFor(element(by.label(`${data.random}message`)).atIndex(0)) + await waitFor(element(by[textMatcher](`${data.random}message`)).atIndex(0)) .toExist() .withTimeout(2000); }); }); describe('Reset E2E key', () => { - it('should reset e2e key', async () => { + before(async () => { await tapBack(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(2000); + }); + it('should reset e2e key', async () => { + // FIXME: too flaky on Android for now... let's fix it later + // It's also flaky on iOS, but it works from time to time + if (device.getPlatform() === 'android') { + return; + } await navigateSecurityPrivacy(); await element(by.id('security-privacy-view-e2e-encryption')).tap(); await waitFor(element(by.id('e2e-encryption-security-view'))) .toBeVisible() .withTimeout(2000); await element(by.id('e2e-encryption-security-view-reset-key').and(by.label('Reset E2E Key'))).tap(); - await waitFor(element(by.text('Are you sure?'))) + await waitFor(element(by[textMatcher]('Are you sure?'))) .toExist() .withTimeout(2000); - await expect(element(by.text("You're going to be logged out."))).toExist(); - await element(by.label('Yes, reset it').and(by.type('UILabel'))).tap(); + await expect(element(by[textMatcher]("You're going to be logged out."))).toExist(); + await element(by[textMatcher]('Yes, reset it').and(by.type(alertButtonType))).tap(); await sleep(2000); + + await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again."))) + .toExist() + .withTimeout(20000); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('workspace-view'))) .toBeVisible() .withTimeout(10000); - await waitFor(element(by.text("You've been logged out by the server. Please log in again."))) - .toExist() - .withTimeout(2000); - await element(by.label('OK').and(by.type('_UIAlertControllerActionView'))).tap(); await element(by.id('workspace-view-login')).tap(); await waitFor(element(by.id('login-view'))) .toBeVisible() .withTimeout(2000); await login(testuser.username, testuser.password); - await waitFor(element(by.id('listheader-encryption').withDescendant(by.label('Save Your Encryption Password')))) + // TODO: assert 'Save Your Encryption Password' + await waitFor(element(by.id('listheader-encryption'))) .toBeVisible() .withTimeout(2000); }); @@ -298,6 +314,14 @@ describe('E2E Encryption', () => { }); describe('Persist Banner', () => { + before(async () => { + // reinstall the app because of one flaky test above + if (device.getPlatform() === 'android') { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); + } + }); it('check save banner', async () => { await checkServer(data.server); await checkBanner(); @@ -315,7 +339,8 @@ describe('E2E Encryption', () => { await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(60000); - await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}\n`); + await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}`); + await element(by.id('new-server-view-input')).tapReturnKey(); await waitFor(element(by.id('workspace-view'))) .toBeVisible() .withTimeout(60000); @@ -328,7 +353,7 @@ describe('E2E Encryption', () => { await element(by.id('register-view-name')).replaceText(data.registeringUser.username); await element(by.id('register-view-username')).replaceText(data.registeringUser.username); await element(by.id('register-view-email')).replaceText(data.registeringUser.email); - await element(by.id('register-view-password')).typeText(data.registeringUser.password); + await element(by.id('register-view-password')).replaceText(data.registeringUser.password); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() diff --git a/e2e/tests/assorted/02-broadcast.spec.js b/e2e/tests/assorted/02-broadcast.spec.js index f0f2192b..95c8c5a3 100644 --- a/e2e/tests/assorted/02-broadcast.spec.js +++ b/e2e/tests/assorted/02-broadcast.spec.js @@ -1,15 +1,17 @@ // const OTP = require('otp.js'); // const GA = OTP.googleAuthenticator; -const { navigateToLogin, login, mockMessage, tapBack, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } = require('../../helpers/app'); const data = require('../../data'); const testuser = data.users.regular; const otheruser = data.users.alternate; describe('Broadcast room', () => { + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(testuser.username, testuser.password); }); @@ -101,7 +103,7 @@ describe('Broadcast room', () => { }); it('should have the message created earlier', async () => { - await waitFor(element(by.label(`${data.random}message`))) + await waitFor(element(by[textMatcher](`${data.random}message`))) .toExist() .withTimeout(60000); }); diff --git a/e2e/tests/assorted/03-profile.spec.js b/e2e/tests/assorted/03-profile.spec.js index 56e2aa32..fbd8e82d 100644 --- a/e2e/tests/assorted/03-profile.spec.js +++ b/e2e/tests/assorted/03-profile.spec.js @@ -1,4 +1,4 @@ -const { navigateToLogin, login, sleep } = require('../../helpers/app'); +const { navigateToLogin, login, sleep, platformTypes } = require('../../helpers/app'); const data = require('../../data'); const profileChangeUser = data.users.profileChanges; @@ -14,8 +14,14 @@ async function waitForToast() { } describe('Profile screen', () => { + let textInputType; + let scrollViewType; + let alertButtonType; + let textMatcher; + before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ textInputType, scrollViewType, alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(profileChangeUser.username, profileChangeUser.password); await element(by.id('rooms-list-view-sidebar')).tap(); @@ -92,8 +98,8 @@ describe('Profile screen', () => { describe('Usage', () => { it('should change name and username', async () => { await element(by.id('profile-view-name')).replaceText(`${profileChangeUser.username}new`); - await element(by.id('profile-view-username')).typeText(`${profileChangeUser.username}new`); - await element(by.type('UIScrollView')).atIndex(1).swipe('up'); + await element(by.id('profile-view-username')).replaceText(`${profileChangeUser.username}new`); + await element(by.type(scrollViewType)).atIndex(1).swipe('up'); await element(by.id('profile-view-submit')).tap(); await waitForToast(); }); @@ -102,12 +108,13 @@ describe('Profile screen', () => { await element(by.id('profile-view-email')).replaceText(`mobile+profileChangesNew${data.random}@rocket.chat`); await element(by.id('profile-view-new-password')).replaceText(`${profileChangeUser.password}new`); await element(by.id('profile-view-submit')).tap(); - await element(by.type('_UIAlertControllerTextField')).typeText(`${profileChangeUser.password}\n`); + await element(by.type(textInputType)).replaceText(`${profileChangeUser.password}`); + await element(by[textMatcher]('Save').and(by.type(alertButtonType))).tap(); await waitForToast(); }); it('should reset avatar', async () => { - await element(by.type('UIScrollView')).atIndex(1).swipe('up'); + await element(by.type(scrollViewType)).atIndex(1).swipe('up'); await element(by.id('profile-view-reset-avatar')).tap(); await waitForToast(); }); diff --git a/e2e/tests/assorted/04-setting.spec.js b/e2e/tests/assorted/04-setting.spec.js index a242aa8b..50c39b1a 100644 --- a/e2e/tests/assorted/04-setting.spec.js +++ b/e2e/tests/assorted/04-setting.spec.js @@ -1,11 +1,15 @@ -const { navigateToLogin, login } = require('../../helpers/app'); +const { navigateToLogin, login, platformTypes } = require('../../helpers/app'); + const data = require('../../data'); const testuser = data.users.regular; describe('Settings screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(testuser.username, testuser.password); await waitFor(element(by.id('rooms-list-view'))) @@ -72,10 +76,10 @@ describe('Settings screen', () => { .toBeVisible() .withTimeout(2000); await element(by.id('settings-view-clear-cache')).tap(); - await waitFor(element(by.text('This will clear all your offline data.'))) + await waitFor(element(by[textMatcher]('This will clear all your offline data.'))) .toExist() .withTimeout(2000); - await element(by.label('Clear').and(by.type('_UIAlertControllerActionView'))).tap(); + await element(by[textMatcher]('Clear').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(5000); diff --git a/e2e/tests/assorted/05-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js index 09ca3435..877ab426 100644 --- a/e2e/tests/assorted/05-joinpublicroom.spec.js +++ b/e2e/tests/assorted/05-joinpublicroom.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, login, mockMessage, tapBack, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } = require('../../helpers/app'); const testuser = data.users.regular; const room = data.channels.detoxpublic.name; @@ -7,21 +7,24 @@ const room = data.channels.detoxpublic.name; async function navigateToRoom() { await searchRoom(room); await element(by.id(`rooms-list-view-item-${room}`)).tap(); - await waitFor(element(by.id('room-view'))) - .toBeVisible() + await waitFor(element(by.id('room-view')).atIndex(0)) + .toExist() .withTimeout(5000); } async function navigateToRoomActions() { - await element(by.id('room-header')).tap(); + await element(by.id(`room-view-title-${room}`)).tap(); await waitFor(element(by.id('room-actions-view'))) .toBeVisible() .withTimeout(5000); } describe('Join public room', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(testuser.username, testuser.password); await navigateToRoom(); @@ -32,10 +35,6 @@ describe('Join public room', () => { await expect(element(by.id('room-view'))).toBeVisible(); }); - // it('should have messages list', async() => { - // await expect(element(by.id('room-view-messages'))).toBeVisible(); - // }); - // Render - Header describe('Header', () => { it('should have actions button ', async () => { @@ -75,16 +74,10 @@ describe('Join public room', () => { await expect(element(by.id('room-actions-info'))).toBeVisible(); }); - // it('should have voice', async() => { - // await expect(element(by.id('room-actions-voice'))).toBeVisible(); - // }); - - // it('should have video', async() => { - // await expect(element(by.id('room-actions-video'))).toBeVisible(); - // }); - it('should have members', async () => { - await expect(element(by.id('room-actions-members'))).toBeVisible(); + await waitFor(element(by.id('room-actions-members'))) + .toBeVisible() + .withTimeout(2000); }); it('should have files', async () => { @@ -147,32 +140,29 @@ describe('Join public room', () => { await navigateToRoomActions(); await expect(element(by.id('room-actions-view'))).toBeVisible(); await expect(element(by.id('room-actions-info'))).toBeVisible(); - // await expect(element(by.id('room-actions-voice'))).toBeVisible(); - // await expect(element(by.id('room-actions-video'))).toBeVisible(); await expect(element(by.id('room-actions-members'))).toBeVisible(); await expect(element(by.id('room-actions-files'))).toBeVisible(); await expect(element(by.id('room-actions-mentioned'))).toBeVisible(); await expect(element(by.id('room-actions-starred'))).toBeVisible(); - await element(by.id('room-actions-scrollview')).swipe('down'); await expect(element(by.id('room-actions-share'))).toBeVisible(); await expect(element(by.id('room-actions-pinned'))).toBeVisible(); await expect(element(by.id('room-actions-notifications'))).toBeVisible(); + await element(by.id('room-actions-scrollview')).scrollTo('bottom'); await expect(element(by.id('room-actions-leave-channel'))).toBeVisible(); }); it('should leave room', async () => { await element(by.id('room-actions-leave-channel')).tap(); - await waitFor(element(by.text('Yes, leave it!'))) + await waitFor(element(by[textMatcher]('Yes, leave it!').and(by.type(alertButtonType)))) .toBeVisible() .withTimeout(5000); - await expect(element(by.text('Yes, leave it!'))).toBeVisible(); - await element(by.text('Yes, leave it!')).tap(); + await element(by[textMatcher]('Yes, leave it!').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(10000); await waitFor(element(by.id(`rooms-list-view-item-${room}`))) .toBeNotVisible() - .withTimeout(60000); + .withTimeout(60000); // flaky on Android }); }); }); diff --git a/e2e/tests/assorted/06-status.spec.js b/e2e/tests/assorted/06-status.spec.js index 710fb4a9..4a6af055 100644 --- a/e2e/tests/assorted/06-status.spec.js +++ b/e2e/tests/assorted/06-status.spec.js @@ -45,8 +45,9 @@ describe('Status screen', () => { .withTimeout(2000); }); + // TODO: flaky it('should change status text', async () => { - await element(by.id('status-view-input')).typeText('status-text-new'); + await element(by.id('status-view-input')).replaceText('status-text-new'); await element(by.id('status-view-submit')).tap(); await waitForToast(); await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))) diff --git a/e2e/tests/assorted/07-changeserver.spec.js b/e2e/tests/assorted/07-changeserver.spec.js index c489e473..abee2006 100644 --- a/e2e/tests/assorted/07-changeserver.spec.js +++ b/e2e/tests/assorted/07-changeserver.spec.js @@ -1,8 +1,8 @@ const data = require('../../data'); -const { navigateToLogin, login, checkServer } = require('../../helpers/app'); +const { navigateToLogin, login, checkServer, platformTypes } = require('../../helpers/app'); const reopenAndCheckServer = async server => { - await device.launchApp({ permissions: { notifications: 'YES' } }); + await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true }); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(10000); @@ -37,7 +37,8 @@ describe('Change server', () => { await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(6000); - await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}\n`); + await element(by.id('new-server-view-input')).replaceText(`${data.alternateServer}`); + await element(by.id('new-server-view-input')).tapReturnKey(); await waitFor(element(by.id('workspace-view'))) .toBeVisible() .withTimeout(10000); diff --git a/e2e/tests/assorted/08-joinprotectedroom.spec.js b/e2e/tests/assorted/08-joinprotectedroom.spec.js index 0960bf49..375b9ff1 100644 --- a/e2e/tests/assorted/08-joinprotectedroom.spec.js +++ b/e2e/tests/assorted/08-joinprotectedroom.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, login, mockMessage, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, searchRoom, sleep } = require('../../helpers/app'); const testuser = data.users.regular; const room = data.channels.detoxpublicprotected.name; @@ -9,15 +9,25 @@ async function navigateToRoom() { await searchRoom(room); await element(by.id(`rooms-list-view-item-${room}`)).tap(); await waitFor(element(by.id('room-view'))) - .toBeVisible() + .toExist() .withTimeout(5000); } async function openJoinCode() { - await element(by.id('room-view-join-button')).tap(); - await waitFor(element(by.id('join-code'))) - .toBeVisible() - .withTimeout(5000); + await waitFor(element(by.id('room-view-join-button'))) + .toExist() + .withTimeout(2000); + let n = 0; + while (n < 3) { + try { + await element(by.id('room-view-join-button')).tap(); + await waitFor(element(by.id('join-code'))) + .toBeVisible() + .withTimeout(500); + } catch (error) { + n += 1; + } + } } describe('Join protected room', () => { diff --git a/e2e/tests/assorted/09-joinfromdirectory.spec.js b/e2e/tests/assorted/09-joinfromdirectory.spec.js index e521e94e..1126264f 100644 --- a/e2e/tests/assorted/09-joinfromdirectory.spec.js +++ b/e2e/tests/assorted/09-joinfromdirectory.spec.js @@ -10,7 +10,7 @@ async function navigateToRoom(search) { .withTimeout(10000); await sleep(300); // app takes some time to animate await element(by.id(`directory-view-item-${search}`)).tap(); - await waitFor(element(by.id('room-view'))) + await waitFor(element(by.id('room-view')).atIndex(0)) .toExist() .withTimeout(5000); await waitFor(element(by.id(`room-view-title-${search}`))) @@ -44,20 +44,20 @@ describe('Join room from directory', () => { .toExist() .withTimeout(2000); await element(by.id('directory-view-dropdown')).tap(); - await element(by.label('Users')).tap(); - await element(by.label('Search by')).tap(); + await element(by.label('Users')).atIndex(0).tap(); + await element(by.label('Search by')).atIndex(0).tap(); await navigateToRoom(data.users.alternate.username); }); - it('should search user and navigate', async () => { + it('should search team and navigate', async () => { await tapBack(); await element(by.id('rooms-list-view-directory')).tap(); await waitFor(element(by.id('directory-view'))) .toExist() .withTimeout(2000); await element(by.id('directory-view-dropdown')).tap(); - await element(by.label('Teams')).tap(); - await element(by.label('Search by')).tap(); + await element(by.label('Teams')).atIndex(0).tap(); + await element(by.label('Search by')).atIndex(0).tap(); await navigateToRoom(data.teams.private.name); }); }); diff --git a/e2e/tests/assorted/10-deleteserver.spec.js b/e2e/tests/assorted/10-deleteserver.spec.js index 78366e6c..d917f298 100644 --- a/e2e/tests/assorted/10-deleteserver.spec.js +++ b/e2e/tests/assorted/10-deleteserver.spec.js @@ -1,9 +1,12 @@ const data = require('../../data'); -const { sleep, navigateToLogin, login, checkServer } = require('../../helpers/app'); +const { sleep, navigateToLogin, login, checkServer, platformTypes } = require('../../helpers/app'); describe('Delete server', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -23,7 +26,8 @@ describe('Delete server', () => { await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(10000); - await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}\n`); + await element(by.id('new-server-view-input')).replaceText(`${data.alternateServer}`); + await element(by.id('new-server-view-input')).tapReturnKey(); await waitFor(element(by.id('workspace-view'))) .toBeVisible() .withTimeout(10000); @@ -36,7 +40,7 @@ describe('Delete server', () => { await element(by.id('register-view-name')).replaceText(data.registeringUser3.username); await element(by.id('register-view-username')).replaceText(data.registeringUser3.username); await element(by.id('register-view-email')).replaceText(data.registeringUser3.email); - await element(by.id('register-view-password')).typeText(data.registeringUser3.password); + await element(by.id('register-view-password')).replaceText(data.registeringUser3.password); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() @@ -51,7 +55,7 @@ describe('Delete server', () => { .toBeVisible() .withTimeout(5000); await element(by.id(`rooms-list-header-server-${data.server}`)).longPress(1500); - await element(by.label('Delete').and(by.type('_UIAlertControllerActionView'))).tap(); + await element(by[textMatcher]('Delete').and(by.type(alertButtonType))).tap(); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await waitFor(element(by.id('rooms-list-header-server-dropdown'))) .toBeVisible() diff --git a/e2e/tests/assorted/11-deeplinking.spec.js b/e2e/tests/assorted/11-deeplinking.spec.js index 135cd574..4917b279 100644 --- a/e2e/tests/assorted/11-deeplinking.spec.js +++ b/e2e/tests/assorted/11-deeplinking.spec.js @@ -1,10 +1,13 @@ const data = require('../../data'); -const { tapBack, checkServer, navigateToRegister } = require('../../helpers/app'); +const { tapBack, checkServer, navigateToRegister, platformTypes } = require('../../helpers/app'); const { get, login, sendMessage } = require('../../helpers/data_setup'); const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' }; + +let amp = '&'; + const getDeepLink = (method, server, params) => { - const deeplink = `rocketchat://${method}?host=${server.replace(/^(http:\/\/|https:\/\/)/, '')}&${params}`; + const deeplink = `rocketchat://${method}?host=${server.replace(/^(http:\/\/|https:\/\/)/, '')}${amp}${params}`; console.log(`Deeplinking to: ${deeplink}`); return deeplink; }; @@ -12,11 +15,17 @@ const getDeepLink = (method, server, params) => { describe('Deep linking', () => { let userId; let authToken; + let scrollViewType; let threadId; + let textMatcher; + let alertButtonType; const threadMessage = `to-thread-${data.random}`; before(async () => { const loginResult = await login(data.users.regular.username, data.users.regular.password); ({ userId, authToken } = loginResult); + const deviceType = device.getPlatform(); + amp = deviceType === 'android' ? '\\&' : '&'; + ({ scrollViewType, textMatcher, alertButtonType } = platformTypes[deviceType]); // create a thread with api const result = await sendMessage(data.users.regular, data.groups.alternate2.name, threadMessage); threadId = result.message._id; @@ -28,10 +37,9 @@ describe('Deep linking', () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true, - url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, 'userId=123&token=abc'), - sourceApp: 'com.apple.mobilesafari' + url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, `userId=123${amp}token=abc`) }); - await waitFor(element(by.text("You've been logged out by the server. Please log in again."))) + await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again."))) .toExist() .withTimeout(10000); // TODO: we need to improve this message }); @@ -43,9 +51,8 @@ describe('Deep linking', () => { url: getDeepLink( DEEPLINK_METHODS.AUTH, data.server, - `userId=${userId}&token=${authToken}&path=group/${data.groups.private.name}` - ), - sourceApp: 'com.apple.mobilesafari' + `userId=${userId}${amp}token=${authToken}${amp}path=group/${data.groups.private.name}` + ) }); await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) .toExist() @@ -56,7 +63,7 @@ describe('Deep linking', () => { .withTimeout(10000); await checkServer(data.server); await waitFor(element(by.id(`rooms-list-view-item-${data.groups.private.name}`))) - .toBeVisible() + .toExist() .withTimeout(2000); }; @@ -70,7 +77,8 @@ describe('Deep linking', () => { await element(by.id('register-view-name')).replaceText(data.registeringUser4.username); await element(by.id('register-view-username')).replaceText(data.registeringUser4.username); await element(by.id('register-view-email')).replaceText(data.registeringUser4.email); - await element(by.id('register-view-password')).typeText(data.registeringUser4.password); + await element(by.id('register-view-password')).replaceText(data.registeringUser4.password); + await element(by.type(scrollViewType)).atIndex(0).scrollTo('bottom'); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() @@ -85,8 +93,7 @@ describe('Deep linking', () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, - url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`), - sourceApp: 'com.apple.mobilesafari' + url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`) }); await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) .toExist() @@ -97,8 +104,7 @@ describe('Deep linking', () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, - url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.alternate2.name}/thread/${threadId}`), - sourceApp: 'com.apple.mobilesafari' + url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.alternate2.name}/thread/${threadId}`) }); await waitFor(element(by.id(`room-view-title-${threadMessage}`))) .toExist() @@ -110,8 +116,7 @@ describe('Deep linking', () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, - url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `rid=${roomResult.data.group._id}`), - sourceApp: 'com.apple.mobilesafari' + url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `rid=${roomResult.data.group._id}`) }); await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) .toExist() @@ -135,8 +140,7 @@ describe('Deep linking', () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, - url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`), - sourceApp: 'com.apple.mobilesafari' + url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`) }); await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) .toExist() @@ -147,8 +151,7 @@ describe('Deep linking', () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, - url: getDeepLink(DEEPLINK_METHODS.ROOM, 'https://google.com'), - sourceApp: 'com.apple.mobilesafari' + url: getDeepLink(DEEPLINK_METHODS.ROOM, 'https://google.com') }); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() diff --git a/e2e/tests/assorted/12-i18n.spec.js b/e2e/tests/assorted/12-i18n.spec.js index b2a545bb..75caea9e 100644 --- a/e2e/tests/assorted/12-i18n.spec.js +++ b/e2e/tests/assorted/12-i18n.spec.js @@ -29,6 +29,9 @@ const navToLanguage = async () => { describe('i18n', () => { describe('OS language', () => { it("OS set to 'en' and proper translate to 'en'", async () => { + if (device.getPlatform() === 'android') { + return; // FIXME: Passing language with launch parameters doesn't work with Android + } await device.launchApp({ ...defaultLaunchArgs, languageAndLocale: { @@ -44,6 +47,9 @@ describe('i18n', () => { }); it("OS set to unavailable language and fallback to 'en'", async () => { + if (device.getPlatform() === 'android') { + return; // FIXME: Passing language with launch parameters doesn't work with Android + } await device.launchApp({ ...defaultLaunchArgs, languageAndLocale: { @@ -74,7 +80,7 @@ describe('i18n', () => { describe('Rocket.Chat language', () => { before(async () => { - await device.launchApp(defaultLaunchArgs); + await device.launchApp({ ...defaultLaunchArgs, delete: true }); await navigateToLogin(); await login(testuser.username, testuser.password); }); @@ -113,7 +119,7 @@ describe('i18n', () => { it("should set unsupported language and fallback to 'en'", async () => { await post('users.setPreferences', { data: { language: 'eo' } }); // Set language to Esperanto - await device.launchApp(defaultLaunchArgs); + await device.launchApp({ ...defaultLaunchArgs, newInstance: true }); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(10000); diff --git a/e2e/tests/assorted/13-display-pref.spec.js b/e2e/tests/assorted/13-display-pref.spec.js index 602838a8..e486a45e 100644 --- a/e2e/tests/assorted/13-display-pref.spec.js +++ b/e2e/tests/assorted/13-display-pref.spec.js @@ -1,4 +1,4 @@ -const { login, navigateToLogin } = require('../../helpers/app'); +const { login, navigateToLogin, sleep } = require('../../helpers/app'); const data = require('../../data'); const goToDisplayPref = async () => { @@ -14,7 +14,7 @@ const goToRoomList = async () => { await element(by.id('sidebar-chats')).tap(); }; -describe('Rooms list screen', () => { +describe('Display prefs', () => { before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); await navigateToLogin(); @@ -89,7 +89,9 @@ describe('Rooms list screen', () => { await expect(element(by.id('display-pref-view-avatar-switch'))).toBeVisible(); await element(by.id('display-pref-view-avatar-switch')).tap(); await goToRoomList(); - await expect(element(by.id('avatar'))).not.toBeVisible(); + await waitFor(element(by.id('avatar').withAncestor(by.id('rooms-list-view-item-general')))) + .not.toBeVisible() + .withTimeout(2000); }); }); }); diff --git a/e2e/tests/init.js b/e2e/tests/init.js index 5bb81a8b..fd2b7345 100644 --- a/e2e/tests/init.js +++ b/e2e/tests/init.js @@ -3,9 +3,11 @@ const adapter = require('detox/runners/mocha/adapter'); const config = require('../../package.json').detox; const { setup } = require('../helpers/data_setup'); +const { prepareAndroid } = require('../helpers/app'); before(async () => { await Promise.all([setup(), detox.init(config, { launchApp: false })]); + await prepareAndroid(); // Make Android less flaky // await dataSetup() // await detox.init(config, { launchApp: false }); // await device.launchApp({ permissions: { notifications: 'YES' } }); diff --git a/e2e/tests/onboarding/01-onboarding.spec.js b/e2e/tests/onboarding/01-onboarding.spec.js index 6edb8b14..c82f2fa2 100644 --- a/e2e/tests/onboarding/01-onboarding.spec.js +++ b/e2e/tests/onboarding/01-onboarding.spec.js @@ -1,8 +1,12 @@ const data = require('../../data'); +const { platformTypes } = require('../../helpers/app'); describe('Onboarding', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(20000); @@ -19,17 +23,13 @@ describe('Onboarding', () => { }); describe('Usage', () => { - // it('should navigate to create new workspace', async() => { - // // webviews are not supported by detox: https://github.com/wix/detox/issues/136#issuecomment-306591554 - // }); - it('should enter an invalid server and get error', async () => { - await element(by.id('new-server-view-input')).typeText('invalidtest\n'); - const errorText = 'Oops!'; - await waitFor(element(by.text(errorText))) - .toBeVisible() - .withTimeout(60000); - await element(by.text('OK')).tap(); + await element(by.id('new-server-view-input')).replaceText('invalidtest'); + await element(by.id('new-server-view-input')).tapReturnKey(); + await waitFor(element(by[textMatcher]('Oops!'))) + .toExist() + .withTimeout(10000); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should tap on "Join our open workspace" and navigate', async () => { @@ -44,7 +44,8 @@ describe('Onboarding', () => { await waitFor(element(by.id('new-server-view'))) .toBeVisible() .withTimeout(5000); - await element(by.id('new-server-view-input')).typeText(`${data.server}\n`); + await element(by.id('new-server-view-input')).replaceText(data.server); + await element(by.id('new-server-view-input')).tapReturnKey(); await waitFor(element(by.id('workspace-view'))) .toBeVisible() .withTimeout(60000); diff --git a/e2e/tests/onboarding/03-forgotpassword.spec.js b/e2e/tests/onboarding/03-forgotpassword.spec.js index 691576ba..b3408bd2 100644 --- a/e2e/tests/onboarding/03-forgotpassword.spec.js +++ b/e2e/tests/onboarding/03-forgotpassword.spec.js @@ -1,9 +1,12 @@ const data = require('../../data'); -const { navigateToLogin } = require('../../helpers/app'); +const { navigateToLogin, platformTypes } = require('../../helpers/app'); describe('Forgot password screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await element(by.id('login-view-forgot-password')).tap(); await waitFor(element(by.id('forgot-password-view'))) @@ -29,10 +32,10 @@ describe('Forgot password screen', () => { it('should reset password and navigate to login', async () => { await element(by.id('forgot-password-view-email')).replaceText(data.users.existing.email); await element(by.id('forgot-password-view-submit')).tap(); - await waitFor(element(by.text('OK'))) + await waitFor(element(by[textMatcher]('OK'))) .toExist() .withTimeout(10000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('login-view'))) .toBeVisible() .withTimeout(60000); diff --git a/e2e/tests/onboarding/04-createuser.spec.js b/e2e/tests/onboarding/04-createuser.spec.js index 92e992ad..72a47082 100644 --- a/e2e/tests/onboarding/04-createuser.spec.js +++ b/e2e/tests/onboarding/04-createuser.spec.js @@ -1,9 +1,12 @@ -const { navigateToRegister } = require('../../helpers/app'); +const { navigateToRegister, platformTypes } = require('../../helpers/app'); const data = require('../../data'); describe('Create user screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToRegister(); }); @@ -50,10 +53,10 @@ describe('Create user screen', () => { await element(by.id('register-view-email')).replaceText(data.users.existing.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); await element(by.id('register-view-submit')).tap(); - await waitFor(element(by.text('Email already exists. [403]')).atIndex(0)) + await waitFor(element(by[textMatcher]('Email already exists. [403]')).atIndex(0)) .toExist() .withTimeout(10000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should submit username already taken and raise error', async () => { @@ -62,10 +65,10 @@ describe('Create user screen', () => { await element(by.id('register-view-email')).replaceText(data.registeringUser.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); await element(by.id('register-view-submit')).tap(); - await waitFor(element(by.text('Username is already in use')).atIndex(0)) + await waitFor(element(by[textMatcher]('Username is already in use')).atIndex(0)) .toExist() .withTimeout(10000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should register', async () => { diff --git a/e2e/tests/onboarding/05-login.spec.js b/e2e/tests/onboarding/05-login.spec.js index 627261c1..f2fb403c 100644 --- a/e2e/tests/onboarding/05-login.spec.js +++ b/e2e/tests/onboarding/05-login.spec.js @@ -1,9 +1,12 @@ -const { navigateToLogin, tapBack } = require('../../helpers/app'); +const { navigateToLogin, tapBack, platformTypes } = require('../../helpers/app'); const data = require('../../data'); describe('Login screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); }); @@ -58,10 +61,10 @@ describe('Login screen', () => { await element(by.id('login-view-email')).replaceText(data.users.regular.username); await element(by.id('login-view-password')).replaceText('NotMyActualPassword'); await element(by.id('login-view-submit')).tap(); - await waitFor(element(by.text('Your credentials were rejected! Please try again.'))) + await waitFor(element(by[textMatcher]('Your credentials were rejected! Please try again.'))) .toBeVisible() .withTimeout(10000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should login with success', async () => { diff --git a/e2e/tests/onboarding/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js index 6cb2d71f..0148e769 100644 --- a/e2e/tests/onboarding/06-roomslist.spec.js +++ b/e2e/tests/onboarding/06-roomslist.spec.js @@ -14,7 +14,9 @@ describe('Rooms list screen', () => { }); it('should have room item', async () => { - await expect(element(by.id('rooms-list-view-item-general'))).toExist(); + await waitFor(element(by.id('rooms-list-view-item-general'))) + .toExist() + .withTimeout(10000); }); // Render - Header diff --git a/e2e/tests/onboarding/07-server-history.spec.js b/e2e/tests/onboarding/07-server-history.spec.js index d57f1a9a..879f4883 100644 --- a/e2e/tests/onboarding/07-server-history.spec.js +++ b/e2e/tests/onboarding/07-server-history.spec.js @@ -25,10 +25,10 @@ describe('Server history', () => { it('should tap on a server history and navigate to login', async () => { await element(by.id(`server-history-${data.server}`)).tap(); - await waitFor(element(by.id('login-view'))) + await waitFor(element(by.id('login-view-email'))) .toBeVisible() .withTimeout(5000); - await expect(element(by.id('login-view-email'))).toHaveText(data.users.regular.username); + await expect(element(by.label(data.users.regular.username).withAncestor(by.id('login-view-email')))); }); it('should delete server from history', async () => { diff --git a/e2e/tests/room/01-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js index a679c3a9..dd9ecbfc 100644 --- a/e2e/tests/room/01-createroom.spec.js +++ b/e2e/tests/room/01-createroom.spec.js @@ -1,9 +1,12 @@ const data = require('../../data'); -const { tapBack, navigateToLogin, login, tryTapping } = require('../../helpers/app'); +const { tapBack, navigateToLogin, login, tryTapping, platformTypes } = require('../../helpers/app'); describe('Create room screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -121,20 +124,26 @@ describe('Create room screen', () => { describe('Usage', () => { it('should get invalid room', async () => { - await element(by.id('create-channel-name')).typeText('general'); + await element(by.id('create-channel-name')).replaceText('general'); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(2000); await element(by.id('create-channel-submit')).tap(); - await waitFor(element(by.text('A channel with name general exists'))) + await waitFor(element(by[textMatcher]('A channel with name general exists'))) .toExist() .withTimeout(60000); - await expect(element(by.text('A channel with name general exists'))).toExist(); - await element(by.text('OK')).tap(); + await expect(element(by[textMatcher]('A channel with name general exists'))).toExist(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should create public room', async () => { const room = `public${data.random}`; await element(by.id('create-channel-name')).replaceText(''); - await element(by.id('create-channel-name')).typeText(room); + await element(by.id('create-channel-name')).replaceText(room); await element(by.id('create-channel-type')).tap(); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(2000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))) .toExist() @@ -175,7 +184,10 @@ describe('Create room screen', () => { await waitFor(element(by.id('create-channel-view'))) .toExist() .withTimeout(5000); - await element(by.id('create-channel-name')).typeText(room); + await element(by.id('create-channel-name')).replaceText(room); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(2000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))) .toExist() @@ -213,7 +225,10 @@ describe('Create room screen', () => { await waitFor(element(by.id('create-channel-view'))) .toExist() .withTimeout(10000); - await element(by.id('create-channel-name')).typeText(room); + await element(by.id('create-channel-name')).replaceText(room); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(2000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))) .toExist() diff --git a/e2e/tests/room/02-room.spec.js b/e2e/tests/room/02-room.spec.js index 9aa62c6d..aaf79273 100644 --- a/e2e/tests/room/02-room.spec.js +++ b/e2e/tests/room/02-room.spec.js @@ -9,7 +9,8 @@ const { starMessage, pinMessage, dismissReviewNag, - tryTapping + tryTapping, + platformTypes } = require('../../helpers/app'); async function navigateToRoom(roomName) { @@ -22,9 +23,12 @@ async function navigateToRoom(roomName) { describe('Room screen', () => { const mainRoom = data.groups.private.name; + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); await navigateToRoom(mainRoom); @@ -79,7 +83,7 @@ describe('Room screen', () => { describe('Messagebox', () => { it('should send message', async () => { await mockMessage('message'); - await expect(element(by.label(`${data.random}message`)).atIndex(0)).toExist(); + await expect(element(by[textMatcher](`${data.random}message`)).atIndex(0)).toExist(); }); it('should show/hide emoji keyboard', async () => { @@ -100,8 +104,8 @@ describe('Room screen', () => { }); it('should show/hide emoji autocomplete', async () => { - await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(':joy'); + await sleep(300); await waitFor(element(by.id('messagebox-container'))) .toExist() .withTimeout(10000); @@ -112,9 +116,8 @@ describe('Room screen', () => { }); it('should show and tap on emoji autocomplete', async () => { - await element(by.id('messagebox-input')).tap(); - await element(by.id('messagebox-input')).replaceText(':'); - await element(by.id('messagebox-input')).typeText('joy'); // workaround for number keyboard + await element(by.id('messagebox-input')).typeText(':joy'); + await sleep(300); await waitFor(element(by.id('messagebox-container'))) .toExist() .withTimeout(10000); @@ -124,9 +127,8 @@ describe('Room screen', () => { }); it('should not show emoji autocomplete on semicolon in middle of a string', async () => { - await element(by.id('messagebox-input')).tap(); - // await element(by.id('messagebox-input')).replaceText(':'); await element(by.id('messagebox-input')).typeText('name:is'); + await sleep(300); await waitFor(element(by.id('messagebox-container'))) .toNotExist() .withTimeout(20000); @@ -135,8 +137,11 @@ describe('Room screen', () => { it('should show and tap on user autocomplete and send mention', async () => { const { username } = data.users.regular; - await element(by.id('messagebox-input')).tap(); + const messageMention = `@${username}`; + const message = `${data.random}mention`; + const fullMessage = `${messageMention} ${message}`; await element(by.id('messagebox-input')).typeText(`@${username}`); + await sleep(300); await waitFor(element(by.id('messagebox-container'))) .toExist() .withTimeout(4000); @@ -144,15 +149,24 @@ describe('Room screen', () => { .toBeVisible() .withTimeout(4000); await tryTapping(element(by.id(`mention-item-${username}`)), 2000, true); - await expect(element(by.id('messagebox-input'))).toHaveText(`@${username} `); + await expect(element(by.id('messagebox-input'))).toHaveText(`${messageMention} `); await tryTapping(element(by.id('messagebox-input')), 2000); - await element(by.id('messagebox-input')).typeText(`${data.random}mention`); - await element(by.id('messagebox-send-message')).tap(); - // await waitFor(element(by.label(`@${ data.user } ${ data.random }mention`)).atIndex(0)).toExist().withTimeout(60000); + if (device.getPlatform() === 'ios') { + await element(by.id('messagebox-input')).typeText(message); + await element(by.id('messagebox-send-message')).tap(); + const fullMessageMatcher = fullMessage.substr(1); // removes `@` + await waitFor(element(by[textMatcher](fullMessageMatcher))) + .toExist() + .withTimeout(60000); + await expect(element(by[textMatcher](fullMessageMatcher))).toExist(); + await element(by[textMatcher](fullMessageMatcher)).atIndex(0).tap(); + } else { + await element(by.id('messagebox-input')).replaceText(fullMessage); + await element(by.id('messagebox-send-message')).tap(); + } }); it('should not show user autocomplete on @ in the middle of a string', async () => { - await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText('email@gmail'); await waitFor(element(by.id('messagebox-container'))) .toNotExist() @@ -161,9 +175,7 @@ describe('Room screen', () => { }); it('should show and tap on room autocomplete', async () => { - await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText('#general'); - // await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000); await waitFor(element(by.id('mention-item-general'))) .toBeVisible() .withTimeout(4000); @@ -181,7 +193,6 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).clearText(); }); it('should draft message', async () => { - await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(`${data.random}draft`); await tapBack(); @@ -197,25 +208,29 @@ describe('Room screen', () => { describe('Message', () => { it('should copy permalink', async () => { - await element(by.label(`${data.random}message`)) + await element(by[textMatcher](`${data.random}message`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Permalink')).atIndex(0).tap(); + await element(by[textMatcher]('Permalink')).atIndex(0).tap(); // TODO: test clipboard }); it('should copy message', async () => { - await element(by.label(`${data.random}message`)) + await element(by[textMatcher](`${data.random}message`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Copy')).atIndex(0).tap(); + await element(by[textMatcher]('Copy')).atIndex(0).tap(); // TODO: test clipboard }); @@ -224,23 +239,33 @@ describe('Room screen', () => { await starMessage('message'); await sleep(1000); // https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2324 - await element(by.label(`${data.random}message`)) + await element(by[textMatcher](`${data.random}message`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'slow', 0.5); - await waitFor(element(by.label('Unstar')).atIndex(0)) + await waitFor(element(by[textMatcher]('Unstar')).atIndex(0)) .toExist() .withTimeout(6000); await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.8); }); it('should react to message', async () => { - await element(by.label(`${data.random}message`)) + await waitFor(element(by[textMatcher](`${data.random}message`))) + .toExist() + .withTimeout(60000); + await element(by[textMatcher](`${data.random}message`)) + .atIndex(0) + .tap(); + await element(by[textMatcher](`${data.random}message`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.id('add-reaction')).tap(); @@ -258,10 +283,12 @@ describe('Room screen', () => { }); it('should react to message with frequently used emoji', async () => { - await element(by.label(`${data.random}message`)) + await element(by[textMatcher](`${data.random}message`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await waitFor(element(by.id('message-actions-emoji-+1'))) @@ -273,7 +300,7 @@ describe('Room screen', () => { .withTimeout(60000); }); - it('should show reaction picker on add reaction button pressed and have frequently used emoji', async () => { + it('should show reaction picker on add reaction button pressed and have frequently used emoji, and dismiss review nag', async () => { await element(by.id('message-add-reaction')).tap(); await waitFor(element(by.id('reaction-picker'))) .toExist() @@ -291,10 +318,6 @@ describe('Room screen', () => { .withTimeout(60000); }); - it('should ask for review', async () => { - await dismissReviewNag(); // TODO: Create a proper test for this elsewhere. - }); - it('should remove reaction', async () => { await element(by.id('message-reaction-:grinning:')).tap(); await waitFor(element(by.id('message-reaction-:grinning:'))) @@ -302,32 +325,43 @@ describe('Room screen', () => { .withTimeout(60000); }); + it('should ask for review', async () => { + await dismissReviewNag(); // TODO: Create a proper test for this elsewhere. + }); + it('should edit message', async () => { await mockMessage('edit'); - await element(by.label(`${data.random}edit`)) + await element(by[textMatcher](`${data.random}edit`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Edit')).atIndex(0).tap(); - await element(by.id('messagebox-input')).typeText('ed'); + await element(by[textMatcher]('Edit')).atIndex(0).tap(); + await element(by.id('messagebox-input')).replaceText(`${data.random}edited`); await element(by.id('messagebox-send-message')).tap(); - await waitFor(element(by.label(`${data.random}edited (edited)`)).atIndex(0)) + await waitFor(element(by[textMatcher](`${data.random}edited (edited)`)).atIndex(0)) .toExist() .withTimeout(60000); }); it('should quote message', async () => { await mockMessage('quote'); - await element(by.label(`${data.random}quote`)) + await element(by[textMatcher](`${data.random}quote`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Quote')).atIndex(0).tap(); - await element(by.id('messagebox-input')).typeText(`${data.random}quoted`); + await element(by[textMatcher]('Quote')).atIndex(0).tap(); + await element(by.id('messagebox-input')).replaceText(`${data.random}quoted`); + await waitFor(element(by.id('messagebox-send-message'))) + .toExist() + .withTimeout(2000); await element(by.id('messagebox-send-message')).tap(); // TODO: test if quote was sent @@ -337,13 +371,13 @@ describe('Room screen', () => { await mockMessage('pin'); await pinMessage('pin'); - await waitFor(element(by.label(`${data.random}pin`)).atIndex(0)) + await waitFor(element(by[textMatcher](`${data.random}pin`)).atIndex(0)) .toExist() .withTimeout(5000); - await waitFor(element(by.label(`${data.users.regular.username} Message pinned`)).atIndex(0)) + await waitFor(element(by[textMatcher](`${data.users.regular.username} Message pinned`)).atIndex(0)) .toExist() .withTimeout(5000); - await element(by.label(`${data.random}pin`)) + await element(by[textMatcher](`${data.random}pin`)) .atIndex(0) .longPress(); await waitFor(element(by.id('action-sheet'))) @@ -351,7 +385,7 @@ describe('Room screen', () => { .withTimeout(1000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await waitFor(element(by.label('Unpin')).atIndex(0)) + await waitFor(element(by[textMatcher]('Unpin')).atIndex(0)) .toExist() .withTimeout(2000); await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.8); @@ -359,26 +393,26 @@ describe('Room screen', () => { it('should delete message', async () => { await mockMessage('delete'); - - await waitFor(element(by.label(`${data.random}delete`)).atIndex(0)).toBeVisible(); - await element(by.label(`${data.random}delete`)) + await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0)).toBeVisible(); + await element(by[textMatcher](`${data.random}delete`)) .atIndex(0) .longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await waitFor(element(by.label('Delete'))) + await waitFor(element(by[textMatcher]('Delete'))) .toExist() .withTimeout(1000); - await element(by.label('Delete')).atIndex(0).tap(); + await element(by[textMatcher]('Delete')).atIndex(0).tap(); const deleteAlertMessage = 'You will not be able to recover this message!'; - await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)) + await waitFor(element(by[textMatcher](deleteAlertMessage)).atIndex(0)) .toExist() .withTimeout(10000); - await element(by.text('Delete')).tap(); - - await waitFor(element(by.label(`${data.random}delete`)).atIndex(0)) + await element(by[textMatcher]('Delete').and(by.type(alertButtonType))).tap(); + await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0)) .toNotExist() .withTimeout(2000); }); diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js index fb06d07d..e0089876 100644 --- a/e2e/tests/room/03-roomactions.spec.js +++ b/e2e/tests/room/03-roomactions.spec.js @@ -1,5 +1,15 @@ const data = require('../../data'); -const { navigateToLogin, login, tapBack, sleep, searchRoom, mockMessage, starMessage, pinMessage } = require('../../helpers/app'); +const { + navigateToLogin, + login, + tapBack, + sleep, + searchRoom, + mockMessage, + starMessage, + pinMessage, + platformTypes +} = require('../../helpers/app'); const { sendMessage } = require('../../helpers/data_setup'); async function navigateToRoomActions(type) { @@ -43,10 +53,13 @@ async function waitForToast() { } describe('Room actions screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); }); describe('Render', () => { @@ -172,36 +185,12 @@ describe('Room actions screen', () => { }); describe('Usage', () => { - describe('TDB', async () => { - // TODO: test into a jitsi call - // it('should NOT navigate to voice call', async() => { - // await waitFor(element(by.id('room-actions-voice'))).toExist(); - // await element(by.id('room-actions-voice')).tap(); - // await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); - // await expect(element(by.id('room-actions-view'))).toExist(); - // }); - // TODO: test into a jitsi call - // it('should NOT navigate to video call', async() => { - // await element(by.id('room-actions-video')).tap(); - // await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); - // await expect(element(by.id('room-actions-view'))).toExist(); - // }); - // TODO: test share room link - // it('should NOT navigate to share room', async() => { - // await waitFor(element(by.id('room-actions-share'))).toExist(); - // await element(by.id('room-actions-share')).tap(); - // await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); - // await expect(element(by.id('room-actions-view'))).toExist(); - // }); - }); - describe('Common', () => { it('should show mentioned messages', async () => { await element(by.id('room-actions-mentioned')).tap(); await waitFor(element(by.id('mentioned-messages-view'))) .toExist() .withTimeout(2000); - // await waitFor(element(by.text(` ${ data.random }mention`))).toExist().withTimeout(60000); await backToActions(); }); @@ -220,23 +209,25 @@ describe('Room actions screen', () => { .withTimeout(5000); // Go to starred messages + await element(by.id('room-actions-view')).swipe('up'); + await waitFor(element(by.id('room-actions-starred'))).toExist(); await element(by.id('room-actions-starred')).tap(); await waitFor(element(by.id('starred-messages-view'))) .toExist() .withTimeout(2000); - await waitFor(element(by.label(`${data.random}messageToStar`).withAncestor(by.id('starred-messages-view')))) + await waitFor(element(by[textMatcher](`${data.random}messageToStar`).withAncestor(by.id('starred-messages-view')))) .toExist() .withTimeout(60000); // Unstar message - await element(by.label(`${data.random}messageToStar`)) + await element(by[textMatcher](`${data.random}messageToStar`)) .atIndex(0) .longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); - await element(by.label('Unstar')).atIndex(0).tap(); + await element(by[textMatcher]('Unstar')).atIndex(0).tap(); - await waitFor(element(by.label(`${data.random}messageToStar`).withAncestor(by.id('starred-messages-view')))) + await waitFor(element(by[textMatcher](`${data.random}messageToStar`).withAncestor(by.id('starred-messages-view')))) .toBeNotVisible() .withTimeout(60000); await backToActions(); @@ -261,40 +252,22 @@ describe('Room actions screen', () => { await waitFor(element(by.id('pinned-messages-view'))) .toExist() .withTimeout(2000); - await waitFor(element(by.label(`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view')))) + await waitFor(element(by[textMatcher](`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view')))) .toExist() .withTimeout(6000); - await element(by.label(`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view'))) + await element(by[textMatcher](`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view'))) .atIndex(0) .longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); - await element(by.label('Unpin')).atIndex(0).tap(); + await element(by[textMatcher]('Unpin')).atIndex(0).tap(); - await waitFor(element(by.label(`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view')))) + await waitFor(element(by[textMatcher](`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view')))) .not.toExist() .withTimeout(6000); await backToActions(); }); - - // it('should search and find a message', async() => { - - // //Go back to room and send a message - // await tapBack(); - // await mockMessage('messageToFind'); - - // //Back into Room Actions - // await element(by.id('room-header')).tap(); - // await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); - - // await element(by.id('room-actions-search')).tap(); - // await waitFor(element(by.id('search-messages-view'))).toExist().withTimeout(2000); - // await expect(element(by.id('search-message-view-input'))).toExist(); - // await element(by.id('search-message-view-input')).replaceText(`/${ data.random }messageToFind/`); - // await waitFor(element(by.label(`${ data.random }messageToFind`).withAncestor(by.id('search-messages-view')))).toExist().withTimeout(60000); - // await backToActions(); - // }); }); describe('Notification', () => { @@ -370,14 +343,14 @@ describe('Room actions screen', () => { .toExist() .withTimeout(2000); await element(by.id('room-actions-leave-channel')).tap(); - await waitFor(element(by.text('Yes, leave it!'))) + await waitFor(element(by[textMatcher]('Yes, leave it!'))) .toExist() .withTimeout(2000); - await element(by.text('Yes, leave it!')).tap(); - await waitFor(element(by.text('You are the last owner. Please set new owner before leaving the room.'))) + await element(by[textMatcher]('Yes, leave it!').and(by.type(alertButtonType))).tap(); + await waitFor(element(by[textMatcher]('You are the last owner. Please set new owner before leaving the room.'))) .toExist() .withTimeout(8000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('room-actions-view'))) .toExist() .withTimeout(2000); @@ -404,6 +377,7 @@ describe('Room actions screen', () => { .withTimeout(4000); await element(by.id('select-users-view-search')).tap(); await element(by.id('select-users-view-search')).replaceText(user.username); + await sleep(300); await waitFor(element(by.id(`select-users-view-item-${user.username}`))) .toExist() .withTimeout(10000); @@ -437,14 +411,30 @@ describe('Room actions screen', () => { await waitFor(element(by.id(`room-members-view-item-${username}`))) .toExist() .withTimeout(5000); - await element(by.id(`room-members-view-item-${username}`)).tap(); - await sleep(300); - await expect(element(by.id('action-sheet'))).toExist(); - await expect(element(by.id('action-sheet-handle'))).toBeVisible(); + let n = 0; + while (n < 3) { + // Max tries three times, in case it does not register the click + try { + await element(by.id(`room-members-view-item-${username}`)).tap(); + await sleep(300); + await waitFor(element(by.id('action-sheet'))) + .toExist() + .withTimeout(5000); + await expect(element(by.id('action-sheet-handle'))).toBeVisible(); + await element(by.id('action-sheet-handle')).swipe('up'); + return; + } catch (e) { + n += 1; + } + } }; const closeActionSheet = async () => { await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6); + await waitFor(element(by.id('action-sheet'))) + .toBeNotVisible() + .withTimeout(1000); + await sleep(100); }; it('should show all users', async () => { @@ -471,11 +461,14 @@ describe('Room actions screen', () => { it('should remove user from room', async () => { await openActionSheet('rocket.cat'); - await element(by.label('Remove from room')).atIndex(0).tap(); - await waitFor(element(by.label('Are you sure?'))) + await waitFor(element(by[textMatcher]('Remove from room'))) + .toExist() + .withTimeout(2000); + await element(by[textMatcher]('Remove from room')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('Are you sure?'))) .toExist() .withTimeout(5000); - await element(by.label('Yes, remove user!').and(by.type('_UIAlertControllerActionView'))).tap(); + await element(by[textMatcher]('Yes, remove user!').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('room-members-view-item-rocket.cat'))) .toBeNotVisible() .withTimeout(60000); @@ -548,24 +541,24 @@ describe('Room actions screen', () => { it('should set/remove as mute', async () => { await openActionSheet(user.username); - await element(by.label('Mute')).atIndex(0).tap(); - await waitFor(element(by.label('Are you sure?'))) + await element(by[textMatcher]('Mute')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('Are you sure?'))) .toExist() .withTimeout(5000); - await element(by.label('Mute').and(by.type('_UIAlertControllerActionView'))).tap(); + await element(by[textMatcher]('Mute').and(by.type(alertButtonType))).tap(); await waitForToast(); await openActionSheet(user.username); - await element(by.label('Unmute')).atIndex(0).tap(); - await waitFor(element(by.label('Are you sure?'))) + await element(by[textMatcher]('Unmute')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('Are you sure?'))) .toExist() .withTimeout(5000); - await element(by.label('Unmute').and(by.type('_UIAlertControllerActionView'))).tap(); + await element(by[textMatcher]('Unmute').and(by.type(alertButtonType))).tap(); await waitForToast(); await openActionSheet(user.username); // Tests if Remove as mute worked - await waitFor(element(by.label('Mute'))) + await waitFor(element(by[textMatcher]('Mute'))) .toExist() .withTimeout(5000); await closeActionSheet(); @@ -576,21 +569,21 @@ describe('Room actions screen', () => { const channelName = `#${data.groups.private.name}`; await sendMessage(user, channelName, message); await openActionSheet(user.username); - await element(by.label('Ignore')).atIndex(0).tap(); + await element(by[textMatcher]('Ignore')).atIndex(0).tap(); await waitForToast(); await backToActions(); await tapBack(); await waitFor(element(by.id('room-view'))) .toExist() .withTimeout(60000); - await waitFor(element(by.label('Message ignored. Tap to display it.')).atIndex(0)) + await waitFor(element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0)) .toExist() .withTimeout(60000); - await element(by.label('Message ignored. Tap to display it.')).atIndex(0).tap(); - await waitFor(element(by.label(message)).atIndex(0)) + await element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0).tap(); + await waitFor(element(by[textMatcher](message)).atIndex(0)) .toExist() .withTimeout(60000); - await element(by.label(message)).atIndex(0).tap(); + await element(by[textMatcher](message)).atIndex(0).tap(); }); it('should navigate to direct message', async () => { @@ -607,7 +600,7 @@ describe('Room actions screen', () => { .toExist() .withTimeout(60000); await openActionSheet(user.username); - await element(by.label('Direct message')).atIndex(0).tap(); + await element(by[textMatcher]('Direct message')).atIndex(0).tap(); await waitFor(element(by.id('room-view'))) .toExist() .withTimeout(60000); @@ -630,11 +623,11 @@ describe('Room actions screen', () => { it('should block/unblock user', async () => { await waitFor(element(by.id('room-actions-block-user'))).toExist(); await element(by.id('room-actions-block-user')).tap(); - await waitFor(element(by.label('Unblock user'))) + await waitFor(element(by[textMatcher]('Unblock user'))) .toExist() .withTimeout(60000); await element(by.id('room-actions-block-user')).tap(); - await waitFor(element(by.label('Block user'))) + await waitFor(element(by[textMatcher]('Block user'))) .toExist() .withTimeout(60000); }); diff --git a/e2e/tests/room/04-discussion.spec.js b/e2e/tests/room/04-discussion.spec.js index e3781262..80fbdb4d 100644 --- a/e2e/tests/room/04-discussion.spec.js +++ b/e2e/tests/room/04-discussion.spec.js @@ -1,4 +1,4 @@ -const { navigateToLogin, login, mockMessage, tapBack, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } = require('../../helpers/app'); const data = require('../../data'); const channel = data.groups.private.name; @@ -12,8 +12,10 @@ const navigateToRoom = async () => { }; describe('Discussion', () => { + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); + ({ textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -24,12 +26,12 @@ describe('Discussion', () => { await waitFor(element(by.id('new-message-view'))) .toExist() .withTimeout(2000); - await element(by.label('Create Discussion')).atIndex(0).tap(); + await element(by[textMatcher]('Create Discussion')).atIndex(0).tap(); await waitFor(element(by.id('create-discussion-view'))) .toExist() .withTimeout(60000); await expect(element(by.id('create-discussion-view'))).toExist(); - await element(by.label('Select a Channel...')).tap(); + await element(by[textMatcher]('Select a Channel...')).tap(); await element(by.id('multi-select-search')).replaceText(`${channel}`); await waitFor(element(by.id(`multi-select-item-${channel}`))) .toExist() @@ -59,7 +61,7 @@ describe('Discussion', () => { await waitFor(element(by.id('action-sheet'))) .toExist() .withTimeout(2000); - await element(by.label('Create Discussion')).atIndex(0).tap(); + await element(by[textMatcher]('Create Discussion')).atIndex(0).tap(); await waitFor(element(by.id('create-discussion-view'))) .toExist() .withTimeout(2000); @@ -86,11 +88,11 @@ describe('Discussion', () => { it('should create discussion', async () => { const discussionName = `${data.random}message`; - await element(by.label(discussionName)).atIndex(0).longPress(); + await element(by[textMatcher](discussionName)).atIndex(0).longPress(); await waitFor(element(by.id('action-sheet'))) .toExist() .withTimeout(2000); - await element(by.label('Start a Discussion')).atIndex(0).tap(); + await element(by[textMatcher]('Start a Discussion')).atIndex(0).tap(); await waitFor(element(by.id('create-discussion-view'))) .toExist() .withTimeout(2000); @@ -136,6 +138,7 @@ describe('Discussion', () => { }); it('should have starred', async () => { + await element(by.id('room-actions-scrollview')).swipe('up', 'slow', 0.5); await expect(element(by.id('room-actions-starred'))).toBeVisible(); }); diff --git a/e2e/tests/room/05-threads.spec.js b/e2e/tests/room/05-threads.spec.js index d6788928..ae3d8def 100644 --- a/e2e/tests/room/05-threads.spec.js +++ b/e2e/tests/room/05-threads.spec.js @@ -1,5 +1,14 @@ const data = require('../../data'); -const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, dismissReviewNag } = require('../../helpers/app'); +const { + navigateToLogin, + login, + mockMessage, + tapBack, + sleep, + searchRoom, + platformTypes, + dismissReviewNag +} = require('../../helpers/app'); async function navigateToRoom(roomName) { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); @@ -7,21 +16,22 @@ async function navigateToRoom(roomName) { await login(data.users.regular.username, data.users.regular.password); await searchRoom(`${roomName}`); await element(by.id(`rooms-list-view-item-${roomName}`)).tap(); - await waitFor(element(by.id('room-view'))) - .toBeVisible() + await waitFor(element(by.id(`room-view-title-${roomName}`))) + .toExist() .withTimeout(5000); } describe('Threads', () => { const mainRoom = data.groups.private.name; + let textMatcher; before(async () => { + ({ textMatcher } = platformTypes[device.getPlatform()]); await navigateToRoom(mainRoom); }); describe('Render', () => { it('should have room screen', async () => { - await expect(element(by.id('room-view'))).toExist(); await waitFor(element(by.id(`room-view-title-${mainRoom}`))) .toExist() .withTimeout(5000); @@ -69,12 +79,15 @@ describe('Threads', () => { const thread = `${data.random}thread`; it('should create thread', async () => { await mockMessage('thread'); - await element(by.label(thread)).atIndex(0).longPress(); + await element(by[textMatcher](thread)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Reply in Thread')).atIndex(0).tap(); - await element(by.id('messagebox-input')).typeText('replied'); + await element(by[textMatcher]('Reply in Thread')).atIndex(0).tap(); + await element(by.id('messagebox-input')).replaceText('replied'); + await waitFor(element(by.id('messagebox-send-message'))) + .toExist() + .withTimeout(2000); await element(by.id('messagebox-send-message')).tap(); await waitFor(element(by.id(`message-thread-button-${thread}`))) .toExist() @@ -84,9 +97,6 @@ describe('Threads', () => { it('should navigate to thread from button', async () => { await element(by.id(`message-thread-button-${thread}`)).tap(); - await waitFor(element(by.id('room-view'))) - .toExist() - .withTimeout(5000); await waitFor(element(by.id(`room-view-title-${thread}`))) .toExist() .withTimeout(5000); @@ -96,13 +106,9 @@ describe('Threads', () => { it('should toggle follow thread', async () => { await element(by.id(`message-thread-button-${thread}`)).tap(); - await waitFor(element(by.id('room-view'))) - .toExist() - .withTimeout(5000); await waitFor(element(by.id(`room-view-title-${thread}`))) .toExist() .withTimeout(5000); - await expect(element(by.id(`room-view-title-${thread}`))).toExist(); await element(by.id('room-view-header-unfollow')).tap(); await waitFor(element(by.id('room-view-header-follow'))) .toExist() @@ -119,14 +125,13 @@ describe('Threads', () => { const messageText = 'threadonly'; await mockMessage(messageText, true); await tapBack(); - await waitFor(element(by.id('room-header').and(by.label(`${mainRoom}`)))) - .toBeVisible() - .withTimeout(2000); - await waitFor(element(by.id('room-header').and(by.label(`${data.random}thread`)))) - .toBeNotVisible() - .withTimeout(2000); - await sleep(500); // TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :( - await waitFor(element(by.label(`${data.random}${messageText}`)).atIndex(0)) + await waitFor(element(by.id(`room-view-title-${data.random}thread`))) + .not.toExist() + .withTimeout(5000); + await waitFor(element(by.id(`room-view-title-${mainRoom}`))) + .toExist() + .withTimeout(5000); + await waitFor(element(by[textMatcher](`${data.random}${messageText}`)).atIndex(0)) .toNotExist() .withTimeout(2000); }); @@ -137,40 +142,39 @@ describe('Threads', () => { await waitFor(element(by.id('messagebox-input-thread'))) .toExist() .withTimeout(5000); - await element(by.id('messagebox-input-thread')).typeText(messageText); + await element(by.id('messagebox-input-thread')).replaceText(messageText); await element(by.id('messagebox-send-to-channel')).tap(); await element(by.id('messagebox-send-message')).tap(); await tapBack(); - await waitFor(element(by.id('room-header').and(by.label(`${mainRoom}`)))) - .toBeVisible() - .withTimeout(2000); - await waitFor(element(by.id('room-header').and(by.label(`${data.random}thread`)))) - .toBeNotVisible() - .withTimeout(2000); - await sleep(500); // TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :( - await waitFor(element(by.label(messageText)).atIndex(0)) + await waitFor(element(by.id(`room-view-title-${data.random}thread`))) + .not.toExist() + .withTimeout(5000); + await waitFor(element(by.id(`room-view-title-${mainRoom}`))) + .toExist() + .withTimeout(5000); + await waitFor(element(by[textMatcher](messageText)).atIndex(0)) .toExist() .withTimeout(2000); }); it('should navigate to thread from thread name', async () => { const messageText = 'navthreadname'; - await mockMessage('dummymessagebetweenthethread'); - await dismissReviewNag(); // TODO: Create a proper test for this elsewhere. + await mockMessage('dummymessagebetweenthethread'); // TODO: Create a proper test for this elsewhere. + await dismissReviewNag(); await element(by.id(`message-thread-button-${thread}`)).tap(); await waitFor(element(by.id('messagebox-input-thread'))) .toExist() .withTimeout(5000); - await element(by.id('messagebox-input-thread')).typeText(messageText); + await element(by.id('messagebox-input-thread')).replaceText(messageText); await element(by.id('messagebox-send-to-channel')).tap(); await element(by.id('messagebox-send-message')).tap(); await tapBack(); - await waitFor(element(by.id('room-header').and(by.label(`${mainRoom}`)))) - .toBeVisible() - .withTimeout(2000); - await waitFor(element(by.id('room-header').and(by.label(`${data.random}thread`)))) - .toBeNotVisible() - .withTimeout(2000); + await waitFor(element(by.id(`room-view-title-${data.random}thread`))) + .not.toExist() + .withTimeout(5000); + await waitFor(element(by.id(`room-view-title-${mainRoom}`))) + .toExist() + .withTimeout(5000); await waitFor(element(by.id(`message-thread-replied-on-${thread}`))) .toBeVisible() .withTimeout(2000); @@ -211,7 +215,7 @@ describe('Threads', () => { await waitFor(element(by.id(`room-view-title-${thread}`))) .toExist() .withTimeout(5000); - await element(by.id('messagebox-input-thread')).typeText(`${thread}draft`); + await element(by.id('messagebox-input-thread')).replaceText(`${thread}draft`); await tapBack(); await element(by.id(`message-thread-button-${thread}`)).tap(); diff --git a/e2e/tests/room/06-createdmgroup.spec.js b/e2e/tests/room/06-createdmgroup.spec.js index 380cb746..4bd8ae3c 100644 --- a/e2e/tests/room/06-createdmgroup.spec.js +++ b/e2e/tests/room/06-createdmgroup.spec.js @@ -1,9 +1,11 @@ const data = require('../../data'); -const { navigateToLogin, login } = require('../../helpers/app'); +const { navigateToLogin, login, platformTypes } = require('../../helpers/app'); describe('Group DM', () => { + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -29,7 +31,7 @@ describe('Group DM', () => { describe('Usage', () => { it('should navigate to create DM', async () => { - await element(by.label('Create Direct Messages')).tap(); + await element(by[textMatcher]('Create Direct Messages')).tap(); }); it('should add users', async () => { diff --git a/e2e/tests/room/07-markasunread.spec.js b/e2e/tests/room/07-markasunread.spec.js index 28b70fba..4fc088eb 100644 --- a/e2e/tests/room/07-markasunread.spec.js +++ b/e2e/tests/room/07-markasunread.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, login, searchRoom, sleep } = require('../../helpers/app'); +const { navigateToLogin, login, searchRoom, sleep, platformTypes } = require('../../helpers/app'); const { sendMessage } = require('../../helpers/data_setup'); async function navigateToRoom(user) { @@ -12,9 +12,11 @@ async function navigateToRoom(user) { describe('Mark as unread', () => { const user = data.users.alternate.username; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); await navigateToRoom(user); @@ -26,16 +28,16 @@ describe('Mark as unread', () => { const message = `${data.random}message-mark-as-unread`; const channelName = `@${data.users.regular.username}`; await sendMessage(data.users.alternate, channelName, message); - await waitFor(element(by.label(message)).atIndex(0)) + await waitFor(element(by[textMatcher](message)).atIndex(0)) .toExist() .withTimeout(30000); await sleep(300); - await element(by.label(message)).atIndex(0).longPress(); + await element(by[textMatcher](message)).atIndex(0).longPress(); await waitFor(element(by.id('action-sheet-handle'))) .toBeVisible() .withTimeout(3000); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Mark Unread')).atIndex(0).tap(); + await element(by[textMatcher]('Mark Unread')).atIndex(0).tap(); await waitFor(element(by.id('rooms-list-view'))) .toExist() .withTimeout(5000); diff --git a/e2e/tests/room/08-roominfo.spec.js b/e2e/tests/room/08-roominfo.spec.js index c3f20a1b..c9fdbb82 100644 --- a/e2e/tests/room/08-roominfo.spec.js +++ b/e2e/tests/room/08-roominfo.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes } = require('../../helpers/app'); const privateRoomName = data.groups.private.name; @@ -25,17 +25,20 @@ async function navigateToRoomInfo(type) { .withTimeout(2000); } +async function swipe(direction) { + await element(by.id('room-info-edit-view-list')).swipe(direction, 'fast', 0.8); +} + async function waitForToast() { - // await waitFor(element(by.id('toast'))).toExist().withTimeout(10000); - // await expect(element(by.id('toast'))).toExist(); - // await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000); - // await expect(element(by.id('toast'))).toBeNotVisible(); await sleep(300); } describe('Room info screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -72,15 +75,15 @@ describe('Room info screen', () => { }); it('should have description', async () => { - await expect(element(by.label('Description'))).toExist(); + await expect(element(by[textMatcher]('Description'))).toExist(); }); it('should have topic', async () => { - await expect(element(by.label('Topic'))).toExist(); + await expect(element(by[textMatcher]('Topic'))).toExist(); }); it('should have announcement', async () => { - await expect(element(by.label('Announcement'))).toExist(); + await expect(element(by[textMatcher]('Announcement'))).toExist(); }); it('should have edit button', async () => { @@ -124,8 +127,7 @@ describe('Room info screen', () => { }); it('should have type switch', async () => { - // Ugly hack to scroll on detox - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.8); + await swipe('up'); await expect(element(by.id('room-info-edit-view-t'))).toExist(); }); @@ -150,44 +152,33 @@ describe('Room info screen', () => { }); after(async () => { - // Ugly hack to scroll on detox - await element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.8); + await swipe('down'); }); }); describe('Usage', () => { - // it('should enter "invalid name" and get error', async() => { - // await element(by.type('UIScrollView')).atIndex(1).swipe('down'); - // await element(by.id('room-info-edit-view-name')).replaceText('invalid name'); - // await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - // await element(by.id('room-info-edit-view-submit')).tap(); - // await waitFor(element(by.text('There was an error while saving settings!'))).toExist().withTimeout(60000); - // await expect(element(by.text('There was an error while saving settings!'))).toExist(); - // await element(by.text('OK')).tap(); - // await waitFor(element(by.text('There was an error while saving settings!'))).toBeNotVisible().withTimeout(10000); - // await element(by.type('UIScrollView')).atIndex(1).swipe('down'); - // }); - it('should change room name', async () => { await element(by.id('room-info-edit-view-name')).replaceText(`${privateRoomName}new`); await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))) .toExist() .withTimeout(2000); - await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${privateRoomName}new`); + const matcher = device.getPlatform() === 'android' ? 'toHaveText' : 'toHaveLabel'; + await expect(element(by.id('room-info-view-name')))[matcher](`${privateRoomName}new`); // change name to original await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))) .toExist() .withTimeout(2000); await element(by.id('room-info-edit-view-name')).replaceText(`${privateRoomName}`); - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); - await element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.8); + await swipe('down'); }); it('should reset form', async () => { @@ -196,10 +187,11 @@ describe('Room info screen', () => { await element(by.id('room-info-edit-view-topic')).replaceText('abc'); await element(by.id('room-info-edit-view-announcement')).replaceText('abc'); await element(by.id('room-info-edit-view-password')).replaceText('abc'); - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-t')).tap(); + await swipe('up'); await element(by.id('room-info-edit-view-ro')).longPress(); // https://github.com/facebook/react-native/issues/28032 await element(by.id('room-info-edit-view-react-when-ro')).tap(); + await swipe('up'); await element(by.id('room-info-edit-view-reset')).tap(); // after reset await expect(element(by.id('room-info-edit-view-name'))).toHaveText(privateRoomName); @@ -207,22 +199,23 @@ describe('Room info screen', () => { await expect(element(by.id('room-info-edit-view-topic'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-announcement'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-password'))).toHaveText(''); - await expect(element(by.id('room-info-edit-view-t'))).toHaveValue('1'); - await expect(element(by.id('room-info-edit-view-ro'))).toHaveValue('0'); + // await swipe('down'); + await expect(element(by.id('room-info-edit-view-t'))).toHaveToggleValue(true); + await expect(element(by.id('room-info-edit-view-ro'))).toHaveToggleValue(false); await expect(element(by.id('room-info-edit-view-react-when-ro'))).toBeNotVisible(); - await element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.8); + await swipe('down'); }); it('should change room description', async () => { await element(by.id('room-info-edit-view-description')).replaceText('new description'); - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))) .toExist() .withTimeout(2000); - await expect(element(by.label('new description').withAncestor(by.id('room-info-view-description')))).toExist(); + await expect(element(by[textMatcher]('new description').withAncestor(by.id('room-info-view-description')))).toExist(); }); it('should change room topic', async () => { @@ -234,14 +227,14 @@ describe('Room info screen', () => { .toExist() .withTimeout(2000); await element(by.id('room-info-edit-view-topic')).replaceText('new topic'); - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))) .toExist() .withTimeout(2000); - await expect(element(by.label('new topic').withAncestor(by.id('room-info-view-topic')))).toExist(); + await expect(element(by[textMatcher]('new topic').withAncestor(by.id('room-info-view-topic')))).toExist(); }); it('should change room announcement', async () => { @@ -253,14 +246,14 @@ describe('Room info screen', () => { .toExist() .withTimeout(2000); await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement'); - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))) .toExist() .withTimeout(2000); - await expect(element(by.label('new announcement').withAncestor(by.id('room-info-view-announcement')))).toExist(); + await expect(element(by[textMatcher]('new announcement').withAncestor(by.id('room-info-view-announcement')))).toExist(); }); it('should change room password', async () => { @@ -271,61 +264,45 @@ describe('Room info screen', () => { await waitFor(element(by.id('room-info-edit-view'))) .toExist() .withTimeout(2000); - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-password')).replaceText('password'); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); }); it('should change room type', async () => { - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('down'); await element(by.id('room-info-edit-view-t')).tap(); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); + await swipe('down'); await element(by.id('room-info-edit-view-t')).tap(); + await swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); }); - // it('should change room read only and allow reactions', async() => { - // await sleep(1000); - // await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - // await element(by.id('room-info-edit-view-ro')).tap(); - // await waitFor(element(by.id('room-info-edit-view-react-when-ro'))).toExist().withTimeout(2000); - // await expect(element(by.id('room-info-edit-view-react-when-ro'))).toExist(); - // await element(by.id('room-info-edit-view-react-when-ro')).tap(); - // await element(by.id('room-info-edit-view-submit')).tap(); - // await waitForToast(); - // // TODO: test if it's possible to react - // }); - it('should archive room', async () => { await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-archive')).tap(); - await waitFor(element(by.text('Yes, archive it!'))) + await waitFor(element(by[textMatcher]('Yes, archive it!'))) .toExist() .withTimeout(5000); - await element(by.text('Yes, archive it!')).tap(); + await element(by[textMatcher]('Yes, archive it!').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('room-info-edit-view-unarchive'))) .toExist() .withTimeout(60000); await expect(element(by.id('room-info-edit-view-archive'))).toBeNotVisible(); - // TODO: needs permission to unarchive - // await element(by.id('room-info-edit-view-archive')).tap(); - // await waitFor(element(by.text('Yes, unarchive it!'))).toExist().withTimeout(5000); - // await expect(element(by.text('Yes, unarchive it!'))).toExist(); - // await element(by.text('Yes, unarchive it!')).tap(); - // await waitFor(element(by.text('ARCHIVE'))).toExist().withTimeout(60000); - // await expect(element(by.text('ARCHIVE'))).toExist(); }); it('should delete room', async () => { - await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); + await swipe('up'); await element(by.id('room-info-edit-view-delete')).tap(); - await waitFor(element(by.text('Yes, delete it!'))) + await waitFor(element(by[textMatcher]('Yes, delete it!'))) .toExist() .withTimeout(5000); - await element(by.text('Yes, delete it!')).tap(); + await element(by[textMatcher]('Yes, delete it!').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('rooms-list-view'))) .toExist() .withTimeout(10000); diff --git a/e2e/tests/room/09-jumptomessage.spec.js b/e2e/tests/room/09-jumptomessage.spec.js index 5bb79151..5b33a47d 100644 --- a/e2e/tests/room/09-jumptomessage.spec.js +++ b/e2e/tests/room/09-jumptomessage.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, tapBack, login, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, tapBack, login, searchRoom, sleep, platformTypes } = require('../../helpers/app'); async function navigateToRoom(roomName) { await searchRoom(`${roomName}`); @@ -7,8 +7,14 @@ async function navigateToRoom(roomName) { await waitFor(element(by.id('room-view'))) .toBeVisible() .withTimeout(5000); + await waitFor(element(by.id(`room-view-title-${roomName}`))) + .toExist() + .withTimeout(5000); } +let textMatcher; +let alertButtonType; + async function clearCache() { await waitFor(element(by.id('room-view'))) .toBeVisible() @@ -26,10 +32,10 @@ async function clearCache() { .toBeVisible() .withTimeout(2000); await element(by.id('settings-view-clear-cache')).tap(); - await waitFor(element(by.text('This will clear all your offline data.'))) + await waitFor(element(by[textMatcher]('This will clear all your offline data.'))) .toExist() .withTimeout(2000); - await element(by.label('Clear').and(by.type('_UIAlertControllerActionView'))).tap(); + await element(by[textMatcher]('Clear').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('rooms-list-view'))) .toBeVisible() .withTimeout(5000); @@ -39,6 +45,10 @@ async function clearCache() { } async function waitForLoading() { + if (device.getPlatform() === 'android') { + await sleep(10000); + return; // FIXME: Loading indicator doesn't animate properly on android + } await waitFor(element(by.id('loading'))) .toBeVisible() .withTimeout(5000); @@ -50,21 +60,22 @@ async function waitForLoading() { describe('Room', () => { before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.adminUser, data.adminPassword); }); it('should jump to an old message and load its surroundings', async () => { await navigateToRoom('jumping'); - await waitFor(element(by.label('Quote first message'))) + await waitFor(element(by[textMatcher]('300'))) .toExist() .withTimeout(5000); - await element(by.label('1')).atIndex(0).tap(); + await element(by[textMatcher]('1')).atIndex(0).tap(); await waitForLoading(); - await waitFor(element(by.label('1')).atIndex(0)) + await waitFor(element(by[textMatcher]('1')).atIndex(0)) .toExist() .withTimeout(10000); - await expect(element(by.label('2'))).toExist(); + await expect(element(by[textMatcher]('2'))).toExist(); }); it('should tap FAB and scroll to bottom', async () => { @@ -72,7 +83,7 @@ describe('Room', () => { .toExist() .withTimeout(5000); await element(by.id('nav-jump-to-bottom')).tap(); - await waitFor(element(by.label('Quote first message'))) + await waitFor(element(by[textMatcher]('Quote first message'))) .toExist() .withTimeout(5000); await clearCache(); @@ -83,14 +94,15 @@ describe('Room', () => { await waitFor(element(by.id('room-view-messages'))) .toExist() .withTimeout(5000); - await waitFor(element(by.label('300'))) + await waitFor(element(by[textMatcher]('300'))) .toExist() .withTimeout(5000); let found = false; while (!found) { - await element(by.id('room-view-messages')).atIndex(0).scroll(500, 'up'); try { - await expect(element(by.label('249'))).toExist(); + const direction = device.getPlatform() === 'android' ? 'down' : 'up'; + await element(by.id('room-view-messages')).scroll(500, direction); + await expect(element(by[textMatcher]('249'))).toExist(); found = true; } catch { // @@ -101,107 +113,130 @@ describe('Room', () => { it('should search for old message and load its surroundings', async () => { await navigateToRoom('jumping'); + await sleep(1000); // wait for proper load the room await element(by.id('room-view-search')).tap(); await waitFor(element(by.id('search-messages-view'))) .toExist() .withTimeout(5000); - await element(by.id('search-message-view-input')).typeText('30\n'); - await waitFor(element(by.label('30')).atIndex(0)) + await element(by.id('search-message-view-input')).replaceText('30'); + await waitFor(element(by[textMatcher]('30')).atIndex(1)) .toExist() - .withTimeout(5000); - await element(by.label('30')).atIndex(0).tap(); + .withTimeout(30000); + await element(by[textMatcher]('30')).atIndex(1).tap(); await waitForLoading(); - await expect(element(by.label('30'))).toExist(); - await expect(element(by.label('31'))).toExist(); - await expect(element(by.label('32'))).toExist(); + await waitFor(element(by[textMatcher]('30')).atIndex(0)) + .toExist() + .withTimeout(30000); + await expect(element(by[textMatcher]('31'))).toExist(); + await expect(element(by[textMatcher]('32'))).toExist(); }); it('should load newer and older messages', async () => { - await element(by.id('room-view-messages')).atIndex(0).swipe('down', 'fast', 0.8); - await waitFor(element(by.label('5'))) - .toExist() - .withTimeout(10000); - await waitFor(element(by.label('Load Older'))) - .toExist() - .withTimeout(5000); - await element(by.label('Load Older')).atIndex(0).tap(); - await waitFor(element(by.label('4'))) + // TODO: couldn't make it work on Android :( + if (device.getPlatform() === 'android') { + return; + } + let found = false; + while (!found) { + try { + // it doesn't recognize this list + await element(by.id('room-view-messages')).scroll(500, 'up'); + await expect(element(by[textMatcher]('Load Older'))).toBeVisible(); + await expect(element(by[textMatcher]('5'))).toExist(); + found = true; + } catch { + // + } + } + await element(by[textMatcher]('Load Older')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('4'))) .toExist() .withTimeout(5000); await element(by.id('room-view-messages')).atIndex(0).swipe('down', 'fast', 0.5); - await waitFor(element(by.label('1'))) + await waitFor(element(by[textMatcher]('1'))) .toExist() .withTimeout(5000); await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'fast', 0.5); - await waitFor(element(by.label('25'))) + await waitFor(element(by[textMatcher]('25'))) .toExist() .withTimeout(5000); await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'fast', 0.5); - await waitFor(element(by.label('50'))) + await waitFor(element(by[textMatcher]('50'))) .toExist() .withTimeout(5000); await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'slow', 0.5); - await waitFor(element(by.label('Load Newer'))) + await waitFor(element(by[textMatcher]('Load Newer'))) .toExist() .withTimeout(5000); - await element(by.label('Load Newer')).atIndex(0).tap(); - await waitFor(element(by.label('104'))) + await element(by[textMatcher]('Load Newer')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('104'))) .toExist() .withTimeout(5000); - await waitFor(element(by.label('Load Newer'))) + await waitFor(element(by[textMatcher]('Load Newer'))) .toExist() .withTimeout(5000); - await element(by.label('Load Newer')).atIndex(0).tap(); - await waitFor(element(by.label('154'))) + await element(by[textMatcher]('Load Newer')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('154'))) .toExist() .withTimeout(5000); - await waitFor(element(by.label('Load Newer'))) + await waitFor(element(by[textMatcher]('Load Newer'))) .toExist() .withTimeout(5000); - await element(by.label('Load Newer')).atIndex(0).tap(); - await waitFor(element(by.label('Load Newer'))) + await element(by[textMatcher]('Load Newer')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('Load Newer'))) .toNotExist() .withTimeout(5000); - await expect(element(by.label('Load More'))).toNotExist(); - await expect(element(by.label('201'))).toExist(); - await expect(element(by.label('202'))).toExist(); + await expect(element(by[textMatcher]('Load More'))).toNotExist(); + await expect(element(by[textMatcher]('201'))).toExist(); + await expect(element(by[textMatcher]('202'))).toExist(); await tapBack(); }); }); const expectThreadMessages = async message => { - await waitFor(element(by.id('room-view-title-jumping-thread'))) + await waitFor(element(by.id('room-view-title-thread 1'))) .toExist() .withTimeout(5000); - await expect(element(by.label(message))).toExist(); + await waitForLoading(); + await expect(element(by[textMatcher](message)).atIndex(0)).toExist(); + await element(by[textMatcher](message)).atIndex(0).tap(); }; describe('Threads', () => { + before(async () => { + await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true }); + }); + it('should navigate to a thread from another room', async () => { await navigateToRoom('jumping'); - await waitFor(element(by.label("Go to jumping-thread's thread")).atIndex(0)) + await waitFor(element(by[textMatcher]("Go to jumping-thread's thread")).atIndex(0)) .toExist() .withTimeout(5000); - await element(by.label("Go to jumping-thread's thread")).atIndex(0).tap(); - await waitForLoading(); + await element(by[textMatcher]("Go to jumping-thread's thread")).atIndex(0).tap(); await expectThreadMessages("Go to jumping-thread's thread"); await tapBack(); }); it('should tap on thread message from main room', async () => { - await waitFor(element(by.label('thread message sent to main room')).atIndex(0)) + await waitFor(element(by.id('room-view-title-jumping-thread'))) .toExist() .withTimeout(5000); - await element(by.label('thread message sent to main room')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('thread message sent to main room'))) + .toExist() + .withTimeout(10000); + await element(by[textMatcher]('thread message sent to main room')).atIndex(0).tap(); await expectThreadMessages('thread message sent to main room'); await tapBack(); }); it('should tap on quote', async () => { - await waitFor(element(by.label('quoted'))) + await waitFor(element(by.id('room-view-title-jumping-thread'))) .toExist() .withTimeout(5000); - await element(by.label('quoted')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('quoted'))) + .toExist() + .withTimeout(5000); + await element(by[textMatcher]('quoted')).atIndex(0).tap(); await expectThreadMessages('quoted'); await tapBack(); }); @@ -214,11 +249,11 @@ describe('Threads', () => { await waitFor(element(by.id('search-messages-view'))) .toExist() .withTimeout(5000); - await element(by.id('search-message-view-input')).typeText('to be searched\n'); - await waitFor(element(by.label('to be searched'))) + await element(by.id('search-message-view-input')).replaceText('to be searched'); + await waitFor(element(by[textMatcher]('to be searched')).atIndex(1)) .toExist() - .withTimeout(5000); - await element(by.label('to be searched')).atIndex(1).tap(); + .withTimeout(30000); + await element(by[textMatcher]('to be searched')).atIndex(1).tap(); await expectThreadMessages('to be searched'); }); diff --git a/e2e/tests/team/01-createteam.spec.js b/e2e/tests/team/01-createteam.spec.js index ad8e3ea0..27993db4 100644 --- a/e2e/tests/team/01-createteam.spec.js +++ b/e2e/tests/team/01-createteam.spec.js @@ -1,11 +1,14 @@ const data = require('../../data'); -const { navigateToLogin, login } = require('../../helpers/app'); +const { navigateToLogin, login, platformTypes } = require('../../helpers/app'); const teamName = `team-${data.random}`; describe('Create team screen', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -41,17 +44,23 @@ describe('Create team screen', () => { describe('Create Team', () => { describe('Usage', () => { it('should get invalid team name', async () => { - await element(by.id('create-channel-name')).typeText(`${data.teams.private.name}`); + await element(by.id('create-channel-name')).replaceText(`${data.teams.private.name}`); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(2000); await element(by.id('create-channel-submit')).tap(); - await waitFor(element(by.text('OK'))) + await waitFor(element(by[textMatcher]('OK').and(by.type(alertButtonType)))) .toBeVisible() .withTimeout(5000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); }); it('should create private team', async () => { await element(by.id('create-channel-name')).replaceText(''); - await element(by.id('create-channel-name')).typeText(teamName); + await element(by.id('create-channel-name')).replaceText(teamName); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(2000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))) .toExist() @@ -81,10 +90,10 @@ describe('Create team screen', () => { await element(by.id('room-info-view-edit-button')).tap(); await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-delete')).tap(); - await waitFor(element(by.text('Yes, delete it!'))) + await waitFor(element(by[textMatcher]('Yes, delete it!'))) .toExist() .withTimeout(5000); - await element(by.text('Yes, delete it!')).tap(); + await element(by[textMatcher]('Yes, delete it!').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('rooms-list-view'))) .toExist() .withTimeout(10000); diff --git a/e2e/tests/team/02-team.spec.js b/e2e/tests/team/02-team.spec.js index 7779cb9a..acd1c89a 100644 --- a/e2e/tests/team/02-team.spec.js +++ b/e2e/tests/team/02-team.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes } = require('../../helpers/app'); async function navigateToRoom(roomName) { await searchRoom(`${roomName}`); @@ -17,6 +17,7 @@ async function openActionSheet(username) { await sleep(300); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); + await element(by.id('action-sheet-handle')).swipe('up'); } async function navigateToRoomActions() { @@ -37,20 +38,41 @@ async function backToActions() { } async function closeActionSheet() { await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6); + await waitFor(element(by.id('action-sheet-handle'))) + .toBeNotVisible() + .withTimeout(3000); + await sleep(200); } async function waitForToast() { await sleep(1000); } +async function swipeTillVisible(container, find, direction = 'up', delta = 0.3, speed = 'slow') { + let found = false; + while (!found) { + try { + await element(container).swipe(direction, speed, delta); + await sleep(200); + await expect(element(find)).toBeVisible(); + found = true; + } catch (e) { + // + } + } +} + describe('Team', () => { const team = data.teams.private.name; const user = data.users.alternate; const room = `private${data.random}-channel-team`; const existingRoom = data.groups.alternate.name; + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); await navigateToRoom(team); @@ -86,7 +108,7 @@ describe('Team', () => { describe('Team Channels Header', () => { it('should have actions button ', async () => { - await expect(element(by.id('room-header'))).toExist(); + await expect(element(by.id('room-header')).atIndex(0)).toExist(); }); it('should have team channels button ', async () => { @@ -124,6 +146,9 @@ describe('Team', () => { await element(by.id('add-channel-team-view-create-channel')).tap(); await element(by.id('select-users-view-search')).replaceText('rocket.cat'); + await waitFor(element(by.id('select-users-view-item-rocket.cat'))) + .toBeVisible() + .withTimeout(5000); await element(by.id('select-users-view-item-rocket.cat')).tap(); await waitFor(element(by.id('selected-user-rocket.cat'))) .toBeVisible() @@ -134,7 +159,10 @@ describe('Team', () => { .toExist() .withTimeout(10000); await element(by.id('create-channel-name')).replaceText(''); - await element(by.id('create-channel-name')).typeText(room); + await element(by.id('create-channel-name')).replaceText(room); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(10000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))) @@ -156,9 +184,9 @@ describe('Team', () => { .toExist() .withTimeout(60000); await expect(element(by.id(`room-view-title-${room}`))).toExist(); - await expect(element(by.id('room-view-header-team-channels'))).toExist(); - await expect(element(by.id('room-view-header-threads'))).toExist(); - await expect(element(by.id('room-view-search'))).toExist(); + await expect(element(by.id('room-view-header-team-channels')).atIndex(0)).toExist(); + await expect(element(by.id('room-view-header-threads')).atIndex(0)).toExist(); + await expect(element(by.id('room-view-search')).atIndex(0)).toExist(); await tapBack(); }); @@ -186,7 +214,7 @@ describe('Team', () => { await expect(element(by.id('room-view-header-team-channels'))).toExist(); await element(by.id('room-view-header-team-channels')).tap(); - await waitFor(element(by.id(`rooms-list-view-item-${existingRoom}`))) + await waitFor(element(by.id(`rooms-list-view-item-${existingRoom}`)).atIndex(0)) .toExist() .withTimeout(10000); }); @@ -195,7 +223,8 @@ describe('Team', () => { await element(by.id(`rooms-list-view-item-${existingRoom}`)) .atIndex(0) .longPress(); - + await sleep(500); + await swipeTillVisible(by.id('action-sheet-remove-from-team'), by.id('action-sheet-delete')); await waitFor(element(by.id('action-sheet-auto-join'))) .toBeVisible() .withTimeout(5000); @@ -224,7 +253,7 @@ describe('Team', () => { await waitFor(element(by.id('auto-join-tag'))) .toBeNotVisible() .withTimeout(5000); - await waitFor(element(by.id(`rooms-list-view-item-${existingRoom}`))) + await waitFor(element(by.id(`rooms-list-view-item-${existingRoom}`)).atIndex(0)) .toExist() .withTimeout(6000); }); @@ -298,22 +327,22 @@ describe('Team', () => { await waitFor( element( - by.label( + by[textMatcher]( 'You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.' ) ) ) .toExist() .withTimeout(2000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('select-list-view-submit'))) .toExist() .withTimeout(2000); await element(by.id('select-list-view-submit')).tap(); - await waitFor(element(by.text('Last owner cannot be removed'))) + await waitFor(element(by[textMatcher]('Last owner cannot be removed'))) .toExist() .withTimeout(8000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); await tapBack(); await waitFor(element(by.id('room-actions-view'))) .toExist() @@ -352,6 +381,9 @@ describe('Team', () => { it('should remove member from team', async () => { await openActionSheet('rocket.cat'); + await waitFor(element(by.id('action-sheet-remove-from-team'))) + .toBeVisible() + .withTimeout(2000); await element(by.id('action-sheet-remove-from-team')).tap(); await waitFor(element(by.id('select-list-view'))) .toExist() @@ -406,14 +438,14 @@ describe('Team', () => { await waitFor( element( - by.label( + by[textMatcher]( 'You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.' ) ) ) .toExist() .withTimeout(2000); - await element(by.text('OK')).tap(); + await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('select-list-view-submit'))) .toExist() .withTimeout(2000); diff --git a/e2e/tests/team/03-moveconvert.spec.js b/e2e/tests/team/03-moveconvert.spec.js index 5cf68bd1..7d4add2a 100644 --- a/e2e/tests/team/03-moveconvert.spec.js +++ b/e2e/tests/team/03-moveconvert.spec.js @@ -1,5 +1,5 @@ const data = require('../../data'); -const { navigateToLogin, login, tapBack, searchRoom, sleep } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, searchRoom, sleep, platformTypes } = require('../../helpers/app'); const toBeConverted = `to-be-converted-${data.random}`; const toBeMoved = `to-be-moved-${data.random}`; @@ -17,7 +17,10 @@ const createChannel = async room => { await waitFor(element(by.id('create-channel-view'))) .toExist() .withTimeout(10000); - await element(by.id('create-channel-name')).typeText(room); + await element(by.id('create-channel-name')).replaceText(room); + await waitFor(element(by.id('create-channel-submit'))) + .toExist() + .withTimeout(10000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))) .toExist() @@ -51,8 +54,11 @@ async function navigateToRoomActions(room) { } describe('Move/Convert Team', () => { + let alertButtonType; + let textMatcher; before(async () => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); }); @@ -69,10 +75,10 @@ describe('Move/Convert Team', () => { .toExist() .withTimeout(2000); await element(by.id('room-actions-convert-to-team')).tap(); - await waitFor(element(by.label('You are converting this Channel to a Team. All Members will be kept.'))) + await waitFor(element(by[textMatcher]('You are converting this Channel to a Team. All Members will be kept.'))) .toExist() .withTimeout(2000); - await element(by.text('Convert')).tap(); + await element(by[textMatcher]('Convert').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('room-view'))) .toExist() .withTimeout(20000); @@ -101,12 +107,14 @@ describe('Move/Convert Team', () => { .toExist() .withTimeout(2000); await element(by.id('room-actions-move-to-team')).tap(); - await waitFor(element(by.id('select-list-view'))) + await waitFor(element(by[textMatcher]('Move to Team')).atIndex(0)) + .toExist() + .withTimeout(2000); + await waitFor(element(by.id('select-list-view-submit'))) .toExist() .withTimeout(2000); await element(by.id('select-list-view-submit')).tap(); - await sleep(2000); - await waitFor(element(by.id('select-list-view'))) + await waitFor(element(by[textMatcher]('Select Team'))) .toExist() .withTimeout(2000); await waitFor(element(by.id(`select-list-view-item-${toBeConverted}`))) @@ -116,14 +124,14 @@ describe('Move/Convert Team', () => { await element(by.id('select-list-view-submit')).atIndex(0).tap(); await waitFor( element( - by.label( + by[textMatcher]( 'After reading the previous intructions about this behavior, do you still want to move this channel to the selected team?' ) ) ) .toExist() .withTimeout(2000); - await element(by.text('Yes, move it!')).tap(); + await element(by[textMatcher]('Yes, move it!').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('room-view-header-team-channels'))) .toExist() .withTimeout(10000); @@ -141,12 +149,11 @@ describe('Move/Convert Team', () => { it('should convert a team to a channel', async () => { await navigateToRoomActions(toBeConverted); await element(by.id('room-actions-scrollview')).scrollTo('bottom'); - await waitFor(element(by.id('room-actions-convert-channel-to-team'))) + await waitFor(element(by[textMatcher]('Convert to Channel'))) .toExist() .withTimeout(2000); - await element(by.id('room-actions-convert-channel-to-team')).tap(); - await sleep(2000); - await waitFor(element(by.id('select-list-view'))) + await element(by[textMatcher]('Convert to Channel')).atIndex(0).tap(); + await waitFor(element(by[textMatcher]('Converting Team to Channel'))) .toExist() .withTimeout(2000); await waitFor(element(by.id(`select-list-view-item-${toBeMoved}`))) @@ -157,10 +164,10 @@ describe('Move/Convert Team', () => { .toExist() .withTimeout(2000); await element(by.id('select-list-view-submit')).tap(); - await waitFor(element(by.label('You are converting this Team to a Channel'))) + await waitFor(element(by[textMatcher]('You are converting this Team to a Channel'))) .toExist() .withTimeout(2000); - await element(by.text('Convert')).tap(); + await element(by[textMatcher]('Convert').and(by.type(alertButtonType))).tap(); await waitFor(element(by.id('room-view'))) .toExist() .withTimeout(20000); diff --git a/package.json b/package.json index 1a0a4851..1582969f 100644 --- a/package.json +++ b/package.json @@ -239,6 +239,18 @@ } } } + }, + "and.emu.debug": { + "device": "Pixel_API_28_AOSP", + "type": "android.emulator", + "binaryPath": "android/app/build/outputs/apk/e2ePlay/debug/app-e2e-play-debug.apk", + "build": "cd android && ./gradlew app:assembleE2ePlayDebug app:assembleE2ePlayDebugAndroidTest -DtestBuildType=debug && cd .." + }, + "and.emu.release": { + "device": "Pixel_API_28_AOSP", + "type": "android.emulator", + "binaryPath": "android/app/build/outputs/apk/e2ePlay/release/app-e2e-play-release.apk", + "build": "cd android && ./gradlew app:assembleE2ePlayRelease app:assembleE2ePlayReleaseAndroidTest -DtestBuildType=release && cd .." } } }