@camunda/e2e-test-suite 0.0.934 → 0.0.936

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createKeycloakUser = exports.createUser = exports.setupKeycloakUser = exports.keycloakAdminCredentials = void 0;
4
4
  const test_1 = require("@playwright/test");
5
+ const sleep_1 = require("../../utils/sleep");
5
6
  const keycloakAdminCredentials = () => {
6
7
  return {
7
8
  username: process.env.DISTRO_QA_E2E_TESTS_KEYCLOAK_USERNAME || 'admin',
@@ -33,13 +34,35 @@ async function createUser(navigationPage, identityPage, keycloakLoginPage, keycl
33
34
  await createIdentityUser(navigationPage, identityPage, username);
34
35
  }
35
36
  exports.createUser = createUser;
37
+ // Keycloak occasionally serves "We are sorry... Unexpected error when
38
+ // handling authentication request to identity provider" instead of
39
+ // completing the admin login (the same transient 5xx NavigationPage.goTo's
40
+ // progressive backoff already guards against for app logins). This flow logs
41
+ // into the Keycloak admin console directly rather than through goTo, so it
42
+ // needs the same retry of the whole login sequence instead of failing the
43
+ // test outright on the first hit.
44
+ async function loginToKeycloakAdmin(navigationPage, keycloakLoginPage, keycloakAdminPage, maxRetries = 5) {
45
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
46
+ try {
47
+ await navigationPage.goToKeycloak();
48
+ await keycloakLoginPage.toBeVisible();
49
+ await keycloakLoginPage.fillUsername((0, exports.keycloakAdminCredentials)().username);
50
+ await keycloakLoginPage.fillPassword((0, exports.keycloakAdminCredentials)().password);
51
+ await keycloakLoginPage.clickLogin();
52
+ await keycloakAdminPage.toBeVisible();
53
+ return;
54
+ }
55
+ catch (error) {
56
+ if (attempt >= maxRetries - 1) {
57
+ throw error;
58
+ }
59
+ console.warn(`[KeycloakUtils] Keycloak admin login attempt ${attempt + 1} failed, retrying: ${error instanceof Error ? error.message : error}`);
60
+ await (0, sleep_1.sleep)(2000 * (attempt + 1));
61
+ }
62
+ }
63
+ }
36
64
  async function createKeycloakUser(navigationPage, keycloakLoginPage, keycloakAdminPage, username, password) {
37
- await navigationPage.goToKeycloak();
38
- await keycloakLoginPage.toBeVisible();
39
- await keycloakLoginPage.fillUsername((0, exports.keycloakAdminCredentials)().username);
40
- await keycloakLoginPage.fillPassword((0, exports.keycloakAdminCredentials)().password);
41
- await keycloakLoginPage.clickLogin();
42
- await keycloakAdminPage.toBeVisible();
65
+ await loginToKeycloakAdmin(navigationPage, keycloakLoginPage, keycloakAdminPage);
43
66
  await keycloakAdminPage.switchToCamundaPlatform();
44
67
  await keycloakAdminPage.clickUsersTab();
45
68
  await keycloakAdminPage.clickAddUser();
@@ -42,5 +42,6 @@ declare class ModelerHomePage {
42
42
  clickManageButton(): Promise<void>;
43
43
  clickUploadFilesButton(): Promise<void>;
44
44
  clickMessageBanner(): Promise<void>;
45
+ private overlayBecameVisible;
45
46
  }
46
47
  export { ModelerHomePage };
@@ -257,17 +257,37 @@ class ModelerHomePage {
257
257
  async clickUploadFilesButton() {
258
258
  await this.uploadFilesButton.click({ timeout: 60000 });
259
259
  }
260
+ // Dismisses both Modeler overlays that can sit over the project list.
260
261
  async clickMessageBanner() {
261
- try {
262
- const banner = this.messageBanner
263
- .or(this.closeButton)
264
- .or(this.modalCloseButton);
265
- await banner.waitFor({ state: 'visible', timeout: 5000 });
266
- await banner.click();
262
+ // Maintenance/downtime banner
263
+ if (await this.overlayBecameVisible(this.messageBanner)) {
264
+ try {
265
+ await this.messageBanner.click();
266
+ }
267
+ catch {
268
+ // Banner dismissed itself between the probe and the click
269
+ }
267
270
  }
268
- catch {
269
- console.log('No banner or close button found to click');
271
+ // "New features" modal, dismissed after the banner rather than through an
272
+ // .or() chain: that chain stopped at whichever overlay matched first,
273
+ // leaving the other one on top to intercept the caller's next click.
274
+ if (await this.overlayBecameVisible(this.closeButton)) {
275
+ try {
276
+ await this.closeButton.click();
277
+ }
278
+ catch {
279
+ // Modal dismissed itself between the probe and the click
280
+ }
270
281
  }
271
282
  }
283
+ // Probe with waitFor, not isVisible: the "New features" modal renders lazily
284
+ // after page load, and isVisible resolves immediately — Playwright ignores
285
+ // its timeout option — so it misses an overlay that has yet to paint.
286
+ async overlayBecameVisible(overlay) {
287
+ return overlay
288
+ .waitFor({ state: 'visible', timeout: 2000 })
289
+ .then(() => true)
290
+ .catch(() => false);
291
+ }
272
292
  }
273
293
  exports.ModelerHomePage = ModelerHomePage;
@@ -30,6 +30,10 @@ declare class NavigationPage {
30
30
  username: string;
31
31
  password: string;
32
32
  }): Promise<void>;
33
+ goToOptimizeWithoutAccess({ username, password, }?: {
34
+ username?: string;
35
+ password?: string;
36
+ }): Promise<void>;
33
37
  goToKeycloak(maxRetries?: number): Promise<void>;
34
38
  goToOCAdmin(sleepTimeout?: number, credentials?: {
35
39
  username: string;
@@ -160,6 +160,27 @@ class NavigationPage {
160
160
  async goToOptimize(sleepTimeout, credentials) {
161
161
  await this.goTo(OPTIMIZE_CONTEXT_PATH, this.optimizePageBanner, sleepTimeout, credentials);
162
162
  }
163
+ // `goTo` ends by asserting the app banner, so it cannot be used to reach
164
+ // Optimize as a user without the Optimize role: that request ends on a
165
+ // `text/plain` 403 page carrying neither the banner nor a login form, and
166
+ // every retry burns a full 60s timeout. Navigate and log in only; the
167
+ // caller asserts what the denied page shows.
168
+ async goToOptimizeWithoutAccess({ username = IDENTITY_FIRSTUSER_USERNAME, password = IDENTITY_FIRSTUSER_PASSWORD, } = {}) {
169
+ await this.page.goto(OPTIMIZE_CONTEXT_PATH, {
170
+ timeout: 60000,
171
+ waitUntil: 'domcontentloaded',
172
+ });
173
+ let gatewayReloads = 0;
174
+ while (gatewayReloads < 8 && (await this.isGatewayErrorPage())) {
175
+ await (0, sleep_1.sleep)(constants_1._1_SECOND_IN_MS * 5);
176
+ await this.page.goto(OPTIMIZE_CONTEXT_PATH, {
177
+ timeout: 60000,
178
+ waitUntil: 'domcontentloaded',
179
+ });
180
+ gatewayReloads++;
181
+ }
182
+ await new LoginPage_1.LoginPage(this.page).login(username, password);
183
+ }
163
184
  async goToKeycloak(maxRetries = 5) {
164
185
  const keycloakUrl = process.env.KEYCLOAK_URL
165
186
  ? `${process.env.KEYCLOAK_URL}/auth`
@@ -46,6 +46,7 @@ declare class ModelerHomePage {
46
46
  clickManageButton(): Promise<void>;
47
47
  clickUploadFilesButton(): Promise<void>;
48
48
  clickMessageBanner(): Promise<void>;
49
+ private overlayBecameVisible;
49
50
  clickIdpApplicationTemplateOption(): Promise<void>;
50
51
  enterIdpApplicationName(name: string): Promise<void>;
51
52
  selectCluster(clusterName: string): Promise<void>;
@@ -247,18 +247,38 @@ class ModelerHomePage {
247
247
  async clickUploadFilesButton() {
248
248
  await this.uploadFilesButton.click({ timeout: 60000 });
249
249
  }
250
+ // Dismisses both Modeler overlays that can sit over the project list.
250
251
  async clickMessageBanner() {
251
- try {
252
- const banner = this.messageBanner
253
- .or(this.closeButton)
254
- .or(this.modalCloseButton);
255
- await banner.waitFor({ state: 'visible', timeout: 5000 });
256
- await banner.click();
252
+ // Maintenance/downtime banner
253
+ if (await this.overlayBecameVisible(this.messageBanner)) {
254
+ try {
255
+ await this.messageBanner.click();
256
+ }
257
+ catch {
258
+ // Banner dismissed itself between the probe and the click
259
+ }
257
260
  }
258
- catch {
259
- console.log('No banner or close button found to click');
261
+ // "New features" modal, dismissed after the banner rather than through an
262
+ // .or() chain: that chain stopped at whichever overlay matched first,
263
+ // leaving the other one on top to intercept the caller's next click.
264
+ if (await this.overlayBecameVisible(this.closeButton)) {
265
+ try {
266
+ await this.closeButton.click();
267
+ }
268
+ catch {
269
+ // Modal dismissed itself between the probe and the click
270
+ }
260
271
  }
261
272
  }
273
+ // Probe with waitFor, not isVisible: the "New features" modal renders lazily
274
+ // after page load, and isVisible resolves immediately — Playwright ignores
275
+ // its timeout option — so it misses an overlay that has yet to paint.
276
+ async overlayBecameVisible(overlay) {
277
+ return overlay
278
+ .waitFor({ state: 'visible', timeout: 2000 })
279
+ .then(() => true)
280
+ .catch(() => false);
281
+ }
262
282
  async clickIdpApplicationTemplateOption() {
263
283
  await this.idpTemplateOption.click({ timeout: 120000 });
264
284
  }
@@ -42,5 +42,6 @@ declare class ModelerHomePage {
42
42
  clickManageButton(): Promise<void>;
43
43
  clickUploadFilesButton(): Promise<void>;
44
44
  clickMessageBanner(): Promise<void>;
45
+ private overlayBecameVisible;
45
46
  }
46
47
  export { ModelerHomePage };
@@ -240,17 +240,37 @@ class ModelerHomePage {
240
240
  async clickUploadFilesButton() {
241
241
  await this.uploadFilesButton.click({ timeout: 60000 });
242
242
  }
243
+ // Dismisses both Modeler overlays that can sit over the project list.
243
244
  async clickMessageBanner() {
244
- try {
245
- const banner = this.messageBanner
246
- .or(this.closeButton)
247
- .or(this.modalCloseButton);
248
- await banner.waitFor({ state: 'visible', timeout: 5000 });
249
- await banner.click();
245
+ // Maintenance/downtime banner
246
+ if (await this.overlayBecameVisible(this.messageBanner)) {
247
+ try {
248
+ await this.messageBanner.click();
249
+ }
250
+ catch {
251
+ // Banner dismissed itself between the probe and the click
252
+ }
250
253
  }
251
- catch {
252
- console.log('No banner or close button found to click');
254
+ // "New features" modal, dismissed after the banner rather than through an
255
+ // .or() chain: that chain stopped at whichever overlay matched first,
256
+ // leaving the other one on top to intercept the caller's next click.
257
+ if (await this.overlayBecameVisible(this.closeButton)) {
258
+ try {
259
+ await this.closeButton.click();
260
+ }
261
+ catch {
262
+ // Modal dismissed itself between the probe and the click
263
+ }
253
264
  }
254
265
  }
266
+ // Probe with waitFor, not isVisible: the "New features" modal renders lazily
267
+ // after page load, and isVisible resolves immediately — Playwright ignores
268
+ // its timeout option — so it misses an overlay that has yet to paint.
269
+ async overlayBecameVisible(overlay) {
270
+ return overlay
271
+ .waitFor({ state: 'visible', timeout: 2000 })
272
+ .then(() => true)
273
+ .catch(() => false);
274
+ }
255
275
  }
256
276
  exports.ModelerHomePage = ModelerHomePage;
@@ -17,8 +17,7 @@ SM_8_10_1.test.describe('Identity User Flow Tests', () => {
17
17
  await (0, _setup_1.captureScreenshot)(page, testInfo);
18
18
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
19
19
  });
20
- //Skipped due to bug 35735: https://github.com/camunda/camunda/issues/35735
21
- SM_8_10_1.test.skip('User Roles User Flow on Management Identity @tasklistV2', async ({ page, navigationPage, optimizeHomePage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, modelerHomePage, settingsPage, browser, }) => {
20
+ (0, SM_8_10_1.test)('User Roles User Flow on Management Identity @tasklistV2', async ({ page, navigationPage, optimizeHomePage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, modelerHomePage, settingsPage, browser, }) => {
22
21
  if (node_process_1.default.env.IS_RBA === 'true' ||
23
22
  node_process_1.default.env.IS_MT === 'true' ||
24
23
  node_process_1.default.env.IS_LICENSE_KEY === 'true') {
@@ -69,11 +68,11 @@ SM_8_10_1.test.describe('Identity User Flow Tests', () => {
69
68
  });
70
69
  if (node_process_1.default.env.IS_OPTIMIZE !== 'false') {
71
70
  await SM_8_10_1.test.step('Ensure Optimize is not Accessible', async () => {
72
- await navigationPage.goToOptimize(undefined, credentials);
73
- await (0, test_1.expect)(optimizeHomePage.optimizeBanner).not.toBeVisible({
71
+ await navigationPage.goToOptimizeWithoutAccess(credentials);
72
+ await (0, test_1.expect)(page.getByText('User has no authorization to access Optimize. Please check your Identity configuration')).toBeVisible({
74
73
  timeout: 60000,
75
74
  });
76
- await (0, test_1.expect)(page.getByText('User has no authorization to access Optimize. Please check your Identity configuration')).toBeVisible({
75
+ await (0, test_1.expect)(optimizeHomePage.optimizeBanner).not.toBeVisible({
77
76
  timeout: 60000,
78
77
  });
79
78
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.934",
3
+ "version": "0.0.936",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",