mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
462 lines
20 KiB
TypeScript
462 lines
20 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {expect, getRandomId, test} from '@mattermost/playwright-lib';
|
|
|
|
test.describe('Single-channel guests', () => {
|
|
test.setTimeout(180000);
|
|
test.describe.configure({mode: 'serial'});
|
|
|
|
test.beforeEach(async ({pw}) => {
|
|
await pw.ensureLicense();
|
|
await pw.skipIfNoLicense();
|
|
});
|
|
|
|
async function setupSingleChannelGuestsTest(pw: any) {
|
|
const {adminClient, adminUser} = await pw.getAdminClient();
|
|
const suffix = getRandomId();
|
|
const team = await adminClient.createTeam({
|
|
name: `scg-${suffix}`,
|
|
display_name: `SCG ${suffix}`,
|
|
type: 'O',
|
|
});
|
|
await adminClient.addToTeam(team.id, adminUser.id);
|
|
return {adminClient, adminUser, team};
|
|
}
|
|
|
|
async function patchGuestEnabled(adminClient: any, enabled: boolean): Promise<boolean> {
|
|
const cfg = await adminClient.getConfig();
|
|
const previous = cfg.GuestAccountsSettings?.Enable ?? false;
|
|
await adminClient.patchConfig({GuestAccountsSettings: {Enable: enabled}});
|
|
return previous;
|
|
}
|
|
|
|
async function navigateWithGuestPatch(page: any, adminClient: any, url: string, guestEnabled: boolean) {
|
|
await page.goto(url);
|
|
await page.waitForLoadState('networkidle');
|
|
await patchGuestEnabled(adminClient, guestEnabled);
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
/**
|
|
* @objective Verify the Single-channel Guests stat card appears on the Site Statistics page when guests are enabled
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled
|
|
*/
|
|
test(
|
|
'displays single-channel guests card on site statistics page when guest accounts are enabled',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminUser, adminClient, team} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Create a guest user and add to one channel
|
|
const guestUser = await adminClient.createUser(await pw.random.user(), '', '');
|
|
await adminClient.updateUserRoles(guestUser.id, 'system_guest');
|
|
await adminClient.addToTeam(team.id, guestUser.id);
|
|
|
|
const channel = await adminClient.createChannel(
|
|
pw.random.channel({teamId: team.id, name: 'guest-channel', displayName: 'Guest Channel', unique: true}),
|
|
);
|
|
await adminClient.addToChannel(guestUser.id, channel.id);
|
|
|
|
// # Log in as admin and navigate to site statistics
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
|
|
// Re-apply patch after initial load + reload to counter WebSocket config resets
|
|
// from concurrent initSetup() calls (default_config has Enable: false).
|
|
await navigateWithGuestPatch(
|
|
systemConsolePage.page,
|
|
adminClient,
|
|
'/admin_console/reporting/system_analytics',
|
|
true,
|
|
);
|
|
|
|
// * Verify the single-channel guests card is visible
|
|
await expect(systemConsolePage.page.getByTestId('singleChannelGuests')).toBeVisible({timeout: 30000});
|
|
|
|
// * Verify the count is at least 1.
|
|
// Analytics are indexed asynchronously on the server — poll with reload until
|
|
// the card reflects the newly created guest (can take several seconds in CI).
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
// Re-apply guest patch before reload: a concurrent initSetup() may
|
|
// reset the config back to the default (Enable: false), which would
|
|
// hide the card and make the textContent call return null.
|
|
await patchGuestEnabled(adminClient, true);
|
|
await systemConsolePage.page.reload();
|
|
await systemConsolePage.page.waitForLoadState('networkidle');
|
|
const text = await systemConsolePage.page
|
|
.getByTestId('singleChannelGuests')
|
|
.textContent()
|
|
.catch(() => '');
|
|
return Number(text?.match(/(\d+)/)?.[1] ?? 0);
|
|
},
|
|
{timeout: 180000, intervals: [3000, 5000, 8000, 12000]},
|
|
)
|
|
.toBeGreaterThanOrEqual(1);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify the Single-channel Guests row appears on the Edition and License page when guests are enabled
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled and a single-channel guest limit configured
|
|
*/
|
|
test(
|
|
'displays single-channel guests row on edition and license page when guest accounts are enabled',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminUser, adminClient, team} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Create a guest user and add to one channel
|
|
const guestUser = await adminClient.createUser(await pw.random.user(), '', '');
|
|
await adminClient.updateUserRoles(guestUser.id, 'system_guest');
|
|
await adminClient.addToTeam(team.id, guestUser.id);
|
|
|
|
const channel = await adminClient.createChannel(
|
|
pw.random.channel({teamId: team.id, name: 'guest-channel', displayName: 'Guest Channel', unique: true}),
|
|
);
|
|
await adminClient.addToChannel(guestUser.id, channel.id);
|
|
|
|
// # Log in as admin and navigate to edition and license page
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
|
|
// Re-apply patch after initial load + reload to counter WebSocket config resets.
|
|
await navigateWithGuestPatch(systemConsolePage.page, adminClient, '/admin_console/about/license', true);
|
|
|
|
// * Verify the single-channel guests row is visible
|
|
await expect(systemConsolePage.page.getByText('SINGLE-CHANNEL GUESTS:')).toBeVisible();
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify the Single-channel Guests stat card is not shown when guest accounts are disabled
|
|
*/
|
|
test(
|
|
'hides single-channel guests card on site statistics page when guest accounts are disabled',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminUser, adminClient} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Disable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, false);
|
|
|
|
// # Log in as admin and navigate to site statistics
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
|
|
// Re-apply patch (disabled) + reload to ensure the browser reads the fresh config.
|
|
await navigateWithGuestPatch(
|
|
systemConsolePage.page,
|
|
adminClient,
|
|
'/admin_console/reporting/system_analytics',
|
|
false,
|
|
);
|
|
|
|
// * Verify the single-channel guests card is not in the DOM
|
|
await expect(systemConsolePage.page.getByTestId('singleChannelGuests')).not.toBeVisible();
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify the server limits API returns single-channel guest count and limit for admin users
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled
|
|
*/
|
|
test(
|
|
'returns single-channel guest data from server limits API for admin users',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminClient} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Fetch server limits
|
|
const {data: limits} = await adminClient.getServerLimits();
|
|
|
|
// * Verify the response includes single-channel guest fields
|
|
expect(limits).toHaveProperty('singleChannelGuestCount');
|
|
expect(limits).toHaveProperty('singleChannelGuestLimit');
|
|
expect(typeof limits.singleChannelGuestCount).toBe('number');
|
|
expect(typeof limits.singleChannelGuestLimit).toBe('number');
|
|
expect(limits.singleChannelGuestCount).toBeGreaterThanOrEqual(0);
|
|
expect(limits.singleChannelGuestLimit).toBeGreaterThanOrEqual(0);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify the single-channel guests card does not show error styling when count is within limit
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled and guest count is within the allowed limit
|
|
*/
|
|
test(
|
|
'shows no error styling on single-channel guests card when count is within limit',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminUser, adminClient, team} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Create a single-channel guest (count will be well within any license limit)
|
|
const guestUser = await adminClient.createUser(await pw.random.user(), '', '');
|
|
await adminClient.updateUserRoles(guestUser.id, 'system_guest');
|
|
await adminClient.addToTeam(team.id, guestUser.id);
|
|
|
|
const channel = await adminClient.createChannel(
|
|
pw.random.channel({
|
|
teamId: team.id,
|
|
name: 'guest-no-overage',
|
|
displayName: 'Guest No Overage',
|
|
unique: true,
|
|
}),
|
|
);
|
|
await adminClient.addToChannel(guestUser.id, channel.id);
|
|
|
|
// # Navigate to site statistics
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
|
|
// Re-apply patch + reload to counter WebSocket config resets.
|
|
await navigateWithGuestPatch(
|
|
systemConsolePage.page,
|
|
adminClient,
|
|
'/admin_console/reporting/system_analytics',
|
|
true,
|
|
);
|
|
|
|
// * Verify the card title does NOT have error class (count is within limit)
|
|
const cardTitle = systemConsolePage.page.getByTestId('singleChannelGuestsTitle');
|
|
await expect(cardTitle).toBeVisible({timeout: 15000});
|
|
await expect(cardTitle).not.toHaveClass(/team_statistics--error/);
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify the dismissible banner is not shown when single-channel guest count is within limit
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled and guest count is within the allowed limit
|
|
*/
|
|
test('does not show guest limit banner when count is within limit', {tag: '@system_console'}, async ({pw}) => {
|
|
const {adminUser, adminClient} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Navigate to system console and re-apply patch + reload so the browser reads
|
|
// the latest config (not a WebSocket-clobbered Redux store from a concurrent initSetup).
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
await patchGuestEnabled(adminClient, true);
|
|
await systemConsolePage.page.reload();
|
|
await systemConsolePage.page.waitForLoadState('networkidle');
|
|
|
|
// * Verify the guest limit banner is not visible (count is within limit)
|
|
await expect(systemConsolePage.page.getByTestId('single_channel_guest_limit_banner')).not.toBeVisible();
|
|
});
|
|
|
|
/**
|
|
* @objective Verify error styling appears on the single-channel guests card and dismissible banner shows when single-channel guest count exceeds the limit
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled
|
|
*/
|
|
test(
|
|
'shows error styling on guests card and banner when single-channel guest count exceeds limit',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminUser, adminClient, team} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Create multiple single-channel guests so the analytics count exceeds the mocked limit
|
|
for (let i = 0; i < 3; i++) {
|
|
const guest = await adminClient.createUser(await pw.random.user(), '', '');
|
|
await adminClient.updateUserRoles(guest.id, 'system_guest');
|
|
await adminClient.addToTeam(team.id, guest.id);
|
|
|
|
const ch = await adminClient.createChannel(
|
|
pw.random.channel({
|
|
teamId: team.id,
|
|
name: `scg-overage-${i}`,
|
|
displayName: `SCG Overage ${i}`,
|
|
unique: true,
|
|
}),
|
|
);
|
|
await adminClient.addToChannel(guest.id, ch.id);
|
|
}
|
|
|
|
// # Log in as admin (new browser context + page).
|
|
const {context, systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
|
|
// # Mock GET /api/v4/limits/server on the *context* before any navigation so the first
|
|
// getServerLimits() populates Redux with a low limit. Also fulfill with an explicit
|
|
// JSON body — route.fulfill({response, json}) does not reliably merge bodies in Playwright.
|
|
await context.route('**/api/v4/limits/server', async (route) => {
|
|
const response = await route.fetch();
|
|
let json: Record<string, unknown> = {};
|
|
try {
|
|
json = (await response.json()) as Record<string, unknown>;
|
|
} catch {
|
|
// ignore — replace with minimal payload
|
|
}
|
|
json.singleChannelGuestLimit = 1;
|
|
json.singleChannelGuestCount = 3;
|
|
await route.fulfill({
|
|
status: response.status(),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(json),
|
|
});
|
|
});
|
|
|
|
// # Navigate to site statistics page, re-applying patch to counter concurrent resets.
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
await navigateWithGuestPatch(
|
|
systemConsolePage.page,
|
|
adminClient,
|
|
'/admin_console/reporting/system_analytics',
|
|
true,
|
|
);
|
|
|
|
// * Verify the card title has error styling
|
|
const cardTitle = systemConsolePage.page.getByTestId('singleChannelGuestsTitle');
|
|
await expect(cardTitle).toBeVisible({timeout: 15000});
|
|
await expect(cardTitle).toHaveClass(/team_statistics--error/);
|
|
|
|
// * Verify the dismissible guest limit banner is visible
|
|
await expect(systemConsolePage.page.getByTestId('single_channel_guest_limit_banner')).toBeVisible();
|
|
},
|
|
);
|
|
|
|
/**
|
|
* @objective Verify that a guest in multiple channels is not counted as a single-channel guest
|
|
*
|
|
* @precondition
|
|
* Server has a non-Entry license with guest accounts enabled
|
|
*/
|
|
test(
|
|
'does not count multi-channel guest as single-channel guest on site statistics page',
|
|
{tag: '@system_console'},
|
|
async ({pw}) => {
|
|
const {adminUser, adminClient, team} = await setupSingleChannelGuestsTest(pw);
|
|
|
|
if (!adminUser) {
|
|
throw new Error('Failed to create admin user');
|
|
}
|
|
|
|
// # Enable guest accounts (narrow patch, not destructive full-config update)
|
|
await patchGuestEnabled(adminClient, true);
|
|
|
|
// # Create a guest user and add to TWO channels
|
|
const multiChannelGuest = await adminClient.createUser(await pw.random.user(), '', '');
|
|
await adminClient.updateUserRoles(multiChannelGuest.id, 'system_guest');
|
|
await adminClient.addToTeam(team.id, multiChannelGuest.id);
|
|
|
|
const channelA = await adminClient.createChannel(
|
|
pw.random.channel({
|
|
teamId: team.id,
|
|
name: 'guest-channel-a',
|
|
displayName: 'Guest Channel A',
|
|
unique: true,
|
|
}),
|
|
);
|
|
const channelB = await adminClient.createChannel(
|
|
pw.random.channel({
|
|
teamId: team.id,
|
|
name: 'guest-channel-b',
|
|
displayName: 'Guest Channel B',
|
|
unique: true,
|
|
}),
|
|
);
|
|
await adminClient.addToChannel(multiChannelGuest.id, channelA.id);
|
|
await adminClient.addToChannel(multiChannelGuest.id, channelB.id);
|
|
|
|
// # Log in as admin and navigate to site statistics, re-applying patch to counter
|
|
// concurrent initSetup() resets (default_config has GuestAccountsSettings.Enable: false).
|
|
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
|
await systemConsolePage.goto();
|
|
await systemConsolePage.toBeVisible();
|
|
await navigateWithGuestPatch(
|
|
systemConsolePage.page,
|
|
adminClient,
|
|
'/admin_console/reporting/system_analytics',
|
|
true,
|
|
);
|
|
|
|
// * Verify the single-channel guests card is visible
|
|
const singleChannelGuestsCard = systemConsolePage.page.getByTestId('singleChannelGuests');
|
|
await expect(singleChannelGuestsCard).toBeVisible({timeout: 15000});
|
|
|
|
// * Verify the count text is present — multi-channel guest should not increment it
|
|
const countText = await singleChannelGuestsCard.textContent();
|
|
const match = countText?.match(/(\d+)/);
|
|
expect(match).toBeTruthy();
|
|
|
|
const singleChannelGuestCount = Number(match![1]);
|
|
|
|
// # Now create a single-channel guest to confirm baseline counting works
|
|
const singleChannelGuest = await adminClient.createUser(await pw.random.user(), '', '');
|
|
await adminClient.updateUserRoles(singleChannelGuest.id, 'system_guest');
|
|
await adminClient.addToTeam(team.id, singleChannelGuest.id);
|
|
await adminClient.addToChannel(singleChannelGuest.id, channelA.id);
|
|
|
|
// # Reload page to get updated stats
|
|
await systemConsolePage.page.reload();
|
|
await systemConsolePage.page.waitForLoadState('networkidle');
|
|
|
|
// * Verify the count increased by exactly 1 for the new single-channel guest
|
|
const updatedCountText = await singleChannelGuestsCard.textContent();
|
|
const updatedMatch = updatedCountText?.match(/(\d+)/);
|
|
expect(updatedMatch).toBeTruthy();
|
|
expect(Number(updatedMatch![1])).toBe(singleChannelGuestCount + 1);
|
|
},
|
|
);
|
|
});
|