MM-29024 Detox/E2E: Add unit and e2e tests for disabled mobile upload (#4954)

* MM-29024 Detox/E2E: Add unit and e2e tests for disabled mobile upload

* Simplified element call

* Remove extra lines

* Added e2e for mobile upload enabled - quick actions

* Fix upload item snap file

* Added check for license

* Fix lint

* Update function export

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Joseph Baylon 2020-11-12 16:29:16 -08:00 committed by GitHub
parent 83a157421c
commit 48dbb75471
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 461 additions and 34 deletions

View file

@ -466,6 +466,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.file_quick_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -491,6 +492,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.image_quick_action"
>
<Icon
color="rgba(61,60,64,0.64)"
@ -516,6 +518,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
"padding": 10,
}
}
testID="post_draft.camera_quick_action"
>
<Icon
color="rgba(61,60,64,0.64)"

View file

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

View file

@ -148,12 +148,16 @@ 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 color = disabled ?
changeOpacity(theme.centerChannelColor, 0.16) :
changeOpacity(theme.centerChannelColor, 0.64);
return (
<TouchableWithFeedback
testID={testID}
disabled={disabled}
onPress={this.handleButtonPress}
style={style.icon}

View file

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

View file

@ -140,12 +140,16 @@ 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 color = disabled ?
changeOpacity(theme.centerChannelColor, 0.16) :
changeOpacity(theme.centerChannelColor, 0.64);
return (
<TouchableWithFeedback
testID={testID}
disabled={disabled}
onPress={this.handleButtonPress}
style={style.icon}

View file

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

View file

@ -155,12 +155,16 @@ 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 color = disabled ?
changeOpacity(theme.centerChannelColor, 0.16) :
changeOpacity(theme.centerChannelColor, 0.64);
return (
<TouchableWithFeedback
testID={testID}
disabled={disabled}
onPress={this.handleButtonPress}
style={style.icon}

View file

@ -0,0 +1,110 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`UploadItem downloadAndUploadFile should match, full snapshot 1`] = `
<View
style={
Object {
"marginLeft": 12,
"paddingTop": 5,
}
}
>
<View
style={
Object {
"borderRadius": 4,
"height": 56,
"width": 56,
}
}
>
<ForwardRef
onPress={[Function]}
>
<View
style={
Object {
"height": 56,
"width": 56,
}
}
>
<FileAttachmentIcon
backgroundColor="rgba(61,60,64,0.08)"
defaultImage={false}
failed={false}
file={
Object {
"loading": false,
}
}
iconSize={48}
smallImage={false}
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>
</ForwardRef>
</View>
<UploadRemove
channelId="channel-id"
onPress={[Function]}
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

@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`UploadRemove should match, full snapshot 1`] = `
<TouchableWithFeedbackIOS
onPress={[Function]}
style={
Object {
"elevation": 11,
"height": 24,
"position": "absolute",
"right": -8,
"top": -7,
"width": 24,
}
}
type="opacity"
>
<View
style={
Object {
"alignSelf": "center",
"backgroundColor": "#ffffff",
"borderRadius": 12,
"height": 25,
"marginTop": 5.4,
"width": 24,
}
}
>
<CompassIcon
color="rgba(61,60,64,0.64)"
name="close-circle"
size={24}
/>
</View>
</TouchableWithFeedbackIOS>
`;

View file

@ -1,7 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import {shallowWithIntl} from 'test/intl-test-helper';
import {Preferences} from '@mm-redux/constants';
import ImageCacheManager from '@utils/image_cache_manager';
@ -21,6 +23,14 @@ describe('UploadItem', () => {
};
describe('downloadAndUploadFile', () => {
test('should match, full snapshot', () => {
const wrapper = shallowWithIntl(
<UploadItem {...props}/>,
);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should upload file', async () => {
const component = shallow(<UploadItem {...props}/>);
component.instance().uploadFile = jest.fn();

View file

@ -0,0 +1,25 @@
// 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';
import UploadRemove from './upload_remove';
describe('UploadRemove', () => {
const props = {
onPress: jest.fn(),
channelId: 'channel-id',
clientId: 'client-id',
theme: Preferences.THEMES.default,
};
test('should match, full snapshot', () => {
const wrapper = shallowWithIntl(
<UploadRemove {...props}/>,
);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -11,6 +11,7 @@ import CustomPropTypes from 'app/constants/custom_prop_types';
export default class TouchableWithFeedbackAndroid extends PureComponent {
static propTypes = {
testID: PropTypes.string,
children: CustomPropTypes.Children,
underlayColor: PropTypes.string,
type: PropTypes.oneOf(['native', 'opacity', 'none']),
@ -21,12 +22,13 @@ export default class TouchableWithFeedbackAndroid extends PureComponent {
};
render() {
const {children, underlayColor, type, ...props} = this.props;
const {testID, children, underlayColor, type, ...props} = this.props;
switch (type) {
case 'native':
return (
<TouchableNativeFeedback
testID={testID}
{...props}
background={TouchableNativeFeedback.Ripple(underlayColor || '#000', false)}
>
@ -38,6 +40,7 @@ export default class TouchableWithFeedbackAndroid extends PureComponent {
case 'opacity':
return (
<TouchableOpacity
testID={testID}
{...props}
>
{children}
@ -46,6 +49,7 @@ export default class TouchableWithFeedbackAndroid extends PureComponent {
case 'none':
return (
<TouchableWithoutFeedback
testID={testID}
{...props}
>
{children}

View file

@ -9,6 +9,7 @@ import CustomPropTypes from 'app/constants/custom_prop_types';
export default class TouchableWithFeedbackIOS extends PureComponent {
static propTypes = {
testID: PropTypes.string,
children: CustomPropTypes.Children,
cancelTouchOnPanning: PropTypes.bool,
type: PropTypes.oneOf(['native', 'opacity', 'none']),
@ -29,12 +30,15 @@ export default class TouchableWithFeedbackIOS extends PureComponent {
}
render() {
const {children, type, ...props} = this.props;
const {testID, children, type, ...props} = this.props;
switch (type) {
case 'native':
return (
<View {...this.panResponder.panHandlers}>
<View
testID={testID}
{...this.panResponder.panHandlers}
>
<TouchableHighlight
{...props}
>
@ -45,6 +49,7 @@ export default class TouchableWithFeedbackIOS extends PureComponent {
case 'opacity':
return (
<TouchableOpacity
testID={testID}
{...props}
>
{children}
@ -53,6 +58,7 @@ export default class TouchableWithFeedbackIOS extends PureComponent {
case 'none':
return (
<TouchableWithoutFeedback
testID={testID}
{...props}
>
{children}

View file

@ -55,7 +55,7 @@ function handleSelectChannel(state, action) {
return data;
}
function handleSetTempUploadFileForPostDraft(state, action) {
export function handleSetTempUploadFilesForPostDraft(state, action) {
if (action.rootId) {
return state;
}
@ -203,7 +203,7 @@ function drafts(state = {}, action) { // eslint-disable-line complexity
case ChannelTypes.SELECT_CHANNEL:
return handleSelectChannel(state, action);
case ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT:
return handleSetTempUploadFileForPostDraft(state, action);
return handleSetTempUploadFilesForPostDraft(state, action);
case ViewTypes.RETRY_UPLOAD_FILE_FOR_POST:
return handleRetryUploadFileForPost(state, action);
case FileTypes.RECEIVED_UPLOAD_FILES:

View file

@ -1,41 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import channelReducer from './channel';
import channelReducer, {handleSetTempUploadFilesForPostDraft} from './channel';
import {ViewTypes} from '@constants';
describe('Reducers.channel', () => {
const initialState = {
displayName: '',
drafts: {},
loading: false,
refreshing: false,
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
unreadMessageCount: {},
};
test('Initial state', () => {
const initialState = {
displayName: '',
drafts: {},
loading: false,
refreshing: false,
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
unreadMessageCount: {},
};
const nextState = channelReducer(
{
displayName: '',
drafts: {},
loading: false,
refreshing: false,
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
unreadMessageCount: {},
},
initialState,
{},
);
expect(nextState).toEqual(initialState);
});
test('handleSetTempUploadFilesForPostDraft - should not throw error when state[action.channelId] is null', () => {
const action = {
channelId: 'channel-id',
clientIds: [],
rootId: null,
type: ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT,
};
const initialState = {
[action.channelId]: null,
};
const expectedState = {
[action.channelId]: {
files: [],
},
};
expect(handleSetTempUploadFilesForPostDraft(initialState, action)).toEqual(expectedState);
});
});

View file

@ -66,7 +66,7 @@ function handleReceivedPostSelected(state, action) {
return data;
}
function handleSetTempUploadFilesForPostDraft(state, action) {
export function handleSetTempUploadFilesForPostDraft(state, action) {
if (!action.rootId) {
return state;
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import channelReducer, {handleSetTempUploadFilesForPostDraft} from './thread';
import {ViewTypes} from '@constants';
describe('Reducers.thread', () => {
test('Initial state', () => {
const initialState = {
drafts: {},
};
const nextState = channelReducer(
initialState,
{},
);
expect(nextState).toEqual(initialState);
});
test('handleSetTempUploadFilesForPostDraft - should not throw error when state[action.rootId] is null', () => {
const action = {
clientIds: [],
rootId: 'root-id',
type: ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT,
};
const initialState = {
[action.rootId]: null,
};
const expectedState = {
[action.rootId]: {
files: [],
},
};
expect(handleSetTempUploadFilesForPostDraft(initialState, action)).toEqual(expectedState);
});
});

View file

@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
class CameraQuickAction {
testID = {
cameraQuickAction: 'post_draft.camera_quick_action',
cameraQuickActionDisabled: 'post_draft.camera_quick_action.disabled',
}
cameraQuickAction = element(by.id(this.testID.cameraQuickAction));
cameraQuickActionDisabled = element(by.id(this.testID.cameraQuickActionDisabled));
toBeVisible = async (options = {disabled: false}) => {
if (options.disabled) {
await expect(this.cameraQuickActionDisabled).toBeVisible();
return this.cameraQuickActionDisabled;
}
await expect(this.cameraQuickAction).toBeVisible();
return this.cameraQuickAction;
}
}
const cameraQuickAction = new CameraQuickAction();
export default cameraQuickAction;

View file

@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
class FileQuickAction {
testID = {
fileQuickAction: 'post_draft.file_quick_action',
fileQuickActionDisabled: 'post_draft.file_quick_action.disabled',
}
fileQuickAction = element(by.id(this.testID.fileQuickAction));
fileQuickActionDisabled = element(by.id(this.testID.fileQuickActionDisabled));
toBeVisible = async (options = {disabled: false}) => {
if (options.disabled) {
await expect(this.fileQuickActionDisabled).toBeVisible();
return this.fileQuickActionDisabled;
}
await expect(this.fileQuickAction).toBeVisible();
return this.fileQuickAction;
}
}
const fileQuickAction = new FileQuickAction();
export default fileQuickAction;

View file

@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
class ImageQuickAction {
testID = {
imageQuickAction: 'post_draft.image_quick_action',
imageQuickActionDisabled: 'post_draft.image_quick_action.disabled',
}
imageQuickAction = element(by.id(this.testID.imageQuickAction));
imageQuickActionDisabled = element(by.id(this.testID.imageQuickActionDisabled));
toBeVisible = async (options = {disabled: false}) => {
if (options.disabled) {
await expect(this.imageQuickActionDisabled).toBeVisible();
return this.imageQuickActionDisabled;
}
await expect(this.imageQuickAction).toBeVisible();
return this.imageQuickAction;
}
}
const imageQuickAction = new ImageQuickAction();
export default imageQuickAction;

View file

@ -2,12 +2,18 @@
// See LICENSE.txt for license information.
import Autocomplete from './autocomplete';
import CameraQuickAction from './camera_quick_action';
import FileQuickAction from './file_quick_action';
import ImageQuickAction from './image_quick_action';
import MainSidebar from './main_sidebar';
import PostOptions from './post_options';
import SettingsSidebar from './settings_sidebar';
export {
Autocomplete,
CameraQuickAction,
FileQuickAction,
ImageQuickAction,
MainSidebar,
PostOptions,
SettingsSidebar,

View file

@ -0,0 +1,43 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// *******************************************************************
// - [#] indicates a test step (e.g. # Go to a screen)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element testID when selecting an element. Create one if none.
// *******************************************************************
import {
CameraQuickAction,
FileQuickAction,
ImageQuickAction,
} from '@support/ui/component';
import {ChannelScreen} from '@support/ui/screen';
import {
Setup,
System,
} from '@support/server_api';
describe('Disable Upload', () => {
beforeAll(async () => {
// * Verify that the server has license
await System.apiRequireLicense();
// # Disable mobile upload
await System.apiUpdateConfig({FileSettings: {EnableMobileUpload: false}});
const {user} = await Setup.apiInit();
await ChannelScreen.open(user);
});
afterAll(async () => {
await ChannelScreen.logout();
});
it('MM-T3453 should disable file, image, camera icons when mobile file upload is disabled', async () => {
// * Verify disabled file, image, camera icons are visible
await expect(CameraQuickAction.cameraQuickActionDisabled).toBeVisible();
await expect(FileQuickAction.fileQuickActionDisabled).toBeVisible();
await expect(ImageQuickAction.imageQuickActionDisabled).toBeVisible();
});
});

View file

@ -0,0 +1,43 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// *******************************************************************
// - [#] indicates a test step (e.g. # Go to a screen)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element testID when selecting an element. Create one if none.
// *******************************************************************
import {
CameraQuickAction,
FileQuickAction,
ImageQuickAction,
} from '@support/ui/component';
import {ChannelScreen} from '@support/ui/screen';
import {
Setup,
System,
} from '@support/server_api';
describe('Enable Upload', () => {
beforeAll(async () => {
// * Verify that the server has license
await System.apiRequireLicense();
// # Enable mobile upload
await System.apiUpdateConfig({FileSettings: {EnableMobileUpload: true}});
const {user} = await Setup.apiInit();
await ChannelScreen.open(user);
});
afterAll(async () => {
await ChannelScreen.logout();
});
it('MM-T3483 should enable file, image, camera icons when mobile file upload is enabled', async () => {
// * Verify enabled file, image, camera icons are visible
await expect(CameraQuickAction.cameraQuickAction).toBeVisible();
await expect(FileQuickAction.fileQuickAction).toBeVisible();
await expect(ImageQuickAction.imageQuickAction).toBeVisible();
});
});