@camunda/e2e-test-suite 0.0.1169 → 0.0.1170
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.
|
@@ -12,7 +12,10 @@ declare class TaskPanelPage {
|
|
|
12
12
|
assertAssigmentStatus(status: string): Promise<void>;
|
|
13
13
|
assignTaskToMe(): Promise<void>;
|
|
14
14
|
completeATask(): Promise<void>;
|
|
15
|
-
|
|
15
|
+
taskCard(name: string, userTaskName?: string): Locator;
|
|
16
|
+
revealTaskCard(card: Locator, name: string): Promise<void>;
|
|
17
|
+
assertTaskVisible(name: string): Promise<void>;
|
|
18
|
+
openTask(name: string, userTaskName?: string): Promise<string>;
|
|
16
19
|
taskListBannerIsVisible(): Promise<void>;
|
|
17
20
|
asssertUnnassignedTaskEmptyMessage(): Promise<void>;
|
|
18
21
|
filterBy(option: 'All open tasks' | 'Unassigned' | 'Assigned to me' | 'Completed' | 'Custom'): Promise<void>;
|
|
@@ -56,16 +56,62 @@ class TaskPanelPage {
|
|
|
56
56
|
await this.assignToMeButton.click();
|
|
57
57
|
await this.completeTaskButton.click();
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
let
|
|
59
|
+
taskCard(name, userTaskName = '') {
|
|
60
|
+
let cards = this.availableTasks
|
|
61
|
+
.getByRole('article')
|
|
62
|
+
.filter({ has: this.page.getByText(name, { exact: true }) });
|
|
61
63
|
if (userTaskName != '') {
|
|
62
|
-
|
|
63
|
-
.getByRole('article')
|
|
64
|
-
.filter({ hasText: name })
|
|
65
|
-
.filter({ hasText: userTaskName });
|
|
64
|
+
cards = cards.filter({ hasText: userTaskName });
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
return cards.first();
|
|
67
|
+
}
|
|
68
|
+
// AvailableTasks.tsx renders the list through @tanstack/react-virtual, so
|
|
69
|
+
// only the cards inside the scroll window (plus a five-item overscan) are
|
|
70
|
+
// in the DOM. A task further down the list is not hidden, it is not
|
|
71
|
+
// rendered at all, so getByText() keeps reporting "element(s) not found"
|
|
72
|
+
// however long it waits. Page through the list until the card renders,
|
|
73
|
+
// wrapping back to the top so the layout's 5s refetch can bring in a task
|
|
74
|
+
// that was still being indexed.
|
|
75
|
+
async revealTaskCard(card, name) {
|
|
76
|
+
const scrollableList = this.availableTasks.getByTestId('scrollable-list');
|
|
77
|
+
await test_1.expect
|
|
78
|
+
.poll(async () => {
|
|
79
|
+
if (await card.isVisible()) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (await scrollableList.isVisible()) {
|
|
83
|
+
await scrollableList.evaluate((list) => {
|
|
84
|
+
const maxScrollTop = list.scrollHeight - list.clientHeight;
|
|
85
|
+
if (list.scrollTop >= maxScrollTop - 1) {
|
|
86
|
+
list.scrollTop = 0;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
list.scrollTop += Math.max(list.clientHeight - 100, 200);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}, {
|
|
94
|
+
timeout: 90000,
|
|
95
|
+
intervals: [500],
|
|
96
|
+
message: `Task "${name}" never rendered in the task list`,
|
|
97
|
+
})
|
|
98
|
+
.toBe(true);
|
|
99
|
+
}
|
|
100
|
+
async assertTaskVisible(name) {
|
|
101
|
+
await this.revealTaskCard(this.taskCard(name), name);
|
|
102
|
+
}
|
|
103
|
+
// Returns the opened task's userTaskKey -- the only identifier that tells
|
|
104
|
+
// two tasks sharing a name apart.
|
|
105
|
+
async openTask(name, userTaskName = '') {
|
|
106
|
+
const card = this.taskCard(name, userTaskName);
|
|
107
|
+
await this.revealTaskCard(card, name);
|
|
108
|
+
await card.click({ timeout: 20000 });
|
|
109
|
+
await this.page.waitForURL(/\/tasklist\/\d+/, { timeout: 30000 });
|
|
110
|
+
const openedTask = /\/tasklist\/(\d+)/.exec(this.page.url());
|
|
111
|
+
if (openedTask === null) {
|
|
112
|
+
throw new Error(`Opening task "${name}" did not open a task`);
|
|
113
|
+
}
|
|
114
|
+
return openedTask[1];
|
|
69
115
|
}
|
|
70
116
|
async taskListBannerIsVisible() {
|
|
71
117
|
await (0, test_1.expect)(this.taskListPageBanner).toBeVisible({ timeout: 30000 });
|
|
@@ -78,23 +124,44 @@ class TaskPanelPage {
|
|
|
78
124
|
// appears. Unlike the old Carbon sidebar, the trigger is always visible
|
|
79
125
|
// -- there's no separate expand/collapse step, and selecting an option
|
|
80
126
|
// closes the menu on its own.
|
|
81
|
-
|
|
82
|
-
|
|
127
|
+
//
|
|
128
|
+
// The trigger also carries the active filter's label, so it doubles as
|
|
129
|
+
// the "selection applied" signal -- the same one camunda/camunda's own
|
|
130
|
+
// Filters.test.tsx and TasklistIndex.page.ts assert on. It replaces the
|
|
131
|
+
// heading the old Carbon panel rendered for the active filter, which the
|
|
132
|
+
// shadcn panel no longer has.
|
|
133
|
+
//
|
|
134
|
+
// TasksLayoutPage renders <Filters disabled={isPending} />, so a task
|
|
135
|
+
// list refetch landing while the menu is open disables the trigger and
|
|
136
|
+
// tears the menu down before Radix runs the item's onSelect: the menu
|
|
137
|
+
// closes but the filter never changes. Re-open and pick again rather
|
|
138
|
+
// than failing on the first dropped selection.
|
|
83
139
|
const menuItem = this.page.getByRole('menuitem', {
|
|
84
140
|
name: option,
|
|
85
141
|
exact: true,
|
|
86
142
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await (0, test_1.expect)(menuItem).toBeHidden({ timeout: 10000 });
|
|
90
|
-
// The trigger renders the active filter's label, so this confirms the
|
|
91
|
-
// selection was applied -- the same signal camunda/camunda's own
|
|
92
|
-
// Filters.test.tsx asserts on the trigger. It replaces the heading the
|
|
93
|
-
// old Carbon panel rendered for the active filter, which the shadcn
|
|
94
|
-
// panel no longer has.
|
|
95
|
-
await (0, test_1.expect)(this.filterSelectButton).toContainText(option, {
|
|
96
|
-
timeout: 10000,
|
|
143
|
+
const triggerShowingOption = this.filterSelectButton.filter({
|
|
144
|
+
hasText: new RegExp(`^${option}$`),
|
|
97
145
|
});
|
|
146
|
+
const maxAttempts = 3;
|
|
147
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
148
|
+
await (0, test_1.expect)(this.filterSelectButton).toBeEnabled({ timeout: 30000 });
|
|
149
|
+
await this.filterSelectButton.click();
|
|
150
|
+
await (0, test_1.expect)(menuItem).toBeVisible({ timeout: 10000 });
|
|
151
|
+
await menuItem.click();
|
|
152
|
+
const applied = await triggerShowingOption
|
|
153
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
154
|
+
.then(() => true)
|
|
155
|
+
.catch(() => false);
|
|
156
|
+
if (applied) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
console.warn(`Attempt ${attempt} to apply the "${option}" task filter did not ` +
|
|
160
|
+
'take effect. Retrying...');
|
|
161
|
+
await this.page.keyboard.press('Escape');
|
|
162
|
+
await (0, test_1.expect)(this.filterSelectButton).toHaveAttribute('aria-expanded', 'false', { timeout: 10000 });
|
|
163
|
+
}
|
|
164
|
+
throw new Error(`Task filter "${option}" was not applied after ${maxAttempts} attempts`);
|
|
98
165
|
}
|
|
99
166
|
}
|
|
100
167
|
exports.TaskPanelPage = TaskPanelPage;
|
|
@@ -42,23 +42,32 @@ exports.validateURL = validateURL;
|
|
|
42
42
|
async function completeTaskWithRetry(taskPanelPage, taskDetailsPage, taskName, taskPriority, userTaskName = '', textInput = '', maxRetries = 5) {
|
|
43
43
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
44
44
|
try {
|
|
45
|
-
await taskPanelPage.openTask(taskName, userTaskName);
|
|
46
|
-
const openTasksWithName = taskPanelPage.availableTasks.getByText(taskName, { exact: true });
|
|
47
|
-
const openTaskCount = await openTasksWithName.count();
|
|
45
|
+
const userTaskKey = await taskPanelPage.openTask(taskName, userTaskName);
|
|
48
46
|
await (0, sleep_1.sleep)(500);
|
|
49
47
|
if (!(await taskDetailsPage.assignedToMeText.isVisible())) {
|
|
50
48
|
await taskDetailsPage.clickAssignToMeButton();
|
|
51
49
|
}
|
|
52
|
-
|
|
50
|
+
// The details panel is fetched after the task is opened, so the
|
|
51
|
+
// priority label can lag the click by more than Playwright's 5s
|
|
52
|
+
// default expect timeout on a loaded c8Run runner. Every other wait
|
|
53
|
+
// in this helper already carries an explicit, generous timeout.
|
|
54
|
+
await (0, test_1.expect)(taskDetailsPage.detailsPanel.getByText(taskPriority)).toBeVisible({ timeout: 30000 });
|
|
53
55
|
if (textInput != '') {
|
|
54
56
|
await taskDetailsPage.clickTextInput();
|
|
55
57
|
await taskDetailsPage.fillTextInput(textInput);
|
|
56
58
|
}
|
|
57
59
|
await taskDetailsPage.taskCompletedBanner.waitFor({ state: 'hidden' });
|
|
58
60
|
await taskDetailsPage.clickCompleteTaskButton();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
// clickCompleteTaskButton() already waits for the "Task completed"
|
|
62
|
+
// banner, so the completion itself is confirmed there. Counting the
|
|
63
|
+
// open cards that share this task's name cannot confirm it: the list
|
|
64
|
+
// is virtualised, so the count only ever covers the scrolled-in
|
|
65
|
+
// window, and any concurrently running copy of this spec completes
|
|
66
|
+
// tasks with the same name -- the count can stay flat or drop by two,
|
|
67
|
+
// and a false failure here sends the loop round to open and complete
|
|
68
|
+
// a second task. Wait for this exact card, keyed on its userTaskKey,
|
|
69
|
+
// to leave the open list instead.
|
|
70
|
+
await (0, test_1.expect)(taskPanelPage.availableTasks.locator(`[data-testid="task-${userTaskKey}"]`)).toHaveCount(0, { timeout: 60000 });
|
|
62
71
|
return;
|
|
63
72
|
}
|
|
64
73
|
catch (error) {
|
|
@@ -67,7 +76,8 @@ async function completeTaskWithRetry(taskPanelPage, taskDetailsPage, taskName, t
|
|
|
67
76
|
}
|
|
68
77
|
else {
|
|
69
78
|
console.error(error);
|
|
70
|
-
throw new Error(`
|
|
79
|
+
throw new Error(`Completing task ${taskName} failed after ${maxRetries} ` +
|
|
80
|
+
`attempts: ${error}`);
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
83
|
}
|
|
@@ -138,10 +138,10 @@ c8Run_8_10_1.test.describe('HTO User Flow Tests', () => {
|
|
|
138
138
|
// The shadcn task panel no longer renders the active filter as a
|
|
139
139
|
// heading (filterBy asserts it on the Filters trigger instead), so
|
|
140
140
|
// wait for a completed task to actually appear in the list -- that
|
|
141
|
-
// also covers the indexing lag before the reload below.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
// also covers the indexing lag before the reload below. The list is
|
|
142
|
+
// virtualised, so this has to page through it rather than wait on a
|
|
143
|
+
// locator that is simply not rendered yet.
|
|
144
|
+
await taskPanelPage.assertTaskVisible('priorityTest4');
|
|
145
145
|
await page.reload();
|
|
146
146
|
await (0, sleep_1.sleep)(1000);
|
|
147
147
|
await taskPanelPage.openTask('priorityTest4');
|