@camunda/e2e-test-suite 0.0.632 → 0.0.634

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.
@@ -63,21 +63,37 @@ class ClusterSecretsPage {
63
63
  return; //No Connector Secrets found in the list
64
64
  }
65
65
  try {
66
- let options = await this.optionsButton.all();
67
- while (options.length > 0) {
68
- const optionButton = options[0];
69
- if (await optionButton.isVisible()) {
66
+ for (let sweep = 0; sweep < 20; sweep++) {
67
+ const options = await this.optionsButton.all();
68
+ if (options.length === 0) {
69
+ break;
70
+ }
71
+ let deletedAny = false;
72
+ for (const optionButton of options) {
73
+ if (!(await optionButton.isVisible().catch(() => false))) {
74
+ continue;
75
+ }
70
76
  await optionButton.click({ timeout: 60000 });
77
+ await (0, test_1.expect)(this.deleteConnectorSecretButton).toBeVisible({
78
+ timeout: 15000,
79
+ });
80
+ if (await this.deleteConnectorSecretButton.isDisabled()) {
81
+ await this.page.keyboard.press('Escape').catch(() => { });
82
+ continue;
83
+ }
71
84
  await this.deleteConnectorSecretButton.click({ timeout: 60000 });
72
85
  await (0, test_1.expect)(this.dialog).toBeVisible({ timeout: 40000 });
73
86
  await this.deleteConnectorSecretSubButton.click();
74
87
  await (0, test_1.expect)(this.page.getByText('Deleting...')).not.toBeVisible({
75
88
  timeout: 60000,
76
89
  });
90
+ deletedAny = true;
91
+ await (0, sleep_1.sleep)(3000);
92
+ break;
93
+ }
94
+ if (!deletedAny) {
95
+ break;
77
96
  }
78
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
79
- await sleep(3000);
80
- options = await this.optionsButton.all();
81
97
  }
82
98
  await (0, test_1.expect)(this.optionsButton).not.toBeVisible({
83
99
  timeout: 30000,
@@ -194,7 +194,16 @@ class ConsoleOrganizationPage {
194
194
  }
195
195
  }
196
196
  async clickUsersTab() {
197
- await (0, test_1.expect)(this.usersTab).toBeVisible({ timeout: 60000 });
197
+ // The Users tab can fail to render if the Org page is mid-load when we
198
+ // arrive. Try once with a shorter window, reload on failure, then give
199
+ // the second attempt the full timeout before surfacing the error.
200
+ try {
201
+ await (0, test_1.expect)(this.usersTab).toBeVisible({ timeout: 30000 });
202
+ }
203
+ catch {
204
+ await this.page.reload();
205
+ await (0, test_1.expect)(this.usersTab).toBeVisible({ timeout: 60000 });
206
+ }
198
207
  await this.usersTab.click({ timeout: 60000 });
199
208
  await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.filterTableSearchbox, {
200
209
  postAction: async () => {
@@ -74,7 +74,13 @@ class HomePage {
74
74
  if (!(await next.isVisible({ timeout: visibleTimeout }))) {
75
75
  return;
76
76
  }
77
- await next.click({ timeout: 30000, force: true });
77
+ try {
78
+ await next.click({ timeout: 30000, force: true });
79
+ }
80
+ catch {
81
+ // Close buttons are occasionally replaced during modal animation;
82
+ // continue and retry with the latest visible control.
83
+ }
78
84
  }
79
85
  }
80
86
  organizationUuid() {
@@ -89,15 +95,13 @@ class HomePage {
89
95
  await this.openOrganizationButton.click({ timeout: 60000 });
90
96
  }
91
97
  async clickSkipCustomization() {
92
- try {
93
- await (0, test_1.expect)(this.buttonSkipCustomization).toBeVisible({ timeout: 30000 });
94
- await this.buttonSkipCustomization.click({ timeout: 60000 });
95
- await this.closeInformationDialog();
96
- return;
97
- }
98
- catch (error) {
99
- console.error(error);
98
+ const hasCustomizationModal = await this.buttonSkipCustomization
99
+ .isVisible({ timeout: 30000 })
100
+ .catch(() => false);
101
+ if (hasCustomizationModal) {
102
+ await this.buttonSkipCustomization.click({ timeout: 60000, force: true });
100
103
  }
104
+ await this.closeInformationDialog();
101
105
  }
102
106
  }
103
107
  exports.HomePage = HomePage;
@@ -19,8 +19,11 @@ class LoginPage {
19
19
  // resolves to the auth0 email input, regardless of redirect timing.
20
20
  this.usernameInput = page
21
21
  .getByLabel('Email address')
22
- .and(page.locator(':not(#c4-invite-email)'));
23
- this.passwordInput = page.getByLabel('Password *');
22
+ .and(page.locator(':not([id="c4-invite-email"])'));
23
+ this.passwordInput = page
24
+ .getByLabel(/^Password\s*\*?$/i)
25
+ .or(page.locator('input[type="password"]'))
26
+ .first();
24
27
  this.continueButton = page.getByRole('button', {
25
28
  name: 'Continue',
26
29
  exact: true,
@@ -62,6 +65,9 @@ class LoginPage {
62
65
  const { username = process.env.C8_USERNAME_TEST, password = process.env.C8_PASSWORD_TEST, } = credentials;
63
66
  // Navigate to app root to start the auth flow.
64
67
  await this.page.goto('/');
68
+ await this.page
69
+ .waitForURL((url) => !url.hostname.includes('console'), { timeout: 60000 })
70
+ .catch(() => { });
65
71
  // SSO re-auth may silently log us back in as the previous user after
66
72
  // logout/session-reset, keeping the page on Console instead of redirecting
67
73
  // to the Auth0 login form. Detect Console (settings button visible) and
@@ -81,9 +87,16 @@ class LoginPage {
81
87
  await this.page.waitForLoadState('domcontentloaded', { timeout: 30000 });
82
88
  }
83
89
  }
84
- await (0, test_1.expect)(this.usernameInput).toBeVisible({ timeout: 120000 });
85
- await this.fillUsername(username);
86
- await this.clickContinueButton();
90
+ const onPasswordStep = (await this.passwordHeading
91
+ .isVisible({ timeout: 5000 })
92
+ .catch(() => false)) ||
93
+ (await this.passwordInput.isVisible({ timeout: 5000 }).catch(() => false));
94
+ if (!onPasswordStep) {
95
+ await (0, test_1.expect)(this.usernameInput).toBeVisible({ timeout: 120000 });
96
+ await this.fillUsername(username);
97
+ await this.clickContinueButton();
98
+ }
99
+ await (0, test_1.expect)(this.passwordInput).toBeVisible({ timeout: 180000 });
87
100
  await this.fillPassword(password);
88
101
  await (0, test_1.expect)(this.loginButton).toBeVisible({ timeout: 120000 });
89
102
  await this.clickLoginButton();
@@ -1,8 +1,12 @@
1
1
  import { Page, Locator } from '@playwright/test';
2
2
  declare class PlayPage {
3
3
  private page;
4
+ readonly diagram: Locator;
5
+ readonly playTab: Locator;
4
6
  readonly completeJobButton: Locator;
5
- readonly startInstanceButton: Locator;
7
+ readonly configureTestPanel: Locator;
8
+ readonly configureTestPanelStartButton: Locator;
9
+ readonly startInstanceOverlayButton: Locator;
6
10
  constructor(page: Page);
7
11
  waitForCompleteJobButtonToBeAvailable(): Promise<void>;
8
12
  clickCompleteJobButton(): Promise<void>;
@@ -2,19 +2,35 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PlayPage = void 0;
4
4
  const test_1 = require("@playwright/test");
5
+ const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
5
6
  const maxWaitTimeSeconds = 180000;
6
7
  class PlayPage {
7
8
  page;
9
+ diagram;
10
+ playTab;
8
11
  completeJobButton;
9
- startInstanceButton;
12
+ configureTestPanel;
13
+ configureTestPanelStartButton;
14
+ startInstanceOverlayButton;
10
15
  constructor(page) {
11
16
  this.page = page;
12
- this.completeJobButton = page
13
- .getByTestId('diagram')
14
- .getByLabel('Complete job');
15
- this.startInstanceButton = this.page
16
- .getByTestId('diagram')
17
- .getByLabel('Start instance', { exact: true });
17
+ this.diagram = page.getByTestId('diagram');
18
+ this.playTab = page.getByRole('tab', { name: 'Play' });
19
+ this.completeJobButton = this.diagram.getByLabel('Complete job');
20
+ // Newer Modeler Play UI: the "Configure Scenario" floating card hosts a
21
+ // primary "Start" Carbon button (variants: "Start", "Start with
22
+ // variables", "Start with a form"). Source:
23
+ // camunda-hub frontend/packages/modeler/play/src/Definition/
24
+ // ConfigureTestPanel/index.tsx — renders <FloatingCard
25
+ // data-testid="configure-test-panel"> with a <Button> whose visible
26
+ // text starts with "Start".
27
+ this.configureTestPanel = page.getByTestId('configure-test-panel');
28
+ this.configureTestPanelStartButton = this.configureTestPanel.getByRole('button', { name: /^Start( with .*)?$/, exact: false });
29
+ // Legacy bpmn-js canvas overlay button (still emitted by older Modeler
30
+ // builds, also relabeled with cached/example-data suffixes). Kept as a
31
+ // fallback for environments that haven't shipped the ConfigureTestPanel
32
+ // yet.
33
+ this.startInstanceOverlayButton = this.diagram.getByLabel('Start instance', { exact: false });
18
34
  }
19
35
  async waitForCompleteJobButtonToBeAvailable() {
20
36
  await (0, test_1.expect)(this.completeJobButton).toBeVisible({
@@ -28,17 +44,62 @@ class PlayPage {
28
44
  await this.completeJobButton.click();
29
45
  }
30
46
  async clickStartInstanceButton() {
31
- await (0, test_1.expect)(this.startInstanceButton).toBeVisible({
32
- timeout: 30000,
33
- });
34
- await this.startInstanceButton.click();
47
+ // The Modeler Play UI has two surfaces that can hold the start trigger:
48
+ // - new: a "Start" Carbon button inside the ConfigureTestPanel
49
+ // floating card (`[data-testid="configure-test-panel"]`). This is
50
+ // what newer Modeler builds render. Variants: "Start",
51
+ // "Start with variables", "Start with a form".
52
+ // - legacy: a bpmn-js canvas overlay labelled "Start instance"
53
+ // (sometimes "Start instance with cached data" once a scenario
54
+ // chip exists). Older Modeler builds emit this; newer builds do
55
+ // not, so it cannot be the only locator.
56
+ //
57
+ // Try whichever appears first. On total failure, reload AND re-open
58
+ // the Play tab (the legacy code path called `page.reload()` alone,
59
+ // which dumped the user back on the Implement tab on stable/8.7).
60
+ const startTrigger = this.configureTestPanelStartButton
61
+ .or(this.startInstanceOverlayButton)
62
+ .first();
63
+ const tryClick = async () => {
64
+ await (0, test_1.expect)(this.diagram).toBeVisible({ timeout: 120000 });
65
+ await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, startTrigger, {
66
+ totalTimeout: 240000,
67
+ visibilityTimeout: 60000,
68
+ maxRetries: 8,
69
+ retryDelayMs: 5000,
70
+ });
71
+ };
72
+ try {
73
+ await tryClick();
74
+ }
75
+ catch (error) {
76
+ console.warn(`clickStartInstanceButton: first pass failed (${error}); reloading, re-opening Play tab, and retrying once`);
77
+ await this.page.reload();
78
+ // After reload, the Modeler defaults to the Implement tab. Move
79
+ // back to Play before trying again — otherwise the diagram
80
+ // visibility check times out against the Implement-tab DOM.
81
+ if (await this.playTab.isVisible({ timeout: 30000 }).catch(() => false)) {
82
+ await this.playTab.click();
83
+ }
84
+ await tryClick();
85
+ }
35
86
  }
36
87
  async dismissStartModal() {
37
- await this.page
38
- .getByRole('button', {
39
- name: 'Start a process instance',
40
- })
41
- .click();
88
+ // The intro dialog cycles labels across Modeler versions/states.
89
+ // Try the known variations in order; first match wins.
90
+ const buttonVariations = [
91
+ 'Start a process instance',
92
+ 'Start another instance',
93
+ 'Start new instance',
94
+ 'Start instance',
95
+ ];
96
+ for (const buttonName of buttonVariations) {
97
+ const button = this.page.getByRole('button', { name: buttonName });
98
+ if ((await button.count()) > 0) {
99
+ await button.first().click();
100
+ return;
101
+ }
102
+ }
42
103
  }
43
104
  async waitForInstanceDetailsToBeLoaded() {
44
105
  const maxRetries = 2;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SignUpPage = void 0;
4
+ const test_1 = require("@playwright/test");
4
5
  class SignUpPage {
5
6
  page;
6
7
  firstNameInput;
@@ -90,10 +91,33 @@ class SignUpPage {
90
91
  await this.clickPasswordInput();
91
92
  await this.fillPasswordInput(password);
92
93
  await this.clickSignupButton();
93
- // Detect completion: URL leaves /signup OR the submit button disappears.
94
- // waitForFunction handles any Auth0 post-signup page variant.
95
- await this.page.waitForFunction(() => window.location.pathname !== '/signup' ||
96
- !document.querySelector('button.signUpButton'), undefined, { timeout: 180000 });
94
+ // Sign-up can complete through multiple UI states across Auth0 variants.
95
+ await test_1.expect
96
+ .poll(async () => {
97
+ const pathname = new URL(this.page.url()).pathname;
98
+ if (pathname !== '/signup') {
99
+ return true;
100
+ }
101
+ const canSeeVerificationText = await this.verifyEmailText
102
+ .isVisible({ timeout: 1000 })
103
+ .catch(() => false);
104
+ if (canSeeVerificationText) {
105
+ return true;
106
+ }
107
+ const canSeeLoginButton = await this.loginToCamundaButton
108
+ .isVisible({ timeout: 1000 })
109
+ .catch(() => false);
110
+ if (canSeeLoginButton) {
111
+ return true;
112
+ }
113
+ const signUpButtonHidden = await this.signupButton
114
+ .isHidden({ timeout: 1000 })
115
+ .catch(() => false);
116
+ return signUpButtonHidden;
117
+ }, {
118
+ timeout: 240000,
119
+ })
120
+ .toBe(true);
97
121
  }
98
122
  async clickFirstNameInput() {
99
123
  await this.firstNameInput.click({ timeout: 30000 });
@@ -229,9 +229,20 @@ class TaskDetailsPage {
229
229
  const lastPdfViewer = containers
230
230
  .last()
231
231
  .locator('.fjs-documentPreview-pdf-viewer');
232
- await (0, test_1.expect)(firstPdfViewer.or(lastPdfViewer)).toBeVisible({
233
- timeout: 60000,
234
- });
232
+ // The PDF viewer can take longer to mount on AWS clusters; if the first
233
+ // wait misses, reload once and give the second wait more headroom before
234
+ // failing the test.
235
+ try {
236
+ await (0, test_1.expect)(firstPdfViewer.or(lastPdfViewer)).toBeVisible({
237
+ timeout: 60000,
238
+ });
239
+ }
240
+ catch {
241
+ await this.page.reload();
242
+ await (0, test_1.expect)(firstPdfViewer.or(lastPdfViewer)).toBeVisible({
243
+ timeout: 90000,
244
+ });
245
+ }
235
246
  }
236
247
  async clickSubmitButton() {
237
248
  await (0, test_1.expect)(this.submitButton).toBeVisible();
@@ -23,12 +23,13 @@ class TaskPanelPage {
23
23
  async openTask(name) {
24
24
  let attempts = 0;
25
25
  const maxAttempts = 3;
26
+ const taskLocator = this.availableTasks
27
+ .getByText(name, { exact: true })
28
+ .first();
26
29
  while (attempts < maxAttempts) {
27
30
  try {
28
- await this.availableTasks
29
- .getByText(name, { exact: true })
30
- .nth(0)
31
- .click({ timeout: 120000 });
31
+ await (0, test_1.expect)(taskLocator).toBeVisible({ timeout: 120000 });
32
+ await taskLocator.click({ timeout: 180000 });
32
33
  return;
33
34
  }
34
35
  catch (error) {
@@ -5,13 +5,37 @@ const test_1 = require("@playwright/test");
5
5
  const ModelerHomePage_1 = require("./ModelerHomePage");
6
6
  const HomePage_1 = require("./HomePage");
7
7
  const sleep_1 = require("../../utils/sleep");
8
- const randomSleep_1 = require("../../utils/randomSleep");
9
8
  const fileUpload_1 = require("../../utils/fileUpload");
10
9
  const mailSlurpClient_1 = require("../../utils/mailSlurpClient");
11
- async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 3, skipOrgAssertion = false) {
10
+ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 5, skipOrgAssertion = false) {
11
+ // Pre-flight worker stagger: spread the initial Auth0 hits across parallel
12
+ // workers BEFORE the first goto, otherwise all workers slam the tenant at
13
+ // the same instant and several get throttled into the retry loop.
14
+ await (0, sleep_1.sleep)(timeout);
12
15
  for (let attempt = 0; attempt < maxRetries; attempt++) {
13
16
  try {
14
- await page.goto('/');
17
+ await page.context().clearCookies();
18
+ await page.goto('about:blank');
19
+ await page
20
+ .evaluate(() => {
21
+ try {
22
+ localStorage.clear();
23
+ }
24
+ catch (_) {
25
+ // Storage can be unavailable in some contexts.
26
+ }
27
+ try {
28
+ sessionStorage.clear();
29
+ }
30
+ catch (_) {
31
+ // Storage can be unavailable in some contexts.
32
+ }
33
+ })
34
+ .catch(() => { });
35
+ await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60000 });
36
+ await page
37
+ .waitForLoadState('networkidle', { timeout: 30000 })
38
+ .catch(() => { });
15
39
  await (0, sleep_1.sleep)(timeout);
16
40
  // Always detect SSO re-auth: after a logout + clearCookies, Auth0 may
17
41
  // silently log the previous user back in without showing the login form.
@@ -23,7 +47,10 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 3
23
47
  .waitFor({ state: 'visible', timeout: 15000 })
24
48
  .then(() => true)
25
49
  .catch(() => false);
26
- if (onAuth0)
50
+ const onPasswordStep = await loginPage.passwordHeading
51
+ .isVisible({ timeout: 3000 })
52
+ .catch(() => false);
53
+ if (onAuth0 || onPasswordStep)
27
54
  break;
28
55
  if (await settingsBtn.isVisible({ timeout: 3000 })) {
29
56
  await settingsBtn.click({ timeout: 30000 });
@@ -31,6 +58,7 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 3
31
58
  await page.waitForLoadState('domcontentloaded', { timeout: 30000 });
32
59
  }
33
60
  }
61
+ await (0, test_1.expect)(loginPage.loginMessage.or(loginPage.passwordHeading)).toBeVisible({ timeout: 60000 });
34
62
  if (skipOrgAssertion) {
35
63
  await loginPage.loginWithoutOrgAssertion(testUser);
36
64
  }
@@ -41,8 +69,13 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 3
41
69
  }
42
70
  catch (error) {
43
71
  if (attempt < maxRetries - 1) {
44
- console.warn(`Attempt ${attempt + 1} failed for logging in. Retrying...`);
45
- await (0, randomSleep_1.randomSleep)(10000, 20000);
72
+ // Exponential backoff with jitter: 10s, 20s, 40s, 80s (capped at 80s).
73
+ // Auth0 throttling under heavy parallel load needs longer recovery
74
+ // than a flat 10-20s — give the tenant time to drain.
75
+ const base = Math.min(10000 * 2 ** attempt, 80000);
76
+ const backoff = base + Math.floor(Math.random() * 10000);
77
+ console.warn(`Attempt ${attempt + 1} failed for logging in. Retrying in ${backoff}ms...`);
78
+ await (0, sleep_1.sleep)(backoff);
46
79
  }
47
80
  else {
48
81
  console.error(error);
@@ -90,10 +90,22 @@ class IdentityHomePage {
90
90
  }
91
91
  }
92
92
  async selectOptionFromDropdown(option) {
93
+ // The Identity "Assign permissions to application" dialog renders the
94
+ // API list as a Carbon Dropdown rather than a native <select>. Using
95
+ // Locator.selectOption() on the underlying `select` updates the form
96
+ // value but does NOT trigger the Carbon component's React state, so
97
+ // the dropdown stays open and the write/read permission checkboxes
98
+ // are never rendered (observed in nightly run 26612986585 — the
99
+ // dropdown popup was still expanded when the test timed out waiting
100
+ // for the `write:*` label).
101
+ //
102
+ // Open the dropdown and click the option from the listbox so the
103
+ // Carbon handler fires, the popup closes, and the form re-renders
104
+ // with the permission rows.
93
105
  await this.select.click();
94
- await this.select.selectOption({
95
- label: option,
96
- });
106
+ await this.page
107
+ .getByRole('option', { name: option, exact: true })
108
+ .click({ timeout: 10000 });
97
109
  await (0, sleep_1.sleep)(1000);
98
110
  }
99
111
  }
@@ -434,7 +434,7 @@ _8_7_1.test.describe('HTO User Flow Tests', () => {
434
434
  await (0, UtilitiesPage_1.completeTaskWithRetry)(taskPanelPage, taskDetailsPage, `${taskName}1`, 'low');
435
435
  await taskPanelPage.filterBy('Completed');
436
436
  await (0, test_1.expect)(page.getByText(processName).first()).toBeVisible({
437
- timeout: 60000,
437
+ timeout: 120000,
438
438
  });
439
439
  await (0, test_1.expect)(page.getByText(processName)).toHaveCount(4);
440
440
  await taskPanelPage.openTask(`${taskName}4`);
@@ -432,9 +432,20 @@ _8_7_1.test.describe('Web Modeler User Flow Tests', () => {
432
432
  });
433
433
  await _8_7_1.test.step('Log in to C8 as New User', async () => {
434
434
  await (0, UtilitiesPage_1.clickInvitationLinkInEmail)(page, id);
435
- await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
436
- timeout: 180000,
437
- });
435
+ // The OAuth redirect chain after the invitation link can stall and
436
+ // never resolve to the Modeler banner within a single wait window.
437
+ // Reload the page once and try again before giving up.
438
+ try {
439
+ await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
440
+ timeout: 90000,
441
+ });
442
+ }
443
+ catch {
444
+ await page.reload();
445
+ await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
446
+ timeout: 180000,
447
+ });
448
+ }
438
449
  await modelerHomePage.clickMessageBanner();
439
450
  await settingsPage.clickOpenSettingsButton();
440
451
  await settingsPage.clickLogoutButton();
@@ -17,20 +17,19 @@ async function createInbox(emailAddress, expriesIn = 1200000) {
17
17
  }
18
18
  exports.createInbox = createInbox;
19
19
  async function deleteInbox(id) {
20
+ if (!id) {
21
+ return;
22
+ }
20
23
  try {
21
24
  console.log(`Deleting inbox ${id}`);
22
- await exports.mailSlurp.deleteInbox(id);
25
+ await Promise.race([
26
+ exports.mailSlurp.deleteInbox(id),
27
+ new Promise((_, reject) => setTimeout(() => reject(new Error('deleteInbox timeout exceeded')), 20000)),
28
+ ]);
23
29
  }
24
30
  catch (error) {
25
- if (error instanceof Error &&
26
- (error.message.includes('404') ||
27
- error.message.includes('does not exist'))) {
28
- console.log('Failed to create inbox, could be a timing issue on MailSlurp side: ' +
29
- String(error));
30
- }
31
- else {
32
- throw new Error('Failed to delete inbox: ' + String(error));
33
- }
31
+ // Cleanup should be best-effort and must not fail the test flow.
32
+ console.warn('Failed to delete inbox: ' + String(error));
34
33
  }
35
34
  }
36
35
  exports.deleteInbox = deleteInbox;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.632",
3
+ "version": "0.0.634",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",