playwright: restructure page and components model (#22602)

Этот коммит содержится в:
Saturnino Abril
2023-03-23 19:01:26 +08:00
коммит произвёл GitHub
родитель 19c7c6fa7a
Коммит 82c9d647b8
28 изменённых файлов: 327 добавлений и 137 удалений

Просмотреть файл

@@ -1,2 +1,3 @@
results
node_modules
playwright-report
test-results

Просмотреть файл

@@ -2,18 +2,10 @@
// See LICENSE.txt for license information.
import {expect} from '@playwright/test';
import {AdminConfig} from '@mattermost/types/config';
import {UserProfile} from '@mattermost/types/users';
import {PluginsResponse} from '@mattermost/types/plugins';
import {
Client,
createRandomTeam,
getAdminClient,
getDefaultAdminUser,
getOnPremServerConfig,
makeClient,
} from './support/server';
import {Client, createRandomTeam, getAdminClient, getDefaultAdminUser, makeClient} from './support/server';
import {boardsPluginId, callsPluginId} from './support/constant';
import {defaultTeam} from './support/util';
import testConfig from './test.config';
@@ -43,9 +35,6 @@ async function sysadminSetup(client: Client, user: UserProfile | null) {
await client.verifyUserEmail(client.token);
}
// Update default server config
const adminConfig = await client.updateConfig(getOnPremServerConfig());
// Log license and config info
await printLicenseInfo(client);
await printClientInfo(client);
@@ -75,9 +64,6 @@ async function sysadminSetup(client: Client, user: UserProfile | null) {
);
}
// Log boards product status
printBoardsProductStatus(adminConfig);
// Ensure all products as plugin are installed and active.
await ensurePluginsLoaded(client);
@@ -110,9 +96,6 @@ async function printClientInfo(client: Client) {
- BuildHash = ${config.BuildHash}
- BuildHashEnterprise = ${config.BuildHashEnterprise}
- BuildEnterpriseReady = ${config.BuildEnterpriseReady}
- BuildHashBoards = ${config.BuildHashBoards}
- BuildBoards = ${config.BuildBoards}
- BuildHashPlaybooks = ${config.BuildHashPlaybooks}
- FeatureFlagAppsEnabled = ${config.FeatureFlagAppsEnabled}
- FeatureFlagBoardsProduct = ${config.FeatureFlagBoardsProduct}
- FeatureFlagCallsEnabled = ${config.FeatureFlagCallsEnabled}
@@ -120,10 +103,10 @@ async function printClientInfo(client: Client) {
}
function getProductsAsPlugin() {
const productsAsPlugin = ['com.mattermost.calls', 'playbooks'];
const productsAsPlugin = [callsPluginId];
if (!testConfig.boardsProductEnabled) {
productsAsPlugin.push('focalboard');
productsAsPlugin.push(boardsPluginId);
}
return productsAsPlugin;
@@ -131,7 +114,7 @@ function getProductsAsPlugin() {
async function ensurePluginsLoaded(client: Client) {
const pluginStatus = await client.getPluginStatuses();
const plugins = (await client.getPlugins()) as PluginsResponse;
const plugins = await client.getPlugins();
getProductsAsPlugin().forEach(async (pluginId) => {
const isInstalled = pluginStatus.some((plugin) => plugin.plugin_id === pluginId);
@@ -154,19 +137,8 @@ async function ensurePluginsLoaded(client: Client) {
});
}
function printBoardsProductStatus(config: AdminConfig) {
// Ensure boards as product is enabled
if (!config.FeatureFlags.BoardsProduct) {
// eslint-disable-next-line no-console
console.log('FeatureFlags.BoardsProduct is disabled. Related visual test will fail.');
} else {
// eslint-disable-next-line no-console
console.log('FeatureFlags.BoardsProduct is enabled.');
}
}
async function printPluginDetails(client: Client) {
const plugins = (await client.getPlugins()) as PluginsResponse;
const plugins = await client.getPlugins();
if (plugins.active.length) {
// eslint-disable-next-line no-console

15
e2e/playwright/package-lock.json сгенерированный
Просмотреть файл

@@ -9,6 +9,7 @@
"@percy/cli": "1.18.0",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.30.0",
"async-wait-until": "2.0.12",
"chalk": "4.1.2",
"deepmerge": "4.3.0",
"dotenv": "16.0.3",
@@ -740,6 +741,15 @@
"node": ">=8"
}
},
"node_modules/async-wait-until": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/async-wait-until/-/async-wait-until-2.0.12.tgz",
"integrity": "sha512-SXy/vDs6UPJMG6YeEYOQ4ilA/JnGxk187KPGqFx9O+qVxsjkSl+jH+3P50qSNyMpEmDgr8qOFGOKCJckWb1i7A==",
"engines": {
"node": ">= 0.12.0",
"npm": ">= 1.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2861,6 +2871,11 @@
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"async-wait-until": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/async-wait-until/-/async-wait-until-2.0.12.tgz",
"integrity": "sha512-SXy/vDs6UPJMG6YeEYOQ4ilA/JnGxk187KPGqFx9O+qVxsjkSl+jH+3P50qSNyMpEmDgr8qOFGOKCJckWb1i7A=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",

Просмотреть файл

@@ -14,6 +14,7 @@
"@percy/cli": "1.18.0",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.30.0",
"async-wait-until": "2.0.12",
"chalk": "4.1.2",
"deepmerge": "4.3.0",
"dotenv": "16.0.3",

Просмотреть файл

@@ -24,9 +24,16 @@ export default defineConfig({
},
use: {
baseURL: testConfig.baseURL,
ignoreHTTPSErrors: true,
headless: testConfig.headless,
locale: 'en-US',
launchOptions: {
args: ['--use-fake-device-for-media-stream', '--use-fake-ui-for-media-stream'],
firefoxUserPrefs: {
'media.navigator.streams.fake': true,
'permissions.default.microphone': 1,
'permissions.default.camera': 1,
},
slowMo: testConfig.slowMo,
},
screenshot: 'only-on-failure',

56
e2e/playwright/sample.env Обычный файл
Просмотреть файл

@@ -0,0 +1,56 @@
# 1. PW_BASE_URL
# - Default to "http://localhost:8065" if not set.
# - When running test inside the Playwright docker image
# PW_BASE_URL=http://host.docker.internal:8065
# 2. NODE_TLS_REJECT_UNAUTHORIZED
# This node environment variable disables the verification of local certificates. Set for local testing only.
# NODE_TLS_REJECT_UNAUTHORIZED=0
# 3. PW_ADMIN_USERNAME
# - Default to "sysadmin" if not set.
# 4. PW_ADMIN_PASSWORD
# - Default to "Sys@dmin-sample1" if not set.
# 5. PW_ADMIN_EMAIL
# - Default to "sysadmin@sample.mattermost.com" if not set.
# 6. PW_BOARDS_PRODUCT_ENABLED
# - Default to "true" if not set. Used to correctly set server config.
# 7. PW_HA_CLUSTER_ENABLED
# - Default to "false" if not set. Set to true if the test server is with HA enabled.
# 8. PW_HA_CLUSTER_NODE_COUNT
# - Default to "2" if not set.
# 9. PW_HA_CLUSTER_NAME
# - Default to "mm_dev_cluster" if not set.
# 10. PW_RESET_BEFORE_TEST
# - Default to "false" if not set. If true, the setup deletes all teams and channels other than the default team which is "ad-1".
# 11. CI
# - Default to "false" if not set.
# 12. PW_HEADLESS
# - Default to "true" if not set. Set to false to run test in head mode.
# 13. PW_SLOWMO
# - Default to "0" if not set which means normal test speed run. Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
# 14. PW_WORKERS
# - Default to "1" if not set. The maximum number of concurrent worker processes to use for parallelizing tests.
# 15. PW_SNAPSHOT_ENABLE
# - Default to "false" if not set. Set to true to enable snapshot testing.
# Note that, snapshot testing should be done in Playwright docker image only.
# This is to ensure that, there's a common base platform for all contributors
# regardless of each local development platform.
# 16. PW_PERCY_ENABLE
# - Default to "false" if not set. Use to save and compare results via https://percy.io/.
# 17. PERCY_TOKEN
# - A token required by https://percy.io/.

Просмотреть файл

@@ -15,12 +15,16 @@ export class TestBrowser {
this.browser = browser;
}
async login(user: UserProfile) {
// Log in via API request and save user storage
const storagePath = await loginByAPI(user.username, user.password);
async login(user: UserProfile | null) {
const options = {storageState: ''};
if (user) {
// Log in via API request and save user storage
const storagePath = await loginByAPI(user.username, user.password);
options.storageState = storagePath;
}
// Sign in a user in new browser context
const context = await this.browser.newContext({storageState: storagePath});
const context = await this.browser.newContext(options);
const page = await context.newPage();
return {context, page};

7
e2e/playwright/support/constant.ts Обычный файл
Просмотреть файл

@@ -0,0 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const boardsPluginId = 'focalboard';
export const boardsProductId = 'boards';
export const callsPluginId = 'com.mattermost.calls';
export const playbooksPluginId = 'playbooks';

Просмотреть файл

@@ -5,6 +5,7 @@ import os from 'node:os';
import {expect, test} from '@playwright/test';
import {boardsPluginId, callsPluginId} from './constant';
import {getAdminClient} from './server/init';
import {isSmallScreen} from './util';
@@ -14,12 +15,22 @@ export async function shouldHaveBoardsEnabled(enabled = true) {
const boardsEnabled =
(typeof config.FeatureFlags.BoardsProduct === 'boolean' && config.FeatureFlags.BoardsProduct) ||
config.PluginSettings.PluginStates['focalboard'].Enable;
config.PluginSettings.PluginStates[boardsPluginId].Enable;
const matched = boardsEnabled === enabled;
expect(matched, matched ? '' : `Boards expect "${enabled}" but actual "${boardsEnabled}"`).toBeTruthy();
}
export async function shouldHaveCallsEnabled(enabled = true) {
const {adminClient} = await getAdminClient();
const config = await adminClient.getConfig();
const callsEnabled = config.PluginSettings.PluginStates[callsPluginId].Enable;
const matched = callsEnabled === enabled;
expect(matched, matched ? '' : `Calls expect "${enabled}" but actual "${callsEnabled}"`).toBeTruthy();
}
export async function shouldHaveFeatureFlag(name: string, value: string | boolean) {
const {adminClient} = await getAdminClient();
const config = await adminClient.getConfig();

Просмотреть файл

@@ -73,8 +73,8 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
};
};
// Should be based only from the generated default config from mattermost-server via "make config-reset"
// Based on v7.9 server
// Should be based only from the generated default config from ./server via "make config-reset"
// Based on v7.10 server
const defaultServerConfig: AdminConfig = {
ServiceSettings: {
SiteURL: '',
@@ -180,9 +180,6 @@ const defaultServerConfig: AdminConfig = {
EnableCustomGroups: true,
SelfHostedPurchase: true,
AllowSyncedDrafts: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
SelfHostedExpansion: false,
},
TeamSettings: {
@@ -636,7 +633,7 @@ const defaultServerConfig: AdminConfig = {
Enable: true,
},
focalboard: {
Enable: false,
Enable: true,
},
playbooks: {
Enable: true,
@@ -689,13 +686,12 @@ const defaultServerConfig: AdminConfig = {
GraphQL: false,
InsightsEnabled: true,
CommandPalette: false,
BoardsProduct: true,
BoardsProduct: false,
SendWelcomePost: true,
WorkTemplate: false,
PostPriority: true,
WysiwygEditor: false,
PeopleProduct: false,
AnnualSubscription: false,
ReduceOnBoardingTaskList: false,
OnboardingAutoShowLinkedBoard: true,
ThreadsEverywhere: false,

Просмотреть файл

@@ -3,6 +3,8 @@
import {Locator, Page} from '@playwright/test';
export {waitUntil} from 'async-wait-until';
const visibilityHidden = 'visibility: hidden !important;';
const hideTeamHeader = `.test-team-header {${visibilityHidden}} `;
const hidePostHeaderTime = `.post__time {${visibilityHidden}} `;

Просмотреть файл

@@ -1,9 +1,15 @@
import {test as base, Browser} from '@playwright/test';
import {TestBrowser} from './browser_context';
import {shouldHaveBoardsEnabled, shouldHaveFeatureFlag, shouldSkipInSmallScreen, shouldRunInLinux} from './flag';
import {
shouldHaveBoardsEnabled,
shouldHaveCallsEnabled,
shouldHaveFeatureFlag,
shouldSkipInSmallScreen,
shouldRunInLinux,
} from './flag';
import {initSetup, getAdminClient} from './server';
import {hideDynamicChannelsContent, waitForAnimationEnd} from './test_action';
import {hideDynamicChannelsContent, waitForAnimationEnd, waitUntil} from './test_action';
import {pages} from './ui/pages';
import {matchSnapshot} from './visual';
@@ -30,24 +36,26 @@ class PlaywrightExtended {
readonly testBrowser: TestBrowser;
// ./flag
readonly shouldHaveBoardsEnabled: typeof shouldHaveBoardsEnabled;
readonly shouldHaveFeatureFlag: typeof shouldHaveFeatureFlag;
readonly shouldSkipInSmallScreen: typeof shouldSkipInSmallScreen;
readonly shouldRunInLinux: typeof shouldRunInLinux;
readonly shouldHaveBoardsEnabled;
readonly shouldHaveCallsEnabled;
readonly shouldHaveFeatureFlag;
readonly shouldSkipInSmallScreen;
readonly shouldRunInLinux;
// ./server
readonly getAdminClient: typeof getAdminClient;
readonly initSetup: typeof initSetup;
readonly getAdminClient;
readonly initSetup;
// ./test_action
readonly hideDynamicChannelsContent: typeof hideDynamicChannelsContent;
readonly waitForAnimationEnd: typeof waitForAnimationEnd;
readonly hideDynamicChannelsContent;
readonly waitForAnimationEnd;
readonly waitUntil;
// ./ui/pages
readonly pages: typeof pages;
readonly pages;
// ./visual
readonly matchSnapshot: typeof matchSnapshot;
readonly matchSnapshot;
constructor(browser: Browser) {
// ./browser_context
@@ -55,6 +63,7 @@ class PlaywrightExtended {
// ./flag
this.shouldHaveBoardsEnabled = shouldHaveBoardsEnabled;
this.shouldHaveCallsEnabled = shouldHaveCallsEnabled;
this.shouldHaveFeatureFlag = shouldHaveFeatureFlag;
this.shouldSkipInSmallScreen = shouldSkipInSmallScreen;
this.shouldRunInLinux = shouldRunInLinux;
@@ -66,6 +75,7 @@ class PlaywrightExtended {
// ./test_action
this.hideDynamicChannelsContent = hideDynamicChannelsContent;
this.waitForAnimationEnd = waitForAnimationEnd;
this.waitUntil = waitUntil;
// ./ui/pages
this.pages = pages;

Просмотреть файл

@@ -4,22 +4,23 @@
import {expect, Locator} from '@playwright/test';
export default class BoardsCreateModal {
readonly locator: Locator;
readonly productSwitchMenu: Locator;
readonly container: Locator;
constructor(locator: Locator) {
this.locator = locator;
readonly productSwitchMenu;
this.productSwitchMenu = locator.getByRole('button', {name: 'Product switch menu'});
constructor(container: Locator) {
this.container = container;
this.productSwitchMenu = container.getByRole('button', {name: 'Product switch menu'});
}
async switchProduct(name: string) {
await this.productSwitchMenu.click();
await this.locator.getByRole('link', {name: `${name}`}).click();
await this.container.getByRole('link', {name: `${name}`}).click();
}
async toBeVisible(name: string) {
await expect(this.locator.getByRole('heading', {name})).toBeVisible();
await expect(this.container.getByRole('heading', {name})).toBeVisible();
}
}

Просмотреть файл

@@ -5,10 +5,11 @@ import {Locator} from '@playwright/test';
export default class BoardsSidebar {
readonly container: Locator;
readonly plusButton: Locator;
readonly createNewBoardMenuItem: Locator;
readonly createNewCategoryMenuItem: Locator;
readonly titles: Locator;
readonly plusButton;
readonly createNewBoardMenuItem;
readonly createNewCategoryMenuItem;
readonly titles;
constructor(container: Locator) {
this.container = container;

Просмотреть файл

@@ -5,7 +5,8 @@ import {expect, Locator} from '@playwright/test';
export default class ChannelsAppBar {
readonly container: Locator;
readonly playbooksIcon: Locator;
readonly playbooksIcon;
constructor(container: Locator) {
this.container = container;

Просмотреть файл

@@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator} from '@playwright/test';
export default class ChannelsHeader {
readonly container: Locator;
constructor(container: Locator) {
this.container = container;
}
async toBeVisible() {
await expect(this.container).toBeVisible();
}
}
export {ChannelsHeader};

Просмотреть файл

@@ -5,12 +5,17 @@ import {expect, Locator} from '@playwright/test';
export default class ChannelsPost {
readonly container: Locator;
readonly profileIcon: Locator;
readonly body;
readonly profileIcon;
readonly replyButton;
constructor(container: Locator) {
this.container = container;
this.body = container.locator('.post__body');
this.profileIcon = container.locator('.profile-icon');
this.replyButton = container.getByRole('button', {name: 'reply'});
}
async toBeVisible() {
@@ -26,6 +31,12 @@ export default class ChannelsPost {
async getProfileImage(username: string) {
return await this.profileIcon.getByAltText(`${username} profile image`);
}
async openRHS() {
await this.container.hover();
await this.replyButton.waitFor();
await this.replyButton.click();
}
}
export {ChannelsPost};

Просмотреть файл

@@ -5,9 +5,10 @@ import {expect, Locator} from '@playwright/test';
export default class ChannelsPostCreate {
readonly container: Locator;
readonly input: Locator;
readonly attachmentButton: Locator;
readonly emojiButton: Locator;
readonly input;
readonly attachmentButton;
readonly emojiButton;
constructor(container: Locator) {
this.container = container;

Просмотреть файл

@@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator} from '@playwright/test';
export default class ChannelsSidebarRight {
readonly container: Locator;
constructor(container: Locator) {
this.container = container;
}
async toBeVisible() {
await expect(this.container).toBeVisible();
}
}
export {ChannelsSidebarRight};

Просмотреть файл

@@ -5,7 +5,8 @@ import {expect, Locator} from '@playwright/test';
export default class GlobalHeader {
readonly container: Locator;
readonly productSwitchMenu: Locator;
readonly productSwitchMenu;
constructor(container: Locator) {
this.container = container;

Просмотреть файл

@@ -2,17 +2,30 @@
// See LICENSE.txt for license information.
import {BoardsSidebar} from './boards/sidebar';
import {ChannelsHeader} from './channels/header';
import {ChannelsAppBar} from './channels/app_bar';
import {ChannelsPostCreate} from './channels/post_create';
import {ChannelsPost} from './channels/post';
import {ChannelsSidebarRight} from './channels/sidebar_right';
import {GlobalHeader} from './global_header';
const components = {
BoardsSidebar,
ChannelsAppBar,
ChannelsHeader,
ChannelsPostCreate,
ChannelsPost,
ChannelsSidebarRight,
GlobalHeader,
};
export {components, BoardsSidebar, ChannelsAppBar, ChannelsPostCreate, ChannelsPost, GlobalHeader};
export {
components,
BoardsSidebar,
ChannelsAppBar,
ChannelsHeader,
ChannelsPostCreate,
ChannelsPost,
ChannelsSidebarRight,
GlobalHeader,
};

Просмотреть файл

@@ -1,21 +1,23 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator, Page} from '@playwright/test';
import {expect, Page} from '@playwright/test';
import {GlobalHeader} from '@e2e-support/ui/components';
import {components} from '@e2e-support/ui/components';
export default class BoardsCreatePage {
readonly boards = 'Boards';
readonly page: Page;
readonly globalHeader: GlobalHeader;
readonly createBoardHeading: Locator;
readonly createEmptyBoardButton: Locator;
readonly useTemplateButton: Locator;
readonly globalHeader;
readonly createBoardHeading;
readonly createEmptyBoardButton;
readonly useTemplateButton;
constructor(page: Page) {
this.page = page;
this.globalHeader = new GlobalHeader(this.page.locator('#global-header'));
this.globalHeader = new components.GlobalHeader(this.page.locator('#global-header'));
this.createBoardHeading = page.getByRole('heading', {name: 'Create a board'});
this.createEmptyBoardButton = page.getByRole('button', {name: ' Create an empty board'});
this.useTemplateButton = page.getByRole('button', {name: 'Use this template'});

Просмотреть файл

@@ -1,23 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator, Page} from '@playwright/test';
import {expect, Page} from '@playwright/test';
import {BoardsSidebar, GlobalHeader} from '@e2e-support/ui/components';
import {components} from '@e2e-support/ui/components';
export default class BoardsViewPage {
readonly boards = 'Boards';
readonly page: Page;
readonly sidebar: BoardsSidebar;
readonly globalHeader: GlobalHeader;
readonly topHead: Locator;
readonly editableTitle: Locator;
readonly shareButton: Locator;
readonly sidebar;
readonly globalHeader;
readonly topHead;
readonly editableTitle;
readonly shareButton;
constructor(page: Page) {
this.page = page;
this.sidebar = new BoardsSidebar(page.locator('.octo-sidebar'));
this.globalHeader = new GlobalHeader(this.page.locator('#global-header'));
this.sidebar = new components.BoardsSidebar(page.locator('.octo-sidebar'));
this.globalHeader = new components.GlobalHeader(this.page.locator('#global-header'));
this.topHead = page.locator('.top-head');
this.editableTitle = this.topHead.getByPlaceholder('Untitled board');
this.shareButton = page.getByRole('button', {name: '󰍁 Share'});

Просмотреть файл

@@ -3,27 +3,32 @@
import {Page} from '@playwright/test';
import {ChannelsAppBar, ChannelsPost, ChannelsPostCreate, GlobalHeader} from '@e2e-support/ui/components';
import {isSmallScreen} from '@e2e-support/util';
import {waitUntil} from '@e2e-support/test_action';
import {components} from '@e2e-support/ui/components';
import {duration, isSmallScreen} from '@e2e-support/util';
export default class ChannelsPage {
readonly channels = 'Channels';
readonly page: Page;
readonly postCreate: ChannelsPostCreate;
readonly globalHeader: GlobalHeader;
readonly appBar: ChannelsAppBar;
readonly postCreate;
readonly globalHeader;
readonly header;
readonly appBar;
readonly sidebarRight;
constructor(page: Page) {
this.page = page;
this.postCreate = new ChannelsPostCreate(page.locator('#post-create'));
this.globalHeader = new GlobalHeader(this.page.locator('#global-header'));
this.appBar = new ChannelsAppBar(page.locator('.app-bar'));
this.postCreate = new components.ChannelsPostCreate(page.locator('#post-create'));
this.globalHeader = new components.GlobalHeader(page.locator('#global-header'));
this.header = new components.ChannelsHeader(page.locator('.channel-header'));
this.appBar = new components.ChannelsAppBar(page.locator('.app-bar'));
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
}
async goto(teamName = '', channelName = '') {
let channelsUrl = '/';
if (teamName) {
channelsUrl += `/${teamName}`;
channelsUrl += `${teamName}`;
if (channelName) {
channelsUrl += `/${channelName}`;
}
@@ -47,25 +52,54 @@ export default class ChannelsPage {
async getFirstPost() {
await this.page.getByTestId('postView').first().waitFor();
const post = await this.page.getByTestId('postView').first();
return new ChannelsPost(post);
return new components.ChannelsPost(post);
}
async getLastPost() {
await this.page.getByTestId('postView').last().waitFor();
const post = await this.page.getByTestId('postView').last();
return new ChannelsPost(post);
return new components.ChannelsPost(post);
}
async getNthPost(index: number) {
await this.page.getByTestId('postView').nth(index).waitFor();
const post = await this.page.getByTestId('postView').nth(index);
return new ChannelsPost(post);
return new components.ChannelsPost(post);
}
async getPostById(id: string) {
await this.page.locator(`[id="post_${id}"]`).waitFor();
const post = await this.page.locator(`[id="post_${id}"]`);
return new ChannelsPost(post);
return new components.ChannelsPost(post);
}
async getRHSPostById(id: string) {
await this.page.locator(`[id="rhsPost_${id}"]`).waitFor();
const post = await this.page.locator(`[id="rhsPost_${id}"]`);
return new components.ChannelsPost(post);
}
async waitUntilLastPostContains(text: string, timeout = duration.ten_sec) {
await waitUntil(
async () => {
const post = await this.getLastPost();
const content = await post.container.textContent();
return content?.includes(text);
},
{timeout}
);
}
async waitUntilPostWithIdContains(id: string, text: string, timeout = duration.ten_sec) {
await waitUntil(
async () => {
const post = await this.getPostById(id);
const content = await post.container.textContent();
return content?.includes(text);
},
{timeout}
);
}
}

Просмотреть файл

@@ -1,14 +1,16 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator, Page} from '@playwright/test';
import {expect, Page} from '@playwright/test';
export default class LandingLoginPage {
readonly page: Page;
readonly isMobile?: boolean;
readonly viewInAppButton: Locator;
readonly viewInDesktopAppButton: Locator;
readonly viewInBrowserButton: Locator;
readonly viewInAppButton;
readonly viewInDesktopAppButton;
readonly viewInBrowserButton;
constructor(page: Page, isMobile?: boolean) {
this.page = page;

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator, Page} from '@playwright/test';
import {expect, Page} from '@playwright/test';
import {AdminConfig} from '@mattermost/types/config';
import {UserProfile} from '@mattermost/types/users';
@@ -10,18 +10,19 @@ export default class LoginPage {
readonly adminConfig: AdminConfig;
readonly page: Page;
readonly title: Locator;
readonly subtitle: Locator;
readonly bodyCard: Locator;
readonly loginInput: Locator;
readonly loginPlaceholder: Locator;
readonly passwordInput: Locator;
readonly signInButton: Locator;
readonly createAccountLink: Locator;
readonly forgotPasswordLink: Locator;
readonly userErrorLabel: Locator;
readonly fieldWithError: Locator;
readonly formContainer: Locator;
readonly title;
readonly subtitle;
readonly bodyCard;
readonly loginInput;
readonly loginPlaceholder;
readonly passwordInput;
readonly signInButton;
readonly createAccountLink;
readonly forgotPasswordLink;
readonly userErrorLabel;
readonly fieldWithError;
readonly formContainer;
constructor(page: Page, adminConfig: AdminConfig) {
this.page = page;

Просмотреть файл

@@ -1,23 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator, Page} from '@playwright/test';
import {expect, Page} from '@playwright/test';
import {duration, wait} from '@e2e-support/util';
export default class SignupPage {
readonly page: Page;
readonly title: Locator;
readonly subtitle: Locator;
readonly bodyCard: Locator;
readonly emailInput: Locator;
readonly usernameInput: Locator;
readonly passwordInput: Locator;
readonly createAccountButton: Locator;
readonly loginLink: Locator;
readonly emailError: Locator;
readonly usernameError: Locator;
readonly passwordError: Locator;
readonly title;
readonly subtitle;
readonly bodyCard;
readonly emailInput;
readonly usernameInput;
readonly passwordInput;
readonly createAccountButton;
readonly loginLink;
readonly emailError;
readonly usernameError;
readonly passwordError;
constructor(page: Page) {
this.page = page;

Просмотреть файл

@@ -13,5 +13,6 @@
"@e2e-test.config": ["test.config.ts"]
}
},
"include": ["./**/*"]
"include": ["./**/*"],
"exclude": ["playwright-report"]
}