CLD-5948 Playwright/E2E: Update dependencies, server default config and its types, and remove Boards and mobile view tests (#24583)

* update dependencies

* update dependencies

* remove mobile view

* remove boards

* update default config  and its types

* update snapshots

* fix formatting

* check works and fix styling
Этот коммит содержится в:
Saturnino Abril
2023-09-20 05:28:35 +08:00
коммит произвёл GitHub
родитель 88d043a971
Коммит b7b08dbc0f
63 изменённых файлов: 657 добавлений и 834 удалений

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

@@ -2,6 +2,5 @@
// See LICENSE.txt for license information.
export const appsPluginId = 'com.mattermost.apps';
export const boardsPluginId = 'focalboard';
export const callsPluginId = 'com.mattermost.calls';
export const playbooksPluginId = 'playbooks';

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

@@ -3,11 +3,10 @@
import os from 'node:os';
import {expect, test} from '@playwright/test';
import {expect} from '@playwright/test';
import {callsPluginId} from './constant';
import {getAdminClient} from './server/init';
import {isSmallScreen} from './util';
export async function shouldHaveCallsEnabled(enabled = true) {
const {adminClient} = await getAdminClient();
@@ -26,14 +25,10 @@ export async function shouldHaveFeatureFlag(name: string, value: string | boolea
const matched = config.FeatureFlags[name] === value;
expect(
matched,
matched ? '' : `FeatureFlags["${name}'] expect "${value}" but actual "${config.FeatureFlags[name]}"`
matched ? '' : `FeatureFlags["${name}'] expect "${value}" but actual "${config.FeatureFlags[name]}"`,
).toBeTruthy();
}
export function shouldSkipInSmallScreen() {
test.skip(({viewport}) => isSmallScreen(viewport), 'Not applicable to mobile device');
}
export async function shouldRunInLinux() {
const platform = os.platform();
await expect(platform, 'Run in Linux or Playwright docker image only').toBe('linux');

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

@@ -35,9 +35,6 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
Enable: testConfig.haClusterEnabled,
ClusterName: testConfig.haClusterName,
},
ExperimentalSettings: {
DisableAppBar: false,
},
PasswordSettings: {
MinimumLength: 5,
Lowercase: false,
@@ -48,9 +45,15 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
},
PluginSettings: {
EnableUploads: true,
Plugins: {
PluginStates: {
'com.mattermost.calls': {
defaultenabled: true,
Enable: false,
},
'com.mattermost.nps': {
Enable: false,
},
playbooks: {
Enable: true,
},
},
},
@@ -65,7 +68,7 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
};
// Should be based only from the generated default config from ./server via "make config-reset"
// Based on v7.10 server
// Based on v9.1 server
const defaultServerConfig: AdminConfig = {
ServiceSettings: {
SiteURL: '',
@@ -157,6 +160,11 @@ const defaultServerConfig: AdminConfig = {
EnableLatex: false,
EnableInlineLatex: true,
PostPriority: true,
AllowPersistentNotifications: true,
AllowPersistentNotificationsForGuests: false,
PersistentNotificationIntervalMinutes: 5,
PersistentNotificationMaxCount: 6,
PersistentNotificationMaxRecipients: 5,
EnableAPIChannelDeletion: false,
EnableLocalMode: false,
LocalModeSocketLocation: '/var/tmp/mattermost_local.socket',
@@ -170,15 +178,11 @@ const defaultServerConfig: AdminConfig = {
EnableCustomGroups: true,
SelfHostedPurchase: true,
AllowSyncedDrafts: true,
AllowPersistentNotifications: true,
PersistentNotificationMaxCount: 6,
PersistentNotificationMaxRecipients: 5,
PersistentNotificationIntervalMinutes: 5,
AllowPersistentNotificationsForGuests: false,
},
TeamSettings: {
SiteName: 'Mattermost',
MaxUsersPerTeam: 50,
EnableJoinLeaveMessageByDefault: true,
EnableUserCreation: true,
EnableOpenServer: false,
EnableUserDeactivation: false,
@@ -222,6 +226,7 @@ const defaultServerConfig: AdminConfig = {
DisableDatabaseSearch: false,
MigrationsStatementTimeoutSeconds: 100000,
ReplicaLagSettings: [],
ReplicaMonitorIntervalSeconds: 5,
},
LogSettings: {
EnableConsole: true,
@@ -236,6 +241,7 @@ const defaultServerConfig: AdminConfig = {
EnableDiagnostics: true,
VerboseDiagnostics: false,
EnableSentry: true,
AdvancedLoggingJSON: {},
AdvancedLoggingConfig: '',
},
ExperimentalAuditSettings: {
@@ -246,6 +252,7 @@ const defaultServerConfig: AdminConfig = {
FileMaxBackups: 0,
FileCompress: false,
FileMaxQueueSize: 1000,
AdvancedLoggingJSON: {},
AdvancedLoggingConfig: '',
},
NotificationLogSettings: {
@@ -257,6 +264,7 @@ const defaultServerConfig: AdminConfig = {
FileLevel: 'INFO',
FileJson: true,
FileLocation: '',
AdvancedLoggingJSON: {},
AdvancedLoggingConfig: '',
},
PasswordSettings: {
@@ -292,6 +300,21 @@ const defaultServerConfig: AdminConfig = {
AmazonS3SSE: false,
AmazonS3Trace: false,
AmazonS3RequestTimeoutMilliseconds: 30000,
DedicatedExportStore: false,
ExportDriverName: 'local',
ExportDirectory: './data/',
ExportAmazonS3AccessKeyId: '',
ExportAmazonS3SecretAccessKey: '',
ExportAmazonS3Bucket: '',
ExportAmazonS3PathPrefix: '',
ExportAmazonS3Region: '',
ExportAmazonS3Endpoint: 's3.amazonaws.com',
ExportAmazonS3SSL: true,
ExportAmazonS3SignV2: false,
ExportAmazonS3SSE: false,
ExportAmazonS3Trace: false,
ExportAmazonS3RequestTimeoutMilliseconds: 30000,
ExportAmazonS3PresignExpiresSeconds: 21600,
},
EmailSettings: {
EnableSignUpWithEmail: true,
@@ -343,7 +366,7 @@ const defaultServerConfig: AdminConfig = {
SupportSettings: {
TermsOfServiceLink: 'https://mattermost.com/pl/terms-of-use/',
PrivacyPolicyLink: 'https://mattermost.com/pl/privacy-policy/',
AboutLink: 'https://docs.mattermost.com/pl/about-mattermost',
AboutLink: 'https://mattermost.com/pl/about-mattermost',
HelpLink: 'https://mattermost.com/pl/help/',
ReportAProblemLink: 'https://mattermost.com/pl/report-a-bug',
ForgotPasswordLink: '',
@@ -538,7 +561,7 @@ const defaultServerConfig: AdminConfig = {
UseNewSAMLLibrary: false,
EnableSharedChannels: false,
EnableRemoteClusterService: false,
DisableAppBar: true,
DisableAppBar: false,
DisableRefetchingOnBrowserFocus: false,
DelayChannelAutocomplete: false,
},
@@ -582,10 +605,14 @@ const defaultServerConfig: AdminConfig = {
DataRetentionSettings: {
EnableMessageDeletion: false,
EnableFileDeletion: false,
EnableBoardsDeletion: false,
MessageRetentionDays: 365,
FileRetentionDays: 365,
BoardsRetentionDays: 365,
DeletionJobStartTime: '02:00',
BatchSize: 3000,
TimeBetweenBatchesMilliseconds: 100,
RetentionIdsBatchSize: 100,
},
MessageExportSettings: {
EnableExport: false,
@@ -624,6 +651,9 @@ const defaultServerConfig: AdminConfig = {
'com.mattermost.nps': {
Enable: true,
},
playbooks: {
Enable: true,
},
},
EnableMarketplace: true,
EnableRemoteMarketplace: true,
@@ -635,6 +665,7 @@ const defaultServerConfig: AdminConfig = {
},
DisplaySettings: {
CustomURLSchemes: [],
MaxMarkdownNodes: 0,
ExperimentalTimezone: true,
},
GuestAccountsSettings: {
@@ -653,30 +684,24 @@ const defaultServerConfig: AdminConfig = {
CloudSettings: {
CWSURL: 'https://customers.mattermost.com',
CWSAPIURL: 'https://portal.internal.prod.cloud.mattermost.com',
CWSMock: false,
},
FeatureFlags: {
TestFeature: 'off',
TestBoolFeature: false,
EnableRemoteClusterService: false,
AppsEnabled: true,
PluginPlaybooks: '',
PluginApps: '',
PluginFocalboard: '',
PluginCalls: '',
PermalinkPreviews: true,
PermalinkPreviews: false,
CallsEnabled: true,
BoardsFeatureFlags: '',
NormalizeLdapDNs: false,
GraphQL: false,
CommandPalette: false,
SendWelcomePost: true,
PostPriority: true,
PostPriority: false,
WysiwygEditor: false,
ThreadsEverywhere: false,
OnboardingTourTips: true,
DeprecateCloudFree: false,
CloudReverseTrial: false,
StreamlinedMarketplace: true
EnableExportDirectDownload: false,
StreamlinedMarketplace: true,
},
ImportSettings: {
Directory: './import',

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

@@ -24,7 +24,7 @@ export async function initSetup({
const {adminClient, adminUser} = await getAdminClient();
if (!adminClient) {
throw new Error(
"Failed to setup admin: Check that you're able to access the server using the same admin credential."
"Failed to setup admin: Check that you're able to access the server using the same admin credential.",
);
}
@@ -83,8 +83,8 @@ export async function initSetup({
// eslint-disable-next-line no-console
console.log(
chalk.green(
`This failed due to the experimental fetch support in Node.js starting v18.0.0.\nYou may set environment variable: "export NODE_OPTIONS='--no-experimental-fetch'", then try again.'`
)
`This failed due to the experimental fetch support in Node.js starting v18.0.0.\nYou may set environment variable: "export NODE_OPTIONS='--no-experimental-fetch'", then try again.'`,
),
);
}
expect(err, 'Should not throw an error').toBeFalsy();

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

@@ -16,6 +16,6 @@ export async function hideDynamicChannelsContent(page: Page) {
export async function waitForAnimationEnd(locator: Locator) {
return locator.evaluate((element) =>
Promise.all(element.getAnimations({subtree: true}).map((animation) => animation.finished))
Promise.all(element.getAnimations({subtree: true}).map((animation) => animation.finished)),
);
}

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

@@ -1,11 +1,10 @@
import {test as base, Browser, Page, ViewportSize} from '@playwright/test';
import {test as base, Browser, Page} from '@playwright/test';
import {AxeResults} from 'axe-core';
import AxeBuilder from '@axe-core/playwright';
import {TestBrowser} from './browser_context';
import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, shouldSkipInSmallScreen, shouldRunInLinux} from './flag';
import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, 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';
@@ -29,8 +28,8 @@ export const test = base.extend<ExtendedFixtures>({
const ab = new AxeBuilderExtended();
await use(ab);
},
pw: async ({browser, viewport}, use) => {
const pw = new PlaywrightExtended(browser, viewport);
pw: async ({browser}, use) => {
const pw = new PlaywrightExtended(browser);
await use(pw);
await pw.testBrowser.close();
},
@@ -47,7 +46,6 @@ class PlaywrightExtended {
// ./flag
readonly shouldHaveCallsEnabled;
readonly shouldHaveFeatureFlag;
readonly shouldSkipInSmallScreen;
readonly shouldRunInLinux;
// ./server
@@ -62,20 +60,16 @@ class PlaywrightExtended {
// ./ui/pages
readonly pages;
// ./util
readonly isSmallScreen;
// ./visual
readonly matchSnapshot;
constructor(browser: Browser, viewport: ViewportSize | null) {
constructor(browser: Browser) {
// ./browser_context
this.testBrowser = new TestBrowser(browser);
// ./flag
this.shouldHaveCallsEnabled = shouldHaveCallsEnabled;
this.shouldHaveFeatureFlag = shouldHaveFeatureFlag;
this.shouldSkipInSmallScreen = shouldSkipInSmallScreen;
this.shouldRunInLinux = shouldRunInLinux;
// ./server
@@ -90,9 +84,6 @@ class PlaywrightExtended {
// ./ui/pages
this.pages = pages;
// ./util
this.isSmallScreen = () => isSmallScreen(viewport);
// ./visual
this.matchSnapshot = matchSnapshot;
}

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

@@ -1,27 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Locator} from '@playwright/test';
export default class BoardsCreateModal {
readonly container: Locator;
readonly productSwitchMenu;
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.container.getByRole('link', {name: `${name}`}).click();
}
async toBeVisible(name: string) {
await expect(this.container.getByRole('heading', {name})).toBeVisible();
}
}
export {BoardsCreateModal};

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

@@ -1,28 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Locator} from '@playwright/test';
export default class BoardsSidebar {
readonly container: Locator;
readonly plusButton;
readonly createNewBoardMenuItem;
readonly createNewCategoryMenuItem;
readonly titles;
constructor(container: Locator) {
this.container = container;
this.plusButton = container.locator('.add-board-icon');
this.createNewBoardMenuItem = container.getByRole('button', {name: 'Create new board'});
this.createNewCategoryMenuItem = container.getByRole('button', {name: 'Create New Category'});
this.titles = container.locator('.SidebarBoardItem > .octo-sidebar-title');
}
async waitForTitle(name: string) {
await this.container.getByRole('button', {name: `${name}`}).waitFor({state: 'visible'});
}
}
export {BoardsSidebar};

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

@@ -47,8 +47,8 @@ export default class ChannelsCenterView {
/**
* Return the Nth post in the Center from the top
* @param index
* @returns
* @param index
* @returns
*/
async getNthPost(index: number) {
const nthPost = this.container.getByTestId('postView').nth(index);
@@ -73,7 +73,7 @@ export default class ChannelsCenterView {
const content = await post.container.textContent();
return content?.includes(text);
},
{timeout}
{timeout},
);
}
@@ -85,7 +85,7 @@ export default class ChannelsCenterView {
return content?.includes(text);
},
{timeout}
{timeout},
);
}
}

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

@@ -15,7 +15,7 @@ export default class EmojiGifPicker {
this.gifTab = container.getByText('GIFs');
this.gifSearchInput = container.getByPlaceholder('Search GIPHY');
this.gifPickerItems = container.locator('.gif-picker__items')
this.gifPickerItems = container.locator('.gif-picker__items');
}
async toBeVisible() {
@@ -24,7 +24,7 @@ export default class EmojiGifPicker {
async openGifTab() {
await expect(this.gifTab).toBeVisible();
await this.gifTab.click({force: true});
await expect(this.gifSearchInput).toBeVisible();
@@ -41,7 +41,7 @@ export default class EmojiGifPicker {
await this.gifPickerItems.locator('img').nth(n).waitFor();
const nthGif = this.gifPickerItems.locator('img').nth(n);
await expect(nthGif).toBeVisible()
await expect(nthGif).toBeVisible();
const nthGifSrc = await nthGif.getAttribute('src');
const nthGifAlt = await nthGif.getAttribute('alt');

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

@@ -1,13 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
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 {ChannelsCenterView} from './channels/center_view'
import {ChannelsCenterView} from './channels/center_view';
import {ChannelsSidebarLeft} from './channels/sidebar_left';
import {ChannelsSidebarRight} from './channels/sidebar_right';
import {DeletePostModal} from './channels/delete_post_modal';
@@ -22,7 +21,6 @@ import {ThreadFooter} from './channels/thread_footer';
import {EmojiGifPicker} from './channels/emoji_gif_picker';
const components = {
BoardsSidebar,
GlobalHeader,
ChannelsCenterView,
ChannelsSidebarLeft,
@@ -45,7 +43,6 @@ const components = {
export {
components,
BoardsSidebar,
GlobalHeader,
ChannelsCenterView,
ChannelsSidebarLeft,

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

@@ -1,47 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Page} from '@playwright/test';
import {components} from '@e2e-support/ui/components';
export default class BoardsCreatePage {
readonly boards = 'Boards';
readonly page: Page;
readonly globalHeader;
readonly createBoardHeading;
readonly createEmptyBoardButton;
readonly useTemplateButton;
constructor(page: Page) {
this.page = page;
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'});
}
async goto(teamId = '') {
let boardsUrl = '/boards';
if (teamId) {
boardsUrl += `/team/${teamId}`;
}
await this.page.goto(boardsUrl);
}
async toBeVisible() {
await this.globalHeader.toBeVisible(this.boards);
await expect(this.createEmptyBoardButton).toBeVisible();
await expect(this.useTemplateButton).toBeVisible();
await expect(this.createBoardHeading).toBeVisible();
}
async createEmptyBoard() {
await this.createEmptyBoardButton.click();
}
}
export {BoardsCreatePage};

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

@@ -1,60 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, Page} from '@playwright/test';
import {components} from '@e2e-support/ui/components';
export default class BoardsViewPage {
readonly boards = 'Boards';
readonly page: Page;
readonly sidebar;
readonly globalHeader;
readonly topHead;
readonly editableTitle;
readonly shareButton;
constructor(page: Page) {
this.page = page;
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'});
}
async goto(teamId = '', boardId = '', viewId = '', cardId = '') {
let boardsUrl = '/boards';
if (teamId) {
boardsUrl += `/team/${teamId}`;
if (boardId) {
boardsUrl += `/${boardId}`;
if (viewId) {
boardsUrl += `/${viewId}`;
if (cardId) {
boardsUrl += `/${cardId}`;
}
}
}
}
await this.page.goto(boardsUrl);
}
async toBeVisible() {
await this.page.waitForLoadState('networkidle');
await this.globalHeader.toBeVisible(this.boards);
await expect(this.shareButton).toBeVisible();
await expect(this.topHead).toBeVisible();
}
async shouldHaveUntitledBoard() {
await this.editableTitle.isVisible();
expect(await this.editableTitle.getAttribute('value')).toBe('');
await expect(this.page.getByTitle('(Untitled Board)')).toBeVisible();
}
}
export {BoardsViewPage};

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

@@ -4,7 +4,6 @@
import {Page} from '@playwright/test';
import {components} from '@e2e-support/ui/components';
import {isSmallScreen} from '@e2e-support/util';
export default class ChannelsPage {
readonly channels = 'Channels';
@@ -19,7 +18,7 @@ export default class ChannelsPage {
readonly findChannelsModal;
readonly deletePostModal;
readonly postDotMenu;
readonly postReminderMenu;
@@ -35,7 +34,7 @@ export default class ChannelsPage {
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
this.appBar = new components.ChannelsAppBar(page.locator('.app-bar'));
// Modals
// Modals
this.findChannelsModal = new components.FindChannelsModal(page.getByRole('dialog', {name: 'Find Channels'}));
this.deletePostModal = new components.DeletePostModal(page.locator('#deletePostModal'));
@@ -48,10 +47,6 @@ export default class ChannelsPage {
}
async toBeVisible() {
if (!isSmallScreen(this.page.viewportSize())) {
await this.globalHeader.toBeVisible(this.channels);
}
await this.centerView.toBeVisible();
}

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

@@ -1,8 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BoardsCreatePage} from './boards_create';
import {BoardsViewPage} from './boards_view';
import {ChannelsPage} from './channels';
import {LandingLoginPage} from './landing_login';
import {LoginPage} from './login';
@@ -10,8 +8,6 @@ import {ResetPasswordPage} from './reset_password';
import {SignupPage} from './signup';
const pages = {
BoardsCreatePage,
BoardsViewPage,
ChannelsPage,
LandingLoginPage,
LoginPage,
@@ -19,4 +15,4 @@ const pages = {
SignupPage,
};
export {pages, BoardsCreatePage, BoardsViewPage, ChannelsPage, LandingLoginPage, LoginPage, SignupPage};
export {pages, ChannelsPage, LandingLoginPage, LoginPage, SignupPage};

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

@@ -44,7 +44,7 @@ export default class SignupPage {
this.createAccountButton = page.locator('button:has-text("Create Account")');
this.emailError = page.locator('text=Please enter a valid email address');
this.usernameError = page.locator(
'text=Usernames have to begin with a lowercase letter and be 3-22 characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.'
'text=Usernames have to begin with a lowercase letter and be 3-22 characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.',
);
this.passwordError = page.locator('text=Must be 5-64 characters long.');

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

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {v4 as uuidv4} from 'uuid';
import {ViewportSize} from '@playwright/test';
const second = 1000;
const minute = 60 * 1000;
@@ -46,7 +45,3 @@ export function getRandomId(length = 7): string {
export const defaultTeam = {name: 'ad-1', displayName: 'eligendi', type: 'O'};
export const illegalRe = /[/?<>\\:*|":&();]/g;
export function isSmallScreen(viewport?: ViewportSize | {width: number; height: number} | null) {
return viewport?.width ? Boolean(viewport?.width <= 390) : true;
}

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

@@ -16,8 +16,8 @@ export async function matchSnapshot(testInfo: TestInfo, testArgs: TestArgs) {
// eslint-disable-next-line no-console
console.log(
chalk.yellow(
`^ Warning: No visual test performed. Run in Linux or Playwright docker image to match snapshot.`
)
`^ Warning: No visual test performed. Run in Linux or Playwright docker image to match snapshot.`,
),
);
return;
}