@owlmeans/test-ui 0.1.18-rc.35 → 0.1.18-rc.37
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/README.md +1 -1
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/testing-ui/SKILL.md +31 -3
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/marketing-consent.d.ts +31 -0
- package/build/marketing-consent.d.ts.map +1 -0
- package/build/marketing-consent.js +53 -0
- package/build/marketing-consent.js.map +1 -0
- package/build/supervisor.d.ts +12 -0
- package/build/supervisor.d.ts.map +1 -1
- package/build/supervisor.js +5 -0
- package/build/supervisor.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +1 -0
- package/src/marketing-consent.ts +72 -0
- package/src/supervisor.ts +20 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
58
58
|
your project's skill store (`.agents/skills/`):
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
61
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.36
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/test-ui",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.37",
|
|
5
|
+
"generatedAt": "2026-09-22T22:24:39.521Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -6,7 +6,7 @@ description: Category-D component-level acceptance tests for OwlMeans Common UI
|
|
|
6
6
|
|
|
7
7
|
# UI Acceptance Tests — Category D (bun test + Playwright as a library)
|
|
8
8
|
|
|
9
|
-
**Install:** `"@owlmeans/test-ui": "^0.1.18-rc.
|
|
9
|
+
**Install:** `"@owlmeans/test-ui": "^0.1.18-rc.37"` in `devDependencies`
|
|
10
10
|
|
|
11
11
|
`@owlmeans/test-ui` depends on `playwright`, so the browser library arrives with it; add
|
|
12
12
|
`playwright` to `devDependencies` as well when a spec imports a launcher itself. The Vite harness
|
|
@@ -70,8 +70,9 @@ sits behind auth without hand-rolling a token:
|
|
|
70
70
|
|---|---|
|
|
71
71
|
| `pregenerateAuthToken(opts)` | Mint an `ED25519-BASIC-TOKEN …` bearer offline from a trusted private key — no browser, no round trip. Options: `{ userId, pk, scopes?, role?, entityId?, profileId?, source? }`. |
|
|
72
72
|
| `authenticateViaSupervisorApi(opts)` | Drive the live PK supervisor flow over the backend API (init → sign → authenticate → dispatch), registering the user on first use. Options: `{ apiBaseUrl, userId, pk, paths?, fetchImpl? }`. |
|
|
73
|
-
| `loginViaDispatcher(page, baseUrl, token, opts?)` | Inject a bearer through the standard `/dispatcher?token=…` route
|
|
74
|
-
| `loginViaSupervisorForm(page, opts)` | Drive the real login form end-to-end — navigate, answer consent, fill user id + key, submit, wait for the landing. Options: `{ baseUrl, userId, pk, path?, expectPath?, timeout?, waitUntil?, consent?, screenshotDir? }`. |
|
|
73
|
+
| `loginViaDispatcher(page, baseUrl, token, opts?)` | Inject a bearer through the standard `/dispatcher?token=…` route, wait for the app to navigate away, then answer marketing consent (see below). Options: `{ dispatcherPath?, waitUntil?, marketingConsent? }`. |
|
|
74
|
+
| `loginViaSupervisorForm(page, opts)` | Drive the real login form end-to-end — navigate, answer consent, fill user id + key, submit, wait for the landing, then answer marketing consent (see below). Options: `{ baseUrl, userId, pk, path?, expectPath?, timeout?, waitUntil?, consent?, screenshotDir?, marketingConsent? }`. |
|
|
75
|
+
| `answerMarketingConsent(page, opts?)` | Answer the `@owlmeans/web-marketing-consent` full-page step if it is shown right after login. Options: `{ accept?: 'all' \| 'none', timeout? }`, default `accept: 'all'`. Returns whether the step was actually shown and answered — `false` means it never appeared. |
|
|
75
76
|
|
|
76
77
|
These need a project whose backend trusts the private key they sign with, so they belong to specs
|
|
77
78
|
that run against a real app rather than a mounted component. See `[[supervisor-auth]]` for the
|
|
@@ -309,3 +310,30 @@ app that ships no consent widget the login helper therefore sits for a full minu
|
|
|
309
310
|
the first field, with nothing on screen to explain it. Drive such an app with `consent: 'ignore'`,
|
|
310
311
|
which skips the call entirely; that is also the switch a spec flips when it wants to answer the
|
|
311
312
|
dialog itself.
|
|
313
|
+
|
|
314
|
+
## `answerMarketingConsent` — cheap absence detection, unlike the cookie dialog
|
|
315
|
+
|
|
316
|
+
`loginViaSupervisorForm` and `loginViaDispatcher` both call `answerMarketingConsent(page, { accept:
|
|
317
|
+
'all' })` right after they confirm the app has navigated away from login/dispatch, and both default
|
|
318
|
+
`marketingConsent` (their own option, not `answerMarketingConsent`'s) to `'save'` — pass
|
|
319
|
+
`'ignore'` to skip the call in a spec that drives the step itself. `@owlmeans/web-marketing-consent`
|
|
320
|
+
renders a full-page step, when shown, with `[data-marketing-consent]` (root),
|
|
321
|
+
`[data-marketing-consent-all]` (select-all), `[data-marketing-consent-item="<key>"]` (per-consent),
|
|
322
|
+
`[data-marketing-consent-save]`, `[data-marketing-consent-skip]` (shown only after a save error) and
|
|
323
|
+
`[data-marketing-consent-error]`.
|
|
324
|
+
|
|
325
|
+
Unlike `acceptConsent`, whose `false` return deliberately costs the full wait (see above),
|
|
326
|
+
`answerMarketingConsent` does NOT pay the full `timeout` (default `6_000`) when the step is absent.
|
|
327
|
+
The screen is opt-in per app and most applications and environments don't have it wired in yet, and this helper
|
|
328
|
+
is called unconditionally by both login helpers — a full-timeout tax on every login, on every app,
|
|
329
|
+
whether or not the screen exists, would be an unacceptable regression to every existing suite. It
|
|
330
|
+
instead races `[data-marketing-consent]` against the generic "the app has already landed" /
|
|
331
|
+
"still on the login screen" markers the login helpers themselves already rely on (`#app-prompt`,
|
|
332
|
+
`[data-login-method]`) via the same `locator('a, b, c').first().waitFor({ state: 'visible' })` idiom
|
|
333
|
+
`loginViaSupervisorForm` uses for its own dialog-vs-form race. By the time a login helper calls this,
|
|
334
|
+
one of those markers has typically already rendered, so on an app without the screen the race
|
|
335
|
+
resolves as soon as it does — no fixed sleep, and no meaningful added latency over what the login
|
|
336
|
+
helper already paid to get there. Returns `false` when the step never appeared; `true` when it did,
|
|
337
|
+
whether the save succeeded or fell back to clicking `[data-marketing-consent-skip]` after a save
|
|
338
|
+
error (logged as a console warning, never thrown) — the step is "answered" either way, so a spec is
|
|
339
|
+
never permanently blocked behind a flaky save.
|
package/build/index.d.ts
CHANGED
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA"}
|
package/build/index.js
CHANGED
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
export interface AnswerMarketingConsentOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Which choice to save when the step is shown. `'all'` (the default) ticks the "select all"
|
|
5
|
+
* checkbox before saving; `'none'` saves with every item left unchecked, which is a valid,
|
|
6
|
+
* fully-compliant state on its own.
|
|
7
|
+
*/
|
|
8
|
+
accept?: 'none' | 'all';
|
|
9
|
+
timeout?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Answer the marketing-consent full-page step (`@owlmeans/web-marketing-consent`) if it is shown
|
|
13
|
+
* right after login, so a spec driving {@link loginViaSupervisorForm} or {@link loginViaDispatcher}
|
|
14
|
+
* is never blocked behind it.
|
|
15
|
+
*
|
|
16
|
+
* The screen is opt-in per app, and most apps/environments will not have it wired in — that makes
|
|
17
|
+
* detecting its ABSENCE cheaply the whole point. A plain `waitFor` on `[data-marketing-consent]`
|
|
18
|
+
* alone would cost the full `timeout` on every login of every app that never renders it (the same
|
|
19
|
+
* caveat `acceptConsent` documents for the cookie dialog). Instead this races the step's own root
|
|
20
|
+
* against the generic OwlMeans "the app has already landed" / "still on the login screen" markers
|
|
21
|
+
* this file's own login helpers already wait on (`#app-prompt`, `[data-login-method]`) — by the
|
|
22
|
+
* time a login helper calls this, one of those has typically already rendered, so the race resolves
|
|
23
|
+
* as soon as it does: no fixed sleep, and in the common "step absent" case, no material added
|
|
24
|
+
* latency over what the login helper already paid to get here.
|
|
25
|
+
*
|
|
26
|
+
* @returns whether the step was actually shown and answered (including the error/skip fallback
|
|
27
|
+
* below); `false` when it never appeared within `timeout`, which is the expected result everywhere
|
|
28
|
+
* the screen is not yet configured or the visitor already has saved consents.
|
|
29
|
+
*/
|
|
30
|
+
export declare const answerMarketingConsent: (page: Page, opts?: AnswerMarketingConsentOptions) => Promise<boolean>;
|
|
31
|
+
//# sourceMappingURL=marketing-consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marketing-consent.d.ts","sourceRoot":"","sources":["../src/marketing-consent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sBAAsB,SAC3B,IAAI,SAAS,6BAA6B,KAC/C,OAAO,CAAC,OAAO,CAoCjB,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const DEFAULT_MARKETING_CONSENT_TIMEOUT = 6_000;
|
|
2
|
+
/**
|
|
3
|
+
* Answer the marketing-consent full-page step (`@owlmeans/web-marketing-consent`) if it is shown
|
|
4
|
+
* right after login, so a spec driving {@link loginViaSupervisorForm} or {@link loginViaDispatcher}
|
|
5
|
+
* is never blocked behind it.
|
|
6
|
+
*
|
|
7
|
+
* The screen is opt-in per app, and most apps/environments will not have it wired in — that makes
|
|
8
|
+
* detecting its ABSENCE cheaply the whole point. A plain `waitFor` on `[data-marketing-consent]`
|
|
9
|
+
* alone would cost the full `timeout` on every login of every app that never renders it (the same
|
|
10
|
+
* caveat `acceptConsent` documents for the cookie dialog). Instead this races the step's own root
|
|
11
|
+
* against the generic OwlMeans "the app has already landed" / "still on the login screen" markers
|
|
12
|
+
* this file's own login helpers already wait on (`#app-prompt`, `[data-login-method]`) — by the
|
|
13
|
+
* time a login helper calls this, one of those has typically already rendered, so the race resolves
|
|
14
|
+
* as soon as it does: no fixed sleep, and in the common "step absent" case, no material added
|
|
15
|
+
* latency over what the login helper already paid to get here.
|
|
16
|
+
*
|
|
17
|
+
* @returns whether the step was actually shown and answered (including the error/skip fallback
|
|
18
|
+
* below); `false` when it never appeared within `timeout`, which is the expected result everywhere
|
|
19
|
+
* the screen is not yet configured or the visitor already has saved consents.
|
|
20
|
+
*/
|
|
21
|
+
export const answerMarketingConsent = async (page, opts) => {
|
|
22
|
+
const timeout = opts?.timeout ?? DEFAULT_MARKETING_CONSENT_TIMEOUT;
|
|
23
|
+
const step = page.locator('[data-marketing-consent]');
|
|
24
|
+
try {
|
|
25
|
+
await page.locator('[data-marketing-consent], #app-prompt, [data-login-method]')
|
|
26
|
+
.first().waitFor({ state: 'visible', timeout });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (await step.count() === 0)
|
|
32
|
+
return false;
|
|
33
|
+
if ((opts?.accept ?? 'all') === 'all') {
|
|
34
|
+
await page.locator('[data-marketing-consent-all]').click();
|
|
35
|
+
}
|
|
36
|
+
// 'none' leaves every item unchecked and clicks nothing else before saving.
|
|
37
|
+
await page.locator('[data-marketing-consent-save]').click();
|
|
38
|
+
const outcome = await Promise.race([
|
|
39
|
+
step.waitFor({ state: 'detached', timeout }).then(() => 'saved'),
|
|
40
|
+
page.locator('[data-marketing-consent-error]').waitFor({ state: 'visible', timeout })
|
|
41
|
+
.then(() => 'error')
|
|
42
|
+
]).catch(() => 'error');
|
|
43
|
+
if (outcome !== 'saved') {
|
|
44
|
+
const skip = page.locator('[data-marketing-consent-skip]');
|
|
45
|
+
if (await skip.count() > 0) {
|
|
46
|
+
console.warn('[@owlmeans/test-ui] marketing consent save did not complete; using the skip link instead');
|
|
47
|
+
await skip.click();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Answered either way: saved, or skipped past a flaky save. Never leaves the caller blocked.
|
|
51
|
+
return true;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=marketing-consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marketing-consent.js","sourceRoot":"","sources":["../src/marketing-consent.ts"],"names":[],"mappings":"AAYA,MAAM,iCAAiC,GAAG,KAAK,CAAA;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EACzC,IAAU,EAAE,IAAoC,EAC9B,EAAE;IACpB,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,iCAAiC,CAAA;IAClE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAA;IAErD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,CAAC,4DAA4D,CAAC;aAC7E,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAE1C,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC;QACtC,MAAM,IAAI,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,KAAK,EAAE,CAAA;IAC5D,CAAC;IACD,4EAA4E;IAE5E,MAAM,IAAI,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,KAAK,EAAE,CAAA;IAE3D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAgB,CAAC;QACzE,IAAI,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;aAClF,IAAI,CAAC,GAAG,EAAE,CAAC,OAAgB,CAAC;KAChC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAgB,CAAC,CAAA;IAEhC,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAA;QAC1D,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,0FAA0F,CAAC,CAAA;YACxG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;IACH,CAAC;IAED,6FAA6F;IAC7F,OAAO,IAAI,CAAA;AACb,CAAC,CAAA"}
|
package/build/supervisor.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export declare const pregenerateAuthToken: (opts: PregenerateAuthTokenOptions) =
|
|
|
35
35
|
export declare const loginViaDispatcher: (page: Page, baseUrl: string, token: string, opts?: {
|
|
36
36
|
dispatcherPath?: string;
|
|
37
37
|
waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle';
|
|
38
|
+
/**
|
|
39
|
+
* Answer the marketing-consent step if it is shown right after the dispatcher navigates away.
|
|
40
|
+
* Defaults to `'save'` (select-all and save) — see {@link answerMarketingConsent}. Pass
|
|
41
|
+
* `'ignore'` to skip the call entirely, e.g. in a spec that answers the step itself.
|
|
42
|
+
*/
|
|
43
|
+
marketingConsent?: 'save' | 'ignore';
|
|
38
44
|
}) => Promise<void>;
|
|
39
45
|
export interface SupervisorApiAuthOptions {
|
|
40
46
|
/** Auth-manager API base URL (the backend serving /authentication/* and /authenticate). */
|
|
@@ -96,6 +102,12 @@ export interface SupervisorFormLoginOptions {
|
|
|
96
102
|
consent?: 'accept' | 'ignore';
|
|
97
103
|
/** When set, capture the filled login form (`supervisor-login-form.png`) before submit. */
|
|
98
104
|
screenshotDir?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Answer the marketing-consent step if it is shown right after landing. Defaults to `'save'`
|
|
107
|
+
* (select-all and save) — see {@link answerMarketingConsent}. Pass `'ignore'` to skip the call
|
|
108
|
+
* entirely, e.g. in a spec that answers the step itself.
|
|
109
|
+
*/
|
|
110
|
+
marketingConsent?: 'save' | 'ignore';
|
|
99
111
|
}
|
|
100
112
|
/**
|
|
101
113
|
* Answer the cookie-consent dialog if one is up, so the page underneath can be driven.
|
|
@@ -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;
|
|
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;AAUrE,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;IACL,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;IAC3F;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CACrC,KACA,OAAO,CAAC,IAAI,CAUd,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;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CACrC;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,CAmCd,CAAA"}
|
package/build/supervisor.js
CHANGED
|
@@ -5,6 +5,7 @@ import { makeKeyPairModel } from '@owlmeans/basic-keys';
|
|
|
5
5
|
import { createIdOfLength } from '@owlmeans/basic-ids';
|
|
6
6
|
import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope';
|
|
7
7
|
import { makeBearer } from '@owlmeans/test-auth';
|
|
8
|
+
import { answerMarketingConsent } from './marketing-consent.js';
|
|
8
9
|
/**
|
|
9
10
|
* Mint a ready-to-use `ED25519-BASIC-TOKEN ...` bearer offline, reusing
|
|
10
11
|
* `@owlmeans/test-auth`'s `makeBearer`. This is the "set a token directly"
|
|
@@ -43,6 +44,8 @@ export const loginViaDispatcher = async (page, baseUrl, token, opts) => {
|
|
|
43
44
|
await page.goto(url.toString(), { waitUntil: opts?.waitUntil ?? 'domcontentloaded' });
|
|
44
45
|
// Wait until the dispatcher has navigated away (token consumed).
|
|
45
46
|
await page.waitForURL(u => !u.pathname.startsWith(path), { timeout: 30_000 });
|
|
47
|
+
if (opts?.marketingConsent !== 'ignore')
|
|
48
|
+
await answerMarketingConsent(page, { accept: 'all' });
|
|
46
49
|
};
|
|
47
50
|
const postJson = async (fetchImpl, url, body) => {
|
|
48
51
|
const res = await fetchImpl(url, {
|
|
@@ -172,5 +175,7 @@ export const loginViaSupervisorForm = async (page, opts) => {
|
|
|
172
175
|
else {
|
|
173
176
|
await page.waitForURL(url => !url.pathname.startsWith(path), { timeout });
|
|
174
177
|
}
|
|
178
|
+
if (opts.marketingConsent !== 'ignore')
|
|
179
|
+
await answerMarketingConsent(page, { accept: 'all' });
|
|
175
180
|
};
|
|
176
181
|
//# sourceMappingURL=supervisor.js.map
|
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;
|
|
1
|
+
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/test-ui",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.37",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"./harness": "./harness/index.html"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
26
|
-
"@owlmeans/basic-envelope": "^0.1.18-rc.
|
|
27
|
-
"@owlmeans/basic-ids": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/basic-keys": "^0.1.18-rc.
|
|
29
|
-
"@owlmeans/test": "^0.1.18-rc.
|
|
30
|
-
"@owlmeans/test-auth": "^0.1.18-rc.
|
|
25
|
+
"@owlmeans/auth": "^0.1.18-rc.34",
|
|
26
|
+
"@owlmeans/basic-envelope": "^0.1.18-rc.35",
|
|
27
|
+
"@owlmeans/basic-ids": "^0.1.18-rc.33",
|
|
28
|
+
"@owlmeans/basic-keys": "^0.1.18-rc.37",
|
|
29
|
+
"@owlmeans/test": "^0.1.18-rc.32",
|
|
30
|
+
"@owlmeans/test-auth": "^0.1.18-rc.35",
|
|
31
31
|
"playwright": "^1.61.1"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Page } from 'playwright'
|
|
2
|
+
|
|
3
|
+
export interface AnswerMarketingConsentOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Which choice to save when the step is shown. `'all'` (the default) ticks the "select all"
|
|
6
|
+
* checkbox before saving; `'none'` saves with every item left unchecked, which is a valid,
|
|
7
|
+
* fully-compliant state on its own.
|
|
8
|
+
*/
|
|
9
|
+
accept?: 'none' | 'all'
|
|
10
|
+
timeout?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULT_MARKETING_CONSENT_TIMEOUT = 6_000
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Answer the marketing-consent full-page step (`@owlmeans/web-marketing-consent`) if it is shown
|
|
17
|
+
* right after login, so a spec driving {@link loginViaSupervisorForm} or {@link loginViaDispatcher}
|
|
18
|
+
* is never blocked behind it.
|
|
19
|
+
*
|
|
20
|
+
* The screen is opt-in per app, and most apps/environments will not have it wired in — that makes
|
|
21
|
+
* detecting its ABSENCE cheaply the whole point. A plain `waitFor` on `[data-marketing-consent]`
|
|
22
|
+
* alone would cost the full `timeout` on every login of every app that never renders it (the same
|
|
23
|
+
* caveat `acceptConsent` documents for the cookie dialog). Instead this races the step's own root
|
|
24
|
+
* against the generic OwlMeans "the app has already landed" / "still on the login screen" markers
|
|
25
|
+
* this file's own login helpers already wait on (`#app-prompt`, `[data-login-method]`) — by the
|
|
26
|
+
* time a login helper calls this, one of those has typically already rendered, so the race resolves
|
|
27
|
+
* as soon as it does: no fixed sleep, and in the common "step absent" case, no material added
|
|
28
|
+
* latency over what the login helper already paid to get here.
|
|
29
|
+
*
|
|
30
|
+
* @returns whether the step was actually shown and answered (including the error/skip fallback
|
|
31
|
+
* below); `false` when it never appeared within `timeout`, which is the expected result everywhere
|
|
32
|
+
* the screen is not yet configured or the visitor already has saved consents.
|
|
33
|
+
*/
|
|
34
|
+
export const answerMarketingConsent = async (
|
|
35
|
+
page: Page, opts?: AnswerMarketingConsentOptions
|
|
36
|
+
): Promise<boolean> => {
|
|
37
|
+
const timeout = opts?.timeout ?? DEFAULT_MARKETING_CONSENT_TIMEOUT
|
|
38
|
+
const step = page.locator('[data-marketing-consent]')
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await page.locator('[data-marketing-consent], #app-prompt, [data-login-method]')
|
|
42
|
+
.first().waitFor({ state: 'visible', timeout })
|
|
43
|
+
} catch {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (await step.count() === 0) return false
|
|
48
|
+
|
|
49
|
+
if ((opts?.accept ?? 'all') === 'all') {
|
|
50
|
+
await page.locator('[data-marketing-consent-all]').click()
|
|
51
|
+
}
|
|
52
|
+
// 'none' leaves every item unchecked and clicks nothing else before saving.
|
|
53
|
+
|
|
54
|
+
await page.locator('[data-marketing-consent-save]').click()
|
|
55
|
+
|
|
56
|
+
const outcome = await Promise.race([
|
|
57
|
+
step.waitFor({ state: 'detached', timeout }).then(() => 'saved' as const),
|
|
58
|
+
page.locator('[data-marketing-consent-error]').waitFor({ state: 'visible', timeout })
|
|
59
|
+
.then(() => 'error' as const)
|
|
60
|
+
]).catch(() => 'error' as const)
|
|
61
|
+
|
|
62
|
+
if (outcome !== 'saved') {
|
|
63
|
+
const skip = page.locator('[data-marketing-consent-skip]')
|
|
64
|
+
if (await skip.count() > 0) {
|
|
65
|
+
console.warn('[@owlmeans/test-ui] marketing consent save did not complete; using the skip link instead')
|
|
66
|
+
await skip.click()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Answered either way: saved, or skipped past a flaky save. Never leaves the caller blocked.
|
|
71
|
+
return true
|
|
72
|
+
}
|
package/src/supervisor.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { makeKeyPairModel } from '@owlmeans/basic-keys'
|
|
|
9
9
|
import { createIdOfLength } from '@owlmeans/basic-ids'
|
|
10
10
|
import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
|
|
11
11
|
import { makeBearer } from '@owlmeans/test-auth'
|
|
12
|
+
import { answerMarketingConsent } from './marketing-consent.js'
|
|
12
13
|
|
|
13
14
|
export interface PregenerateAuthTokenOptions {
|
|
14
15
|
/** Target user id / email the token represents. */
|
|
@@ -61,7 +62,15 @@ export const pregenerateAuthToken = async (opts: PregenerateAuthTokenOptions): P
|
|
|
61
62
|
*/
|
|
62
63
|
export const loginViaDispatcher = async (
|
|
63
64
|
page: Page, baseUrl: string, token: string,
|
|
64
|
-
opts?: {
|
|
65
|
+
opts?: {
|
|
66
|
+
dispatcherPath?: string, waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle'
|
|
67
|
+
/**
|
|
68
|
+
* Answer the marketing-consent step if it is shown right after the dispatcher navigates away.
|
|
69
|
+
* Defaults to `'save'` (select-all and save) — see {@link answerMarketingConsent}. Pass
|
|
70
|
+
* `'ignore'` to skip the call entirely, e.g. in a spec that answers the step itself.
|
|
71
|
+
*/
|
|
72
|
+
marketingConsent?: 'save' | 'ignore'
|
|
73
|
+
}
|
|
65
74
|
): Promise<void> => {
|
|
66
75
|
const path = opts?.dispatcherPath ?? '/dispatcher'
|
|
67
76
|
const url = new URL(path, baseUrl)
|
|
@@ -70,6 +79,8 @@ export const loginViaDispatcher = async (
|
|
|
70
79
|
await page.goto(url.toString(), { waitUntil: opts?.waitUntil ?? 'domcontentloaded' })
|
|
71
80
|
// Wait until the dispatcher has navigated away (token consumed).
|
|
72
81
|
await page.waitForURL(u => !u.pathname.startsWith(path), { timeout: 30_000 })
|
|
82
|
+
|
|
83
|
+
if (opts?.marketingConsent !== 'ignore') await answerMarketingConsent(page, { accept: 'all' })
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
export interface SupervisorApiAuthOptions {
|
|
@@ -187,6 +198,12 @@ export interface SupervisorFormLoginOptions {
|
|
|
187
198
|
consent?: 'accept' | 'ignore'
|
|
188
199
|
/** When set, capture the filled login form (`supervisor-login-form.png`) before submit. */
|
|
189
200
|
screenshotDir?: string
|
|
201
|
+
/**
|
|
202
|
+
* Answer the marketing-consent step if it is shown right after landing. Defaults to `'save'`
|
|
203
|
+
* (select-all and save) — see {@link answerMarketingConsent}. Pass `'ignore'` to skip the call
|
|
204
|
+
* entirely, e.g. in a spec that answers the step itself.
|
|
205
|
+
*/
|
|
206
|
+
marketingConsent?: 'save' | 'ignore'
|
|
190
207
|
}
|
|
191
208
|
|
|
192
209
|
const DEFAULT_SUPERVISOR_PATH = '/authentication/login/pk-supervisor'
|
|
@@ -265,4 +282,6 @@ export const loginViaSupervisorForm = async (
|
|
|
265
282
|
} else {
|
|
266
283
|
await page.waitForURL(url => !url.pathname.startsWith(path), { timeout })
|
|
267
284
|
}
|
|
285
|
+
|
|
286
|
+
if (opts.marketingConsent !== 'ignore') await answerMarketingConsent(page, { accept: 'all' })
|
|
268
287
|
}
|