@camunda/e2e-test-suite 0.0.891 → 0.0.893
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/ConnectorMarketplacePage.d.ts +3 -0
- package/dist/pages/SM-8.10/ConnectorMarketplacePage.js +47 -8
- package/dist/pages/SM-8.10/ModelerCreatePage.d.ts +7 -0
- package/dist/pages/SM-8.10/ModelerCreatePage.js +139 -25
- package/dist/tests/SM-8.10/connectors-user-flows.spec.js +29 -6
- package/dist/tests/SM-8.10/document-handling-user-flows.spec.js +2 -1
- package/dist/tests/SM-8.10/mt-enabled-user-flows.spec.js +6 -3
- package/package.json +1 -1
|
@@ -3,7 +3,9 @@ declare class ConnectorMarketplacePage {
|
|
|
3
3
|
private page;
|
|
4
4
|
readonly searchForConnectorTextbox: Locator;
|
|
5
5
|
readonly downloadToProjectButton: Locator;
|
|
6
|
+
readonly addToProjectButton: Locator;
|
|
6
7
|
readonly replaceResourceButton: Locator;
|
|
8
|
+
readonly saveAsCopyButton: Locator;
|
|
7
9
|
readonly cancelButton: Locator;
|
|
8
10
|
readonly marketplaceErrorMessage: Locator;
|
|
9
11
|
readonly noResultsMessage: Locator;
|
|
@@ -15,5 +17,6 @@ declare class ConnectorMarketplacePage {
|
|
|
15
17
|
clickReplaceResourceButton(): Promise<void>;
|
|
16
18
|
waitForConnectorSearchResults(): Promise<void>;
|
|
17
19
|
downloadConnectorToProject(): Promise<void>;
|
|
20
|
+
private confirmImport;
|
|
18
21
|
}
|
|
19
22
|
export { ConnectorMarketplacePage };
|
|
@@ -7,7 +7,9 @@ class ConnectorMarketplacePage {
|
|
|
7
7
|
page;
|
|
8
8
|
searchForConnectorTextbox;
|
|
9
9
|
downloadToProjectButton;
|
|
10
|
+
addToProjectButton;
|
|
10
11
|
replaceResourceButton;
|
|
12
|
+
saveAsCopyButton;
|
|
11
13
|
cancelButton;
|
|
12
14
|
marketplaceErrorMessage;
|
|
13
15
|
noResultsMessage;
|
|
@@ -18,9 +20,13 @@ class ConnectorMarketplacePage {
|
|
|
18
20
|
.getByRole('button', { name: 'Download to project' })
|
|
19
21
|
.first();
|
|
20
22
|
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
|
|
23
|
+
this.addToProjectButton = page.getByRole('button', {
|
|
24
|
+
name: 'Add to project',
|
|
25
|
+
});
|
|
21
26
|
this.replaceResourceButton = page.getByRole('button', {
|
|
22
27
|
name: 'Replace resource',
|
|
23
28
|
});
|
|
29
|
+
this.saveAsCopyButton = page.getByRole('button', { name: 'Save as copy' });
|
|
24
30
|
this.marketplaceErrorMessage = page.getByText('an error occurred, please try again later');
|
|
25
31
|
this.noResultsMessage = page.getByText('find a match for your search phrase');
|
|
26
32
|
}
|
|
@@ -42,7 +48,7 @@ class ConnectorMarketplacePage {
|
|
|
42
48
|
await this.replaceResourceButton.click({ timeout: 30000 });
|
|
43
49
|
}
|
|
44
50
|
async waitForConnectorSearchResults() {
|
|
45
|
-
const MAX_RETRIES =
|
|
51
|
+
const MAX_RETRIES = 4;
|
|
46
52
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
47
53
|
await (0, test_1.expect)(this.downloadToProjectButton
|
|
48
54
|
.or(this.marketplaceErrorMessage)
|
|
@@ -52,6 +58,10 @@ class ConnectorMarketplacePage {
|
|
|
52
58
|
}
|
|
53
59
|
console.log(`Marketplace search attempt ${attempt} of ${MAX_RETRIES} returned an error or no results. Retrying search...`);
|
|
54
60
|
if (attempt < MAX_RETRIES) {
|
|
61
|
+
// The marketplace search API returns transient 5xx. Back off for
|
|
62
|
+
// growing intervals before re-issuing the query so a short outage
|
|
63
|
+
// self-heals instead of burning all attempts inside a few seconds.
|
|
64
|
+
await (0, sleep_1.sleep)(attempt * 5000);
|
|
55
65
|
const searchText = await this.searchForConnectorTextbox.inputValue();
|
|
56
66
|
await this.searchForConnectorTextbox.clear();
|
|
57
67
|
await (0, sleep_1.sleep)(2000);
|
|
@@ -63,15 +73,44 @@ class ConnectorMarketplacePage {
|
|
|
63
73
|
}
|
|
64
74
|
async downloadConnectorToProject() {
|
|
65
75
|
await this.clickDownloadToProjectButton();
|
|
66
|
-
|
|
67
|
-
await Promise.race([
|
|
68
|
-
this.replaceResourceButton.click({ timeout: 20000 }),
|
|
69
|
-
this.cancelButton.click({ timeout: 20000 }),
|
|
70
|
-
]);
|
|
71
|
-
}
|
|
72
|
-
catch (e) {
|
|
76
|
+
if (await this.confirmImport()) {
|
|
73
77
|
return;
|
|
74
78
|
}
|
|
79
|
+
// The modal is up but rendered none of the known import actions. Dismiss it
|
|
80
|
+
// so it does not block the next click on the diagram, then fail here:
|
|
81
|
+
// continuing would leave the diagram's connector template unresolved
|
|
82
|
+
// ("Template: Not found") and only surface 60s later as a missing
|
|
83
|
+
// properties-panel group, pointing at the wrong step.
|
|
84
|
+
if (await this.cancelButton.isVisible().catch(() => false)) {
|
|
85
|
+
await this.cancelButton.click({ timeout: 20000 });
|
|
86
|
+
throw new Error('Connector import modal offered no import action ("Add to project", "Replace resource" or "Save as copy"); the import was abandoned.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Hub's import modal labels its primary button after the project state:
|
|
90
|
+
// "Add to project" when the template is new to the project, "Replace
|
|
91
|
+
// resource" when the same template is already there, "Save as copy" on a
|
|
92
|
+
// conflicting version. Probe them in order and click the one actually
|
|
93
|
+
// rendered; clicking "Cancel" instead abandons the import and leaves the
|
|
94
|
+
// diagram's connector template unresolved ("Template: Not found"), which
|
|
95
|
+
// only surfaces much later as a missing properties-panel group.
|
|
96
|
+
async confirmImport() {
|
|
97
|
+
const importButtons = [
|
|
98
|
+
this.addToProjectButton,
|
|
99
|
+
this.replaceResourceButton,
|
|
100
|
+
this.saveAsCopyButton,
|
|
101
|
+
];
|
|
102
|
+
const deadline = Date.now() + 20000;
|
|
103
|
+
do {
|
|
104
|
+
for (const button of importButtons) {
|
|
105
|
+
if (await button.isVisible().catch(() => false)) {
|
|
106
|
+
await button.click({ timeout: 20000 });
|
|
107
|
+
await (0, test_1.expect)(button).toBeHidden({ timeout: 60000 });
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
await (0, sleep_1.sleep)(1000);
|
|
112
|
+
} while (Date.now() < deadline);
|
|
113
|
+
return false;
|
|
75
114
|
}
|
|
76
115
|
}
|
|
77
116
|
exports.ConnectorMarketplacePage = ConnectorMarketplacePage;
|
|
@@ -48,6 +48,8 @@ declare class ModelerCreatePage {
|
|
|
48
48
|
readonly secondPlacedGateway: Locator;
|
|
49
49
|
readonly secondPlacedElement: Locator;
|
|
50
50
|
readonly appendPreButton: Locator;
|
|
51
|
+
readonly appendAnythingEntry: Locator;
|
|
52
|
+
readonly appendPopup: Locator;
|
|
51
53
|
readonly appendGatewayButton: Locator;
|
|
52
54
|
readonly parallelGatewayOption: Locator;
|
|
53
55
|
readonly connectToOtherElementButton: Locator;
|
|
@@ -105,6 +107,8 @@ declare class ModelerCreatePage {
|
|
|
105
107
|
fillElementIdInput(id: string): Promise<void>;
|
|
106
108
|
clickStartEventElement(): Promise<void>;
|
|
107
109
|
selectStartEventElement(): Promise<void>;
|
|
110
|
+
private clickCreatePadEntry;
|
|
111
|
+
private appendViaPopup;
|
|
108
112
|
clickAppendTaskButton(): Promise<void>;
|
|
109
113
|
hoverOnLocator(locator: Locator, selector?: string): Promise<void>;
|
|
110
114
|
clickAppendGatewayButton(): Promise<void>;
|
|
@@ -112,6 +116,7 @@ declare class ModelerCreatePage {
|
|
|
112
116
|
clickChangeTypeButton(): Promise<void>;
|
|
113
117
|
clickUserTaskOption(): Promise<void>;
|
|
114
118
|
clickServiceTaskOption(): Promise<void>;
|
|
119
|
+
private appendEndEvent;
|
|
115
120
|
clickAppendEndEventButton(parentElement?: string): Promise<void>;
|
|
116
121
|
clickFirstPlacedElement(): Promise<void>;
|
|
117
122
|
clickSecondPlacedElement(): Promise<void>;
|
|
@@ -139,10 +144,12 @@ declare class ModelerCreatePage {
|
|
|
139
144
|
clickDeploySubButton(): Promise<void>;
|
|
140
145
|
clickCancelButton(): Promise<void>;
|
|
141
146
|
private recoverModelerSession;
|
|
147
|
+
private searchAndClickRestConnector;
|
|
142
148
|
clickRestConnectorOption(recoverSession?: () => Promise<void>): Promise<void>;
|
|
143
149
|
clickMarketPlaceButton(): Promise<void>;
|
|
144
150
|
completeDeploymentEndpointConfiguration(): Promise<void>;
|
|
145
151
|
completePlayConfiguration(): Promise<void>;
|
|
152
|
+
filterChangeElementPopup(searchTerm: string): Promise<void>;
|
|
146
153
|
clickWebhookMessageStartEventConnectorOption(): Promise<void>;
|
|
147
154
|
clickWebhookIdInput(): Promise<void>;
|
|
148
155
|
clearWebhookIdInput(): Promise<void>;
|
|
@@ -53,6 +53,8 @@ class ModelerCreatePage {
|
|
|
53
53
|
secondPlacedGateway;
|
|
54
54
|
secondPlacedElement;
|
|
55
55
|
appendPreButton;
|
|
56
|
+
appendAnythingEntry;
|
|
57
|
+
appendPopup;
|
|
56
58
|
appendGatewayButton;
|
|
57
59
|
parallelGatewayOption;
|
|
58
60
|
connectToOtherElementButton;
|
|
@@ -104,6 +106,8 @@ class ModelerCreatePage {
|
|
|
104
106
|
this.startEventElement = page.locator('.djs-hit').first();
|
|
105
107
|
this.appendPreButton = page.locator('.djs-create-pad-icon > svg').first();
|
|
106
108
|
this.appendTaskButton = page.getByRole('button', { name: 'Append task' });
|
|
109
|
+
this.appendAnythingEntry = page.locator('.djs-create-pad-entries [data-entry-id="append"]');
|
|
110
|
+
this.appendPopup = page.locator('.djs-popup.bpmn-append');
|
|
107
111
|
this.changeTypeButton = page.getByRole('button', {
|
|
108
112
|
name: 'Change element',
|
|
109
113
|
exact: true,
|
|
@@ -148,20 +152,27 @@ class ModelerCreatePage {
|
|
|
148
152
|
.getByRole('dialog')
|
|
149
153
|
.getByRole('button', { name: 'Deploy' });
|
|
150
154
|
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
|
|
151
|
-
this.restConnectorOption = page
|
|
155
|
+
this.restConnectorOption = page
|
|
156
|
+
.locator('.djs-popup-results')
|
|
157
|
+
.locator('[data-id="replace.template-io.camunda.connectors.HttpJson.v2"]');
|
|
152
158
|
this.changeElementSearchInput = page.locator('.djs-popup-search input');
|
|
153
|
-
|
|
159
|
+
// A title query is ambiguous: when the filtered result list has no connector
|
|
160
|
+
// match the popup also renders an empty-state link carrying the same title,
|
|
161
|
+
// so the query resolves to two elements and clicking throws in strict mode.
|
|
162
|
+
// Target the popup header entry by its stable entry id instead.
|
|
163
|
+
this.marketPlaceButton = page.locator('[data-id="browseConnectors"]');
|
|
154
164
|
this.clientIdTextbox = page.getByLabel('Client ID');
|
|
155
165
|
this.clientSecretTextbox = page.getByLabel('Client secret');
|
|
156
166
|
this.rememberCredentialsCheckbox = page.getByText('Remember credentials');
|
|
157
|
-
this.webhookMessageStartEventConnectorOption = page
|
|
167
|
+
this.webhookMessageStartEventConnectorOption = page
|
|
168
|
+
.locator('.djs-popup-results')
|
|
169
|
+
.getByRole('option', {
|
|
158
170
|
name: 'Webhook Message Start Event Connector',
|
|
159
|
-
exact: true,
|
|
160
171
|
});
|
|
161
172
|
this.webhookIdInput = page.getByLabel('Webhook ID');
|
|
162
|
-
this.firstElement = page.locator('
|
|
163
|
-
this.secondElement = page.locator('
|
|
164
|
-
this.thirdElement = page.locator('
|
|
173
|
+
this.firstElement = page.locator('.djs-element.djs-shape').nth(0);
|
|
174
|
+
this.secondElement = page.locator('.djs-element.djs-shape').nth(1);
|
|
175
|
+
this.thirdElement = page.locator('.djs-element.djs-shape').nth(2);
|
|
165
176
|
this.implementationSection = page.locator('[data-group-id="group-userTaskImplementation"]');
|
|
166
177
|
this.implementationOptions = page.locator('#bio-properties-panel-userTaskImplementation');
|
|
167
178
|
this.assignmentSection = page.locator('[data-group-id="group-assignmentDefinition"]');
|
|
@@ -175,9 +186,7 @@ class ModelerCreatePage {
|
|
|
175
186
|
this.secondPlacedGateway = page
|
|
176
187
|
.locator('[data-element-id*="Gateway"]')
|
|
177
188
|
.last();
|
|
178
|
-
this.secondPlacedElement = page
|
|
179
|
-
.locator('[class= "djs-element djs-shape"]')
|
|
180
|
-
.last();
|
|
189
|
+
this.secondPlacedElement = page.locator('.djs-element.djs-shape').last();
|
|
181
190
|
this.connectToOtherElementButton = page.getByRole('button', {
|
|
182
191
|
name: 'Connect to other element',
|
|
183
192
|
});
|
|
@@ -185,7 +194,9 @@ class ModelerCreatePage {
|
|
|
185
194
|
this.intermediateBoundaryEvent = page.getByRole('button', {
|
|
186
195
|
name: 'Append intermediate/boundary',
|
|
187
196
|
});
|
|
188
|
-
this.intermediateWebhookConnectorOption = page
|
|
197
|
+
this.intermediateWebhookConnectorOption = page
|
|
198
|
+
.locator('.djs-popup-results')
|
|
199
|
+
.getByRole('option', {
|
|
189
200
|
name: 'Webhook Intermediate Event Connector',
|
|
190
201
|
});
|
|
191
202
|
this.correlationKeyPayloadInput = page.getByLabel('Correlation key (payload)');
|
|
@@ -201,9 +212,10 @@ class ModelerCreatePage {
|
|
|
201
212
|
name: 'candidate groups',
|
|
202
213
|
});
|
|
203
214
|
this.tenantIdInput = page.getByLabel('Tenant ID (Optional)');
|
|
204
|
-
this.timerEventOption = page
|
|
215
|
+
this.timerEventOption = page
|
|
216
|
+
.locator('.djs-popup-results')
|
|
217
|
+
.getByRole('option', {
|
|
205
218
|
name: 'Timer intermediate catch event',
|
|
206
|
-
exact: true,
|
|
207
219
|
});
|
|
208
220
|
this.timerEventSettings = page.getByText(/^Timer$/).first();
|
|
209
221
|
this.timerType = page.getByLabel('Type');
|
|
@@ -426,9 +438,66 @@ class ModelerCreatePage {
|
|
|
426
438
|
async selectStartEventElement() {
|
|
427
439
|
await this.startEventElement.click();
|
|
428
440
|
}
|
|
441
|
+
// The create pad's direct append icons no longer carry the bpmn-js `title`
|
|
442
|
+
// attributes ("Append task", "Append intermediate/boundary", "Append end
|
|
443
|
+
// event"), so append through the "append anything" entry instead: it opens
|
|
444
|
+
// the bpmn-append popup whose entries are addressed by the stable `data-id`
|
|
445
|
+
// ids bpmn-js declares in PopupEntries. Since camunda-bpmn-js 5.30.0 those
|
|
446
|
+
// entries sit in drill-in categories, so filter with the popup search first
|
|
447
|
+
// and fall back to walking the categories when search does not surface it.
|
|
448
|
+
// The append create pad renders its entries asynchronously once the source
|
|
449
|
+
// element is selected, so wait for the requested entry instead of probing for
|
|
450
|
+
// it. Entries are addressed by the `data-entry-id` bpmn-js declares on them
|
|
451
|
+
// ("append.end-event", "append.intermediate-event", "append.append-task").
|
|
452
|
+
async clickCreatePadEntry(entryId) {
|
|
453
|
+
const entry = this.page.locator(`.djs-create-pad-entries [data-entry-id="${entryId}"]`);
|
|
454
|
+
try {
|
|
455
|
+
await entry.waitFor({ state: 'visible', timeout: 30000 });
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
await entry.click({ timeout: 30000 });
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
async appendViaPopup(entryId, categoryIds, searchTerm) {
|
|
464
|
+
await this.hoverOnLocator(this.appendPreButton);
|
|
465
|
+
await this.appendAnythingEntry.click({ timeout: 30000 });
|
|
466
|
+
await (0, test_1.expect)(this.appendPopup).toBeVisible({ timeout: 30000 });
|
|
467
|
+
const entry = this.appendPopup.locator(`[data-id="${entryId}"]`).first();
|
|
468
|
+
const search = this.appendPopup.locator('.djs-popup-search input');
|
|
469
|
+
const hasSearch = await search
|
|
470
|
+
.isVisible({ timeout: 5000 })
|
|
471
|
+
.catch(() => false);
|
|
472
|
+
if (hasSearch) {
|
|
473
|
+
await search.fill(searchTerm);
|
|
474
|
+
}
|
|
475
|
+
const surfaced = await entry.isVisible({ timeout: 5000 }).catch(() => false);
|
|
476
|
+
if (!surfaced) {
|
|
477
|
+
if (hasSearch) {
|
|
478
|
+
await search.fill('');
|
|
479
|
+
}
|
|
480
|
+
// The popup drills into a category based on its own hover-tracked
|
|
481
|
+
// selection, so hover each category before clicking it.
|
|
482
|
+
for (const categoryId of categoryIds) {
|
|
483
|
+
const category = this.appendPopup
|
|
484
|
+
.locator(`[data-id="${categoryId}"]`)
|
|
485
|
+
.first();
|
|
486
|
+
await category.scrollIntoViewIfNeeded({ timeout: 15000 });
|
|
487
|
+
await category.hover({ timeout: 15000 });
|
|
488
|
+
await category.click({ timeout: 15000 });
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
await entry.scrollIntoViewIfNeeded({ timeout: 30000 });
|
|
492
|
+
await entry.click({ timeout: 30000 });
|
|
493
|
+
await (0, test_1.expect)(this.appendPopup).toBeHidden({ timeout: 30000 });
|
|
494
|
+
}
|
|
429
495
|
async clickAppendTaskButton() {
|
|
430
496
|
await this.hoverOnLocator(this.appendPreButton);
|
|
431
|
-
await this.
|
|
497
|
+
if (await this.clickCreatePadEntry('append.append-task')) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
await this.appendViaPopup('append-task', ['append-tasks'], 'Task');
|
|
432
501
|
}
|
|
433
502
|
async hoverOnLocator(locator, selector = '.djs-create-pad-entry') {
|
|
434
503
|
const canvasState = await Promise.race([
|
|
@@ -543,9 +612,15 @@ class ModelerCreatePage {
|
|
|
543
612
|
}
|
|
544
613
|
throw new Error(`Failed to click the button after ${maxRetries} attempts.`);
|
|
545
614
|
}
|
|
615
|
+
async appendEndEvent() {
|
|
616
|
+
if (await this.clickCreatePadEntry('append.end-event')) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
await this.appendViaPopup('append-none-end-event', ['append-events', 'append-end-events'], 'End event');
|
|
620
|
+
}
|
|
546
621
|
async clickAppendEndEventButton(parentElement) {
|
|
547
622
|
try {
|
|
548
|
-
await this.
|
|
623
|
+
await this.appendEndEvent();
|
|
549
624
|
}
|
|
550
625
|
catch (error) {
|
|
551
626
|
await this.page.reload();
|
|
@@ -558,7 +633,7 @@ class ModelerCreatePage {
|
|
|
558
633
|
await this.clickSecondPlacedGateway();
|
|
559
634
|
}
|
|
560
635
|
await this.hoverOnLocator(this.appendElementButton);
|
|
561
|
-
await this.
|
|
636
|
+
await this.appendEndEvent();
|
|
562
637
|
}
|
|
563
638
|
}
|
|
564
639
|
async clickFirstPlacedElement() {
|
|
@@ -686,22 +761,39 @@ class ModelerCreatePage {
|
|
|
686
761
|
});
|
|
687
762
|
await (0, test_1.expect)(this.secondElement).toBeVisible({ timeout: 60000 });
|
|
688
763
|
}
|
|
764
|
+
// The change-element popup virtualizes its list: connector templates render
|
|
765
|
+
// below the built-in BPMN tasks and are not in the queryable DOM slice until
|
|
766
|
+
// the list is filtered, so every attempt has to re-type the filter -- a freshly
|
|
767
|
+
// (re)opened popup is always unfiltered, and asserting straight on the entry
|
|
768
|
+
// then times out against an off-screen node. Waiting for the search input also
|
|
769
|
+
// confirms the popup actually opened: the "Change element" click reports
|
|
770
|
+
// success even when the popup does not render. Use pressSequentially (not
|
|
771
|
+
// fill): fill sets the value in one shot and the single input event can land
|
|
772
|
+
// before bpmn-js attaches its search handler during the popup re-render.
|
|
773
|
+
async searchAndClickRestConnector() {
|
|
774
|
+
await (0, test_1.expect)(this.changeElementSearchInput).toBeVisible({ timeout: 30000 });
|
|
775
|
+
await this.changeElementSearchInput.click();
|
|
776
|
+
await this.changeElementSearchInput.fill('');
|
|
777
|
+
await this.changeElementSearchInput.pressSequentially('REST Outbound', {
|
|
778
|
+
delay: 50,
|
|
779
|
+
});
|
|
780
|
+
await (0, test_1.expect)(this.restConnectorOption).toBeVisible({ timeout: 90000 });
|
|
781
|
+
await this.restConnectorOption.click({ timeout: 90000 });
|
|
782
|
+
}
|
|
689
783
|
async clickRestConnectorOption(recoverSession) {
|
|
690
784
|
const maxRetries = 4;
|
|
691
785
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
692
786
|
try {
|
|
693
787
|
if (retries === 0) {
|
|
694
|
-
//
|
|
695
|
-
await
|
|
696
|
-
await this.restConnectorOption.click({ timeout: 60000 });
|
|
788
|
+
// Panel already open from the caller (modelRestConnector in UtilitiesPage)
|
|
789
|
+
await this.searchAndClickRestConnector();
|
|
697
790
|
}
|
|
698
791
|
else if (retries === 1 || retries === 2) {
|
|
699
792
|
// Recover session cleanly instead of page.reload()
|
|
700
793
|
await this.recoverModelerSession(recoverSession);
|
|
701
794
|
await this.secondElement.click({ timeout: 60000 });
|
|
702
795
|
await this.changeTypeButton.click({ force: true, timeout: 60000 });
|
|
703
|
-
await
|
|
704
|
-
await this.restConnectorOption.click({ timeout: 90000 });
|
|
796
|
+
await this.searchAndClickRestConnector();
|
|
705
797
|
}
|
|
706
798
|
else {
|
|
707
799
|
// Fourth attempt: install via marketplace (real product flow)
|
|
@@ -711,10 +803,13 @@ class ModelerCreatePage {
|
|
|
711
803
|
await this.clickMarketPlaceButton();
|
|
712
804
|
const marketplace = new ConnectorMarketplacePage_1.ConnectorMarketplacePage(this.page);
|
|
713
805
|
await marketplace.clickSearchForConnectorTextbox();
|
|
714
|
-
await (0, sleep_1.sleep)(5000);
|
|
715
806
|
await marketplace.fillSearchForConnectorTextbox('REST Connector');
|
|
807
|
+
await marketplace.waitForConnectorSearchResults();
|
|
716
808
|
await marketplace.downloadConnectorToProject();
|
|
717
|
-
|
|
809
|
+
// Marketplace closes the change-type panel; reopen it so the newly
|
|
810
|
+
// installed connector appears in the list.
|
|
811
|
+
await this.changeTypeButton.click({ force: true, timeout: 60000 });
|
|
812
|
+
await this.searchAndClickRestConnector();
|
|
718
813
|
}
|
|
719
814
|
return;
|
|
720
815
|
}
|
|
@@ -824,11 +919,23 @@ class ModelerCreatePage {
|
|
|
824
919
|
}
|
|
825
920
|
}
|
|
826
921
|
}
|
|
922
|
+
// Connector templates nest under the change-element popup's "Templates"
|
|
923
|
+
// category and every entry now renders a description, so filter the popup by
|
|
924
|
+
// name to surface the entry as a flat search result before clicking it.
|
|
925
|
+
async filterChangeElementPopup(searchTerm) {
|
|
926
|
+
const hasSearch = await this.changeElementSearchInput
|
|
927
|
+
.isVisible({ timeout: 5000 })
|
|
928
|
+
.catch(() => false);
|
|
929
|
+
if (hasSearch) {
|
|
930
|
+
await this.changeElementSearchInput.fill(searchTerm);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
827
933
|
async clickWebhookMessageStartEventConnectorOption() {
|
|
828
934
|
const maxRetries = 3;
|
|
829
935
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
830
936
|
try {
|
|
831
937
|
if (retries === 0) {
|
|
938
|
+
await this.filterChangeElementPopup('Webhook Message Start Event Connector');
|
|
832
939
|
await this.webhookMessageStartEventConnectorOption.click({
|
|
833
940
|
timeout: 30000,
|
|
834
941
|
});
|
|
@@ -836,6 +943,7 @@ class ModelerCreatePage {
|
|
|
836
943
|
else {
|
|
837
944
|
await this.page.reload();
|
|
838
945
|
await this.changeTypeButton.click({ force: true, timeout: 30000 });
|
|
946
|
+
await this.filterChangeElementPopup('Webhook Message Start Event Connector');
|
|
839
947
|
await this.webhookMessageStartEventConnectorOption.click({
|
|
840
948
|
timeout: 90000,
|
|
841
949
|
});
|
|
@@ -997,7 +1105,10 @@ class ModelerCreatePage {
|
|
|
997
1105
|
}
|
|
998
1106
|
}
|
|
999
1107
|
async clickIntermediateBoundaryEvent() {
|
|
1000
|
-
await this.
|
|
1108
|
+
if (await this.clickCreatePadEntry('append.intermediate-event')) {
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
await this.appendViaPopup('append-none-intermediate-throwing', ['append-events', 'append-intermediate-throw-events'], 'Intermediate throw event');
|
|
1001
1112
|
}
|
|
1002
1113
|
async clickIntermediateWebhookConnectorOption() {
|
|
1003
1114
|
const max = 3;
|
|
@@ -1079,7 +1190,10 @@ class ModelerCreatePage {
|
|
|
1079
1190
|
await (0, sleep_1.sleep)(1000);
|
|
1080
1191
|
}
|
|
1081
1192
|
async clickTimerEventOption() {
|
|
1082
|
-
|
|
1193
|
+
// The change-element popup renders its results list asynchronously, so wait
|
|
1194
|
+
// past the 10s default action timeout before clicking.
|
|
1195
|
+
await (0, test_1.expect)(this.timerEventOption).toBeVisible({ timeout: 60000 });
|
|
1196
|
+
await this.timerEventOption.click({ timeout: 30000 });
|
|
1083
1197
|
}
|
|
1084
1198
|
async clickTimerEventSettings() {
|
|
1085
1199
|
await this.timerEventSettings.click();
|
|
@@ -15,7 +15,8 @@ SM_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
15
15
|
await (0, _setup_1.captureScreenshot)(page, testInfo);
|
|
16
16
|
await (0, _setup_1.captureFailureVideo)(page, testInfo);
|
|
17
17
|
});
|
|
18
|
-
|
|
18
|
+
// Skipped due to bug #7716: https://github.com/camunda/connectors/issues/7716
|
|
19
|
+
SM_8_10_1.test.skip('REST Connector No Auth User Flow', async ({ page, operateHomePage, modelerHomePage, operateProcessInstancePage, modelerCreatePage, connectorSettingsPage, operateProcessesPage, connectorMarketplacePage, navigationPage, }) => {
|
|
19
20
|
SM_8_10_1.test.slow();
|
|
20
21
|
const processName = 'REST_Connector_No_Auth_Process' + (await (0, _setup_1.generateRandomStringAsync)(3));
|
|
21
22
|
await SM_8_10_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
|
|
@@ -43,7 +44,8 @@ SM_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
});
|
|
46
|
-
|
|
47
|
+
// Skipped due to bug #8162: https://github.com/camunda/connectors/issues/8162
|
|
48
|
+
SM_8_10_1.test.skip('REST Connector Bearer Token Auth User Flow', async ({ page, operateHomePage, modelerHomePage, modelerCreatePage, connectorSettingsPage, navigationPage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, }) => {
|
|
47
49
|
SM_8_10_1.test.slow();
|
|
48
50
|
const processName = 'REST_Connector_Bearer_Auth_Process' +
|
|
49
51
|
(await (0, _setup_1.generateRandomStringAsync)(3));
|
|
@@ -97,6 +99,7 @@ SM_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
97
99
|
await modelerCreatePage.clickStartEventElement();
|
|
98
100
|
await modelerCreatePage.clickChangeTypeButton();
|
|
99
101
|
try {
|
|
102
|
+
await modelerCreatePage.filterChangeElementPopup('Webhook Message Start Event Connector');
|
|
100
103
|
await (0, test_1.expect)(modelerCreatePage.webhookMessageStartEventConnectorOption).toBeVisible({
|
|
101
104
|
timeout: 15000,
|
|
102
105
|
});
|
|
@@ -123,8 +126,23 @@ SM_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
123
126
|
await SM_8_10_1.test.step('Make Authorization Request', async () => {
|
|
124
127
|
const baseURL = process.env.PLAYWRIGHT_BASE_URL ||
|
|
125
128
|
`http://gke-${process.env.BASE_URL}.ci.distro.ultrawombat.com`;
|
|
126
|
-
const
|
|
127
|
-
|
|
129
|
+
const url = `${baseURL}/connectors/inbound/test-webhook-id${randomString}`;
|
|
130
|
+
// Poll for inbound endpoint readiness — connector deployment is async
|
|
131
|
+
// after the diagram is deployed.
|
|
132
|
+
const deadline = Date.now() + 180000;
|
|
133
|
+
let response;
|
|
134
|
+
let attempt = 0;
|
|
135
|
+
while (Date.now() < deadline) {
|
|
136
|
+
attempt++;
|
|
137
|
+
response = await request.post(url);
|
|
138
|
+
if (response.status() === 200)
|
|
139
|
+
break;
|
|
140
|
+
console.error(`Webhook readiness attempt ${attempt}: ${url} -> ${response.status()}`);
|
|
141
|
+
await (0, sleep_1.sleep)(10000);
|
|
142
|
+
}
|
|
143
|
+
if (!response || response.status() !== 200) {
|
|
144
|
+
throw new Error(`Webhook request failed with status ${response?.status()} after ${attempt} attempts`);
|
|
145
|
+
}
|
|
128
146
|
});
|
|
129
147
|
await SM_8_10_1.test.step('Assert Diagram Has Successfully Completed in Operate', async () => {
|
|
130
148
|
await navigationPage.goToOperate();
|
|
@@ -134,7 +152,8 @@ SM_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
134
152
|
(0, test_1.expect)(result).toBe('completed');
|
|
135
153
|
});
|
|
136
154
|
});
|
|
137
|
-
|
|
155
|
+
// Skipped due to bug #7716: https://github.com/camunda/connectors/issues/7716
|
|
156
|
+
SM_8_10_1.test.skip('Connector Secrets User Flow', async ({ operateHomePage, modelerHomePage, navigationPage, modelerCreatePage, connectorSettingsPage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, }) => {
|
|
138
157
|
SM_8_10_1.test.slow();
|
|
139
158
|
const processName = 'REST_Connector_Process' + (await (0, _setup_1.generateRandomStringAsync)(3));
|
|
140
159
|
await SM_8_10_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
|
|
@@ -191,7 +210,11 @@ SM_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
191
210
|
await modelerCreatePage.clickIntermediateBoundaryEvent();
|
|
192
211
|
await modelerCreatePage.clickChangeTypeButton();
|
|
193
212
|
try {
|
|
194
|
-
|
|
213
|
+
// Probe for the template this flow needs: an intermediate event's
|
|
214
|
+
// change-element list never offers the message start event connector,
|
|
215
|
+
// so probing for that one always fell through to the marketplace.
|
|
216
|
+
await modelerCreatePage.filterChangeElementPopup('Webhook Intermediate Event Connector');
|
|
217
|
+
await (0, test_1.expect)(modelerCreatePage.intermediateWebhookConnectorOption).toBeVisible({
|
|
195
218
|
timeout: 15000,
|
|
196
219
|
});
|
|
197
220
|
}
|
|
@@ -116,7 +116,8 @@ if (process.env.IS_DS === 'true') {
|
|
|
116
116
|
await operateProcessInstancePage.assertProcessVariableContainsText('Upload_Files', 'aws');
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
|
-
|
|
119
|
+
// Skipped due to bug #8162: https://github.com/camunda/connectors/issues/8162
|
|
120
|
+
SM_8_10_1.test.skip('Document Handling Connectors User Flow - AWS @tasklistV2', async ({ page, navigationPage, modelerHomePage, modelerCreatePage, operateHomePage, operateProcessesPage, connectorMarketplacePage, operateProcessInstancePage, ocIdentityHomePage, ocIdentityRolesPage, }) => {
|
|
120
121
|
const processName = 'Document_Handling_Connectors_User_Flow_AWS_Process' +
|
|
121
122
|
(await (0, _setup_1.generateRandomStringAsync)(3));
|
|
122
123
|
const baseURL = process.env.PLAYWRIGHT_BASE_URL ||
|
|
@@ -127,7 +127,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
127
127
|
}
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
|
-
|
|
130
|
+
// Skipped due to bug #8162: https://github.com/camunda/connectors/issues/8162
|
|
131
|
+
SM_8_10_1.test.skip('Main User Can Create & Access Tenant - Connectors Flow @tasklistV2', async ({ page, modelerHomePage, modelerCreatePage, operateProcessInstancePage, operateProcessesPage, operateHomePage, navigationPage, managementIdentityTenantPage, connectorSettingsPage, connectorMarketplacePage, optimizeHomePage, optimizeDashboardPage, ocIdentityHomePage, ocTenantPage, }) => {
|
|
131
132
|
SM_8_10_1.test.slow();
|
|
132
133
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
133
134
|
const processName = 'Main_User_Connectors_Flow_' + randomString;
|
|
@@ -259,7 +260,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
259
260
|
});
|
|
260
261
|
}
|
|
261
262
|
});
|
|
262
|
-
|
|
263
|
+
// Skipped due to bug #8162: https://github.com/camunda/connectors/issues/8162
|
|
264
|
+
SM_8_10_1.test.skip('Second User Can Access Tenant Created By Main User - Connectors Flow @tasklistV2', async ({ page, modelerHomePage, modelerCreatePage, operateProcessInstancePage, operateProcessesPage, operateHomePage, navigationPage, managementIdentityTenantPage, connectorSettingsPage, connectorMarketplacePage, optimizeHomePage, optimizeDashboardPage, ocIdentityHomePage, ocTenantPage, managementIdentityPage, keycloakAdminPage, keycloakLoginPage, ocIdentityRolesPage, ocIdentityMappingRulesPage, browser, }) => {
|
|
263
265
|
SM_8_10_1.test.slow();
|
|
264
266
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
265
267
|
const processName = 'Second_User_Connectors_Flow_' + randomString;
|
|
@@ -388,7 +390,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
388
390
|
});
|
|
389
391
|
}
|
|
390
392
|
});
|
|
391
|
-
|
|
393
|
+
// Skipped due to bug #8162: https://github.com/camunda/connectors/issues/8162
|
|
394
|
+
SM_8_10_1.test.skip('User Can Be Assigned To Multiple Tenants @tasklistV2', async ({ page, modelerHomePage, modelerCreatePage, operateProcessInstancePage, operateProcessesPage, operateHomePage, navigationPage, managementIdentityTenantPage, connectorSettingsPage, connectorMarketplacePage, ocIdentityHomePage, ocTenantPage, managementIdentityPage, }) => {
|
|
392
395
|
SM_8_10_1.test.slow();
|
|
393
396
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
394
397
|
const processName1 = 'Multiple_Tenant_Flow_1' + randomString;
|