MM-30286 Detox/E2E: Add e2e test for MM-T3236 and added basic unit tests (#4969)

* MM-30286 Detox/E2E: Add e2e test for MM-T3236

* Fix more testID hierarchies

* Fix typo

* Fix failing test

* Remove extra lines

* Updated to use string interpolation

* Update channels list element query

* Updated channel item unit test to be more consistent

* Fix error text testID hierarchies; fix edit channel info testIDs

* Fix snap file

* Fix line return
This commit is contained in:
Joseph Baylon 2020-11-24 00:58:09 -08:00 committed by GitHub
parent dcaaaee44c
commit 5f6fd6df7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
127 changed files with 2074 additions and 334 deletions

View file

@ -30,6 +30,7 @@ exports[`EditChannelInfo should match snapshot 1`] = `
"flex": 1,
}
}
testID="edit_channel_info"
>
<TouchableWithoutFeedback
onPress={[Function]}
@ -87,7 +88,7 @@ exports[`EditChannelInfo should match snapshot 1`] = `
"paddingHorizontal": 15,
}
}
testID="edit_channel.name.input"
testID="edit_channel_info.name.input"
underlineColorAndroid="transparent"
value="display_name"
/>
@ -161,7 +162,7 @@ exports[`EditChannelInfo should match snapshot 1`] = `
},
]
}
testID="edit_channel.purpose.input"
testID="edit_channel_info.purpose.input"
textAlignVertical="top"
underlineColorAndroid="transparent"
value="purpose"
@ -251,7 +252,7 @@ exports[`EditChannelInfo should match snapshot 1`] = `
},
]
}
testID="edit_channel.header.input"
testID="edit_channel_info.header.input"
textAlignVertical="top"
underlineColorAndroid="transparent"
value="header"

View file

@ -11,6 +11,7 @@ import EditChannelInfo from './index';
describe('EditChannelInfo', () => {
const baseProps = {
testID: 'edit_channel_info',
theme: Preferences.THEMES.default,
deviceWidth: 400,
deviceHeight: 600,

View file

@ -228,7 +228,10 @@ export default class EditChannelInfo extends PureComponent {
style={style.errorContainer}
>
<View style={style.errorWrapper}>
<ErrorText error={error}/>
<ErrorText
testID='edit_channel_info.error.text'
error={error}
/>
</View>
</SafeAreaView>
);
@ -263,7 +266,7 @@ export default class EditChannelInfo extends PureComponent {
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
testID='edit_channel.name.input'
testID='edit_channel_info.name.input'
ref={this.nameInput}
value={displayName}
onChangeText={this.onDisplayNameChangeText}
@ -296,7 +299,7 @@ export default class EditChannelInfo extends PureComponent {
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
testID='edit_channel.purpose.input'
testID='edit_channel_info.purpose.input'
ref={this.purposeInput}
value={purpose}
onChangeText={this.onPurposeChangeText}
@ -339,7 +342,7 @@ export default class EditChannelInfo extends PureComponent {
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
testID={'edit_channel.header.input'}
testID='edit_channel_info.header.input'
ref={this.headerInput}
value={header}
onChangeText={this.onHeaderChangeText}

View file

@ -33,6 +33,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
"paddingVertical": 5,
}
}
testID="emoji_picker"
>
<Search
autoCapitalize="none"
@ -66,6 +67,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="emoji_picker.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.8)"
titleCancelColor="#3d3c40"

View file

@ -12,8 +12,9 @@ import EmojiPickerBase, {getStyleSheetFromTheme} from './emoji_picker_base';
export default class EmojiPicker extends EmojiPickerBase {
render() {
const {formatMessage} = this.context.intl;
const {theme} = this.props;
const {testID, theme} = this.props;
const {searchTerm} = this.state;
const searchBarTestID = `${testID}.search_bar`;
const styles = getStyleSheetFromTheme(theme);
const searchBarInput = {
@ -24,8 +25,12 @@ export default class EmojiPicker extends EmojiPickerBase {
return (
<React.Fragment>
<View style={styles.searchBar}>
<View
testID={testID}
style={styles.searchBar}
>
<SearchBar
testID={searchBarTestID}
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -18,8 +18,9 @@ import EmojiPickerBase, {getStyleSheetFromTheme, SCROLLVIEW_NATIVE_ID} from './e
export default class EmojiPicker extends EmojiPickerBase {
render() {
const {formatMessage} = this.context.intl;
const {isLandscape, theme} = this.props;
const {testID, isLandscape, theme} = this.props;
const {searchTerm} = this.state;
const searchBarTestID = `${testID}.search_bar`;
const styles = getStyleSheetFromTheme(theme);
const shorten = DeviceTypes.IS_IPHONE_WITH_INSETS && isLandscape ? 6 : 2;
@ -46,8 +47,12 @@ export default class EmojiPicker extends EmojiPickerBase {
keyboardVerticalOffset={keyboardOffset}
style={styles.flex}
>
<View style={styles.searchBar}>
<View
testID={testID}
style={styles.searchBar}
>
<SearchBar
testID={searchBarTestID}
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -34,6 +34,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
const fuse = new Fuse(emojis, options);
const baseProps = {
testID: 'emoji_picker',
actions: {
getCustomEmojis: jest.fn(),
incrementEmojiPickerPage: jest.fn(),

View file

@ -41,6 +41,7 @@ export function filterEmojiSearchInput(searchText) {
export default class EmojiPicker extends PureComponent {
static propTypes = {
testID: PropTypes.string,
customEmojisEnabled: PropTypes.bool.isRequired,
customEmojiPage: PropTypes.number.isRequired,
deviceWidth: PropTypes.number.isRequired,

View file

@ -12,13 +12,14 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
export default class ErrorText extends PureComponent {
static propTypes = {
testID: PropTypes.string,
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
textStyle: CustomPropTypes.Style,
theme: PropTypes.object.isRequired,
};
render() {
const {error, textStyle, theme} = this.props;
const {testID, error, textStyle, theme} = this.props;
if (!error) {
return null;
}
@ -29,7 +30,7 @@ export default class ErrorText extends PureComponent {
if (intl) {
return (
<FormattedText
testID='error.text'
testID={testID}
id={intl.id}
defaultMessage={intl.defaultMessage}
values={intl.values}
@ -40,7 +41,7 @@ export default class ErrorText extends PureComponent {
return (
<Text
testID='error.text'
testID={testID}
style={[GlobalStyles.errorLabel, style.errorLabel, textStyle]}
>
{error.message || error}

View file

@ -9,6 +9,7 @@ import ErrorText from './error_text.js';
describe('ErrorText', () => {
const baseProps = {
testID: 'error.text',
textStyle: {
fontSize: 14,
marginHorizontal: 15,

View file

@ -384,6 +384,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"height": 44,
}
}
testID="post_draft.quick_actions"
>
<View
accessible={true}
@ -403,6 +404,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.quick_actions.at_input_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -428,6 +430,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.quick_actions.slash_input_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -453,7 +456,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.file_quick_action"
testID="post_draft.quick_actions.file_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -479,7 +482,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.image_quick_action"
testID="post_draft.quick_actions.image_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -505,7 +508,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.camera_quick_action"
testID="post_draft.quick_actions.camera_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -521,7 +524,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"paddingRight": 8,
}
}
testID="post_draft.send.button.disabled"
testID="post_draft.send_action.send.button.disabled"
>
<View
style={

View file

@ -0,0 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PostDraft Archived should match snapshot 1`] = `
<Connect(Archived)
deactivated={false}
intl={
Object {
"defaultFormats": Object {},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": "span",
"timeZone": null,
}
}
reactReduxForwardedRef={null}
rootId="root-id"
selectPenultimateChannel={[MockFunction]}
teamId="team-id"
testID="post_draft.archived"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
`;

View file

@ -14,6 +14,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
export default class Archived extends PureComponent {
static propTypes = {
testID: PropTypes.string,
deactivated: PropTypes.bool,
rootId: PropTypes.string,
selectPenultimateChannel: PropTypes.func.isRequired,
@ -46,12 +47,12 @@ export default class Archived extends PureComponent {
};
render() {
const {theme} = this.props;
const {testID, theme} = this.props;
const style = getStyleSheet(theme);
return (
<View
testID='post_draft.archived'
testID={testID}
style={style.archivedWrapper}
>
<FormattedMarkdownText

View file

@ -0,0 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import Preferences from '@mm-redux/constants/preferences';
import {shallowWithIntl} from 'test/intl-test-helper';
import Archived from './index';
describe('PostDraft Archived', () => {
const baseProps = {
selectPenultimateChannel: jest.fn(),
testID: 'post_draft.archived',
deactivated: false,
rootId: 'root-id',
teamId: 'team-id',
theme: Preferences.THEMES.default,
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<Archived {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -409,6 +409,9 @@ export default class DraftInput extends PureComponent {
rootId,
theme,
} = this.props;
const postInputTestID = `${testID}.post.input`;
const quickActionsTestID = `${testID}.quick_actions`;
const sendActionTestID = `${testID}.send_action`;
const style = getStyleSheet(theme);
return (
@ -435,6 +438,7 @@ export default class DraftInput extends PureComponent {
disableScrollViewPanResponder={true}
>
<PostInput
testID={postInputTestID}
channelDisplayName={channelDisplayName}
channelId={channelId}
cursorPositionEvent={cursorPositionEvent}
@ -455,6 +459,7 @@ export default class DraftInput extends PureComponent {
/>
<View style={style.actionsContainer}>
<QuickActions
testID={quickActionsTestID}
ref={this.quickActions}
fileCount={files.length}
inputEventType={valueEvent}
@ -462,6 +467,7 @@ export default class DraftInput extends PureComponent {
theme={theme}
/>
<SendAction
testID={sendActionTestID}
disabled={!this.state.canSubmit}
handleSendMessage={this.handleSendMessage}
theme={theme}

View file

@ -15,6 +15,7 @@ import ReadOnly from './read_only';
export default class PostDraft extends PureComponent {
static propTypes = {
testID: PropTypes.string,
accessoriesContainerID: PropTypes.string,
canPost: PropTypes.bool.isRequired,
channelId: PropTypes.string.isRequired,
@ -55,6 +56,7 @@ export default class PostDraft extends PureComponent {
render = () => {
const {
testID,
accessoriesContainerID,
canPost,
channelId,
@ -71,8 +73,11 @@ export default class PostDraft extends PureComponent {
} = this.props;
if (channelIsArchived || deactivatedChannel) {
const archivedTestID = `${testID}.archived`;
return (
<Archived
testID={archivedTestID}
defactivated={deactivatedChannel}
rootId={rootId}
theme={theme}
@ -83,14 +88,19 @@ export default class PostDraft extends PureComponent {
const readonly = channelIsReadOnly || !canPost;
if (readonly) {
const readOnlyTestID = `${testID}.read_only`;
return (
<ReadOnly theme={theme}/>
<ReadOnly
testID={readOnlyTestID}
theme={theme}
/>
);
}
const draftInput = (
<DraftInput
testID='post_draft'
testID={testID}
ref={this.draftInput}
channelId={channelId}
cursorPositionEvent={cursorPositionEvent}

View file

@ -43,6 +43,7 @@ const store = mockStore(state);
describe('PostDraft', () => {
const baseProps = {
testID: 'post_draft',
canPost: true,
channelId: 'channel-id',
channelIsArchived: false,

View file

@ -27,6 +27,7 @@ export default class PostInput extends PureComponent {
};
static propTypes = {
testID: PropTypes.string,
channelDisplayName: PropTypes.string,
channelId: PropTypes.string.isRequired,
cursorPositionEvent: PropTypes.string,
@ -267,7 +268,7 @@ export default class PostInput extends PureComponent {
render() {
const {formatMessage} = this.context.intl;
const {channelDisplayName, isLandscape, theme} = this.props;
const {testID, channelDisplayName, isLandscape, theme} = this.props;
const style = getStyleSheet(theme);
const placeholder = this.getPlaceHolder();
let maxHeight = 150;
@ -278,7 +279,7 @@ export default class PostInput extends PureComponent {
return (
<PasteableTextInput
testID='post_draft.post.input'
testID={testID}
ref={this.input}
style={{...style.input, maxHeight}}
onChangeText={this.handleTextChange}

View file

@ -11,6 +11,7 @@ import PostInput from './post_input';
describe('PostInput', () => {
const baseProps = {
testID: 'post_draft.post.input',
channelDisplayName: 'Test Channel',
channelId: 'channel-id',
cursorPositionEvent: '',

View file

@ -0,0 +1,195 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QuickActions should match snapshot 1`] = `
<View
style={
Object {
"display": "flex",
"flexDirection": "row",
"height": 44,
}
}
testID="post_draft.quick_actions"
>
<InputQuickAction
disabled={false}
inputType="at"
onTextChange={[Function]}
testID="post_draft.quick_actions.at_input_action"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
value=""
/>
<InputQuickAction
disabled={false}
inputType="slash"
onTextChange={[Function]}
testID="post_draft.quick_actions.slash_input_action"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
value=""
/>
<FileQuickAction
disabled={false}
fileCount={1}
maxFileCount={5}
onUploadFiles={[Function]}
testID="post_draft.quick_actions.file_action"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ImageQuickAction
disabled={false}
fileCount={1}
maxFileCount={5}
onUploadFiles={[Function]}
testID="post_draft.quick_actions.image_action"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<CameraQuickAction
disabled={false}
fileCount={1}
maxFileCount={5}
onUploadFiles={[Function]}
testID="post_draft.quick_actions.camera_action"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
</View>
`;

View file

@ -10,7 +10,7 @@ exports[`CameraButton should match snapshot 1`] = `
"padding": 10,
}
}
testID="post_draft.camera_quick_action"
testID="post_draft.quick_actions.camera_action"
type="opacity"
>
<CompassIcon

View file

@ -18,6 +18,7 @@ jest.mock('react-native-image-picker', () => ({
describe('CameraButton', () => {
const formatMessage = jest.fn();
const baseProps = {
testID: 'post_draft.quick_actions.camera_action',
fileCount: 0,
maxFileCount: 5,
onShowFileMaxWarning: jest.fn(),

View file

@ -22,6 +22,7 @@ import {changeOpacity} from '@utils/theme';
export default class CameraQuickAction extends PureComponent {
static propTypes = {
testID: PropTypes.string,
disabled: PropTypes.bool,
fileCount: PropTypes.number,
maxFileCount: PropTypes.number,
@ -147,17 +148,17 @@ export default class CameraQuickAction extends PureComponent {
};
render() {
const {disabled, theme} = this.props;
const testID = disabled ?
'post_draft.camera_quick_action.disabled' :
'post_draft.camera_quick_action';
const {testID, disabled, theme} = this.props;
const actionTestID = disabled ?
`${testID}.disabled` :
testID;
const color = disabled ?
changeOpacity(theme.centerChannelColor, 0.16) :
changeOpacity(theme.centerChannelColor, 0.64);
return (
<TouchableWithFeedback
testID={testID}
testID={actionTestID}
disabled={disabled}
onPress={this.handleButtonPress}
style={style.icon}

View file

@ -10,7 +10,7 @@ exports[`FileQuickAction should match snapshot 1`] = `
"padding": 10,
}
}
testID="post_draft.file_quick_action"
testID="post_draft.quick_actions.file_action"
type="opacity"
>
<CompassIcon

View file

@ -15,6 +15,7 @@ jest.mock('react-intl');
describe('FileQuickAction', () => {
const formatMessage = jest.fn();
const baseProps = {
testID: 'post_draft.quick_actions.file_action',
fileCount: 0,
maxFileCount: 5,
onShowFileMaxWarning: jest.fn(),

View file

@ -20,6 +20,7 @@ const ShareExtension = NativeModules.MattermostShare;
export default class FileQuickAction extends PureComponent {
static propTypes = {
testID: PropTypes.string,
disabled: PropTypes.bool,
fileCount: PropTypes.number,
maxFileCount: PropTypes.number,
@ -139,17 +140,17 @@ export default class FileQuickAction extends PureComponent {
};
render() {
const {disabled, theme} = this.props;
const testID = disabled ?
'post_draft.file_quick_action.disabled' :
'post_draft.file_quick_action';
const {testID, disabled, theme} = this.props;
const actionTestID = disabled ?
`${testID}.disabled` :
testID;
const color = disabled ?
changeOpacity(theme.centerChannelColor, 0.16) :
changeOpacity(theme.centerChannelColor, 0.64);
return (
<TouchableWithFeedback
testID={testID}
testID={actionTestID}
disabled={disabled}
onPress={this.handleButtonPress}
style={style.icon}

View file

@ -10,7 +10,7 @@ exports[`ImageQuickAction should match snapshot 1`] = `
"padding": 10,
}
}
testID="post_draft.image_quick_action"
testID="post_draft.quick_actions.image_action"
type="opacity"
>
<CompassIcon

View file

@ -18,6 +18,7 @@ jest.mock('react-native-image-picker', () => ({
describe('ImageQuickAction', () => {
const formatMessage = jest.fn();
const baseProps = {
testID: 'post_draft.quick_actions.image_action',
fileCount: 0,
maxFileCount: 5,
onShowFileMaxWarning: jest.fn(),

View file

@ -17,6 +17,7 @@ import {changeOpacity} from '@utils/theme';
export default class ImageQuickAction extends PureComponent {
static propTypes = {
testID: PropTypes.string,
disabled: PropTypes.bool,
fileCount: PropTypes.number,
maxFileCount: PropTypes.number,
@ -154,17 +155,17 @@ export default class ImageQuickAction extends PureComponent {
};
render() {
const {disabled, theme} = this.props;
const testID = disabled ?
'post_draft.image_quick_action.disabled' :
'post_draft.image_quick_action';
const {testID, disabled, theme} = this.props;
const actionTestID = disabled ?
`${testID}.disabled` :
testID;
const color = disabled ?
changeOpacity(theme.centerChannelColor, 0.16) :
changeOpacity(theme.centerChannelColor, 0.64);
return (
<TouchableWithFeedback
testID={testID}
testID={actionTestID}
disabled={disabled}
onPress={this.handleButtonPress}
style={style.icon}

View file

@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`InputQuickAction should match snapshot 1`] = `
<TouchableWithFeedbackIOS
disabled={false}
onPress={[Function]}
style={
Object {
"alignItems": "center",
"justifyContent": "center",
"padding": 10,
}
}
testID="post_draft.quick_actions.input_action"
type="opacity"
>
<CompassIcon
color="rgba(61,60,64,0.64)"
name="at"
size={24}
/>
</TouchableWithFeedbackIOS>
`;

View file

@ -11,6 +11,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
export default class InputQuickAction extends PureComponent {
static propTypes = {
testID: PropTypes.string,
disabled: PropTypes.bool,
inputType: PropTypes.oneOf(['at', 'slash']).isRequired,
onTextChange: PropTypes.func.isRequired,
@ -56,11 +57,15 @@ export default class InputQuickAction extends PureComponent {
}
render() {
const {disabled, theme} = this.props;
const {testID, disabled, theme} = this.props;
const actionTestID = disabled ?
`${testID}.disabled` :
testID;
const style = getStyleSheet(theme);
return (
<TouchableWithFeedback
testID={actionTestID}
disabled={disabled}
onPress={this.onPress}
style={style.icon}

View file

@ -0,0 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import InputQuickAction from './index';
describe('InputQuickAction', () => {
const baseProps = {
onTextChange: jest.fn(),
testID: 'post_draft.quick_actions.input_action',
disabled: false,
inputType: 'at',
theme: Preferences.THEMES.default,
value: 'value',
};
test('should match snapshot', () => {
const wrapper = shallow(<InputQuickAction {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -15,6 +15,7 @@ import InputAction from './input_quick_action';
export default class QuickActions extends PureComponent {
static propTypes = {
testID: PropTypes.string,
canUploadFiles: PropTypes.bool,
fileCount: PropTypes.number,
inputEventType: PropTypes.string.isRequired,
@ -64,10 +65,16 @@ export default class QuickActions extends PureComponent {
render() {
const {
testID,
canUploadFiles,
fileCount,
theme,
} = this.props;
const atInputActionTestID = `${testID}.at_input_action`;
const slashInputActionTestID = `${testID}.slash_input_action`;
const fileActionTestID = `${testID}.file_action`;
const imageActionTestID = `${testID}.image_action`;
const cameraActionTestID = `${testID}.camera_action`;
const uploadProps = {
disabled: !canUploadFiles,
fileCount,
@ -77,8 +84,12 @@ export default class QuickActions extends PureComponent {
};
return (
<View style={style.quickActionsContainer}>
<View
testID={testID}
style={style.quickActionsContainer}
>
<InputAction
testID={atInputActionTestID}
disabled={this.state.atDisabled}
inputType='at'
onTextChange={this.handleOnTextChange}
@ -86,14 +97,24 @@ export default class QuickActions extends PureComponent {
value={this.state.inputValue}
/>
<InputAction
testID={slashInputActionTestID}
disabled={this.state.slashDisabled}
inputType='slash'
onTextChange={this.handleOnTextChange}
theme={theme}
/>
<FileAction {...uploadProps}/>
<ImageAction {...uploadProps}/>
<CameraAction {...uploadProps}/>
<FileAction
testID={fileActionTestID}
{...uploadProps}
/>
<ImageAction
testID={imageActionTestID}
{...uploadProps}
/>
<CameraAction
testID={cameraActionTestID}
{...uploadProps}
/>
</View>
);
}

View file

@ -0,0 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import QuickActions from './quick_actions';
describe('QuickActions', () => {
const baseProps = {
onTextChange: jest.fn(),
testID: 'post_draft.quick_actions',
canUploadFiles: true,
fileCount: 1,
inputEventType: 'input-event-type',
maxFileSize: 10,
theme: Preferences.THEMES.default,
};
test('should match snapshot', () => {
const wrapper = shallow(<QuickActions {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PostDraft ReadOnly Should match snapshot 1`] = `
exports[`PostDraft ReadOnly should match snapshot 1`] = `
<RNCSafeAreaView
edges={
Array [

View file

@ -11,10 +11,11 @@ import type {Theme} from '@mm-redux/types/preferences';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
interface ReadOnlyProps {
testID?: string;
theme: Theme;
}
const ReadOnlyChannnel = ({theme}: ReadOnlyProps): ReactNode => {
const ReadOnlyChannnel = ({testID, theme}: ReadOnlyProps): ReactNode => {
const style = getStyle(theme);
return (
<SafeAreaView
@ -22,8 +23,8 @@ const ReadOnlyChannnel = ({theme}: ReadOnlyProps): ReactNode => {
style={style.background}
>
<View
testID={testID}
style={style.container}
testID='post_draft.read_only'
>
<CompassIcon
name='glasses'

View file

@ -9,13 +9,14 @@ import {shallowWithIntl} from 'test/intl-test-helper';
import ReadOnly from './index';
describe('PostDraft ReadOnly', () => {
test('Should match snapshot', () => {
const wrapper = shallowWithIntl(
<ReadOnly
theme={Preferences.THEMES.default}
/>,
);
const baseProps = {
testID: 'post_draft.read_only',
theme: Preferences.THEMES.default,
};
expect(wrapper).toMatchSnapshot();
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<ReadOnly {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -8,7 +8,7 @@ exports[`SendAction should change theme backgroundColor to 0.3 opacity 1`] = `
"paddingRight": 8,
}
}
testID="post_draft.send.button.disabled"
testID="post_draft.send_action.send.button.disabled"
>
<View
style={
@ -45,7 +45,7 @@ exports[`SendAction should match snapshot 1`] = `
"paddingRight": 8,
}
}
testID="post_draft.send.button"
testID="post_draft.send_action.send.button"
type="opacity"
>
<View
@ -78,7 +78,7 @@ exports[`SendAction should render theme backgroundColor 1`] = `
"paddingRight": 8,
}
}
testID="post_draft.send.button"
testID="post_draft.send_action.send.button"
type="opacity"
>
<View

View file

@ -10,13 +10,15 @@ import TouchableWithFeedback from '@components/touchable_with_feedback';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
function SendButton(props) {
const {theme} = props;
const {testID, theme} = props;
const sendButtonTestID = `${testID}.send.button`;
const sendButtonDisabledTestID = `${testID}.send.button.disabled`;
const style = getStyleSheet(theme);
if (props.disabled) {
return (
<View
testID='post_draft.send.button.disabled'
testID={sendButtonDisabledTestID}
style={style.sendButtonContainer}
>
<View style={[style.sendButton, style.disableButton]}>
@ -32,7 +34,7 @@ function SendButton(props) {
return (
<TouchableWithFeedback
testID='post_draft.send.button'
testID={sendButtonTestID}
onPress={props.handleSendMessage}
style={style.sendButtonContainer}
type={'opacity'}
@ -49,6 +51,7 @@ function SendButton(props) {
}
SendButton.propTypes = {
testID: PropTypes.string,
handleSendMessage: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,

View file

@ -11,6 +11,7 @@ import SendAction from './index';
describe('SendAction', () => {
const baseProps = {
testID: 'post_draft.send_action',
theme: Preferences.THEMES.default,
handleSendMessage: jest.fn(),
disabled: false,

View file

@ -515,6 +515,7 @@ exports[`PostHeader should match snapshot when post isBot and shouldRenderReplyB
"justifyContent": "flex-end",
}
}
testID="post_header.reply"
>
<TouchableWithFeedbackIOS
onPress={[MockFunction]}
@ -667,6 +668,7 @@ exports[`PostHeader should match snapshot when post isBot and shouldRenderReplyB
"justifyContent": "flex-end",
}
}
testID="post_header.reply"
>
<TouchableWithFeedbackIOS
onPress={[MockFunction]}
@ -866,6 +868,7 @@ exports[`PostHeader should match snapshot when post should display reply button
"justifyContent": "flex-end",
}
}
testID="post_header.reply"
>
<TouchableWithFeedbackIOS
onPress={[MockFunction]}

View file

@ -227,7 +227,10 @@ export default class PostHeader extends PureComponent {
}
return (
<View style={style.replyWrapper}>
<View
testID='post_header.reply'
style={style.replyWrapper}
>
<TouchableWithFeedback
onPress={onPress}
style={style.replyIconContainer}

View file

@ -39,6 +39,7 @@ const SCROLL_POSITION_CONFIG = {
export default class PostList extends PureComponent {
static propTypes = {
testID: PropTypes.string,
actions: PropTypes.shape({
closePermalink: PropTypes.func.isRequired,
handleSelectChannelByName: PropTypes.func.isRequired,
@ -288,6 +289,7 @@ export default class PostList extends PureComponent {
renderItem = ({item, index}) => {
const {
testID,
highlightPinnedOrFlagged,
highlightPostId,
isSearchResult,
@ -360,6 +362,7 @@ export default class PostList extends PureComponent {
const postId = item;
return (
<MemoizedPost
testID={testID}
postId={postId}
highlight={highlightPostId === postId}
isLastPost={lastPostIndex === index}
@ -545,12 +548,12 @@ export default class PostList extends PureComponent {
}
}
function PostComponent({postId, highlightPostId, lastPostIndex, index, ...postProps}) {
const testID = `post_list.post.${postId}`;
function PostComponent({testID, postId, highlightPostId, lastPostIndex, index, ...postProps}) {
const postTestID = `${testID}.post.${postId}`;
return (
<Post
testID={testID}
testID={postTestID}
postId={postId}
highlight={highlightPostId === postId}
isLastPost={lastPostIndex === index}
@ -560,6 +563,7 @@ function PostComponent({postId, highlightPostId, lastPostIndex, index, ...postPr
}
PostComponent.propTypes = {
testID: PropTypes.string,
postId: PropTypes.string.isRequired,
highlightPostId: PropTypes.string,
lastPostIndex: PropTypes.number,

View file

@ -0,0 +1,136 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SearchBar should match snapshot 1`] = `
<View
style={
Array [
Object {
"alignItems": "center",
"flexDirection": "row",
"height": 20,
"justifyContent": "flex-start",
"overflow": "hidden",
},
Object {},
]
}
testID="search_bar"
>
<ForwardRef(AnimatedComponentWrapper)
style={
Array [
Object {
"flex": 1,
},
Object {
"height": 10,
"marginRight": 5,
},
Object {
"marginLeft": 0,
},
]
}
>
<ForwardRef(Themed.SearchBar)
autoCapitalize="auto-capitalize"
autoCorrect={false}
autoFocus={true}
blurOnSubmit={true}
cancelButtonProps={
Object {
"buttonTextStyle": Object {
"color": "#aaaaaa",
"fontSize": 14,
},
}
}
cancelButtonTitle="cancel title"
cancelIcon={null}
clearIcon={
<CompassIcon
name="close-circle"
onPress={[Function]}
size={18}
style={
Object {
"color": "#cccccc",
}
}
testID="search_bar.search.clear.button"
/>
}
containerStyle={
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"paddingBottom": 0,
"paddingTop": 0,
}
}
disableFullscreenUI={true}
editable={true}
enablesReturnKeyAutomatically={true}
inputContainerStyle={
Object {
"backgroundColor": undefined,
"borderRadius": 2,
"height": 10,
}
}
inputStyle={
Object {
"backgroundColor": "transparent",
"color": "#fff",
"fontSize": 14,
"height": 10,
"marginLeft": 4,
"marginTop": 0,
"paddingTop": 0,
}
}
keyboardAppearance="keyboard-appearance"
keyboardType="keyboard-type"
leftIconContainerStyle={
Object {
"marginLeft": 4,
"width": 30,
}
}
onBlur={[Function]}
onCancel={[Function]}
onChangeText={[Function]}
onClear={[Function]}
onFocus={[Function]}
onSelectionChange={[Function]}
onSubmitEditing={[Function]}
placeholder="placeholder"
placeholderTextColor="#000000"
platform="ios"
returnKeyType="return-key-type"
searchIcon={
<CompassIcon
name="magnify"
size={24}
style={
Array [
Object {
"flex": 1,
},
Object {
"color": "#bbbbbb",
"top": 8,
},
]
}
/>
}
selectionColor="#dddddd"
showCancel={true}
testID="search_bar.search.input"
underlineColorAndroid="transparent"
value="value"
/>
</ForwardRef(AnimatedComponentWrapper)>
</View>
`;

View file

@ -25,6 +25,7 @@ const LEFT_COMPONENT_INITIAL_POSITION = Platform.OS === 'ios' ? 7 : 0;
export default class Search extends PureComponent {
static propTypes = {
testID: PropTypes.string,
onBlur: PropTypes.func,
onFocus: PropTypes.func,
onSearchButtonPress: PropTypes.func,
@ -214,7 +215,10 @@ export default class Search extends PureComponent {
}
render() {
const {backgroundColor, inputHeight, inputStyle, placeholderTextColor, tintColorSearch, cancelButtonStyle, tintColorDelete, titleCancelColor, searchBarRightMargin, containerHeight} = this.props;
const {testID, backgroundColor, inputHeight, inputStyle, placeholderTextColor, tintColorSearch, cancelButtonStyle, tintColorDelete, titleCancelColor, searchBarRightMargin, containerHeight} = this.props;
const searchClearButtonTestID = `${testID}.search.clear.button`;
const searchBackButtonTestID = `${testID}.search.back.button`;
const searchInputTestID = `${testID}.search.input`;
const searchBarStyle = getSearchBarStyle(
backgroundColor,
cancelButtonStyle,
@ -236,6 +240,7 @@ export default class Search extends PureComponent {
if (Platform.OS === 'ios') {
clearIcon = (
<CompassIcon
testID={searchClearButtonTestID}
name='close-circle'
size={18}
style={{color: searchBarStyle.clearIconColorIos}}
@ -258,6 +263,7 @@ export default class Search extends PureComponent {
(
<TouchableWithoutFeedback onPress={this.onCancel}>
<CompassIcon
testID={searchBackButtonTestID}
name='arrow-left'
size={this.props.backArrowSize}
color={searchBarStyle.clearIconColorAndroid}
@ -284,6 +290,7 @@ export default class Search extends PureComponent {
clearIcon = (
<CompassIcon
testID={searchClearButtonTestID}
name='close'
size={this.props.deleteIconSize}
color={searchBarStyle.clearIconColorAndroid}
@ -293,7 +300,10 @@ export default class Search extends PureComponent {
}
return (
<View style={[searchBarStyle.container, this.props.containerStyle]}>
<View
testID={testID}
style={[searchBarStyle.container, this.props.containerStyle]}
>
{((this.props.leftComponent) ?
<Animated.View
style={[styles.leftComponent, {
@ -315,6 +325,7 @@ export default class Search extends PureComponent {
]}
>
<SearchBar
testID={searchInputTestID}
autoCapitalize={this.props.autoCapitalize}
autoCorrect={false}
autoFocus={this.props.autoFocus}

View file

@ -0,0 +1,56 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallowWithIntl} from 'test/intl-test-helper';
import SearchBar from './index';
jest.useFakeTimers();
describe('SearchBar', () => {
const baseProps = {
onBlur: jest.fn,
onFocus: jest.fn,
onSearchButtonPress: jest.fn,
onChangeText: jest.fn,
onCancelButtonPress: jest.fn,
onSelectionChange: jest.fn,
testID: 'search_bar',
backgroundColor: '#ffffff',
placeholderTextColor: '#000000',
titleCancelColor: '#aaaaaa',
tintColorSearch: '#bbbbbb',
tintColorDelete: '#cccccc',
selectionColor: '#dddddd',
inputStyle: {},
containerStyle: {},
cancelButtonStyle: {},
autoFocus: true,
placeholder: 'placeholder',
cancelTitle: 'cancel title',
returnKeyType: 'return-key-type',
keyboardType: 'keyboard-type',
autoCapitalize: 'auto-capitalize',
inputHeight: 10,
editable: true,
blurOnSubmit: true,
keyboardShouldPersist: true,
value: 'value',
keyboardAppearance: 'keyboard-appearance',
showArrow: true,
searchBarRightMargin: 5,
searchIconSize: 1,
backArrowSize: 1,
deleteIconSize: 1,
showCancel: true,
containerHeight: 20,
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<SearchBar {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -16,6 +16,7 @@ exports[`ChannelItem should match snapshot 1`] = `
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -93,7 +94,7 @@ exports[`ChannelItem should match snapshot 1`] = `
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>
@ -118,6 +119,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -205,7 +207,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
{displayname} (you)
</Text>
@ -230,6 +232,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -317,7 +320,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
{displayname} (you)
</Text>
@ -342,6 +345,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -429,7 +433,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>
@ -454,6 +458,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -531,7 +536,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>
@ -556,6 +561,7 @@ exports[`ChannelItem should match snapshot for deactivated user and not searchRe
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -633,7 +639,7 @@ exports[`ChannelItem should match snapshot for deactivated user and not searchRe
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>
@ -658,6 +664,7 @@ exports[`ChannelItem should match snapshot for isManualUnread 1`] = `
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -735,7 +742,7 @@ exports[`ChannelItem should match snapshot for isManualUnread 1`] = `
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>
@ -764,6 +771,7 @@ exports[`ChannelItem should match snapshot with draft 1`] = `
undefined,
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -841,7 +849,7 @@ exports[`ChannelItem should match snapshot with draft 1`] = `
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>
@ -868,6 +876,7 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
},
]
}
testID="main.sidebar.channels_list.list.channel_item"
>
<View
style={
@ -945,7 +954,7 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
},
]
}
testID="channel_item.display_name"
testID="main.sidebar.channels_list.list.channel_item.display_name"
>
display_name
</Text>

View file

@ -18,6 +18,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
export default class ChannelItem extends PureComponent {
static propTypes = {
testID: PropTypes.string,
channelId: PropTypes.string.isRequired,
channel: PropTypes.object,
currentChannelId: PropTypes.string.isRequired,
@ -61,6 +62,7 @@ export default class ChannelItem extends PureComponent {
render() {
const {
testID,
channelId,
currentChannelId,
displayName,
@ -165,12 +167,17 @@ export default class ChannelItem extends PureComponent {
/>
);
const displayNameTestID = `${testID}.display_name`;
return (
<TouchableHighlight
underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
onPress={this.onPress}
>
<View style={[style.container, mutedStyle]}>
<View
testID={testID}
style={[style.container, mutedStyle]}
>
{extraBorder}
<View style={[style.item, extraItemStyle]}>
{icon}
@ -178,7 +185,7 @@ export default class ChannelItem extends PureComponent {
style={[style.text, extraTextStyle]}
ellipsizeMode='tail'
numberOfLines={1}
testID='channel_item.display_name'
testID={displayNameTestID}
>
{channelDisplayName}
</Text>

View file

@ -7,7 +7,7 @@ import {TouchableHighlight} from 'react-native';
import Preferences from '@mm-redux/constants/preferences';
import ChannelItem from './channel_item.js';
import ChannelItem from './channel_item';
jest.useFakeTimers();
jest.mock('react-intl');
@ -22,6 +22,7 @@ describe('ChannelItem', () => {
};
const baseProps = {
testID: 'main.sidebar.channels_list.list.channel_item',
channelId: 'channel_id',
channel,
currentChannelId: 'current_channel_id',

View file

@ -27,6 +27,7 @@ let FilteredList = null;
export default class ChannelsList extends PureComponent {
static propTypes = {
testID: PropTypes.string,
onJoinChannel: PropTypes.func.isRequired,
onSearchEnds: PropTypes.func.isRequired,
onSearchStart: PropTypes.func.isRequired,
@ -92,15 +93,18 @@ export default class ChannelsList extends PureComponent {
render() {
const {intl} = this.context;
const {onShowTeams, theme} = this.props;
const {testID, onShowTeams, theme} = this.props;
const {searching, term} = this.state;
const styles = getStyleSheet(theme);
const filteredListTestID = `${testID}.filtered_list`;
const listTestID = `${testID}.list`;
const searchBarTestID = `${testID}.search_bar`;
let list;
if (searching) {
list = (
<FilteredList
testID={filteredListTestID}
onSelectChannel={this.onSelectChannel}
styles={styles}
term={term}
@ -109,6 +113,7 @@ export default class ChannelsList extends PureComponent {
} else {
list = (
<List
testID={listTestID}
onSelectChannel={this.onSelectChannel}
styles={styles}
/>
@ -128,6 +133,7 @@ export default class ChannelsList extends PureComponent {
style={styles.searchContainer}
>
<SearchBar
testID={searchBarTestID}
ref={this.setSearchBarRef}
placeholder={intl.formatMessage({id: 'mobile.channel_drawer.search', defaultMessage: 'Jump to...'})}
cancelTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
@ -154,9 +160,9 @@ export default class ChannelsList extends PureComponent {
return (
<SafeAreaView
testID={testID}
edges={['left']}
style={styles.container}
testID='channels.list'
>
<View style={styles.headerContainer}>
{title}

View file

@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ChannelsList FilteredList should match snapshot 1`] = `
<FilteredList
actions={
Object {
"getProfilesInTeam": [MockFunction],
"makeGroupMessageVisibleIfNecessary": [MockFunction],
"searchChannels": [MockFunction],
"searchProfiles": [MockFunction],
}
}
archivedChannels={Array []}
channels={Object {}}
currentChannel={Object {}}
currentTeam={Object {}}
currentUserId="current-user-id"
groupChannelMemberDetails={Object {}}
intl={
Object {
"defaultFormats": Object {},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": "span",
"timeZone": null,
}
}
isLandscape={false}
onSelectChannel={[MockFunction]}
otherChannels={Array []}
pastDirectMessages={Array []}
profiles={Object {}}
restrictDms={false}
searchOrder={Array []}
statuses={Object {}}
styles={Object {}}
teamProfiles={Object {}}
teammateNameDisplay="teammate-name-display"
term="term"
testID="main.sidebar.channels_list.filtered_list"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
`;

View file

@ -24,6 +24,7 @@ const VIEWABILITY_CONFIG = ListTypes.VISIBILITY_CONFIG_DEFAULTS;
class FilteredList extends Component {
static propTypes = {
testID: PropTypes.string,
actions: PropTypes.shape({
getProfilesInTeam: PropTypes.func.isRequired,
makeGroupMessageVisibleIfNecessary: PropTypes.func.isRequired,
@ -339,8 +340,12 @@ class FilteredList extends Component {
keyExtractor = (item) => item.id || item;
renderItem = ({item}) => {
const {testID} = this.props;
const channelItemTestID = `${testID}.channel_item`;
return (
<ChannelItem
testID={channelItemTestID}
channelId={item.id}
channel={item}
isSearchResult={true}
@ -371,10 +376,11 @@ class FilteredList extends Component {
};
render() {
const {styles} = this.props;
const {testID, styles} = this.props;
const {dataSource} = this.state;
return (
<View
testID={testID}
style={styles.container}
>
<SectionList

View file

@ -0,0 +1,46 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallowWithIntl} from 'test/intl-test-helper';
import Preferences from '@mm-redux/constants/preferences';
import FilteredList from './filtered_list';
describe('ChannelsList FilteredList', () => {
const baseProps = {
actions: {
getProfilesInTeam: jest.fn(),
makeGroupMessageVisibleIfNecessary: jest.fn(),
searchChannels: jest.fn(),
searchProfiles: jest.fn(),
},
onSelectChannel: jest.fn(),
testID: 'main.sidebar.channels_list.filtered_list',
channels: {},
currentTeam: {},
currentUserId: 'current-user-id',
currentChannel: {},
groupChannelMemberDetails: {},
teammateNameDisplay: 'teammate-name-display',
otherChannels: [],
archivedChannels: [],
profiles: {},
teamProfiles: {},
searchOrder: [],
pastDirectMessages: [],
restrictDms: false,
statuses: {},
styles: {},
term: 'term',
theme: Preferences.THEMES.default,
isLandscape: false,
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<FilteredList {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ChannelsList List should match snapshot 1`] = `
<View
onLayout={[Function]}
testID="main.sidebar.channels_list.list"
>
<SectionList
contentContainerStyle={
Object {
"paddingBottom": 44,
}
}
data={Array []}
disableVirtualization={false}
horizontal={false}
initialNumToRender={10}
keyExtractor={[Function]}
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="always"
maxToRenderPerBatch={10}
onEndReachedThreshold={2}
onScrollBeginDrag={[Function]}
onViewableItemsChanged={[Function]}
renderItem={[Function]}
renderSectionHeader={[Function]}
scrollEventThrottle={50}
sections={Array []}
stickySectionHeadersEnabled={true}
updateCellsBatchingPeriod={50}
viewabilityConfig={
Object {
"itemVisiblePercentThreshold": 100,
"waitForInteraction": true,
}
}
windowSize={21}
/>
</View>
`;

View file

@ -38,6 +38,7 @@ let UnreadIndicator = null;
export default class List extends PureComponent {
static propTypes = {
testID: PropTypes.string,
canJoinPublicChannels: PropTypes.bool.isRequired,
canCreatePrivateChannels: PropTypes.bool.isRequired,
canCreatePublicChannels: PropTypes.bool.isRequired,
@ -311,10 +312,12 @@ export default class List extends PureComponent {
};
renderItem = ({item}) => {
const {favoriteChannelIds, unreadChannelIds} = this.props;
const {testID, favoriteChannelIds, unreadChannelIds} = this.props;
const channelItemTestID = `${testID}.channel_item`;
return (
<ChannelItem
testID={channelItemTestID}
channelId={item}
isUnread={unreadChannelIds.includes(item)}
isFavorite={favoriteChannelIds.includes(item)}
@ -390,13 +393,14 @@ export default class List extends PureComponent {
};
render() {
const {styles, theme} = this.props;
const {testID, styles, theme} = this.props;
const {sections, showIndicator} = this.state;
const paddingBottom = this.listContentPadding();
return (
<View
testID={testID}
style={styles.container}
onLayout={this.onLayout}
>

View file

@ -0,0 +1,31 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import List from './list';
describe('ChannelsList List', () => {
const baseProps = {
onSelectChannel: jest.fn(),
testID: 'main.sidebar.channels_list.list',
canJoinPublicChannels: true,
canCreatePrivateChannels: true,
canCreatePublicChannels: true,
favoriteChannelIds: [],
unreadChannelIds: [],
styles: {},
theme: Preferences.THEMES.default,
orderedChannelIds: [],
isLandscape: false,
};
test('should match snapshot', () => {
const wrapper = shallow(<List {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -230,6 +230,7 @@ export default class MainSidebarBase extends Component {
style={style.swiperContent}
>
<ChannelsList
testID='main.sidebar.channels_list'
ref={this.channelListRef}
onSelectChannel={this.selectChannel}
onJoinChannel={this.joinChannel}

View file

@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AddReaction should match snapshot 1`] = `
<View
style={
Object {
"flex": 1,
}
}
testID="add_reaction.screen"
>
<Connect(EmojiPicker)
deviceWidth={750}
onEmojiPress={[Function]}
/>
</View>
`;

View file

@ -0,0 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallowWithIntl} from 'test/intl-test-helper';
import AddReaction from './index';
describe('AddReaction', () => {
const baseProps = {
onEmojiPress: jest.fn(),
componentId: 'component-id',
closeButton: {},
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<AddReaction {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -74,6 +74,7 @@ export default class ChannelAndroid extends ChannelBase {
<ChannelPostList registerTypingAnimation={this.registerTypingAnimation}/>
</View>
<PostDraft
testID='channel.post_draft'
ref={this.postDraft}
screenId={this.props.componentId}
registerTypingAnimation={this.registerTypingAnimation}

View file

@ -100,6 +100,7 @@ export default class ChannelIOS extends ChannelBase {
{indicators}
{renderDraftArea &&
<PostDraft
testID='channel.post_draft'
accessoriesContainerID={ACCESSORIES_CONTAINER_NATIVE_ID}
cursorPositionEvent={CHANNEL_POST_TEXTBOX_CURSOR_CHANGE}
ref={this.postDraft}

View file

@ -35,6 +35,7 @@ exports[`ChannelPostList should match snapshot 1`] = `
renderReplies={true}
scrollViewNativeID="channel-id"
showMoreMessagesButton={true}
testID="channel.post_list"
/>
<Connect(RetryBarIndicator) />
</ForwardRef(AnimatedComponentWrapper)>

View file

@ -192,6 +192,7 @@ export default class ChannelPostList extends PureComponent {
} else {
component = (
<PostList
testID='channel.post_list'
postIds={postIds}
lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(postIds) : -1}
extraData={postIds.length !== 0}

View file

@ -332,12 +332,14 @@ export default class ChannelAddMembers extends PureComponent {
return (
<KeyboardLayout>
<SafeAreaView
testID='channel_add_members.screen'
edges={['bottom', 'left', 'right']}
style={style.container}
>
<StatusBar/>
<View style={style.searchBar}>
<SearchBar
testID='channel_add_members.search_bar'
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -15,6 +15,7 @@ exports[`ChannelMembers should match snapshot 1`] = `
"flex": 1,
}
}
testID="channel_members.screen"
>
<Connect(StatusBar) />
<View
@ -57,6 +58,7 @@ exports[`ChannelMembers should match snapshot 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="channel_members.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -354,12 +354,14 @@ export default class ChannelMembers extends PureComponent {
return (
<KeyboardLayout>
<SafeAreaView
testID='channel_members.screen'
edges={['bottom', 'left', 'right']}
style={style.container}
>
<StatusBar/>
<View style={style.searchBar}>
<SearchBar
testID='channel_members.search_bar'
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -42,10 +42,10 @@ export default class CreateChannel extends PureComponent {
};
rightButton = {
testID: 'create_channel.create.button',
id: 'create-channel',
enabled: false,
showAsAction: 'always',
testID: 'edit_channel.create.button',
};
constructor(props, context) {

View file

@ -68,6 +68,7 @@ export default class EditChannel extends PureComponent {
};
rightButton = {
testID: 'edit_channel.save.button',
id: 'edit-channel',
enabled: false,
showAsAction: 'always',

View file

@ -224,17 +224,24 @@ export default class EditPost extends PureComponent {
displayError = (
<View style={[style.errorContainerSplit, {width: deviceWidth}]}>
<ErrorText
testID='edit_post.error.text'
error={error}
textStyle={style.errorWrap}
/>
<ErrorText error={errorExtra}/>
<ErrorText
testID='edit_post.error.text.extra'
error={errorExtra}
/>
</View>
);
} else {
displayError = (
<View style={[style.errorContainer, {width: deviceWidth}]}>
<View style={style.errorWrapper}>
<ErrorText error={error}/>
<ErrorText
testID='edit_post.error.text'
error={error}
/>
</View>
</View>
);

View file

@ -583,6 +583,7 @@ export default class EditProfile extends PureComponent {
<View style={style.errorContainer}>
<View style={style.errorWrapper}>
<ErrorText
testID='edit_profile.error.text'
error={error}
textStyle={style.errorText}
/>
@ -592,7 +593,10 @@ export default class EditProfile extends PureComponent {
}
return (
<SafeAreaView style={style.flex}>
<SafeAreaView
testID='edit_profile.screen'
style={style.flex}
>
<StatusBar/>
<KeyboardAwareScrollView
bounces={false}

View file

@ -7,6 +7,7 @@ exports[`ForgotPassword match snapshot after success of sendPasswordResetEmail 1
"flex": 1,
}
}
testID="forgot_password.screen"
>
<Connect(StatusBar) />
<TouchableWithoutFeedback
@ -92,6 +93,7 @@ exports[`ForgotPassword should match snapshot 1`] = `
"flex": 1,
}
}
testID="forgot_password.screen"
>
<Connect(StatusBar) />
<TouchableWithoutFeedback
@ -205,6 +207,7 @@ exports[`ForgotPassword snapshot for error on failure of email regex 1`] = `
"flex": 1,
}
}
testID="forgot_password.screen"
>
<Connect(StatusBar) />
<TouchableWithoutFeedback

View file

@ -87,6 +87,7 @@ export default class ForgotPassword extends PureComponent {
if (this.state.error) {
displayError = (
<ErrorText
testID='forgot_password.error.text'
error={this.state.error}
textStyle={style.errorText}
/>
@ -148,7 +149,10 @@ export default class ForgotPassword extends PureComponent {
);
}
return (
<SafeAreaView style={style.container}>
<SafeAreaView
testID='forgot_password.screen'
style={style.container}
>
<StatusBar/>
<TouchableWithoutFeedback
onPress={this.blur}

View file

@ -7,6 +7,7 @@ exports[`InteractiveDialog should display introduction text if present 1`] = `
"backgroundColor": "rgba(61,60,64,0.03)",
}
}
testID="interactive_dialog.screen"
>
<ScrollView
style={

View file

@ -178,7 +178,10 @@ export default class InteractiveDialog extends PureComponent {
const style = getStyleFromTheme(theme);
return (
<SafeAreaView style={style.container}>
<SafeAreaView
testID='interactive_dialog.screen'
style={style.container}
>
<ScrollView
ref={this.scrollView}
style={style.scrollView}
@ -186,6 +189,7 @@ export default class InteractiveDialog extends PureComponent {
<StatusBar/>
{error && (
<ErrorText
testID='interactive_dialog.error.text'
textStyle={style.errorContainer}
error={error}
/>

View file

@ -338,7 +338,7 @@ export default class Login extends PureComponent {
proceed = (
<Button
testID='signin.button'
testID='login.signin.button'
onPress={this.preSignIn}
containerStyle={[GlobalStyles.signupButton, additionalStyle]}
>
@ -396,9 +396,12 @@ export default class Login extends PureComponent {
defaultMessage='All team communication in one place, searchable and accessible anywhere'
/>
</View>
<ErrorText error={this.state.error}/>
<ErrorText
testID='login.error.text'
error={this.state.error}
/>
<TextInput
testID='username.input'
testID='login.username.input'
autoCapitalize='none'
autoCorrect={false}
blurOnSubmit={false}
@ -414,7 +417,7 @@ export default class Login extends PureComponent {
underlineColorAndroid='transparent'
/>
<TextInput
testID='password.input'
testID='login.password.input'
autoCapitalize='none'
autoCorrect={false}
disableFullscreenUI={true}

View file

@ -133,7 +133,10 @@ export default class Mfa extends PureComponent {
}
return (
<SafeAreaView style={style.flex}>
<SafeAreaView
testID='mfa.screen'
style={style.flex}
>
<KeyboardAvoidingView
behavior='padding'
style={style.flex}
@ -155,7 +158,10 @@ export default class Mfa extends PureComponent {
defaultMessage="To complete the sign in process, please enter a token from your smartphone's authenticator"
/>
</View>
<ErrorText error={this.state.error}/>
<ErrorText
testID='mfa.error.text'
error={this.state.error}
/>
<TextInputWithLocalizedPlaceholder
ref={this.inputRef}
value={this.state.token}

View file

@ -50,6 +50,7 @@ exports[`MoreChannels should match snapshot 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="more_channels.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -457,6 +457,7 @@ export default class MoreChannels extends PureComponent {
style={style.searchBar}
>
<SearchBar
testID='more_channels.search_bar'
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -480,6 +480,7 @@ export default class MoreDirectMessages extends PureComponent {
<StatusBar/>
<View style={style.searchBar}>
<SearchBar
testID='more_direct_messages.search_bar'
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -310,6 +310,7 @@ export default class Permalink extends PureComponent {
} else {
postList = (
<PostList
testID='permalink.post_list'
highlightPostId={focusedPostId}
indicateNewMessages={false}
isSearchResult={false}

View file

@ -15,6 +15,7 @@ exports[`Search RecentItem should match snapshot and respond to events 1`] = `
"height": 42,
}
}
testID="search.recent_item.test"
>
<Text
style={

View file

@ -0,0 +1,171 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Search should match snapshot 1`] = `
<Connect(KeyboardLayout)>
<Connect(StatusBar) />
<View
style={
Object {
"backgroundColor": "#1153ab",
"height": 61,
"paddingLeft": 14,
"paddingRight": 5,
"paddingTop": 14,
"width": "100%",
}
}
testID="search.screen"
>
<RNCSafeAreaView
edges={
Array [
"left",
"right",
]
}
>
<Search
autoCapitalize="none"
backArrowSize={28}
backgroundColor="transparent"
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={33}
containerStyle={
Object {
"padding": 0,
}
}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
"backgroundColor": "rgba(255,255,255,0.2)",
"color": "#ffffff",
"fontSize": 15,
}
}
keyboardAppearance="light"
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(255,255,255,0.5)"
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
selectionColor="rgba(255,255,255,0.5)"
showArrow={true}
showCancel={true}
testID="search.search_bar"
tintColorDelete="rgba(255,255,255,0.5)"
tintColorSearch="rgba(255,255,255,0.5)"
titleCancelColor="#ffffff"
value="initial value "
/>
</RNCSafeAreaView>
</View>
<RNCSafeAreaView
edges={
Array [
"bottom",
"left",
"right",
]
}
style={
Object {
"flex": 1,
}
}
>
<SectionList
ListFooterComponent={[Function]}
data={Array []}
disableVirtualization={false}
horizontal={false}
initialNumToRender={10}
keyExtractor={[Function]}
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="always"
maxToRenderPerBatch={10}
onEndReachedThreshold={2}
onLayout={[Function]}
onScroll={[Function]}
onViewableItemsChanged={[Function]}
renderSectionHeader={[Function]}
scrollEventThrottle={60}
sections={
Array [
Object {
"ItemSeparatorComponent": [Function],
"data": Array [
Object {
"description": "to find posts from specific users",
"modifier": "username",
"testID": "search.from_section",
"value": "from:",
},
Object {
"description": "to find posts in specific channels",
"modifier": ":channel-name",
"testID": "search.in_section",
"value": "in:",
},
Object {
"description": "to find posts on a specific date",
"modifier": "YYYY-MM-DD",
"testID": "search.on_section",
"value": "on:",
},
Object {
"description": "to find posts after a specific date",
"modifier": "YYYY-MM-DD",
"testID": "search.after_section",
"value": "after:",
},
Object {
"description": "to find posts before a specific date",
"modifier": "YYYY-MM-DD",
"testID": "search.before_section",
"value": "before:",
},
],
"key": "modifiers",
"keyExtractor": [Function],
"renderItem": [Function],
"title": "",
},
Object {
"data": Array [],
"key": "results",
"keyExtractor": [Function],
"renderItem": [Function],
"title": "Search Results",
},
]
}
stickySectionHeadersEnabled={true}
style={
Object {
"flex": 1,
}
}
updateCellsBatchingPeriod={50}
windowSize={21}
/>
</RNCSafeAreaView>
<Connect(Autocomplete)
cursorPosition={14}
enableDateSuggestion={true}
isSearch={true}
onChangeText={[Function]}
value="initial value "
/>
</Connect(KeyboardLayout)>
`;

View file

@ -32,6 +32,7 @@ export default class RecentItem extends PureComponent {
render() {
const {item, theme} = this.props;
const style = getStyleFromTheme(theme);
const testID = `search.recent_item.${item.terms}`;
return (
<TouchableHighlight
@ -40,6 +41,7 @@ export default class RecentItem extends PureComponent {
onPress={this.handlePress}
>
<View
testID={testID}
style={style.recentItemContainer}
>
<Text

View file

@ -575,7 +575,7 @@ export default class Search extends PureComponent {
const sectionsData = [{
value: 'from:',
testID: 'search_from.section',
testID: 'search.from_section',
modifier: `${intl.formatMessage({id: 'mobile.search.from_modifier_title', defaultMessage: 'username'})}`,
description: intl.formatMessage({
id: 'mobile.search.from_modifier_description',
@ -583,7 +583,7 @@ export default class Search extends PureComponent {
}),
}, {
value: 'in:',
testID: 'search_in.section',
testID: 'search.in_section',
modifier: `:${intl.formatMessage({id: 'mobile.search.in_modifier_title', defaultMessage: 'channel-name'})}`,
description: intl.formatMessage({
id: 'mobile.search.in_modifier_description',
@ -595,7 +595,7 @@ export default class Search extends PureComponent {
if (this.props.enableDateSuggestion) {
sectionsData.push({
value: 'on:',
testID: 'search_on.section',
testID: 'search.on_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.on_modifier_description',
@ -604,7 +604,7 @@ export default class Search extends PureComponent {
});
sectionsData.push({
value: 'after:',
testID: 'search_after.section',
testID: 'search.after_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.after_modifier_description',
@ -613,7 +613,7 @@ export default class Search extends PureComponent {
});
sectionsData.push({
value: 'before:',
testID: 'search_before.section',
testID: 'search.before_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.before_modifier_description',
@ -714,6 +714,7 @@ export default class Search extends PureComponent {
>
<SafeAreaView edges={['left', 'right']}>
<SearchBar
testID='search.search_bar'
ref={this.setSearchBarRef}
placeholder={intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import Preferences from '@mm-redux/constants/preferences';
import {shallowWithIntl} from 'test/intl-test-helper';
import Search from './search';
jest.useFakeTimers();
describe('Search', () => {
const baseProps = {
actions: {
clearSearch: jest.fn(),
closePermalink: jest.fn(),
handleSearchDraftChanged: jest.fn(),
getPostThread: jest.fn(),
removeSearchTerms: jest.fn(),
searchPostsWithParams: jest.fn(),
getMorePostsForSearch: jest.fn(),
selectPost: jest.fn(),
showPermalink: jest.fn(),
},
currentTeamId: 'current-team-id',
initialValue: 'initial value',
isLandscape: false,
postIds: [],
archivedPostIds: [],
recent: [],
isSearchGettingMore: true,
theme: Preferences.THEMES.default,
enableDateSuggestion: true,
timezoneOffsetInSeconds: 0,
viewArchivedChannels: false,
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<Search {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -520,7 +520,7 @@ export default class SelectServer extends PureComponent {
style={GlobalStyles.logo}
/>
<View testID='header.text'>
<View testID='select_server.header.text'>
<FormattedText
style={[GlobalStyles.header, GlobalStyles.label]}
id='mobile.components.select_server_view.enterServerUrl'
@ -528,7 +528,7 @@ export default class SelectServer extends PureComponent {
/>
</View>
<TextInput
testID='server_url.input'
testID='select_server.server_url.input'
ref={this.inputRef}
value={url}
editable={!inputDisabled}
@ -548,7 +548,7 @@ export default class SelectServer extends PureComponent {
disableFullscreenUI={true}
/>
<Button
testID='connect.button'
testID='select_server.connect.button'
onPress={this.handleConnect}
containerStyle={[GlobalStyles.signupButton, style.connectButton]}
>
@ -558,7 +558,10 @@ export default class SelectServer extends PureComponent {
</Text>
</Button>
<View>
<ErrorText error={error}/>
<ErrorText
testID='select_server.error.text'
error={error}
/>
</View>
</View>
</TouchableWithoutFeedback>

View file

@ -32,7 +32,7 @@ describe('SelectServer', () => {
const button = getByText('Connect');
fireEvent.press(button);
await waitFor(() => expect(getByTestId('error.text')).toBeTruthy());
await waitFor(() => expect(getByTestId('select_server.error.text')).toBeTruthy());
expect(getByText('Please enter a valid server URL')).toBeTruthy();
});
@ -41,13 +41,13 @@ describe('SelectServer', () => {
<SelectServer {...baseProps}/>,
);
const urlInput = getByTestId('server_url.input');
const urlInput = getByTestId('select_server.server_url.input');
fireEvent.changeText(urlInput, ' ');
const button = getByText('Connect');
fireEvent.press(button);
await waitFor(() => expect(getByTestId('error.text')).toBeTruthy());
await waitFor(() => expect(getByTestId('select_server.error.text')).toBeTruthy());
expect(getByText('Please enter a valid server URL')).toBeTruthy();
});
@ -56,13 +56,13 @@ describe('SelectServer', () => {
<SelectServer {...baseProps}/>,
);
const urlInput = getByTestId('server_url.input');
const urlInput = getByTestId('select_server.server_url.input');
fireEvent.changeText(urlInput, 'ht://invalid:8065');
const button = getByText('Connect');
fireEvent.press(button);
await waitFor(() => expect(getByTestId('error.text')).toBeTruthy());
await waitFor(() => expect(getByTestId('select_server.error.text')).toBeTruthy());
expect(getByText('URL must start with http:// or https://')).toBeTruthy();
});
@ -71,13 +71,13 @@ describe('SelectServer', () => {
<SelectServer {...baseProps}/>,
);
const urlInput = getByTestId('server_url.input');
const urlInput = getByTestId('select_server.server_url.input');
fireEvent.changeText(urlInput, 'http://localhost:8065');
const button = getByText('Connect');
fireEvent.press(button);
expect(queryByTestId('error.text')).toBeNull();
expect(queryByTestId('select_server.error.text')).toBeNull();
await waitFor(() => expect(getByText('Connecting...')).toBeTruthy());
});
});

View file

@ -17,6 +17,7 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
"paddingLeft": 8,
}
}
testID="selector.screen"
>
<Search
autoCapitalize="none"
@ -50,6 +51,7 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="selector.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -118,6 +120,7 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
"paddingLeft": 8,
}
}
testID="selector.screen"
>
<Search
autoCapitalize="none"
@ -151,6 +154,7 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="selector.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -219,6 +223,7 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
"paddingLeft": 8,
}
}
testID="selector.screen"
>
<Search
autoCapitalize="none"
@ -252,6 +257,7 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="selector.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -327,6 +333,7 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
"paddingLeft": 8,
}
}
testID="selector.screen"
>
<Search
autoCapitalize="none"
@ -360,6 +367,7 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="selector.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -428,6 +436,7 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
"paddingLeft": 8,
}
}
testID="selector.screen"
>
<Search
autoCapitalize="none"
@ -461,6 +470,7 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="selector.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -529,6 +539,7 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
"paddingLeft": 8,
}
}
testID="selector.screen"
>
<Search
autoCapitalize="none"
@ -562,6 +573,7 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
searchIconSize={24}
showArrow={false}
showCancel={true}
testID="selector.search_bar"
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -317,8 +317,12 @@ export default class SelectorScreen extends PureComponent {
return (
<SafeAreaView style={style.container}>
<StatusBar/>
<View style={style.searchBar}>
<View
testID='selector.screen'
style={style.searchBar}
>
<SearchBar
testID='selector.search_bar'
ref={this.setSearchBarRef}
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -0,0 +1,94 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Settings SelectTimezone should match snapshot 1`] = `
<RNCSafeAreaView
edges={
Array [
"left",
"right",
]
}
style={
Object {
"backgroundColor": "#ffffff",
"flex": 1,
}
}
testID="settings.select_timezone.screen"
>
<Connect(StatusBar) />
<View
style={
Object {
"backgroundColor": "#1153ab",
"height": 44,
"paddingLeft": 8,
"width": "100%",
}
}
>
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
"backgroundColor": "rgba(255,255,255,0.2)",
"color": "#ffffff",
"fontSize": 15,
}
}
keyboardAppearance="light"
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onChangeText={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(255,255,255,0.5)"
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
selectionColor="rgba(255,255,255,0.5)"
showArrow={false}
showCancel={true}
testID="settings.select_timezone.search_bar"
tintColorDelete="rgba(255,255,255,0.5)"
tintColorSearch="rgba(255,255,255,0.5)"
titleCancelColor="#ffffff"
value=""
/>
</View>
<FlatList
data={Array []}
disableVirtualization={false}
getItemLayout={[Function]}
horizontal={false}
initialNumToRender={10}
initialScrollIndex={1}
keyExtractor={[Function]}
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="always"
maxToRenderPerBatch={15}
numColumns={1}
onEndReachedThreshold={2}
removeClippedSubviews={false}
renderItem={[Function]}
scrollEventThrottle={50}
updateCellsBatchingPeriod={50}
viewabilityConfig={
Object {
"itemVisiblePercentThreshold": 100,
"waitForInteraction": false,
}
}
windowSize={21}
/>
</RNCSafeAreaView>
`;

View file

@ -108,12 +108,14 @@ export default class Timezone extends PureComponent {
return (
<SafeAreaView
testID='settings.select_timezone.screen'
edges={['left', 'right']}
style={style.container}
>
<StatusBar/>
<View style={style.header}>
<SearchBar
testID='settings.select_timezone.search_bar'
ref={this.setSearchBarRef}
placeholder={intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}

View file

@ -0,0 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallowWithIntl} from 'test/intl-test-helper';
import Preferences from '@mm-redux/constants/preferences';
import SelectTimezone from './select_timezone';
describe('Settings SelectTimezone', () => {
const baseProps = {
onBack: jest.fn(),
selectedTimezone: 'selected-timezone',
initialScrollIndex: 1,
timezones: [],
theme: Preferences.THEMES.default,
isLandscape: false,
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(<SelectTimezone {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -23,6 +23,7 @@ exports[`thread should match snapshot, has root post 1`] = `
"paddingBottom": 0,
}
}
testID="thread.screen"
>
<Connect(PostList)
currentUserId="member_user_id"
@ -46,6 +47,7 @@ exports[`thread should match snapshot, has root post 1`] = `
/>
}
scrollViewNativeID="threadPostList"
testID="thread.post_list"
/>
</ForwardRef(AnimatedComponentWrapper)>
</React.Fragment>
@ -58,6 +60,7 @@ exports[`thread should match snapshot, has root post 1`] = `
registerTypingAnimation={[Function]}
rootId="root_id"
scrollViewNativeID="threadPostList"
testID="thread.post_draft"
valueEvent="onThreadTextBoxValueChange"
/>
<View
@ -134,6 +137,7 @@ exports[`thread should match snapshot, render footer 1`] = `
/>
}
scrollViewNativeID="threadPostList"
testID="thread.post_list"
/>
`;
@ -154,6 +158,7 @@ exports[`thread should match snapshot, render footer 2`] = `
}
renderFooter={null}
scrollViewNativeID="threadPostList"
testID="thread.post_list"
/>
`;

View file

@ -30,8 +30,12 @@ export default class ThreadAndroid extends ThreadBase {
if (this.hasRootPost()) {
content = (
<>
<Animated.View style={{flex: 1, paddingBottom: this.bottomPadding}}>
<Animated.View
testID='thread.screen'
style={{flex: 1, paddingBottom: this.bottomPadding}}
>
<PostList
testID='thread.post_list'
renderFooter={this.renderFooter()}
indicateNewMessages={false}
postIds={postIds}
@ -43,6 +47,7 @@ export default class ThreadAndroid extends ThreadBase {
/>
</Animated.View>
<PostDraft
testID='thread.post_draft'
ref={this.postDraft}
channelId={channelId}
channelIsArchived={channelIsArchived}

View file

@ -43,8 +43,12 @@ export default class ThreadIOS extends ThreadBase {
if (this.hasRootPost()) {
content = (
<>
<Animated.View style={{flex: 1, paddingBottom: this.bottomPadding}}>
<Animated.View
testID='thread.screen'
style={{flex: 1, paddingBottom: this.bottomPadding}}
>
<PostList
testID='thread.post_list'
renderFooter={this.renderFooter()}
indicateNewMessages={false}
postIds={postIds}
@ -61,6 +65,7 @@ export default class ThreadIOS extends ThreadBase {
postDraft = (
<PostDraft
testID='thread.post_draft'
accessoriesContainerID={ACCESSORIES_CONTAINER_NATIVE_ID}
channelId={channelId}
channelIsArchived={channelIsArchived}

View file

@ -3,21 +3,16 @@
class CameraQuickAction {
testID = {
cameraQuickAction: 'post_draft.camera_quick_action',
cameraQuickActionDisabled: 'post_draft.camera_quick_action.disabled',
cameraActionSuffix: 'post_draft.quick_actions.camera_action',
cameraActionDisabledSuffix: 'post_draft.quick_actions.camera_action.disabled',
}
cameraQuickAction = element(by.id(this.testID.cameraQuickAction));
cameraQuickActionDisabled = element(by.id(this.testID.cameraQuickActionDisabled));
getCameraQuickAction = (screenPrefix) => {
return element(by.id(`${screenPrefix}${this.testID.cameraActionSuffix}`));
}
toBeVisible = async (options = {disabled: false}) => {
if (options.disabled) {
await expect(this.cameraQuickActionDisabled).toBeVisible();
return this.cameraQuickActionDisabled;
}
await expect(this.cameraQuickAction).toBeVisible();
return this.cameraQuickAction;
getCameraQuickActionDisabled = (screenPrefix) => {
return element(by.id(`${screenPrefix}${this.testID.cameraActionDisabledSuffix}`));
}
}

View file

@ -0,0 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
class EditChannelInfo {
testID = {
nameInput: 'edit_channel_info.name.input',
purposeInput: 'edit_channel_info.purpose.input',
headerInput: 'edit_channel_info.header.input',
}
nameInput = element(by.id(this.testID.nameInput));
purposeInput = element(by.id(this.testID.purposeInput));
headerInput = element(by.id(this.testID.headerInput));
}
const editChannelInfo = new EditChannelInfo();
export default editChannelInfo;

View file

@ -3,21 +3,16 @@
class FileQuickAction {
testID = {
fileQuickAction: 'post_draft.file_quick_action',
fileQuickActionDisabled: 'post_draft.file_quick_action.disabled',
fileActionSuffix: 'post_draft.quick_actions.file_action',
fileActionDisabledSuffix: 'post_draft.quick_actions.file_action.disabled',
}
fileQuickAction = element(by.id(this.testID.fileQuickAction));
fileQuickActionDisabled = element(by.id(this.testID.fileQuickActionDisabled));
getFileQuickAction = (screenPrefix) => {
return element(by.id(`${screenPrefix}${this.testID.fileActionSuffix}`));
}
toBeVisible = async (options = {disabled: false}) => {
if (options.disabled) {
await expect(this.fileQuickActionDisabled).toBeVisible();
return this.fileQuickActionDisabled;
}
await expect(this.fileQuickAction).toBeVisible();
return this.fileQuickAction;
getFileQuickActionDisabled = (screenPrefix) => {
return element(by.id(`${screenPrefix}${this.testID.fileActionDisabledSuffix}`));
}
}

View file

@ -3,21 +3,16 @@
class ImageQuickAction {
testID = {
imageQuickAction: 'post_draft.image_quick_action',
imageQuickActionDisabled: 'post_draft.image_quick_action.disabled',
imageActionSuffix: 'post_draft.quick_actions.image_action',
imageActionDisabledSuffix: 'post_draft.quick_actions.image_action.disabled',
}
imageQuickAction = element(by.id(this.testID.imageQuickAction));
imageQuickActionDisabled = element(by.id(this.testID.imageQuickActionDisabled));
getImageQuickAction = (screenPrefix) => {
return element(by.id(`${screenPrefix}${this.testID.imageActionSuffix}`));
}
toBeVisible = async (options = {disabled: false}) => {
if (options.disabled) {
await expect(this.imageQuickActionDisabled).toBeVisible();
return this.imageQuickActionDisabled;
}
await expect(this.imageQuickAction).toBeVisible();
return this.imageQuickAction;
getImageQuickActionDisabled = (screenPrefix) => {
return element(by.id(`${screenPrefix}${this.testID.imageActionDisabledSuffix}`));
}
}

View file

@ -3,24 +3,32 @@
import Autocomplete from './autocomplete';
import CameraQuickAction from './camera_quick_action';
import EditChannelInfo from './edit_channel_info';
import FileQuickAction from './file_quick_action';
import ImageQuickAction from './image_quick_action';
import InputQuickAction from './input_quick_action';
import MainSidebar from './main_sidebar';
import Post from './post';
import PostDraft from './post_draft';
import PostOptions from './post_options';
import RecentItem from './recent_item';
import SearchBar from './search_bar';
import SendButton from './send_button';
import SettingsSidebar from './settings_sidebar';
export {
Autocomplete,
CameraQuickAction,
EditChannelInfo,
FileQuickAction,
ImageQuickAction,
InputQuickAction,
MainSidebar,
Post,
PostDraft,
PostOptions,
RecentItem,
SearchBar,
SendButton,
SettingsSidebar,
};

Some files were not shown because too many files have changed in this diff Show more