mirror of
https://github.com/mattermost/mattermost.git
synced 2026-08-26 13:17:29 -05:00
chore(playwright): upgrade to v1.59 and to typescript@6.0 (#36071)
This commit is contained in:
@@ -284,7 +284,7 @@ $(if mme2e_is_token_in_list "playwright" "$ENABLED_DOCKER_SERVICES"; then
|
||||
# shellcheck disable=SC2016
|
||||
echo '
|
||||
playwright:
|
||||
image: mcr.microsoft.com/playwright:v1.58.0-noble
|
||||
image: mcr.microsoft.com/playwright:v1.59.1-noble
|
||||
entrypoint: ["/bin/bash", "-c"]
|
||||
command:
|
||||
- |
|
||||
|
||||
@@ -150,7 +150,7 @@ test(
|
||||
Change to the `./` project directory, then run the docker container. (See https://playwright.dev/docs/docker for reference.)
|
||||
|
||||
```bash
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.58.0-noble /bin/bash
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.59.1-noble /bin/bash
|
||||
```
|
||||
|
||||
#### 2. Inside the docker container
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mattermost/playwright-lib",
|
||||
"version": "11.4.0",
|
||||
"version": "11.7.0",
|
||||
"description": "A comprehensive end-to-end testing library for Mattermost web, desktop and plugin applications using Playwright",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -20,6 +20,7 @@
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js"
|
||||
}
|
||||
@@ -45,13 +46,13 @@
|
||||
"@axe-core/playwright": "4.11.1",
|
||||
"@mattermost/client": "file:../../../webapp/platform/client",
|
||||
"@mattermost/types": "file:../../../webapp/platform/types",
|
||||
"@percy/cli": "1.31.8",
|
||||
"@percy/playwright": "1.0.10",
|
||||
"@percy/cli": "1.31.11",
|
||||
"@percy/playwright": "1.1.0",
|
||||
"async-wait-until": "2.0.31",
|
||||
"axe-core": "4.11.1",
|
||||
"axe-core": "4.11.2",
|
||||
"chalk": "5.6.2",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv": "17.2.3",
|
||||
"dotenv": "17.4.2",
|
||||
"luxon": "3.7.2",
|
||||
"mime-types": "3.0.2",
|
||||
"uuid": "13.0.0"
|
||||
@@ -60,11 +61,11 @@
|
||||
"@rollup/plugin-typescript": "12.3.0",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/node": "25.2.0",
|
||||
"rollup": "4.57.1",
|
||||
"@types/node": "25.6.0",
|
||||
"rollup": "4.60.1",
|
||||
"rollup-plugin-copy": "3.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@playwright/test": ">=1.55.0"
|
||||
"@playwright/test": ">=1.59.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,16 @@ export class TestBrowser {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the auth state of an existing context to a different user
|
||||
* without creating a new context. After switching, pages in the context
|
||||
* should be reloaded to pick up the new auth state.
|
||||
*/
|
||||
async switchUser(context: BrowserContext, user: UserProfile) {
|
||||
const storagePath = await loginByAPI(user.username, user.password);
|
||||
await context.setStorageState(storagePath);
|
||||
}
|
||||
|
||||
async close() {
|
||||
for (const context of this.contexts) {
|
||||
await context.close();
|
||||
|
||||
@@ -7,7 +7,7 @@ export {baseGlobalSetup} from './global_setup';
|
||||
export {TestBrowser} from './browser_context';
|
||||
export {getBlobFromAsset, getFileFromAsset} from './file';
|
||||
export {decomposeKorean, koreanTestPhrase, typeHangulCharacterWithIme, typeHangulWithIme} from './ime';
|
||||
export {duration, wait, newTestPassword} from './util';
|
||||
export {duration, getRandomId, wait, newTestPassword} from './util';
|
||||
|
||||
export {
|
||||
ChannelsPage,
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function createUserWithAttributes(
|
||||
client: Client4,
|
||||
attributes: Record<string, string>,
|
||||
): Promise<UserProfile> {
|
||||
const randomId = await getRandomId();
|
||||
const randomId = getRandomId();
|
||||
// Ensure username starts with a letter (Mattermost requirement)
|
||||
const username = `user${randomId}`.toLowerCase();
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ export async function createMockAIAgent(
|
||||
overrides: CreateMockAIAgentOverrides = {},
|
||||
): Promise<{agent: AIBridgeMockAgent; service: AIBridgeMockService; state: AIBridgeMockState}> {
|
||||
const state = await getAIBridgeMock(adminClient);
|
||||
const randomId = await getRandomId();
|
||||
const randomId = getRandomId();
|
||||
|
||||
const service: AIBridgeMockService = {
|
||||
id: overrides.service?.id ?? overrides.agent?.service_id ?? `mock-service-${randomId}`,
|
||||
|
||||
@@ -86,7 +86,7 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
|
||||
};
|
||||
|
||||
// Should be based only from the generated default config from ./server via "make config-reset"
|
||||
// Based on v11.4 server
|
||||
// Based on v11.7 server
|
||||
const defaultServerConfig: AdminConfig = {
|
||||
ServiceSettings: {
|
||||
SiteURL: '',
|
||||
@@ -175,6 +175,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
EnableAPIUserDeletion: false,
|
||||
EnableAPIPostDeletion: false,
|
||||
EnableDesktopLandingPage: true,
|
||||
MinimumDesktopAppVersion: '',
|
||||
ExperimentalEnableHardenedMode: false,
|
||||
ExperimentalStrictCSRFEnforcement: false,
|
||||
EnableEmailInvitations: false,
|
||||
@@ -214,7 +215,6 @@ const defaultServerConfig: AdminConfig = {
|
||||
EnableWebHubChannelIteration: false,
|
||||
FrameAncestors: '',
|
||||
DeleteAccountLink: '',
|
||||
MinimumDesktopAppVersion: '',
|
||||
},
|
||||
TeamSettings: {
|
||||
SiteName: 'Mattermost',
|
||||
@@ -431,6 +431,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
DiscoveryEndpoint: '',
|
||||
ButtonText: '',
|
||||
ButtonColor: '',
|
||||
UsePreferredUsername: false,
|
||||
},
|
||||
GoogleSettings: {
|
||||
Enable: false,
|
||||
@@ -444,6 +445,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
DiscoveryEndpoint: '',
|
||||
ButtonText: '',
|
||||
ButtonColor: '',
|
||||
UsePreferredUsername: false,
|
||||
},
|
||||
Office365Settings: {
|
||||
Enable: false,
|
||||
@@ -455,6 +457,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
UserAPIEndpoint: 'https://graph.microsoft.com/v1.0/me',
|
||||
DiscoveryEndpoint: '',
|
||||
DirectoryId: '',
|
||||
UsePreferredUsername: false,
|
||||
},
|
||||
OpenIdSettings: {
|
||||
Enable: false,
|
||||
@@ -467,6 +470,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
DiscoveryEndpoint: '',
|
||||
ButtonText: '',
|
||||
ButtonColor: '#145DBF',
|
||||
UsePreferredUsername: false,
|
||||
},
|
||||
LdapSettings: {
|
||||
Enable: false,
|
||||
@@ -775,11 +779,13 @@ const defaultServerConfig: AdminConfig = {
|
||||
ContentFlagging: true,
|
||||
InteractiveDialogAppsForm: true,
|
||||
EnableMattermostEntry: true,
|
||||
MobileSSOCodeExchange: true,
|
||||
AutoTranslation: false,
|
||||
MobileSSOCodeExchange: false,
|
||||
AutoTranslation: true,
|
||||
BurnOnRead: true,
|
||||
EnableAIPluginBridge: false,
|
||||
EnableAIRecaps: false,
|
||||
IntegratedBoards: false,
|
||||
CJKSearch: false,
|
||||
},
|
||||
ImportSettings: {
|
||||
Directory: './import',
|
||||
@@ -845,9 +851,10 @@ const defaultServerConfig: AdminConfig = {
|
||||
},
|
||||
AutoTranslationSettings: {
|
||||
Enable: false,
|
||||
RestrictDMAndGM: false,
|
||||
Provider: '',
|
||||
TargetLanguages: ['en'],
|
||||
Workers: 4,
|
||||
Workers: 6,
|
||||
TimeoutMs: 5000,
|
||||
LibreTranslate: {
|
||||
URL: '',
|
||||
@@ -856,6 +863,5 @@ const defaultServerConfig: AdminConfig = {
|
||||
Agents: {
|
||||
LLMServiceID: '',
|
||||
},
|
||||
RestrictDMAndGM: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function createRandomTeam(
|
||||
type: TeamType = 'O',
|
||||
unique = true,
|
||||
): Promise<Team> {
|
||||
const randomSuffix = await getRandomId();
|
||||
const randomSuffix = getRandomId();
|
||||
|
||||
const team = {
|
||||
name: unique ? `${name}-${randomSuffix}` : name,
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function createNewUserProfile(
|
||||
}
|
||||
|
||||
export async function createRandomUser(prefix = 'user') {
|
||||
const randomId = await getRandomId();
|
||||
const randomId = getRandomId();
|
||||
|
||||
const user = {
|
||||
email: `${prefix}${randomId}@sample.mattermost.com`,
|
||||
|
||||
@@ -50,7 +50,7 @@ import {
|
||||
import {pages} from './ui/pages';
|
||||
import {matchSnapshot} from './visual';
|
||||
import {stubNotification, waitForNotification} from './mock_browser_api';
|
||||
import {duration, getRandomId, simpleEmailRe, wait} from './util';
|
||||
import {duration, getRandomId, newTestPassword, simpleEmailRe, wait} from './util';
|
||||
|
||||
export {expect} from '@playwright/test';
|
||||
|
||||
@@ -131,6 +131,7 @@ export class PlaywrightExtended {
|
||||
|
||||
// ./util
|
||||
readonly duration;
|
||||
readonly newTestPassword;
|
||||
readonly simpleEmailRe;
|
||||
readonly wait;
|
||||
|
||||
@@ -206,6 +207,7 @@ export class PlaywrightExtended {
|
||||
// ./util
|
||||
this.duration = duration;
|
||||
this.wait = wait;
|
||||
this.newTestPassword = newTestPassword;
|
||||
this.simpleEmailRe = simpleEmailRe;
|
||||
|
||||
this.random = {
|
||||
|
||||
@@ -4,16 +4,7 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
|
||||
// Lazy-load the ESM-only uuid package dynamically
|
||||
let uuidv4: (() => string) | null = null;
|
||||
|
||||
async function loadUuid() {
|
||||
if (!uuidv4) {
|
||||
const {v4} = await import('uuid');
|
||||
uuidv4 = v4;
|
||||
}
|
||||
return uuidv4!;
|
||||
}
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
const second = 1000;
|
||||
const minute = 60 * 1000;
|
||||
|
||||
@@ -38,15 +29,9 @@ export const wait = async (ms = 0): Promise<void> => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a random ID string.
|
||||
* Because uuid is dynamically loaded, this is async.
|
||||
*/
|
||||
export async function getRandomId(length = 7): Promise<string> {
|
||||
export function getRandomId(length = 7): string {
|
||||
const MAX_SUBSTRING_INDEX = 27;
|
||||
|
||||
const v4 = await loadUuid();
|
||||
return v4()
|
||||
return uuidv4()
|
||||
.replace(/-/g, '')
|
||||
.substring(MAX_SUBSTRING_INDEX - length, MAX_SUBSTRING_INDEX);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,9 @@
|
||||
"module": "ESNext",
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"downlevelIteration": true,
|
||||
"baseUrl": "src",
|
||||
"rootDir": "src",
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
|
||||
Generated
+918
-516
File diff suppressed because it is too large
Load Diff
@@ -34,19 +34,19 @@
|
||||
"@mattermost/types": "file:../../webapp/platform/types"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.58.1",
|
||||
"@playwright/test": "1.59.1",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.58.1",
|
||||
"cross-env": "10.1.0",
|
||||
"dayjs": "1.11.19",
|
||||
"dayjs": "1.11.20",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-import-resolver-typescript": "4.4.4",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"glob": "13.0.1",
|
||||
"glob": "13.0.6",
|
||||
"luxon": "3.7.2",
|
||||
"prettier": "3.8.1",
|
||||
"typescript": "5.9.3",
|
||||
"prettier": "3.8.2",
|
||||
"typescript": "6.0.2",
|
||||
"zod": "4.3.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default defineConfig({
|
||||
},
|
||||
screenshot: 'only-on-failure',
|
||||
timezoneId: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
trace: 'off',
|
||||
trace: 'retain-on-failure-and-retries',
|
||||
video: 'retain-on-failure',
|
||||
actionTimeout: duration.half_min,
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ test.beforeEach(async ({pw}) => {
|
||||
|
||||
test('should succeed with File', async ({pw}) => {
|
||||
// # Prepare data with File
|
||||
const clientId = await pw.random.id();
|
||||
const clientId = pw.random.id();
|
||||
const formData = new FormData();
|
||||
formData.set('channel_id', townSquareChannel.id);
|
||||
formData.set('client_ids', clientId);
|
||||
@@ -40,7 +40,7 @@ test('should succeed with File', async ({pw}) => {
|
||||
|
||||
test('should succeed with Blob', async ({pw}) => {
|
||||
// # Prepare data with Blob
|
||||
const clientId = await pw.random.id();
|
||||
const clientId = pw.random.id();
|
||||
const formData = new FormData();
|
||||
formData.set('channel_id', townSquareChannel.id);
|
||||
formData.set('client_ids', clientId);
|
||||
@@ -69,7 +69,7 @@ test('should succeed even with channel_id only', async () => {
|
||||
});
|
||||
|
||||
test('should fail on invalid channel ID', async ({pw}) => {
|
||||
const clientId = await pw.random.id();
|
||||
const clientId = pw.random.id();
|
||||
|
||||
// # Set with invalid channel ID
|
||||
let formData = new FormData();
|
||||
@@ -92,7 +92,7 @@ test('should fail on invalid channel ID', async ({pw}) => {
|
||||
});
|
||||
|
||||
test('should fail on missing files', async ({pw}) => {
|
||||
const clientId = await pw.random.id();
|
||||
const clientId = pw.random.id();
|
||||
|
||||
// # Set with invalid channel ID
|
||||
const formData = new FormData();
|
||||
@@ -105,7 +105,7 @@ test('should fail on missing files', async ({pw}) => {
|
||||
});
|
||||
|
||||
test('should fail on incorrect order setting up FormData', async ({pw}) => {
|
||||
const clientId = await pw.random.id();
|
||||
const clientId = pw.random.id();
|
||||
|
||||
// # Set with files before client_ids
|
||||
const formData = new FormData();
|
||||
|
||||
@@ -11,10 +11,10 @@ import type {PlaywrightExtended} from '@mattermost/playwright-lib';
|
||||
* @objective Verify a user can create a selected-channels AI recap and receive the mocked summary without reloading the page
|
||||
*/
|
||||
test('creates selected-channels recap and auto-renders mocked summary', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapHighlight = `Deterministic highlight ${await pw.random.id()}`;
|
||||
const recapActionItem = `Deterministic action item ${await pw.random.id()}`;
|
||||
const recapTitle = `AI recap ${await pw.random.id()}`;
|
||||
const sourceMessage = `Please summarize this update ${await pw.random.id()}`;
|
||||
const recapHighlight = `Deterministic highlight ${pw.random.id()}`;
|
||||
const recapActionItem = `Deterministic action item ${pw.random.id()}`;
|
||||
const recapTitle = `AI recap ${pw.random.id()}`;
|
||||
const sourceMessage = `Please summarize this update ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state and configure one deterministic recap agent.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -82,11 +82,11 @@ test('creates selected-channels recap and auto-renders mocked summary', {tag: '@
|
||||
* @objective Verify the all-unreads recap flow skips channel selection and only summarizes unread channels
|
||||
*/
|
||||
test('creates all-unreads recap for only unread channels', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapTitle = `Unread recap ${await pw.random.id()}`;
|
||||
const sourceMessageOne = `Unread source one ${await pw.random.id()}`;
|
||||
const sourceMessageTwo = `Unread source two ${await pw.random.id()}`;
|
||||
const highlightOne = `Unread highlight one ${await pw.random.id()}`;
|
||||
const highlightTwo = `Unread highlight two ${await pw.random.id()}`;
|
||||
const recapTitle = `Unread recap ${pw.random.id()}`;
|
||||
const sourceMessageOne = `Unread source one ${pw.random.id()}`;
|
||||
const sourceMessageTwo = `Unread source two ${pw.random.id()}`;
|
||||
const highlightOne = `Unread highlight one ${pw.random.id()}`;
|
||||
const highlightTwo = `Unread highlight two ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state, clear baseline unread state, and configure two deterministic recap completions.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -190,9 +190,9 @@ test('disables recap creation when the bridge is unavailable', {tag: '@ai_recaps
|
||||
* @objective Verify marking a recap as read moves it from the Unread tab to the Read tab
|
||||
*/
|
||||
test('moves a recap from unread to read when marked read', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapTitle = `Read state recap ${await pw.random.id()}`;
|
||||
const recapHighlight = `Read state highlight ${await pw.random.id()}`;
|
||||
const sourceMessage = `Read state source ${await pw.random.id()}`;
|
||||
const recapTitle = `Read state recap ${pw.random.id()}`;
|
||||
const recapHighlight = `Read state highlight ${pw.random.id()}`;
|
||||
const sourceMessage = `Read state source ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state, configure the recap bridge, and seed a completed recap for the user.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -243,9 +243,9 @@ test('moves a recap from unread to read when marked read', {tag: '@ai_recaps'},
|
||||
* @objective Verify deleting a recap removes it from the recaps list
|
||||
*/
|
||||
test('deletes a recap from the recaps page', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapTitle = `Delete recap ${await pw.random.id()}`;
|
||||
const recapHighlight = `Delete recap highlight ${await pw.random.id()}`;
|
||||
const sourceMessage = `Delete recap source ${await pw.random.id()}`;
|
||||
const recapTitle = `Delete recap ${pw.random.id()}`;
|
||||
const recapHighlight = `Delete recap highlight ${pw.random.id()}`;
|
||||
const sourceMessage = `Delete recap source ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state, configure the recap bridge, and seed a completed recap for the user.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -292,10 +292,10 @@ test('deletes a recap from the recaps page', {tag: '@ai_recaps'}, async ({pw}) =
|
||||
* @objective Verify regenerating a recap returns it to processing and replaces the rendered summary with the latest mocked response
|
||||
*/
|
||||
test('regenerates a recap with a new mocked summary', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapTitle = `Regenerated recap ${await pw.random.id()}`;
|
||||
const firstHighlight = `Original summary ${await pw.random.id()}`;
|
||||
const secondHighlight = `Regenerated summary ${await pw.random.id()}`;
|
||||
const sourceMessage = `Regenerate source ${await pw.random.id()}`;
|
||||
const recapTitle = `Regenerated recap ${pw.random.id()}`;
|
||||
const firstHighlight = `Original summary ${pw.random.id()}`;
|
||||
const secondHighlight = `Regenerated summary ${pw.random.id()}`;
|
||||
const sourceMessage = `Regenerate source ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state, queue two recap completions, and seed the first completed recap.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -353,9 +353,9 @@ test('regenerates a recap with a new mocked summary', {tag: '@ai_recaps'}, async
|
||||
* @objective Verify a failed recap renders the failed state and can recover after regeneration
|
||||
*/
|
||||
test('recovers a failed recap through regeneration', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapTitle = `Failed recap ${await pw.random.id()}`;
|
||||
const recoveredHighlight = `Recovered highlight ${await pw.random.id()}`;
|
||||
const sourceMessage = `Failed recap source ${await pw.random.id()}`;
|
||||
const recapTitle = `Failed recap ${pw.random.id()}`;
|
||||
const recoveredHighlight = `Recovered highlight ${pw.random.id()}`;
|
||||
const sourceMessage = `Failed recap source ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state, queue a failing completion followed by a successful completion, and seed the failed recap.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -408,9 +408,9 @@ test('recovers a failed recap through regeneration', {tag: '@ai_recaps'}, async
|
||||
* @objective Verify recap channel card actions can mark a source channel as read and navigate back into that channel
|
||||
*/
|
||||
test('executes recap channel card actions', {tag: '@ai_recaps'}, async ({pw}) => {
|
||||
const recapTitle = `Channel actions recap ${await pw.random.id()}`;
|
||||
const recapHighlight = `Channel action highlight ${await pw.random.id()}`;
|
||||
const sourceMessage = `Channel action source ${await pw.random.id()}`;
|
||||
const recapTitle = `Channel actions recap ${pw.random.id()}`;
|
||||
const recapHighlight = `Channel action highlight ${pw.random.id()}`;
|
||||
const sourceMessage = `Channel action source ${pw.random.id()}`;
|
||||
|
||||
// # Initialize the test server state, configure the recap bridge, and seed a completed recap with one unread channel.
|
||||
const {adminClient, adminUser, team, user, userClient} = await pw.initSetup();
|
||||
@@ -482,13 +482,13 @@ async function setupRecapBridge(
|
||||
|
||||
const {agent, service} = await pw.createMockAIAgent(adminClient, {
|
||||
agent: {
|
||||
id: `recap-agent-${await pw.random.id()}`,
|
||||
id: `recap-agent-${pw.random.id()}`,
|
||||
displayName: 'Recap Summary Agent',
|
||||
username: `recap.summary.${await pw.random.id()}`,
|
||||
username: `recap.summary.${pw.random.id()}`,
|
||||
is_default: true,
|
||||
},
|
||||
service: {
|
||||
id: `recap-service-${await pw.random.id()}`,
|
||||
id: `recap-service-${pw.random.id()}`,
|
||||
name: 'Recap Summary Service',
|
||||
type: 'anthropic',
|
||||
},
|
||||
@@ -519,7 +519,7 @@ async function createUnreadChannelFixture(
|
||||
const channel = await adminClient.createChannel(
|
||||
pw.random.channel({
|
||||
teamId,
|
||||
name: `recap${await pw.random.id()}`,
|
||||
name: `recap${pw.random.id()}`,
|
||||
displayName,
|
||||
unique: false,
|
||||
}),
|
||||
|
||||
+20
-20
@@ -383,7 +383,7 @@ test.describe('Anonymous URLs', () => {
|
||||
await channelsPage.goto(team.name);
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
const channelDisplayName = `Archived Anonymous ${await pw.random.id()}`;
|
||||
const channelDisplayName = `Archived Anonymous ${pw.random.id()}`;
|
||||
await createChannelFromUI(channelsPage, channelDisplayName);
|
||||
|
||||
const createdChannel = await getChannelByDisplayName(adminClient, team.id, channelDisplayName);
|
||||
@@ -423,8 +423,8 @@ test.describe('Anonymous URLs', () => {
|
||||
await adminClient.addToTeam(team.id, adminUser.id);
|
||||
|
||||
// # Create a legacy channel and team before enabling anonymous URLs
|
||||
const legacyChannelSlug = `legacy-channel-${await pw.random.id()}`;
|
||||
const legacyChannelDisplayName = `Legacy Channel ${await pw.random.id()}`;
|
||||
const legacyChannelSlug = `legacy-channel-${pw.random.id()}`;
|
||||
const legacyChannelDisplayName = `Legacy Channel ${pw.random.id()}`;
|
||||
const legacyChannel = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: legacyChannelSlug,
|
||||
@@ -432,8 +432,8 @@ test.describe('Anonymous URLs', () => {
|
||||
type: 'O',
|
||||
});
|
||||
|
||||
const legacyTeamSlug = `legacy-team-${await pw.random.id()}`;
|
||||
const legacyTeamDisplayName = `Legacy Team ${await pw.random.id()}`;
|
||||
const legacyTeamSlug = `legacy-team-${pw.random.id()}`;
|
||||
const legacyTeamDisplayName = `Legacy Team ${pw.random.id()}`;
|
||||
const legacyTeam = await adminClient.createTeam({
|
||||
name: legacyTeamSlug,
|
||||
display_name: legacyTeamDisplayName,
|
||||
@@ -460,7 +460,7 @@ test.describe('Anonymous URLs', () => {
|
||||
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${legacyChannelSlug}`);
|
||||
|
||||
// # Create a new channel after the anonymous URL toggle
|
||||
const anonymousChannelDisplayName = `Anonymous Channel ${await pw.random.id()}`;
|
||||
const anonymousChannelDisplayName = `Anonymous Channel ${pw.random.id()}`;
|
||||
await channelsPage.goto(team.name);
|
||||
await channelsPage.toBeVisible();
|
||||
await createChannelFromUI(channelsPage, anonymousChannelDisplayName);
|
||||
@@ -472,7 +472,7 @@ test.describe('Anonymous URLs', () => {
|
||||
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${anonymousChannel.name}`);
|
||||
|
||||
// # Create a new team after the anonymous URL toggle
|
||||
const anonymousTeamDisplayName = `Anonymous Team ${await pw.random.id()}`;
|
||||
const anonymousTeamDisplayName = `Anonymous Team ${pw.random.id()}`;
|
||||
await createTeamFromUI(channelsPage, anonymousTeamDisplayName);
|
||||
|
||||
const anonymousTeam = await getTeamByDisplayName(adminClient, anonymousTeamDisplayName);
|
||||
@@ -504,8 +504,8 @@ test.describe('Anonymous URLs', () => {
|
||||
const dmChannel = await adminClient.createDirectChannel([adminUser.id, secondUser.id]);
|
||||
const gmChannel = await adminClient.createGroupChannel([adminUser.id, secondUser.id, thirdUser.id]);
|
||||
|
||||
const dmMessage = `Anonymous URL DM ${await pw.random.id()}`;
|
||||
const gmMessage = `Anonymous URL GM ${await pw.random.id()}`;
|
||||
const dmMessage = `Anonymous URL DM ${pw.random.id()}`;
|
||||
const gmMessage = `Anonymous URL GM ${pw.random.id()}`;
|
||||
await adminClient.createPost({channel_id: dmChannel.id, message: dmMessage});
|
||||
await adminClient.createPost({channel_id: gmChannel.id, message: gmMessage});
|
||||
|
||||
@@ -553,7 +553,7 @@ test.describe('Anonymous URLs', () => {
|
||||
await channelsPage.goto(team.name);
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
const originalDisplayName = `Original Channel ${await pw.random.id()}`;
|
||||
const originalDisplayName = `Original Channel ${pw.random.id()}`;
|
||||
await createChannelFromUI(channelsPage, originalDisplayName);
|
||||
|
||||
const createdChannel = await getChannelByDisplayName(adminClient, team.id, originalDisplayName);
|
||||
@@ -561,7 +561,7 @@ test.describe('Anonymous URLs', () => {
|
||||
expectObfuscatedSlug(originalSlug);
|
||||
|
||||
// # Rename the channel from channel settings
|
||||
const renamedDisplayName = `Renamed Channel ${await pw.random.id()}`;
|
||||
const renamedDisplayName = `Renamed Channel ${pw.random.id()}`;
|
||||
const channelSettingsModal = await channelsPage.openChannelSettings();
|
||||
const infoTab = await channelSettingsModal.openInfoTab();
|
||||
await infoTab.updateName(renamedDisplayName);
|
||||
@@ -606,7 +606,7 @@ test.describe('Anonymous URLs', () => {
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
const originalTeamDisplayName = `Original Team ${await pw.random.id()}`;
|
||||
const originalTeamDisplayName = `Original Team ${pw.random.id()}`;
|
||||
await createTeamFromUI(channelsPage, originalTeamDisplayName);
|
||||
|
||||
const createdTeam = await getTeamByDisplayName(adminClient, originalTeamDisplayName);
|
||||
@@ -614,7 +614,7 @@ test.describe('Anonymous URLs', () => {
|
||||
expectObfuscatedSlug(originalTeamSlug);
|
||||
|
||||
// # Rename the team from team settings
|
||||
const renamedTeamDisplayName = `Renamed Team ${await pw.random.id()}`;
|
||||
const renamedTeamDisplayName = `Renamed Team ${pw.random.id()}`;
|
||||
const teamSettingsModal = await channelsPage.openTeamSettings();
|
||||
const infoTab = await teamSettingsModal.openInfoTab();
|
||||
await infoTab.updateName(renamedTeamDisplayName);
|
||||
@@ -662,11 +662,11 @@ test.describe('Anonymous URLs', () => {
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
const displayName = `Permalink Channel ${await pw.random.id()}`;
|
||||
const displayName = `Permalink Channel ${pw.random.id()}`;
|
||||
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
||||
|
||||
// # Publish a post that will be opened via permalink
|
||||
const message = `Anonymous permalink message ${await pw.random.id()}`;
|
||||
const message = `Anonymous permalink message ${pw.random.id()}`;
|
||||
await channelsPage.postMessage(message);
|
||||
|
||||
const lastPost = await channelsPage.getLastPost();
|
||||
@@ -708,7 +708,7 @@ test.describe('Anonymous URLs', () => {
|
||||
|
||||
const createdChannels = [];
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const displayName = `Search Test Channel ${i} ${await pw.random.id()}`;
|
||||
const displayName = `Search Test Channel ${i} ${pw.random.id()}`;
|
||||
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
||||
createdChannels.push({channel, displayName});
|
||||
}
|
||||
@@ -749,9 +749,9 @@ test.describe('Anonymous URLs', () => {
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
const displayName = `Search Channel ${await pw.random.id()}`;
|
||||
const displayName = `Search Channel ${pw.random.id()}`;
|
||||
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
||||
const message = `AnonymousSearchableMessage${await pw.random.id()}`;
|
||||
const message = `AnonymousSearchableMessage${pw.random.id()}`;
|
||||
await channelsPage.postMessage(message);
|
||||
|
||||
const lastPost = await channelsPage.getLastPost();
|
||||
@@ -800,7 +800,7 @@ test.describe('Anonymous URLs', () => {
|
||||
const createdChannels = [];
|
||||
|
||||
for (const template of channelTemplates) {
|
||||
const displayName = `${template} ${(await pw.random.id()).slice(0, 6)}`;
|
||||
const displayName = `${template} ${pw.random.id().slice(0, 6)}`;
|
||||
const channel = await createAnonymousUrlChannel(
|
||||
channelsPage,
|
||||
adminClient,
|
||||
@@ -871,7 +871,7 @@ test.describe('Anonymous URLs', () => {
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
const displayName = `Calls & Planning 🚀 ${(await pw.random.id()).slice(0, 6)}`;
|
||||
const displayName = `Calls & Planning 🚀 ${pw.random.id().slice(0, 6)}`;
|
||||
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
||||
await adminClient.addToChannel(adminUser.id, channel.id);
|
||||
|
||||
|
||||
+17
-18
@@ -12,7 +12,6 @@ import {
|
||||
test,
|
||||
setMockSourceLanguage,
|
||||
} from '@mattermost/playwright-lib';
|
||||
import {getRandomId} from 'utils/utils';
|
||||
|
||||
const POST_TYPE_AUTOTRANSLATION_CHANGE = 'system_autotranslation';
|
||||
|
||||
@@ -38,7 +37,7 @@ test(
|
||||
});
|
||||
|
||||
// # Create a channel and enable autotranslation on it
|
||||
const channelName = `autotranslation-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -104,7 +103,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-admin-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-admin-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -147,7 +146,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-system-msg-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-system-msg-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -191,7 +190,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-new-only-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-new-only-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -261,7 +260,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-tooltip-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-tooltip-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -301,7 +300,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-disable-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-disable-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -371,7 +370,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-default-on-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-default-on-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -434,7 +433,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-ephemeral-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-ephemeral-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -478,7 +477,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-revert-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-revert-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -556,7 +555,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-lang-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-lang-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -659,7 +658,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-indicator-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-indicator-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -760,7 +759,7 @@ test.fixme(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-modal-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-modal-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -854,7 +853,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-dotmenu-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-dotmenu-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -947,7 +946,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-toggle-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-toggle-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
@@ -996,7 +995,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const translatedChannelName = `autotranslation-badge-${await getRandomId()}`;
|
||||
const translatedChannelName = `autotranslation-badge-${pw.random.id()}`;
|
||||
const translatedChannel = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: translatedChannelName,
|
||||
@@ -1007,7 +1006,7 @@ test(
|
||||
await adminClient.addToChannel(user.id, translatedChannel.id);
|
||||
await setUserChannelAutotranslation(userClient, translatedChannel.id, true);
|
||||
|
||||
const noTranslationChannelName = `no-translation-${await getRandomId()}`;
|
||||
const noTranslationChannelName = `no-translation-${pw.random.id()}`;
|
||||
const noTranslationChannel = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: noTranslationChannelName,
|
||||
@@ -1047,7 +1046,7 @@ test(
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-unsupported-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-unsupported-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ import {
|
||||
test,
|
||||
SystemConsolePage,
|
||||
} from '@mattermost/playwright-lib';
|
||||
import {getRandomId} from 'utils/utils';
|
||||
|
||||
test(
|
||||
'permission exists; Channel Administrators have Manage Channel Auto Translation ON',
|
||||
@@ -89,7 +88,7 @@ test.describe('autotranslation configuration tests', () => {
|
||||
targetLanguages: ['en', 'es'],
|
||||
});
|
||||
|
||||
const channelName = `autotranslation-perm-${await getRandomId()}`;
|
||||
const channelName = `autotranslation-perm-${pw.random.id()}`;
|
||||
const created = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName,
|
||||
|
||||
+23
-14
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {Page} from '@playwright/test';
|
||||
import type {Disposable, Page} from '@playwright/test';
|
||||
|
||||
interface MockTranslateRequest {
|
||||
q?: string;
|
||||
@@ -117,8 +117,15 @@ export function resetMockSourceLanguage(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock the autotranslation API route using Playwright's route interception
|
||||
* This replaces the need for an external mock server
|
||||
* Mock the autotranslation API route using Playwright's route interception.
|
||||
* This replaces the need for an external mock server.
|
||||
*
|
||||
* Returns a `Disposable` to remove the mock routes. Use with `await using`
|
||||
* for automatic cleanup or call `.dispose()` manually:
|
||||
*
|
||||
* ```ts
|
||||
* await using mock = await mockAutotranslationRoute(page);
|
||||
* ```
|
||||
*
|
||||
* @param page - Playwright Page object
|
||||
* @param options - Optional configuration
|
||||
@@ -131,7 +138,7 @@ export async function mockAutotranslationRoute(
|
||||
sourceLanguage?: string;
|
||||
supportedLanguages?: string[];
|
||||
},
|
||||
): Promise<void> {
|
||||
): Promise<Disposable> {
|
||||
// Reset mockSourceLanguage to avoid state leakage between tests
|
||||
mockSourceLanguage = options?.sourceLanguage || 'es';
|
||||
|
||||
@@ -139,7 +146,7 @@ export async function mockAutotranslationRoute(
|
||||
|
||||
// Mock LibreTranslate API endpoint
|
||||
// Handles both /translate and /detect endpoints
|
||||
await page.route('**/api/translate', async (route) => {
|
||||
const translateRoute = await page.route('**/api/translate', async (route) => {
|
||||
const request = route.request();
|
||||
const method = request.method();
|
||||
|
||||
@@ -254,7 +261,7 @@ export async function mockAutotranslationRoute(
|
||||
});
|
||||
|
||||
// Mock language detection endpoint (if used separately)
|
||||
await page.route('**/api/detect', async (route) => {
|
||||
const detectRoute = await page.route('**/api/detect', async (route) => {
|
||||
// Language detection is mocked to always return the configured source language
|
||||
// regardless of the input text
|
||||
|
||||
@@ -272,13 +279,15 @@ export async function mockAutotranslationRoute(
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all mock routes for autotranslation
|
||||
* Useful for cleaning up between tests or switching to real API
|
||||
*/
|
||||
export async function unmockAutotranslationRoute(page: Page): Promise<void> {
|
||||
await page.unroute('**/api/translate');
|
||||
await page.unroute('**/api/detect');
|
||||
return {
|
||||
async dispose() {
|
||||
await translateRoute.dispose();
|
||||
await detectRoute.dispose();
|
||||
},
|
||||
async [Symbol.asyncDispose]() {
|
||||
await translateRoute.dispose();
|
||||
await detectRoute.dispose();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
|
||||
// # Enable BoR and send message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const secretMessage = `DM secret ${await pw.random.id()}`;
|
||||
const secretMessage = `DM secret ${pw.random.id()}`;
|
||||
await senderPage.postMessage(secretMessage);
|
||||
|
||||
// # Get sender's view of the post
|
||||
@@ -128,7 +128,7 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
|
||||
// # Enable BoR and send message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const secretMessage = `GM secret ${await pw.random.id()}`;
|
||||
const secretMessage = `GM secret ${pw.random.id()}`;
|
||||
await senderPage.postMessage(secretMessage);
|
||||
|
||||
// # Get sender's view
|
||||
@@ -195,7 +195,7 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
|
||||
// # Enable BoR and send message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const secretMessage = `To be deleted ${await pw.random.id()}`;
|
||||
const secretMessage = `To be deleted ${pw.random.id()}`;
|
||||
await senderPage.postMessage(secretMessage);
|
||||
|
||||
// # Get the post ID
|
||||
@@ -247,7 +247,7 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
|
||||
// # Enable BoR and send message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const secretMessage = `Receiver will burn ${await pw.random.id()}`;
|
||||
const secretMessage = `Receiver will burn ${pw.random.id()}`;
|
||||
await senderPage.postMessage(secretMessage);
|
||||
|
||||
// # Login as receiver
|
||||
@@ -302,7 +302,7 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
|
||||
// # Enable BoR and send message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const secretMessage = `Count test ${await pw.random.id()}`;
|
||||
const secretMessage = `Count test ${pw.random.id()}`;
|
||||
await senderPage.postMessage(secretMessage);
|
||||
|
||||
// # Get sender's view
|
||||
@@ -332,12 +332,12 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
|
||||
// # Send first BoR message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message1 = `First BoR ${await pw.random.id()}`;
|
||||
const message1 = `First BoR ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message1);
|
||||
|
||||
// # Send second BoR message (toggle again to ensure BoR is on)
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message2 = `Second BoR ${await pw.random.id()}`;
|
||||
const message2 = `Second BoR ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message2);
|
||||
|
||||
// # Login as receiver and verify they can see concealed messages
|
||||
@@ -395,7 +395,7 @@ test.describe('Burn-on-Read in DMs and GMs', () => {
|
||||
expect(isEnabledBefore).toBe(true);
|
||||
|
||||
// # Send a message
|
||||
const message1 = `BoR message ${await pw.random.id()}`;
|
||||
const message1 = `BoR message ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message1);
|
||||
|
||||
// * Verify BoR is disabled after sending (toggle resets)
|
||||
|
||||
@@ -23,7 +23,7 @@ test.describe('Burn-on-Read Receiver Flow', () => {
|
||||
|
||||
// # Post BoR message
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const secretMessage = `Secret message ${await pw.random.id()}`;
|
||||
const secretMessage = `Secret message ${pw.random.id()}`;
|
||||
await senderPage.postMessage(secretMessage);
|
||||
|
||||
// # Login as receiver in new context
|
||||
@@ -72,7 +72,7 @@ test.describe('Burn-on-Read Receiver Flow', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message = `To be burned ${await pw.random.id()}`;
|
||||
const message = `To be burned ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message);
|
||||
|
||||
// # Login as receiver
|
||||
@@ -126,7 +126,7 @@ test.describe('Burn-on-Read Receiver Flow', () => {
|
||||
await senderPage.toBeVisible();
|
||||
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message = `Test message ${await pw.random.id()}`;
|
||||
const message = `Test message ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message);
|
||||
|
||||
// # Login as receiver
|
||||
@@ -180,7 +180,7 @@ test.describe('Burn-on-Read Receiver Flow', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message = `Timer test ${await pw.random.id()}`;
|
||||
const message = `Timer test ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message);
|
||||
|
||||
// # Login as receiver
|
||||
@@ -241,7 +241,7 @@ test.describe('Burn-on-Read Receiver Flow', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message = `Auto-delete test ${await pw.random.id()}`;
|
||||
const message = `Auto-delete test ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message);
|
||||
|
||||
// # Login as receiver
|
||||
@@ -295,7 +295,7 @@ test.describe('Burn-on-Read Receiver Flow', () => {
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message = `Badge test ${await pw.random.id()}`;
|
||||
const message = `Badge test ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message);
|
||||
|
||||
// # Login as receiver
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No reply test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No reply test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
@@ -44,7 +44,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No pin test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No pin test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
@@ -72,7 +72,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No edit test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No edit test ${pw.random.id()}`);
|
||||
|
||||
const borPost = await senderPage.getLastPost();
|
||||
|
||||
@@ -94,7 +94,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No forward test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No forward test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
@@ -122,7 +122,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No copy test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No copy test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
@@ -150,7 +150,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No copy link test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No copy link test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
@@ -178,7 +178,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`Sender copy link test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`Sender copy link test ${pw.random.id()}`);
|
||||
|
||||
const borPost = await senderPage.getLastPost();
|
||||
|
||||
@@ -200,7 +200,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`No follow test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`No follow test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
@@ -228,7 +228,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
const message = `Keyboard test ${await pw.random.id()}`;
|
||||
const message = `Keyboard test ${pw.random.id()}`;
|
||||
await senderPage.postMessage(message);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
@@ -260,7 +260,7 @@ test.describe('Burn-on-Read Restrictions', () => {
|
||||
await senderPage.goto(team.name, `@${receiver.username}`);
|
||||
await senderPage.toBeVisible();
|
||||
await senderPage.centerView.postCreate.toggleBurnOnRead();
|
||||
await senderPage.postMessage(`Delete test ${await pw.random.id()}`);
|
||||
await senderPage.postMessage(`Delete test ${pw.random.id()}`);
|
||||
|
||||
const {channelsPage: receiverPage} = await pw.testBrowser.login(receiver);
|
||||
await receiverPage.goto(team.name, `@${sender.username}`);
|
||||
|
||||
+2
-3
@@ -2,7 +2,6 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {test} from '@mattermost/playwright-lib';
|
||||
import {getRandomId} from 'utils/utils';
|
||||
|
||||
test('Should show channel banner when configured', async ({pw}) => {
|
||||
const {adminUser, adminClient} = await pw.initSetup();
|
||||
@@ -13,7 +12,7 @@ test('Should show channel banner when configured', async ({pw}) => {
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
await channelsPage.newChannel(await getRandomId(), 'O');
|
||||
await channelsPage.newChannel(pw.random.id(), 'O');
|
||||
|
||||
let channelSettingsModal = await channelsPage.openChannelSettings();
|
||||
let configurationTab = await channelSettingsModal.openConfigurationTab();
|
||||
@@ -58,7 +57,7 @@ test('Should render markdown', async ({pw}) => {
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
await channelsPage.newChannel(await getRandomId(), 'O');
|
||||
await channelsPage.newChannel(pw.random.id(), 'O');
|
||||
|
||||
const channelSettingsModal = await channelsPage.openChannelSettings();
|
||||
const configurationTab = await channelSettingsModal.openConfigurationTab();
|
||||
|
||||
@@ -15,7 +15,7 @@ test('MM-65630-1 Search results should show popout button that opens results in
|
||||
);
|
||||
await adminClient.addToChannel(user.id, channel.id);
|
||||
|
||||
const uniqueText = `popout-search-test-${await pw.random.id()}`;
|
||||
const uniqueText = `popout-search-test-${pw.random.id()}`;
|
||||
await adminClient.createPost({
|
||||
channel_id: channel.id,
|
||||
message: uniqueText,
|
||||
@@ -64,7 +64,7 @@ test('MM-65630-2 Recent mentions popout should open with the right results', asy
|
||||
);
|
||||
await adminClient.addToChannel(user.id, channel.id);
|
||||
|
||||
const mentionText = `hey @${user.username} check this mention-${await pw.random.id()}`;
|
||||
const mentionText = `hey @${user.username} check this mention-${pw.random.id()}`;
|
||||
await adminClient.createPost({
|
||||
channel_id: channel.id,
|
||||
message: mentionText,
|
||||
@@ -111,7 +111,7 @@ test('MM-65630-3 Saved messages popout should open with the right results', asyn
|
||||
);
|
||||
await adminClient.addToChannel(user.id, channel.id);
|
||||
|
||||
const savedText = `saved-message-test-${await pw.random.id()}`;
|
||||
const savedText = `saved-message-test-${pw.random.id()}`;
|
||||
const post = await adminClient.createPost({
|
||||
channel_id: channel.id,
|
||||
message: savedText,
|
||||
@@ -167,7 +167,7 @@ test('MM-65630-4 Search popout should not show popout button in the popout windo
|
||||
);
|
||||
await adminClient.addToChannel(user.id, channel.id);
|
||||
|
||||
const uniqueText = `no-button-test-${await pw.random.id()}`;
|
||||
const uniqueText = `no-button-test-${pw.random.id()}`;
|
||||
await adminClient.createPost({
|
||||
channel_id: channel.id,
|
||||
message: uniqueText,
|
||||
|
||||
+1
-6
@@ -7,12 +7,7 @@ let keywords: string[];
|
||||
const highlightWithoutNotificationClass = 'non-notification-highlight';
|
||||
|
||||
test.beforeEach(async ({pw}) => {
|
||||
keywords = [
|
||||
`AB${await pw.random.id()}`,
|
||||
`CD${await pw.random.id()}`,
|
||||
`EF${await pw.random.id()}`,
|
||||
`Highlight me ${await pw.random.id()}`,
|
||||
];
|
||||
keywords = [`AB${pw.random.id()}`, `CD${pw.random.id()}`, `EF${pw.random.id()}`, `Highlight me ${pw.random.id()}`];
|
||||
});
|
||||
|
||||
test('MM-T5465-1 Should add the keyword when enter, comma or tab is pressed on the textbox', async ({pw}) => {
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ test('MM-66937 Invite modal results match the current input state', async ({pw})
|
||||
const {adminUser, adminClient, team} = await pw.initSetup();
|
||||
|
||||
// # Create two users not on the team whose usernames share a prefix but differ at the end
|
||||
const randomPrefix = await pw.random.id(8);
|
||||
const randomPrefix = pw.random.id(8);
|
||||
const user1 = await adminClient.createUser(await pw.random.user(randomPrefix + 'a'), '', '');
|
||||
const user2 = await adminClient.createUser(await pw.random.user(randomPrefix + 'b'), '', '');
|
||||
|
||||
@@ -63,7 +63,7 @@ test('MM-66937 Invite modal results match the current input state when typing in
|
||||
const {adminUser, adminClient, team} = await pw.initSetup();
|
||||
|
||||
// # Create a users with a Korean name (plus a prefix to avoid interfering test runs)
|
||||
const randomPrefix = await pw.random.id(8);
|
||||
const randomPrefix = pw.random.id(8);
|
||||
const user = await adminClient.createUser(
|
||||
{
|
||||
...(await pw.random.user(randomPrefix + 'a')),
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ test('MM-T388 Invite new user to closed team with email domain restriction', {ta
|
||||
await membersInvitedModal.clickInviteMore();
|
||||
|
||||
// # Invite a user with an invalid email domain (not sample.mattermost.com)
|
||||
const invalidEmail = `user.${await pw.random.id()}@invalid.com`;
|
||||
const invalidEmail = `user.${pw.random.id()}@invalid.com`;
|
||||
await inviteModal.inviteByEmail(invalidEmail);
|
||||
|
||||
// * Verify that the invite failed with the correct domain restriction error
|
||||
|
||||
+6
-6
@@ -62,7 +62,7 @@ test.describe('Team Settings Modal - Complete Test Suite', () => {
|
||||
await expect(teamSettings.infoSettings.nameInput).toHaveValue(team.display_name);
|
||||
|
||||
// # Edit team name
|
||||
const newTeamName = `Updated Team ${await pw.random.id()}`;
|
||||
const newTeamName = `Updated Team ${pw.random.id()}`;
|
||||
await teamSettings.infoSettings.updateName(newTeamName);
|
||||
|
||||
// # Save changes
|
||||
@@ -100,7 +100,7 @@ test.describe('Team Settings Modal - Complete Test Suite', () => {
|
||||
const teamSettings = await channelsPage.openTeamSettings();
|
||||
|
||||
// # Edit team description
|
||||
const newDescription = `Test description ${await pw.random.id()}`;
|
||||
const newDescription = `Test description ${pw.random.id()}`;
|
||||
await teamSettings.infoSettings.updateDescription(newDescription);
|
||||
|
||||
// # Save changes
|
||||
@@ -138,7 +138,7 @@ test.describe('Team Settings Modal - Complete Test Suite', () => {
|
||||
const teamSettings = await channelsPage.openTeamSettings();
|
||||
|
||||
// # Edit team name to create unsaved changes
|
||||
const newTeamName = `Modified Team ${await pw.random.id()}`;
|
||||
const newTeamName = `Modified Team ${pw.random.id()}`;
|
||||
await teamSettings.infoSettings.updateName(newTeamName);
|
||||
|
||||
// # Try to close modal (first attempt)
|
||||
@@ -181,7 +181,7 @@ test.describe('Team Settings Modal - Complete Test Suite', () => {
|
||||
await expect(teamSettings.accessTab).toBeVisible();
|
||||
|
||||
// # Edit team name in Info tab (create unsaved changes)
|
||||
const newTeamName = `Modified Team ${await pw.random.id()}`;
|
||||
const newTeamName = `Modified Team ${pw.random.id()}`;
|
||||
await teamSettings.infoSettings.updateName(newTeamName);
|
||||
|
||||
// # Try to switch to Access tab
|
||||
@@ -219,7 +219,7 @@ test.describe('Team Settings Modal - Complete Test Suite', () => {
|
||||
const teamSettings = await channelsPage.openTeamSettings();
|
||||
|
||||
// # Edit team name
|
||||
const newTeamName = `Updated Team ${await pw.random.id()}`;
|
||||
const newTeamName = `Updated Team ${pw.random.id()}`;
|
||||
await teamSettings.infoSettings.updateName(newTeamName);
|
||||
|
||||
// # Save changes
|
||||
@@ -257,7 +257,7 @@ test.describe('Team Settings Modal - Complete Test Suite', () => {
|
||||
const teamSettings = await channelsPage.openTeamSettings();
|
||||
|
||||
// # Edit team name
|
||||
const newTeamName = `Modified Team ${await pw.random.id()}`;
|
||||
const newTeamName = `Modified Team ${pw.random.id()}`;
|
||||
await teamSettings.infoSettings.updateName(newTeamName);
|
||||
|
||||
// * Verify input shows new value
|
||||
|
||||
@@ -61,7 +61,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policy1Name = `LDAP AutoAdd Single ${await pw.random.id()}`;
|
||||
const policy1Name = `LDAP AutoAdd Single ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policy1Name,
|
||||
attribute: 'Department',
|
||||
@@ -134,7 +134,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
|
||||
// Create policy with contains operator: Department contains "Eng"
|
||||
const policy2Name = `LDAP AutoAdd Contains ${await pw.random.id()}`;
|
||||
const policy2Name = `LDAP AutoAdd Contains ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy2Name,
|
||||
celExpression: 'user.attributes.Department.contains("Eng")',
|
||||
@@ -240,7 +240,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policy1Name = `LDAP Sync Equals ${await pw.random.id()}`;
|
||||
const policy1Name = `LDAP Sync Equals ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policy1Name,
|
||||
attribute: 'Department',
|
||||
@@ -311,7 +311,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
|
||||
// Create policy with 'in' operator (user.attributes.Department in ["Engineering", "Product"])
|
||||
const policy2Name = `LDAP Sync In ${await pw.random.id()}`;
|
||||
const policy2Name = `LDAP Sync In ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy2Name,
|
||||
celExpression: 'user.attributes.Department in ["Engineering", "Product"]',
|
||||
@@ -416,7 +416,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policy1Name = `LDAP Remove StartsWith ${await pw.random.id()}`;
|
||||
const policy1Name = `LDAP Remove StartsWith ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy1Name,
|
||||
celExpression: 'user.attributes.Department.startsWith("Eng")',
|
||||
@@ -484,7 +484,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
|
||||
// Create policy with TWO attributes: Department == "Engineering"
|
||||
// Note: Using single attribute with == since we can't reliably set multiple different attribute types
|
||||
const policy2Name = `LDAP Remove TwoAttr ${await pw.random.id()}`;
|
||||
const policy2Name = `LDAP Remove TwoAttr ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policy2Name,
|
||||
attribute: 'Department',
|
||||
@@ -571,7 +571,7 @@ test.describe('ABAC LDAP Integration - Sync', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policyName = `Dynamic Policy ${await pw.random.id()}`;
|
||||
const policyName = `Dynamic Policy ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
|
||||
+7
-7
@@ -117,7 +117,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
// Create policy with just Department (Text) first to verify users have attributes
|
||||
const policyName = `Multi-Attr Policy ${await pw.random.id()}`;
|
||||
const policyName = `Multi-Attr Policy ${pw.random.id()}`;
|
||||
|
||||
// Start with just Text attribute to debug
|
||||
// User 1 and 2 have Department=Engineering, User 3 has Department=Sales
|
||||
@@ -292,7 +292,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
const channel1 = await createPrivateChannelForABAC(adminClient, team.id);
|
||||
await adminClient.addToChannel(salesUser.id, channel1.id); // Sales user in channel initially
|
||||
|
||||
const policy1Name = `IsNot Policy ${await pw.random.id()}`;
|
||||
const policy1Name = `IsNot Policy ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy1Name,
|
||||
celExpression: 'user.attributes.Department != "Sales"',
|
||||
@@ -345,7 +345,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
const channel2 = await createPrivateChannelForABAC(adminClient, team.id);
|
||||
await adminClient.addToChannel(salesUser.id, channel2.id); // Sales user in channel initially
|
||||
|
||||
const policy2Name = `In Policy ${await pw.random.id()}`;
|
||||
const policy2Name = `In Policy ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy2Name,
|
||||
celExpression: 'user.attributes.Department in ["Engineering", "DevOps"]',
|
||||
@@ -396,7 +396,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
const channel3 = await createPrivateChannelForABAC(adminClient, team.id);
|
||||
await adminClient.addToChannel(salesUser.id, channel3.id);
|
||||
|
||||
const policy3Name = `StartsWith Policy ${await pw.random.id()}`;
|
||||
const policy3Name = `StartsWith Policy ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy3Name,
|
||||
celExpression: 'user.attributes.Department.startsWith("Eng")',
|
||||
@@ -447,7 +447,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
const channel4 = await createPrivateChannelForABAC(adminClient, team.id);
|
||||
await adminClient.addToChannel(salesUser.id, channel4.id);
|
||||
|
||||
const policy4Name = `EndsWith Policy ${await pw.random.id()}`;
|
||||
const policy4Name = `EndsWith Policy ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy4Name,
|
||||
celExpression: 'user.attributes.Department.endsWith("ing")',
|
||||
@@ -498,7 +498,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
const channel5 = await createPrivateChannelForABAC(adminClient, team.id);
|
||||
await adminClient.addToChannel(salesUser.id, channel5.id);
|
||||
|
||||
const policy5Name = `Contains Policy ${await pw.random.id()}`;
|
||||
const policy5Name = `Contains Policy ${pw.random.id()}`;
|
||||
await createAdvancedPolicy(systemConsolePage.page, {
|
||||
name: policy5Name,
|
||||
celExpression: 'user.attributes.Department.contains("gineer")',
|
||||
@@ -632,7 +632,7 @@ test.describe('ABAC Policies - Advanced Policies', () => {
|
||||
|
||||
// # Create policy with complex CEL expression using || and ()
|
||||
// Expression: Department == "Engineering" OR (Department == "Sales" AND Location == "Remote")
|
||||
const policyName = `Complex Policy ${await pw.random.id()}`;
|
||||
const policyName = `Complex Policy ${pw.random.id()}`;
|
||||
const complexExpression =
|
||||
'user.attributes.Department == "Engineering" || (user.attributes.Department == "Sales" && user.attributes.Location == "Remote")';
|
||||
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ test.describe('ABAC Policies - Channel Integration', () => {
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
// Create policy without channels (we'll link via Channel Config)
|
||||
const policyName = `Channel Config Policy ${await pw.random.id()}`;
|
||||
const policyName = `Channel Config Policy ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
@@ -224,7 +224,7 @@ test.describe('ABAC Policies - Channel Integration', () => {
|
||||
await navigateToABACPage(page);
|
||||
await enableABAC(page);
|
||||
|
||||
const policyName = `ABAC-GroupSync-Test-${await pw.random.id()}`;
|
||||
const policyName = `ABAC-GroupSync-Test-${pw.random.id()}`;
|
||||
await createBasicPolicy(page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
@@ -239,7 +239,7 @@ test.describe('ABAC Policies - Channel Integration', () => {
|
||||
// This works without LDAP configuration
|
||||
// ===========================================
|
||||
|
||||
const groupSyncChannelName = `ABAC-GroupSync-${await pw.random.id()}`;
|
||||
const groupSyncChannelName = `ABAC-GroupSync-${pw.random.id()}`;
|
||||
const groupSyncChannel = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: groupSyncChannelName.toLowerCase().replace(/[^a-z0-9]/g, ''),
|
||||
|
||||
+3
-3
@@ -101,7 +101,7 @@ test.describe('ABAC Policies - Create Policies', () => {
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
// Use the working createBasicPolicy helper (same as MM-T5784)
|
||||
const policyName = `Engineering Policy ${await pw.random.id()}`;
|
||||
const policyName = `Engineering Policy ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
@@ -279,7 +279,7 @@ test.describe('ABAC Policies - Create Policies', () => {
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
// Use createBasicPolicy with autoSync: true
|
||||
const policyName = `Auto-Add Policy ${await pw.random.id()}`;
|
||||
const policyName = `Auto-Add Policy ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
@@ -388,7 +388,7 @@ test.describe('ABAC Policies - Create Policies', () => {
|
||||
await enableABAC(page);
|
||||
|
||||
// Create the first policy
|
||||
const policyName = `Duplicate Test ${await pw.random.id()}`;
|
||||
const policyName = `Duplicate Test ${pw.random.id()}`;
|
||||
await createBasicPolicy(page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ test.describe('ABAC Policy Management - Delete Policies', () => {
|
||||
// 1. policyWithChannel - has a channel assigned
|
||||
// 2. policyWithoutChannel - has NO channels assigned
|
||||
// ===========================================
|
||||
const uniqueId = await pw.random.id();
|
||||
const uniqueId = pw.random.id();
|
||||
const policyWithChannelName = `ABAC-WithChannel-${uniqueId}`;
|
||||
const policyWithoutChannelName = `ABAC-NoChannel-${uniqueId}`;
|
||||
|
||||
|
||||
+6
-6
@@ -67,7 +67,7 @@ test.describe('ABAC Policy Management - Edit Policies', () => {
|
||||
await adminClient.addToTeam(team.id, salesUser.id);
|
||||
|
||||
// Create channel - use direct API call for more control
|
||||
const channelName = `abac-edit-test-${await pw.random.id()}`;
|
||||
const channelName = `abac-edit-test-${pw.random.id()}`;
|
||||
|
||||
const privateChannel = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
@@ -106,7 +106,7 @@ test.describe('ABAC Policy Management - Edit Policies', () => {
|
||||
// ===========================================
|
||||
// SETUP: Create policy with ORIGINAL value (Engineering), Auto-add OFF
|
||||
// ===========================================
|
||||
const policyName = `ABAC-Edit-Test-${await pw.random.id()}`;
|
||||
const policyName = `ABAC-Edit-Test-${pw.random.id()}`;
|
||||
|
||||
await createBasicPolicy(page, {
|
||||
name: policyName,
|
||||
@@ -392,7 +392,7 @@ test.describe('ABAC Policy Management - Edit Policies', () => {
|
||||
// PRECONDITION: Create ORIGINAL policy with ONE attribute (Department=Engineering)
|
||||
// Auto-add ON so users are auto-added
|
||||
// ===========================================
|
||||
const policyName = `ABAC-AddAttr-Test-${await pw.random.id()}`;
|
||||
const policyName = `ABAC-AddAttr-Test-${pw.random.id()}`;
|
||||
|
||||
await createBasicPolicy(page, {
|
||||
name: policyName,
|
||||
@@ -652,7 +652,7 @@ test.describe('ABAC Policy Management - Edit Policies', () => {
|
||||
// Department=Engineering AND Office=Remote
|
||||
// Auto-add ON
|
||||
// ===========================================
|
||||
const policyName = `ABAC-RemoveRule-${await pw.random.id()}`;
|
||||
const policyName = `ABAC-RemoveRule-${pw.random.id()}`;
|
||||
|
||||
// Use advanced mode for multi-attribute policy
|
||||
await createAdvancedPolicy(page, {
|
||||
@@ -837,7 +837,7 @@ test.describe('ABAC Policy Management - Edit Policies', () => {
|
||||
await enableABAC(page);
|
||||
|
||||
// Create two policies with different names
|
||||
const policyName1 = `Edit Dup Test A ${await pw.random.id()}`;
|
||||
const policyName1 = `Edit Dup Test A ${pw.random.id()}`;
|
||||
await createBasicPolicy(page, {
|
||||
name: policyName1,
|
||||
attribute: 'Department',
|
||||
@@ -850,7 +850,7 @@ test.describe('ABAC Policy Management - Edit Policies', () => {
|
||||
await navigateToABACPage(page);
|
||||
|
||||
const privateChannel2 = await createPrivateChannelForABAC(adminClient, team.id);
|
||||
const policyName2 = `Edit Dup Test B ${await pw.random.id()}`;
|
||||
const policyName2 = `Edit Dup Test B ${pw.random.id()}`;
|
||||
await createBasicPolicy(page, {
|
||||
name: policyName2,
|
||||
attribute: 'Department',
|
||||
|
||||
+4
-4
@@ -71,7 +71,7 @@ test.describe('ABAC User Attributes - Attribute Changes', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policyName = `Engineering Access ${await pw.random.id()}`;
|
||||
const policyName = `Engineering Access ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
@@ -219,7 +219,7 @@ test.describe('ABAC User Attributes - Attribute Changes', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policyName = `Engineering Manual Add ${await pw.random.id()}`;
|
||||
const policyName = `Engineering Manual Add ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policyName,
|
||||
attribute: 'Department',
|
||||
@@ -309,7 +309,7 @@ test.describe('ABAC User Attributes - Attribute Changes', () => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
await enableABAC(systemConsolePage.page);
|
||||
|
||||
const policy1Name = `Engineering Access NoAutoAdd ${await pw.random.id()}`;
|
||||
const policy1Name = `Engineering Access NoAutoAdd ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policy1Name,
|
||||
attribute: 'Department',
|
||||
@@ -387,7 +387,7 @@ test.describe('ABAC User Attributes - Attribute Changes', () => {
|
||||
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
|
||||
const policy2Name = `Engineering Access WithAutoAdd ${await pw.random.id()}`;
|
||||
const policy2Name = `Engineering Access WithAutoAdd ${pw.random.id()}`;
|
||||
await createBasicPolicy(systemConsolePage.page, {
|
||||
name: policy2Name,
|
||||
attribute: 'Department',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {type PlaywrightExtended, expect, newTestPassword, test} from '@mattermost/playwright-lib';
|
||||
import {type PlaywrightExtended, expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test('MM-T5520-1 should activate and deactivate users', async ({pw}) => {
|
||||
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
|
||||
@@ -108,13 +108,13 @@ test('MM-T5520-4 should reset the users password', async ({pw}) => {
|
||||
await actionMenu.clickResetPassword();
|
||||
|
||||
// # Enter a random password and click Reset
|
||||
await systemConsolePage.users.resetPasswordModal.fillPassword(newTestPassword());
|
||||
await systemConsolePage.users.resetPasswordModal.fillPassword(pw.newTestPassword());
|
||||
await systemConsolePage.users.resetPasswordModal.reset();
|
||||
});
|
||||
|
||||
test('MM-T5520-5 should change the users email', async ({pw}) => {
|
||||
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
|
||||
const newEmail = `${await pw.random.id()}@example.com`;
|
||||
const newEmail = `${pw.random.id()}@example.com`;
|
||||
|
||||
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
|
||||
|
||||
|
||||
+2
-2
@@ -143,7 +143,7 @@ test(
|
||||
}
|
||||
|
||||
// # Create two channels
|
||||
const ch1Name = `count-ch1-${await pw.random.id()}`;
|
||||
const ch1Name = `count-ch1-${pw.random.id()}`;
|
||||
const channel1 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: ch1Name.toLowerCase().replace(/[^a-z0-9-]/g, ''),
|
||||
@@ -151,7 +151,7 @@ test(
|
||||
type: 'O',
|
||||
});
|
||||
|
||||
const ch2Name = `count-ch2-${await pw.random.id()}`;
|
||||
const ch2Name = `count-ch2-${pw.random.id()}`;
|
||||
const channel2 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: ch2Name.toLowerCase().replace(/[^a-z0-9-]/g, ''),
|
||||
|
||||
+3
-3
@@ -204,7 +204,7 @@ test(
|
||||
}
|
||||
|
||||
// # Create a channel
|
||||
const channelName = `guest-ch-${await pw.random.id()}`;
|
||||
const channelName = `guest-ch-${pw.random.id()}`;
|
||||
const channel = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: channelName.toLowerCase().replace(/[^a-z0-9-]/g, ''),
|
||||
@@ -260,7 +260,7 @@ test(
|
||||
}
|
||||
|
||||
// # Create two channels
|
||||
const ch1Name = `guest-multi-1-${await pw.random.id()}`;
|
||||
const ch1Name = `guest-multi-1-${pw.random.id()}`;
|
||||
const channel1 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: ch1Name.toLowerCase().replace(/[^a-z0-9-]/g, ''),
|
||||
@@ -268,7 +268,7 @@ test(
|
||||
type: 'O',
|
||||
});
|
||||
|
||||
const ch2Name = `guest-multi-2-${await pw.random.id()}`;
|
||||
const ch2Name = `guest-multi-2-${pw.random.id()}`;
|
||||
const channel2 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: ch2Name.toLowerCase().replace(/[^a-z0-9-]/g, ''),
|
||||
|
||||
+6
-6
@@ -13,8 +13,8 @@ test('displays and allows editing of authentication data field', {tag: '@user_ma
|
||||
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Generate unique auth data to avoid unique constraint errors
|
||||
const originalAuthData = `auth-data-${await pw.random.id()}`;
|
||||
const newAuthData = `auth-data-${await pw.random.id()}`;
|
||||
const originalAuthData = `auth-data-${pw.random.id()}`;
|
||||
const newAuthData = `auth-data-${pw.random.id()}`;
|
||||
|
||||
// # Update user to have an auth service (simulate SAML/LDAP user)
|
||||
await adminClient.updateUserAuth(user.id, {
|
||||
@@ -60,7 +60,7 @@ test('disables email and username fields for users with auth service', {tag: '@u
|
||||
// # Update user to have an auth service
|
||||
await adminClient.updateUserAuth(user.id, {
|
||||
auth_service: 'ldap',
|
||||
auth_data: `ldap-user-data-${await pw.random.id()}`,
|
||||
auth_data: `ldap-user-data-${pw.random.id()}`,
|
||||
});
|
||||
|
||||
// # Navigate to user detail page
|
||||
@@ -102,8 +102,8 @@ test('allows editing email and username fields for regular users', {tag: '@user_
|
||||
await expect(userCard.usernameInput).toBeEnabled();
|
||||
|
||||
// # Update both email and username
|
||||
const newEmail = `updated-${await pw.random.id()}@example.com`;
|
||||
const newUsername = `updated-${await pw.random.id()}`;
|
||||
const newEmail = `updated-${pw.random.id()}@example.com`;
|
||||
const newUsername = `updated-${pw.random.id()}`;
|
||||
|
||||
await userCard.emailInput.clear();
|
||||
await userCard.emailInput.fill(newEmail);
|
||||
@@ -173,7 +173,7 @@ test('allows cancelling save confirmation dialog', {tag: '@user_management'}, as
|
||||
const {userDetail} = systemConsolePage.users;
|
||||
|
||||
// # Update email field
|
||||
const newEmail = `cancelled-${await pw.random.id()}@example.com`;
|
||||
const newEmail = `cancelled-${pw.random.id()}@example.com`;
|
||||
await userDetail.userCard.emailInput.clear();
|
||||
await userDetail.userCard.emailInput.fill(newEmail);
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "es2022",
|
||||
"lib": ["esnext", "dom"],
|
||||
"types": ["node"],
|
||||
"declaration": true,
|
||||
"declarationDir": "dist",
|
||||
"strict": true,
|
||||
@@ -14,7 +16,6 @@
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/asset": ["./asset"]
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
let uuidv4: (() => string) | null = null;
|
||||
|
||||
export async function getRandomId(length = 7): Promise<string> {
|
||||
if (!uuidv4) {
|
||||
const {v4} = await import('uuid');
|
||||
uuidv4 = v4;
|
||||
}
|
||||
|
||||
const MAX_SUBSTRING_INDEX = 27;
|
||||
return uuidv4()
|
||||
.replace(/-/g, '')
|
||||
.substring(MAX_SUBSTRING_INDEX - length, MAX_SUBSTRING_INDEX);
|
||||
}
|
||||
@@ -691,6 +691,7 @@ export type SSOSettings = {
|
||||
DiscoveryEndpoint: string;
|
||||
ButtonText: string;
|
||||
ButtonColor: string;
|
||||
UsePreferredUsername: boolean;
|
||||
};
|
||||
|
||||
export type Office365Settings = {
|
||||
@@ -703,6 +704,7 @@ export type Office365Settings = {
|
||||
UserAPIEndpoint: string;
|
||||
DiscoveryEndpoint: string;
|
||||
DirectoryId: string;
|
||||
UsePreferredUsername: boolean;
|
||||
};
|
||||
|
||||
export type LdapSettings = {
|
||||
|
||||
Reference in New Issue
Block a user