@camunda/e2e-test-suite 0.0.615 → 0.0.617
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.
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LoginPage = void 0;
|
|
4
|
-
const test_1 = require("@playwright/test");
|
|
5
4
|
class LoginPage {
|
|
6
5
|
page;
|
|
7
6
|
usernameInput;
|
|
@@ -17,18 +16,31 @@ class LoginPage {
|
|
|
17
16
|
this.page = page;
|
|
18
17
|
this.usernameInput = page.getByLabel('Username or email');
|
|
19
18
|
this.passwordInput = page.getByLabel('Password');
|
|
20
|
-
this.loginButton = page.getByRole('button', { name:
|
|
19
|
+
this.loginButton = page.getByRole('button', { name: /log in|sign in/i });
|
|
21
20
|
this.coreUsernameInput = page.getByPlaceholder('Username');
|
|
22
21
|
this.corePasswordInput = page.getByPlaceholder('Password');
|
|
23
22
|
this.coreLoginButton = page.getByRole('button', { name: 'Login' });
|
|
24
23
|
this.keycloakBanner = page.locator('body#keycloak-bg[data-page-id="login-login"]');
|
|
25
24
|
}
|
|
26
25
|
async detectLoginForm() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
// If we're on an app page that's about to redirect to KC, wait for
|
|
27
|
+
// the navigation to complete before looking for login form elements.
|
|
28
|
+
const url = this.page.url();
|
|
29
|
+
if (!url.includes('/auth/') && !url.includes('/login-callback')) {
|
|
30
|
+
await this.page
|
|
31
|
+
.waitForURL((u) => u.pathname.includes('/auth/'), {
|
|
32
|
+
timeout: 30000,
|
|
33
|
+
waitUntil: 'domcontentloaded',
|
|
34
|
+
})
|
|
35
|
+
.catch(() => { });
|
|
36
|
+
}
|
|
37
|
+
const kcUsername = this.page.locator('#username');
|
|
38
|
+
const anyForm = kcUsername
|
|
39
|
+
.or(this.coreLoginButton)
|
|
40
|
+
.or(this.keycloakBanner)
|
|
41
|
+
.first();
|
|
42
|
+
await anyForm.waitFor({ state: 'visible', timeout: 30000 });
|
|
43
|
+
if (await this.coreLoginButton.isVisible()) {
|
|
32
44
|
this.useCore = true;
|
|
33
45
|
}
|
|
34
46
|
else if (await this.keycloakBanner.isVisible()) {
|
|
@@ -78,17 +90,6 @@ class LoginPage {
|
|
|
78
90
|
async login(username, password) {
|
|
79
91
|
try {
|
|
80
92
|
await this.detectLoginForm();
|
|
81
|
-
if (await this.page
|
|
82
|
-
.locator('body#keycloak-bg[data-page-id="login-login"]')
|
|
83
|
-
.isVisible()) {
|
|
84
|
-
await (0, test_1.expect)(this.keycloakBanner).toBeVisible({ timeout: 15000 });
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
const activeLoginButton = this.useCore
|
|
88
|
-
? this.coreLoginButton
|
|
89
|
-
: this.loginButton;
|
|
90
|
-
await (0, test_1.expect)(activeLoginButton).toBeVisible({ timeout: 15000 });
|
|
91
|
-
}
|
|
92
93
|
}
|
|
93
94
|
catch (error) {
|
|
94
95
|
return;
|
|
@@ -77,7 +77,13 @@ class NavigationPage {
|
|
|
77
77
|
.first();
|
|
78
78
|
await (0, test_1.expect)(loginEntryPoint).toBeVisible({ timeout: 60000 });
|
|
79
79
|
const isBannerVisible = await banner.isVisible().catch(() => false);
|
|
80
|
-
|
|
80
|
+
// Guard against the Modeler SPA rendering its header on the login
|
|
81
|
+
// page before the OAuth redirect fires. If the banner is visible
|
|
82
|
+
// but the URL is still a login/auth path, the user is NOT actually
|
|
83
|
+
// authenticated — wait for the real login form to appear.
|
|
84
|
+
const currentUrl = this.page.url();
|
|
85
|
+
const onLoginPath = currentUrl.includes('/login') || currentUrl.includes('/auth/');
|
|
86
|
+
if (isBannerVisible && !onLoginPath) {
|
|
81
87
|
const loginVisible = await loginPage.usernameInput.isVisible();
|
|
82
88
|
if (loginVisible) {
|
|
83
89
|
await loginPage.login(username, password);
|
|
@@ -21,6 +21,7 @@ declare class OperateProcessInstancePage {
|
|
|
21
21
|
assertEitherIncidentOrCompletedIconVisible(): Promise<string>;
|
|
22
22
|
assertProcessCompleteStatusWithRetry(timeout?: number, maxRetries?: number): Promise<void>;
|
|
23
23
|
assertProcessVariableContainsText(variableName: string, text: string): Promise<void>;
|
|
24
|
+
private captureVariableJsonValue;
|
|
24
25
|
assertResultVariableVisibleWithRetry(variableName: string): Promise<void>;
|
|
25
26
|
assertActiveTokenIsPresent(): Promise<void>;
|
|
26
27
|
assertVariablesListVisible(timeout?: number): Promise<void>;
|
|
@@ -162,8 +162,21 @@ class OperateProcessInstancePage {
|
|
|
162
162
|
await (0, test_1.expect)(this.page.getByTestId(`variable-${variableName}`)).toContainText(text, { timeout: 8000 });
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
|
-
catch (
|
|
166
|
-
|
|
165
|
+
catch (uiError) {
|
|
166
|
+
// Document-typed variables render as a compact summary
|
|
167
|
+
// (e.g. "3 documents" or "<docId> <size>") that omits metadata
|
|
168
|
+
// such as filename, storeId, and contentType. Fall back to the
|
|
169
|
+
// raw JSON value from the variables API response.
|
|
170
|
+
try {
|
|
171
|
+
const value = await this.captureVariableJsonValue(variableName);
|
|
172
|
+
if (value != null && value.includes(text)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (apiError) {
|
|
177
|
+
console.log(`captureVariableJsonValue for ${variableName} attempt ${attempt + 1}/${maxRetries} failed: ${apiError}`);
|
|
178
|
+
}
|
|
179
|
+
console.log(`assertProcessVariableContainsText attempt ${attempt + 1}/${maxRetries} failed: ${uiError}`);
|
|
167
180
|
if (attempt < maxRetries - 1) {
|
|
168
181
|
await this.page.reload({ timeout: 10000 }).catch(() => { });
|
|
169
182
|
await this.page.waitForLoadState('domcontentloaded').catch(() => { });
|
|
@@ -172,6 +185,34 @@ class OperateProcessInstancePage {
|
|
|
172
185
|
}
|
|
173
186
|
throw new Error(`Failed to assert variable ${variableName} contains "${text}" after ${maxRetries} attempts.`);
|
|
174
187
|
}
|
|
188
|
+
async captureVariableJsonValue(variableName) {
|
|
189
|
+
let capturedValue = null;
|
|
190
|
+
const responsePromise = this.page
|
|
191
|
+
.waitForResponse(async (resp) => {
|
|
192
|
+
if (!resp.url().includes('/v2/variables/search') ||
|
|
193
|
+
resp.status() !== 200) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const body = await resp.text();
|
|
198
|
+
const data = JSON.parse(body);
|
|
199
|
+
const item = data?.items?.find((i) => i.name === variableName);
|
|
200
|
+
if (item?.value != null) {
|
|
201
|
+
capturedValue = String(item.value);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}, { timeout: 15000 })
|
|
210
|
+
.catch(() => null);
|
|
211
|
+
await this.page.reload({ timeout: 10000 }).catch(() => { });
|
|
212
|
+
await this.page.waitForLoadState('domcontentloaded').catch(() => { });
|
|
213
|
+
await responsePromise;
|
|
214
|
+
return capturedValue;
|
|
215
|
+
}
|
|
175
216
|
async assertResultVariableVisibleWithRetry(variableName) {
|
|
176
217
|
const maxRetries = 3;
|
|
177
218
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|