diff --git a/detox/e2e/support/ui/component/bottom_sheet.js b/detox/e2e/support/ui/component/bottom_sheet.js index 6589d6066..cb2cdc880 100644 --- a/detox/e2e/support/ui/component/bottom_sheet.js +++ b/detox/e2e/support/ui/component/bottom_sheet.js @@ -11,6 +11,9 @@ class BottomSheet { archivedChannelsOption = isAndroid() ? element(by.text('Archived Channels')) : element(by.label('Archived Channels')).atIndex(0); awayOption = isAndroid() ? element(by.text('Away')) : element(by.label('Away')).atIndex(0); cancelOption = isAndroid() ? element(by.text('Cancel')) : element(by.label('Cancel')).atIndex(0); + copyHeaderOption = isAndroid() ? element(by.text('Copy Header')) : element(by.label('Copy Header')).atIndex(0); + copyPurposeOption = isAndroid() ? element(by.text('Copy Purpose')) : element(by.label('Copy Purpose')).atIndex(0); + copyUrlOption = isAndroid() ? element(by.text('Copy URL')) : element(by.label('Copy URL')).atIndex(0); doNotDisturbOption = isAndroid() ? element(by.text('Do Not Disturb')) : element(by.label('Do No Disturb')).atIndex(0); offlineOption = isAndroid() ? element(by.text('Offline')) : element(by.label('Offline')).atIndex(0); onlineOption = isAndroid() ? element(by.text('Online')) : element(by.label('Online')).atIndex(0); diff --git a/detox/e2e/support/ui/screen/channel_info.js b/detox/e2e/support/ui/screen/channel_info.js index e27293595..052630086 100644 --- a/detox/e2e/support/ui/screen/channel_info.js +++ b/detox/e2e/support/ui/screen/channel_info.js @@ -3,7 +3,7 @@ import {Alert} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; -import {timeouts, wait} from '@support/utils'; +import {isAndroid, timeouts, wait} from '@support/utils'; class ChannelInfoScreen { testID = { @@ -78,7 +78,7 @@ class ChannelInfoScreen { await expect(this.channelInfoScreen).not.toBeVisible(); } - archiveChannel = async ({confirm = true, publicChannel = true} = {}) => { + archiveChannel = async ({confirm = true, publicChannel = true, description = null} = {}) => { await this.channelInfoScrollView.scrollTo('bottom'); await this.archiveAction.tap(); const { @@ -92,6 +92,10 @@ class ChannelInfoScreen { } else { await expect(archivePrivateChannelTitle).toBeVisible(); } + if (description) { + const descriptionElement = isAndroid() ? element(by.text(description)) : element(by.label(description)).atIndex(0); + await expect(descriptionElement).toBeVisible(); + } await expect(noButton).toBeVisible(); await expect(yesButton).toBeVisible(); if (confirm) { @@ -105,7 +109,7 @@ class ChannelInfoScreen { } } - leaveChannel = async ({confirm = true, publicChannel = true} = {}) => { + leaveChannel = async ({confirm = true, publicChannel = true, description = null} = {}) => { await this.channelInfoScrollView.scrollTo('bottom'); await this.leaveAction.tap(); const { @@ -119,6 +123,10 @@ class ChannelInfoScreen { } else { await expect(leavePrivateChannelTitle).toBeVisible(); } + if (description) { + const descriptionElement = isAndroid() ? element(by.text(description)) : element(by.label(description)).atIndex(0); + await expect(descriptionElement).toBeVisible(); + } await expect(noButton).toBeVisible(); await expect(yesButton).toBeVisible(); if (confirm) { diff --git a/detox/e2e/test/channel_info/channel_info.e2e.js b/detox/e2e/test/channel_info/channel_info.e2e.js new file mode 100644 index 000000000..e6f1965e9 --- /dev/null +++ b/detox/e2e/test/channel_info/channel_info.e2e.js @@ -0,0 +1,248 @@ +// 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 {BottomSheet} from '@support/ui/component'; +import { + ChannelInfoScreen, + ChannelScreen, + EditChannelScreen, + MoreDirectMessagesScreen, +} from '@support/ui/screen'; +import { + Channel, + Setup, +} from '@support/server_api'; +import {isIos, timeouts, wait} from '@support/utils'; + +describe('Channel Info', () => { + const { + goToChannel, + postInput, + } = ChannelScreen; + const { + headerInput, + nameInput, + purposeInput, + saveButton, + } = EditChannelScreen; + let testChannel1; + let testChannel2; + let townSquareChannel; + + beforeAll(async () => { + const {channel, team, user} = await Setup.apiInit(); + testChannel1 = channel; + + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.name, 'town-square')); + ({channel: testChannel2} = await Channel.apiCreateChannel({type: 'O', teamId: team.id})); + await Channel.apiAddUserToChannel(user.id, testChannel2.id); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T849 should be able to display channel info', async () => { + // # Open channel info screen + await ChannelInfoScreen.open(); + + // * Verify channel info is displayed + await expect(ChannelInfoScreen.channelDisplayName).toHaveText(townSquareChannel.display_name); + + // # Go back to channel + await ChannelInfoScreen.close(); + }); + + it('MM-T866 whitespaces on channel name should be trimmed on alert popups', async () => { + const { + archiveChannel, + leaveChannel, + } = ChannelInfoScreen; + + // # Add whitespaces to channel name + await ChannelScreen.goToChannel(testChannel1.display_name); + await ChannelInfoScreen.open(); + await EditChannelScreen.open(); + await nameInput.replaceText(` ${testChannel1.display_name} `); + await saveButton.tap(); + + // * Verify channel name is trimmed on alert popups + await leaveChannel({confirm: false, description: `Are you sure you want to leave the public channel ${testChannel1.display_name}?`}); + await archiveChannel({confirm: false, description: `Are you sure you want to archive the public channel ${testChannel1.display_name}?`}); + + // # Go back to channel + await ChannelInfoScreen.close(); + }); + + it('MM-T867 should be able to copy channel header text', async () => { + // # Edit channel header + await goToChannel(townSquareChannel.display_name); + const expectedHeader = `Channel header: ${townSquareChannel.display_name.toLowerCase()}`; + await ChannelInfoScreen.open(); + await EditChannelScreen.open(); + await headerInput.replaceText(expectedHeader); + await saveButton.tap(); + + // # Copy channel header text + await element(by.text(expectedHeader)).longPress(); + await BottomSheet.copyHeaderOption.tap(); + + // # Go back to channel + await ChannelInfoScreen.close(); + + // Tapping on Paste currently works on iOS only + if (isIos()) { + // # Paste to channel post draft + await postInput.longPress(); + await element(by.text('Paste')).tap(); + + // * Verify header text is pasted to channel post draft + await expect(postInput).toHaveText(expectedHeader); + await postInput.clearText(); + } + }); + + it('MM-T868 should be able to copy channel purpose text', async () => { + // # Edit channel purpose + await goToChannel(townSquareChannel.display_name); + const expectedPurpose = `Channel purpose: ${townSquareChannel.display_name.toLowerCase()}`; + await ChannelInfoScreen.open(); + await EditChannelScreen.open(); + await purposeInput.replaceText(expectedPurpose); + await saveButton.tap(); + + // # Copy channel purpose text + await element(by.text(expectedPurpose)).longPress(); + await BottomSheet.copyPurposeOption.tap(); + + // # Go back to channel + await ChannelInfoScreen.close(); + + // Tapping on Paste currently works on iOS only + if (isIos()) { + // # Paste to channel post draft + await postInput.longPress(); + await element(by.text('Paste')).tap(); + + // * Verify purpose text is pasted to channel post draft + await expect(postInput).toHaveText(expectedPurpose); + await postInput.clearText(); + } + }); + + it('MM-T869 should be able to copy url from channel header', async () => { + // # Edit channel header + const expectedHeaderUrl = 'https://mattermost.com'; + await goToChannel(townSquareChannel.display_name); + await ChannelInfoScreen.open(); + await EditChannelScreen.open(); + await headerInput.replaceText(expectedHeaderUrl); + await saveButton.tap(); + + // # Copy channel url + await element(by.text(expectedHeaderUrl)).longPress(); + await BottomSheet.copyUrlOption.tap(); + + // # Go back to channel + await ChannelInfoScreen.close(); + + // Tapping on Paste currently works on iOS only + if (isIos()) { + // # Paste to channel post draft + await postInput.longPress(); + await element(by.text('Paste')).tap(); + + // * Verify url is pasted to channel post draft + await expect(postInput).toHaveText(expectedHeaderUrl); + await postInput.clearText(); + } + }); + + it('MM-T1733 should not render markdown in channel purpose', async () => { + // # Edit channel purpose + const expectedPurpose = '[Mattermost](https://mattermost.com)'; + await goToChannel(townSquareChannel.display_name); + await ChannelInfoScreen.open(); + await EditChannelScreen.open(); + await purposeInput.replaceText(expectedPurpose); + await saveButton.tap(); + + // * Verify purpose is literal text + await expect(element(by.text(expectedPurpose))).toBeVisible(); + + // # Go back to channel + await ChannelInfoScreen.close(); + }); + + it('MM-T2980 should be able to save channel name up to 64 characters only', async () => { + // # Open edit channel screen + const channelName64Char = 'ChannelName12345678901234567890123456789012345678901234567890123'; + await ChannelScreen.goToChannel(testChannel2.display_name); + await ChannelInfoScreen.open(); + await EditChannelScreen.open(); + + // # Attempt to edit channel name with more than 64 characters + await nameInput.replaceText(channelName64Char + 'x'); + if (isIos()) { + // # Attempt to save + await saveButton.tap(); + + // * Verify error message is displayed + await expect(element(by.text('Channel name must be less than 64 characters'))).toBeVisible(); + } else { + // * Verify extra characters are trimmed on Android + await expect(nameInput).toHaveText(channelName64Char); + } + + // # Edit channel name with exactly 64 characters + await nameInput.replaceText(channelName64Char); + await saveButton.tap(); + + // * Verify channel name is saved + await expect(ChannelInfoScreen.channelDisplayName).toHaveText(channelName64Char); + + // # Go back to channel + await ChannelInfoScreen.close(); + }); + + it('MM-T3406 should render correct GM member count in channel info header', async () => { + const { + getUserAtIndex, + startButton, + } = MoreDirectMessagesScreen; + + // # Open more direct messages screen + await ChannelScreen.goToChannel(townSquareChannel.display_name); + await ChannelScreen.openMainSidebar(); + await MoreDirectMessagesScreen.open(); + + // # Wait for some profiles to load + await wait(timeouts.ONE_SEC); + + // # Select 3 profiles + await getUserAtIndex(0).tap(); + await getUserAtIndex(1).tap(); + await getUserAtIndex(2).tap(); + + // # Create a GM with selected profiles + await startButton.tap(); + + // # Open channel info screen + await ChannelInfoScreen.open(); + + // * Verify GM member count is 3 + await expect(ChannelInfoScreen.channelIconGMMemberCount).toHaveText('3'); + + // # Close channel info screen + await ChannelInfoScreen.close(); + }); +}); diff --git a/detox/e2e/test/channel_info/header.e2e.js b/detox/e2e/test/channel_info/header.e2e.js deleted file mode 100644 index f28e5119d..000000000 --- a/detox/e2e/test/channel_info/header.e2e.js +++ /dev/null @@ -1,60 +0,0 @@ -// 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 { - ChannelInfoScreen, - ChannelScreen, - MoreDirectMessagesScreen, -} from '@support/ui/screen'; -import {timeouts, wait} from '@support/utils'; -import {Setup} from '@support/server_api'; - -describe('Channel Info Header', () => { - beforeAll(async () => { - const {user} = await Setup.apiInit(); - - // # Open channel screen - await ChannelScreen.open(user); - }); - - afterAll(async () => { - await ChannelScreen.logout(); - }); - - it('MM-T3406 should render correct GM member count in channel info header', async () => { - // # Open more direct messages screen - await ChannelScreen.openMainSidebar(); - await MoreDirectMessagesScreen.open(); - - // # Wait for some profiles to load - await wait(timeouts.ONE_SEC); - - const { - getUserAtIndex, - startButton, - } = MoreDirectMessagesScreen; - - // # Select 3 profiles - await getUserAtIndex(0).tap(); - await getUserAtIndex(1).tap(); - await getUserAtIndex(2).tap(); - - // # Create a GM with selected profiles - await startButton.tap(); - - // # Open channel info screen - await ChannelInfoScreen.open(); - - // * Verify GM member count is 3 - await expect(ChannelInfoScreen.channelIconGMMemberCount).toHaveText('3'); - - // # Close channel info screen - await ChannelInfoScreen.close(); - }); -}); diff --git a/detox/e2e/test/channels/create_public_channel.e2e.js b/detox/e2e/test/channels/create_public_channel.e2e.js index 86922ae5a..65fd1b765 100644 --- a/detox/e2e/test/channels/create_public_channel.e2e.js +++ b/detox/e2e/test/channels/create_public_channel.e2e.js @@ -19,6 +19,16 @@ import {Setup} from '@support/server_api'; import {isAndroid} from '@support/utils'; describe('Channels', () => { + const { + channelIntro, + openMainSidebar, + } = ChannelScreen; + const { + nameInput, + purposeInput, + headerInput, + } = CreateChannelScreen; + beforeAll(async () => { const {user} = await Setup.apiInit(); @@ -30,9 +40,24 @@ describe('Channels', () => { await ChannelScreen.logout(); }); + it('MM-T838 should be able to create channel using non-latin characters', async () => { + // # Open more channels screen + await openMainSidebar(); + await MoreChannelsScreen.open(); + + // # Create new channel with non-latin characters + const expectedChannelName = '熊本'; + await MoreChannelsScreen.createButton.tap(); + await nameInput.replaceText(expectedChannelName); + await CreateChannelScreen.createButton.tap(); + + // * Verify channel is created + await expect(channelIntro).toHaveText('Beginning of ' + expectedChannelName); + }); + it('MM-T3201 Create public channel', async () => { // # Open more channels screen - await ChannelScreen.openMainSidebar(); + await openMainSidebar(); await MoreChannelsScreen.open(); // * Expect a list of public channels, initially empty @@ -45,12 +70,6 @@ describe('Channels', () => { const createChannelScreen = await CreateChannelScreen.toBeVisible(); await expect(element(by.text('New Public Channel'))).toBeVisible(); - const { - nameInput, - purposeInput, - headerInput, - } = CreateChannelScreen; - // # Fill data await nameInput.typeText('a'); await attemptToTapCreateButton();