mirror of
https://github.com/mattermost/mattermost.git
synced 2026-08-26 21:27:40 -05:00
E2E/Playwright: Upgrade Playwright to 1.61 and its deps (#37277)
* chore: upgrade playwright to 1.61 and its deps * fix failed tests * address comments * use assignTeamsToPolicy helper method
This commit is contained in:
@@ -210,7 +210,7 @@ jobs:
|
||||
# tar for all future runs — no need for a retry loop.
|
||||
run: |
|
||||
set -euo pipefail
|
||||
IMAGE="mcr.microsoft.com/playwright:v1.59.1-noble"
|
||||
IMAGE="mcr.microsoft.com/playwright:v1.61.0-noble"
|
||||
TAR="/tmp/playwright-docker-image.tar"
|
||||
if [ -f "${TAR}" ]; then
|
||||
echo "Loading Playwright image from GitHub Actions cache"
|
||||
|
||||
@@ -289,7 +289,7 @@ $(if mme2e_is_token_in_list "playwright" "$ENABLED_DOCKER_SERVICES"; then
|
||||
# shellcheck disable=SC2016
|
||||
echo '
|
||||
playwright:
|
||||
image: mcr.microsoft.com/playwright:v1.59.1-noble
|
||||
image: mcr.microsoft.com/playwright:v1.61.0-noble
|
||||
entrypoint: ["/bin/bash", "-c"]
|
||||
command:
|
||||
- |
|
||||
|
||||
@@ -150,7 +150,7 @@ test(
|
||||
Change to the `./` project directory, then run the docker container. (See https://playwright.dev/docs/docker for reference.)
|
||||
|
||||
```bash
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.59.1-noble /bin/bash
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.61.0-noble /bin/bash
|
||||
```
|
||||
|
||||
#### 2. Inside the docker container
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mattermost/playwright-lib",
|
||||
"version": "11.7.0",
|
||||
"version": "11.10.0",
|
||||
"description": "A comprehensive end-to-end testing library for Mattermost web, desktop and plugin applications using Playwright",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -43,26 +43,26 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@axe-core/playwright": "4.11.1",
|
||||
"@axe-core/playwright": "4.12.1",
|
||||
"@mattermost/client": "file:../../../webapp/platform/client",
|
||||
"@mattermost/types": "file:../../../webapp/platform/types",
|
||||
"@percy/cli": "1.31.11",
|
||||
"@percy/cli": "1.32.2",
|
||||
"@percy/playwright": "1.1.0",
|
||||
"async-wait-until": "2.0.31",
|
||||
"axe-core": "4.11.2",
|
||||
"axe-core": "4.12.1",
|
||||
"chalk": "5.6.2",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv": "17.4.2",
|
||||
"luxon": "3.7.2",
|
||||
"mime-types": "3.0.2",
|
||||
"uuid": "13.0.0"
|
||||
"uuid": "14.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "12.3.0",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/luxon": "3.7.2",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/node": "25.6.0",
|
||||
"rollup": "4.60.1",
|
||||
"@types/node": "26.0.1",
|
||||
"rollup": "4.62.2",
|
||||
"rollup-plugin-copy": "3.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import merge from 'deepmerge';
|
||||
import type {
|
||||
AccessControlSettings,
|
||||
AdminConfig,
|
||||
ClusterSettings,
|
||||
EmailSettings,
|
||||
@@ -26,6 +27,7 @@ export function mergeWithOnPremServerConfig(overrides: Partial<AdminConfig>): Ad
|
||||
}
|
||||
|
||||
type TestAdminConfig = {
|
||||
AccessControlSettings: Partial<AccessControlSettings>;
|
||||
ClusterSettings: Partial<ClusterSettings>;
|
||||
EmailSettings: Partial<EmailSettings>;
|
||||
ExperimentalSettings: Partial<ExperimentalSettings>;
|
||||
@@ -39,11 +41,16 @@ type TestAdminConfig = {
|
||||
// On-prem setting that is different from the default
|
||||
const onPremServerConfig = (): Partial<TestAdminConfig> => {
|
||||
return {
|
||||
AccessControlSettings: {
|
||||
EnableAttributeBasedAccessControl: true,
|
||||
EnableUserManagedAttributes: true,
|
||||
},
|
||||
ClusterSettings: {
|
||||
Enable: testConfig.haClusterEnabled,
|
||||
ClusterName: testConfig.haClusterName,
|
||||
},
|
||||
EmailSettings: {
|
||||
FeedbackName: 'Mattermost',
|
||||
PushNotificationServer: testConfig.pushNotificationServer,
|
||||
},
|
||||
LogSettings: {
|
||||
@@ -77,6 +84,7 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
|
||||
EnableSecurityFixAlert: false,
|
||||
GiphySdkKey: 's0glxvzVg9azvPipKxcPLpXV0q1x1fVP',
|
||||
EnableTesting: true,
|
||||
AllowedUntrustedInternalConnections: 'localhost 127.0.0.1',
|
||||
},
|
||||
TeamSettings: {
|
||||
EnableOpenServer: true,
|
||||
@@ -86,7 +94,7 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
|
||||
};
|
||||
|
||||
// Should be based only from the generated default config from ./server via "make config-reset"
|
||||
// Based on v11.7 server
|
||||
// Based on v11.9 server
|
||||
const defaultServerConfig: AdminConfig = {
|
||||
ServiceSettings: {
|
||||
SiteURL: '',
|
||||
@@ -129,7 +137,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
EnableClientPerformanceDebugging: false,
|
||||
EnableSecurityFixAlert: true,
|
||||
EnableInsecureOutgoingConnections: false,
|
||||
AllowedUntrustedInternalConnections: 'localhost,127.0.0.1',
|
||||
AllowedUntrustedInternalConnections: '',
|
||||
EnableMultifactorAuthentication: false,
|
||||
EnforceMultifactorAuthentication: false,
|
||||
EnableUserAccessTokens: false,
|
||||
@@ -233,6 +241,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
EnableLastActiveTime: true,
|
||||
UserStatusAwayTimeout: 300,
|
||||
MaxChannelsPerTeam: 2000,
|
||||
EnableChannelCategorySorting: true,
|
||||
MaxNotificationsPerChannel: 1000,
|
||||
EnableConfirmNotificationsToChannel: true,
|
||||
TeammateNameDisplay: 'username',
|
||||
@@ -240,7 +249,6 @@ const defaultServerConfig: AdminConfig = {
|
||||
LockTeammateNameDisplay: false,
|
||||
ExperimentalPrimaryTeam: '',
|
||||
ExperimentalDefaultChannels: [],
|
||||
EnableChannelCategorySorting: true,
|
||||
},
|
||||
ClientRequirements: {
|
||||
AndroidLatestVersion: '',
|
||||
@@ -289,7 +297,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
Certificate: '',
|
||||
},
|
||||
PasswordSettings: {
|
||||
MinimumLength: 14,
|
||||
MinimumLength: 8,
|
||||
Lowercase: false,
|
||||
Number: false,
|
||||
Uppercase: false,
|
||||
@@ -324,6 +332,15 @@ const defaultServerConfig: AdminConfig = {
|
||||
AmazonS3RequestTimeoutMilliseconds: 30000,
|
||||
AmazonS3UploadPartSizeBytes: 5242880,
|
||||
AmazonS3StorageClass: '',
|
||||
AzureStorageAccount: '',
|
||||
AzureAuthMode: 'shared_key',
|
||||
AzureAccessKey: '',
|
||||
AzureContainer: '',
|
||||
AzurePathPrefix: '',
|
||||
AzureCloud: 'commercial',
|
||||
AzureEndpoint: '',
|
||||
AzureSSL: true,
|
||||
AzureRequestTimeoutMilliseconds: 30000,
|
||||
DedicatedExportStore: false,
|
||||
ExportDriverName: 'local',
|
||||
ExportDirectory: './data/',
|
||||
@@ -341,6 +358,16 @@ const defaultServerConfig: AdminConfig = {
|
||||
ExportAmazonS3PresignExpiresSeconds: 21600,
|
||||
ExportAmazonS3UploadPartSizeBytes: 104857600,
|
||||
ExportAmazonS3StorageClass: '',
|
||||
ExportAzureStorageAccount: '',
|
||||
ExportAzureAuthMode: 'shared_key',
|
||||
ExportAzureAccessKey: '',
|
||||
ExportAzureContainer: '',
|
||||
ExportAzurePathPrefix: '',
|
||||
ExportAzureCloud: 'commercial',
|
||||
ExportAzureEndpoint: '',
|
||||
ExportAzureSSL: true,
|
||||
ExportAzureRequestTimeoutMilliseconds: 30000,
|
||||
ExportAzurePresignExpiresSeconds: 21600,
|
||||
},
|
||||
EmailSettings: {
|
||||
EnableSignUpWithEmail: true,
|
||||
@@ -349,7 +376,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
SendEmailNotifications: true,
|
||||
UseChannelInEmailNotifications: false,
|
||||
RequireEmailVerification: false,
|
||||
FeedbackName: 'Mattermost',
|
||||
FeedbackName: '',
|
||||
FeedbackEmail: 'test@example.com',
|
||||
ReplyToAddress: 'test@example.com',
|
||||
FeedbackOrganization: '',
|
||||
@@ -655,7 +682,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
ClientKey: '',
|
||||
Trace: '',
|
||||
IgnoredPurgeIndexes: '',
|
||||
EnableSearchPublicChannelsWithoutMembership: false,
|
||||
EnableSearchPublicChannelsWithoutMembership: true,
|
||||
},
|
||||
DataRetentionSettings: {
|
||||
EnableMessageDeletion: false,
|
||||
@@ -672,6 +699,12 @@ const defaultServerConfig: AdminConfig = {
|
||||
RetentionIdsBatchSize: 100,
|
||||
PreservePinnedPosts: false,
|
||||
},
|
||||
MobileEphemeralModeSettings: {
|
||||
Enable: false,
|
||||
DisconnectionTimeoutSeconds: 60,
|
||||
OfflinePersistenceTimerHours: 24,
|
||||
AutoCacheCleanupDays: 7,
|
||||
},
|
||||
MessageExportSettings: {
|
||||
EnableExport: false,
|
||||
ExportFormat: 'actiance',
|
||||
@@ -774,20 +807,28 @@ const defaultServerConfig: AdminConfig = {
|
||||
ExperimentalAuditSettingsSystemConsoleUI: true,
|
||||
CustomProfileAttributes: true,
|
||||
AttributeBasedAccessControl: true,
|
||||
PermissionPolicies: true,
|
||||
AttributeValueMasking: false,
|
||||
PermissionPolicies: false,
|
||||
ChannelPermissionPolicies: false,
|
||||
PolicySimulation: false,
|
||||
ContentFlagging: true,
|
||||
EnableMattermostEntry: true,
|
||||
MobileSSOCodeExchange: false,
|
||||
EnableShiftEscapeToMarkAllRead: false,
|
||||
AutoTranslation: true,
|
||||
ClassificationMarkings: true,
|
||||
BurnOnRead: true,
|
||||
EnableAIPluginBridge: false,
|
||||
EnableAIRecaps: false,
|
||||
ClassificationMarkings: true,
|
||||
PropertyFieldRank: true,
|
||||
IntegratedBoards: false,
|
||||
CJKSearch: true,
|
||||
AggregatePluginMetrics: false,
|
||||
ManagedChannelCategories: false,
|
||||
MobileEphemeralMode: true,
|
||||
SessionAttributes: false,
|
||||
DiscoverableChannels: false,
|
||||
MobileEphemeralMode: false,
|
||||
PropertyFieldRank: false,
|
||||
TeamMembershipAccessControl: false,
|
||||
MmBlocksEnabled: true,
|
||||
},
|
||||
ImportSettings: {
|
||||
@@ -817,13 +858,13 @@ const defaultServerConfig: AdminConfig = {
|
||||
MemberSyncBatchSize: 20,
|
||||
},
|
||||
AccessControlSettings: {
|
||||
EnableAttributeBasedAccessControl: true,
|
||||
EnableUserManagedAttributes: true,
|
||||
EnableAttributeBasedAccessControl: false,
|
||||
EnableUserManagedAttributes: false,
|
||||
TrustProxyDeviceIdentityHeader: false,
|
||||
EnforceDeviceIDConsistency: false,
|
||||
},
|
||||
ContentFlaggingSettings: {
|
||||
EnableContentFlagging: true,
|
||||
EnableContentFlagging: false,
|
||||
NotificationSettings: {
|
||||
EventTargetMapping: {
|
||||
assigned: ['reviewers'],
|
||||
@@ -850,7 +891,7 @@ const defaultServerConfig: AdminConfig = {
|
||||
CommonReviewers: true,
|
||||
CommonReviewerIds: [],
|
||||
TeamReviewersSetting: {},
|
||||
SystemAdminsAsReviewers: true,
|
||||
SystemAdminsAsReviewers: false,
|
||||
TeamAdminsAsReviewers: true,
|
||||
},
|
||||
},
|
||||
@@ -869,10 +910,4 @@ const defaultServerConfig: AdminConfig = {
|
||||
LLMServiceID: '',
|
||||
},
|
||||
},
|
||||
MobileEphemeralModeSettings: {
|
||||
Enable: false,
|
||||
DisconnectionTimeoutSeconds: 60,
|
||||
OfflinePersistenceTimerHours: 24,
|
||||
AutoCacheCleanupDays: 7,
|
||||
},
|
||||
};
|
||||
|
||||
+1
-6
@@ -52,12 +52,7 @@ export class ColumnToggleMenu {
|
||||
}
|
||||
|
||||
type RoleFilter =
|
||||
| 'Any'
|
||||
| 'System Admin'
|
||||
| 'Member'
|
||||
| 'Guests (all)'
|
||||
| 'Guests in a single channel'
|
||||
| 'Guests in multiple channels';
|
||||
'Any' | 'System Admin' | 'Member' | 'Guests (all)' | 'Guests in a single channel' | 'Guests in multiple channels';
|
||||
type StatusFilter = 'Any' | 'Activated users' | 'Deactivated users';
|
||||
|
||||
/**
|
||||
|
||||
Generated
+2046
-2867
File diff suppressed because it is too large
Load Diff
@@ -35,19 +35,20 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mattermost/eslint-plugin": "file:../../webapp/platform/eslint-plugin",
|
||||
"@playwright/test": "1.59.1",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/pg": "8.15.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.58.1",
|
||||
"@playwright/test": "1.61.1",
|
||||
"@types/luxon": "3.7.2",
|
||||
"@types/pg": "8.20.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"cross-env": "10.1.0",
|
||||
"dayjs": "1.11.20",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"dayjs": "1.11.21",
|
||||
"eslint": "9.39.4",
|
||||
"eslint-import-resolver-typescript": "4.4.5",
|
||||
"globals": "17.7.0",
|
||||
"glob": "13.0.6",
|
||||
"luxon": "3.7.2",
|
||||
"pg": "8.13.1",
|
||||
"prettier": "3.8.2",
|
||||
"typescript": "6.0.2",
|
||||
"zod": "4.3.6"
|
||||
"pg": "8.22.0",
|
||||
"prettier": "3.9.1",
|
||||
"typescript": "6.0.3",
|
||||
"zod": "4.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import {
|
||||
* MM-T5800: Policy enforcement after attribute change (bidirectional)
|
||||
*/
|
||||
test('MM-T5800 Policy enforcement after attribute change (bidirectional)', async ({pw}) => {
|
||||
test.setTimeout(120000);
|
||||
test.setTimeout(180000);
|
||||
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
|
||||
+9
-11
@@ -9,6 +9,7 @@ import {
|
||||
createTeamAdmin,
|
||||
waitForAttributeViewToInclude,
|
||||
} from '../../../channels/team_settings/helpers';
|
||||
import {assignTeamsToPolicy} from '../teams/helpers';
|
||||
import {enableUserManagedAttributes} from '../support';
|
||||
|
||||
import {
|
||||
@@ -66,21 +67,18 @@ test.describe('Attribute-Value Masking - Admin Roles', {tag: ['@abac', '@abac_ma
|
||||
`user.attributes.${fieldName} in ["Alpha", "Bravo"]`,
|
||||
);
|
||||
policyIds.push(policyId);
|
||||
await setFieldAsSharedOnly(fieldId);
|
||||
|
||||
// Assign team and a channel to the policy so team settings shows the policy
|
||||
// and the Remove link is present to exercise the "even after removing all channels" path.
|
||||
// Assign channel first so the attribute view can be primed before
|
||||
// we flip the field to shared_only (self-inclusion filter in
|
||||
// SearchTeamAccessPolicies requires the view to be current).
|
||||
await adminClient.addToTeam(team.id, adminUser.id);
|
||||
const channel = await createPrivateChannel(adminClient, team.id);
|
||||
await assignChannelsToPolicy(adminClient, policyId, [channel.id]);
|
||||
try {
|
||||
await (adminClient as any).doFetch(
|
||||
`${(adminClient as any).getBaseRoute()}/access_control_policies/${policyId}/teams`,
|
||||
{method: 'POST', body: JSON.stringify({team_id: team.id})},
|
||||
);
|
||||
} catch {
|
||||
// best-effort assignment — test still validates button state
|
||||
}
|
||||
await waitForAttributeViewToInclude(adminClient, `user.attributes.${fieldName} in ["Alpha", "Bravo"]`, [
|
||||
adminUser.id,
|
||||
]);
|
||||
await setFieldAsSharedOnly(fieldId);
|
||||
await assignTeamsToPolicy(adminClient, policyId, [team.id]);
|
||||
|
||||
const {page} = await pw.testBrowser.login(adminUser);
|
||||
const channelsPage = new ChannelsPage(page);
|
||||
|
||||
+30
-12
@@ -5,6 +5,7 @@ import {expect, test, navigateToABACPage, runSyncJob, verifyUserInChannel} from
|
||||
|
||||
import type {CustomProfileAttribute} from '../../../channels/custom_profile_attributes/helpers';
|
||||
import {setupCustomProfileAttributeFields} from '../../../channels/custom_profile_attributes/helpers';
|
||||
import {waitForAttributeViewToInclude} from '../../../channels/team_settings/helpers';
|
||||
import {
|
||||
ensureUserAttributes,
|
||||
createUserForABAC,
|
||||
@@ -67,14 +68,19 @@ test('MM-T5786 Test "is not" (!=) operator in Simple mode', async ({pw}) => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
}
|
||||
|
||||
await waitForAttributeViewToInclude(adminClient, 'user.attributes.Department != "Sales"', [engineerUser.id]);
|
||||
await activatePolicy(adminClient, policyId);
|
||||
await runSyncJob(systemConsolePage.page);
|
||||
await waitForPolicySyncJob(adminClient, policyId);
|
||||
|
||||
const engInChannel = await verifyUserInChannel(adminClient, engineerUser.id, channel.id);
|
||||
const salesInChannel = await verifyUserInChannel(adminClient, salesUser.id, channel.id);
|
||||
expect(engInChannel).toBe(true);
|
||||
expect(salesInChannel).toBe(false);
|
||||
await expect
|
||||
.poll(() => verifyUserInChannel(adminClient, engineerUser.id, channel.id), {
|
||||
timeout: 15_000,
|
||||
intervals: [500, 1000, 2000],
|
||||
message: 'engineerUser should be in channel after sync',
|
||||
})
|
||||
.toBe(true);
|
||||
expect(await verifyUserInChannel(adminClient, salesUser.id, channel.id)).toBe(false);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -128,14 +134,21 @@ test('MM-T5786 Test "in" operator in Simple mode', async ({pw}) => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
}
|
||||
|
||||
await waitForAttributeViewToInclude(adminClient, 'user.attributes.Department in ["Engineering", "DevOps"]', [
|
||||
engineerUser.id,
|
||||
]);
|
||||
await activatePolicy(adminClient, policyId);
|
||||
await runSyncJob(systemConsolePage.page);
|
||||
await waitForPolicySyncJob(adminClient, policyId);
|
||||
|
||||
const engInChannel = await verifyUserInChannel(adminClient, engineerUser.id, channel.id);
|
||||
const salesInChannel = await verifyUserInChannel(adminClient, salesUser.id, channel.id);
|
||||
expect(engInChannel).toBe(true);
|
||||
expect(salesInChannel).toBe(false);
|
||||
await expect
|
||||
.poll(() => verifyUserInChannel(adminClient, engineerUser.id, channel.id), {
|
||||
timeout: 15_000,
|
||||
intervals: [500, 1000, 2000],
|
||||
message: 'engineerUser should be in channel after sync',
|
||||
})
|
||||
.toBe(true);
|
||||
expect(await verifyUserInChannel(adminClient, salesUser.id, channel.id)).toBe(false);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -189,12 +202,17 @@ test('MM-T5786 Test "starts with" operator in Simple mode', async ({pw}) => {
|
||||
await navigateToABACPage(systemConsolePage.page);
|
||||
}
|
||||
|
||||
await waitForAttributeViewToInclude(adminClient, 'user.attributes.Department.startsWith("Eng")', [engineerUser.id]);
|
||||
await activatePolicy(adminClient, policyId);
|
||||
await runSyncJob(systemConsolePage.page);
|
||||
await waitForPolicySyncJob(adminClient, policyId);
|
||||
|
||||
const engInChannel = await verifyUserInChannel(adminClient, engineerUser.id, channel.id);
|
||||
const salesInChannel = await verifyUserInChannel(adminClient, salesUser.id, channel.id);
|
||||
expect(engInChannel).toBe(true);
|
||||
expect(salesInChannel).toBe(false);
|
||||
await expect
|
||||
.poll(() => verifyUserInChannel(adminClient, engineerUser.id, channel.id), {
|
||||
timeout: 15_000,
|
||||
intervals: [500, 1000, 2000],
|
||||
message: 'engineerUser should be in channel after sync',
|
||||
})
|
||||
.toBe(true);
|
||||
expect(await verifyUserInChannel(adminClient, salesUser.id, channel.id)).toBe(false);
|
||||
});
|
||||
|
||||
+29
-4
@@ -20,6 +20,7 @@ import {
|
||||
getPolicyIdByName,
|
||||
enableUserManagedAttributes,
|
||||
} from '../support';
|
||||
import {deleteFieldFromDB} from '../masking/masking_db_setup';
|
||||
|
||||
// Restore AccessControlSettings to the shared baseline expected by
|
||||
// `specs/test_setup.ts` (ABAC enabled) after this file's tests complete, so
|
||||
@@ -62,13 +63,25 @@ test('MM-T5791 Editing policy to add attribute with auto-add enabled', async ({p
|
||||
|
||||
const {adminUser, adminClient, team} = await pw.initSetup();
|
||||
|
||||
// Use ensure-exists pattern - non-destructive, safe for parallel test runs
|
||||
// Ensure Office field exists with correct managed:admin permissions.
|
||||
// Fields created by older server versions may lack permission_values, causing 403
|
||||
// when setting attribute values — delete and recreate if permission_values is missing.
|
||||
// Legacy fields with permission_field=null reject API deletion (403), so fall back to DB.
|
||||
const existingFields = await adminClient.getCustomProfileAttributeFields();
|
||||
const attributeFieldsMap: Record<string, any> = {};
|
||||
for (const field of existingFields) {
|
||||
attributeFieldsMap[field.id] = field;
|
||||
}
|
||||
if (!existingFields.some((f: any) => f.name === 'Office')) {
|
||||
const existingOffice = existingFields.find((f: any) => f.name === 'Office') as any;
|
||||
if (!existingOffice || !(existingOffice as any).permission_values) {
|
||||
if (existingOffice) {
|
||||
try {
|
||||
await adminClient.deleteCustomProfileAttributeField(existingOffice.id);
|
||||
} catch {
|
||||
await deleteFieldFromDB(existingOffice.id);
|
||||
}
|
||||
delete attributeFieldsMap[existingOffice.id];
|
||||
}
|
||||
const officeField = await adminClient.createCustomProfileAttributeField({
|
||||
name: 'Office',
|
||||
type: 'text',
|
||||
@@ -318,13 +331,25 @@ test('MM-T5792 Editing policy to remove attribute rule with auto-add enabled', a
|
||||
|
||||
const {adminUser, adminClient, team} = await pw.initSetup();
|
||||
|
||||
// Use ensure-exists pattern - non-destructive, safe for parallel test runs
|
||||
// Ensure Office field exists with correct managed:admin permissions.
|
||||
// Fields created by older server versions may lack permission_values, causing 403
|
||||
// when setting attribute values — delete and recreate if permission_values is missing.
|
||||
// Legacy fields with permission_field=null reject API deletion (403), so fall back to DB.
|
||||
const existingFields = await adminClient.getCustomProfileAttributeFields();
|
||||
const attributeFieldsMap: Record<string, any> = {};
|
||||
for (const field of existingFields) {
|
||||
attributeFieldsMap[field.id] = field;
|
||||
}
|
||||
if (!existingFields.some((f: any) => f.name === 'Office')) {
|
||||
const existingOffice = existingFields.find((f: any) => f.name === 'Office') as any;
|
||||
if (!existingOffice || !(existingOffice as any).permission_values) {
|
||||
if (existingOffice) {
|
||||
try {
|
||||
await adminClient.deleteCustomProfileAttributeField(existingOffice.id);
|
||||
} catch {
|
||||
await deleteFieldFromDB(existingOffice.id);
|
||||
}
|
||||
delete attributeFieldsMap[existingOffice.id];
|
||||
}
|
||||
const officeField = await adminClient.createCustomProfileAttributeField({
|
||||
name: 'Office',
|
||||
type: 'text',
|
||||
|
||||
@@ -1000,7 +1000,7 @@ export async function waitForLatestSyncJob(
|
||||
* Uses `expect.poll` with 500 ms intervals and a 30 s timeout so jobs that are
|
||||
* briefly delayed in the queue do not cause spurious failures.
|
||||
*/
|
||||
export async function waitForPolicySyncJob(client: Client4, policyId: string): Promise<void> {
|
||||
export async function waitForPolicySyncJob(client: Client4, policyId: string, timeoutMs = 60_000): Promise<void> {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
@@ -1030,9 +1030,9 @@ export async function waitForPolicySyncJob(client: Client4, policyId: string): P
|
||||
}
|
||||
},
|
||||
{
|
||||
timeout: 30_000,
|
||||
timeout: timeoutMs,
|
||||
intervals: [500, 500, 500, 1000, 1000, 2000],
|
||||
message: `Policy sync job for ${policyId} did not reach success within 30 s`,
|
||||
message: `Policy sync job for ${policyId} did not reach success within ${timeoutMs / 1000} s`,
|
||||
},
|
||||
)
|
||||
.toBe('success');
|
||||
|
||||
@@ -585,6 +585,15 @@ export type FileSettings = {
|
||||
AmazonS3RequestTimeoutMilliseconds: number;
|
||||
AmazonS3UploadPartSizeBytes: number;
|
||||
AmazonS3StorageClass: string;
|
||||
AzureStorageAccount: string;
|
||||
AzureAuthMode: string;
|
||||
AzureAccessKey: string;
|
||||
AzureContainer: string;
|
||||
AzurePathPrefix: string;
|
||||
AzureCloud: string;
|
||||
AzureEndpoint: string;
|
||||
AzureSSL: boolean;
|
||||
AzureRequestTimeoutMilliseconds: number;
|
||||
DedicatedExportStore: boolean;
|
||||
ExportDriverName: string;
|
||||
ExportDirectory: string;
|
||||
@@ -602,6 +611,16 @@ export type FileSettings = {
|
||||
ExportAmazonS3PresignExpiresSeconds: number;
|
||||
ExportAmazonS3UploadPartSizeBytes: number;
|
||||
ExportAmazonS3StorageClass: string;
|
||||
ExportAzureStorageAccount: string;
|
||||
ExportAzureAuthMode: string;
|
||||
ExportAzureAccessKey: string;
|
||||
ExportAzureContainer: string;
|
||||
ExportAzurePathPrefix: string;
|
||||
ExportAzureCloud: string;
|
||||
ExportAzureEndpoint: string;
|
||||
ExportAzureSSL: boolean;
|
||||
ExportAzureRequestTimeoutMilliseconds: number;
|
||||
ExportAzurePresignExpiresSeconds: number;
|
||||
};
|
||||
|
||||
export type EmailSettings = {
|
||||
|
||||
Reference in New Issue
Block a user