@camunda/e2e-test-suite 0.0.1221 → 0.0.1222

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.
@@ -469,9 +469,16 @@ class ModelerHomePage {
469
469
  });
470
470
  }
471
471
  async createCrossComponentProjectFolder() {
472
- await (0, sleep_1.sleep)(15000);
473
472
  await this.dismissOverlays();
474
- if (await this.crossComponentProjectFolder.isVisible()) {
473
+ // The project list can take well over a minute to render after a session
474
+ // recovery (observed spinners up to ~70s), so wait for the tile itself
475
+ // rather than a fixed sleep -- a short sleep misclassifies a slow-loading
476
+ // list as missing and creates a duplicate folder.
477
+ const folderExists = await this.crossComponentProjectFolder
478
+ .waitFor({ state: 'visible', timeout: 90000 })
479
+ .then(() => true)
480
+ .catch(() => false);
481
+ if (folderExists) {
475
482
  console.log('Cross Component Project folder already exists. Clicking into it');
476
483
  await this.clickCrossComponentProjectFolder();
477
484
  return;
@@ -18,6 +18,11 @@ export declare function loginWithRetry(page: Page, loginPage: LoginPage, testUse
18
18
  username: string;
19
19
  password: string;
20
20
  }, timeout: number, maxRetries?: number, skipOrgAssertion?: boolean): Promise<void>;
21
+ export declare function isSignedOut(page: Page): boolean;
22
+ export declare function withSessionRecovery(page: Page, loginPage: LoginPage, appsPage: AppsPage, modelerBanner: Locator, testUser: {
23
+ username: string;
24
+ password: string;
25
+ }, loginStagger: number, action: () => Promise<void>): Promise<void>;
21
26
  export declare function modelRestConnector(modelerCreatePage: ModelerCreatePage, connectorSettingsPage: ConnectorSettingsPage, processName: string, url: string, auth: string, resultExpression: string, resultVariable?: string, basicAuthCredentials?: {
22
27
  username: string;
23
28
  password: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.clickInvitationLinkInEmail = exports.enableRBA = exports.disableRBA = exports.modelDiagramFromFile = exports.runProcess = exports.runMultipleProcesses = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.loginWithRetry = void 0;
3
+ exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.clickInvitationLinkInEmail = exports.enableRBA = exports.disableRBA = exports.modelDiagramFromFile = exports.runProcess = exports.runMultipleProcesses = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.withSessionRecovery = exports.isSignedOut = 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");
@@ -104,6 +104,65 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 5
104
104
  }
105
105
  }
106
106
  exports.loginWithRetry = loginWithRetry;
107
+ // True when the tab is sitting on the SaaS sign-in page: either the Auth0
108
+ // tenant (weblogin.<host>) or the Modeler's own /login route.
109
+ function isSignedOut(page) {
110
+ try {
111
+ const { hostname, pathname } = new URL(page.url());
112
+ return (hostname.startsWith('weblogin.') ||
113
+ (hostname.startsWith('modeler.') && /^\/login\/?$/.test(pathname)));
114
+ }
115
+ catch {
116
+ // Not a parsable URL yet (e.g. a tab still on about:blank).
117
+ return false;
118
+ }
119
+ }
120
+ exports.isSignedOut = isSignedOut;
121
+ // A mid-flow sign-out redirects to the IdP asynchronously, so at the instant
122
+ // `action()` throws the tab can still report the Modeler URL. Give the
123
+ // redirect a bounded window to commit before deciding, so a genuinely dropped
124
+ // session is recovered rather than mistaken for "still signed in".
125
+ async function landedOnLoginPage(page, timeout) {
126
+ if (isSignedOut(page)) {
127
+ return true;
128
+ }
129
+ return page
130
+ .waitForURL((url) => url.hostname.startsWith('weblogin.') ||
131
+ (url.hostname.startsWith('modeler.') &&
132
+ /^\/login\/?$/.test(url.pathname)), { timeout })
133
+ .then(() => true)
134
+ .catch((error) => {
135
+ if (error instanceof test_1.errors.TimeoutError) {
136
+ return false;
137
+ }
138
+ throw error;
139
+ });
140
+ }
141
+ // Runs a Modeler-home action and, if it fails because the session was dropped
142
+ // mid-flow (a SaaS provisioning-burst artifact -- the tab lands on the Auth0
143
+ // login page and the projects list never renders), signs back in, re-enters
144
+ // Modeler and retries once. Any failure that does NOT land on the login page
145
+ // is re-thrown unchanged, so a genuine product or selector break is never
146
+ // masked.
147
+ async function withSessionRecovery(page, loginPage, appsPage, modelerBanner, testUser, loginStagger, action) {
148
+ try {
149
+ await action();
150
+ return;
151
+ }
152
+ catch (error) {
153
+ if (!(await landedOnLoginPage(page, 15000))) {
154
+ throw error;
155
+ }
156
+ console.warn(`Session was dropped mid-flow (tab landed on ${page.url()}); ` +
157
+ 'signing back in and retrying once.');
158
+ }
159
+ await loginWithRetry(page, loginPage, testUser, loginStagger);
160
+ await appsPage.clickCamundaApps();
161
+ await appsPage.clickModeler();
162
+ await (0, test_1.expect)(modelerBanner).toBeVisible({ timeout: 60000 });
163
+ await action();
164
+ }
165
+ exports.withSessionRecovery = withSessionRecovery;
107
166
  async function modelRestConnector(modelerCreatePage, connectorSettingsPage, processName, url, auth, resultExpression, resultVariable = '', basicAuthCredentials = {
108
167
  username: '',
109
168
  password: '',
@@ -37,7 +37,7 @@ _8_7_1.test.describe('Smoke Tests', () => {
37
37
  await (0, _setup_1.captureScreenshot)(page, testInfo);
38
38
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
39
39
  });
40
- (0, _8_7_1.test)('Most Common Flow User Flow With All Apps', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
40
+ (0, _8_7_1.test)('Most Common Flow User Flow With All Apps', async ({ page, loginPage, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }, testInfo) => {
41
41
  _8_7_1.test.slow();
42
42
  const randomString = useFixedNames
43
43
  ? 'UpgradeTest'
@@ -60,7 +60,13 @@ _8_7_1.test.describe('Smoke Tests', () => {
60
60
  });
61
61
  });
62
62
  await _8_7_1.test.step('Create form to be linked for Zeebe user task', async () => {
63
- await modelerHomePage.clickCrossComponentProjectFolder();
63
+ // The shared test-setup project creates this folder, but under the
64
+ // SaaS provisioning burst the session can drop mid-flow (tab lands on
65
+ // login, projects list never renders) or the per-user setup can miss
66
+ // the folder. createCrossComponentProjectFolder clicks into it when
67
+ // present and creates it only when genuinely missing;
68
+ // withSessionRecovery re-signs-in and retries once on a dropped session.
69
+ await (0, UtilitiesPage_1.withSessionRecovery)(page, loginPage, appsPage, modelerHomePage.modelerPageBanner, testUser, (testInfo.workerIndex + 1) * 1000, () => modelerHomePage.createCrossComponentProjectFolder());
64
70
  await modelerHomePage.createForm(formName);
65
71
  await formJsPage.deployForm(CLUSTER_NAME);
66
72
  });
@@ -182,7 +188,7 @@ _8_7_1.test.describe('Smoke Tests', () => {
182
188
  });
183
189
  });
184
190
  });
185
- (0, _8_7_1.test)('Most Common REST Connector User Flow', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, connectorSettingsPage, }) => {
191
+ (0, _8_7_1.test)('Most Common REST Connector User Flow', async ({ page, loginPage, homePage, modelerHomePage, appsPage, modelerCreatePage, connectorSettingsPage, }, testInfo) => {
186
192
  _8_7_1.test.slow();
187
193
  const randomString = useFixedNames
188
194
  ? 'UpgradeTest'
@@ -199,7 +205,10 @@ _8_7_1.test.describe('Smoke Tests', () => {
199
205
  });
200
206
  });
201
207
  await _8_7_1.test.step('Open Cross Component Test Project and create a BPMN diagram template', async () => {
202
- await modelerHomePage.clickCrossComponentProjectFolder();
208
+ // See the note in the flow above: ensure the per-user project exists
209
+ // rather than hanging on a click when the shared setup missed it, and
210
+ // recover a session dropped by the SaaS provisioning burst.
211
+ await (0, UtilitiesPage_1.withSessionRecovery)(page, loginPage, appsPage, modelerHomePage.modelerPageBanner, testUser, (testInfo.workerIndex + 1) * 1000, () => modelerHomePage.createCrossComponentProjectFolder());
203
212
  await modelerHomePage.clickDiagramTypeDropdown();
204
213
  await modelerHomePage.clickBpmnTemplateOption();
205
214
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1221",
3
+ "version": "0.0.1222",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",