diff --git a/.circleci/config.yml b/.circleci/config.yml index cda06c134..46099d3a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,6 +36,9 @@ commands: name: Test command: | npx detox test << parameters.folder >> --configuration ios.sim.release --cleanup + when: always + - store_artifacts: + path: ./artifacts install-npm-modules: description: Install NPM modules diff --git a/.gitignore b/.gitignore index fee7bf865..18624dd0b 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ buck-out/ coverage +artifacts .vscode/ e2e/docker/rc_test_env/docker-compose.yml e2e/docker/data/db \ No newline at end of file diff --git a/app/containers/LoginServices.js b/app/containers/LoginServices.js index 11ffc6d97..31c8bbaf2 100644 --- a/app/containers/LoginServices.js +++ b/app/containers/LoginServices.js @@ -15,6 +15,7 @@ import OrSeparator from './OrSeparator'; import Touch from '../utils/touch'; import I18n from '../i18n'; import random from '../utils/random'; +import { logEvent, events } from '../utils/log'; import RocketChat from '../lib/rocketchat'; const BUTTON_HEIGHT = 48; @@ -77,6 +78,7 @@ class LoginServices extends React.PureComponent { } onPressFacebook = () => { + logEvent(events.LOGIN_WITH_FACEBOOK); const { services, server } = this.props; const { clientId } = services.facebook; const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth'; @@ -88,6 +90,7 @@ class LoginServices extends React.PureComponent { } onPressGithub = () => { + logEvent(events.LOGIN_WITH_GITHUB); const { services, server } = this.props; const { clientId } = services.github; const endpoint = `https://github.com/login?client_id=${ clientId }&return_to=${ encodeURIComponent('/login/oauth/authorize') }`; @@ -99,6 +102,7 @@ class LoginServices extends React.PureComponent { } onPressGitlab = () => { + logEvent(events.LOGIN_WITH_GITLAB); const { services, server, Gitlab_URL } = this.props; const { clientId } = services.gitlab; const baseURL = Gitlab_URL ? Gitlab_URL.trim().replace(/\/*$/, '') : 'https://gitlab.com'; @@ -111,6 +115,7 @@ class LoginServices extends React.PureComponent { } onPressGoogle = () => { + logEvent(events.LOGIN_WITH_GOOGLE); const { services, server } = this.props; const { clientId } = services.google; const endpoint = 'https://accounts.google.com/o/oauth2/auth'; @@ -122,6 +127,7 @@ class LoginServices extends React.PureComponent { } onPressLinkedin = () => { + logEvent(events.LOGIN_WITH_LINKEDIN); const { services, server } = this.props; const { clientId } = services.linkedin; const endpoint = 'https://www.linkedin.com/oauth/v2/authorization'; @@ -133,6 +139,7 @@ class LoginServices extends React.PureComponent { } onPressMeteor = () => { + logEvent(events.LOGIN_WITH_METEOR); const { services, server } = this.props; const { clientId } = services['meteor-developer']; const endpoint = 'https://www.meteor.com/oauth2/authorize'; @@ -143,6 +150,7 @@ class LoginServices extends React.PureComponent { } onPressTwitter = () => { + logEvent(events.LOGIN_WITH_TWITTER); const { server } = this.props; const state = this.getOAuthState(); const url = `${ server }/_oauth/twitter/?requestTokenAndRedirect=true&state=${ state }`; @@ -150,6 +158,7 @@ class LoginServices extends React.PureComponent { } onPressWordpress = () => { + logEvent(events.LOGIN_WITH_WORDPRESS); const { services, server } = this.props; const { clientId, serverURL } = services.wordpress; const endpoint = `${ serverURL }/oauth/authorize`; @@ -161,6 +170,7 @@ class LoginServices extends React.PureComponent { } onPressCustomOAuth = (loginService) => { + logEvent(events.LOGIN_WITH_CUSTOM_OAUTH); const { server } = this.props; const { serverURL, authorizePath, clientId, scope, service @@ -175,6 +185,7 @@ class LoginServices extends React.PureComponent { } onPressSaml = (loginService) => { + logEvent(events.LOGIN_WITH_SAML); const { server } = this.props; const { clientConfig } = loginService; const { provider } = clientConfig; @@ -184,6 +195,7 @@ class LoginServices extends React.PureComponent { } onPressCas = () => { + logEvent(events.LOGIN_WITH_CAS); const { server, CAS_login_url } = this.props; const ssoToken = random(17); const url = `${ CAS_login_url }?service=${ server }/_cas/${ ssoToken }`; diff --git a/app/containers/TwoFactor/index.js b/app/containers/TwoFactor/index.js index e59ed0c1a..011b5e3de 100644 --- a/app/containers/TwoFactor/index.js +++ b/app/containers/TwoFactor/index.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { View, Text } from 'react-native'; +import { View, Text, InteractionManager } from 'react-native'; import _ from 'lodash'; import PropTypes from 'prop-types'; import { sha256 } from 'js-sha256'; @@ -99,6 +99,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => { InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())} returnKeyType='send' autoCapitalize='none' onChangeText={setCode} diff --git a/app/containers/TwoFactor/styles.js b/app/containers/TwoFactor/styles.js index 36608408a..1c5c789cb 100644 --- a/app/containers/TwoFactor/styles.js +++ b/app/containers/TwoFactor/styles.js @@ -14,9 +14,10 @@ export default StyleSheet.create({ borderRadius: 4 }, title: { - fontSize: 14, + fontSize: 16, paddingBottom: 8, - ...sharedStyles.textBold + ...sharedStyles.textBold, + ...sharedStyles.textAlignCenter }, subtitle: { fontSize: 14, diff --git a/app/sagas/login.js b/app/sagas/login.js index 587b9b7ba..199952190 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -17,7 +17,7 @@ import { import { roomsRequest } from '../actions/rooms'; import { toMomentLocale } from '../utils/moment'; import RocketChat from '../lib/rocketchat'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import I18n from '../i18n'; import database from '../lib/database'; import EventEmitter from '../utils/events'; @@ -32,6 +32,7 @@ const loginCall = args => RocketChat.login(args); const logoutCall = args => RocketChat.logout(args); const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false }) { + logEvent(events.DEFAULT_LOGIN); try { let result; if (credentials.resume) { @@ -52,6 +53,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE if (logoutOnError && (e.data && e.data.message && /you've been logged out by the server/i.test(e.data.message))) { yield put(logout(true)); } else { + logEvent(events.DEFAULT_LOGIN_FAIL); yield put(loginFailure(e)); } } diff --git a/app/utils/log/events.js b/app/utils/log/events.js new file mode 100644 index 000000000..01297cd2a --- /dev/null +++ b/app/utils/log/events.js @@ -0,0 +1,24 @@ +export default { + JOIN_A_WORKSPACE: 'join_a_workspace', + CREATE_NEW_WORKSPACE: 'create_new_workspace', + CREATE_NEW_WORKSPACE_FAIL: 'create_new_workspace_fail', + CONNECT_TO_WORKSPACE: 'connect_to_workspace', + CONNECT_TO_WORKSPACE_FAIL: 'connect_to_workspace_fail', + JOIN_OPEN_WORKSPACE: 'join_open_workspace', + DEFAULT_LOGIN: 'default_login', + DEFAULT_LOGIN_FAIL: 'default_login_fail', + DEFAULT_SIGN_UP: 'default_sign_up', + DEFAULT_SIGN_UP_FAIL: 'default_sign_up_fail', + FORGOT_PASSWORD: 'forgot_password', + LOGIN_WITH_FACEBOOK: 'login_with_facebook', + LOGIN_WITH_GITHUB: 'login_with_github', + LOGIN_WITH_GITLAB: 'login_with_gitlab', + LOGIN_WITH_LINKEDIN: 'login_with_linkedin', + LOGIN_WITH_GOOGLE: 'login_with_google', + LOGIN_WITH_METEOR: 'login_with_meteor', + LOGIN_WITH_TWITTER: 'login_with_twitter', + LOGIN_WITH_WORDPRESS: 'login_with_wordpress', + LOGIN_WITH_CUSTOM_OAUTH: 'login_with_custom_oauth', + LOGIN_WITH_SAML: 'login_with_saml', + LOGIN_WITH_CAS: 'login_with_cas' +}; diff --git a/app/utils/log.js b/app/utils/log/index.js similarity index 77% rename from app/utils/log.js rename to app/utils/log/index.js index f2d0fe2f6..2a8b6720e 100644 --- a/app/utils/log.js +++ b/app/utils/log/index.js @@ -1,12 +1,14 @@ import { Client } from 'bugsnag-react-native'; import firebase from 'react-native-firebase'; -import config from '../../config'; +import config from '../../../config'; +import events from './events'; const bugsnag = new Client(config.BUGSNAG_API_KEY); export const { analytics } = firebase; export const loggerConfig = bugsnag.config; export const { leaveBreadcrumb } = bugsnag; +export { events }; let metadata = {}; @@ -16,6 +18,11 @@ export const logServerVersion = (serverVersion) => { }; }; +export const logEvent = (eventName, payload) => { + analytics().logEvent(eventName, payload); + leaveBreadcrumb(eventName, payload); +}; + export const setCurrentScreen = (currentScreen) => { analytics().setCurrentScreen(currentScreen); leaveBreadcrumb(currentScreen, { type: 'navigation' }); diff --git a/app/views/AdminPanelView/index.js b/app/views/AdminPanelView/index.js index 283e2ef74..41005fc93 100644 --- a/app/views/AdminPanelView/index.js +++ b/app/views/AdminPanelView/index.js @@ -31,6 +31,8 @@ class AdminPanelView extends React.Component { {}} source={{ uri: `${ baseUrl }/admin/info?layout=embedded` }} injectedJavaScript={`Meteor.loginWithToken('${ token }', function() { })`} /> diff --git a/app/views/LoginView.js b/app/views/LoginView.js index abc901272..9e566b5be 100644 --- a/app/views/LoginView.js +++ b/app/views/LoginView.js @@ -6,7 +6,7 @@ import { import { connect } from 'react-redux'; import equal from 'deep-equal'; -import { analytics } from '../utils/log'; +import { logEvent, events } from '../utils/log'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import I18n from '../i18n'; @@ -103,6 +103,7 @@ class LoginView extends React.Component { } forgotPassword = () => { + logEvent(events.FORGOT_PASSWORD); const { navigation, Site_Name } = this.props; navigation.navigate('ForgotPasswordView', { title: Site_Name }); } @@ -121,7 +122,6 @@ class LoginView extends React.Component { const { loginRequest } = this.props; Keyboard.dismiss(); loginRequest({ user, password }); - analytics().logEvent('login'); } renderUserForm = () => { diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js index cd443781b..f49ddbabd 100644 --- a/app/views/NewServerView.js +++ b/app/views/NewServerView.js @@ -20,7 +20,7 @@ import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import I18n from '../i18n'; import { isIOS } from '../utils/deviceInfo'; import { themes } from '../constants/colors'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import { animateNextTransition } from '../utils/layoutAnimation'; import { withTheme } from '../theme'; import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch'; @@ -124,6 +124,7 @@ class NewServerView extends React.Component { } submit = async() => { + logEvent(events.CONNECT_TO_WORKSPACE); const { text, certificate } = this.state; const { connectServer } = this.props; let cert = null; @@ -135,6 +136,7 @@ class NewServerView extends React.Component { try { await FileSystem.copyAsync({ from: certificate.path, to: certificatePath }); } catch (e) { + logEvent(events.CONNECT_TO_WORKSPACE_FAIL); log(e); } cert = { @@ -152,6 +154,7 @@ class NewServerView extends React.Component { } connectOpen = () => { + logEvent(events.JOIN_OPEN_WORKSPACE); this.setState({ connectingOpen: true }); const { connectServer } = this.props; connectServer('https://open.rocket.chat'); diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js index 0a1ac7383..0d509e0d4 100644 --- a/app/views/OnboardingView/index.js +++ b/app/views/OnboardingView/index.js @@ -14,6 +14,7 @@ import { isTablet } from '../../utils/deviceInfo'; import { themes } from '../../constants/colors'; import { withTheme } from '../../theme'; import FormContainer, { FormContainerInner } from '../../containers/FormContainer'; +import { logEvent, events } from '../../utils/log'; class OnboardingView extends React.Component { static navigationOptions = { @@ -69,15 +70,17 @@ class OnboardingView extends React.Component { } connectServer = () => { + logEvent(events.JOIN_A_WORKSPACE); const { navigation } = this.props; navigation.navigate('NewServerView'); } createWorkspace = async() => { + logEvent(events.CREATE_NEW_WORKSPACE); try { await Linking.openURL('https://cloud.rocket.chat/trial'); } catch { - // do nothing + logEvent(events.CREATE_NEW_WORKSPACE_FAIL); } } diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js index 1e0ce2654..d95fb7938 100644 --- a/app/views/RegisterView.js +++ b/app/views/RegisterView.js @@ -6,7 +6,7 @@ import { import { connect } from 'react-redux'; import RNPickerSelect from 'react-native-picker-select'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import I18n from '../i18n'; @@ -114,6 +114,7 @@ class RegisterView extends React.Component { } submit = async() => { + logEvent(events.DEFAULT_SIGN_UP); if (!this.valid()) { return; } @@ -149,6 +150,7 @@ class RegisterView extends React.Component { return loginRequest({ user: email, password }); } if (e.data?.error) { + logEvent(events.DEFAULT_SIGN_UP_FAIL); showErrorAlert(e.data.error, I18n.t('Oops')); } } diff --git a/e2e/data.js b/e2e/data.js index 4c4c8ca16..fa153468d 100644 --- a/e2e/data.js +++ b/e2e/data.js @@ -29,10 +29,15 @@ const data = { } }, channels: { - public: { + detoxpublic: { name: 'detox-public' } }, + groups: { + private: { + name: `detox-private-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, diff --git a/e2e/data/data.cloud.js b/e2e/data/data.cloud.js index 4c4c8ca16..fa153468d 100644 --- a/e2e/data/data.cloud.js +++ b/e2e/data/data.cloud.js @@ -29,10 +29,15 @@ const data = { } }, channels: { - public: { + detoxpublic: { name: 'detox-public' } }, + groups: { + private: { + name: `detox-private-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, diff --git a/e2e/data/data.docker.js b/e2e/data/data.docker.js index 5a7ed505c..95f31ca9f 100644 --- a/e2e/data/data.docker.js +++ b/e2e/data/data.docker.js @@ -29,10 +29,15 @@ const data = { } }, channels: { - public: { + detoxpublic: { name: 'detox-public' } }, + groups: { + private: { + name: `detox-private-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js index 88ef3d5ad..92feb61d3 100644 --- a/e2e/helpers/app.js +++ b/e2e/helpers/app.js @@ -31,7 +31,6 @@ async function login(username, password) { await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); await element(by.id('login-view-email')).replaceText(username); await element(by.id('login-view-password')).replaceText(password); - await sleep(300); await element(by.id('login-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); } @@ -61,6 +60,33 @@ async function mockMessage(message) { await element(by.label(`${ data.random }${ message }`)).atIndex(0).tap(); }; +async function starMessage(message){ + const messageLabel = `${ data.random }${ message }` + await waitFor(element(by.label(messageLabel))).toBeVisible().withTimeout(5000); + await element(by.label(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')).tap(); + await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); +}; + +async function pinMessage(message){ + const messageLabel = `${ data.random }${ message }` + await waitFor(element(by.label(messageLabel)).atIndex(0)).toExist(); + await element(by.label(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')).tap(); + await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); +} + +async function dismissReviewNag(){ + await waitFor(element(by.text('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 +} + async function tapBack() { await element(by.id('header-back')).atIndex(0).tap(); } @@ -74,7 +100,22 @@ async function searchRoom(room) { 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(2000); +} + +async function tryTapping(theElement, timeout, longtap = false){ + try { + if(longtap){ + await theElement.longPress() + } else { + await theElement.tap() + } + } catch(e) { + if(timeout <= 0){ //TODO: Maths. How closely has the timeout been honoured here? + throw e + } + await sleep(100) + await tryTapping(theElement, timeout - 100) + } } module.exports = { @@ -84,7 +125,11 @@ module.exports = { login, logout, mockMessage, + starMessage, + pinMessage, + dismissReviewNag, tapBack, sleep, - searchRoom + searchRoom, + tryTapping }; \ No newline at end of file diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js index 84e5927ea..d16b41a55 100644 --- a/e2e/helpers/data_setup.js +++ b/e2e/helpers/data_setup.js @@ -38,7 +38,7 @@ const createUser = async (username, password, name, email) => { } const createChannelIfNotExists = async (channelname) => { - console.log(`Creating channel ${channelname}`) + console.log(`Creating public channel ${channelname}`) try { await rocketchat.post('channels.create', { "name": channelname @@ -49,7 +49,24 @@ const createChannelIfNotExists = async (channelname) => { } catch (infoError) { console.log(JSON.stringify(createError)) console.log(JSON.stringify(infoError)) - throw "Failed to find or create channel" + throw "Failed to find or create public channel" + } + } +} + +const createGroupIfNotExists = async (groupname) => { + console.log(`Creating private group ${groupname}`) + try { + await rocketchat.post('groups.create', { + "name": groupname + }) + } catch (createError) { + try { //Maybe it exists already? + await rocketchat.get(`group.info?roomName=${groupname}`) + } catch (infoError) { + console.log(JSON.stringify(createError)) + console.log(JSON.stringify(infoError)) + throw "Failed to find or create private group" } } } @@ -71,6 +88,15 @@ const setup = async () => { } } + await login(data.users.regular.username, data.users.regular.password) + + for (var groupKey in data.groups) { + if (data.groups.hasOwnProperty(groupKey)) { + const group = data.groups[groupKey] + await createGroupIfNotExists(group.name) + } + } + return } diff --git a/e2e/tests/assorted/01-changeserver.spec.js b/e2e/tests/assorted/01-changeserver.spec.js index 0664970a7..7210d7060 100644 --- a/e2e/tests/assorted/01-changeserver.spec.js +++ b/e2e/tests/assorted/01-changeserver.spec.js @@ -9,12 +9,12 @@ const checkServer = async(server) => { await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000); - await expect(element(by.label(label))).toBeVisible(); await element(by.id('sidebar-close-drawer')).tap(); } describe('Change server', () => { before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); @@ -28,8 +28,6 @@ describe('Change server', () => { await sleep(5000); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); - await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); - await sleep(1000); await element(by.id('rooms-list-header-server-add')).tap(); // TODO: refactor @@ -37,19 +35,16 @@ describe('Change server', () => { await element(by.id('new-server-view-input')).replaceText(data.alternateServer); await element(by.id('new-server-view-button')).tap(); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('workspace-view'))).toBeVisible(); await element(by.id('workspace-view-register')).tap(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('register-view'))).toBeVisible(); + // Register new user 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')).replaceText(data.registeringUser.password); - await sleep(1000); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); // For a sanity test, to make sure roomslist is showing correct rooms // app CANNOT show public room created on previous tests @@ -59,11 +54,8 @@ describe('Change server', () => { }); it('should change back', async() => { - await sleep(5000); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); - await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); - await sleep(1000); await element(by.id(`rooms-list-header-server-${ data.server }`)).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await checkServer(data.server); diff --git a/e2e/tests/assorted/02-broadcast.spec.js b/e2e/tests/assorted/02-broadcast.spec.js index 1b2fcc1db..9e87c5e14 100644 --- a/e2e/tests/assorted/02-broadcast.spec.js +++ b/e2e/tests/assorted/02-broadcast.spec.js @@ -23,28 +23,16 @@ describe('Broadcast room', () => { await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000); await element(by.id('select-users-view-search')).replaceText(otheruser.username); await waitFor(element(by.id(`select-users-view-item-${ otheruser.username }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`select-users-view-item-${ otheruser.username }`))).toBeVisible(); await element(by.id(`select-users-view-item-${ otheruser.username }`)).tap(); await waitFor(element(by.id(`selected-user-${ otheruser.username }`))).toBeVisible().withTimeout(5000); - await sleep(1000); await element(by.id('selected-users-view-submit')).tap(); - await sleep(1000); await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000); await element(by.id('create-channel-name')).replaceText(`broadcast${ data.random }`); - await sleep(2000); - await element(by.id('create-channel-broadcast')).tap(); - if (device.getPlatform() === 'ios') { //Because this tap is FLAKY on iOS - await expect(element(by.id('create-channel-broadcast'))).toHaveValue('1') - } - await sleep(500); + await element(by.id('create-channel-broadcast')).longPress(); //https://github.com/facebook/react-native/issues/28032 await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('room-view'))).toBeVisible(); await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible(); - await sleep(1000); await element(by.id('room-view-header-actions')).tap(); - await sleep(1000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); await element(by.id('room-actions-info')).tap(); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); @@ -64,25 +52,19 @@ describe('Broadcast room', () => { it('should login as user without write message authorization and enter room', async() => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); - await element(by.id('login-view-email')).replaceText(otheruser.username); - await element(by.id('login-view-password')).replaceText(otheruser.password); - await sleep(1000); - await element(by.id('login-view-submit')).tap(); + await login(otheruser.username, otheruser.password); + //await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000); //await expect(element(by.id('two-factor'))).toBeVisible(); //const code = GA.gen(data.alternateUserTOTPSecret); //await element(by.id('two-factor-input')).replaceText(code); - //await sleep(1000); //await element(by.id('two-factor-send')).tap(); - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); + await searchRoom(`broadcast${ data.random }`); await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist(); await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible(); - await sleep(1000); }); it('should not have messagebox', async() => { @@ -95,7 +77,6 @@ describe('Broadcast room', () => { it('should have the message created earlier', async() => { await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible(); }); it('should have reply button', async() => { @@ -104,9 +85,7 @@ describe('Broadcast room', () => { it('should tap on reply button and navigate to direct room', async() => { await element(by.id('message-broadcast-reply')).tap(); - await sleep(1000); await waitFor(element(by.id(`room-view-title-${ testuser.username }`))).toBeVisible().withTimeout(5000); - await expect(element(by.id(`room-view-title-${ testuser.username }`))).toBeVisible(); }); it('should reply broadcasted message', async() => { diff --git a/e2e/tests/assorted/03-profile.spec.js b/e2e/tests/assorted/03-profile.spec.js index e5c321757..a000f6437 100644 --- a/e2e/tests/assorted/03-profile.spec.js +++ b/e2e/tests/assorted/03-profile.spec.js @@ -13,7 +13,7 @@ async function waitForToast() { // await expect(element(by.id('toast'))).toBeVisible(); // await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000); // await expect(element(by.id('toast'))).toBeNotVisible(); - await sleep(5000); + await sleep(1); } describe('Profile screen', () => { @@ -24,7 +24,6 @@ describe('Profile screen', () => { await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-profile'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('sidebar-profile'))).toBeVisible(); await element(by.id('sidebar-profile')).tap(); await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000); }); @@ -60,22 +59,18 @@ describe('Profile screen', () => { it('should have reset avatar button', async() => { await waitFor(element(by.id('profile-view-reset-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-reset-avatar'))).toExist(); }); it('should have upload avatar button', async() => { await waitFor(element(by.id('profile-view-upload-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-upload-avatar'))).toExist(); }); it('should have avatar url button', async() => { await waitFor(element(by.id('profile-view-avatar-url-button'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-avatar-url-button'))).toExist(); }); it('should have submit button', async() => { await waitFor(element(by.id('profile-view-submit'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-submit'))).toExist(); }); }); @@ -84,9 +79,7 @@ describe('Profile screen', () => { await element(by.type('UIScrollView')).atIndex(1).swipe('down'); await element(by.id('profile-view-name')).replaceText(`${ profileChangeUser.username }new`); await element(by.id('profile-view-username')).replaceText(`${ profileChangeUser.username }new`); - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); await element(by.id('profile-view-submit')).tap(); await waitForToast(); }); @@ -103,7 +96,6 @@ describe('Profile screen', () => { it('should reset avatar', async() => { await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); 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 c2cfae6f1..951ccc70d 100644 --- a/e2e/tests/assorted/04-setting.spec.js +++ b/e2e/tests/assorted/04-setting.spec.js @@ -1,12 +1,18 @@ const { device, expect, element, by, waitFor } = require('detox'); +const { navigateToLogin, login } = require('../../helpers/app'); + +const data = require('../../data'); + +const testuser = data.users.regular describe('Settings screen', () => { before(async() => { - await device.launchApp({ newInstance: true }); + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000); diff --git a/e2e/tests/assorted/05-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js index d68d1a955..1c946f0c2 100644 --- a/e2e/tests/assorted/05-joinpublicroom.spec.js +++ b/e2e/tests/assorted/05-joinpublicroom.spec.js @@ -2,12 +2,12 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app'); -const room = 'detox-public'; +const testuser = data.users.regular +const room = data.channels.detoxpublic.name; async function navigateToRoom() { - await sleep(2000); await searchRoom(room); await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000); await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap(); @@ -15,15 +15,15 @@ async function navigateToRoom() { } async function navigateToRoomActions() { - await sleep(2000); await element(by.id('room-view-header-actions')).tap(); - await sleep(2000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); } describe('Join public room', () => { before(async() => { - await device.launchApp({ newInstance: true }); + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); await navigateToRoom(); }); @@ -167,9 +167,7 @@ describe('Join public room', () => { await element(by.text('Yes, leave it!')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); // await element(by.id('rooms-list-view-search')).typeText(''); - await sleep(2000); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible(); }); }); }); diff --git a/e2e/tests/assorted/06-status.spec.js b/e2e/tests/assorted/06-status.spec.js index ee592d41f..887dcb1e0 100644 --- a/e2e/tests/assorted/06-status.spec.js +++ b/e2e/tests/assorted/06-status.spec.js @@ -1,14 +1,21 @@ const { expect, element, by, waitFor } = require('detox'); -const { sleep } = require('../../helpers/app'); +const { navigateToLogin, login, sleep } = require('../../helpers/app'); + +const data = require('../../data'); +const testuser = data.users.regular async function waitForToast() { - await sleep(5000); + await sleep(1); } describe('Status screen', () => { - before(async() => { + before(async () => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); + await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-custom-status'))).toBeVisible().withTimeout(2000); @@ -17,30 +24,27 @@ describe('Status screen', () => { await waitFor(element(by.id('status-view'))).toBeVisible().withTimeout(2000); }); - describe('Render', async() => { - it('should have status input', async() => { + describe('Render', async () => { + it('should have status input', async () => { await expect(element(by.id('status-view-input'))).toBeVisible(); await expect(element(by.id('status-view-online'))).toExist(); await expect(element(by.id('status-view-busy'))).toExist(); await expect(element(by.id('status-view-away'))).toExist(); await expect(element(by.id('status-view-offline'))).toExist(); - }); - }); - - describe('Usage', async() => { - it('should change status', async() => { - await sleep(1000); - await element(by.id('status-view-busy')).tap(); - await sleep(1000); - await expect(element(by.id('status-view-current-busy'))).toExist(); - }); + }); + }); - it('should change status text', async() => { + describe('Usage', async () => { + it('should change status', async () => { + await element(by.id('status-view-busy')).tap(); + await expect(element(by.id('status-view-current-busy'))).toExist(); + }); + + it('should change status text', async () => { await element(by.id('status-view-input')).replaceText('status-text-new'); - await sleep(1000); await element(by.id('status-view-submit')).tap(); await waitForToast(); await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toBeVisible().withTimeout(2000); - }); - }); -}); + }); + }); +}); \ No newline at end of file diff --git a/e2e/tests/init.js b/e2e/tests/init.js index bef12f6d3..9eaa721b6 100644 --- a/e2e/tests/init.js +++ b/e2e/tests/init.js @@ -1,11 +1,21 @@ const detox = require('detox'); const config = require('../../package.json').detox; const dataSetup = require('../helpers/data_setup') +const adapter = require('detox/runners/mocha/adapter'); before(async() => { - await dataSetup() - await detox.init(config, { launchApp: false }); - await device.launchApp({ permissions: { notifications: 'YES' } }); + await Promise.all([dataSetup(), detox.init(config, { launchApp: false })]) + //await dataSetup() + //await detox.init(config, { launchApp: false }); + //await device.launchApp({ permissions: { notifications: 'YES' } }); +}); + +beforeEach(async function() { + await adapter.beforeEach(this); +}); + +afterEach(async function() { + await adapter.afterEach(this); }); after(async() => { diff --git a/e2e/tests/onboarding/01-onboarding.spec.js b/e2e/tests/onboarding/01-onboarding.spec.js index c84928d60..9c049099e 100644 --- a/e2e/tests/onboarding/01-onboarding.spec.js +++ b/e2e/tests/onboarding/01-onboarding.spec.js @@ -31,7 +31,6 @@ describe('Onboarding', () => { it('should navigate to join a workspace', async() => { await element(by.id('join-workspace')).tap(); await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('new-server-view'))).toBeVisible(); }); it('should enter an invalid server and get error', async() => { @@ -39,14 +38,12 @@ describe('Onboarding', () => { await element(by.id('new-server-view-button')).tap(); const errorText = 'Oops!'; await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000); - await expect(element(by.text(errorText))).toBeVisible(); await element(by.text('OK')).tap(); }); it('should tap on "Join our open workspace" and navigate', async() => { await element(by.id('new-server-view-open')).tap(); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('workspace-view'))).toBeVisible(); }); it('should enter a valid server without login services and navigate to login', async() => { @@ -57,7 +54,6 @@ describe('Onboarding', () => { await element(by.id('new-server-view-input')).replaceText(data.server); await element(by.id('new-server-view-button')).tap(); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('workspace-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/02-legal.spec.js b/e2e/tests/onboarding/02-legal.spec.js index e8109ece5..191e0647c 100644 --- a/e2e/tests/onboarding/02-legal.spec.js +++ b/e2e/tests/onboarding/02-legal.spec.js @@ -4,54 +4,62 @@ const { const { navigateToRegister, navigateToLogin } = require('../../helpers/app'); describe('Legal screen', () => { - it('should have legal button on login', async() => { - await device.launchApp({ newInstance: true }); - await navigateToLogin(); - await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('login-view-more'))).toBeVisible(); - }); - it('should navigate to legal from login', async() => { - await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); - await element(by.id('login-view-more')).tap(); - }); - - it('should have legal button on register', async() => { - await device.launchApp({ newInstance: true }); - await navigateToRegister(); - await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('register-view-more'))).toBeVisible(); - }); - - it('should navigate to legal from register', async() => { - await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); - await element(by.id('register-view-more')).tap(); - }); - - it('should have legal screen', async() => { - await expect(element(by.id('legal-view'))).toBeVisible(); - }); - - it('should have terms of service button', async() => { - await expect(element(by.id('legal-terms-button'))).toBeVisible(); - }); - - it('should have privacy policy button', async() => { - await expect(element(by.id('legal-privacy-button'))).toBeVisible(); - }); + describe('From Login', () => { + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + }); - - // We can't simulate how webview behaves, so I had to disable :( - // it('should navigate to terms', async() => { - // await element(by.id('legal-terms-button')).tap(); - // await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('terms-view'))).toBeVisible(); - // }); + it('should have legal button on login', async() => { + await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); + }); - // it('should navigate to privacy', async() => { - // await tapBack(); - // await element(by.id('legal-privacy-button')).tap(); - // await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('privacy-view'))).toBeVisible(); - // }); + it('should navigate to legal from login', async() => { + await expect(element(by.id('login-view-more'))).toBeVisible(); + await element(by.id('login-view-more')).tap(); + await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000) + }); + }); + + describe('From Register', () => { + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToRegister(); + }); + + it('should have legal button on register', async() => { + await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); + }); + + it('should navigate to legal from register', async() => { + await expect(element(by.id('register-view-more'))).toBeVisible(); + await element(by.id('register-view-more')).tap(); + await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000); + }); + + it('should have terms of service button', async() => { + await expect(element(by.id('legal-terms-button'))).toBeVisible(); + }); + + it('should have privacy policy button', async() => { + await expect(element(by.id('legal-privacy-button'))).toBeVisible(); + }); + + // We can't simulate how webview behaves, so I had to disable :( + /* + it('should navigate to terms', async() => { + await element(by.id('legal-terms-button')).tap(); + await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.id('terms-view'))).toBeVisible(); + }); + + it('should navigate to privacy', async() => { + await tapBack(); + await element(by.id('legal-privacy-button')).tap(); + await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.id('privacy-view'))).toBeVisible(); + }); + */ + }); }); diff --git a/e2e/tests/onboarding/03-forgotpassword.spec.js b/e2e/tests/onboarding/03-forgotpassword.spec.js index 2fa710469..88d4c3e90 100644 --- a/e2e/tests/onboarding/03-forgotpassword.spec.js +++ b/e2e/tests/onboarding/03-forgotpassword.spec.js @@ -32,7 +32,6 @@ describe('Forgot password screen', () => { await element(by.id('forgot-password-view-submit')).tap(); await element(by.text('OK')).tap(); await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('login-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/04-createuser.spec.js b/e2e/tests/onboarding/04-createuser.spec.js index 6abd8fc16..aa75807f2 100644 --- a/e2e/tests/onboarding/04-createuser.spec.js +++ b/e2e/tests/onboarding/04-createuser.spec.js @@ -53,10 +53,8 @@ describe('Create user screen', () => { await element(by.id('register-view-username')).replaceText(data.registeringUser.username); await element(by.id('register-view-email')).replaceText(data.users.existing.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); - await sleep(300); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.text('Email already exists. [403]')).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text('Email already exists. [403]')).atIndex(0)).toExist(); await element(by.text('OK')).tap(); }); @@ -65,10 +63,8 @@ describe('Create user screen', () => { await element(by.id('register-view-username')).replaceText(data.users.existing.username); await element(by.id('register-view-email')).replaceText(data.registeringUser.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); - await sleep(300); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.text('Username is already in use')).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text('Username is already in use')).atIndex(0)).toExist(); await element(by.text('OK')).tap(); }); @@ -77,10 +73,8 @@ describe('Create user screen', () => { 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')).replaceText(data.registeringUser.password); - await sleep(300); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/05-login.spec.js b/e2e/tests/onboarding/05-login.spec.js index 3734b543d..7c6b4853c 100644 --- a/e2e/tests/onboarding/05-login.spec.js +++ b/e2e/tests/onboarding/05-login.spec.js @@ -44,33 +44,27 @@ describe('Login screen', () => { it('should navigate to register', async() => { await element(by.id('login-view-register')).tap(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('register-view'))).toBeVisible(); await tapBack(); }); it('should navigate to forgot password', async() => { await element(by.id('login-view-forgot-password')).tap(); await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000); - await expect(element(by.id('forgot-password-view'))).toExist(); await tapBack(); }); it('should insert wrong password and get error', async() => { await element(by.id('login-view-email')).replaceText(data.users.regular.username); await element(by.id('login-view-password')).replaceText('NotMyActualPassword'); - await sleep(300); await element(by.id('login-view-submit')).tap(); await waitFor(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible().withTimeout(10000); - await expect(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible(); await element(by.text('OK')).tap(); }); it('should login with success', async() => { await element(by.id('login-view-password')).replaceText(data.users.regular.password); - await sleep(300); await element(by.id('login-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js index 5e7e1e8aa..bbe0b886b 100644 --- a/e2e/tests/onboarding/06-roomslist.spec.js +++ b/e2e/tests/onboarding/06-roomslist.spec.js @@ -1,9 +1,17 @@ const { device, expect, element, by, waitFor } = require('detox'); -const { logout, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { login, navigateToLogin, logout, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const data = require('../../data'); describe('Rooms list screen', () => { + + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password) + }); + describe('Render', () => { it('should have rooms list screen', async() => { await expect(element(by.id('rooms-list-view'))).toBeVisible(); @@ -29,18 +37,12 @@ describe('Rooms list screen', () => { it('should search room and navigate', async() => { await searchRoom('rocket.cat'); await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible(); await element(by.id('rooms-list-view-item-rocket.cat')).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000); - await expect(element(by.id('room-view'))).toBeVisible(); await waitFor(element(by.id('room-view-title-rocket.cat'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('room-view-title-rocket.cat'))).toBeVisible(); await tapBack(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); - await sleep(2000); await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000); - await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist(); }); it('should logout', async() => { diff --git a/e2e/tests/room/01-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js index 1cfbf16a7..35b007dba 100644 --- a/e2e/tests/room/01-createroom.spec.js +++ b/e2e/tests/room/01-createroom.spec.js @@ -2,48 +2,50 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { tapBack, sleep, navigateToLogin, login } = require('../../helpers/app'); +const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app'); + + describe('Create room screen', () => { before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); - await element(by.id('rooms-list-view-create-channel')).tap(); - await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); }); describe('New Message', async() => { + before(async() => { + await element(by.id('rooms-list-view-create-channel')).tap(); + }); + describe('Render', async() => { it('should have new message screen', async() => { - await expect(element(by.id('new-message-view'))).toExist(); + await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000); }); it('should have search input', async() => { - await waitFor(element(by.id('new-message-view-search'))).toExist().withTimeout(2000); - await expect(element(by.id('new-message-view-search'))).toExist(); + await waitFor(element(by.id('new-message-view-search'))).toBeVisible().withTimeout(2000); }); }) describe('Usage', async() => { it('should back to rooms list', async() => { - await sleep(1000); + await waitFor(element(by.id('new-message-view-close'))).toBeVisible().withTimeout(2000); await element(by.id('new-message-view-close')).tap(); - await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); - await expect(element(by.id('rooms-list-view'))).toExist(); - await element(by.id('rooms-list-view-create-channel')).tap(); + + await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); + + await tryTapping(element(by.id('rooms-list-view-create-channel')), 3000); + //await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await expect(element(by.id('new-message-view'))).toExist(); }); it('should search user and navigate', async() => { await element(by.id('new-message-view-search')).replaceText('rocket.cat'); await waitFor(element(by.id('new-message-view-item-rocket.cat'))).toExist().withTimeout(60000); - await expect(element(by.id('new-message-view-item-rocket.cat'))).toExist(); await element(by.id('new-message-view-item-rocket.cat')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(10000); - await expect(element(by.id('room-view'))).toExist(); await waitFor(element(by.id('room-view-title-rocket.cat'))).toExist().withTimeout(60000); - await expect(element(by.id('room-view-title-rocket.cat'))).toExist(); await tapBack(); await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); }); @@ -51,11 +53,8 @@ describe('Create room screen', () => { it('should navigate to select users', async() => { await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await expect(element(by.id('new-message-view'))).toExist(); - await sleep(1000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000); - await expect(element(by.id('select-users-view'))).toExist(); }); }) }); @@ -108,7 +107,6 @@ describe('Create room screen', () => { const room = `public${ data.random }`; await element(by.id('create-channel-name')).replaceText(room); await element(by.id('create-channel-type')).tap(); - await sleep(1000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); await expect(element(by.id('room-view'))).toExist(); @@ -123,20 +121,15 @@ describe('Create room screen', () => { it('should create private room', async() => { const room = `private${ data.random }`; await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); - // await device.launchApp({ newInstance: true }); - await sleep(1000); await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('select-users-view-item-rocket.cat')).tap(); await waitFor(element(by.id('selected-user-rocket.cat'))).toExist().withTimeout(5000); await element(by.id('selected-users-view-submit')).tap(); await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000); await element(by.id('create-channel-name')).replaceText(room); - await sleep(1000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); await expect(element(by.id('room-view'))).toExist(); @@ -152,17 +145,13 @@ describe('Create room screen', () => { const room = `empty${ data.random }`; await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); // await device.launchApp({ newInstance: true }); - await sleep(1000); await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('selected-users-view-submit')).tap(); await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000); await element(by.id('create-channel-name')).replaceText(room); - await sleep(1000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); await expect(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 86fdfc21f..00d5d434d 100644 --- a/e2e/tests/room/02-room.spec.js +++ b/e2e/tests/room/02-room.spec.js @@ -2,27 +2,29 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, starMessage, pinMessage, dismissReviewNag, tryTapping } = require('../../helpers/app'); -async function navigateToRoom() { - await searchRoom(`private${ data.random }`); - await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toExist().withTimeout(60000); - await element(by.id(`rooms-list-view-item-private${ data.random }`)).tap(); +async function navigateToRoom(roomName) { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + await searchRoom(`${ roomName }`); + await waitFor(element(by.id(`rooms-list-view-item-${ roomName }`))).toExist().withTimeout(60000); + await element(by.id(`rooms-list-view-item-${ roomName }`)).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000); } describe('Room screen', () => { - const mainRoom = `private${ data.random }`; + const mainRoom = data.groups.private.name; before(async() => { - await navigateToRoom(); + await navigateToRoom(mainRoom); }); describe('Render', async() => { 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); - await expect(element(by.id(`room-view-title-${ mainRoom }`))).toExist(); }); // Render - Header @@ -69,22 +71,15 @@ describe('Room screen', () => { await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toExist(); }); - it('should ask for review', async() => { - await waitFor(element(by.text('Are you enjoying this app?'))).toExist().withTimeout(60000); - await expect(element(by.text('Are you enjoying this app?')).atIndex(0)).toExist(); - await element(by.label('No').and(by.type('_UIAlertControllerActionView'))).tap(); // Tap `no` on ask for review alert - }) it('should show/hide emoji keyboard', async () => { if (device.getPlatform() === 'android') { await element(by.id('messagebox-open-emoji')).tap(); await waitFor(element(by.id('messagebox-keyboard-emoji'))).toExist().withTimeout(10000); - await expect(element(by.id('messagebox-keyboard-emoji'))).toExist(); await expect(element(by.id('messagebox-close-emoji'))).toExist(); await expect(element(by.id('messagebox-open-emoji'))).toBeNotVisible(); await element(by.id('messagebox-close-emoji')).tap(); await waitFor(element(by.id('messagebox-keyboard-emoji'))).toBeNotVisible().withTimeout(10000); - await expect(element(by.id('messagebox-keyboard-emoji'))).toBeNotVisible(); await expect(element(by.id('messagebox-close-emoji'))).toBeNotVisible(); await expect(element(by.id('messagebox-open-emoji'))).toExist(); } @@ -94,10 +89,8 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(':joy'); await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(10000); - await expect(element(by.id('messagebox-container'))).toExist(); await element(by.id('messagebox-input')).clearText(); await waitFor(element(by.id('messagebox-container'))).toBeNotVisible().withTimeout(10000); - await expect(element(by.id('messagebox-container'))).toBeNotVisible(); }); it('should show and tap on emoji autocomplete', async() => { @@ -105,8 +98,6 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).replaceText(':'); await element(by.id('messagebox-input')).typeText('joy'); // workaround for number keyboard await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(10000); - await expect(element(by.id('messagebox-container'))).toExist(); - await sleep(1000); await element(by.id('mention-item-joy')).tap(); await expect(element(by.id('messagebox-input'))).toHaveText(':joy: '); await element(by.id('messagebox-input')).clearText(); @@ -116,25 +107,22 @@ describe('Room screen', () => { const username = data.users.regular.username await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(`@${ username }`); - await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(60000); - await expect(element(by.id('messagebox-container'))).toExist(); - await sleep(1000); - await element(by.id(`mention-item-${ username }`)).tap(); + await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000); + await waitFor(element(by.id(`mention-item-${ username }`))).toBeVisible().withTimeout(4000) + await tryTapping(element(by.id(`mention-item-${ username }`)), 2000, true); await expect(element(by.id('messagebox-input'))).toHaveText(`@${ username } `); - await element(by.id('messagebox-input')).tap(); + 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); - await sleep(2000); }); 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(60000); - await expect(element(by.id('messagebox-container'))).toExist(); - await sleep(1000); - await element(by.id('mention-item-general')).tap(); + //await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000); + await waitFor(element(by.id('mention-item-general'))).toBeVisible().withTimeout(4000); + await tryTapping(element(by.id('mention-item-general')), 2000, true) await expect(element(by.id('messagebox-input'))).toHaveText('#general '); await element(by.id('messagebox-input')).clearText(); }); @@ -147,7 +135,6 @@ describe('Room screen', () => { 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')).tap(); - await sleep(1000); // TODO: test clipboard }); @@ -158,28 +145,20 @@ describe('Room screen', () => { 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')).tap(); - await sleep(1000); // TODO: test clipboard }); it('should star message', async() => { - await element(by.label(`${ data.random }message`)).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')).tap(); - await sleep(1000); - await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); + await starMessage('message') + await sleep(1000) //https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2324 await element(by.label(`${ data.random }message`)).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 waitFor(element(by.label('Unstar'))).toBeVisible().withTimeout(2000); - await expect(element(by.label('Unstar'))).toBeVisible(); await element(by.id('action-sheet-backdrop')).tap(); - await sleep(1000); }); it('should react to message', async() => { @@ -189,14 +168,10 @@ describe('Room screen', () => { await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.id('add-reaction')).tap(); await waitFor(element(by.id('reaction-picker'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('reaction-picker'))).toBeVisible(); await element(by.id('reaction-picker-😃')).tap(); await waitFor(element(by.id('reaction-picker-grinning'))).toExist().withTimeout(2000); - await expect(element(by.id('reaction-picker-grinning'))).toExist(); await element(by.id('reaction-picker-grinning')).tap(); await waitFor(element(by.id('message-reaction-:grinning:'))).toExist().withTimeout(60000); - await expect(element(by.id('message-reaction-:grinning:'))).toExist(); - await sleep(1000); }); it('should react to message with frequently used emoji', async() => { @@ -205,30 +180,27 @@ describe('Room screen', () => { 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'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('message-actions-emoji-+1'))).toBeVisible(); await element(by.id('message-actions-emoji-+1')).tap(); await waitFor(element(by.id('message-reaction-:+1:'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('message-reaction-:+1:'))).toBeVisible(); - await sleep(1000); }); it('should show reaction picker on add reaction button pressed and have frequently used emoji', async() => { await element(by.id('message-add-reaction')).tap(); await waitFor(element(by.id('reaction-picker'))).toExist().withTimeout(2000); - await expect(element(by.id('reaction-picker'))).toExist(); await waitFor(element(by.id('reaction-picker-grinning'))).toExist().withTimeout(2000); - await expect(element(by.id('reaction-picker-grinning'))).toExist(); await element(by.id('reaction-picker-😃')).tap(); await waitFor(element(by.id('reaction-picker-grimacing'))).toExist().withTimeout(2000); await element(by.id('reaction-picker-grimacing')).tap(); await waitFor(element(by.id('message-reaction-:grimacing:'))).toExist().withTimeout(60000); - await sleep(1000); }); + + 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:'))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id('message-reaction-:grinning:'))).toBeNotVisible(); }); it('should edit message', async() => { @@ -241,7 +213,6 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).typeText('ed'); await element(by.id('messagebox-send-message')).tap(); await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist().withTimeout(60000); - await expect(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist(); }); it('should quote message', async() => { @@ -253,46 +224,39 @@ describe('Room screen', () => { await element(by.label('Quote')).tap(); await element(by.id('messagebox-input')).typeText(`${ data.random }quoted`); await element(by.id('messagebox-send-message')).tap(); - await sleep(1000); // TODO: test if quote was sent }); it('should pin message', async() => { - await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist(); - await element(by.label(`${ data.random }edited (edited)`)).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')).tap(); - await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); - await sleep(1500); - - await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toBeVisible(); - await element(by.label(`${ data.random }edited (edited)`)).atIndex(0).longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await mockMessage('pin') + await pinMessage('pin') + + await waitFor(element(by.label(`${ data.random }pin`)).atIndex(0)).toBeVisible().withTimeout(2000); + await waitFor(element(by.label('Message pinned')).atIndex(0)).toBeVisible().withTimeout(2000); + await element(by.label(`${ data.random }pin`)).atIndex(0).longPress(); + await waitFor(element(by.id('action-sheet'))).toExist().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'))).toBeVisible().withTimeout(2000); - await expect(element(by.label('Unpin'))).toBeVisible(); await element(by.id('action-sheet-backdrop')).tap(); }); it('should delete message', async() => { - await waitFor(element(by.label(`${ data.random }quoted`)).atIndex(0)).toBeVisible(); - await element(by.label(`${ data.random }quoted`)).atIndex(0).longPress(); + await mockMessage('delete') + + await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toBeVisible(); + await element(by.label(`${ data.random }delete`)).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('Delete')).tap(); const deleteAlertMessage = 'You will not be able to recover this message!'; - await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text(deleteAlertMessage)).atIndex(0)).toExist(); + await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)).toExist().withTimeout(10000); await element(by.text('Delete')).tap(); - await sleep(1000); - await expect(element(by.label(`${ data.random }quoted`)).atIndex(0)).toNotExist(); + await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toNotExist().withTimeout(2000); }); }); @@ -317,7 +281,6 @@ describe('Room screen', () => { await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000); await expect(element(by.id(`room-view-title-${ thread }`))).toExist(); await tapBack(); - await sleep(1000); }); it('should toggle follow thread', async() => { @@ -332,10 +295,13 @@ describe('Room screen', () => { await waitFor(element(by.id('room-view-header-unfollow'))).toExist().withTimeout(60000); await expect(element(by.id('room-view-header-unfollow'))).toExist(); await tapBack(); - await sleep(1000); }); it('should navigate to thread from thread name', async() => { + await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ mainRoom }`)))).toBeVisible().withTimeout(2000); + await waitFor(element(by.id('room-view-header-actions').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 mockMessage('dummymessagebetweenthethread'); await element(by.label(thread)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); @@ -352,10 +318,10 @@ describe('Room screen', () => { await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000); await expect(element(by.id(`room-view-title-${ thread }`))).toExist(); await tapBack(); - await sleep(1000); }); it('should navigate to thread from threads view', async() => { + await waitFor(element(by.id('room-view-header-threads'))).toExist().withTimeout(1000); await element(by.id('room-view-header-threads')).tap(); await waitFor(element(by.id('thread-messages-view'))).toExist().withTimeout(5000); await expect(element(by.id('thread-messages-view'))).toExist(); diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js index dd5c8defd..bd5b7c6bc 100644 --- a/e2e/tests/room/03-roomactions.spec.js +++ b/e2e/tests/room/03-roomactions.spec.js @@ -2,7 +2,7 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, sleep, searchRoom, mockMessage, starMessage, pinMessage } = require('../../helpers/app'); const scrollDown = 200; @@ -11,13 +11,12 @@ async function navigateToRoomActions(type) { if (type === 'd') { room = 'rocket.cat'; } else { - room = `private${ data.random }`; + room = data.groups.private.name; } await searchRoom(room); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000); await element(by.id(`rooms-list-view-item-${ room }`)).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-view-header-actions')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); } @@ -25,7 +24,6 @@ async function navigateToRoomActions(type) { async function backToActions() { await tapBack(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); - await expect(element(by.id('room-actions-view'))).toExist(); } async function backToRoomsList() { @@ -36,10 +34,16 @@ async function backToRoomsList() { } describe('Room actions screen', () => { + + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + }); + describe('Render', async() => { describe('Direct', async() => { before(async() => { - await device.launchApp({ newInstance: true }); await navigateToRoomActions('d'); }); @@ -197,65 +201,89 @@ describe('Room actions screen', () => { 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 expect(element(by.id('mentioned-messages-view'))).toExist(); // await waitFor(element(by.text(` ${ data.random }mention`))).toExist().withTimeout(60000); - // await expect(element(by.text(` ${ data.random }mention`))).toExist(); await backToActions(); }); it('should show starred message and unstar it', async() => { + + //Go back to room and send a message + await tapBack(); + await mockMessage('messageToStar'); + + //Star the message + await starMessage('messageToStar') + + //Back into Room Actions + await element(by.id('room-view-header-actions')).tap(); + await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); + + //Go to starred messages await element(by.id('room-actions-starred')).tap(); await waitFor(element(by.id('starred-messages-view'))).toExist().withTimeout(2000); - await sleep(1000); - await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeVisible(); - await element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view'))).longPress(); - + await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000); + + //Unstar message + await element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view'))).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.label('Unstar')).tap(); - await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible(); + await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible().withTimeout(60000); await backToActions(); }); it('should show pinned message and unpin it', async() => { + + //Go back to room and send a message + await tapBack(); + await mockMessage('messageToPin'); + + //Pin the message + await pinMessage('messageToPin') + + //Back into Room Actions + await element(by.id('room-view-header-actions')).tap(); + await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); + await waitFor(element(by.id('room-actions-pinned'))).toExist(); await element(by.id('room-actions-pinned')).tap(); await waitFor(element(by.id('pinned-messages-view'))).toExist().withTimeout(2000); - await sleep(1000); - await waitFor(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeVisible(); - await element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view'))).longPress(); + await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000); + await element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view'))).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.label('Unpin')).tap(); - await waitFor(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible(); + await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000); 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-view-header-actions')).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 }message/`); - await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('search-messages-view')))).toExist().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('search-messages-view')))).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', async() => { it('should navigate to notification preference view', async() => { - await waitFor(element(by.id('room-actions-notifications'))).toExist(); - await expect(element(by.id('room-actions-notifications'))).toExist(); + await waitFor(element(by.id('room-actions-notifications'))).toExist().withTimeout(2000); await element(by.id('room-actions-notifications')).tap(); await waitFor(element(by.id('notification-preference-view'))).toExist().withTimeout(2000); - await expect(element(by.id('notification-preference-view'))).toExist(); }); it('should have receive notification option', async() => { @@ -271,30 +299,25 @@ describe('Room actions screen', () => { }); it('should have push notification option', async() => { - await waitFor(element(by.id('notification-preference-view-push-notification'))).toExist(); - await expect(element(by.id('notification-preference-view-push-notification'))).toExist(); + await waitFor(element(by.id('notification-preference-view-push-notification'))).toExist().withTimeout(4000); }); it('should have notification audio option', async() => { - await waitFor(element(by.id('notification-preference-view-audio'))).toExist(); - await expect(element(by.id('notification-preference-view-audio'))).toExist(); + await waitFor(element(by.id('notification-preference-view-audio'))).toExist().withTimeout(4000); }); it('should have notification sound option', async() => { // Ugly hack to scroll on detox await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom'); - await waitFor(element(by.id('notification-preference-view-sound'))).toExist(); - await expect(element(by.id('notification-preference-view-sound'))).toExist(); + await waitFor(element(by.id('notification-preference-view-sound'))).toExist().withTimeout(4000); }); it('should have notification duration option', async() => { - await waitFor(element(by.id('notification-preference-view-notification-duration'))).toExist(); - await expect(element(by.id('notification-preference-view-notification-duration'))).toExist(); + await waitFor(element(by.id('notification-preference-view-notification-duration'))).toExist().withTimeout(4000); }); it('should have email alert option', async() => { - await waitFor(element(by.id('notification-preference-view-email-alert'))).toExist(); - await expect(element(by.id('notification-preference-view-email-alert'))).toExist(); + await waitFor(element(by.id('notification-preference-view-email-alert'))).toExist().withTimeout(4000); }); after(async() => { @@ -309,34 +332,28 @@ describe('Room actions screen', () => { const user = data.users.alternate it('should tap on leave channel and raise alert', async() => { - await waitFor(element(by.id('room-actions-leave-channel'))).toExist(); - await expect(element(by.id('room-actions-leave-channel'))).toExist(); + await waitFor(element(by.id('room-actions-leave-channel'))).toExist().withTimeout(2000); await element(by.id('room-actions-leave-channel')).tap(); await waitFor(element(by.text('Yes, leave it!'))).toExist().withTimeout(2000); - await expect(element(by.text('Yes, leave it!'))).toExist(); 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.'))).toExist().withTimeout(60000); - await expect(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist(); + await waitFor(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist().withTimeout(8000); await element(by.text('OK')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); }); it('should add user to the room', async() => { - await waitFor(element(by.id('room-actions-add-user'))).toExist(); + await waitFor(element(by.id('room-actions-add-user'))).toExist().withTimeout(4000); await element(by.id('room-actions-add-user')).tap(); await element(by.id('select-users-view-search')).tap(); await element(by.id('select-users-view-search')).replaceText(user.username); - await waitFor(element(by.id(`select-users-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`select-users-view-item-${ user.username }`))).toExist(); + await waitFor(element(by.id(`select-users-view-item-${ user.username }`))).toExist().withTimeout(10000); await element(by.id(`select-users-view-item-${ user.username }`)).tap(); await waitFor(element(by.id(`selected-user-${ user.username }`))).toExist().withTimeout(5000); - await expect(element(by.id(`selected-user-${ user.username }`))).toExist(); await element(by.id('selected-users-view-submit')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); await element(by.id('room-actions-members')).tap(); await element(by.id('room-members-view-toggle-status')).tap(); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); await backToActions(1); }); @@ -344,26 +361,20 @@ describe('Room actions screen', () => { before(async() => { await element(by.id('room-actions-members')).tap(); await waitFor(element(by.id('room-members-view'))).toExist().withTimeout(2000); - await expect(element(by.id('room-members-view'))).toExist(); }); it('should show all users', async() => { - await sleep(1000); await element(by.id('room-members-view-toggle-status')).tap(); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); }); it('should filter user', async() => { await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); await element(by.id('room-members-view-search')).replaceText('rocket'); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toBeNotVisible(); await element(by.id('room-members-view-search')).tap(); await element(by.id('room-members-view-search')).clearText(''); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); }); // FIXME: mute/unmute isn't working @@ -391,9 +402,7 @@ describe('Room actions screen', () => { await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(5000); await element(by.id(`room-members-view-item-${ user.username }`)).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); - await expect(element(by.id('room-view'))).toExist(); await waitFor(element(by.id(`room-view-title-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-view-title-${ user.username }`))).toExist(); await tapBack(); await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); }); @@ -407,13 +416,10 @@ describe('Room actions screen', () => { it('should block/unblock user', async() => { await waitFor(element(by.id('room-actions-block-user'))).toExist(); - await sleep(1000); await element(by.id('room-actions-block-user')).tap(); await waitFor(element(by.label('Unblock user'))).toExist().withTimeout(60000); - await expect(element(by.label('Unblock user'))).toExist(); await element(by.id('room-actions-block-user')).tap(); await waitFor(element(by.label('Block user'))).toExist().withTimeout(60000); - await expect(element(by.label('Block user'))).toExist(); }); }); }); diff --git a/e2e/tests/room/04-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js index 35a45441b..9382f16cf 100644 --- a/e2e/tests/room/04-roominfo.spec.js +++ b/e2e/tests/room/04-roominfo.spec.js @@ -2,23 +2,23 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app'); + +const privateRoomName = data.groups.private.name async function navigateToRoomInfo(type) { let room; if (type === 'd') { room = 'rocket.cat'; } else { - room = `private${ data.random }`; + room = privateRoomName; } await searchRoom(room); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000); await element(by.id(`rooms-list-view-item-${ room }`)).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-view-header-actions')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); - await sleep(1000); await element(by.id('room-actions-info')).tap(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); } @@ -28,13 +28,19 @@ async function waitForToast() { // 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(5000); + await sleep(1); } describe('Room info screen', () => { + + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + }); + describe('Direct', async() => { before(async() => { - await device.launchApp({ newInstance: true }); await navigateToRoomInfo('d'); }); @@ -42,11 +48,16 @@ describe('Room info screen', () => { await expect(element(by.id('room-info-view'))).toExist(); await expect(element(by.id('room-info-view-name'))).toExist(); }); + + after(async() => { + await tapBack() + await tapBack() + await tapBack() + }) }); describe('Channel/Group', async() => { before(async() => { - await device.launchApp({ newInstance: true }); await navigateToRoomInfo('c'); }); @@ -78,7 +89,6 @@ describe('Room info screen', () => { describe('Render Edit', async() => { before(async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); @@ -141,7 +151,6 @@ describe('Room info screen', () => { }); describe('Usage', async() => { - const room = `private${ data.random }`; // 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'); @@ -155,22 +164,17 @@ describe('Room info screen', () => { // }); it('should change room name', async() => { - await element(by.id('room-info-edit-view-name')).replaceText(`${ room }new`); + await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }new`); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); - await sleep(5000); await tapBack(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); - await sleep(1000); - await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${ room }new`); + await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${ privateRoomName }new`); // change name to original await element(by.id('room-info-view-edit-button')).tap(); - await sleep(1000); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); - await element(by.id('room-info-edit-view-name')).replaceText(`${ room }`); + await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }`); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await element(by.type('UIScrollView')).atIndex(1).swipe('down'); @@ -184,14 +188,11 @@ describe('Room info screen', () => { await element(by.id('room-info-edit-view-password')).replaceText('abc'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-t')).tap(); - await sleep(1000); - await element(by.id('room-info-edit-view-ro')).tap(); - await sleep(1000); + 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 sleep(1000); await element(by.id('room-info-edit-view-reset')).tap(); // after reset - await expect(element(by.id('room-info-edit-view-name'))).toHaveText(room); + await expect(element(by.id('room-info-edit-view-name'))).toHaveText(privateRoomName); await expect(element(by.id('room-info-edit-view-description'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-topic'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-announcement'))).toHaveText(''); @@ -203,55 +204,45 @@ describe('Room info screen', () => { }); it('should change room description', async() => { - await sleep(1000); await element(by.id('room-info-edit-view-description')).replaceText('new description'); await element(by.type('UIScrollView')).atIndex(1).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 sleep(1000); await expect(element(by.label('new description').withAncestor(by.id('room-info-view-description')))).toExist(); }); it('should change room topic', async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-info-edit-view-topic')).replaceText('new topic'); await element(by.type('UIScrollView')).atIndex(1).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 sleep(1000); await expect(element(by.label('new topic').withAncestor(by.id('room-info-view-topic')))).toExist(); }); it('should change room announcement', async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement'); await element(by.type('UIScrollView')).atIndex(1).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 sleep(1000); await expect(element(by.label('new announcement').withAncestor(by.id('room-info-view-announcement')))).toExist(); }); it('should change room password', async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-password')).replaceText('password'); await element(by.id('room-info-edit-view-submit')).tap(); @@ -259,7 +250,6 @@ describe('Room info screen', () => { }); it('should change room type', async() => { - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-t')).tap(); await element(by.id('room-info-edit-view-submit')).tap(); @@ -282,14 +272,11 @@ describe('Room info screen', () => { // }); it('should archive room', async() => { - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-archive')).tap(); await waitFor(element(by.text('Yes, archive it!'))).toExist().withTimeout(5000); - await expect(element(by.text('Yes, archive it!'))).toExist(); await element(by.text('Yes, archive it!')).tap(); await waitFor(element(by.id('room-info-edit-view-unarchive'))).toExist().withTimeout(60000); - await expect(element(by.id('room-info-edit-view-unarchive'))).toExist(); 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(); @@ -301,16 +288,12 @@ describe('Room info screen', () => { }); it('should delete room', async() => { - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-delete')).tap(); await waitFor(element(by.text('Yes, delete it!'))).toExist().withTimeout(5000); - await expect(element(by.text('Yes, delete it!'))).toExist(); await element(by.text('Yes, delete it!')).tap(); await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000); - await sleep(2000); - await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible(); + await waitFor(element(by.id(`rooms-list-view-item-${ privateRoomName }`))).toBeNotVisible().withTimeout(60000); }); }); }); diff --git a/package.json b/package.json index 1f7dd4f17..d992a2728 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "babel-plugin-transform-remove-console": "^6.9.4", "babel-runtime": "^6.26.0", "bugsnag-sourcemaps": "1.3.0", - "codecov": "3.6.5", + "codecov": "3.7.1", "detox": "^16.9.0", "emotion-theming": "10.0.27", "eslint": "6.8.0", @@ -196,6 +196,20 @@ "type": "ios.simulator", "device": { "type": "iPhone 11 Pro" + }, + "artifacts": { + "plugins": { + "screenshot": { + "enabled": true, + "shouldTakeAutomaticSnapshots": true, + "keepOnlyFailedTestsArtifacts": true, + "takeWhen": { + "testStart": true, + "testDone": true, + "appNotReady": true + } + } + } } } } diff --git a/yarn.lock b/yarn.lock index 8ea560782..f9a717d55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5004,10 +5004,10 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codecov@3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.6.5.tgz#d73ce62e8a021f5249f54b073e6f2d6a513f172a" - integrity sha512-v48WuDMUug6JXwmmfsMzhCHRnhUf8O3duqXvltaYJKrO1OekZWpB/eH6iIoaxMl8Qli0+u3OxptdsBOYiD7VAQ== +codecov@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.7.1.tgz#434cb8d55f18ef01672e5739d3d266696bebc202" + integrity sha512-JHWxyPTkMLLJn9SmKJnwAnvY09kg2Os2+Ux+GG7LwZ9g8gzDDISpIN5wAsH1UBaafA/yGcd3KofMaorE8qd6Lw== dependencies: argv "0.0.2" ignore-walk "3.0.3" @@ -5121,8 +5121,8 @@ commondir@^1.0.1: integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= "commonmark-react-renderer@git+https://github.com/RocketChat/commonmark-react-renderer.git": - version "4.3.5" - resolved "git+https://github.com/RocketChat/commonmark-react-renderer.git#996553c27ab6457775ba1f4b6a5a6a20ce7ddfbc" + version "4.3.4" + resolved "git+https://github.com/RocketChat/commonmark-react-renderer.git#1264ac7b1c13d9be3e2f67eec6702a3132f4fac2" dependencies: lodash.assign "^4.2.0" lodash.isplainobject "^4.0.6" @@ -5130,14 +5130,14 @@ commondir@^1.0.1: xss-filters "^1.2.6" "commonmark@git+https://github.com/RocketChat/commonmark.js.git": - version "0.29.2" - resolved "git+https://github.com/RocketChat/commonmark.js.git#020b5af060459ac2010e382e9ae431d48ed39777" + version "0.29.0" + resolved "git+https://github.com/RocketChat/commonmark.js.git#5d293fe9ba83a3e6f842d5d3f41a9b57c35bea1f" dependencies: - entities "~2.0" - mdurl "~1.0.1" - minimist ">=1.2.2" + entities "~ 1.1.1" + mdurl "~ 1.0.1" + minimist "~ 1.2.0" string.prototype.repeat "^0.2.0" - xregexp "^4.3.0" + xregexp "4.1.1" compare-urls@^2.0.0: version "2.0.0" @@ -6127,7 +6127,7 @@ enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -entities@^1.1.1, entities@^1.1.2: +entities@^1.1.1, entities@^1.1.2, "entities@~ 1.1.1": version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -6137,16 +6137,16 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436" integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw== -entities@~2.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" - integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== - -envinfo@^7.1.0, envinfo@^7.5.0: +envinfo@^7.1.0: version "7.5.1" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.1.tgz#93c26897225a00457c75e734d354ea9106a72236" integrity sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ== +envinfo@^7.5.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.0.tgz#fbfa46d739dec0554ef40220cd91fb20f64c9698" + integrity sha512-XX0+kACx7HcIFhar/JjsDtDIVcC8hnzQO1Asehq+abs+v9MtzpUuujFb6eBTT4lF9j2Bh6d2XFngbFRryjUAeQ== + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -10348,7 +10348,7 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -mdurl@~1.0.1: +"mdurl@~ 1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= @@ -10892,7 +10892,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimist@>=1.2.2, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, "minimist@~ 1.2.0": version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -15957,7 +15957,12 @@ xpipe@^1.0.5: resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf" integrity sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98= -xregexp@^4.2.4, xregexp@^4.3.0: +xregexp@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.1.1.tgz#eb8a032aa028d403f7b1b22c47a5f16c24b21d8d" + integrity sha512-QJ1gfSUV7kEOLfpKFCjBJRnfPErUzkNKFMso4kDSmGpp3x6ZgkyKf74inxI7PnnQCFYq5TqYJCd7DrgDN8Q05A== + +xregexp@^4.2.4: version "4.3.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==