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
This commit is contained in:
Saturnino Abril 2023-09-20 05:28:35 +08:00 committed by GitHub
parent 88d043a971
commit b7b08dbc0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 657 additions and 834 deletions

View File

@ -6,7 +6,7 @@
# Typically run the local server with: # Typically run the local server with:
cd server && make run cd server && make run
# Or build and distribute webapp including channels, boards and playbooks # Or build and distribute webapp including channels and playbooks
# so that their product URLs do not rely on Webpack dev server. # so that their product URLs do not rely on Webpack dev server.
# Especially important when running test inside the Playwright's docker container. # Especially important when running test inside the Playwright's docker container.
cd webapp && make dist cd webapp && make dist
@ -45,7 +45,7 @@ npm run test
Change to root directory, run docker container Change to root directory, run docker container
``` ```
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.36.0-focal /bin/bash docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.38.0-jammy /bin/bash
``` ```
#### 2. Inside the docker container #### 2. Inside the docker container

View File

@ -48,7 +48,7 @@ async function sysadminSetup(client: Client, user: UserProfile | null) {
await client.createTeam(createRandomTeam(defaultTeam.name, defaultTeam.displayName, 'O', false)); await client.createTeam(createRandomTeam(defaultTeam.name, defaultTeam.displayName, 'O', false));
} else if (myDefaultTeam && testConfig.resetBeforeTest) { } else if (myDefaultTeam && testConfig.resetBeforeTest) {
await Promise.all( await Promise.all(
myTeams.filter((team) => team.name !== defaultTeam.name).map((team) => client.deleteTeam(team.id)) myTeams.filter((team) => team.name !== defaultTeam.name).map((team) => client.deleteTeam(team.id)),
); );
const myChannels = await client.getMyChannels(myDefaultTeam.id); const myChannels = await client.getMyChannels(myDefaultTeam.id);
@ -61,7 +61,7 @@ async function sysadminSetup(client: Client, user: UserProfile | null) {
channel.name !== 'off-topic' channel.name !== 'off-topic'
); );
}) })
.map((channel) => client.deleteChannel(channel.id)) .map((channel) => client.deleteChannel(channel.id)),
); );
} }
@ -170,7 +170,7 @@ async function ensureServerDeployment(client: Client) {
sameClusterName, sameClusterName,
sameClusterName sameClusterName
? '' ? ''
: `Should have cluster name set and as expected. Got "${ClusterName}" but expected "${haClusterName}"` : `Should have cluster name set and as expected. Got "${ClusterName}" but expected "${haClusterName}"`,
).toBe(true); ).toBe(true);
const clusterInfo = await client.getClusterStatus(); const clusterInfo = await client.getClusterStatus();
@ -179,12 +179,12 @@ async function ensureServerDeployment(client: Client) {
sameCount, sameCount,
sameCount sameCount
? '' ? ''
: `Should match number of nodes in a cluster as expected. Got "${clusterInfo?.length}" but expected "${haClusterNodeCount}"` : `Should match number of nodes in a cluster as expected. Got "${clusterInfo?.length}" but expected "${haClusterNodeCount}"`,
).toBe(true); ).toBe(true);
clusterInfo.forEach((info) => clusterInfo.forEach((info) =>
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`hostname: ${info.hostname}, version: ${info.version}, config_hash: ${info.config_hash}`) console.log(`hostname: ${info.hostname}, version: ${info.version}, config_hash: ${info.config_hash}`),
); );
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -4,34 +4,36 @@
"percy": "cross-env PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=iphone --project=ipad", "percy": "cross-env PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=iphone --project=ipad",
"tsc": "tsc -b", "tsc": "tsc -b",
"lint": "eslint . --ext .js,.ts", "lint": "eslint . --ext .js,.ts",
"prettier": "prettier --write .", "prettier": "prettier . --check",
"prettier:fix": "prettier --write .",
"check": "npm run tsc && npm run lint && npm run prettier", "check": "npm run tsc && npm run lint && npm run prettier",
"codegen": "cross-env playwright codegen $PW_BASE_URL", "codegen": "cross-env playwright codegen $PW_BASE_URL",
"playwright-ui": "playwright test --ui", "playwright-ui": "playwright test --ui",
"test-slomo": "cross-env PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test", "test-slomo": "cross-env PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test",
"show-report": "npx playwright show-report" "show-report": "npx playwright show-report",
"postinstall": "npx playwright install"
}, },
"dependencies": { "dependencies": {
"@axe-core/playwright": "4.7.3", "@axe-core/playwright": "4.7.3",
"@percy/cli": "1.26.2", "@percy/cli": "1.27.1",
"@percy/playwright": "1.0.4", "@percy/playwright": "1.0.4",
"@playwright/test": "1.36.1", "@playwright/test": "1.38.0",
"async-wait-until": "2.0.12", "async-wait-until": "2.0.12",
"axe-core": "4.7.2", "axe-core": "4.8.1",
"chalk": "4.1.2", "chalk": "4.1.2",
"deepmerge": "4.3.1", "deepmerge": "4.3.1",
"dotenv": "16.3.1", "dotenv": "16.3.1",
"form-data": "4.0.0", "form-data": "4.0.0",
"isomorphic-unfetch": "4.0.2", "isomorphic-unfetch": "4.0.2",
"uuid": "9.0.0" "uuid": "9.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/uuid": "9.0.2", "@types/uuid": "9.0.4",
"@typescript-eslint/eslint-plugin": "6.1.0", "@typescript-eslint/eslint-plugin": "6.7.0",
"@typescript-eslint/parser": "6.1.0", "@typescript-eslint/parser": "6.7.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "8.45.0", "eslint": "8.49.0",
"prettier": "2.8.7", "prettier": "3.0.3",
"typescript": "5.0.4" "typescript": "5.2.2"
} }
} }

View File

@ -52,13 +52,6 @@ export default defineConfig({
}, },
}, },
projects: [ projects: [
{
name: 'iphone',
use: {
browserName: 'chromium',
...devices['iPhone 13 Pro'],
},
},
{ {
name: 'ipad', name: 'ipad',
use: { use: {

View File

@ -16,41 +16,38 @@
# 5. PW_ADMIN_EMAIL # 5. PW_ADMIN_EMAIL
# - Default to "sysadmin@sample.mattermost.com" if not set. # - Default to "sysadmin@sample.mattermost.com" if not set.
# 6. PW_BOARDS_PRODUCT_ENABLED # 6. PW_HA_CLUSTER_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. # - Default to "false" if not set. Set to true if the test server is with HA enabled.
# 8. PW_HA_CLUSTER_NODE_COUNT # 7. PW_HA_CLUSTER_NODE_COUNT
# - Default to "2" if not set. # - Default to "2" if not set.
# 9. PW_HA_CLUSTER_NAME # 8. PW_HA_CLUSTER_NAME
# - Default to "mm_dev_cluster" if not set. # - Default to "mm_dev_cluster" if not set.
# 10. PW_RESET_BEFORE_TEST # 9. 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". # - 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 # 10. CI
# - Default to "false" if not set. # - Default to "false" if not set.
# 12. PW_HEADLESS # 11. PW_HEADLESS
# - Default to "false" or headless mode if not set. Set to true to run test in headed mode. # - Default to "false" or headless mode if not set. Set to true to run test in headed mode.
# 13. PW_SLOWMO # 12. 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. # - 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 # 13. PW_WORKERS
# - Default to "1" if not set. The maximum number of concurrent worker processes to use for parallelizing tests. # - Default to "1" if not set. The maximum number of concurrent worker processes to use for parallelizing tests.
# 15. PW_SNAPSHOT_ENABLE # 14. PW_SNAPSHOT_ENABLE
# - Default to "false" if not set. Set to true to enable snapshot testing. # - 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. # 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 # This is to ensure that, there's a common base platform for all contributors
# regardless of each local development platform. # regardless of each local development platform.
# 16. PW_PERCY_ENABLE # 15. PW_PERCY_ENABLE
# - Default to "false" if not set. Use to save and compare results via https://percy.io/. # - Default to "false" if not set. Use to save and compare results via https://percy.io/.
# 17. PERCY_TOKEN # 16. PERCY_TOKEN
# - A token required by https://percy.io/. # - A token required by https://percy.io/.

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ export async function initSetup({
const {adminClient, adminUser} = await getAdminClient(); const {adminClient, adminUser} = await getAdminClient();
if (!adminClient) { if (!adminClient) {
throw new Error( 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 // eslint-disable-next-line no-console
console.log( console.log(
chalk.green( 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(); expect(err, 'Should not throw an error').toBeFalsy();

View File

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

View File

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

View File

@ -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};

View File

@ -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};

View File

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

View File

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

View File

@ -1,13 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {BoardsSidebar} from './boards/sidebar';
import {ChannelsHeader} from './channels/header'; import {ChannelsHeader} from './channels/header';
import {ChannelsHeaderMobile} from './channels/header_mobile'; import {ChannelsHeaderMobile} from './channels/header_mobile';
import {ChannelsAppBar} from './channels/app_bar'; import {ChannelsAppBar} from './channels/app_bar';
import {ChannelsPostCreate} from './channels/post_create'; import {ChannelsPostCreate} from './channels/post_create';
import {ChannelsPost} from './channels/post'; import {ChannelsPost} from './channels/post';
import {ChannelsCenterView} from './channels/center_view' import {ChannelsCenterView} from './channels/center_view';
import {ChannelsSidebarLeft} from './channels/sidebar_left'; import {ChannelsSidebarLeft} from './channels/sidebar_left';
import {ChannelsSidebarRight} from './channels/sidebar_right'; import {ChannelsSidebarRight} from './channels/sidebar_right';
import {DeletePostModal} from './channels/delete_post_modal'; import {DeletePostModal} from './channels/delete_post_modal';
@ -22,7 +21,6 @@ import {ThreadFooter} from './channels/thread_footer';
import {EmojiGifPicker} from './channels/emoji_gif_picker'; import {EmojiGifPicker} from './channels/emoji_gif_picker';
const components = { const components = {
BoardsSidebar,
GlobalHeader, GlobalHeader,
ChannelsCenterView, ChannelsCenterView,
ChannelsSidebarLeft, ChannelsSidebarLeft,
@ -45,7 +43,6 @@ const components = {
export { export {
components, components,
BoardsSidebar,
GlobalHeader, GlobalHeader,
ChannelsCenterView, ChannelsCenterView,
ChannelsSidebarLeft, ChannelsSidebarLeft,

View File

@ -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};

View File

@ -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};

View File

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

View File

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

View File

@ -44,7 +44,7 @@ export default class SignupPage {
this.createAccountButton = page.locator('button:has-text("Create Account")'); this.createAccountButton = page.locator('button:has-text("Create Account")');
this.emailError = page.locator('text=Please enter a valid email address'); this.emailError = page.locator('text=Please enter a valid email address');
this.usernameError = page.locator( 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.'); this.passwordError = page.locator('text=Must be 5-64 characters long.');

View File

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

View File

@ -16,8 +16,8 @@ export async function matchSnapshot(testInfo: TestInfo, testArgs: TestArgs) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log( console.log(
chalk.yellow( 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; return;
} }

View File

@ -4,8 +4,6 @@
import {Page, ViewportSize} from '@playwright/test'; import {Page, ViewportSize} from '@playwright/test';
import * as dotenv from 'dotenv'; import * as dotenv from 'dotenv';
import {callsPluginId} from '@e2e-support/constant';
dotenv.config(); dotenv.config();
export type TestArgs = { export type TestArgs = {
@ -47,7 +45,7 @@ const config: TestConfig = {
ensurePluginsInstalled: ensurePluginsInstalled:
typeof process.env?.PW_ENSURE_PLUGINS_INSTALLED === 'string' typeof process.env?.PW_ENSURE_PLUGINS_INSTALLED === 'string'
? process.env.PW_ENSURE_PLUGINS_INSTALLED.split(',') ? process.env.PW_ENSURE_PLUGINS_INSTALLED.split(',')
: [callsPluginId], : [],
haClusterEnabled: parseBool(process.env.PW_HA_CLUSTER_ENABLED, false), haClusterEnabled: parseBool(process.env.PW_HA_CLUSTER_ENABLED, false),
haClusterNodeCount: parseNumber(process.env.PW_HA_CLUSTER_NODE_COUNT, 2), haClusterNodeCount: parseNumber(process.env.PW_HA_CLUSTER_NODE_COUNT, 2),
haClusterName: process.env.PW_HA_CLUSTER_NAME || 'mm_dev_cluster', haClusterName: process.env.PW_HA_CLUSTER_NAME || 'mm_dev_cluster',

View File

@ -1,44 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@e2e-support/test_fixture';
import {shouldSkipInSmallScreen} from '@e2e-support/flag';
shouldSkipInSmallScreen();
test('MM-T4274 Create an Empty Board', async ({pw, pages}) => {
// Create and sign in a new user
const {user} = await pw.initSetup();
// 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();
// Switch to Boards page
await channelsPage.globalHeader.switchProduct('Boards');
// Should have redirected to boards create page
const boardsCreatePage = new pages.BoardsCreatePage(page);
await boardsCreatePage.toBeVisible();
// Create empty board
await boardsCreatePage.createEmptyBoard();
// Should have redirected to boards view page
const boardsViewPage = new pages.BoardsViewPage(page);
await boardsViewPage.toBeVisible();
await boardsViewPage.shouldHaveUntitledBoard();
// Type new title and hit enter
const title = 'Testing';
await boardsViewPage.editableTitle.fill(title);
await boardsViewPage.editableTitle.press('Enter');
// Should update the title in heading and in sidebar
expect(await boardsViewPage.editableTitle.getAttribute('value')).toBe(title);
await boardsViewPage.sidebar.waitForTitle(title);
});

View File

@ -22,7 +22,7 @@ test('MM-T5435_1 Global Drafts link in sidebar should be hidden when another use
createRandomPost({ createRandomPost({
channel_id: channel.id, channel_id: channel.id,
user_id: adminUser.id, user_id: adminUser.id,
}) }),
); );
// # Log in as user in new browser context // # Log in as user in new browser context

View File

@ -28,7 +28,8 @@ test('MM-T5445 Should search, select and post correct Gif when Gif picker is ope
await channelPage.emojiGifPickerPopup.searchGif('hello'); await channelPage.emojiGifPickerPopup.searchGif('hello');
// # Select the first gif // # Select the first gif
const {img: firstSearchGifResult, alt: altOfFirstSearchGifResult} = await channelPage.emojiGifPickerPopup.getNthGif(0); const {img: firstSearchGifResult, alt: altOfFirstSearchGifResult} =
await channelPage.emojiGifPickerPopup.getNthGif(0);
await firstSearchGifResult.click(); await firstSearchGifResult.click();
// # Send the selected gif as a message // # Send the selected gif as a message
@ -82,7 +83,8 @@ test('MM-T5446 Should search, select and post correct Gif when Gif picker is ope
await channelPage.emojiGifPickerPopup.searchGif('hello'); await channelPage.emojiGifPickerPopup.searchGif('hello');
// # Select the first gif // # Select the first gif
const {img: firstSearchGifResult, alt: altOfFirstSearchGifResult} = await channelPage.emojiGifPickerPopup.getNthGif(0); const {img: firstSearchGifResult, alt: altOfFirstSearchGifResult} =
await channelPage.emojiGifPickerPopup.getNthGif(0);
await firstSearchGifResult.click(); await firstSearchGifResult.click();
// # Send the selected gif as a message in the thread // # Send the selected gif as a message in the thread

View File

@ -37,9 +37,7 @@ test('MM-T5424 Find channel search returns only 50 results when there are more t
await channelsPage.toBeVisible(); await channelsPage.toBeVisible();
// # Click on "Find channel" and type "test_channel" // # Click on "Find channel" and type "test_channel"
if (pw.isSmallScreen()) { await channelsPage.centerView.headerMobile.toggleSidebar();
await channelsPage.centerView.headerMobile.toggleSidebar();
}
await channelsPage.sidebarLeft.findChannelButton.click(); await channelsPage.sidebarLeft.findChannelButton.click();
await channelsPage.findChannelsModal.toBeVisible(); await channelsPage.findChannelsModal.toBeVisible();

View File

@ -1,24 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@e2e-support/test_fixture';
import {shouldSkipInSmallScreen} from '@e2e-support/flag';
shouldSkipInSmallScreen();
test('Board template', async ({pw, pages, browserName, viewport}, testInfo) => {
// Create and sign in a new user
const {user} = await pw.initSetup();
// Log in a user in new browser context
const {page} = await pw.testBrowser.login(user);
// Should have redirected to boards create page
const boardsCreatePage = new pages.BoardsCreatePage(page);
await boardsCreatePage.goto();
await boardsCreatePage.toBeVisible();
// Match snapshot of create board page
const testArgs = {page, browserName, viewport};
await pw.matchSnapshot(testInfo, testArgs);
});

View File

@ -1,32 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@e2e-support/test_fixture';
import {shouldSkipInSmallScreen} from '@e2e-support/flag';
shouldSkipInSmallScreen();
test('View untitled board', async ({pw, pages, browserName, viewport}, testInfo) => {
// Create and sign in a new user
const {user} = await pw.initSetup();
// Log in a user in new browser context
const {page} = await pw.testBrowser.login(user);
// Should have redirected to boards create page
const boardsCreatePage = new pages.BoardsCreatePage(page);
await boardsCreatePage.goto();
await boardsCreatePage.toBeVisible();
// Create empty board
await boardsCreatePage.createEmptyBoard();
// Should have redirected to boards view page
const boardsViewPage = new pages.BoardsViewPage(page);
await boardsViewPage.toBeVisible();
await boardsViewPage.shouldHaveUntitledBoard();
// Match snapshot of create board page
const testArgs = {page, browserName, viewport};
await pw.matchSnapshot(testInfo, testArgs);
});

View File

@ -15,16 +15,8 @@ test('Intro to channel as regular user', async ({pw, pages, browserName, viewpor
await channelsPage.goto(); await channelsPage.goto();
await channelsPage.toBeVisible(); await channelsPage.toBeVisible();
// Wait for Boards' bot image to be loaded
// await pw.shouldHaveFeatureFlag('OnboardingAutoShowLinkedBoard', true);
// const boardsWelcomePost = await channelsPage.getFirstPost();
// await expect(await boardsWelcomePost.getProfileImage('boards')).toBeVisible();
// await wait(duration.one_sec);
// Wait for Playbooks icon to be loaded in App bar, except in iphone // Wait for Playbooks icon to be loaded in App bar, except in iphone
if (!pw.isSmallScreen()) { await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
}
// Hide dynamic elements of Channels page // Hide dynamic elements of Channels page
await pw.hideDynamicChannelsContent(page); await pw.hideDynamicChannelsContent(page);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 KiB

After

Width:  |  Height:  |  Size: 397 KiB

View File

@ -405,6 +405,7 @@ export type TeamSettings = {
ExperimentalPrimaryTeam: string; ExperimentalPrimaryTeam: string;
ExperimentalDefaultChannels: string[]; ExperimentalDefaultChannels: string[];
EnableLastActiveTime: boolean; EnableLastActiveTime: boolean;
EnableJoinLeaveMessageByDefault: boolean;
}; };
export type ClientRequirements = { export type ClientRequirements = {
@ -429,6 +430,7 @@ export type SqlSettings = {
DisableDatabaseSearch: boolean; DisableDatabaseSearch: boolean;
MigrationsStatementTimeoutSeconds: number; MigrationsStatementTimeoutSeconds: number;
ReplicaLagSettings: ReplicaLagSetting[]; ReplicaLagSettings: ReplicaLagSetting[];
ReplicaMonitorIntervalSeconds: number;
}; };
export type LogSettings = { export type LogSettings = {
@ -445,6 +447,7 @@ export type LogSettings = {
VerboseDiagnostics: boolean; VerboseDiagnostics: boolean;
EnableSentry: boolean; EnableSentry: boolean;
AdvancedLoggingConfig: string; AdvancedLoggingConfig: string;
AdvancedLoggingJSON: Record<string, any>;
}; };
export type ExperimentalAuditSettings = { export type ExperimentalAuditSettings = {
@ -456,6 +459,7 @@ export type ExperimentalAuditSettings = {
FileCompress: boolean; FileCompress: boolean;
FileMaxQueueSize: number; FileMaxQueueSize: number;
AdvancedLoggingConfig: string; AdvancedLoggingConfig: string;
AdvancedLoggingJSON: Record<string, any>;
}; };
export type NotificationLogSettings = { export type NotificationLogSettings = {
@ -468,6 +472,7 @@ export type NotificationLogSettings = {
FileJson: boolean; FileJson: boolean;
FileLocation: string; FileLocation: string;
AdvancedLoggingConfig: string; AdvancedLoggingConfig: string;
AdvancedLoggingJSON: Record<string, any>;
}; };
export type PasswordSettings = { export type PasswordSettings = {
@ -504,6 +509,21 @@ export type FileSettings = {
AmazonS3SSE: boolean; AmazonS3SSE: boolean;
AmazonS3Trace: boolean; AmazonS3Trace: boolean;
AmazonS3RequestTimeoutMilliseconds: number; AmazonS3RequestTimeoutMilliseconds: number;
DedicatedExportStore: boolean;
ExportDriverName: string;
ExportDirectory: string;
ExportAmazonS3AccessKeyId: string;
ExportAmazonS3SecretAccessKey: string;
ExportAmazonS3Bucket: string;
ExportAmazonS3PathPrefix: string;
ExportAmazonS3Region: string;
ExportAmazonS3Endpoint: string;
ExportAmazonS3SSL: boolean;
ExportAmazonS3SignV2: boolean;
ExportAmazonS3SSE: boolean;
ExportAmazonS3Trace: boolean;
ExportAmazonS3RequestTimeoutMilliseconds: number;
ExportAmazonS3PresignExpiresSeconds: number;
}; };
export type EmailSettings = { export type EmailSettings = {
@ -793,6 +813,10 @@ export type DataRetentionSettings = {
FileRetentionDays: number; FileRetentionDays: number;
DeletionJobStartTime: string; DeletionJobStartTime: string;
BatchSize: number; BatchSize: number;
EnableBoardsDeletion: boolean,
BoardsRetentionDays: number;
TimeBetweenBatchesMilliseconds: number;
RetentionIdsBatchSize: number;
}; };
export type MessageExportSettings = { export type MessageExportSettings = {
@ -842,6 +866,7 @@ export type PluginSettings = {
export type DisplaySettings = { export type DisplaySettings = {
CustomURLSchemes: string[]; CustomURLSchemes: string[];
ExperimentalTimezone: boolean; ExperimentalTimezone: boolean;
MaxMarkdownNodes: number;
}; };
export type GuestAccountsSettings = { export type GuestAccountsSettings = {
@ -862,6 +887,7 @@ export type ImageProxySettings = {
export type CloudSettings = { export type CloudSettings = {
CWSURL: string; CWSURL: string;
CWSAPIURL: string; CWSAPIURL: string;
CWSMock: boolean;
}; };
export type FeatureFlags = Record<string, string | boolean>; export type FeatureFlags = Record<string, string | boolean>;