From 182ab69d6f31396b24054f0d12cd443f038e9c8c Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 23 May 2018 10:39:18 -0300 Subject: [PATCH] Detox tests E2E (#283) --- .circleci/config.yml | 51 +++ .eslintignore | 1 + README.md | 13 + app/ReactotronConfig.js | 3 +- app/containers/Button/index.js | 3 +- app/containers/CloseModalButton.js | 6 +- app/containers/EmojiPicker/EmojiCategory.js | 1 + app/containers/EmojiPicker/TabBar.js | 1 + app/containers/MessageActions.js | 1 + app/containers/MessageBox/EmojiKeyboard.js | 2 +- app/containers/MessageBox/Recording.js | 3 +- app/containers/MessageBox/index.js | 32 +- app/containers/Sidebar.js | 21 +- app/containers/TextInput.js | 29 +- app/containers/message/index.js | 4 +- app/lib/methods/canOpenRoom.js | 1 + .../helpers/mergeSubscriptionsRooms.js | 2 +- app/lib/methods/sendMessage.js | 10 +- app/lib/methods/subscriptions/rooms.js | 27 +- app/lib/rocketchat.js | 122 +++--- app/presentation/RoomItem.js | 6 +- app/sagas/login.js | 2 +- app/sagas/rooms.js | 16 +- app/views/CreateChannelView.js | 7 +- app/views/ForgotPasswordView.js | 4 +- app/views/ListServerView.js | 2 +- app/views/LoginSignupView.js | 4 +- app/views/LoginView.js | 17 +- app/views/MentionedMessagesView/index.js | 25 +- app/views/NewServerView.js | 4 +- app/views/PinnedMessagesView/index.js | 3 +- app/views/RegisterView.js | 18 +- app/views/RoomActionsView/index.js | 79 ++-- app/views/RoomFilesView/index.js | 25 +- app/views/RoomInfoEditView/SwitchContainer.js | 6 +- app/views/RoomInfoEditView/index.js | 24 +- app/views/RoomInfoView/index.js | 16 +- app/views/RoomInfoView/styles.js | 3 +- app/views/RoomMembersView/index.js | 4 + app/views/RoomView/Header/index.js | 17 +- app/views/RoomView/ListView.js | 1 + app/views/RoomView/ReactionPicker.js | 5 +- app/views/RoomView/index.js | 6 +- app/views/RoomsListView/Header/index.js | 24 +- app/views/RoomsListView/index.js | 5 +- app/views/SearchMessagesView/index.js | 2 + app/views/SelectedUsersView.js | 6 +- app/views/SnippetedMessagesView/index.js | 25 +- app/views/StarredMessagesView/index.js | 3 +- e2e/00-addserver.spec.js | 50 +++ e2e/01-welcome.spec.js | 46 ++ e2e/02-forgotpassword.spec.js | 45 ++ e2e/03-createuser.spec.js | 148 +++++++ e2e/04-login.spec.js | 92 ++++ e2e/05-roomslist.spec.js | 112 +++++ e2e/06-createroom.spec.js | 109 +++++ e2e/07-room.spec.js | 318 ++++++++++++++ e2e/08-roomactions.spec.js | 403 ++++++++++++++++++ e2e/09-roominfo.spec.js | 326 ++++++++++++++ e2e/10-changeserver.spec.js | 60 +++ e2e/data.js | 11 + e2e/helpers/app.js | 41 ++ e2e/helpers/random.js | 9 + e2e/helpers/screenshot.js | 23 + e2e/init.js | 11 + e2e/mocha.opts | 1 + package-lock.json | 178 ++++++++ package.json | 15 + 68 files changed, 2485 insertions(+), 205 deletions(-) create mode 100644 e2e/00-addserver.spec.js create mode 100644 e2e/01-welcome.spec.js create mode 100644 e2e/02-forgotpassword.spec.js create mode 100644 e2e/03-createuser.spec.js create mode 100644 e2e/04-login.spec.js create mode 100644 e2e/05-roomslist.spec.js create mode 100644 e2e/06-createroom.spec.js create mode 100644 e2e/07-room.spec.js create mode 100644 e2e/08-roomactions.spec.js create mode 100644 e2e/09-roominfo.spec.js create mode 100644 e2e/10-changeserver.spec.js create mode 100644 e2e/data.js create mode 100644 e2e/helpers/app.js create mode 100644 e2e/helpers/random.js create mode 100644 e2e/helpers/screenshot.js create mode 100644 e2e/init.js create mode 100644 e2e/mocha.opts diff --git a/.circleci/config.yml b/.circleci/config.yml index 94faa75cd..750a4726f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,6 +35,52 @@ jobs: command: | npx codecov + e2e-test: + macos: + xcode: "9.0" + + environment: + BASH_ENV: "~/.nvm/nvm.sh" + + steps: + - checkout + + - run: + name: Install Node 8 + command: | + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash + source ~/.nvm/nvm.sh + # https://github.com/creationix/nvm/issues/1394 + set +e + nvm install 8 + + - run: + name: Install appleSimUtils + command: | + brew update + brew tap wix/brew + brew install applesimutils + + - run: + name: Install NPM modules + command: | + rm -rf node_modules + npm install + npm install -g detox-cli + + - run: + name: Build + command: | + detox build + + - run: + name: Test + command: | + detox test + + - store_artifacts: + path: /tmp/screenshots + android-build: <<: *defaults docker: @@ -215,10 +261,14 @@ workflows: build-and-test: jobs: - lint-testunit + - e2e-test: + requires: + - lint-testunit - ios-build: requires: - lint-testunit + - e2e-test - ios-testflight: requires: - ios-build @@ -235,3 +285,4 @@ workflows: - android-build: requires: - lint-testunit + - e2e-test diff --git a/.eslintignore b/.eslintignore index 9bc674ea2..573933545 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ __tests__ node_modules coverage +e2e diff --git a/README.md b/README.md index e764f1a20..62d11f3b9 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,19 @@ Follow the [React Native Getting Started Guide](https://facebook.github.io/react $ npm run android ``` +# Detox (end-to-end tests) +- Build your app + +```bash +$ detox build +``` + +- Run tests + +```bash +$ detox test +``` + # Storybook - General requirements - Install storybook diff --git a/app/ReactotronConfig.js b/app/ReactotronConfig.js index 528ce16d6..a6d4b34f3 100644 --- a/app/ReactotronConfig.js +++ b/app/ReactotronConfig.js @@ -12,5 +12,6 @@ if (__DEV__) { .connect(); // Running on android device // $ adb reverse tcp:9090 tcp:9090 - console.warn = Reactotron.log; + // Reactotron.clear(); + // console.warn = Reactotron.log; } diff --git a/app/containers/Button/index.js b/app/containers/Button/index.js index 7477060d9..f72880b2c 100644 --- a/app/containers/Button/index.js +++ b/app/containers/Button/index.js @@ -60,7 +60,7 @@ export default class Button extends React.PureComponent { render() { const { - title, type, onPress, disabled + title, type, onPress, disabled, ...otherProps } = this.props; return ( this.props.navigation.dispatch(NavigationActions.back())} style={styles.button}> + this.props.navigation.dispatch(NavigationActions.back())} + style={styles.button} + testID='close-modal-button' + > this.props.onEmojiSelected(emoji)} + testID={`reaction-picker-${ emoji.isCustom ? emoji.content : emoji }`} > {renderEmoji(emoji, size)} ); diff --git a/app/containers/EmojiPicker/TabBar.js b/app/containers/EmojiPicker/TabBar.js index 4886006a6..9eeaf6e5c 100644 --- a/app/containers/EmojiPicker/TabBar.js +++ b/app/containers/EmojiPicker/TabBar.js @@ -20,6 +20,7 @@ export default class TabBar extends React.PureComponent { key={tab} onPress={() => this.props.goToPage(i)} style={styles.tab} + testID={`reaction-picker-${ tab }`} > {tab} {this.props.activeTab === i ? : } diff --git a/app/containers/MessageActions.js b/app/containers/MessageActions.js index 0a82d50ff..d126600df 100644 --- a/app/containers/MessageActions.js +++ b/app/containers/MessageActions.js @@ -337,6 +337,7 @@ export default class MessageActions extends React.Component { this.ActionSheet = o} title='Messages actions' + testID='message-actions' options={this.options} cancelButtonIndex={this.CANCEL_INDEX} destructiveButtonIndex={this.DELETE_INDEX} diff --git a/app/containers/MessageBox/EmojiKeyboard.js b/app/containers/MessageBox/EmojiKeyboard.js index 9a6cb8b91..ec60b34b8 100644 --- a/app/containers/MessageBox/EmojiKeyboard.js +++ b/app/containers/MessageBox/EmojiKeyboard.js @@ -13,7 +13,7 @@ export default class EmojiKeyboard extends React.PureComponent { render() { return ( - + this.onEmojiSelected(emoji)} /> diff --git a/app/containers/MessageBox/Recording.js b/app/containers/MessageBox/Recording.js index 10e32b898..a89dab115 100644 --- a/app/containers/MessageBox/Recording.js +++ b/app/containers/MessageBox/Recording.js @@ -100,7 +100,8 @@ export default class extends React.PureComponent { render() { return ( diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js index 90b2197f9..fcec6933c 100644 --- a/app/containers/MessageBox/index.js +++ b/app/containers/MessageBox/index.js @@ -88,7 +88,6 @@ export default class MessageBox extends React.PureComponent { const regexp = /(#|@|:)([a-z0-9._-]+)$/im; const result = lastNativeText.substr(0, cursor).match(regexp); - if (!result) { return this.stopTrackingMention(); } @@ -111,6 +110,7 @@ export default class MessageBox extends React.PureComponent { accessibilityLabel='Cancel editing' accessibilityTraits='button' onPress={() => this.editCancel()} + testID='messagebox-cancel-editing' />); } return !this.state.showEmojiKeyboard ? () : ( this.closeEmoji()} style={styles.actionButtons} accessibilityLabel='Close emoji selector' accessibilityTraits='button' name='keyboard' + testID='messagebox-close-emoji' />); } get rightButtons() { @@ -138,6 +140,7 @@ export default class MessageBox extends React.PureComponent { accessibilityLabel='Send message' accessibilityTraits='button' onPress={() => this.submit(this.state.text)} + testID='messagebox-send-message' />); return icons; } @@ -148,6 +151,7 @@ export default class MessageBox extends React.PureComponent { accessibilityLabel='Send audio message' accessibilityTraits='button' onPress={() => this.recordAudioMessage()} + testID='messagebox-send-audio' />); icons.push( this.addFile()} + testID='messagebox-actions' />); return icons; } @@ -438,6 +443,7 @@ export default class MessageBox extends React.PureComponent { this._onPressMention(item)} + testID={`mention-item-${ this.state.trackingType === MENTIONS_TRACKING_TYPE_EMOJIS ? item.name || item : item.username || item.name }`} > {this.state.trackingType === MENTIONS_TRACKING_TYPE_EMOJIS ? [ @@ -463,14 +469,15 @@ export default class MessageBox extends React.PureComponent { return null; } return ( - this.renderMentionItem(item)} - keyExtractor={item => item._id || item} - keyboardShouldPersistTaps='always' - /> + + this.renderMentionItem(item)} + keyExtractor={item => item._id || item} + keyboardShouldPersistTaps='always' + /> + ); }; @@ -481,7 +488,11 @@ export default class MessageBox extends React.PureComponent { return ( [ this.renderMentions(), - + {this.leftButtons} this.component = component} @@ -496,6 +507,7 @@ export default class MessageBox extends React.PureComponent { defaultValue='' multiline placeholderTextColor='#9EA2A8' + testID='messagebox-input' /> {this.rightButtons} diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js index 4ba019fcd..df34b7ff1 100644 --- a/app/containers/Sidebar.js +++ b/app/containers/Sidebar.js @@ -67,7 +67,7 @@ export default class Sidebar extends Component { onPressItem = (item) => { this.props.selectServer(item.id); - this.props.navigation.dispatch(DrawerActions.closeDrawer()); + this.closeDrawer(); } getState = () => ({ @@ -78,12 +78,17 @@ export default class Sidebar extends Component { this.setState(this.getState()); } + closeDrawer = () => { + this.props.navigation.dispatch(DrawerActions.closeDrawer()); + } + renderItem = ({ item, separators }) => ( { this.onPressItem(item); }} + testID={`sidebar-${ item.id }`} > @@ -96,14 +101,18 @@ export default class Sidebar extends Component { render() { return ( - + { this.props.logout(); }} + onPress={() => { + this.closeDrawer(); + this.props.logout(); + }} + testID='sidebar-logout' > @@ -112,7 +121,11 @@ export default class Sidebar extends Component { { this.props.navigation.navigate({ key: 'AddServer', routeName: 'AddServer' }); }} + onPress={() => { + this.closeDrawer(); + this.props.navigation.navigate({ key: 'AddServer', routeName: 'AddServer' }); + }} + testID='sidebar-add-server' > diff --git a/app/containers/TextInput.js b/app/containers/TextInput.js index 389f4b63e..885b93022 100644 --- a/app/containers/TextInput.js +++ b/app/containers/TextInput.js @@ -75,22 +75,37 @@ export default class RCTextInput extends React.PureComponent { showPassword: false } - icon = ({ name, onPress, style }) => + icon = ({ + name, + onPress, + style, + testID + }) => - iconLeft = name => this.icon({ name, onPress: null, style: { left: 0 } }); + iconLeft = name => this.icon({ + name, + onPress: null, + style: { left: 0 }, + testID: this.props.testID ? `${ this.props.testID }-icon-left` : null + }); - iconPassword = name => this.icon({ name, onPress: () => this.tooglePassword(), style: { right: 0 } }); + iconPassword = name => this.icon({ + name, + onPress: () => this.tooglePassword(), + style: { right: 0 }, + testID: this.props.testID ? `${ this.props.testID }-icon-right` : null + }); tooglePassword = () => this.setState({ showPassword: !this.state.showPassword }); render() { const { - label, error, secureTextEntry, containerStyle, inputRef, iconLeft, inputStyle, ...inputProps + label, error, secureTextEntry, containerStyle, inputRef, iconLeft, inputStyle, testID, placeholder, ...inputProps } = this.props; const { showPassword } = this.state; return ( - { label && {label} } + {label && {label} } {iconLeft && this.iconLeft(iconLeft)} diff --git a/app/containers/message/index.js b/app/containers/message/index.js index 12c9f08e7..4254a4cfd 100644 --- a/app/containers/message/index.js +++ b/app/containers/message/index.js @@ -270,6 +270,7 @@ export default class Message extends React.Component { onPress={() => this.onReactionPress(reaction.emoji)} onLongPress={() => this.onReactionLongPress()} key={reaction.emoji} + testID={`message-reaction-${ reaction.emoji }`} > this.props.toggleReactionPicker(this.parseMessage())} - key='add-reaction' + key='message-add-reaction' + testID='message-add-reaction' style={styles.reactionContainer} > diff --git a/app/lib/methods/canOpenRoom.js b/app/lib/methods/canOpenRoom.js index f336b1adc..30dc0809f 100644 --- a/app/lib/methods/canOpenRoom.js +++ b/app/lib/methods/canOpenRoom.js @@ -40,6 +40,7 @@ async function canOpenRoomDDP(...args) { export default async function canOpenRoom({ rid, path }) { const { database: db } = database; + const room = db.objects('subscriptions').filtered('rid == $0', rid); if (room.length) { return true; diff --git a/app/lib/methods/helpers/mergeSubscriptionsRooms.js b/app/lib/methods/helpers/mergeSubscriptionsRooms.js index 15cc768f1..4289fe5de 100644 --- a/app/lib/methods/helpers/mergeSubscriptionsRooms.js +++ b/app/lib/methods/helpers/mergeSubscriptionsRooms.js @@ -15,7 +15,7 @@ export const merge = (subscription, room) => { subscription.joinCodeRequired = room.joinCodeRequired; if (room.muted && room.muted.length) { - subscription.muted = room.muted.filter(role => role).map(role => ({ value: role })); + subscription.muted = room.muted.filter(user => user).map(user => ({ value: user })); } } if (subscription.roles && subscription.roles.length) { diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js index 4cd194692..9a01c065a 100644 --- a/app/lib/methods/sendMessage.js +++ b/app/lib/methods/sendMessage.js @@ -21,9 +21,13 @@ export const getMessage = (rid, msg = {}) => { username: reduxStore.getState().login.user.username } }; - database.write(() => { - database.create('messages', message, true); - }); + try { + database.write(() => { + database.create('messages', message, true); + }); + } catch (error) { + console.warn('getMessage', error); + } return message; }; diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js index db98e9ac6..279eb1c4f 100644 --- a/app/lib/methods/subscriptions/rooms.js +++ b/app/lib/methods/subscriptions/rooms.js @@ -1,6 +1,8 @@ +import Random from 'react-native-meteor/lib/Random'; import database from '../../realm'; import { merge } from '../helpers/mergeSubscriptionsRooms'; import protectedFunction from '../helpers/protectedFunction'; +import messagesStatus from '../../../constants/messagesStatus'; import log from '../../../utils/log'; export default async function subscribeRooms(id) { @@ -26,7 +28,9 @@ export default async function subscribeRooms(id) { }, 5000); }; - if (this.ddp) { + if (!this.ddp && this._login) { + loop(); + } else { this.ddp.on('logged', () => { clearTimeout(timer); timer = false; @@ -48,7 +52,7 @@ export default async function subscribeRooms(id) { const [, ev] = ddpMessage.fields.eventName.split('/'); if (/subscriptions/.test(ev)) { const tpm = merge(data); - return database.write(() => { + database.write(() => { database.create('subscriptions', tpm, true); }); } @@ -58,6 +62,25 @@ export default async function subscribeRooms(id) { merge(sub, data); }); } + if (/message/.test(ev)) { + const [args] = ddpMessage.fields.args; + const _id = Random.id(); + const message = { + _id, + rid: args.rid, + msg: args.msg, + ts: new Date(), + _updatedAt: new Date(), + status: messagesStatus.SENT, + u: { + _id, + username: 'rocket.cat' + } + }; + requestAnimationFrame(() => database.write(() => { + database.create('messages', message, true); + })); + } })); } diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index a40a85972..8605c747c 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -1,7 +1,6 @@ import { AsyncStorage, Platform } from 'react-native'; import { hashPassword } from 'react-native-meteor/lib/utils'; import foreach from 'lodash/forEach'; -import Random from 'react-native-meteor/lib/Random'; import RNFetchBlob from 'react-native-fetch-blob'; import reduxStore from './createStore'; @@ -23,8 +22,6 @@ import { someoneTyping, roomMessageReceived } from '../actions/room'; import { setRoles } from '../actions/roles'; import Ddp from './ddp'; -import normalizeMessage from './methods/helpers/normalizeMessage'; - import subscribeRooms from './methods/subscriptions/rooms'; import subscribeRoom from './methods/subscriptions/room'; @@ -167,9 +164,11 @@ const RocketChat = { this.ddp.on('disconnected', protectedFunction(() => { reduxStore.dispatch(disconnect()); + console.warn(this.ddp); })); this.ddp.on('stream-room-messages', (ddpMessage) => { + // TODO: debounce const message = _buildMessage(ddpMessage.fields.args[0]); requestAnimationFrame(() => reduxStore.dispatch(roomMessageReceived(message))); }); @@ -182,65 +181,66 @@ const RocketChat = { return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); })); - this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => { - const [type, data] = ddpMessage.fields.args; - const [, ev] = ddpMessage.fields.eventName.split('/'); - if (/subscriptions/.test(ev)) { - if (data.roles) { - data.roles = data.roles.map(role => ({ value: role })); - } - if (data.blocker) { - data.blocked = true; - } else { - data.blocked = false; - } - if (data.mobilePushNotifications === 'nothing') { - data.notifications = true; - } else { - data.notifications = false; - } - database.write(() => { - database.create('subscriptions', data, true); - }); - } - if (/rooms/.test(ev) && type === 'updated') { - const sub = database.objects('subscriptions').filtered('rid == $0', data._id)[0]; + // this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => { + // console.warn('rc.stream-notify-user') + // const [type, data] = ddpMessage.fields.args; + // const [, ev] = ddpMessage.fields.eventName.split('/'); + // if (/subscriptions/.test(ev)) { + // if (data.roles) { + // data.roles = data.roles.map(role => ({ value: role })); + // } + // if (data.blocker) { + // data.blocked = true; + // } else { + // data.blocked = false; + // } + // if (data.mobilePushNotifications === 'nothing') { + // data.notifications = true; + // } else { + // data.notifications = false; + // } + // database.write(() => { + // database.create('subscriptions', data, true); + // }); + // } + // if (/rooms/.test(ev) && type === 'updated') { + // const sub = database.objects('subscriptions').filtered('rid == $0', data._id)[0]; - database.write(() => { - sub.roomUpdatedAt = data._updatedAt; - sub.lastMessage = normalizeMessage(data.lastMessage); - sub.ro = data.ro; - sub.description = data.description; - sub.topic = data.topic; - sub.announcement = data.announcement; - sub.reactWhenReadOnly = data.reactWhenReadOnly; - sub.archived = data.archived; - sub.joinCodeRequired = data.joinCodeRequired; - if (data.muted) { - sub.muted = data.muted.map(m => ({ value: m })); - } - }); - } - if (/message/.test(ev)) { - const [args] = ddpMessage.fields.args; - const _id = Random.id(); - const message = { - _id, - rid: args.rid, - msg: args.msg, - ts: new Date(), - _updatedAt: new Date(), - status: messagesStatus.SENT, - u: { - _id, - username: 'rocket.cat' - } - }; - requestAnimationFrame(() => database.write(() => { - database.create('messages', message, true); - })); - } - })); + // database.write(() => { + // sub.roomUpdatedAt = data._updatedAt; + // sub.lastMessage = normalizeMessage(data.lastMessage); + // sub.ro = data.ro; + // sub.description = data.description; + // sub.topic = data.topic; + // sub.announcement = data.announcement; + // sub.reactWhenReadOnly = data.reactWhenReadOnly; + // sub.archived = data.archived; + // sub.joinCodeRequired = data.joinCodeRequired; + // if (data.muted) { + // sub.muted = data.muted.map(m => ({ value: m })); + // } + // }); + // } + // if (/message/.test(ev)) { + // const [args] = ddpMessage.fields.args; + // const _id = Random.id(); + // const message = { + // _id, + // rid: args.rid, + // msg: args.msg, + // ts: new Date(), + // _updatedAt: new Date(), + // status: messagesStatus.SENT, + // u: { + // _id, + // username: 'rocket.cat' + // } + // }; + // requestAnimationFrame(() => database.write(() => { + // database.create('messages', message, true); + // })); + // } + // })); this.ddp.on('rocketchat_starred_message', protectedFunction((ddpMessage) => { if (ddpMessage.msg === 'added') { diff --git a/app/presentation/RoomItem.js b/app/presentation/RoomItem.js index 7674d95de..01ddac4a7 100644 --- a/app/presentation/RoomItem.js +++ b/app/presentation/RoomItem.js @@ -171,7 +171,8 @@ export default class RoomItem extends React.Component { onLongPress: PropTypes.func, user: PropTypes.object, avatarSize: PropTypes.number, - statusStyle: ViewPropTypes.style + statusStyle: ViewPropTypes.style, + testID: PropTypes.string } static defaultProps = { @@ -238,7 +239,7 @@ export default class RoomItem extends React.Component { render() { const { - favorite, unread, userMentions, name, _updatedAt, alert, type + favorite, unread, userMentions, name, _updatedAt, alert, type, testID } = this.props; const date = this.formatDate(_updatedAt); @@ -266,6 +267,7 @@ export default class RoomItem extends React.Component { activeOpacity={0.5} accessibilityLabel={accessibilityLabel} accessibilityTraits='selected' + testID={testID} > {this.icon} diff --git a/app/sagas/login.js b/app/sagas/login.js index cd7abe7ee..92602eb41 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -164,7 +164,7 @@ const watchLoginOpen = function* watchLoginOpen() { } const sub = yield RocketChat.subscribe('meteor.loginServiceConfiguration'); yield take(types.LOGIN.CLOSE); - sub.unsubscribe(); + yield sub.unsubscribe().catch(err => console.warn(err)); } catch (e) { log('watchLoginOpen', e); } diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 61a46a792..c3808310f 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -140,12 +140,16 @@ const updateLastOpen = function* updateLastOpen() { const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) { NavigationService.goRoomsList(); yield delay(1000); - database.write(() => { - const messages = database.objects('messages').filtered('rid = $0', rid); - database.delete(messages); - const subscription = database.objects('subscriptions').filtered('rid = $0', rid); - database.delete(subscription); - }); + try { + database.write(() => { + const messages = database.objects('messages').filtered('rid = $0', rid); + database.delete(messages); + const subscription = database.objects('subscriptions').filtered('rid = $0', rid); + database.delete(subscription); + }); + } catch (error) { + console.warn('goRoomsListAndDelete', error); + } }; const handleLeaveRoom = function* handleLeaveRoom({ rid }) { diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 8b8e350bc..35253d467 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -62,7 +62,7 @@ export default class CreateChannelView extends LoggedView { } return ( - + {this.props.createChannel.error.reason} ); @@ -75,6 +75,7 @@ export default class CreateChannelView extends LoggedView { style={[{ flexGrow: 0, flexShrink: 1 }]} value={this.state.type} onValueChange={type => this.setState({ type })} + testID='create-channel-type' /> {this.state.type ? 'Public' : 'Private'} @@ -90,7 +91,7 @@ export default class CreateChannelView extends LoggedView { keyboardVerticalOffset={128} > - + {this.renderChannelNameError()} {this.renderTypeSwitch()} @@ -126,6 +128,7 @@ export default class CreateChannelView extends LoggedView { ? styles.disabledButton : styles.enabledButton ]} + testID='create-channel-submit' > CREATE diff --git a/app/views/ForgotPasswordView.js b/app/views/ForgotPasswordView.js index f5f639b19..b702ea258 100644 --- a/app/views/ForgotPasswordView.js +++ b/app/views/ForgotPasswordView.js @@ -80,7 +80,7 @@ export default class ForgotPasswordView extends LoggedView { keyboardVerticalOffset={128} > - + this.validate(email)} onSubmitEditing={() => this.resetPassword()} + testID='forgot-password-view-email' /> @@ -98,6 +99,7 @@ export default class ForgotPasswordView extends LoggedView { title='Reset password' type='primary' onPress={this.resetPassword} + testID='forgot-password-view-submit' /> diff --git a/app/views/ListServerView.js b/app/views/ListServerView.js index 6eaa5b3d4..8dd905838 100644 --- a/app/views/ListServerView.js +++ b/app/views/ListServerView.js @@ -209,7 +209,7 @@ class ListServerView extends LoggedView { render() { return ( - + - + this.props.navigation.navigate({ key: 'Login', routeName: 'Login' })} + testID='welcome-view-login' />