@owlmeans/test-ui 0.1.18-rc.10 → 0.1.18-rc.11
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/build/supervisor.d.ts +35 -0
- package/build/supervisor.d.ts.map +1 -1
- package/build/supervisor.js +49 -5
- package/build/supervisor.js.map +1 -1
- package/package.json +1 -1
- package/src/supervisor.ts +70 -6
package/build/supervisor.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export declare const pregenerateAuthToken: (opts: PregenerateAuthTokenOptions) =
|
|
|
34
34
|
*/
|
|
35
35
|
export declare const loginViaDispatcher: (page: Page, baseUrl: string, token: string, opts?: {
|
|
36
36
|
dispatcherPath?: string;
|
|
37
|
+
waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle';
|
|
37
38
|
}) => Promise<void>;
|
|
38
39
|
export interface SupervisorApiAuthOptions {
|
|
39
40
|
/** Auth-manager API base URL (the backend serving /authentication/* and /authenticate). */
|
|
@@ -78,9 +79,43 @@ export interface SupervisorFormLoginOptions {
|
|
|
78
79
|
/** Predicate/URL fragment that signals a successful landing (default: leaving the login path). */
|
|
79
80
|
expectPath?: string;
|
|
80
81
|
timeout?: number;
|
|
82
|
+
/**
|
|
83
|
+
* What counts as "arrived" for the initial navigation. Defaults to `domcontentloaded`.
|
|
84
|
+
*
|
|
85
|
+
* See the note on `MountOptions.waitUntil`: `load` is playwright's default and is wrong for a
|
|
86
|
+
* real application, because it waits for every subresource — a tag manager, an analytics beacon
|
|
87
|
+
* or any pixel that never settles holds it open until the navigation times out, with the login
|
|
88
|
+
* form rendered and fillable the whole time.
|
|
89
|
+
*/
|
|
90
|
+
waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle';
|
|
91
|
+
/**
|
|
92
|
+
* What to do about a cookie-consent dialog covering the form. Defaults to `accept`, because an
|
|
93
|
+
* OwlMeans app will not start an authentication flow until consent is answered — see
|
|
94
|
+
* {@link acceptConsent}. Pass `ignore` only in a spec that answers the dialog itself.
|
|
95
|
+
*/
|
|
96
|
+
consent?: 'accept' | 'ignore';
|
|
81
97
|
/** When set, capture the filled login form (`supervisor-login-form.png`) before submit. */
|
|
82
98
|
screenshotDir?: string;
|
|
83
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Answer the cookie-consent dialog if one is up, so the page underneath can be driven.
|
|
102
|
+
*
|
|
103
|
+
* An OwlMeans app asks for consent BEFORE it will start an authentication flow, and the dialog is
|
|
104
|
+
* a modal with no dismissal — by design, since a decision is what the gate is waiting for. For a
|
|
105
|
+
* test that means the login form renders, resolves, reports itself "visible, enabled and stable"
|
|
106
|
+
* and still cannot be clicked, because the overlay intercepts the pointer. Playwright retries for
|
|
107
|
+
* the full timeout and then blames the button, which is the most misleading failure in this file:
|
|
108
|
+
* nothing is wrong with the form.
|
|
109
|
+
*
|
|
110
|
+
* Accepting is the honest default for a login helper — the flow it is about to drive is exactly
|
|
111
|
+
* what the dialog is gating — but it is best-effort: an app without the widget, or one where the
|
|
112
|
+
* visitor already decided, simply has no dialog and this returns at once.
|
|
113
|
+
*
|
|
114
|
+
* @returns whether a dialog was actually answered.
|
|
115
|
+
*/
|
|
116
|
+
export declare const acceptConsent: (page: Page, opts?: {
|
|
117
|
+
timeout?: number;
|
|
118
|
+
}) => Promise<boolean>;
|
|
84
119
|
/**
|
|
85
120
|
* Drive the PK-based supervisor login FORM end-to-end (the faithful path that
|
|
86
121
|
* exercises the real supervisor plugin + registration): navigate to the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAyB,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AASrE,MAAM,WAAW,2BAA2B;IAC1C,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,SAAgB,2BAA2B,KAAG,OAAO,CAAC,MAAM,CAe5F,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SACvB,IAAI,WAAW,MAAM,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAyB,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AASrE,MAAM,WAAW,2BAA2B;IAC1C,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,SAAgB,2BAA2B,KAAG,OAAO,CAAC,MAAM,CAe5F,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SACvB,IAAI,WAAW,MAAM,SAAS,MAAM,SACnC;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;CAAE,KACrG,OAAO,CAAC,IAAI,CAQd,CAAA;AAED,MAAM,WAAW,wBAAwB;IACvC,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAA;IACV,kDAAkD;IAClD,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAgBD;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,SACjC,wBAAwB,KAC7B,OAAO,CAAC,MAAM,CAkChB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,SAAgB,IAAI,OAAO,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,CAM1F,CAAA;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAA;IACV,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kGAAkG;IAClG,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;IAClE;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC7B,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAID;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,SAClB,IAAI,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KACtC,OAAO,CAAC,OAAO,CAajB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,SAC3B,IAAI,QAAQ,0BAA0B,KAC3C,OAAO,CAAC,IAAI,CAiCd,CAAA"}
|
package/build/supervisor.js
CHANGED
|
@@ -39,7 +39,8 @@ export const loginViaDispatcher = async (page, baseUrl, token, opts) => {
|
|
|
39
39
|
const path = opts?.dispatcherPath ?? '/dispatcher';
|
|
40
40
|
const url = new URL(path, baseUrl);
|
|
41
41
|
url.searchParams.set('token', token);
|
|
42
|
-
|
|
42
|
+
// `domcontentloaded`, not playwright's `load` default — see the note on `MountOptions.waitUntil`.
|
|
43
|
+
await page.goto(url.toString(), { waitUntil: opts?.waitUntil ?? 'domcontentloaded' });
|
|
43
44
|
// Wait until the dispatcher has navigated away (token consumed).
|
|
44
45
|
await page.waitForURL(u => !u.pathname.startsWith(path), { timeout: 30_000 });
|
|
45
46
|
};
|
|
@@ -106,6 +107,36 @@ export const saveScreenshot = async (page, dir, name) => {
|
|
|
106
107
|
return file;
|
|
107
108
|
};
|
|
108
109
|
const DEFAULT_SUPERVISOR_PATH = '/authentication/login/pk-supervisor';
|
|
110
|
+
/**
|
|
111
|
+
* Answer the cookie-consent dialog if one is up, so the page underneath can be driven.
|
|
112
|
+
*
|
|
113
|
+
* An OwlMeans app asks for consent BEFORE it will start an authentication flow, and the dialog is
|
|
114
|
+
* a modal with no dismissal — by design, since a decision is what the gate is waiting for. For a
|
|
115
|
+
* test that means the login form renders, resolves, reports itself "visible, enabled and stable"
|
|
116
|
+
* and still cannot be clicked, because the overlay intercepts the pointer. Playwright retries for
|
|
117
|
+
* the full timeout and then blames the button, which is the most misleading failure in this file:
|
|
118
|
+
* nothing is wrong with the form.
|
|
119
|
+
*
|
|
120
|
+
* Accepting is the honest default for a login helper — the flow it is about to drive is exactly
|
|
121
|
+
* what the dialog is gating — but it is best-effort: an app without the widget, or one where the
|
|
122
|
+
* visitor already decided, simply has no dialog and this returns at once.
|
|
123
|
+
*
|
|
124
|
+
* @returns whether a dialog was actually answered.
|
|
125
|
+
*/
|
|
126
|
+
export const acceptConsent = async (page, opts) => {
|
|
127
|
+
const dialog = page.locator('[data-consent-dialog]');
|
|
128
|
+
try {
|
|
129
|
+
await dialog.waitFor({ state: 'visible', timeout: opts?.timeout ?? 5_000 });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
// Accept-all rather than essential-only: a test asserting a narrower decision should make that
|
|
135
|
+
// decision itself, and silently choosing the minimum here would hide it from the spec.
|
|
136
|
+
await page.locator('[data-consent-accept-all]').click();
|
|
137
|
+
await dialog.waitFor({ state: 'detached', timeout: opts?.timeout ?? 5_000 });
|
|
138
|
+
return true;
|
|
139
|
+
};
|
|
109
140
|
/**
|
|
110
141
|
* Drive the PK-based supervisor login FORM end-to-end (the faithful path that
|
|
111
142
|
* exercises the real supervisor plugin + registration): navigate to the
|
|
@@ -115,13 +146,26 @@ const DEFAULT_SUPERVISOR_PATH = '/authentication/login/pk-supervisor';
|
|
|
115
146
|
export const loginViaSupervisorForm = async (page, opts) => {
|
|
116
147
|
const path = opts.path ?? DEFAULT_SUPERVISOR_PATH;
|
|
117
148
|
const timeout = opts.timeout ?? 60_000;
|
|
118
|
-
await page.goto(new URL(path, opts.baseUrl).toString()
|
|
119
|
-
|
|
120
|
-
|
|
149
|
+
await page.goto(new URL(path, opts.baseUrl).toString(), {
|
|
150
|
+
waitUntil: opts.waitUntil ?? 'domcontentloaded', timeout
|
|
151
|
+
});
|
|
152
|
+
// Wait for the app to MOUNT before deciding whether there is a dialog to answer. `domcontentloaded`
|
|
153
|
+
// returns while the SPA is still booting, and on a dev server that boot is occasionally tens of
|
|
154
|
+
// seconds — so a short consent probe reports "no dialog", the dialog then mounts over the form,
|
|
155
|
+
// and the fill below waits out its own timeout on a field that is present but covered. Either
|
|
156
|
+
// marker proves the app is up: the dialog, or the form itself when consent was already given.
|
|
157
|
+
await page.locator('[data-consent-dialog], [data-testid="supervisor-user-id"]')
|
|
158
|
+
.first().waitFor({ state: 'visible', timeout });
|
|
159
|
+
if (opts.consent !== 'ignore')
|
|
160
|
+
await acceptConsent(page, { timeout });
|
|
161
|
+
// The form renders only AFTER the dialog is dismissed, so these carry the caller's budget rather
|
|
162
|
+
// than playwright's 30s default — the whole point of accepting an explicit `timeout` here.
|
|
163
|
+
await page.getByTestId('supervisor-user-id').fill(opts.userId, { timeout });
|
|
164
|
+
await page.getByTestId('supervisor-pk').fill(opts.pk, { timeout });
|
|
121
165
|
if (opts.screenshotDir != null) {
|
|
122
166
|
await saveScreenshot(page, opts.screenshotDir, 'supervisor-login-form');
|
|
123
167
|
}
|
|
124
|
-
await page.getByTestId('supervisor-submit').click();
|
|
168
|
+
await page.getByTestId('supervisor-submit').click({ timeout });
|
|
125
169
|
if (opts.expectPath != null) {
|
|
126
170
|
await page.waitForURL(url => url.toString().includes(opts.expectPath), { timeout });
|
|
127
171
|
}
|
package/build/supervisor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGzC,OAAO,EACL,iBAAiB,EAAE,QAAQ,IAAI,IAAI,EAAE,kBAAkB,EAAE,sBAAsB,EAChF,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAmBhD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EAAE,IAAiC,EAAmB,EAAE;IAC/F,MAAM,IAAI,GAAS;QACjB,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM;QACxC,UAAU,EAAE,IAAI,CAAC,QAAQ;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC;QAC5B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI;QAC5B,IAAI,EAAE,iBAAiB,CAAC,iBAAiB;QACzC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;QACzB,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,IAAI,IAAI,EAAE;KACtB,CAAA;IAED,OAAO,UAAU,CAAC,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;AACpD,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,IAAU,EAAE,OAAe,EAAE,KAAa,
|
|
1
|
+
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/supervisor.ts
CHANGED
|
@@ -60,12 +60,14 @@ export const pregenerateAuthToken = async (opts: PregenerateAuthTokenOptions): P
|
|
|
60
60
|
* (e.g. to force an external IdP) should use {@link loginViaSupervisorForm}.
|
|
61
61
|
*/
|
|
62
62
|
export const loginViaDispatcher = async (
|
|
63
|
-
page: Page, baseUrl: string, token: string,
|
|
63
|
+
page: Page, baseUrl: string, token: string,
|
|
64
|
+
opts?: { dispatcherPath?: string, waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle' }
|
|
64
65
|
): Promise<void> => {
|
|
65
66
|
const path = opts?.dispatcherPath ?? '/dispatcher'
|
|
66
67
|
const url = new URL(path, baseUrl)
|
|
67
68
|
url.searchParams.set('token', token)
|
|
68
|
-
|
|
69
|
+
// `domcontentloaded`, not playwright's `load` default — see the note on `MountOptions.waitUntil`.
|
|
70
|
+
await page.goto(url.toString(), { waitUntil: opts?.waitUntil ?? 'domcontentloaded' })
|
|
69
71
|
// Wait until the dispatcher has navigated away (token consumed).
|
|
70
72
|
await page.waitForURL(u => !u.pathname.startsWith(path), { timeout: 30_000 })
|
|
71
73
|
}
|
|
@@ -168,12 +170,60 @@ export interface SupervisorFormLoginOptions {
|
|
|
168
170
|
/** Predicate/URL fragment that signals a successful landing (default: leaving the login path). */
|
|
169
171
|
expectPath?: string
|
|
170
172
|
timeout?: number
|
|
173
|
+
/**
|
|
174
|
+
* What counts as "arrived" for the initial navigation. Defaults to `domcontentloaded`.
|
|
175
|
+
*
|
|
176
|
+
* See the note on `MountOptions.waitUntil`: `load` is playwright's default and is wrong for a
|
|
177
|
+
* real application, because it waits for every subresource — a tag manager, an analytics beacon
|
|
178
|
+
* or any pixel that never settles holds it open until the navigation times out, with the login
|
|
179
|
+
* form rendered and fillable the whole time.
|
|
180
|
+
*/
|
|
181
|
+
waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle'
|
|
182
|
+
/**
|
|
183
|
+
* What to do about a cookie-consent dialog covering the form. Defaults to `accept`, because an
|
|
184
|
+
* OwlMeans app will not start an authentication flow until consent is answered — see
|
|
185
|
+
* {@link acceptConsent}. Pass `ignore` only in a spec that answers the dialog itself.
|
|
186
|
+
*/
|
|
187
|
+
consent?: 'accept' | 'ignore'
|
|
171
188
|
/** When set, capture the filled login form (`supervisor-login-form.png`) before submit. */
|
|
172
189
|
screenshotDir?: string
|
|
173
190
|
}
|
|
174
191
|
|
|
175
192
|
const DEFAULT_SUPERVISOR_PATH = '/authentication/login/pk-supervisor'
|
|
176
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Answer the cookie-consent dialog if one is up, so the page underneath can be driven.
|
|
196
|
+
*
|
|
197
|
+
* An OwlMeans app asks for consent BEFORE it will start an authentication flow, and the dialog is
|
|
198
|
+
* a modal with no dismissal — by design, since a decision is what the gate is waiting for. For a
|
|
199
|
+
* test that means the login form renders, resolves, reports itself "visible, enabled and stable"
|
|
200
|
+
* and still cannot be clicked, because the overlay intercepts the pointer. Playwright retries for
|
|
201
|
+
* the full timeout and then blames the button, which is the most misleading failure in this file:
|
|
202
|
+
* nothing is wrong with the form.
|
|
203
|
+
*
|
|
204
|
+
* Accepting is the honest default for a login helper — the flow it is about to drive is exactly
|
|
205
|
+
* what the dialog is gating — but it is best-effort: an app without the widget, or one where the
|
|
206
|
+
* visitor already decided, simply has no dialog and this returns at once.
|
|
207
|
+
*
|
|
208
|
+
* @returns whether a dialog was actually answered.
|
|
209
|
+
*/
|
|
210
|
+
export const acceptConsent = async (
|
|
211
|
+
page: Page, opts?: { timeout?: number }
|
|
212
|
+
): Promise<boolean> => {
|
|
213
|
+
const dialog = page.locator('[data-consent-dialog]')
|
|
214
|
+
try {
|
|
215
|
+
await dialog.waitFor({ state: 'visible', timeout: opts?.timeout ?? 5_000 })
|
|
216
|
+
} catch {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
// Accept-all rather than essential-only: a test asserting a narrower decision should make that
|
|
220
|
+
// decision itself, and silently choosing the minimum here would hide it from the spec.
|
|
221
|
+
await page.locator('[data-consent-accept-all]').click()
|
|
222
|
+
await dialog.waitFor({ state: 'detached', timeout: opts?.timeout ?? 5_000 })
|
|
223
|
+
|
|
224
|
+
return true
|
|
225
|
+
}
|
|
226
|
+
|
|
177
227
|
/**
|
|
178
228
|
* Drive the PK-based supervisor login FORM end-to-end (the faithful path that
|
|
179
229
|
* exercises the real supervisor plugin + registration): navigate to the
|
|
@@ -185,16 +235,30 @@ export const loginViaSupervisorForm = async (
|
|
|
185
235
|
): Promise<void> => {
|
|
186
236
|
const path = opts.path ?? DEFAULT_SUPERVISOR_PATH
|
|
187
237
|
const timeout = opts.timeout ?? 60_000
|
|
188
|
-
await page.goto(new URL(path, opts.baseUrl).toString()
|
|
238
|
+
await page.goto(new URL(path, opts.baseUrl).toString(), {
|
|
239
|
+
waitUntil: opts.waitUntil ?? 'domcontentloaded', timeout
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
// Wait for the app to MOUNT before deciding whether there is a dialog to answer. `domcontentloaded`
|
|
243
|
+
// returns while the SPA is still booting, and on a dev server that boot is occasionally tens of
|
|
244
|
+
// seconds — so a short consent probe reports "no dialog", the dialog then mounts over the form,
|
|
245
|
+
// and the fill below waits out its own timeout on a field that is present but covered. Either
|
|
246
|
+
// marker proves the app is up: the dialog, or the form itself when consent was already given.
|
|
247
|
+
await page.locator('[data-consent-dialog], [data-testid="supervisor-user-id"]')
|
|
248
|
+
.first().waitFor({ state: 'visible', timeout })
|
|
249
|
+
|
|
250
|
+
if (opts.consent !== 'ignore') await acceptConsent(page, { timeout })
|
|
189
251
|
|
|
190
|
-
|
|
191
|
-
|
|
252
|
+
// The form renders only AFTER the dialog is dismissed, so these carry the caller's budget rather
|
|
253
|
+
// than playwright's 30s default — the whole point of accepting an explicit `timeout` here.
|
|
254
|
+
await page.getByTestId('supervisor-user-id').fill(opts.userId, { timeout })
|
|
255
|
+
await page.getByTestId('supervisor-pk').fill(opts.pk, { timeout })
|
|
192
256
|
|
|
193
257
|
if (opts.screenshotDir != null) {
|
|
194
258
|
await saveScreenshot(page, opts.screenshotDir, 'supervisor-login-form')
|
|
195
259
|
}
|
|
196
260
|
|
|
197
|
-
await page.getByTestId('supervisor-submit').click()
|
|
261
|
+
await page.getByTestId('supervisor-submit').click({ timeout })
|
|
198
262
|
|
|
199
263
|
if (opts.expectPath != null) {
|
|
200
264
|
await page.waitForURL(url => url.toString().includes(opts.expectPath!), { timeout })
|