@camunda/e2e-test-suite 0.0.1140 → 0.0.1142
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.
|
@@ -79,6 +79,50 @@ class LoginPage {
|
|
|
79
79
|
await this.clickLoginButton();
|
|
80
80
|
await (0, UtilitiesPage_1.assertTestUsesCorrectOrganization)(this.page);
|
|
81
81
|
}
|
|
82
|
+
// The identity provider's email step submits through client-side JS, so a
|
|
83
|
+
// `Continue` click that lands while the form is still hydrating is swallowed:
|
|
84
|
+
// the click resolves, nothing navigates, and the page silently stays on the
|
|
85
|
+
// email step. `fillPassword` then waits out its whole 120s budget for a
|
|
86
|
+
// password field that will never render, and because every one of
|
|
87
|
+
// loginWithRetry's 5 attempts loses the click the same way the only symptom
|
|
88
|
+
// is "Login failed after 5 attempts: ... waiting for getByLabel(/^Password/)
|
|
89
|
+
// to be visible" -- the nightly failure in run 34319318325.
|
|
90
|
+
//
|
|
91
|
+
// Re-submitting the email is idempotent, so spend the same overall budget on
|
|
92
|
+
// three attempts instead of one, and only re-click while the email step is
|
|
93
|
+
// still on screen: that proves the previous click did not advance the flow.
|
|
94
|
+
async submitUsernameStep(username) {
|
|
95
|
+
const maxAttempts = 3;
|
|
96
|
+
const passwordStepTimeout = 40000;
|
|
97
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
98
|
+
await this.fillUsername(username);
|
|
99
|
+
await this.clickContinueButton();
|
|
100
|
+
const reachedPasswordStep = await this.passwordInput
|
|
101
|
+
.waitFor({ state: 'visible', timeout: passwordStepTimeout })
|
|
102
|
+
.then(() => true)
|
|
103
|
+
.catch(() => false);
|
|
104
|
+
if (reachedPasswordStep) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Surface a rate-limit page with the marker loginWithRetry keys its
|
|
108
|
+
// 60-90s backoff on, rather than burning the remaining attempts on a
|
|
109
|
+
// form the provider is not going to serve.
|
|
110
|
+
if (await this.rateLimitError
|
|
111
|
+
.first()
|
|
112
|
+
.isVisible()
|
|
113
|
+
.catch(() => false)) {
|
|
114
|
+
throw new Error('AUTH0_RATE_LIMIT: identity provider returned an error page');
|
|
115
|
+
}
|
|
116
|
+
// Neither the password step nor the email step is on screen: some
|
|
117
|
+
// interstitial we do not model. Hand back to fillPassword's own wait
|
|
118
|
+
// instead of re-typing blindly into a page we cannot identify.
|
|
119
|
+
if (!(await this.usernameInput.locator.isVisible().catch(() => false))) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
console.warn(`Email step did not advance to the password step (attempt ${attempt}/${maxAttempts}); re-submitting`);
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`Login form stayed on the email step after ${maxAttempts} submissions`);
|
|
125
|
+
}
|
|
82
126
|
async loginWithTestUser(credentials = {}) {
|
|
83
127
|
const { username = process.env.C8_USERNAME_TEST, password = process.env.C8_PASSWORD_TEST, } = credentials;
|
|
84
128
|
// Skip navigation if the auth0 login page is already loaded — re-navigating
|
|
@@ -106,8 +150,7 @@ class LoginPage {
|
|
|
106
150
|
(await this.passwordInput.isVisible({ timeout: 5000 }).catch(() => false));
|
|
107
151
|
if (!onPasswordStep) {
|
|
108
152
|
await (0, test_1.expect)(this.usernameInput.locator).toBeVisible({ timeout: 120000 });
|
|
109
|
-
await this.
|
|
110
|
-
await this.clickContinueButton();
|
|
153
|
+
await this.submitUsernameStep(username);
|
|
111
154
|
}
|
|
112
155
|
await this.fillPassword(password);
|
|
113
156
|
await (0, test_1.expect)(this.loginButton).toBeVisible({ timeout: 120000 });
|
|
@@ -36,9 +36,29 @@ class PlayPage {
|
|
|
36
36
|
await (0, test_1.expect)(this.completeJobButton).toBeVisible({
|
|
37
37
|
timeout: maxWaitTimeSeconds,
|
|
38
38
|
});
|
|
39
|
+
// Modeler keeps the overlay mounted but disabled while a previous job
|
|
40
|
+
// action is still in flight or waiting for the polled statistics to catch
|
|
41
|
+
// up (camunda-hub test-studio JobCompleteButton -> useAsyncActionState), so
|
|
42
|
+
// "visible" alone does not mean "ready to click". Wait for enabled too.
|
|
43
|
+
await (0, test_1.expect)(this.completeJobButton).toBeEnabled({
|
|
44
|
+
timeout: maxWaitTimeSeconds,
|
|
45
|
+
});
|
|
39
46
|
}
|
|
40
47
|
async clickCompleteJobButton() {
|
|
41
|
-
|
|
48
|
+
// The complete-job action is a bpmn-js canvas overlay pinned to the active
|
|
49
|
+
// element, so it is re-laid-out (and briefly detached) whenever the diagram
|
|
50
|
+
// re-renders -- which it does on every job/instance update arriving from
|
|
51
|
+
// the cluster. A bare click that lands in that window silently resolves
|
|
52
|
+
// against a stale node, the job is never completed, and the failure only
|
|
53
|
+
// surfaces one call later as waitForNextElementToBeActive timing out on the
|
|
54
|
+
// next element that consequently never became active. Retry the click
|
|
55
|
+
// against a fresh visibility + clickability check instead.
|
|
56
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.completeJobButton, {
|
|
57
|
+
totalTimeout: maxWaitTimeSeconds,
|
|
58
|
+
visibilityTimeout: 30000,
|
|
59
|
+
maxRetries: 3,
|
|
60
|
+
retryDelayMs: 3000,
|
|
61
|
+
});
|
|
42
62
|
}
|
|
43
63
|
async clickStartInstanceButton() {
|
|
44
64
|
const startTrigger = this.configureTestPanelStartButton
|
|
@@ -88,8 +108,38 @@ class PlayPage {
|
|
|
88
108
|
}
|
|
89
109
|
}
|
|
90
110
|
async waitForNextElementToBeActive(historyItem) {
|
|
91
|
-
|
|
111
|
+
// clickCompleteJobButton now retries a click that *throws*, but a click
|
|
112
|
+
// swallowed by a re-laid-out overlay resolves successfully without
|
|
113
|
+
// completing anything, so the retry helper alone cannot catch it. The only
|
|
114
|
+
// symptom is this locator timing out on an element that never became
|
|
115
|
+
// active -- exactly the nightly failure on 'service-task*'.
|
|
116
|
+
//
|
|
117
|
+
// Once the next element has stayed absent well past any normal Play advance
|
|
118
|
+
// latency AND a complete-job overlay is still sitting enabled on the
|
|
119
|
+
// canvas, the previous click demonstrably did not land, so re-issue it. The
|
|
120
|
+
// grace period matters: it keeps us from completing the job of the very
|
|
121
|
+
// element we are waiting for during the brief window where its overlay has
|
|
122
|
+
// rendered but its history entry has not. The assertion is unchanged --
|
|
123
|
+
// this still only passes on a visible history entry.
|
|
124
|
+
const element = this.page.getByText(new RegExp(`^${historyItem}`, 'i'));
|
|
125
|
+
const recoveryGraceMs = 60000;
|
|
126
|
+
const startedAt = Date.now();
|
|
127
|
+
await (0, test_1.expect)(async () => {
|
|
128
|
+
if (Date.now() - startedAt > recoveryGraceMs) {
|
|
129
|
+
const isPreviousJobStillPending = (await this.completeJobButton.isVisible().catch(() => false)) &&
|
|
130
|
+
(await this.completeJobButton.isEnabled().catch(() => false));
|
|
131
|
+
// Re-check immediately before clicking: if the element became active
|
|
132
|
+
// in the meantime, the pending overlay is its own and must be left
|
|
133
|
+
// alone for the caller's next step.
|
|
134
|
+
if (isPreviousJobStillPending &&
|
|
135
|
+
!(await element.isVisible().catch(() => false))) {
|
|
136
|
+
await this.completeJobButton.click({ timeout: 30000 });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
await (0, test_1.expect)(element).toBeVisible({ timeout: 15000 });
|
|
140
|
+
}).toPass({
|
|
92
141
|
timeout: maxWaitTimeSeconds,
|
|
142
|
+
intervals: [5000, 10000, 15000],
|
|
93
143
|
});
|
|
94
144
|
}
|
|
95
145
|
async waitForProcessToBeCompleted() {
|