mirror of
https://github.com/hanzoai/team.git
synced 2026-11-01 07:55:31 +00:00
* COmposing messages with redacted URLs
* Handled non member channels
* Some refinements
* Optimizations
* lint fixes
* cleaned up hasObfuscatedSlug test
* Fixed a test
* Added system console setting
* WIP
* fixed channel seelection double selection bug
* LInt fixes
* i18n fixes
* fixed test
* CI
* renamed setting
* lint fixes
* lint fixes
* WIP
* Combined TeamSignupDisplayNamePage and TeamUrl component into a single CreateTeamForm component
* Converted CreateTeamForm to functional component
* Refactored to mnake code cleaner
* Handle team creation with setting enabled
* Skipped team URL step if secure URL feature is enabled
* Managed button text and steps in team creation flow
* lint fixes
* Don't register team URL path when using secure URL
* Display team display name instead of name in system console top nav bar
* Fixed tests
* Fixed coderabbit issues
* Fixed type errors
* Optimization
* improvements
* Handled API errors during team creation when using secure URL setting
* Some refinements
* Added test
* Updaetd tests, and trimming when reading instead of writing
* Added tests for new components
* Added BackstageNavbar tests
* Restored package lock
* lint fix
* Updaetd plugin API
* Updated team creation tests
* Added tests for ChannelNameFormField
* Added plugin API tests
* Updated API terst
* Review fixes
* Added test for ConvertGmToChannelModal component
* Added EA license check for secure urls feature
* restored package lock
* Fixed GM conversion test
* Fixed team creation tests
* remove message composition changes
* remove message composition changes
* remove message composition changes
* restored a file
* lint fix
* renamed feature
* used model.SafeDereference
* Added E2E tests
* add secure URL Playwright coverage
Expand the secure URLs Playwright coverage to validate creation, routing, rename flows, system console configuration, and search/navigation behavior while adding the page objects needed to keep the tests maintainable.
Made-with: Cursor
* rename secure URLs copy to anonymous URLs
Align the admin console and Playwright coverage with the Anonymous URLs feature name while preserving the existing UseAnonymousURLs config behavior and validating the renamed test surfaces.
Made-with: Cursor
* Update team creation CTA for anonymous URLs
Show Create in the single-step anonymous URL flow while preserving Next and Finish in the standard team creation flow. Update unit and Playwright coverage to match the revised create-team UX.
Made-with: Cursor
---------
(cherry picked from commit d81e1722cf)
Co-authored-by: Harshil Sharma <[email protected]>
Co-authored-by: maria.nunez <[email protected]>
901 lines
42 KiB
TypeScript
901 lines
42 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {expect, test} from '@mattermost/playwright-lib';
|
|
|
|
const OBFUSCATED_SLUG_RE = /^[a-z0-9]{26}$/;
|
|
|
|
async function skipIfNoAdvancedLicense(adminClient: any) {
|
|
const license = await adminClient.getClientLicenseOld();
|
|
test.skip(license.SkuShortName !== 'advanced', 'Skipping test - server does not have enterprise advanced license');
|
|
}
|
|
|
|
async function setAnonymousUrls(adminClient: any, enabled: boolean) {
|
|
await adminClient.patchConfig({
|
|
PrivacySettings: {
|
|
UseAnonymousURLs: enabled,
|
|
},
|
|
});
|
|
}
|
|
|
|
function expectObfuscatedSlug(slug: string) {
|
|
expect(slug).toMatch(OBFUSCATED_SLUG_RE);
|
|
}
|
|
|
|
function expectReadableSlug(slug: string, expectedSlug?: string) {
|
|
if (expectedSlug) {
|
|
expect(slug).toBe(expectedSlug);
|
|
}
|
|
|
|
expect(slug).not.toMatch(OBFUSCATED_SLUG_RE);
|
|
}
|
|
|
|
async function createChannelFromUI(channelsPage: any, displayName: string) {
|
|
const newChannelModal = await channelsPage.openNewChannelModal();
|
|
await newChannelModal.fillDisplayName(displayName);
|
|
await newChannelModal.create();
|
|
await channelsPage.toBeVisible();
|
|
}
|
|
|
|
async function createTeamFromUI(channelsPage: any, displayName: string) {
|
|
const createTeamForm = await channelsPage.openCreateTeamForm();
|
|
await createTeamForm.fillTeamName(displayName);
|
|
await createTeamForm.submitDisplayName();
|
|
await channelsPage.toBeVisible();
|
|
}
|
|
|
|
async function getChannelByDisplayName(adminClient: any, teamId: string, displayName: string) {
|
|
const channels = await adminClient.getChannels(teamId);
|
|
const channel = channels.find((candidate: any) => candidate.display_name === displayName);
|
|
|
|
expect(channel).toBeDefined();
|
|
|
|
return channel!;
|
|
}
|
|
|
|
async function getTeamByDisplayName(adminClient: any, displayName: string) {
|
|
const teams = await adminClient.getMyTeams();
|
|
const team = teams.find((candidate: any) => candidate.display_name === displayName);
|
|
|
|
expect(team).toBeDefined();
|
|
|
|
return team!;
|
|
}
|
|
|
|
async function createAnonymousUrlChannel(
|
|
channelsPage: any,
|
|
adminClient: any,
|
|
teamName: string,
|
|
teamId: string,
|
|
displayName: string,
|
|
) {
|
|
await createChannelFromUI(channelsPage, displayName);
|
|
await channelsPage.centerView.header.toHaveTitle(displayName);
|
|
|
|
const channel = await getChannelByDisplayName(adminClient, teamId, displayName);
|
|
expectObfuscatedSlug(channel.name);
|
|
await expect(channelsPage.page).toHaveURL(new RegExp(`/${teamName}/channels/${channel.name}$`));
|
|
|
|
return channel;
|
|
}
|
|
|
|
test.describe('Anonymous URLs', () => {
|
|
/**
|
|
* @objective Verify that the anonymous URLs setting can be toggled on from System Console and persists after navigation
|
|
*
|
|
* @precondition
|
|
* Server must have an Enterprise Advanced license
|
|
*/
|
|
test(
|
|
'enables anonymous URLs setting from System Console and verifies it persists',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and check license
|
|
const {adminUser, adminClient} = await pw.initSetup({withDefaultProfileImage: false});
|
|
const license = await adminClient.getClientLicenseOld();
|
|
test.skip(
|
|
license.SkuShortName !== 'advanced',
|
|
'Skipping test - server does not have enterprise advanced license',
|
|
);
|
|
|
|
// # Log in as admin
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
|
|
// # Visit System Console
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
|
|
// # Navigate to Users and Teams
|
|
await systemConsolePage.sidebar.siteConfiguration.usersAndTeams.click();
|
|
await systemConsolePage.usersAndTeams.toBeVisible();
|
|
|
|
// * Verify the anonymous URLs radio group is visible
|
|
await systemConsolePage.usersAndTeams.useAnonymousURLs.toBeVisible();
|
|
|
|
// * Verify the setting is initially false
|
|
await systemConsolePage.usersAndTeams.useAnonymousURLs.toBeFalse();
|
|
|
|
// # Enable anonymous URLs by clicking the True radio
|
|
await systemConsolePage.usersAndTeams.useAnonymousURLs.selectTrue();
|
|
|
|
// * Verify it is now true
|
|
await systemConsolePage.usersAndTeams.useAnonymousURLs.toBeTrue();
|
|
|
|
// # Save settings
|
|
await systemConsolePage.usersAndTeams.save();
|
|
await pw.waitUntil(async () => (await systemConsolePage.usersAndTeams.saveButton.textContent()) === 'Save');
|
|
|
|
// # Navigate away and come back
|
|
await systemConsolePage.sidebar.siteConfiguration.notifications.click();
|
|
await systemConsolePage.notifications.toBeVisible();
|
|
|
|
await systemConsolePage.sidebar.siteConfiguration.usersAndTeams.click();
|
|
await systemConsolePage.usersAndTeams.toBeVisible();
|
|
|
|
// * Verify the setting is still enabled
|
|
await systemConsolePage.usersAndTeams.useAnonymousURLs.toBeTrue();
|
|
|
|
// # Reset to false for cleanup
|
|
await systemConsolePage.usersAndTeams.useAnonymousURLs.selectFalse();
|
|
await systemConsolePage.usersAndTeams.save();
|
|
await pw.waitUntil(async () => (await systemConsolePage.usersAndTeams.saveButton.textContent()) === 'Save');
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that the channel URL editor is hidden when creating a new channel with anonymous URLs enabled
|
|
*
|
|
* @precondition
|
|
* Server must have an Enterprise Advanced license
|
|
*/
|
|
test(
|
|
'hides channel URL editor when creating new channel with anonymous URLs enabled',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and configure anonymous URLs
|
|
const {adminUser, adminClient} = await pw.initSetup({withDefaultProfileImage: false});
|
|
const license = await adminClient.getClientLicenseOld();
|
|
test.skip(
|
|
license.SkuShortName !== 'advanced',
|
|
'Skipping test - server does not have enterprise advanced license',
|
|
);
|
|
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in and go to channels
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Open new channel modal
|
|
await channelsPage.sidebarLeft.browseOrCreateChannelButton.click();
|
|
await channelsPage.page.locator('#createNewChannelMenuItem').click();
|
|
await channelsPage.newChannelModal.toBeVisible();
|
|
|
|
// # Fill in a channel name
|
|
await channelsPage.newChannelModal.fillDisplayName('Anonymous Test Channel');
|
|
|
|
// * Verify the URL editor section is not visible
|
|
await expect(channelsPage.newChannelModal.urlSection).not.toBeVisible();
|
|
|
|
// # Cancel modal
|
|
await channelsPage.newChannelModal.cancel();
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that a channel created with anonymous URLs enabled has an obfuscated slug that does not match the display name
|
|
*
|
|
* @precondition
|
|
* Server must have an Enterprise Advanced license
|
|
*/
|
|
test(
|
|
'creates channel with obfuscated URL slug when anonymous URLs enabled',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup
|
|
const {adminUser, adminClient, team} = await pw.initSetup({withDefaultProfileImage: false});
|
|
const license = await adminClient.getClientLicenseOld();
|
|
test.skip(
|
|
license.SkuShortName !== 'advanced',
|
|
'Skipping test - server does not have enterprise advanced license',
|
|
);
|
|
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Add admin to the test team so they can create channels there
|
|
await adminClient.addToTeam(team.id, adminUser.id);
|
|
|
|
// # Log in and go to the test team
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto(team.name);
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Create a new channel via UI
|
|
const channelDisplayName = 'Obfuscated Channel ' + Date.now();
|
|
await channelsPage.sidebarLeft.browseOrCreateChannelButton.click();
|
|
await channelsPage.page.locator('#createNewChannelMenuItem').click();
|
|
await channelsPage.newChannelModal.toBeVisible();
|
|
await channelsPage.newChannelModal.fillDisplayName(channelDisplayName);
|
|
await channelsPage.newChannelModal.create();
|
|
|
|
// # Wait for channel to be created and navigated to
|
|
await channelsPage.toBeVisible();
|
|
await pw.wait(pw.duration.two_sec);
|
|
|
|
// # Fetch all channels for the team to find the newly created one by display name
|
|
const allChannels = await adminClient.getChannels(team.id);
|
|
const createdChannel = allChannels.find((ch) => ch.display_name === channelDisplayName);
|
|
|
|
// * Verify channel was created
|
|
expect(createdChannel).toBeDefined();
|
|
|
|
// * Verify the slug does not match a cleaned version of the display name
|
|
const humanReadableSlug = channelDisplayName.toLowerCase().replace(/\s+/g, '-');
|
|
expect(createdChannel!.name).not.toBe(humanReadableSlug);
|
|
|
|
// * Verify the slug looks like a model.NewId (26 chars, alphanumeric)
|
|
expect(createdChannel!.name).toMatch(/^[a-z0-9]{26}$/);
|
|
|
|
// * Verify display name is preserved
|
|
expect(createdChannel!.display_name).toBe(channelDisplayName);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that the channel URL editor is visible when creating a new channel with anonymous URLs disabled (default)
|
|
*/
|
|
test(
|
|
'shows channel URL editor when creating new channel with anonymous URLs disabled',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup
|
|
const {adminUser} = await pw.initSetup({withDefaultProfileImage: false});
|
|
|
|
// # Log in and go to channels
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Open new channel modal
|
|
await channelsPage.sidebarLeft.browseOrCreateChannelButton.click();
|
|
await channelsPage.page.locator('#createNewChannelMenuItem').click();
|
|
await channelsPage.newChannelModal.toBeVisible();
|
|
|
|
// # Type a display name to trigger URL generation
|
|
await channelsPage.newChannelModal.fillDisplayName('Test Channel URL');
|
|
|
|
// * Verify the URL editor section is visible
|
|
await expect(channelsPage.newChannelModal.urlSection).toBeVisible();
|
|
|
|
// # Cancel modal
|
|
await channelsPage.newChannelModal.cancel();
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that the team URL step is skipped when creating a team with anonymous URLs enabled and the team is created directly after entering display name
|
|
*
|
|
* @precondition
|
|
* Server must have an Enterprise Advanced license
|
|
*/
|
|
test(
|
|
'skips team URL step when creating team with anonymous URLs enabled',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup
|
|
const {adminUser, adminClient} = await pw.initSetup({withDefaultProfileImage: false});
|
|
const license = await adminClient.getClientLicenseOld();
|
|
test.skip(
|
|
license.SkuShortName !== 'advanced',
|
|
'Skipping test - server does not have enterprise advanced license',
|
|
);
|
|
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in and go to channels
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Open team menu and click Create a team
|
|
await channelsPage.sidebarLeft.teamMenuButton.click();
|
|
await channelsPage.teamMenu.toBeVisible();
|
|
await channelsPage.teamMenu.clickCreateTeam();
|
|
|
|
// # Wait for create team form
|
|
await channelsPage.createTeamForm.toBeVisible();
|
|
|
|
// * Verify the display name input is visible
|
|
await expect(channelsPage.createTeamForm.teamNameInput).toBeVisible();
|
|
|
|
// * Verify the submit button text says "Create" because team creation stays on a single step
|
|
await expect(channelsPage.createTeamForm.teamNameSubmitButton).toContainText('Create');
|
|
|
|
// # Enter team name and submit
|
|
const teamName = 'Anonymous Team ' + Date.now();
|
|
await channelsPage.createTeamForm.fillTeamName(teamName);
|
|
await channelsPage.createTeamForm.submitDisplayName();
|
|
|
|
// * Verify the team is created and user is redirected (no URL step shown)
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Verify the team has an obfuscated slug
|
|
const teams = await adminClient.getMyTeams();
|
|
const createdTeam = teams.find((t) => t.display_name === teamName);
|
|
expect(createdTeam).toBeDefined();
|
|
expect(createdTeam!.name).toMatch(/^[a-z0-9]{26}$/);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that the team URL step is shown when creating a team with anonymous URLs disabled (default)
|
|
*/
|
|
test(
|
|
'shows team URL step when creating team with anonymous URLs disabled',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup
|
|
const {adminUser} = await pw.initSetup({withDefaultProfileImage: false});
|
|
|
|
// # Log in and go to channels
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Open team menu and click Create a team
|
|
await channelsPage.sidebarLeft.teamMenuButton.click();
|
|
await channelsPage.teamMenu.toBeVisible();
|
|
await channelsPage.teamMenu.clickCreateTeam();
|
|
|
|
// # Wait for create team form
|
|
await channelsPage.createTeamForm.toBeVisible();
|
|
|
|
// * Verify the submit button says "Next" on the first step because the team URL step follows
|
|
await expect(channelsPage.createTeamForm.teamNameSubmitButton).toContainText('Next');
|
|
|
|
// # Enter team name and click Next
|
|
await channelsPage.createTeamForm.fillTeamName('Test Team URL Step');
|
|
await channelsPage.createTeamForm.submitDisplayName();
|
|
|
|
// * Verify the team URL step is now visible
|
|
await expect(channelsPage.createTeamForm.teamURLInput).toBeVisible();
|
|
await expect(channelsPage.createTeamForm.teamURLSubmitButton).toBeVisible();
|
|
await expect(channelsPage.createTeamForm.teamURLSubmitButton).toContainText('Finish');
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that an archived channel created with anonymous URLs keeps its obfuscated route and becomes usable again after unarchiving.
|
|
*/
|
|
test(
|
|
'preserves archived anonymous channel routes and restores channel access after unarchive',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and enable anonymous URLs
|
|
const {adminUser, adminClient, team} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
await adminClient.addToTeam(team.id, adminUser.id);
|
|
|
|
// # Log in as admin and create a channel with an obfuscated slug
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto(team.name);
|
|
await channelsPage.toBeVisible();
|
|
|
|
const channelDisplayName = `Archived Anonymous ${await pw.random.id()}`;
|
|
await createChannelFromUI(channelsPage, channelDisplayName);
|
|
|
|
const createdChannel = await getChannelByDisplayName(adminClient, team.id, channelDisplayName);
|
|
expectObfuscatedSlug(createdChannel.name);
|
|
|
|
// # Archive the channel and preserve the anonymous URL slug
|
|
await adminClient.deleteChannel(createdChannel.id);
|
|
const archivedChannel = await adminClient.getChannel(createdChannel.id);
|
|
|
|
// * Verify archiving does not rotate the anonymous URL route slug
|
|
expect(archivedChannel.name).toBe(createdChannel.name);
|
|
|
|
// # Restore the archived channel and verify the anonymous URL slug is preserved
|
|
const restoredChannel = await adminClient.unarchiveChannel(createdChannel.id);
|
|
expect(restoredChannel.name).toBe(createdChannel.name);
|
|
|
|
// # Open the restored channel again from the sidebar
|
|
await channelsPage.page.reload();
|
|
await channelsPage.sidebarLeft.goToItem(createdChannel.name);
|
|
|
|
// * Verify the restored channel still uses the original anonymous URL route
|
|
await channelsPage.centerView.header.toHaveTitle(channelDisplayName);
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${createdChannel.name}`);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that enabling anonymous URLs does not rewrite existing readable slugs and only affects channels and teams created afterward.
|
|
*/
|
|
test(
|
|
'keeps existing readable routes unchanged and obfuscates only newly created channels and teams',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup with anonymous URLs disabled by default
|
|
const {adminUser, adminClient, team} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
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 legacyChannel = await adminClient.createChannel({
|
|
team_id: team.id,
|
|
name: legacyChannelSlug,
|
|
display_name: legacyChannelDisplayName,
|
|
type: 'O',
|
|
});
|
|
|
|
const legacyTeamSlug = `legacy-team-${await pw.random.id()}`;
|
|
const legacyTeamDisplayName = `Legacy Team ${await pw.random.id()}`;
|
|
const legacyTeam = await adminClient.createTeam({
|
|
name: legacyTeamSlug,
|
|
display_name: legacyTeamDisplayName,
|
|
type: 'O',
|
|
} as any);
|
|
|
|
expectReadableSlug(legacyChannel.name, legacyChannelSlug);
|
|
expectReadableSlug(legacyTeam.name, legacyTeamSlug);
|
|
|
|
// # Enable anonymous URLs after the legacy channel and team already exist
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
const legacyChannelAfterToggle = await adminClient.getChannel(legacyChannel.id);
|
|
const legacyTeamAfterToggle = await adminClient.getTeam(legacyTeam.id);
|
|
|
|
// * Verify the pre-existing slugs remain readable and unchanged
|
|
expectReadableSlug(legacyChannelAfterToggle.name, legacyChannelSlug);
|
|
expectReadableSlug(legacyTeamAfterToggle.name, legacyTeamSlug);
|
|
|
|
// # Log in and verify the original readable channel route still works
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto(team.name, legacyChannelSlug);
|
|
await channelsPage.toBeVisible();
|
|
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()}`;
|
|
await channelsPage.goto(team.name);
|
|
await channelsPage.toBeVisible();
|
|
await createChannelFromUI(channelsPage, anonymousChannelDisplayName);
|
|
|
|
const anonymousChannel = await getChannelByDisplayName(adminClient, team.id, anonymousChannelDisplayName);
|
|
|
|
// * Verify only the new channel receives an obfuscated slug
|
|
expectObfuscatedSlug(anonymousChannel.name);
|
|
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()}`;
|
|
await createTeamFromUI(channelsPage, anonymousTeamDisplayName);
|
|
|
|
const anonymousTeam = await getTeamByDisplayName(adminClient, anonymousTeamDisplayName);
|
|
|
|
// * Verify only the new team receives an obfuscated slug
|
|
expectObfuscatedSlug(anonymousTeam.name);
|
|
await expect(channelsPage.page).toHaveURL(new RegExp(`/${anonymousTeam.name}/`));
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that direct and group messages continue using message routes and are excluded from anonymous URL slug obfuscation.
|
|
*/
|
|
test(
|
|
'keeps direct and group message routes readable when anonymous URLs are enabled',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup, create message participants, and enable anonymous URLs
|
|
const {adminUser, adminClient, team} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
await adminClient.addToTeam(team.id, adminUser.id);
|
|
|
|
const secondUser = await pw.createNewUserProfile(adminClient, {prefix: 'anonymousurlsdm'});
|
|
const thirdUser = await pw.createNewUserProfile(adminClient, {prefix: 'anonymousurlsgm'});
|
|
await adminClient.addToTeam(team.id, secondUser.id);
|
|
await adminClient.addToTeam(team.id, thirdUser.id);
|
|
|
|
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()}`;
|
|
await adminClient.createPost({channel_id: dmChannel.id, message: dmMessage});
|
|
await adminClient.createPost({channel_id: gmChannel.id, message: gmMessage});
|
|
|
|
// * Verify DM and GM channel identifiers are not replaced with obfuscated slugs
|
|
expect(dmChannel.type).toBe('D');
|
|
expect(gmChannel.type).toBe('G');
|
|
expectReadableSlug(dmChannel.name);
|
|
expectReadableSlug(gmChannel.name);
|
|
expect(dmChannel.name).toContain(adminUser.id);
|
|
expect(dmChannel.name).toContain(secondUser.id);
|
|
|
|
// # Log in as admin and open the DM route
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto(team.name, `@${secondUser.username}`);
|
|
await channelsPage.toBeVisible();
|
|
|
|
// * Verify the DM still uses the standard message route
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/messages/@${secondUser.username}`);
|
|
await channelsPage.centerView.waitUntilLastPostContains(dmMessage);
|
|
|
|
// # Open the GM route
|
|
await channelsPage.gotoMessage(team.name, gmChannel.name);
|
|
await channelsPage.toBeVisible();
|
|
|
|
// * Verify the GM still uses the standard message route
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/messages/${gmChannel.name}`);
|
|
await channelsPage.centerView.waitUntilLastPostContains(gmMessage);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that renaming an anonymous URL channel changes the display name without rewriting its obfuscated channel slug.
|
|
*/
|
|
test(
|
|
'renames an anonymous URL channel without changing its obfuscated route',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and create an anonymous URL channel
|
|
const {adminUser, adminClient, team} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
await adminClient.addToTeam(team.id, adminUser.id);
|
|
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto(team.name);
|
|
await channelsPage.toBeVisible();
|
|
|
|
const originalDisplayName = `Original Channel ${await pw.random.id()}`;
|
|
await createChannelFromUI(channelsPage, originalDisplayName);
|
|
|
|
const createdChannel = await getChannelByDisplayName(adminClient, team.id, originalDisplayName);
|
|
const originalSlug = createdChannel.name;
|
|
expectObfuscatedSlug(originalSlug);
|
|
|
|
// # Rename the channel from channel settings
|
|
const renamedDisplayName = `Renamed Channel ${await pw.random.id()}`;
|
|
const channelSettingsModal = await channelsPage.openChannelSettings();
|
|
const infoTab = await channelSettingsModal.openInfoTab();
|
|
await infoTab.updateName(renamedDisplayName);
|
|
await channelSettingsModal.save();
|
|
|
|
await pw.waitUntil(
|
|
async () => (await adminClient.getChannel(createdChannel.id)).display_name === renamedDisplayName,
|
|
);
|
|
await channelSettingsModal.close();
|
|
|
|
const renamedChannel = await adminClient.getChannel(createdChannel.id);
|
|
|
|
// * Verify the channel name changes without rotating the anonymous URL slug
|
|
expect(renamedChannel.display_name).toBe(renamedDisplayName);
|
|
expect(renamedChannel.name).toBe(originalSlug);
|
|
expectObfuscatedSlug(renamedChannel.name);
|
|
|
|
// # Reopen the channel using its original obfuscated route
|
|
await channelsPage.goto(team.name, originalSlug);
|
|
await channelsPage.toBeVisible();
|
|
|
|
// * Verify the obfuscated route still resolves to the renamed channel
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${originalSlug}`);
|
|
await channelsPage.centerView.header.toHaveTitle(renamedDisplayName);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that renaming an anonymous URL team changes the display name without rewriting its obfuscated team slug.
|
|
*/
|
|
test(
|
|
'renames an anonymous URL team without changing its obfuscated route',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and enable anonymous URLs
|
|
const {adminUser, adminClient} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in as admin and create a team with an obfuscated slug
|
|
const {channelsPage} = await pw.testBrowser.login(adminUser);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
const originalTeamDisplayName = `Original Team ${await pw.random.id()}`;
|
|
await createTeamFromUI(channelsPage, originalTeamDisplayName);
|
|
|
|
const createdTeam = await getTeamByDisplayName(adminClient, originalTeamDisplayName);
|
|
const originalTeamSlug = createdTeam.name;
|
|
expectObfuscatedSlug(originalTeamSlug);
|
|
|
|
// # Rename the team from team settings
|
|
const renamedTeamDisplayName = `Renamed Team ${await pw.random.id()}`;
|
|
const teamSettingsModal = await channelsPage.openTeamSettings();
|
|
const infoTab = await teamSettingsModal.openInfoTab();
|
|
await infoTab.updateName(renamedTeamDisplayName);
|
|
await teamSettingsModal.save();
|
|
|
|
await pw.waitUntil(
|
|
async () => (await adminClient.getTeam(createdTeam.id)).display_name === renamedTeamDisplayName,
|
|
);
|
|
await teamSettingsModal.close();
|
|
|
|
const renamedTeam = await adminClient.getTeam(createdTeam.id);
|
|
|
|
// * Verify the team name changes without rotating the anonymous URL slug
|
|
expect(renamedTeam.display_name).toBe(renamedTeamDisplayName);
|
|
expect(renamedTeam.name).toBe(originalTeamSlug);
|
|
expectObfuscatedSlug(renamedTeam.name);
|
|
|
|
// # Reopen the team using its original obfuscated route
|
|
await channelsPage.goto(originalTeamSlug);
|
|
await channelsPage.toBeVisible();
|
|
|
|
// * Verify the obfuscated team route still resolves to the renamed team
|
|
await expect(channelsPage.page).toHaveURL(new RegExp(`/${originalTeamSlug}/`));
|
|
|
|
const reopenedTeamSettings = await channelsPage.openTeamSettings();
|
|
await expect(reopenedTeamSettings.infoSettings.nameInput).toHaveValue(renamedTeamDisplayName);
|
|
await reopenedTeamSettings.close();
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that post permalinks created in anonymous URL channels continue to resolve after the feature is turned off.
|
|
*/
|
|
test(
|
|
'opens anonymous channel permalinks before and after disabling anonymous URLs',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and enable anonymous URLs
|
|
const {adminClient, team, user} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in and create an anonymous URL channel
|
|
const {channelsPage} = await pw.testBrowser.login(user);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
const displayName = `Permalink Channel ${await 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()}`;
|
|
await channelsPage.postMessage(message);
|
|
|
|
const lastPost = await channelsPage.getLastPost();
|
|
const postId = await lastPost.getId();
|
|
const permalink = `/${team.name}/pl/${postId}`;
|
|
|
|
// # Open the permalink while anonymous URLs are enabled
|
|
await channelsPage.page.goto(permalink);
|
|
|
|
// * Verify the permalink resolves to the channel's obfuscated route
|
|
await channelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${channel.name}`);
|
|
await channelsPage.centerView.waitUntilPostWithIdContains(postId, message);
|
|
|
|
// # Disable anonymous URLs and reopen the same permalink
|
|
await setAnonymousUrls(adminClient, false);
|
|
await channelsPage.page.goto(permalink);
|
|
|
|
// * Verify the permalink still resolves to the existing obfuscated route
|
|
await channelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${channel.name}`);
|
|
await channelsPage.centerView.waitUntilPostWithIdContains(postId, message);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that channel search finds anonymous URL channels by display name and navigates to their obfuscated routes.
|
|
*/
|
|
test('channel search finds channels with obfuscated URLs', {tag: '@anonymous_urls'}, async ({pw}) => {
|
|
// # Initialize setup and enable anonymous URLs
|
|
const {adminClient, team, user} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in and create anonymous URL channels
|
|
const {channelsPage} = await pw.testBrowser.login(user);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
const createdChannels = [];
|
|
for (let i = 1; i <= 3; i++) {
|
|
const displayName = `Search Test Channel ${i} ${await pw.random.id()}`;
|
|
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
|
createdChannels.push({channel, displayName});
|
|
}
|
|
|
|
const targetChannel = createdChannels[0];
|
|
|
|
// # Open Find Channels and search by display name
|
|
await channelsPage.goto(team.name, 'town-square');
|
|
await channelsPage.toBeVisible();
|
|
await channelsPage.sidebarLeft.findChannelButton.click();
|
|
await channelsPage.findChannelsModal.toBeVisible();
|
|
await channelsPage.findChannelsModal.input.fill(targetChannel.displayName.substring(0, 15));
|
|
|
|
// * Verify the anonymous URL channel appears in results
|
|
const result = channelsPage.findChannelsModal.getResult(targetChannel.channel.name);
|
|
await expect(result).toBeVisible();
|
|
await expect(result).toContainText(targetChannel.displayName);
|
|
|
|
// # Select the matching anonymous URL channel
|
|
await channelsPage.findChannelsModal.selectChannel(targetChannel.channel.name);
|
|
|
|
// * Verify navigation lands on the obfuscated route
|
|
await channelsPage.centerView.header.toHaveTitle(targetChannel.displayName);
|
|
await expect(channelsPage.page).toHaveURL(`/${team.name}/channels/${targetChannel.channel.name}`);
|
|
});
|
|
|
|
/**
|
|
* @objective Verify that post search results navigate back to the correct anonymous URL channel route.
|
|
*/
|
|
test('navigates post search results back to anonymous URL channels', {tag: '@anonymous_urls'}, async ({pw}) => {
|
|
// # Initialize setup and enable anonymous URLs
|
|
const {adminClient, team, user} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in and create an anonymous URL channel with a searchable post
|
|
const {channelsPage} = await pw.testBrowser.login(user);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
const displayName = `Search Channel ${await pw.random.id()}`;
|
|
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
|
const message = `AnonymousSearchableMessage${await pw.random.id()}`;
|
|
await channelsPage.postMessage(message);
|
|
|
|
const lastPost = await channelsPage.getLastPost();
|
|
const postId = await lastPost.getId();
|
|
|
|
// # Open message search from another channel
|
|
await channelsPage.goto(team.name, 'town-square');
|
|
await channelsPage.toBeVisible();
|
|
await channelsPage.globalHeader.openSearch();
|
|
await channelsPage.searchBox.searchInput.fill(message);
|
|
await channelsPage.searchBox.searchInput.press('Enter');
|
|
|
|
const searchItem = channelsPage.page.getByTestId('search-item-container').first();
|
|
|
|
// * Verify the post appears in results
|
|
await expect(searchItem).toContainText(message, {timeout: 15000});
|
|
|
|
// # Jump from the search result back to the anonymous URL channel
|
|
await searchItem.getByRole('link', {name: 'Jump'}).click();
|
|
|
|
// * Verify navigation returns to the anonymous URL route and highlights the expected post
|
|
await channelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(channelsPage.page).toHaveURL(new RegExp(`/${team.name}/channels/${channel.name}`));
|
|
await channelsPage.centerView.waitUntilPostWithIdContains(postId, message);
|
|
});
|
|
|
|
/**
|
|
* @objective Verify that anonymous URLs preserve edge-case channel display names while keeping channels discoverable and fully usable.
|
|
*/
|
|
test(
|
|
'creates anonymous URL channels from unicode and emoji display names and keeps them discoverable',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and enable anonymous URLs
|
|
const {user, adminClient, team} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
await setAnonymousUrls(adminClient, true);
|
|
|
|
// # Log in and open the test team
|
|
const {channelsPage} = await pw.testBrowser.login(user);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
// # Create channels with edge-case display names
|
|
const channelTemplates = ['Design review 🚀', 'Roadmap 中文', 'Support & Ops عربى'];
|
|
const createdChannels = [];
|
|
|
|
for (const template of channelTemplates) {
|
|
const displayName = `${template} ${(await pw.random.id()).slice(0, 6)}`;
|
|
const channel = await createAnonymousUrlChannel(
|
|
channelsPage,
|
|
adminClient,
|
|
team.name,
|
|
team.id,
|
|
displayName,
|
|
);
|
|
|
|
createdChannels.push({channel, displayName});
|
|
|
|
// # Post in the new anonymous URL channel
|
|
const message = `anonymous-url edge case ${displayName}`;
|
|
await channelsPage.postMessage(message);
|
|
|
|
// * Verify the channel keeps the exact display name and remains usable
|
|
const lastPost = await channelsPage.getLastPost();
|
|
await expect(lastPost.body).toContainText(message);
|
|
await channelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(channelsPage.page).toHaveURL(new RegExp(`/${team.name}/channels/${channel.name}$`));
|
|
}
|
|
|
|
const searchableChannel = createdChannels[1];
|
|
|
|
// # Reopen the channel from Find Channels using its display name
|
|
await channelsPage.goto(team.name, 'town-square');
|
|
await channelsPage.toBeVisible();
|
|
await channelsPage.sidebarLeft.findChannelButton.click();
|
|
await channelsPage.findChannelsModal.toBeVisible();
|
|
await channelsPage.findChannelsModal.input.fill(searchableChannel.displayName);
|
|
|
|
// * Verify search results use the display name and navigate back to the obfuscated slug
|
|
await expect(channelsPage.findChannelsModal.getResult(searchableChannel.channel.name)).toBeVisible();
|
|
await channelsPage.findChannelsModal.selectChannel(searchableChannel.channel.name);
|
|
await channelsPage.centerView.header.toHaveTitle(searchableChannel.displayName);
|
|
await expect(channelsPage.page).toHaveURL(
|
|
new RegExp(`/${team.name}/channels/${searchableChannel.channel.name}$`),
|
|
);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that anonymous URL channels with special-character names still expose the Calls entry point after direct navigation.
|
|
*
|
|
* @precondition
|
|
* Calls plugin must be installed in the test environment.
|
|
*/
|
|
test(
|
|
'shows calls entry point in anonymous URL channels with special-character names and preserves the obfuscated route',
|
|
{tag: '@anonymous_urls'},
|
|
async ({pw}) => {
|
|
// # Initialize setup and validate Calls prerequisites
|
|
const {adminUser, adminClient, team, user} = await pw.initSetup({withDefaultProfileImage: false});
|
|
await skipIfNoAdvancedLicense(adminClient);
|
|
|
|
const pluginStatuses = await adminClient.getPluginStatuses();
|
|
test.skip(
|
|
!pluginStatuses.some((plugin: {plugin_id: string}) => plugin.plugin_id === 'com.mattermost.calls'),
|
|
'Skipping test - Calls plugin is not installed',
|
|
);
|
|
|
|
await pw.ensurePluginsLoaded(['com.mattermost.calls']);
|
|
await pw.shouldHaveCallsEnabled();
|
|
await setAnonymousUrls(adminClient, true);
|
|
await adminClient.addToTeam(team.id, adminUser.id);
|
|
|
|
// # Create an anonymous URL channel with a special-character display name
|
|
const {channelsPage} = await pw.testBrowser.login(user);
|
|
await channelsPage.goto();
|
|
await channelsPage.toBeVisible();
|
|
|
|
const displayName = `Calls & Planning 🚀 ${(await pw.random.id()).slice(0, 6)}`;
|
|
const channel = await createAnonymousUrlChannel(channelsPage, adminClient, team.name, team.id, displayName);
|
|
await adminClient.addToChannel(adminUser.id, channel.id);
|
|
|
|
// * Verify the current anonymous URL channel shows the Calls entry point
|
|
await channelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(channelsPage.centerView.header.callButton).toBeVisible();
|
|
|
|
// # Open the anonymous URL directly as another member
|
|
const {channelsPage: otherChannelsPage} = await pw.testBrowser.login(adminUser);
|
|
await otherChannelsPage.goto(team.name, channel.name);
|
|
await otherChannelsPage.toBeVisible();
|
|
|
|
// * Verify direct navigation keeps the obfuscated slug and still exposes Calls
|
|
await otherChannelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(otherChannelsPage.page).toHaveURL(new RegExp(`/${team.name}/channels/${channel.name}$`));
|
|
await expect(otherChannelsPage.centerView.header.callButton).toBeVisible();
|
|
|
|
// # Open the Calls entry point from the anonymous URL channel
|
|
await otherChannelsPage.centerView.header.openCalls();
|
|
|
|
// * Verify opening Calls does not break the anonymous URL route context
|
|
await otherChannelsPage.centerView.header.toHaveTitle(displayName);
|
|
await expect(otherChannelsPage.page).toHaveURL(new RegExp(`/${team.name}/channels/${channel.name}`));
|
|
},
|
|
);
|
|
});
|