@camunda/e2e-test-suite 0.0.154 → 0.0.156

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.
@@ -21,7 +21,10 @@ class AppsPage {
21
21
  camundaAppsButton;
22
22
  constructor(page) {
23
23
  this.page = page;
24
- this.modelerLink = page.getByRole('link', { name: 'Modeler', exact: true });
24
+ this.modelerLink = page
25
+ .getByRole('link', { name: 'Modeler', exact: true })
26
+ .or(page.getByLabel('App Panel').getByLabel('Camunda Modeler'))
27
+ .first();
25
28
  this.appSwitcherButton = page.getByLabel('App Switcher');
26
29
  this.tasklistLink = page.getByRole('link', { name: 'Tasklist', exact: true });
27
30
  this.operateLink = page.getByRole('link', { name: 'Operate', exact: true });
@@ -12,6 +12,7 @@ declare class FormJsPage {
12
12
  readonly documentReferenceInput: Locator;
13
13
  readonly generalPanel: Locator;
14
14
  readonly textField: Locator;
15
+ readonly textFieldInForm: Locator;
15
16
  constructor(page: Page);
16
17
  generateAIForm(request?: string): Promise<void>;
17
18
  clickAIFormGeneratorButton(): Promise<void>;
@@ -16,6 +16,7 @@ class FormJsPage {
16
16
  documentReferenceInput;
17
17
  generalPanel;
18
18
  textField;
19
+ textFieldInForm;
19
20
  constructor(page) {
20
21
  this.page = page;
21
22
  this.aiFormGeneratorButton = page.getByRole('button', {
@@ -33,6 +34,9 @@ class FormJsPage {
33
34
  });
34
35
  this.generalPanel = page.locator('[data-group-id="group-general"]').first();
35
36
  this.textField = page.locator('button[data-field-type="textfield"]');
37
+ this.textFieldInForm = page
38
+ .getByLabel('Form Definition')
39
+ .getByText('Text field');
36
40
  }
37
41
  async generateAIForm(request = 'Create a form with the following fields: 1. A text field with the label "Full Name" 2. A number with the label "Count" 3. A date input with the label "Date of birth"4. A Checkbox with the label "Agree"') {
38
42
  await (0, test_1.expect)(this.aiFormGeneratorButton).toBeVisible({
@@ -118,7 +118,9 @@ class AppsPage {
118
118
  catch (error) {
119
119
  console.warn(`Click attempt ${retries + 1} failed: ${error}`);
120
120
  await this.page.waitForLoadState('domcontentloaded');
121
- await this.page.reload();
121
+ if (!this.page.isClosed()) {
122
+ await this.page.reload();
123
+ }
122
124
  await (0, sleep_1.sleep)(20000);
123
125
  }
124
126
  }
@@ -152,7 +154,9 @@ class AppsPage {
152
154
  catch (error) {
153
155
  console.warn(`Click attempt ${retries + 1} failed: ${error}`);
154
156
  await (0, UtilitiesPage_1.waitForLoadingToFinish)(this.page);
155
- await this.page.reload();
157
+ if (!this.page.isClosed()) {
158
+ await this.page.reload();
159
+ }
156
160
  await (0, sleep_1.sleep)(20000);
157
161
  }
158
162
  }
@@ -226,7 +230,9 @@ class AppsPage {
226
230
  catch (error) {
227
231
  console.warn(`Click attempt ${retries + 1} failed: ${error}`);
228
232
  await this.page.waitForLoadState('domcontentloaded');
229
- await this.page.reload();
233
+ if (!this.page.isClosed()) {
234
+ await this.page.reload();
235
+ }
230
236
  await (0, sleep_1.sleep)(20000);
231
237
  }
232
238
  }
@@ -260,7 +266,9 @@ class AppsPage {
260
266
  catch (error) {
261
267
  console.warn(`Click attempt ${retries + 1} failed: ${error}`);
262
268
  await this.page.waitForLoadState('domcontentloaded');
263
- await this.page.reload();
269
+ if (!this.page.isClosed()) {
270
+ await this.page.reload();
271
+ }
264
272
  await (0, sleep_1.sleep)(20000);
265
273
  }
266
274
  }
@@ -37,6 +37,7 @@ declare class ClusterSecretsPage {
37
37
  name: string;
38
38
  value: string;
39
39
  }[]): Promise<void>;
40
+ clickImportButton(): Promise<void>;
40
41
  bulkImportSecrets(secrets: {
41
42
  name: string;
42
43
  value: string;
@@ -187,17 +187,24 @@ class ClusterSecretsPage {
187
187
  }
188
188
  }
189
189
  }
190
+ async clickImportButton() {
191
+ await (0, test_1.expect)(this.importButton).toBeVisible({ timeout: 30000 });
192
+ await this.importButton.click();
193
+ }
190
194
  async bulkImportSecrets(secrets) {
191
195
  const mapped = secrets
192
196
  .map((secret) => `${secret.name}=${secret.value}`)
193
197
  .join('\n');
194
- await this.importButton.click();
195
- await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.fromFileButton, {
196
- postAction: async () => {
197
- await this.importButton.click();
198
- },
199
- });
200
- await this.fromFileButton.click();
198
+ await this.clickImportButton();
199
+ if (!(await this.dialog.isVisible())) {
200
+ await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.fromFileButton, {
201
+ postAction: async () => {
202
+ await this.clickImportButton();
203
+ },
204
+ });
205
+ await this.fromFileButton.click();
206
+ }
207
+ await (0, test_1.expect)(this.dialog).toBeVisible();
201
208
  await this.bulkImportTextArea.click();
202
209
  await this.bulkImportTextArea.fill(mapped);
203
210
  await this.dialogImportButton.click();
@@ -23,6 +23,7 @@ declare class ModelerHomePage {
23
23
  readonly createIdpApplicationButton: Locator;
24
24
  readonly dialog: Locator;
25
25
  readonly closeButton: Locator;
26
+ readonly cancelButton: Locator;
26
27
  readonly optionsButton: Locator;
27
28
  readonly formNameBreadcrumb: (formName: string) => Locator;
28
29
  constructor(page: Page);
@@ -28,6 +28,7 @@ class ModelerHomePage {
28
28
  createIdpApplicationButton;
29
29
  dialog;
30
30
  closeButton;
31
+ cancelButton;
31
32
  optionsButton;
32
33
  formNameBreadcrumb;
33
34
  constructor(page) {
@@ -45,6 +46,7 @@ class ModelerHomePage {
45
46
  name: 'Create',
46
47
  exact: true,
47
48
  });
49
+ this.cancelButton = page.getByRole('button', { name: 'Cancel' });
48
50
  this.chooseBpmnTemplateButton = page.getByRole('button', {
49
51
  name: 'Choose BPMN template',
50
52
  });
@@ -194,10 +196,12 @@ class ModelerHomePage {
194
196
  }
195
197
  async clickMessageBanner() {
196
198
  try {
197
- await Promise.race([
198
- this.messageBanner.click({ timeout: 30000 }),
199
- this.closeButton.click({ timeout: 30000 }),
200
- ]);
199
+ const button = this.messageBanner
200
+ .or(this.closeButton)
201
+ .or(this.cancelButton)
202
+ .first();
203
+ await (0, test_1.expect)(button).toBeVisible({ timeout: 15000 });
204
+ await button.click();
201
205
  }
202
206
  catch {
203
207
  console.log('No banner or close button found to click');
@@ -6,6 +6,6 @@ declare class OptimizeDashboardPage {
6
6
  clickFilterTable(): Promise<void>;
7
7
  fillFilterTable(processName: string): Promise<void>;
8
8
  processLinkAssertion(processName: string, maxRetries: number, retryDelay?: number): Promise<void>;
9
- processOwnerNameAssertion(processName: string): Promise<void>;
9
+ processOwnerNameAssertion(processName: string, userEmail: string): Promise<void>;
10
10
  }
11
11
  export { OptimizeDashboardPage };
@@ -36,24 +36,27 @@ class OptimizeDashboardPage {
36
36
  }
37
37
  }
38
38
  }
39
- async processOwnerNameAssertion(processName) {
39
+ async processOwnerNameAssertion(processName, userEmail) {
40
40
  const maxRetries = 15;
41
41
  const retryDelay = 60000;
42
+ const localPart = userEmail.split('@')[0];
43
+ const uuid = localPart.replace('qa-user-', '');
44
+ const result = !uuid ? 'QA Camunda' : `QA User ${uuid}`;
42
45
  for (let attempt = 0; attempt < maxRetries; attempt++) {
43
46
  try {
44
- await this.page.reload();
45
47
  await (0, test_1.expect)(this.page.getByRole('row', {
46
- name: `${processName}\nProcess\n\tQA Camunda`,
48
+ name: `${processName}\nProcess\n\t${result}`,
47
49
  })).toBeVisible({ timeout: 90000 });
48
50
  return;
49
51
  }
50
52
  catch (error) {
51
53
  if (attempt < maxRetries - 1) {
52
- console.warn(`Attempt ${attempt + 1} failed for asserting owner name in Optimize.. Retrying...`);
54
+ console.warn(`Attempt ${attempt + 1} failed for asserting owner name ${result} assertion for ${processName} in Optimize.. Retrying...`);
55
+ await this.page.reload();
53
56
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
54
57
  }
55
58
  else {
56
- throw new Error(`Owner name assertion failed after ${maxRetries} attempts`);
59
+ throw new Error(`Owner name ${result} assertion for ${processName} failed after ${maxRetries} attempts`);
57
60
  }
58
61
  }
59
62
  }
@@ -34,6 +34,7 @@ export declare function runMultipleProcesses(clusterName: string, page: Page, mo
34
34
  }): Promise<void>;
35
35
  export declare function disableRBA(clusterName: string, homePage: HomePage, clusterPage: ClusterPage, clusterDetailsPage: ClusterDetailsPage, appsPage: AppsPage): Promise<void>;
36
36
  export declare function enableAuthorizations(clusterName: string, homePage: HomePage, clusterPage: ClusterPage, clusterDetailsPage: ClusterDetailsPage, appsPage: AppsPage): Promise<void>;
37
+ export declare function assertLatestAlertEmail(id: string, mailSlurp: MailSlurp, processName: string, alertText: string): Promise<string>;
37
38
  export declare function clickInvitationLinkInEmail(page: Page, id: string, mailSlurp: MailSlurp): Promise<void>;
38
39
  export declare function assertTestUsesCorrectOrganization(page: Page): Promise<void>;
39
40
  export declare function deleteAllUserGroups(homePage: HomePage, organizationPage: ConsoleOrganizationPage, appsPage: AppsPage): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.waitForLoadingToFinish = exports.assertTestUsesCorrectOrganizationFromModeler = exports.expectCountToBeOneOf = exports.createAPIClient = exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.clickInvitationLinkInEmail = exports.enableAuthorizations = exports.disableRBA = exports.runMultipleProcesses = exports.modelDiagramFromFile = exports.runProcess = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.loginWithRetry = void 0;
3
+ exports.waitForLoadingToFinish = exports.assertTestUsesCorrectOrganizationFromModeler = exports.expectCountToBeOneOf = exports.createAPIClient = exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.clickInvitationLinkInEmail = exports.assertLatestAlertEmail = exports.enableAuthorizations = exports.disableRBA = exports.runMultipleProcesses = exports.modelDiagramFromFile = exports.runProcess = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.loginWithRetry = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const ModelerHomePage_1 = require("./ModelerHomePage");
6
6
  const HomePage_1 = require("./HomePage");
@@ -267,6 +267,27 @@ async function enableAuthorizations(clusterName, homePage, clusterPage, clusterD
267
267
  await clusterDetailsPage.assertComponentsHealth();
268
268
  }
269
269
  exports.enableAuthorizations = enableAuthorizations;
270
+ async function assertLatestAlertEmail(id, mailSlurp, processName, alertText) {
271
+ const maxRetries = 3;
272
+ for (let retries = 0; retries < maxRetries; retries++) {
273
+ try {
274
+ const email = await mailSlurp.waitForLatestEmail(id, 120000);
275
+ if (email && email.body) {
276
+ (0, test_1.expect)(email.body).toContain(processName);
277
+ (0, test_1.expect)(email.body).toContain(alertText);
278
+ return email.body;
279
+ }
280
+ else {
281
+ throw new Error('Email or email body is null or undefined.');
282
+ }
283
+ }
284
+ catch (error) {
285
+ console.warn(`Click attempt ${retries + 1} failed: ${error}`);
286
+ }
287
+ }
288
+ throw new Error(`Failed to get latest alert (${processName}) email for ${id} after ${maxRetries} attempts.`);
289
+ }
290
+ exports.assertLatestAlertEmail = assertLatestAlertEmail;
270
291
  async function clickInvitationLinkInEmail(page, id, mailSlurp) {
271
292
  const email = await mailSlurp.waitForLatestEmail(id, 120000);
272
293
  if (email && email.body) {
@@ -141,6 +141,7 @@ _8_7_1.test.describe('Web Modeler User Flow Tests', () => {
141
141
  await (0, test_1.expect)(appsPage.tasklistLink).not.toBeVisible();
142
142
  await (0, test_1.expect)(appsPage.modelerLink).toBeVisible();
143
143
  await appsPage.clickModeler();
144
+ await homePage.clickSkipCustomization();
144
145
  await modelerHomePage.createCrossComponentProjectFolder();
145
146
  await modelerHomePage.clickDiagramTypeDropdown();
146
147
  await modelerHomePage.clickBpmnTemplateOption();
@@ -162,8 +163,10 @@ _8_7_1.test.describe('Web Modeler User Flow Tests', () => {
162
163
  });
163
164
  await settingsPage.clickOpenSettingsButton();
164
165
  await settingsPage.clickLogoutButton();
165
- await loginPage.login(testUser);
166
+ await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, testUser, 1000);
166
167
  await (0, sleep_1.sleep)(30000);
168
+ await appsPage.clickCamundaApps();
169
+ await appsPage.clickModeler();
167
170
  await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
168
171
  timeout: 90000,
169
172
  });
@@ -621,9 +624,12 @@ _8_7_1.test.describe('Web Modeler User Flow Tests', () => {
621
624
  await modelerHomePage.clickDiagramTypeDropdown();
622
625
  await modelerHomePage.clickFormOption();
623
626
  await modelerHomePage.enterFormName(formName);
627
+ await (0, sleep_1.sleep)(2000);
624
628
  await formJsPage.dragAndDrop(formJsPage.textField, formJsPage.formEditor);
625
629
  await formJsPage.clickGeneralPropertiesPanel();
626
630
  await formJsPage.fillKeyInput('Public_Form_Text_Field');
631
+ await (0, test_1.expect)(formJsPage.textFieldInForm).toBeVisible();
632
+ await (0, sleep_1.sleep)(5000);
627
633
  });
628
634
  await _8_7_1.test.step('Add A BPMN Template To The Project', async () => {
629
635
  await modelerHomePage.clickProjectBreadcrumb();
@@ -110,6 +110,7 @@ _8_8_1.test.describe('User Roles User Flow', () => {
110
110
  await (0, UtilitiesPage_1.waitForLoadingToFinish)(page);
111
111
  await appsPage.clickCamundaApps();
112
112
  await appsPage.clickModeler();
113
+ await homePage.clickSkipCustomization();
113
114
  await modelerHomePage.createCrossComponentProjectFolder();
114
115
  await modelerHomePage.clickDiagramTypeDropdown();
115
116
  await modelerHomePage.clickBpmnTemplateOption();
@@ -200,6 +201,7 @@ _8_8_1.test.describe('User Roles User Flow', () => {
200
201
  await _8_8_1.test.step('Navigate to Modeler and Ensure Diagram Failed To Deploy', async () => {
201
202
  await appsPage.clickCamundaApps();
202
203
  await appsPage.clickModeler();
204
+ await homePage.clickSkipCustomization();
203
205
  await modelerHomePage.createCrossComponentProjectFolder();
204
206
  await modelerHomePage.clickDiagramTypeDropdown();
205
207
  await modelerHomePage.clickBpmnTemplateOption();
@@ -295,6 +297,7 @@ _8_8_1.test.describe('User Roles User Flow', () => {
295
297
  await _8_8_1.test.step('Navigate to Modeler and Ensure Diagram Can Be Deployed', async () => {
296
298
  await appsPage.clickCamundaApps();
297
299
  await appsPage.clickModeler();
300
+ await homePage.clickSkipCustomization();
298
301
  await modelerHomePage.createCrossComponentProjectFolder();
299
302
  await modelerHomePage.clickDiagramTypeDropdown();
300
303
  await modelerHomePage.clickBpmnTemplateOption();
@@ -7,8 +7,8 @@ const OperateProcessInstancePage_1 = require("../../pages/8.8/OperateProcessInst
7
7
  const formatDate_1 = require("../../utils/formatDate");
8
8
  const UtilitiesPage_1 = require("../../pages/8.8/UtilitiesPage");
9
9
  const sleep_1 = require("../../utils/sleep");
10
- const googleapi_1 = require("../../utils/googleapi");
11
10
  const users_1 = require("../../utils/users");
11
+ const mailSlurpClient_1 = require("../../utils/mailSlurpClient");
12
12
  const testUser = (0, users_1.getTestUser)('eighteenthUser');
13
13
  _8_8_1.test.describe.configure({ mode: 'parallel' });
14
14
  _8_8_1.test.describe('Console User Flow Tests @tasklistV2', () => {
@@ -69,9 +69,9 @@ _8_8_1.test.describe('Console User Flow Tests @tasklistV2', () => {
69
69
  _8_8_1.test.slow();
70
70
  const processName = 'Email_Alert_Process' + (await (0, _setup_1.generateRandomStringAsync)(3));
71
71
  const invalidURl = 'https://invalid';
72
- const senderEmail = 'no-reply@camunda.io';
73
72
  let lastTriggerTime = 0;
74
73
  const alertFlowClusterName = 'Alert Flow Cluster';
74
+ const { id } = await (0, mailSlurpClient_1.createInbox)(testUser.username);
75
75
  await _8_8_1.test.step('Create Cluster', async () => {
76
76
  await homePage.clickClusters();
77
77
  await clusterPage.createClusterIfNotExists(alertFlowClusterName);
@@ -142,11 +142,7 @@ _8_8_1.test.describe('Console User Flow Tests @tasklistV2', () => {
142
142
  });
143
143
  await _8_8_1.test.step('Verify Alerts Received via Email for Incident', async () => {
144
144
  await (0, sleep_1.sleep)(60000);
145
- const token = await (0, googleapi_1.getGoogleAccessToken)();
146
- const messages = await (0, googleapi_1.getGmailMessagesWithRetry)(token, senderEmail, processName);
147
- (0, test_1.expect)(messages.length).toBe(1);
148
- const emailDetails = await (0, googleapi_1.getLatestEmailDetails)(token, await messages[0]['id']);
149
- (0, test_1.expect)(emailDetails).toContain('New Incidents: 1');
145
+ await (0, UtilitiesPage_1.assertLatestAlertEmail)(id, mailSlurpClient_1.mailSlurp, processName, 'New Incidents: 1');
150
146
  lastTriggerTime = Math.floor(Date.now() / 1000); // Current time in seconds (Unix timestamp)
151
147
  console.log('Captured Target Time:', lastTriggerTime);
152
148
  });
@@ -184,10 +180,7 @@ _8_8_1.test.describe('Console User Flow Tests @tasklistV2', () => {
184
180
  });
185
181
  await _8_8_1.test.step('Verify Alerts Received via Email for Incident', async () => {
186
182
  await (0, sleep_1.sleep)(60000);
187
- const token = await (0, googleapi_1.getGoogleAccessToken)();
188
- const messages = await (0, googleapi_1.getGmailMessagesWithRetry)(token, senderEmail, processName, lastTriggerTime);
189
- const emailDetails = await (0, googleapi_1.getLatestEmailDetails)(token, await messages[0]['id']);
190
- (0, test_1.expect)(emailDetails).toContain('New Incidents: 2'); //# of alerts triggered since the last one
183
+ await (0, UtilitiesPage_1.assertLatestAlertEmail)(id, mailSlurpClient_1.mailSlurp, processName, 'New Incidents: 2');
191
184
  });
192
185
  });
193
186
  });
@@ -242,7 +242,7 @@ _8_8_1.test.describe('Optimize User Flow Tests', () => {
242
242
  await optimizeTabOptimizeHomePage.clickDashboardLink();
243
243
  await (0, sleep_1.sleep)(90000);
244
244
  await operateTabOptimizeDashboardPage.processLinkAssertion(processName, 6);
245
- await operateTabOptimizeDashboardPage.processOwnerNameAssertion(processName);
245
+ await operateTabOptimizeDashboardPage.processOwnerNameAssertion(processName, testUser.username);
246
246
  });
247
247
  });
248
248
  });
@@ -20,6 +20,7 @@ async function deleteOrganization(page, uuid) {
20
20
  client_id: process.env.EXTERNAL_CONSOLE_API_CLIENT_ID,
21
21
  client_secret: process.env.EXTERNAL_CONSOLE_API_CLIENT_SECRET,
22
22
  },
23
+ timeout: 60000,
23
24
  });
24
25
  if (tokenResponse.status() !== 200) {
25
26
  const errorBody = await tokenResponse.text();
@@ -1,4 +1,4 @@
1
1
  import { MailSlurp } from 'mailslurp-client';
2
2
  export declare const mailSlurp: MailSlurp;
3
- export declare function createInbox(expriesIn?: number): Promise<import("mailslurp-client").InboxDto>;
3
+ export declare function createInbox(emailAddress?: string, expriesIn?: number): Promise<import("mailslurp-client").InboxDto>;
4
4
  export declare function deleteInbox(id: string): Promise<void>;
@@ -5,10 +5,15 @@ const mailslurp_client_1 = require("mailslurp-client");
5
5
  exports.mailSlurp = new mailslurp_client_1.MailSlurp({
6
6
  apiKey: process.env.MAIL_SLURP_API_KEY,
7
7
  });
8
- async function createInbox(expriesIn = 1200000) {
9
- const inbox = await exports.mailSlurp.createInbox(undefined, undefined, undefined, undefined, undefined, undefined, undefined, expriesIn);
10
- console.log(`Created inbox ${inbox.id}`);
11
- return inbox;
8
+ async function createInbox(emailAddress, expriesIn = 1200000) {
9
+ try {
10
+ const inbox = await exports.mailSlurp.createInbox(emailAddress, undefined, undefined, undefined, undefined, undefined, undefined, expriesIn);
11
+ console.log(`Created inbox ${inbox.id}`);
12
+ return inbox;
13
+ }
14
+ catch (e) {
15
+ throw new Error('Failed to create inbox: ' + e);
16
+ }
12
17
  }
13
18
  exports.createInbox = createInbox;
14
19
  async function deleteInbox(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.154",
3
+ "version": "0.0.156",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",