mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
E2E/Playwright: MM-T5424 Find channel limit to 50 results (#23248)
* fix aria-label of Find Channels modal * add components * add test for MM-T5424 --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
30a053314b
commit
8fdbbf3d5b
11 changed files with 187 additions and 30 deletions
|
|
@ -4,25 +4,32 @@
|
|||
import {getRandomId} from '@e2e-support/util';
|
||||
import {Channel, ChannelType} from '@mattermost/types/channels';
|
||||
|
||||
export function createRandomChannel(
|
||||
teamId: string,
|
||||
name: string,
|
||||
displayName: string,
|
||||
type: ChannelType = 'O',
|
||||
purpose = '',
|
||||
header = '',
|
||||
unique = true
|
||||
): Channel {
|
||||
const randomSuffix = getRandomId();
|
||||
type ChannelInput = {
|
||||
teamId: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
type?: ChannelType;
|
||||
purpose?: string;
|
||||
header?: string;
|
||||
unique?: boolean;
|
||||
};
|
||||
|
||||
export function createRandomChannel(channelInput: ChannelInput): Channel {
|
||||
const channel = {
|
||||
team_id: teamId,
|
||||
name: unique ? `${name}-${randomSuffix}` : name,
|
||||
display_name: unique ? `${displayName} ${randomSuffix}` : displayName,
|
||||
type,
|
||||
purpose,
|
||||
header,
|
||||
team_id: channelInput.teamId,
|
||||
name: channelInput.name,
|
||||
display_name: channelInput.displayName,
|
||||
type: channelInput.type || 'O',
|
||||
purpose: channelInput.type || '',
|
||||
header: channelInput.type || '',
|
||||
};
|
||||
|
||||
if (channelInput.unique) {
|
||||
const randomSuffix = getRandomId();
|
||||
|
||||
channel.name = `${channelInput.name}-${randomSuffix}`;
|
||||
channel.display_name = `${channelInput.displayName} ${randomSuffix}`;
|
||||
}
|
||||
|
||||
return channel as Channel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import {test as base, Browser} from '@playwright/test';
|
||||
import {test as base, Browser, ViewportSize} from '@playwright/test';
|
||||
|
||||
import {TestBrowser} from './browser_context';
|
||||
import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, shouldSkipInSmallScreen, shouldRunInLinux} from './flag';
|
||||
import {initSetup, getAdminClient} from './server';
|
||||
import {isSmallScreen} from './util';
|
||||
import {hideDynamicChannelsContent, waitForAnimationEnd, waitUntil} from './test_action';
|
||||
import {pages} from './ui/pages';
|
||||
import {matchSnapshot} from './visual';
|
||||
|
|
@ -15,8 +16,8 @@ type ExtendedFixtures = {
|
|||
};
|
||||
|
||||
export const test = base.extend<ExtendedFixtures>({
|
||||
pw: async ({browser}, use) => {
|
||||
const pw = new PlaywrightExtended(browser);
|
||||
pw: async ({browser, viewport}, use) => {
|
||||
const pw = new PlaywrightExtended(browser, viewport);
|
||||
await use(pw);
|
||||
await pw.testBrowser.close();
|
||||
},
|
||||
|
|
@ -48,10 +49,13 @@ class PlaywrightExtended {
|
|||
// ./ui/pages
|
||||
readonly pages;
|
||||
|
||||
// ./util
|
||||
readonly isSmallScreen;
|
||||
|
||||
// ./visual
|
||||
readonly matchSnapshot;
|
||||
|
||||
constructor(browser: Browser) {
|
||||
constructor(browser: Browser, viewport: ViewportSize | null) {
|
||||
// ./browser_context
|
||||
this.testBrowser = new TestBrowser(browser);
|
||||
|
||||
|
|
@ -73,6 +77,9 @@ class PlaywrightExtended {
|
|||
// ./ui/pages
|
||||
this.pages = pages;
|
||||
|
||||
// ./util
|
||||
this.isSmallScreen = () => isSmallScreen(viewport);
|
||||
|
||||
// ./visual
|
||||
this.matchSnapshot = matchSnapshot;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
export default class FindChannelsModal {
|
||||
readonly container: Locator;
|
||||
readonly input;
|
||||
readonly searchList;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
this.input = container.getByRole('textbox', {name: 'quick switch input'});
|
||||
this.searchList = container.locator('.suggestion-list__item');
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
export {FindChannelsModal};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
export default class ChannelsHeaderMobile {
|
||||
readonly container: Locator;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
async toggleSidebar() {
|
||||
await this.container.getByRole('button', {name: 'Toggle sidebar Menu Icon'}).click();
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
export {ChannelsHeaderMobile};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
export default class ChannelsSidebarLeft {
|
||||
readonly container: Locator;
|
||||
readonly findChannelButton;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
this.findChannelButton = container.getByRole('button', {name: 'Find Channels'});
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
export {ChannelsSidebarLeft};
|
||||
|
|
@ -3,19 +3,25 @@
|
|||
|
||||
import {BoardsSidebar} from './boards/sidebar';
|
||||
import {ChannelsHeader} from './channels/header';
|
||||
import {ChannelsHeaderMobile} from './channels/header_mobile';
|
||||
import {ChannelsAppBar} from './channels/app_bar';
|
||||
import {ChannelsPostCreate} from './channels/post_create';
|
||||
import {ChannelsPost} from './channels/post';
|
||||
import {ChannelsSidebarLeft} from './channels/sidebar_left';
|
||||
import {ChannelsSidebarRight} from './channels/sidebar_right';
|
||||
import {FindChannelsModal} from './channels/find_channels_modal';
|
||||
import {GlobalHeader} from './global_header';
|
||||
|
||||
const components = {
|
||||
BoardsSidebar,
|
||||
ChannelsAppBar,
|
||||
ChannelsHeader,
|
||||
ChannelsHeaderMobile,
|
||||
ChannelsPostCreate,
|
||||
ChannelsPost,
|
||||
ChannelsSidebarLeft,
|
||||
ChannelsSidebarRight,
|
||||
FindChannelsModal,
|
||||
GlobalHeader,
|
||||
};
|
||||
|
||||
|
|
@ -24,8 +30,11 @@ export {
|
|||
BoardsSidebar,
|
||||
ChannelsAppBar,
|
||||
ChannelsHeader,
|
||||
ChannelsHeaderMobile,
|
||||
ChannelsPostCreate,
|
||||
ChannelsPost,
|
||||
ChannelsSidebarLeft,
|
||||
ChannelsSidebarRight,
|
||||
FindChannelsModal,
|
||||
GlobalHeader,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,17 +11,23 @@ export default class ChannelsPage {
|
|||
readonly channels = 'Channels';
|
||||
readonly page: Page;
|
||||
readonly postCreate;
|
||||
readonly findChannelsModal;
|
||||
readonly globalHeader;
|
||||
readonly header;
|
||||
readonly headerMobile;
|
||||
readonly appBar;
|
||||
readonly sidebarLeft;
|
||||
readonly sidebarRight;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.postCreate = new components.ChannelsPostCreate(page.locator('#post-create'));
|
||||
this.findChannelsModal = new components.FindChannelsModal(page.getByRole('dialog', {name: 'Find Channels'}));
|
||||
this.globalHeader = new components.GlobalHeader(page.locator('#global-header'));
|
||||
this.header = new components.ChannelsHeader(page.locator('.channel-header'));
|
||||
this.headerMobile = new components.ChannelsHeaderMobile(page.locator('.navbar'));
|
||||
this.appBar = new components.ChannelsAppBar(page.locator('.app-bar'));
|
||||
this.sidebarLeft = new components.ChannelsSidebarLeft(page.locator('#SidebarContainer'));
|
||||
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@e2e-support/test_fixture';
|
||||
import {createRandomChannel} from '@e2e-support/server';
|
||||
|
||||
test('MM-T5424 Find channel search returns only 50 results when there are more than 50 channels with similar names', async ({
|
||||
pw,
|
||||
pages,
|
||||
}) => {
|
||||
const {adminClient, user, team} = await pw.initSetup();
|
||||
|
||||
const commonName = 'test_channel';
|
||||
|
||||
// # Create more than 50 channels with similar names
|
||||
const channelsRes = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
let suffix = i.toString();
|
||||
if (i < 10) {
|
||||
suffix = `0${i}`;
|
||||
}
|
||||
const channel = createRandomChannel({
|
||||
teamId: team.id,
|
||||
name: `${commonName}_${suffix}`,
|
||||
displayName: `Test Channel ${suffix}`,
|
||||
});
|
||||
channelsRes.push(adminClient.createChannel(channel));
|
||||
}
|
||||
await Promise.all(channelsRes);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit a default channel page
|
||||
const channelsPage = new pages.ChannelsPage(page);
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on "Find channel" and type "test_channel"
|
||||
if (pw.isSmallScreen()) {
|
||||
await channelsPage.headerMobile.toggleSidebar();
|
||||
}
|
||||
await channelsPage.sidebarLeft.findChannelButton.click();
|
||||
|
||||
await channelsPage.findChannelsModal.toBeVisible();
|
||||
await channelsPage.findChannelsModal.input.fill(commonName);
|
||||
|
||||
const limitCount = 50;
|
||||
|
||||
// # Only 50 results for similar name should be displayed.
|
||||
await expect(channelsPage.findChannelsModal.searchList).toHaveCount(limitCount);
|
||||
|
||||
for (let i = 0; i < limitCount; i++) {
|
||||
let suffix = i.toString();
|
||||
if (i < 10) {
|
||||
suffix = `0${i}`;
|
||||
}
|
||||
|
||||
await expect(channelsPage.findChannelsModal.container.getByTestId(`${commonName}_${suffix}`)).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@e2e-support/test_fixture';
|
||||
import {isSmallScreen} from '@e2e-support/util';
|
||||
|
||||
test('Intro to channel as regular user', async ({pw, pages, browserName, viewport}, testInfo) => {
|
||||
// Create and sign in a new user
|
||||
|
|
@ -23,7 +22,7 @@ test('Intro to channel as regular user', async ({pw, pages, browserName, viewpor
|
|||
// await wait(duration.one_sec);
|
||||
|
||||
// Wait for Playbooks icon to be loaded in App bar, except in iphone
|
||||
if (!isSmallScreen(viewport)) {
|
||||
if (!pw.isSmallScreen()) {
|
||||
await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
exports[`components/QuickSwitchModal should match snapshot 1`] = `
|
||||
<Modal
|
||||
animation={false}
|
||||
aria-describedby="quickSwitchHeader"
|
||||
aria-labelledby="quickSwitchModalLabel"
|
||||
aria-describedby="quickSwitchHeaderWithHint"
|
||||
aria-labelledby="quickSwitchHeader"
|
||||
autoFocus={true}
|
||||
backdrop={true}
|
||||
bsClass="modal"
|
||||
|
|
@ -42,9 +42,11 @@ exports[`components/QuickSwitchModal should match snapshot 1`] = `
|
|||
>
|
||||
<div
|
||||
className="channel-switcher__header"
|
||||
id="quickSwitchHeader"
|
||||
id="quickSwitchHeaderWithHint"
|
||||
>
|
||||
<h1>
|
||||
<h1
|
||||
id="quickSwitchHeader"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Find Channels"
|
||||
id="quick_switch_modal.switchChannels"
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ export default class QuickSwitchModal extends React.PureComponent<Props, State>
|
|||
const providers: SwitchChannelProvider[] = this.channelProviders;
|
||||
|
||||
const header = (
|
||||
<h1>
|
||||
<h1 id='quickSwitchHeader'>
|
||||
<FormattedMessage
|
||||
id='quick_switch_modal.switchChannels'
|
||||
defaultMessage='Find Channels'
|
||||
|
|
@ -190,8 +190,8 @@ export default class QuickSwitchModal extends React.PureComponent<Props, State>
|
|||
enforceFocus={false}
|
||||
restoreFocus={false}
|
||||
role='dialog'
|
||||
aria-labelledby='quickSwitchModalLabel'
|
||||
aria-describedby='quickSwitchHeader'
|
||||
aria-labelledby='quickSwitchHeader'
|
||||
aria-describedby='quickSwitchHeaderWithHint'
|
||||
animation={false}
|
||||
>
|
||||
<Modal.Header
|
||||
|
|
@ -201,7 +201,7 @@ export default class QuickSwitchModal extends React.PureComponent<Props, State>
|
|||
<Modal.Body>
|
||||
<div
|
||||
className='channel-switcher__header'
|
||||
id='quickSwitchHeader'
|
||||
id='quickSwitchHeaderWithHint'
|
||||
>
|
||||
{header}
|
||||
<div
|
||||
|
|
|
|||
Loading…
Reference in a new issue