@camunda/e2e-test-suite 0.0.1196 → 0.0.1197
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.
- package/dist/pages/SM-8.10/TaskPanelPage.d.ts +6 -3
- package/dist/pages/SM-8.10/TaskPanelPage.js +106 -32
- package/dist/tests/SM-8.10/hto-user-flows.spec.js +1 -1
- package/dist/tests/SM-8.10/migration-path-user-flows.spec.js +1 -1
- package/dist/tests/SM-8.10/rba-enabled-user-flows.spec.js +2 -2
- package/package.json +1 -1
|
@@ -2,9 +2,11 @@ import { Page, Locator } from '@playwright/test';
|
|
|
2
2
|
declare class TaskPanelPage {
|
|
3
3
|
private page;
|
|
4
4
|
readonly availableTasks: Locator;
|
|
5
|
-
readonly
|
|
5
|
+
readonly filterPanelExpandButton: Locator;
|
|
6
|
+
readonly filterPanelCollapseButton: Locator;
|
|
7
|
+
readonly filterSelectButton: Locator;
|
|
8
|
+
readonly filterControls: Locator;
|
|
6
9
|
readonly tasklistBanner: Locator;
|
|
7
|
-
readonly collapseFilter: Locator;
|
|
8
10
|
readonly processesPageTab: Locator;
|
|
9
11
|
readonly tasksTab: Locator;
|
|
10
12
|
readonly assignToMeButton: Locator;
|
|
@@ -14,8 +16,9 @@ declare class TaskPanelPage {
|
|
|
14
16
|
constructor(page: Page);
|
|
15
17
|
openTask(name: string, assignButton?: boolean): Promise<void>;
|
|
16
18
|
filterBy(option: 'All open' | 'Unassigned' | 'Assigned to me' | 'Completed'): Promise<void>;
|
|
17
|
-
private
|
|
19
|
+
private openFilterControls;
|
|
18
20
|
private static appliedFilterUrl;
|
|
21
|
+
activeFilter(option: string): Locator;
|
|
19
22
|
clickCollapseFilter(): Promise<void>;
|
|
20
23
|
scrollToLastTask(name: string): Promise<void>;
|
|
21
24
|
scrollToFirstTask(name: string): Promise<void>;
|
|
@@ -6,9 +6,11 @@ const constants_1 = require("../../utils/constants");
|
|
|
6
6
|
class TaskPanelPage {
|
|
7
7
|
page;
|
|
8
8
|
availableTasks;
|
|
9
|
-
|
|
9
|
+
filterPanelExpandButton;
|
|
10
|
+
filterPanelCollapseButton;
|
|
11
|
+
filterSelectButton;
|
|
12
|
+
filterControls;
|
|
10
13
|
tasklistBanner;
|
|
11
|
-
collapseFilter;
|
|
12
14
|
processesPageTab;
|
|
13
15
|
tasksTab;
|
|
14
16
|
assignToMeButton;
|
|
@@ -18,9 +20,36 @@ class TaskPanelPage {
|
|
|
18
20
|
constructor(page) {
|
|
19
21
|
this.page = page;
|
|
20
22
|
this.availableTasks = page.getByTitle('Available tasks');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
// SM-8.10 is tested against two different Tasklist shells, so every filter
|
|
24
|
+
// locator below has to match either one.
|
|
25
|
+
//
|
|
26
|
+
// `stable/8.10` -- which is what the published `8.10-SNAPSHOT` images this
|
|
27
|
+
// repo's own CI deploys are built from -- still renders the Carbon
|
|
28
|
+
// `CollapsiblePanel`: a collapsed rail carrying the expand control, an
|
|
29
|
+
// expanded `<nav id="task-nav-bar">` listing each filter as a `<Link>`,
|
|
30
|
+
// and an `<h1>` naming the active filter.
|
|
31
|
+
// camunda/camunda `main` is 8.11.0-SNAPSHOT and has replaced that with the
|
|
32
|
+
// Design System `FilterSelect`: one always-visible trigger (accessible name
|
|
33
|
+
// "Filters") opening a menu of `menuitem`s, no expand/collapse control and
|
|
34
|
+
// no `<h1>`. The monorepo's helm e2e deploys an image built from that
|
|
35
|
+
// commit, so it is the shell seen there.
|
|
36
|
+
// Matching only one shell is what made this pair of surfaces trade
|
|
37
|
+
// failures: targeting the Carbon rail found no expand control on the
|
|
38
|
+
// monorepo run, and targeting the dropdown found no "Filters" button at
|
|
39
|
+
// all here (`<element(s) not found>`).
|
|
40
|
+
this.filterPanelExpandButton = page.locator('button[aria-controls="task-nav-bar"][aria-expanded="false"]');
|
|
41
|
+
this.filterPanelCollapseButton = page.locator('button[aria-controls="task-nav-bar"][aria-expanded="true"]');
|
|
42
|
+
this.filterSelectButton = page.getByRole('button', {
|
|
43
|
+
name: 'Filters',
|
|
44
|
+
exact: true,
|
|
45
|
+
});
|
|
46
|
+
// "the task panel's filter control is on screen", whichever shell renders
|
|
47
|
+
// it: the Carbon rail shows the expand control when collapsed and the
|
|
48
|
+
// collapse control when expanded, never both.
|
|
49
|
+
this.filterControls = this.filterPanelExpandButton
|
|
50
|
+
.or(this.filterPanelCollapseButton)
|
|
51
|
+
.or(this.filterSelectButton)
|
|
52
|
+
.first();
|
|
24
53
|
// Same Design System header rollout NavigationPage.tasklistPageBanner
|
|
25
54
|
// covers: "Tasklist" is a breadcrumb link once the Carbon shell is gone,
|
|
26
55
|
// and "Camunda logo Tasklist" while it is still served.
|
|
@@ -29,7 +58,6 @@ class TaskPanelPage {
|
|
|
29
58
|
.getByRole('link', { name: 'Tasklist', exact: true })
|
|
30
59
|
.or(page.getByRole('link', { name: 'Camunda logo Tasklist' }))
|
|
31
60
|
.first();
|
|
32
|
-
this.collapseFilter = page.locator('button[aria-controls="task-nav-bar"][aria-expanded="true"]');
|
|
33
61
|
this.processesPageTab = page.getByRole('link', { name: 'Processes' });
|
|
34
62
|
this.tasksTab = page.getByRole('link', { name: 'Tasks', exact: true });
|
|
35
63
|
this.assignToMeButton = page.getByRole('button', { name: 'Assign to me' });
|
|
@@ -72,28 +100,37 @@ class TaskPanelPage {
|
|
|
72
100
|
}
|
|
73
101
|
}
|
|
74
102
|
async filterBy(option) {
|
|
103
|
+
// Both shells list the options only once their control is open, so open it
|
|
104
|
+
// first and then pick from whichever of the two the deployed build renders:
|
|
105
|
+
// a `<Link>` in the Carbon panel's expanded nav, or a `role="menuitem"` in
|
|
106
|
+
// the Design System dropdown. The Carbon links are scoped to #task-nav-bar
|
|
107
|
+
// so "Completed" cannot pick up a custom filter or a task row carrying the
|
|
108
|
+
// same words. Both shells spell the default filter "All open tasks" while
|
|
109
|
+
// callers pass "All open".
|
|
110
|
+
//
|
|
75
111
|
// The task list refetches every 5s behind a suspense boundary, so a poll
|
|
76
|
-
// landing on the
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
112
|
+
// landing on the pick can tear the control down before the router
|
|
113
|
+
// navigates: it closes but the filter never changes, and the test fails
|
|
114
|
+
// much later on an assertion that reads the still-unfiltered list. Confirm
|
|
115
|
+
// the route actually carries the filter, and re-pick if it does not.
|
|
116
|
+
const optionName = option === 'All open' ? 'All open tasks' : option;
|
|
117
|
+
const filterOption = this.page
|
|
118
|
+
.locator('#task-nav-bar')
|
|
119
|
+
.getByRole('link', { name: optionName, exact: true })
|
|
120
|
+
.or(this.page.getByRole('menuitem', { name: optionName, exact: true }));
|
|
81
121
|
const maxAttempts = 3;
|
|
82
122
|
let lastError;
|
|
83
123
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
84
124
|
try {
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
// "All open tasks" while callers pass "All open".
|
|
88
|
-
const filterLink = this.page.getByRole('link', { name: option });
|
|
89
|
-
// Only the first attempt waits out a slow first render. Later
|
|
90
|
-
// attempts re-pick from a panel that has already rendered once, so
|
|
125
|
+
// Only the first attempt waits out a slow first render; later
|
|
126
|
+
// attempts re-pick from a page that has already rendered once, so
|
|
91
127
|
// they stay short -- three attempts at the full budget would outlast
|
|
92
|
-
// the 12-minute test timeout and turn a dropped
|
|
128
|
+
// the 12-minute test timeout and turn a dropped pick into a timeout
|
|
93
129
|
// with no retry spent.
|
|
94
130
|
const renderTimeout = attempt === 1 ? 120000 : 15000;
|
|
95
|
-
await
|
|
96
|
-
await
|
|
131
|
+
await this.openFilterControls();
|
|
132
|
+
await (0, test_1.expect)(filterOption).toBeVisible({ timeout: renderTimeout });
|
|
133
|
+
await filterOption.click({ timeout: renderTimeout });
|
|
97
134
|
const applied = await this.page
|
|
98
135
|
.waitForURL(TaskPanelPage.appliedFilterUrl(option), { timeout: 15000 })
|
|
99
136
|
.then(() => true)
|
|
@@ -104,30 +141,47 @@ class TaskPanelPage {
|
|
|
104
141
|
lastError = new Error(`Filter "${option}" was picked but the URL never carried it`);
|
|
105
142
|
}
|
|
106
143
|
catch (error) {
|
|
107
|
-
// Without this catch, a thrown assertion (the
|
|
108
|
-
//
|
|
109
|
-
// on attempt 1, so attempts 2 and 3 -- and their shorter,
|
|
110
|
-
// failing budget -- could never run. maxAttempts was dead code
|
|
111
|
-
// every failure mode except a click that landed but didn't navigate.
|
|
144
|
+
// Without this catch, a thrown assertion (the control never appearing,
|
|
145
|
+
// or the option never rendering) propagated straight out of filterBy()
|
|
146
|
+
// on attempt 1, so attempts 2 and 3 -- and their shorter,
|
|
147
|
+
// faster-failing budget -- could never run. maxAttempts was dead code.
|
|
112
148
|
lastError = error;
|
|
113
149
|
}
|
|
114
150
|
if (attempt < maxAttempts) {
|
|
115
151
|
console.warn(`Attempt ${attempt} did not apply the "${option}" filter. Retrying...`);
|
|
116
152
|
}
|
|
153
|
+
// This attempt did not land, which leaves the Design System menu in
|
|
154
|
+
// whichever state Radix last put it. Clicking that trigger again
|
|
155
|
+
// toggles, so on a still-open menu the next attempt would *close* it and
|
|
156
|
+
// then burn its whole budget waiting for an option that is gone. Escape
|
|
157
|
+
// resets it, and is a no-op on both an already-closed menu and the
|
|
158
|
+
// Carbon panel (whose expanded state openFilterControls() probes for).
|
|
159
|
+
// It sits outside the catch so a dead page surfaces as itself rather
|
|
160
|
+
// than replacing the real failure.
|
|
161
|
+
await this.page.keyboard.press('Escape');
|
|
117
162
|
}
|
|
118
163
|
throw new Error(`Filter "${option}" was not applied after ${maxAttempts} attempts: ${lastError}`);
|
|
119
164
|
}
|
|
120
|
-
// The
|
|
165
|
+
// The Carbon panel renders either the collapsed rail (which carries the
|
|
121
166
|
// expand control) or the expanded nav -- never both -- and picking a filter
|
|
122
|
-
// leaves it expanded, so a retry must not click "expand" a second time.
|
|
123
|
-
|
|
124
|
-
|
|
167
|
+
// leaves it expanded, so a retry must not click "expand" a second time. The
|
|
168
|
+
// Design System dropdown has the opposite need: it closes on select, so it
|
|
169
|
+
// has to be re-opened every attempt. Probing for the Carbon collapse control
|
|
170
|
+
// first tells the two apart without branching on a version flag.
|
|
171
|
+
async openFilterControls() {
|
|
172
|
+
const alreadyExpanded = await this.filterPanelCollapseButton
|
|
125
173
|
.waitFor({ state: 'visible', timeout: 2000 })
|
|
126
174
|
.then(() => true)
|
|
127
175
|
.catch(() => false);
|
|
128
|
-
if (
|
|
129
|
-
|
|
176
|
+
if (alreadyExpanded) {
|
|
177
|
+
return;
|
|
130
178
|
}
|
|
179
|
+
// click() auto-waits for the control, so this budget doubles as the wait
|
|
180
|
+
// for a slow first render of whichever shell is served.
|
|
181
|
+
await this.filterPanelExpandButton
|
|
182
|
+
.or(this.filterSelectButton)
|
|
183
|
+
.first()
|
|
184
|
+
.click({ timeout: 60000 });
|
|
131
185
|
}
|
|
132
186
|
// Tasklist keeps the active filter in the `filter` search param. "All open"
|
|
133
187
|
// is the route's default and is stripped from the URL, so it is asserted as
|
|
@@ -144,8 +198,28 @@ class TaskPanelPage {
|
|
|
144
198
|
? /\/tasklist(?:\/[^?]*)?(?:\?(?!.*\bfilter=).*)?$/
|
|
145
199
|
: new RegExp(`[?&]filter=${param}(?:&|$)`);
|
|
146
200
|
}
|
|
201
|
+
// Where each shell shows which filter is currently applied: the Carbon
|
|
202
|
+
// panel renders an `<h1>` naming it, while the Design System dropdown has no
|
|
203
|
+
// heading and instead carries the label on the trigger itself.
|
|
204
|
+
activeFilter(option) {
|
|
205
|
+
return this.page
|
|
206
|
+
.getByRole('heading', { name: option })
|
|
207
|
+
.or(this.filterSelectButton.getByText(option, { exact: true }))
|
|
208
|
+
.first();
|
|
209
|
+
}
|
|
210
|
+
// Picking a filter leaves the Carbon panel expanded, and it floats over the
|
|
211
|
+
// task list -- callers that go straight to a task row would be clicking
|
|
212
|
+
// through the overlay. The Design System dropdown closes itself on select
|
|
213
|
+
// and has no collapse control at all, so this probes rather than clicking
|
|
214
|
+
// unconditionally: on that shell it is a no-op the callers can keep calling.
|
|
147
215
|
async clickCollapseFilter() {
|
|
148
|
-
await this.
|
|
216
|
+
const expanded = await this.filterPanelCollapseButton
|
|
217
|
+
.waitFor({ state: 'visible', timeout: 2000 })
|
|
218
|
+
.then(() => true)
|
|
219
|
+
.catch(() => false);
|
|
220
|
+
if (expanded) {
|
|
221
|
+
await this.filterPanelCollapseButton.click({ timeout: 45000 });
|
|
222
|
+
}
|
|
149
223
|
}
|
|
150
224
|
async scrollToLastTask(name) {
|
|
151
225
|
await this.page.getByText(name).last().scrollIntoViewIfNeeded();
|
|
@@ -84,7 +84,7 @@ SM_8_10_1.test.describe('HTO User Flow Tests', () => {
|
|
|
84
84
|
await (0, UtilitiesPage_1.completeTaskWithRetry)(page, taskPanelPage, taskDetailsPage, `${taskName}1`, 'low');
|
|
85
85
|
await taskPanelPage.filterBy('Completed');
|
|
86
86
|
await taskPanelPage.clickCollapseFilter();
|
|
87
|
-
await (0, test_1.expect)(
|
|
87
|
+
await (0, test_1.expect)(taskPanelPage.activeFilter('Completed')).toBeVisible({
|
|
88
88
|
timeout: 45000,
|
|
89
89
|
});
|
|
90
90
|
await page.reload();
|
|
@@ -50,7 +50,7 @@ if (process.env.IS_MIGRATION === 'true') {
|
|
|
50
50
|
await navigationPage.goToTasklist();
|
|
51
51
|
await taskPanelPage.filterBy('Completed');
|
|
52
52
|
await taskPanelPage.clickCollapseFilter();
|
|
53
|
-
await (0, test_1.expect)(
|
|
53
|
+
await (0, test_1.expect)(taskPanelPage.activeFilter('Completed')).toBeVisible({
|
|
54
54
|
timeout: 45000,
|
|
55
55
|
});
|
|
56
56
|
await (0, test_1.expect)(page.getByText(processName).first()).toBeVisible({
|
|
@@ -691,14 +691,14 @@ if (node_process_1.default.env.IS_RBA === 'true') {
|
|
|
691
691
|
const secondProcessTaskCount = await taskPanelPage.taskCount(`${processName}2`);
|
|
692
692
|
await taskPanelPage.clickProcessesTab();
|
|
693
693
|
await taskProcessesPage.startProcess(`${processName}1`);
|
|
694
|
-
await (0, test_1.expect)(taskPanelPage.
|
|
694
|
+
await (0, test_1.expect)(taskPanelPage.filterControls).toBeVisible({
|
|
695
695
|
timeout: 120000,
|
|
696
696
|
});
|
|
697
697
|
const updatedFirstProcessTaskCount = await taskPanelPage.taskCount(`${processName}1`);
|
|
698
698
|
(0, test_1.expect)(updatedFirstProcessTaskCount).toBe(firstProcessTaskCount + 1);
|
|
699
699
|
await taskPanelPage.clickProcessesTab();
|
|
700
700
|
await taskProcessesPage.startProcess(`${processName}2`);
|
|
701
|
-
await (0, test_1.expect)(taskPanelPage.
|
|
701
|
+
await (0, test_1.expect)(taskPanelPage.filterControls).toBeVisible({
|
|
702
702
|
timeout: 120000,
|
|
703
703
|
});
|
|
704
704
|
const updatedSecondProcessTaskCount = await taskPanelPage.taskCount(`${processName}2`);
|