chore(playwright): upgrade to v1.59 and to typescript@6.0 (#36071)

This commit is contained in:
sabril
2026-04-15 17:43:52 +08:00
committed by GitHub
parent c66bb0ecdb
commit bff3577690
45 changed files with 1157 additions and 764 deletions
+1 -1
View File
@@ -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:
- |
+1 -1
View File
@@ -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
+9 -8
View File
@@ -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();
+1 -1
View File
@@ -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,
},
};
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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`,
+3 -1
View File
@@ -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 = {
+3 -18
View File
@@ -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);
}
+2 -3
View File
@@ -9,10 +9,9 @@
"module": "ESNext",
"resolveJsonModule": true,
"skipLibCheck": true,
"downlevelIteration": true,
"baseUrl": "src",
"rootDir": "src",
"paths": {
"@/*": ["*"]
"@/*": ["./src/*"]
}
},
"include": ["src"],
+918 -516
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -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"
}
}
+1 -1
View File
@@ -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,
}),
@@ -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);
@@ -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,
@@ -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,
@@ -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,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,
@@ -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}) => {
@@ -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')),
@@ -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
@@ -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',
@@ -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")';
@@ -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, ''),
@@ -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',
@@ -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}`;
@@ -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',
@@ -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);
@@ -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, ''),
@@ -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, ''),
@@ -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);
+4 -3
View File
@@ -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"]
}
-16
View File
@@ -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);
}
+2
View File
@@ -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 = {