@camunda/e2e-test-suite 0.0.944 → 0.0.945

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.
Files changed (38) hide show
  1. package/dist/pages/8.10/AppsPage.d.ts +5 -0
  2. package/dist/pages/8.10/AppsPage.js +152 -0
  3. package/dist/pages/8.10/ModelerHomePage.d.ts +1 -0
  4. package/dist/pages/8.10/ModelerHomePage.js +52 -5
  5. package/dist/pages/8.10/OperateHomePage.js +23 -2
  6. package/dist/pages/8.10/OperateProcessInstancePage.d.ts +17 -3
  7. package/dist/pages/8.10/OperateProcessInstancePage.js +718 -16
  8. package/dist/pages/8.10/OperateProcessesPage.d.ts +1 -0
  9. package/dist/pages/8.10/OperateProcessesPage.js +38 -2
  10. package/dist/pages/8.10/OptimizeHomePage.d.ts +5 -0
  11. package/dist/pages/8.10/OptimizeHomePage.js +138 -0
  12. package/dist/pages/8.10/OptimizeReportPage.d.ts +1 -0
  13. package/dist/pages/8.10/OptimizeReportPage.js +25 -0
  14. package/dist/pages/8.10/TaskPanelPage.d.ts +2 -1
  15. package/dist/pages/8.10/TaskPanelPage.js +37 -9
  16. package/dist/pages/8.10/UtilitiesPage.d.ts +2 -1
  17. package/dist/pages/8.10/UtilitiesPage.js +155 -23
  18. package/dist/pages/SM-8.10/KeycloakAdminPage.js +1 -1
  19. package/dist/pages/SM-8.10/KeycloakLoginPage.d.ts +1 -0
  20. package/dist/pages/SM-8.10/KeycloakLoginPage.js +8 -1
  21. package/dist/pages/SM-8.10/LoginPage.d.ts +1 -1
  22. package/dist/pages/SM-8.10/LoginPage.js +11 -2
  23. package/dist/pages/SM-8.10/NavigationPage.d.ts +1 -0
  24. package/dist/pages/SM-8.10/NavigationPage.js +86 -8
  25. package/dist/pages/SM-8.10/OCIdentityHomePage.js +12 -2
  26. package/dist/pages/SM-8.10/OCIdentityRolesPage.d.ts +3 -0
  27. package/dist/pages/SM-8.10/OCIdentityRolesPage.js +110 -4
  28. package/dist/pages/SM-8.10/OperateHomePage.js +13 -1
  29. package/dist/pages/SM-8.10/OperateProcessesPage.d.ts +1 -0
  30. package/dist/pages/SM-8.10/OperateProcessesPage.js +37 -18
  31. package/dist/pages/SM-8.10/OptimizeReportPage.js +21 -1
  32. package/dist/pages/SM-8.10/TaskDetailsPage.js +7 -0
  33. package/dist/pages/SM-8.10/UtilitiesPage.js +15 -4
  34. package/dist/pages/SM-8.10/optimizeReportUtils.js +30 -4
  35. package/dist/tests/8.10/smoke-tests.spec.js +182 -44
  36. package/dist/utils/constants.d.ts +1 -0
  37. package/dist/utils/constants.js +4 -0
  38. package/package.json +1 -1
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assertReportWithRefreshes = exports.createReportForProcess = void 0;
4
4
  const UtilitiesPage_1 = require("../SM-8.10/UtilitiesPage");
5
5
  const UtilitiesPage_2 = require("../SM-8.10/UtilitiesPage");
6
+ const constants_1 = require("../../utils/constants");
6
7
  const createReportForProcess = async (optimizeCollectionsPage, optimizeReportPage, processName) => {
7
8
  const maxRetries = 30;
8
9
  for (let attempt = 0; attempt < maxRetries; attempt++) {
@@ -34,18 +35,43 @@ const assertReportWithRefreshes = async (page, optimizeHomePage, optimizeCollect
34
35
  // refresh cycles for Optimize to catch up has a bigger underlying
35
36
  // problem worth surfacing.
36
37
  const maxRetries = 10;
37
- for (let attempt = 0; attempt < maxRetries; attempt++) {
38
+ // Playwright treats a 0 timeout as "no timeout", so passing 0 here made the
39
+ // visibility wait poll one already-rendered report view until the 720s test
40
+ // timeout killed the test. Optimize report views do not live-refresh, so that
41
+ // single wait could never observe the batch import landing, and it consumed
42
+ // every cycle this loop exists to spread out — the reason the failure surfaces
43
+ // as a bare "Test timeout of 720000ms exceeded". Bound each cycle instead, so
44
+ // the reload between cycles actually refetches. 60s (rather than the 90s the
45
+ // identical assertion gets in optimize-user-flows.spec.ts) keeps several
46
+ // cycles inside the smoke test's shared budget.
47
+ const attemptTimeout = constants_1._1_SECOND_IN_MS * 60;
48
+ // Stop starting cycles in time to throw the error below, instead of being
49
+ // killed mid-cycle by the test timeout, which reports no cause at all.
50
+ const deadline = Date.now() + constants_1._1_MINUTE_IN_MS * 6;
51
+ let attempts = 0;
52
+ let lastError;
53
+ while (attempts < maxRetries && Date.now() < deadline) {
54
+ attempts++;
38
55
  try {
39
56
  await optimizeHomePage.clickCollectionsLink();
40
57
  await (0, UtilitiesPage_1.assertPageTextWithRetry)(page, reportName, false);
41
58
  await optimizeCollectionsPage.clickMostRecentProcessReport(reportName);
42
- await (0, UtilitiesPage_2.assertLocatorVisibleWithRetry)(page, locator, text, 0);
59
+ await (0, UtilitiesPage_2.assertLocatorVisibleWithRetry)(page, locator, text, attemptTimeout);
43
60
  return;
44
61
  }
45
62
  catch (error) {
46
- // nothing to do. Just loop back to the collections
63
+ lastError = error;
64
+ // Once the test budget is gone (or the page is closed) every remaining
65
+ // attempt rejects instantly, so this loop used to spin out and report
66
+ // "after 10 attempts" for a failure that happened somewhere else
67
+ // entirely. Surface the real error instead of retrying into it.
68
+ if (page.isClosed() || String(error).includes('Test timeout')) {
69
+ throw error;
70
+ }
71
+ // otherwise nothing to do. Just loop back to the collections
47
72
  }
48
73
  }
49
- throw new Error(`Failed to assert report ${reportName} after ${maxRetries} attempts`);
74
+ throw new Error(`Failed to assert report ${reportName} after ${attempts} attempts: ` +
75
+ String(lastError));
50
76
  };
51
77
  exports.assertReportWithRefreshes = assertReportWithRefreshes;
@@ -35,21 +35,35 @@ _8_10_1.test.describe('Smoke Tests', () => {
35
35
  const userTaskName = 'zeebeUserTaskWithForm' + randomString;
36
36
  const reportName = await (0, _setup_1.generateRandomStringAsync)(5);
37
37
  let appTab;
38
+ // One wall-clock deadline for the whole flow. The per-hop budgets below
39
+ // (2 x 8 min Operate import, 10 min Tasklist, 10 x 2 min Optimize import)
40
+ // sum to well over this test's own 36 min timeout, so a run where several
41
+ // hops were merely slow could be killed by the harness mid-wait and
42
+ // reported as a bare "Test timeout of 2160000ms exceeded" — no failing
43
+ // assertion, no named hop, and no screenshot. Clamping each hop to the time
44
+ // actually left makes the slow hop fail with its own diagnostic instead.
45
+ // The reserve covers the login already spent in `beforeEach` plus the
46
+ // afterEach screenshot/video capture, which is what the on-call reads.
47
+ // In a healthy run nothing is clamped: each wait resolves long before the
48
+ // deadline, so `flowBudget` returns the full budget it was asked for.
49
+ const flowDeadline = Date.now() + _8_10_1.test.info().timeout - 120000;
50
+ const flowBudget = (wanted) => Math.max(30000, Math.min(wanted, flowDeadline - Date.now()));
38
51
  await _8_10_1.test.step('Navigate to Web Modeler', async () => {
39
52
  await (0, test_1.expect)(homePage.camundaComponentsButton).toBeVisible({
40
53
  timeout: constants_1.TIMEOUT.medium,
41
54
  });
42
- await appsPage.clickCamundaApps();
43
- await appsPage.clickModeler();
44
- await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
45
- timeout: constants_1.TIMEOUT.navigation,
46
- });
55
+ await appsPage.openModelerWithRetry(modelerHomePage.modelerPageBanner, constants_1.TIMEOUT.navigation);
47
56
  });
48
57
  await _8_10_1.test.step('Create form to be linked for Zeebe user task', async () => {
49
- await modelerHomePage.clickCrossComponentProjectFolder();
58
+ // Ensure the per-user project exists before using it: the shared
59
+ // test-setup project creates it, but if that per-user setup step is
60
+ // absent/flaky the folder is missing and clicking it hangs forever.
61
+ // createCrossComponentProjectFolder clicks into the folder when present
62
+ // (the normal path) and creates it only when it is genuinely missing.
63
+ await modelerHomePage.createCrossComponentProjectFolder();
50
64
  await modelerHomePage.clickDiagramTypeDropdown();
51
65
  await modelerHomePage.clickFormOption();
52
- await modelerHomePage.enterFormName(formName);
66
+ await modelerHomePage.enterFormNameWithRetry(formName);
53
67
  await modelerHomePage.assertFormBreadcrumbVisible(formName);
54
68
  await formJsPage.deployForm(CLUSTER_NAME);
55
69
  // Wait for the form to propagate to the cluster before it can be embedded
@@ -91,52 +105,152 @@ _8_10_1.test.describe('Smoke Tests', () => {
91
105
  });
92
106
  });
93
107
  await _8_10_1.test.step('Open process instance in Operate and verify it is active', async () => {
94
- await modelerCreatePage.clickViewProcessInstanceLink();
95
- appTab = await page.waitForEvent('popup', { timeout: constants_1.TIMEOUT.navigation });
108
+ // Start latching the Operate deep link on the CONTEXT before clicking.
109
+ // Playwright raises `popup` only once the popup's initial navigation
110
+ // response has started loading, so a listener attached to the popup page
111
+ // afterwards can never see the deep-link request -- and the auth redirect
112
+ // chain of a session-less tab drops that path, so nothing else observes it
113
+ // either. Missing it leaves the retry loop below polling the Operate app
114
+ // root for the whole budget (see OperateProcessInstancePage).
115
+ const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
116
+ // Open the Operate tab through the retrying helper: it registers the
117
+ // popup wait before each click (`waitForEvent` only sees events raised
118
+ // after it is called, and the tab opens while the click is still
119
+ // resolving) and re-clicks when a click resolved without opening a tab,
120
+ // which is how this step used to sit out its whole 60s budget.
121
+ appTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink, constants_1.TIMEOUT.navigation);
96
122
  const appTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(appTab);
97
- // Reload between attempts so a lagging Operate import is picked up
98
- // a single fixed wait never recovers if the instance page renders
99
- // before the instance is imported on a fresh SaaS cluster.
100
- await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(appTabProcessInstancePage, appTabProcessInstancePage.activeIcon, 'active icon in Operate', constants_1.TIMEOUT.navigation);
101
- await appTabProcessInstancePage.closePopOverIfVisible();
123
+ // Capture the deep link first: when the instance is not imported yet
124
+ // Operate 404s and replaces the URL with the processes list, so the
125
+ // retry loop has to re-navigate to this URL instead of reloading the
126
+ // list page (see OperateProcessInstancePage).
127
+ await appTabProcessInstancePage.rememberInstanceUrl(constants_1.TIMEOUT.navigation, watchedInstanceUrl);
128
+ // Poll the instance deep link rather than the page Operate bounced us
129
+ // to: while the Zeebe -> Operate import lags, the deep link 404s and
130
+ // Operate replaces the URL with the processes list, where the instance
131
+ // header can never render. The previous loop spent most of its ~11 min
132
+ // idling on that list page; this re-asks Operate instead.
133
+ //
134
+ // `processImport`, the same budget the COMPLETED-icon gate below uses:
135
+ // both wait on the identical Zeebe -> Operate import, so the shorter
136
+ // `processActive` (300s) made this the first place a lagging import ran
137
+ // out of road ("active icon in Operate timed out after 300000ms" with the
138
+ // instance header never rendering). This does not lengthen the test in
139
+ // practice -- the import lag is paid once, so time spent here is time the
140
+ // COMPLETED-icon gate no longer needs.
141
+ await appTabProcessInstancePage.waitForInstanceLocator(appTabProcessInstancePage.activeIcon, 'active icon', flowBudget(constants_1.TIMEOUT.processImport));
142
+ // The active-icon wait above already proves the instance page rendered,
143
+ // so only the "what's new" popup is left to dismiss. Re-polling for the
144
+ // "Instance History" panel here would spend a second 8 min budget on a
145
+ // weaker version of the same gate.
146
+ await appTabProcessInstancePage.dismissWhatsNewIfVisible();
102
147
  });
103
148
  await _8_10_1.test.step('Navigate to Tasklist and complete both user tasks', async () => {
104
149
  const appTabAppsPage = new AppsPage_1.AppsPage(appTab);
105
150
  const appTabTaskPanelPage = new TaskPanelPage_1.TaskPanelPage(appTab);
106
151
  const appTabTaskDetailsPage = new TaskDetailsPage_1.TaskDetailsPage(appTab);
107
- await appTabAppsPage.clickCamundaApps();
108
- await appTabAppsPage.clickTasklist(CLUSTER_NAME);
109
- await (0, UtilitiesPage_1.completeTaskWithRetry)(appTabTaskPanelPage, appTabTaskDetailsPage, `${userTaskName}1`, 'Medium');
110
- await (0, UtilitiesPage_1.completeTaskWithRetry)(appTabTaskPanelPage, appTabTaskDetailsPage, `${userTaskName}2`, 'Medium');
152
+ // Gate the hop on Tasklist's own banner, for the same reason the Operate
153
+ // hop below is gated: `clickTasklist` returns as soon as its click
154
+ // resolves, so a hand-off that never landed left this tab on Operate —
155
+ // and the task poll below then spent its whole budget reloading Operate
156
+ // looking for a row that can only exist in Tasklist (see
157
+ // `openTasklistWithRetry`).
158
+ await appTabAppsPage.openTasklistWithRetry(appTabTaskPanelPage.taskListPageBanner, CLUSTER_NAME, constants_1.TIMEOUT.navigation);
159
+ // One budget for the whole Tasklist hop. The two tasks are parallel
160
+ // branches of the same instance, so they are imported together: the
161
+ // first wait absorbs the importer lag and the rest are already present.
162
+ // Per-call budgets are what killed this test -- 10 x 120s inside
163
+ // completeTaskWithRetry's 3 attempts is a 60 min worst case for ONE
164
+ // task, more than the 36 min test timeout, so a lagging importer ran the
165
+ // clock out mid-wait and the run reported "Test timeout exceeded" with no
166
+ // failing assertion instead of naming the task that never arrived.
167
+ const tasklistDeadline = Date.now() + flowBudget(10 * 60 * 1000);
168
+ const tasklistBudget = () => Math.max(30000, tasklistDeadline - Date.now());
169
+ await (0, UtilitiesPage_1.completeTaskWithRetry)(appTabTaskPanelPage, appTabTaskDetailsPage, `${userTaskName}1`, 'Medium', 3, tasklistBudget());
170
+ await (0, UtilitiesPage_1.completeTaskWithRetry)(appTabTaskPanelPage, appTabTaskDetailsPage, `${userTaskName}2`, 'Medium', 3, tasklistBudget());
171
+ // Re-select the row between attempts instead of asserting once: the click
172
+ // in `openTask` can land while the Completed list is still re-rendering
173
+ // after the filter change, which leaves the details panel on the
174
+ // previously selected task (or on its empty state) with nothing left to
175
+ // move it. A single 30s wait then reported
176
+ // "getByTestId('details-info').getByText('<task>') ... element(s) not
177
+ // found". Re-clicking the row is what recovers it, so the assertion
178
+ // itself is unchanged — the details panel must still name the task.
179
+ const assertTaskDetails = async (taskName) => {
180
+ await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(appTab, appTabTaskDetailsPage.detailsInfo.getByText(taskName), `${taskName} in the task details panel`, constants_1.TIMEOUT.medium, false, 3, appTabTaskPanelPage.taskInList(taskName));
181
+ };
111
182
  await appTabTaskPanelPage.filterBy('Completed');
112
- await appTabTaskPanelPage.openTask(`${userTaskName}1`);
113
- await (0, test_1.expect)(appTabTaskDetailsPage.detailsInfo.getByText(`${userTaskName}1`)).toBeVisible({ timeout: constants_1.TIMEOUT.medium });
114
- await appTabTaskPanelPage.openTask(`${userTaskName}2`);
115
- await (0, test_1.expect)(appTabTaskDetailsPage.detailsInfo.getByText(`${userTaskName}2`)).toBeVisible({ timeout: constants_1.TIMEOUT.medium });
183
+ await appTabTaskPanelPage.openTask(`${userTaskName}1`, tasklistBudget());
184
+ await assertTaskDetails(`${userTaskName}1`);
185
+ await appTabTaskPanelPage.openTask(`${userTaskName}2`, tasklistBudget());
186
+ await assertTaskDetails(`${userTaskName}2`);
116
187
  });
117
188
  await _8_10_1.test.step('Assert process completion in Operate', async () => {
118
189
  const appTabAppsPage = new AppsPage_1.AppsPage(appTab);
119
190
  const appTabHomePage = new OperateHomePage_1.OperateHomePage(appTab);
120
191
  const appTabProcessesPage = new OperateProcessesPage_1.OperateProcessesPage(appTab);
121
192
  const appTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(appTab);
122
- await appTabAppsPage.clickCamundaApps();
123
- await appTabAppsPage.clickOperate(CLUSTER_NAME);
193
+ // Gate the hop on Operate's own banner: `clickOperate` returns as soon as
194
+ // its click resolves, so a hand-off that never landed left the tab on
195
+ // Tasklist — whose nav also has a "Processes" link, so the next step
196
+ // clicked THAT and then waited out its budget for Operate's Processes
197
+ // view (see `openOperateWithRetry`).
198
+ await appTabAppsPage.openOperateWithRetry(appTabHomePage.operateBanner, CLUSTER_NAME, constants_1.TIMEOUT.navigation);
124
199
  await appTabHomePage.clickProcessesTab();
125
200
  await (0, test_1.expect)(appTabProcessesPage.processCompletedCheckbox).toBeVisible({
126
201
  timeout: constants_1.TIMEOUT.short,
127
202
  });
128
203
  await appTabProcessesPage.clickProcessCompletedCheckbox();
129
204
  await appTabProcessesPage.clickProcessInstanceLink(processName);
130
- await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(appTabProcessInstancePage, appTabProcessInstancePage.completedIcon, 'completed icon in Operate', constants_1.TIMEOUT.processComplete);
205
+ // Poll the instance deep link for the COMPLETED icon, the same way the
206
+ // active-icon gate above does. `assertLocatorVisibleWithRetry` is blind
207
+ // to Operate's 404 bounce: the completed filter also lists still-ACTIVE
208
+ // instances, so this link is often opened before the import has caught
209
+ // up, and each of its 6 attempts then idled a full 120s on a page that
210
+ // could not render the header -- 13 min for only 6 re-asks. This budget
211
+ // re-asks Operate ~16 times and leaves more of the test's 36 min to the
212
+ // Optimize import later on.
213
+ await appTabProcessInstancePage.rememberInstanceUrl(constants_1.TIMEOUT.navigation);
214
+ await appTabProcessInstancePage.waitForInstanceLocator(appTabProcessInstancePage.completedIcon, 'completed icon', flowBudget(constants_1.TIMEOUT.processImport));
131
215
  });
132
216
  await _8_10_1.test.step('Assert process imported in Optimize and create user task report', async () => {
133
217
  const appTabAppsPage = new AppsPage_1.AppsPage(appTab);
134
218
  const optimizeHomePage = new OptimizeHomePage_1.OptimizeHomePage(appTab);
135
219
  const optimizeCollectionsPage = new OptimizeCollectionsPage_1.OptimizeCollectionsPage(appTab);
136
220
  const optimizeReportPage = new OptimizeReportPage_1.OptimizeReportPage(appTab);
137
- await appTabAppsPage.clickCamundaApps();
138
- await appTabAppsPage.clickOptimize(CLUSTER_NAME);
139
- await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(appTab, appTab.getByRole('link', { name: processName }), `process ${processName} in Optimize`, constants_1.TIMEOUT.optimizeImport);
221
+ // Gate the hop on Optimize's own banner, for the same reason the Operate
222
+ // and Tasklist hops above are gated: `clickOptimize` returns as soon as
223
+ // its click resolves, so a hand-off that never landed left this tab off
224
+ // Optimize — and the import poll below then spent its whole 20 min
225
+ // budget reloading a page that can never render a process definition
226
+ // (see `openOptimizeWithRetry`).
227
+ await appTabAppsPage.openOptimizeWithRetry(optimizeHomePage.optimizeBanner, CLUSTER_NAME, constants_1.TIMEOUT.navigation);
228
+ // Reloads for ~10 rounds of the 2 min import window, dismissing the
229
+ // onboarding modal that reappears on every load and covers the list.
230
+ // Two extra rounds over the previous 8: the Optimize import is the
231
+ // slowest hop in this flow and the time comes out of the Operate wait
232
+ // above, so the test's overall worst case still shrinks.
233
+ // Rounds are derived from the time the flow has left rather than fixed at
234
+ // 10, for the same reason the waits above are clamped: 10 x 2 min is 20
235
+ // min of the test's 36, so on a run that already spent its budget on the
236
+ // Operate imports these rounds were what pushed it past the harness
237
+ // timeout. At least 2 rounds are always attempted, and a healthy run
238
+ // still gets all 10.
239
+ // Deriving only the ROUND COUNT from `flowBudget` left the wait itself
240
+ // unclamped: the helper's budget is `timeout * maxRetries`, computed from
241
+ // the per-round timeout it is handed, so the `max(2, ...)` floor kept at
242
+ // least 2 x 120s of polling even on a flow with seconds left -- and the
243
+ // report-creation steps below then ran past the test's own 36 min
244
+ // timeout. That is the bare "Test timeout of 2160000ms exceeded" with no
245
+ // failing assertion and no named hop. Splitting the budget across the
246
+ // rounds makes `timeout * maxRetries` equal the time the flow actually
247
+ // has, so a lagging Optimize import fails with its own diagnostic
248
+ // ("process <name> in Optimize failed after ...ms of polling") instead of
249
+ // being killed by the harness. A healthy run is unchanged: the full
250
+ // 10 x 120s is still granted whenever the flow has it to give.
251
+ const optimizeBudget = flowBudget(10 * constants_1.TIMEOUT.optimizeImport);
252
+ const optimizeRounds = Math.max(2, Math.floor(optimizeBudget / constants_1.TIMEOUT.optimizeImport));
253
+ await optimizeHomePage.assertProcessImportedWithRetry(processName, Math.floor(optimizeBudget / optimizeRounds), optimizeRounds);
140
254
  await optimizeHomePage.clickCollectionsLink();
141
255
  await optimizeCollectionsPage.clickCreateNewButtonWithRetry();
142
256
  await optimizeCollectionsPage.clickReportOption();
@@ -165,10 +279,17 @@ _8_10_1.test.describe('Smoke Tests', () => {
165
279
  timeout: constants_1.TIMEOUT.medium,
166
280
  });
167
281
  await optimizeCollectionsPage.clickMostRecentProcessReport(reportName);
168
- await optimizeReportPage.waitUntilLocatorIsVisible(optimizeReportPage.oneUserTaskInstance.first(), appTab);
169
- await (0, test_1.expect)(optimizeReportPage.oneUserTaskInstance).toHaveCount(2, {
170
- timeout: constants_1.TIMEOUT.navigation,
171
- });
282
+ // Poll the row count itself across reloads: the user-task instance data
283
+ // arrives on a later Optimize import cycle than the definition, so
284
+ // probing visibility first and then asserting the count once reported
285
+ // "expected 2, received 0" while the import was still in flight.
286
+ // Clamped for the same reason as the import wait above: this is the last
287
+ // hop of the flow and its default 4 x 60s is spent after the slowest
288
+ // wait in the test, so leaving it unclamped re-opens the same overrun
289
+ // the budget above closes. The retry count is unchanged -- only the time
290
+ // each attempt may idle is bounded by what the flow has left.
291
+ const countRetries = 4;
292
+ await optimizeReportPage.assertUserTaskInstanceCountWithRetry(2, appTab, countRetries, Math.floor(flowBudget(countRetries * constants_1.TIMEOUT.navigation) / countRetries));
172
293
  });
173
294
  });
174
295
  (0, _8_10_1.test)('Most Common REST Connector User Flow', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, connectorSettingsPage, }) => {
@@ -179,14 +300,12 @@ _8_10_1.test.describe('Smoke Tests', () => {
179
300
  await (0, test_1.expect)(homePage.camundaComponentsButton).toBeVisible({
180
301
  timeout: constants_1.TIMEOUT.medium,
181
302
  });
182
- await appsPage.clickCamundaApps();
183
- await appsPage.clickModeler();
184
- await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
185
- timeout: constants_1.TIMEOUT.navigation,
186
- });
303
+ await appsPage.openModelerWithRetry(modelerHomePage.modelerPageBanner, constants_1.TIMEOUT.navigation);
187
304
  });
188
305
  await _8_10_1.test.step('Open Cross Component Test Project and create a BPMN diagram template', async () => {
189
- await modelerHomePage.clickCrossComponentProjectFolder();
306
+ // See note in the flow above: ensure the per-user project exists rather
307
+ // than hanging on a click when the shared setup did not create it.
308
+ await modelerHomePage.createCrossComponentProjectFolder();
190
309
  await modelerHomePage.clickDiagramTypeDropdown();
191
310
  await modelerHomePage.clickBpmnTemplateOption();
192
311
  });
@@ -200,13 +319,32 @@ _8_10_1.test.describe('Smoke Tests', () => {
200
319
  });
201
320
  });
202
321
  await _8_10_1.test.step('Assert process completes in Operate and verify connector result', async () => {
203
- await modelerCreatePage.clickViewProcessInstanceLink();
204
- const operateTab = await page.waitForEvent('popup', {
205
- timeout: constants_1.TIMEOUT.navigation,
206
- });
322
+ // Context-level deep-link watcher before the click — same reason as in
323
+ // the flow above: the popup event fires too late to observe the
324
+ // deep-link request, so without this the retry loop below can only poll
325
+ // the Operate app root (see OperateProcessInstancePage).
326
+ const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
327
+ // Same retrying open as in the flow above: the listener goes up before
328
+ // the click, and a click that resolved without opening a tab is retried
329
+ // instead of being waited out.
330
+ const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink, constants_1.TIMEOUT.navigation);
207
331
  const operateProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
208
- await operateProcessInstancePage.closePopOverIfVisible();
209
- await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateProcessInstancePage, operateProcessInstancePage.completedIcon, 'completed icon in Operate', constants_1.TIMEOUT.processComplete);
332
+ // Capture the deep link before Operate can bounce the tab to the
333
+ // processes list on a not-yet-imported instance (see
334
+ // OperateProcessInstancePage) — otherwise every retry below reloads
335
+ // the list page, where the instance header can never render.
336
+ await operateProcessInstancePage.rememberInstanceUrl(constants_1.TIMEOUT.navigation, watchedInstanceUrl);
337
+ // Wait for the COMPLETED icon as the single import gate, polling the deep
338
+ // link so a bounce to the processes list is re-navigated instead of
339
+ // waited out: this tab is opened seconds after the instance starts, so
340
+ // the first query almost always 404s. `assertLocatorVisibleWithRetry`
341
+ // does not know about the bounce and burned 120s per attempt on the list
342
+ // page, which is what reported "completed icon in Operate failed after 6
343
+ // attempts" whenever the import lagged. The popup is dismissed
344
+ // afterwards, once the last re-navigation has settled, so it cannot cover
345
+ // the variables assertions.
346
+ await operateProcessInstancePage.waitForInstanceLocator(operateProcessInstancePage.completedIcon, 'completed icon', constants_1.TIMEOUT.processImport);
347
+ await operateProcessInstancePage.dismissWhatsNewIfVisible();
210
348
  await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateProcessInstancePage.variablesList, 'variable list in Operate', constants_1.TIMEOUT.navigation);
211
349
  await (0, test_1.expect)(operateProcessInstancePage.connectorResultVariableName('message')).toBeVisible({ timeout: constants_1.TIMEOUT.navigation });
212
350
  await (0, test_1.expect)(operateProcessInstancePage
@@ -20,5 +20,6 @@ export declare const TIMEOUT: {
20
20
  navigation: number;
21
21
  processActive: number;
22
22
  processComplete: number;
23
+ processImport: number;
23
24
  optimizeImport: number;
24
25
  };
@@ -28,5 +28,9 @@ exports.TIMEOUT = {
28
28
  navigation: 60000,
29
29
  processActive: 300000,
30
30
  processComplete: 120000,
31
+ // Total budget for polling an Operate instance deep link until the
32
+ // Zeebe -> Operate import has caught up (see `waitForInstanceLocator`).
33
+ // Spent as ~30s polls that each re-ask Operate, not as one long idle wait.
34
+ processImport: 480000,
31
35
  optimizeImport: 120000,
32
36
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.944",
3
+ "version": "0.0.945",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",