upgrade playwright dependencies, screenshots and tests (#29466)
@@ -275,7 +275,7 @@ $(if mme2e_is_token_in_list "webhook-interactions" "$ENABLED_DOCKER_SERVICES"; t
|
||||
$(if mme2e_is_token_in_list "playwright" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
playwright:
|
||||
image: mcr.microsoft.com/playwright:v1.46.1
|
||||
image: mcr.microsoft.com/playwright:v1.49.0-noble
|
||||
entrypoint: ["/bin/bash", "-c"]
|
||||
command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"]
|
||||
env_file:
|
||||
|
||||
@@ -43,8 +43,8 @@ ${MME2E_DC_SERVER} exec -i -u "$MME2E_UID" -- playwright bash -c "cd e2e-tests/p
|
||||
# Documentation on the results.json file: https://playwright.dev/docs/api/class-testcase#test-case-expected-status
|
||||
# NB: the following line is needed only for compatibility reasons, to support RollingRelease tests for versions prior to v10.1.0
|
||||
# It can be removed after releases <=v10.0.x are phased out
|
||||
mv -v ../playwright/playwright-report/results.json ../playwright/results/results.json 2>/dev/null || true
|
||||
jq -f /dev/stdin ../playwright/results/results.json > ../playwright/results/summary.json <<EOF
|
||||
mv -v ../playwright/playwright-report/results.json ../playwright/results/reporter/results.json 2>/dev/null || true
|
||||
jq -f /dev/stdin ../playwright/results/reporter/results.json > ../playwright/results/summary.json <<EOF
|
||||
{
|
||||
passed: .stats.expected,
|
||||
failed: .stats.unexpected,
|
||||
|
||||
3
e2e-tests/.gitignore
поставляемый
@@ -9,3 +9,6 @@
|
||||
|
||||
# node
|
||||
*.lock
|
||||
|
||||
# build
|
||||
*.tsbuildinfo
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
playwright-report
|
||||
test-results
|
||||
results
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-console": "error"
|
||||
},
|
||||
"env": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,10 @@ npm run test
|
||||
|
||||
#### 1. Run docker container using latest focal version
|
||||
|
||||
Change to the root directory, then run the docker container.
|
||||
Change to the root directory, then run the docker container. (See https://playwright.dev/docs/docker for reference.)
|
||||
|
||||
```
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.46.1 /bin/bash
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.49.0-noble /bin/bash
|
||||
```
|
||||
|
||||
#### 2. Inside the docker container
|
||||
|
||||
45
e2e-tests/playwright/eslint.config.mjs
Обычный файл
@@ -0,0 +1,45 @@
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
||||
import globals from 'globals';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import path from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import js from '@eslint/js';
|
||||
import {FlatCompat} from '@eslint/eslintrc';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ['**/node_modules', '**/playwright-report', '**/test-results', '**/results'],
|
||||
},
|
||||
...compat.extends('eslint:recommended', 'plugin:@typescript-eslint/recommended').map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.s'],
|
||||
})),
|
||||
{
|
||||
files: ['**/*.ts', '**/*.s'],
|
||||
plugins: {
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
parser: tsParser,
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'no-console': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
2340
e2e-tests/playwright/package-lock.json
сгенерированный
@@ -4,7 +4,7 @@
|
||||
"test:update-snapshots": "cross-env PW_SNAPSHOT_ENABLE=true playwright test --update-snapshots",
|
||||
"percy": "cross-env PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=ipad",
|
||||
"tsc": "tsc -b",
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
"lint": "eslint .",
|
||||
"prettier": "prettier . --check",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"check": "npm run tsc && npm run lint && npm run prettier",
|
||||
@@ -15,27 +15,27 @@
|
||||
"postinstall": "script/post_install.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@axe-core/playwright": "4.10.0",
|
||||
"@percy/cli": "1.29.3",
|
||||
"@percy/playwright": "1.0.6",
|
||||
"@playwright/test": "1.46.1",
|
||||
"@axe-core/playwright": "4.10.1",
|
||||
"@percy/cli": "1.30.3",
|
||||
"@percy/playwright": "1.0.7",
|
||||
"@playwright/test": "1.49.0",
|
||||
"async-wait-until": "2.0.12",
|
||||
"axe-core": "4.10.0",
|
||||
"axe-core": "4.10.2",
|
||||
"chalk": "4.1.2",
|
||||
"dayjs": "1.11.13",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv": "16.4.5",
|
||||
"form-data-encoder": "4.0.2",
|
||||
"formdata-node": "6.0.3",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/uuid": "9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
"@typescript-eslint/parser": "7.2.0",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.16.0",
|
||||
"@typescript-eslint/parser": "8.16.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.57.0",
|
||||
"prettier": "3.2.5",
|
||||
"typescript": "5.4.5"
|
||||
"eslint": "9.16.0",
|
||||
"prettier": "3.4.1",
|
||||
"typescript": "5.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,10 @@ import {defineConfig, devices} from '@playwright/test';
|
||||
import {duration} from '@e2e-support/util';
|
||||
import testConfig from '@e2e-test.config';
|
||||
|
||||
const defaultOutputFolder = './results';
|
||||
|
||||
export default defineConfig({
|
||||
globalSetup: require.resolve('./global_setup'),
|
||||
forbidOnly: testConfig.isCI,
|
||||
outputDir: './results/tests',
|
||||
outputDir: './results/output',
|
||||
retries: testConfig.isCI ? 2 : 0,
|
||||
testDir: 'tests',
|
||||
timeout: duration.one_min,
|
||||
@@ -80,9 +78,9 @@ export default defineConfig({
|
||||
},
|
||||
],
|
||||
reporter: [
|
||||
['html', {open: 'never', outputFolder: defaultOutputFolder}],
|
||||
['json', {outputFile: `${defaultOutputFolder}/results.json`}],
|
||||
['junit', {outputFile: `${defaultOutputFolder}/results.xml`}],
|
||||
['html', {open: 'never', outputFolder: './results/reporter'}],
|
||||
['json', {outputFile: './results/reporter/results.json'}],
|
||||
['junit', {outputFile: './results/reporter/results.xml'}],
|
||||
['list'],
|
||||
],
|
||||
});
|
||||
|
||||
@@ -8,14 +8,14 @@ dayjs.extend(duration);
|
||||
const {TYPE, SERVER_TYPE, BRANCH, PULL_REQUEST, BUILD_ID, MM_ENV, MM_DOCKER_IMAGE, MM_DOCKER_TAG, RELEASE_DATE} =
|
||||
process.env;
|
||||
|
||||
const resultsFile = 'results/results.json';
|
||||
const resultsFile = 'results/reporter/results.json';
|
||||
const summaryFile = 'results/summary.json';
|
||||
const results = JSON.parse(fs.readFileSync(resultsFile, 'utf8'));
|
||||
const summary = JSON.parse(fs.readFileSync(summaryFile, 'utf8'));
|
||||
const passRate = summary.passed / (summary.passed + summary.failed);
|
||||
const passRate = (summary.passed * 100) / (summary.passed + summary.failed);
|
||||
const totalSpecs = summary.passed + summary.failed;
|
||||
const playwrightVersion = results.config.version;
|
||||
const playwrightDuration = dayjs.duration(results.stats.duration, 'seconds').format('HH:mm:ss');
|
||||
const playwrightDuration = dayjs.duration(results.stats.duration, 'millisecond').format('HH:mm:ss');
|
||||
|
||||
function generateTitle() {
|
||||
let dockerImageLink = '';
|
||||
@@ -68,7 +68,7 @@ function generateWebhookBody() {
|
||||
}
|
||||
}
|
||||
|
||||
const summaryField = `${(passRate * 100).toFixed(2)}% (${summary.passed}/${totalSpecs}) | ${playwrightDuration} | playwright@${playwrightVersion}`;
|
||||
const summaryField = `${passRate.toFixed(2)}% (${summary.passed}/${totalSpecs}) | ${playwrightDuration} | playwright@${playwrightVersion}`;
|
||||
const serverTypeField = SERVER_TYPE ? '\nTest server: ' + SERVER_TYPE : '';
|
||||
const mmEnvField = MM_ENV ? '\nTest server override: ' + MM_ENV : '';
|
||||
const rollingReleaseMatchRegex = BUILD_ID?.match(/-rolling(?<version>[^-]+)-/);
|
||||
|
||||
@@ -64,7 +64,6 @@ export async function requestTrialLicense() {
|
||||
users: 100,
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
expect(error, 'Failed to request trial license').toBeFalsy();
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
|
||||
};
|
||||
|
||||
// Should be based only from the generated default config from ./server via "make config-reset"
|
||||
// Based on v10.2 server
|
||||
// Based on v10.3 server
|
||||
const defaultServerConfig: AdminConfig = {
|
||||
ServiceSettings: {
|
||||
SiteURL: '',
|
||||
@@ -198,6 +198,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
RefreshPostStatsRunTime: '00:00',
|
||||
MaximumPayloadSizeBytes: 300000,
|
||||
MaximumURLLength: 2048,
|
||||
ScheduledPosts: true,
|
||||
},
|
||||
TeamSettings: {
|
||||
SiteName: 'Mattermost',
|
||||
@@ -685,6 +686,9 @@ const defaultServerConfig: AdminConfig = {
|
||||
'com.mattermost.nps': {
|
||||
Enable: true,
|
||||
},
|
||||
'mattermost-ai': {
|
||||
Enable: true,
|
||||
},
|
||||
playbooks: {
|
||||
Enable: true,
|
||||
},
|
||||
@@ -724,6 +728,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
TestFeature: 'off',
|
||||
TestBoolFeature: false,
|
||||
EnableRemoteClusterService: false,
|
||||
EnableSharedChannelsDMs: false,
|
||||
AppsEnabled: false,
|
||||
PermalinkPreviews: false,
|
||||
NormalizeLdapDNs: false,
|
||||
@@ -741,6 +746,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
WebSocketEventScope: true,
|
||||
NotificationMonitoring: true,
|
||||
ExperimentalAuditSettingsSystemConsoleUI: false,
|
||||
ExperimentalCrossTeamSearch: false,
|
||||
},
|
||||
ImportSettings: {
|
||||
Directory: './import',
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
export default class SearchPopover {
|
||||
readonly container: Locator;
|
||||
|
||||
readonly messagesButton;
|
||||
readonly filesButton;
|
||||
readonly searchInput;
|
||||
readonly searchBoxClose;
|
||||
readonly selectedSuggestion;
|
||||
readonly searchHints;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
this.messagesButton = container.getByRole('button', {name: 'Messages'});
|
||||
this.filesButton = container.getByRole('button', {name: 'Files'});
|
||||
this.searchInput = container.getByLabel('Search messages');
|
||||
this.searchBoxClose = container.getByTestId('searchBoxClose');
|
||||
this.selectedSuggestion = container.locator('.suggestion--selected').locator('.suggestion-list__main');
|
||||
this.searchHints = container.locator('#searchHints');
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
|
||||
getSelectedSuggestion() {
|
||||
return this.searchHints.locator('.suggestion--selected');
|
||||
}
|
||||
}
|
||||
|
||||
export {SearchPopover};
|
||||
@@ -43,6 +43,11 @@ export default class GlobalHeader {
|
||||
await expect(this.searchBox).toBeVisible();
|
||||
await this.searchBox.click();
|
||||
}
|
||||
|
||||
async closeSearch() {
|
||||
await expect(this.searchBox).toBeVisible();
|
||||
await this.searchBox.getByTestId('input-clear').click();
|
||||
}
|
||||
}
|
||||
|
||||
export {GlobalHeader};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {FindChannelsModal} from './channels/find_channels_modal';
|
||||
import {SettingsModal} from './channels/settings/settings_modal';
|
||||
import {Footer} from './footer';
|
||||
import {GlobalHeader} from './global_header';
|
||||
import {SearchPopover} from './channels/search_popover';
|
||||
import {MainHeader} from './main_header';
|
||||
import {PostDotMenu} from './channels/post_dot_menu';
|
||||
import {PostReminderMenu} from './channels/post_reminder_menu';
|
||||
@@ -33,6 +34,7 @@ import {SystemUsersColumnToggleMenu} from './system_console/sections/system_user
|
||||
|
||||
const components = {
|
||||
GlobalHeader,
|
||||
SearchPopover,
|
||||
ChannelsCenterView,
|
||||
ChannelsSidebarLeft,
|
||||
ChannelsSidebarRight,
|
||||
|
||||
@@ -11,6 +11,7 @@ export default class ChannelsPage {
|
||||
readonly page: Page;
|
||||
|
||||
readonly globalHeader;
|
||||
readonly searchPopover;
|
||||
readonly centerView;
|
||||
readonly scheduledDraftDropdown;
|
||||
readonly scheduledDraftModal;
|
||||
@@ -33,6 +34,7 @@ export default class ChannelsPage {
|
||||
|
||||
// The main areas of the app
|
||||
this.globalHeader = new components.GlobalHeader(page.locator('#global-header'));
|
||||
this.searchPopover = new components.SearchPopover(page.locator('#searchPopover'));
|
||||
this.centerView = new components.ChannelsCenterView(page.getByTestId('channel_view'));
|
||||
this.sidebarLeft = new components.ChannelsSidebarLeft(page.locator('#SidebarContainer'));
|
||||
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function matchSnapshot(testInfo: TestInfo, testArgs: TestArgs, opti
|
||||
|
||||
if (testConfig.snapshotEnabled) {
|
||||
// Visual test with built-in snapshot
|
||||
const filename = testInfo.title.replace(illegalRe, '').replace(/\s/g, '-').trim().toLowerCase();
|
||||
const filename = testInfo.title.trim().replace(illegalRe, '').replace(/\s/g, '-').trim().toLowerCase();
|
||||
await expect(testArgs.page).toHaveScreenshot(`${filename}.png`, {fullPage: true, ...options});
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ test('MM-T5435_1 Global Drafts link in sidebar should be hidden when another use
|
||||
try {
|
||||
await adminClient.deletePost(adminPost.id);
|
||||
} catch (error) {
|
||||
throw new Error('Failed to delete post by admin');
|
||||
throw new Error(`Failed to delete post by admin: ${error}`);
|
||||
}
|
||||
|
||||
// # Open the last post in the channel sent by admin again
|
||||
|
||||
@@ -17,16 +17,42 @@ test('Search box suggestion must be case insensitive', async ({pw, pages}) => {
|
||||
// # Open the search UI
|
||||
await channelsPage.globalHeader.openSearch();
|
||||
|
||||
const searchInput = await page.getByPlaceholder('Search messages');
|
||||
const searchWord = 'off';
|
||||
const searchOutput = 'In:off-topic';
|
||||
const channelName = 'Off-Topic';
|
||||
|
||||
// * it's working when using lowercase
|
||||
await searchInput.fill('In:off');
|
||||
await searchInput.press('Enter');
|
||||
await expect(searchInput).toHaveValue('In:off-topic ');
|
||||
// Should work as expected when using lowercase
|
||||
// # Type in lowercase "off" to search for the "Off-Topic" channel
|
||||
const {searchInput} = await channelsPage.searchPopover;
|
||||
await searchInput.pressSequentially(`In:${searchWord}`);
|
||||
|
||||
// * it's working when using uppercase
|
||||
await searchInput.clear();
|
||||
await searchInput.fill('In:Off');
|
||||
// * The suggestion should be visible
|
||||
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
|
||||
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(channelName);
|
||||
|
||||
// # Press Enter to select the suggestion and another Enter to search
|
||||
await searchInput.press('Enter');
|
||||
await expect(searchInput).toHaveValue('In:off-topic ');
|
||||
await searchInput.press('Enter');
|
||||
|
||||
// * The search box should contain the selected suggestion
|
||||
await expect(channelsPage.globalHeader.searchBox.getByText(searchOutput, {exact: true})).toBeVisible();
|
||||
|
||||
// Should work as expected when using uppercase
|
||||
// # Close then open the search UI
|
||||
await channelsPage.globalHeader.closeSearch();
|
||||
await channelsPage.globalHeader.openSearch();
|
||||
|
||||
// # Type in uppercase "OFF" to search for the "Off-Topic" channel
|
||||
await searchInput.pressSequentially(`In:${searchWord.toUpperCase()}`);
|
||||
|
||||
// * The suggestion should be visible
|
||||
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
|
||||
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(channelName);
|
||||
|
||||
// # Press Enter to select the suggestion and another Enter to search
|
||||
await searchInput.press('Enter');
|
||||
await searchInput.press('Enter');
|
||||
|
||||
// * The search box should contain the selected suggestion
|
||||
await expect(channelsPage.globalHeader.searchBox.getByText(searchOutput, {exact: true})).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -8,15 +8,8 @@ async function interceptConfigWithLandingPage(page: Page, enabled: boolean) {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify({
|
||||
EmailLoginButtonBorderColor: '#2389D7',
|
||||
EmailLoginButtonColor: '#0000',
|
||||
EmailLoginButtonTextColor: '#2389D7',
|
||||
EnableDesktopLandingPage: enabled,
|
||||
EnableSignInWithEmail: 'true',
|
||||
EnableSignInWithUsername: 'true',
|
||||
EnableSignUpWithEmail: 'true',
|
||||
SiteName: 'Mattermost',
|
||||
SiteURL: 'http://localhost:8065',
|
||||
}),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
});
|
||||
@@ -29,7 +22,7 @@ test('MM-T5640_1 should not see landing page ', async ({pw, pages, page}) => {
|
||||
await interceptConfigWithLandingPage(page, false);
|
||||
|
||||
// Navigate to your starting URL
|
||||
await page.goto('http://localhost:8065');
|
||||
await page.goto('/');
|
||||
|
||||
// Wait until the URL contains '/login'
|
||||
await page.waitForURL(/.*\/login.*/);
|
||||
@@ -44,11 +37,11 @@ test('MM-T5640_1 should not see landing page ', async ({pw, pages, page}) => {
|
||||
|
||||
test('MM-T5640_2 should see landing page', async ({pages, isMobile, page}) => {
|
||||
// Navigate to your starting URL
|
||||
await page.goto('http://localhost:8065');
|
||||
await page.goto('/');
|
||||
|
||||
await page.evaluate(() => localStorage.clear());
|
||||
|
||||
await page.goto('http://localhost:8065');
|
||||
await page.goto('/');
|
||||
|
||||
// Wait until the URL contains '/landing'
|
||||
await page.waitForURL(/.*\/landing.*/);
|
||||
|
||||
@@ -7,6 +7,7 @@ test('/login', async ({pw, pages, page, browserName, viewport}, testInfo) => {
|
||||
// Go to login page
|
||||
const {adminClient} = await pw.getAdminClient();
|
||||
const adminConfig = await adminClient.getConfig();
|
||||
const license = await adminClient.getClientLicenseOld();
|
||||
const loginPage = new pages.LoginPage(page, adminConfig);
|
||||
await loginPage.goto();
|
||||
await loginPage.toBeVisible();
|
||||
@@ -16,7 +17,8 @@ test('/login', async ({pw, pages, page, browserName, viewport}, testInfo) => {
|
||||
|
||||
// Match snapshot of login page
|
||||
const testArgs = {page, browserName, viewport};
|
||||
await pw.matchSnapshot(testInfo, testArgs);
|
||||
const editionSuffix = license.IsLicensed === 'true' ? '' : 'free edition';
|
||||
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} ${editionSuffix}`}, testArgs);
|
||||
|
||||
// Click sign in button without entering user credential
|
||||
await loginPage.signInButton.click();
|
||||
@@ -24,5 +26,5 @@ test('/login', async ({pw, pages, page, browserName, viewport}, testInfo) => {
|
||||
await pw.waitForAnimationEnd(loginPage.bodyCard);
|
||||
|
||||
// Match snapshot of login page with error
|
||||
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} error`}, testArgs);
|
||||
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} error ${editionSuffix}`}, testArgs);
|
||||
});
|
||||
|
||||
Двоичные данные
e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-free-edition-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 138 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-free-edition-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 275 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-free-edition-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 125 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-free-edition-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 139 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-free-edition-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 280 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-free-edition-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 120 KiB |
@@ -7,6 +7,7 @@ test('/signup_email', async ({pw, pages, page, browserName, viewport}, testInfo)
|
||||
// Go to login page
|
||||
const {adminClient} = await pw.getAdminClient();
|
||||
const adminConfig = await adminClient.getConfig();
|
||||
const license = await adminClient.getClientLicenseOld();
|
||||
const loginPage = new pages.LoginPage(page, adminConfig);
|
||||
await loginPage.goto();
|
||||
await loginPage.toBeVisible();
|
||||
@@ -23,7 +24,8 @@ test('/signup_email', async ({pw, pages, page, browserName, viewport}, testInfo)
|
||||
|
||||
// Match snapshot of signup_email page
|
||||
const testArgs = {page, browserName, viewport};
|
||||
await pw.matchSnapshot(testInfo, testArgs);
|
||||
const editionSuffix = license.IsLicensed === 'true' ? '' : 'free edition';
|
||||
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} ${editionSuffix}`}, testArgs);
|
||||
|
||||
// Click sign in button without entering user credential
|
||||
const invalidUser = {email: 'invalid', username: 'a', password: 'b'};
|
||||
@@ -34,5 +36,5 @@ test('/signup_email', async ({pw, pages, page, browserName, viewport}, testInfo)
|
||||
await pw.waitForAnimationEnd(signupPage.bodyCard);
|
||||
|
||||
// Match snapshot of signup_email page
|
||||
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} error`}, testArgs);
|
||||
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} error ${editionSuffix}`}, testArgs);
|
||||
});
|
||||
|
||||
|
После Ширина: | Высота: | Размер: 159 KiB |
|
После Ширина: | Высота: | Размер: 301 KiB |
|
После Ширина: | Высота: | Размер: 161 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-free-edition-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 153 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-free-edition-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 296 KiB |
Двоичные данные
e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-free-edition-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 155 KiB |
@@ -225,6 +225,7 @@ const SearchBox = forwardRef(
|
||||
role='searchbox'
|
||||
>
|
||||
<CloseIcon
|
||||
data-testid='searchBoxClose'
|
||||
className='btn btn-icon btn-m'
|
||||
onClick={closeHandler}
|
||||
>
|
||||
|
||||
@@ -400,6 +400,7 @@ export type ServiceSettings = {
|
||||
EnableAPIPostDeletion: boolean;
|
||||
EnableDesktopLandingPage: boolean;
|
||||
MaximumURLLength: number;
|
||||
ScheduledPosts: boolean;
|
||||
};
|
||||
|
||||
export type TeamSettings = {
|
||||
|
||||