@owlmeans/test-ui 0.1.18-rc.50 → 0.1.18-rc.51

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 CHANGED
@@ -9,7 +9,8 @@ These are **not** end-to-end UI tests — they exercise individual components in
9
9
  - `browserHelper.launchBrowser(opts?)` — return the shared chromium instance for this `bun test` process. Idempotent.
10
10
  - `browserHelper.closeBrowser()` — tear it down. Call from `afterAll`.
11
11
  - `browserHelper.withPage(fn)` — convenience: lease a fresh context+page for the duration of `fn`, then dispose the context.
12
- - `mountComponent({ url, component?, props? })` — opens a fresh context, navigates to a harness URL with the component / props encoded, returns `{ page, close }` so the spec can assert against `page.locator(...)` and dispose the context when done.
12
+ - `mountComponent({ url, component?, props?, consentGeo? })` — opens a fresh context, navigates to a harness URL with the component / props encoded, returns `{ page, close }` so the spec can assert against `page.locator(...)` and dispose the context when done. `consentGeo` pins where the visitor is before the first navigation.
13
+ - `consentGeoTestHelper.mockConsentGeo(pageOrContext, geo)` / `.traceCalls(page)` — answer the page's same-origin `/cdn-cgi/trace` from an init script (`'US'`, or `{ country?, fail?, delayMs?, gpc? }`), so a consent test does not depend on the runner's own country; count the trace requests.
13
14
  - `makePageHelper(page)` — drives one page of an OwlMeans app: `.acceptConsent(opts?)`, `.answerMarketingConsent(opts?)`, `.loginViaDispatcher(baseUrl, token, opts?)`, `.loginViaSupervisorForm(opts)`, `.saveScreenshot(dir, name)`.
14
15
  - `supervisorAuthHelper` — bearers without a browser: `.pregenerateAuthToken(opts)` (signed offline) and `.authenticateViaSupervisorApi(opts)` (the live supervisor flow over the API).
15
16
  - `Browser`, `BrowserContext`, `Page`, `Locator` — re-exports of the Playwright types so consumers don't need a direct `playwright` import.
@@ -60,7 +61,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
60
61
  your project's skill store (`.agents/skills/`):
61
62
 
62
63
  ```sh
63
- npx @owlmeans/agent-skills@^0.1.18-rc.51
64
+ npx @owlmeans/agent-skills@^0.1.18-rc.52
64
65
  ```
65
66
 
66
67
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/test-ui",
4
- "version": "0.1.18-rc.50",
5
- "generatedAt": "2026-10-06T11:45:57.653Z",
4
+ "version": "0.1.18-rc.51",
5
+ "generatedAt": "2026-10-08T21:27:54.284Z",
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.50"` in `devDependencies`
9
+ **Install:** `"@owlmeans/test-ui": "^0.1.18-rc.51"` 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
@@ -57,7 +57,8 @@ Downloads the chromium binary the `playwright` library drives. CI installs it in
57
57
  | `browserHelper.withPage(fn)` | Lease a fresh context+page for the duration of `fn`, dispose context on completion. |
58
58
  | `mountComponent(opts)` | Open a fresh context, navigate to `opts.url`, return `Mounted` = `{ page, close }`. `component` and `props`, when given, are appended as `?component=` and `?props=`; omit them when the harness mounts a fixed root, which is what every harness here does. |
59
59
  | `MountOptions` | `{ url, component?, props?, waitUntil?, timeout? }` — `waitUntil` defaults to `domcontentloaded`, see below. |
60
- | `makePageHelper(page).acceptConsent({ timeout? })` | Wait up to `timeout` (default 5s) for `[data-consent-dialog]`, accept all, wait for it to detach. Returns whether it answered one — and costs that whole wait when it does not. |
60
+ | `makePageHelper(page).acceptConsent({ timeout? })` | Wait up to `timeout` (default 5s) for the consent store to SETTLE — `[data-consent-dialog]` up (bar or window), or `<html data-consent="decided">` — then accept all and wait for it to detach. Returns whether it answered one; a decision made without UI returns `false` at once. |
61
+ | `consentGeoTestHelper.mockConsentGeo(pageOrContext, geo)` / `.traceCalls(page)` | Pin where the visitor is: an init script answering the same-origin `/cdn-cgi/trace` (`'US'`, or `{ country?, fail?: true \| 'hang', delayMs?, gpc? }`), installed for the NEXT navigation; counts the calls in the page. `mountComponent({ …, consentGeo })` installs it before the first one. |
61
62
  | `makePageHelper(page).saveScreenshot(dir, name)` | Full-page PNG to `<dir>/<name>.png`, creating `dir`. Returns the absolute path. |
62
63
  | Re-exports: `Browser`, `BrowserContext`, `Page`, `Locator` | Playwright types — no direct `playwright` import needed. |
63
64
 
@@ -290,7 +291,8 @@ the filled form just before submit.
290
291
  ## The consent dialog blocks the login form, and the failure blames the button
291
292
 
292
293
  An OwlMeans app asks for cookie consent **before** it will start an authentication flow, and the
293
- dialog is a modal with no dismissal — a decision is what the gate is waiting for. Until it is
294
+ surface — the bar on its transparent overlay, or the preferences window — offers no dismissal: a
295
+ decision is what the gate is waiting for. Until it is
294
296
  answered the overlay intercepts pointer events, so a login form renders, resolves, reports itself
295
297
  `visible, enabled and stable`, and still cannot be clicked. Playwright retries for the full timeout
296
298
  and then reports `click: Timeout … waiting for getByTestId('supervisor-submit')`, naming the
@@ -298,18 +300,26 @@ button. Nothing is wrong with the button; read the `subtree intercepts pointer e
298
300
  names `[data-consent-dialog]`.
299
301
 
300
302
  `makePageHelper(page).loginViaSupervisorForm` therefore calls `.acceptConsent()` after navigating. `.acceptConsent` is
301
- public for specs that drive their own login: it waits for `[data-consent-dialog]` to become
302
- visible, clicks `[data-consent-accept-all]`, waits for the dialog to detach, and reports whether it
303
- answered one. It accepts **all** categories deliberately — a spec asserting a narrower decision must
304
- make that decision itself rather than inherit a silent minimum.
305
-
306
- **The `false` return costs the full wait.** It is what the visibility wait rejects into, not a cheap
307
- probe, so a page with no dialog pays the timeout — 5s by `makePageHelper(page).acceptConsent`'s own default, but
308
- `.loginViaSupervisorForm` passes its `timeout` straight through and that defaults to `60_000`. On an
309
- app that ships no consent widget the login helper therefore sits for a full minute before it fills
310
- the first field, with nothing on screen to explain it. Drive such an app with `consent: 'ignore'`,
311
- which skips the call entirely; that is also the switch a spec flips when it wants to answer the
312
- dialog itself.
303
+ public for specs that drive their own login: it waits for the consent store to SETTLE — never while
304
+ the visitor is still being located, when the transparent overlay is up and would take the click —
305
+ then clicks `[data-consent-accept-all]` on whichever surface asks, waits for it to detach, and
306
+ reports whether it answered one. It accepts **all** categories deliberately — a spec asserting a
307
+ narrower decision must make that decision itself rather than inherit a silent minimum.
308
+
309
+ **Where the app asks depends on where the runner is.** With the geo gate on, a Cloudflare-proxied
310
+ app asks only visitors located in a consent country — and the trace answers with the RUNNER's
311
+ country. A spec whose assertions need the surface pins it: `mountComponent({ url, consentGeo: 'PL' })`
312
+ or `consentGeoTestHelper.mockConsentGeo(context, 'PL')` before the navigation; a spec about the
313
+ automatic decision pins `'US'`. Never `page.route` for it — interception disables the cache for
314
+ every request, and a vite-served application stalls on its hundreds of unbundled modules.
315
+
316
+ **A `false` is cheap when the app says so, and costs the full wait when it cannot.** An app on a
317
+ consent package that marks `<html data-consent>` settles at once on a stored or automatic
318
+ decision. One older than that marker — or one that ships no consent widget — only ever settles by
319
+ showing a dialog, so a page with none pays the timeout: 5s by `.acceptConsent`'s own default, but
320
+ `.loginViaSupervisorForm` passes its `timeout` straight through and that defaults to `60_000`.
321
+ Drive an app with no widget with `consent: 'ignore'`, which skips the call entirely; that is also
322
+ the switch a spec flips when it wants to answer the dialog itself.
313
323
 
314
324
  ## `makePageHelper(page).answerMarketingConsent` — cheap absence detection, unlike the cookie dialog
315
325
 
@@ -0,0 +1,36 @@
1
+ import type { BrowserContext, Page } from 'playwright';
2
+ /**
3
+ * A fake answer from Cloudflare's trace endpoint: a country code (`'US'`), or
4
+ * `{ country?, fail?, delayMs?, gpc? }`.
5
+ */
6
+ export type ConsentGeoMock = string | ConsentGeoMockOptions;
7
+ export interface ConsentGeoMockOptions {
8
+ /** The `loc=` the fake trace answers with. Defaults to `PL` — a consent country. */
9
+ country?: string;
10
+ /** `true` answers 404 (the host is not behind Cloudflare); `'hang'` never answers. */
11
+ fail?: boolean | 'hang';
12
+ /** Answer only after this many ms — long enough to see the spinner. */
13
+ delayMs?: number;
14
+ /** Send Global Privacy Control (`navigator.globalPrivacyControl`). */
15
+ gpc?: boolean;
16
+ /** The trace path the page asks. Defaults to `/cdn-cgi/trace`. */
17
+ path?: string;
18
+ }
19
+ /**
20
+ * Pinning where a visitor is, so a consent test does not depend on where its runner happens to be:
21
+ * every app behind Cloudflare answers the trace with the RUNNER's country.
22
+ */
23
+ export interface ConsentGeoTestHelper {
24
+ /**
25
+ * Answer the page's same-origin trace request from inside the page: an init script that wraps
26
+ * `window.fetch` before any page script runs, and counts the calls in
27
+ * `window.__consentTraceCalls`. Never `page.route` — interception disables the cache for every
28
+ * request, and a vite-served application stalls on its hundreds of unbundled modules.
29
+ *
30
+ * Applies to the NEXT navigation of the page or context it is installed on.
31
+ */
32
+ mockConsentGeo: (target: Page | BrowserContext, geo: ConsentGeoMock) => Promise<void>;
33
+ /** How many trace requests the page made since it loaded (`mockConsentGeo` only). */
34
+ traceCalls: (page: Page) => Promise<number>;
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/consent/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,qBAAqB,CAAA;AAE3D,MAAM,WAAW,qBAAqB;IACpC,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sFAAsF;IACtF,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,IAAI,GAAG,cAAc,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrF,qFAAqF;IACrF,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/consent/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { ConsentGeoTestHelper } from './consent/types.js';
2
+ export declare const createConsentGeoTestHelper: () => ConsentGeoTestHelper;
3
+ export declare const consentGeoTestHelper: ConsentGeoTestHelper;
4
+ //# sourceMappingURL=consent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAyC,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAErG,eAAO,MAAM,0BAA0B,QAAO,oBAyC7C,CAAA;AAED,eAAO,MAAM,oBAAoB,sBAA+B,CAAA"}
@@ -0,0 +1,41 @@
1
+ import { DEFAULT_CONSENT_GEO_COUNTRY, DEFAULT_CONSENT_TRACE_PATH } from './consts.local.js';
2
+ export const createConsentGeoTestHelper = () => {
3
+ const mockConsentGeo = async (target, geo) => {
4
+ const options = {
5
+ country: typeof geo === 'string' ? geo : geo.country ?? DEFAULT_CONSENT_GEO_COUNTRY,
6
+ delayMs: typeof geo === 'string' ? 0 : geo.delayMs ?? 0,
7
+ path: typeof geo === 'string' ? DEFAULT_CONSENT_TRACE_PATH : geo.path ?? DEFAULT_CONSENT_TRACE_PATH,
8
+ ...(typeof geo !== 'string' && geo.fail != null ? { fail: geo.fail } : {}),
9
+ ...(typeof geo !== 'string' && geo.gpc != null ? { gpc: geo.gpc } : {}),
10
+ };
11
+ // A string, not a function: it runs in the page, and this package compiles without DOM types.
12
+ await target.addInitScript({ content: `(function(arg){
13
+ if (arg.gpc != null) {
14
+ Object.defineProperty(Navigator.prototype, 'globalPrivacyControl', { get: function () { return arg.gpc }, configurable: true })
15
+ }
16
+ window.__consentTraceCalls = 0
17
+ var original = window.fetch.bind(window)
18
+ window.fetch = function (input, init) {
19
+ var href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url
20
+ var url = new URL(href, location.href)
21
+ if (url.origin !== location.origin || url.pathname !== arg.path) return original(input, init)
22
+ window.__consentTraceCalls += 1
23
+ var answer = function () {
24
+ if (arg.fail === 'hang') return new Promise(function () {})
25
+ if (arg.fail === true) return Promise.resolve(new Response('not found', { status: 404 }))
26
+ return Promise.resolve(new Response(
27
+ 'fl=0f0\\nh=' + location.hostname + '\\nip=203.0.113.7\\nts=0\\nvisit_scheme=https\\ncolo=TST\\nloc=' + arg.country + '\\n',
28
+ { status: 200, headers: { 'content-type': 'text/plain' } }
29
+ ))
30
+ }
31
+ return arg.delayMs > 0
32
+ ? new Promise(function (resolve) { setTimeout(resolve, arg.delayMs) }).then(answer)
33
+ : answer()
34
+ }
35
+ })(${JSON.stringify(options)})` });
36
+ };
37
+ const traceCalls = async (page) => await page.evaluate('window.__consentTraceCalls || 0');
38
+ return { mockConsentGeo, traceCalls };
39
+ };
40
+ export const consentGeoTestHelper = createConsentGeoTestHelper();
41
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;AAG3F,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAyB,EAAE;IACnE,MAAM,cAAc,GAAG,KAAK,EAAE,MAA6B,EAAE,GAAmB,EAAiB,EAAE;QACjG,MAAM,OAAO,GAA+E;YAC1F,OAAO,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,2BAA2B;YACnF,OAAO,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC;YACvD,IAAI,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,0BAA0B;YACnG,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1E,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxE,CAAA;QAED,8FAA8F;QAC9F,MAAM,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;SAuBjC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACpC,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,KAAK,EAAE,IAAU,EAAmB,EAAE,CACvD,MAAM,IAAI,CAAC,QAAQ,CAAC,iCAAiC,CAAW,CAAA;IAElE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAA;AACvC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAA0B,EAAE,CAAA"}
@@ -1,3 +1,9 @@
1
1
  export declare const DEFAULT_MARKETING_CONSENT_TIMEOUT = 30000;
2
2
  export declare const DEFAULT_SUPERVISOR_PATH = "/authentication/login/pk-supervisor";
3
+ /** A consent country: a mocked visitor is asked unless the spec says otherwise. */
4
+ export declare const DEFAULT_CONSENT_GEO_COUNTRY = "PL";
5
+ /** `@owlmeans/consent`'s `CONSENT_TRACE_PATH`, repeated: this package depends on no consent package. */
6
+ export declare const DEFAULT_CONSENT_TRACE_PATH = "/cdn-cgi/trace";
7
+ /** What `acceptConsent` treats as "nothing to answer": the store settled on a decision without UI. */
8
+ export declare const CONSENT_SETTLED_WITHOUT_UI = "html[data-consent=\"decided\"], html[data-consent=\"idle\"]";
3
9
  //# sourceMappingURL=consts.local.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,QAAS,CAAA;AAEvD,eAAO,MAAM,uBAAuB,wCAAwC,CAAA"}
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,QAAS,CAAA;AAEvD,eAAO,MAAM,uBAAuB,wCAAwC,CAAA;AAE5E,mFAAmF;AACnF,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAE/C,wGAAwG;AACxG,eAAO,MAAM,0BAA0B,mBAAmB,CAAA;AAE1D,sGAAsG;AACtG,eAAO,MAAM,0BAA0B,gEAA4D,CAAA"}
@@ -1,3 +1,9 @@
1
1
  export const DEFAULT_MARKETING_CONSENT_TIMEOUT = 30_000;
2
2
  export const DEFAULT_SUPERVISOR_PATH = '/authentication/login/pk-supervisor';
3
+ /** A consent country: a mocked visitor is asked unless the spec says otherwise. */
4
+ export const DEFAULT_CONSENT_GEO_COUNTRY = 'PL';
5
+ /** `@owlmeans/consent`'s `CONSENT_TRACE_PATH`, repeated: this package depends on no consent package. */
6
+ export const DEFAULT_CONSENT_TRACE_PATH = '/cdn-cgi/trace';
7
+ /** What `acceptConsent` treats as "nothing to answer": the store settled on a decision without UI. */
8
+ export const CONSENT_SETTLED_WITHOUT_UI = 'html[data-consent="decided"], html[data-consent="idle"]';
3
9
  //# sourceMappingURL=consts.local.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iCAAiC,GAAG,MAAM,CAAA;AAEvD,MAAM,CAAC,MAAM,uBAAuB,GAAG,qCAAqC,CAAA"}
1
+ {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iCAAiC,GAAG,MAAM,CAAA;AAEvD,MAAM,CAAC,MAAM,uBAAuB,GAAG,qCAAqC,CAAA;AAE5E,mFAAmF;AACnF,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAA;AAE/C,wGAAwG;AACxG,MAAM,CAAC,MAAM,0BAA0B,GAAG,gBAAgB,CAAA;AAE1D,sGAAsG;AACtG,MAAM,CAAC,MAAM,0BAA0B,GAAG,yDAAyD,CAAA"}
package/build/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './browser.js';
2
+ export * from './consent.js';
3
+ export type * from './consent/types.js';
2
4
  export type * from './browser/types.js';
3
5
  export * from './marketing-consent.js';
4
6
  export type * from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,mBAAmB,oBAAoB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,mBAAmB,iBAAiB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,mBAAmB,uBAAuB,CAAA;AAC1C,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,cAAc,CAAA;AAC5B,mBAAmB,oBAAoB,CAAA;AACvC,mBAAmB,oBAAoB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,mBAAmB,iBAAiB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,mBAAmB,uBAAuB,CAAA;AAC1C,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA"}
package/build/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './browser.js';
2
+ export * from './consent.js';
2
3
  export * from './marketing-consent.js';
3
4
  export * from './mount.js';
4
5
  export * from './page.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAE5B,cAAc,wBAAwB,CAAA;AAEtC,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AAEzB,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAG5B,cAAc,wBAAwB,CAAA;AAEtC,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AAEzB,cAAc,iBAAiB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAYvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,SAAgB,YAAY,KAAG,OAAO,CAAC,OAAO,CAYxE,CAAA"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAYvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,SAAgB,YAAY,KAAG,OAAO,CAAC,OAAO,CAexE,CAAA"}
package/build/mount.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { browserHelper } from './browser.js';
2
+ import { consentGeoTestHelper } from './consent.js';
2
3
  const buildUrl = (opts) => {
3
4
  const params = new URLSearchParams();
4
5
  if (opts.component != null)
@@ -26,6 +27,9 @@ const buildUrl = (opts) => {
26
27
  export const mountComponent = async (opts) => {
27
28
  const browser = await browserHelper.launchBrowser();
28
29
  const context = await browser.newContext();
30
+ if (opts.consentGeo != null) {
31
+ await consentGeoTestHelper.mockConsentGeo(context, opts.consentGeo);
32
+ }
29
33
  const page = await context.newPage();
30
34
  await page.goto(buildUrl(opts), {
31
35
  waitUntil: opts.waitUntil ?? 'domcontentloaded',
@@ -1 +1 @@
1
- {"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAG5C,MAAM,QAAQ,GAAG,CAAC,IAAkB,EAAU,EAAE;IAC9C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACnE,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACvE,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;IAC5B,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC,GAAG,CAAA;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IAC9C,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,GAAG,EAAE,EAAE,CAAA;AACjC,CAAC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,IAAkB,EAAoB,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,aAAa,EAAE,CAAA;IACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAA;IAC1C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IACpC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAC9B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;QAC/C,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3D,CAAC,CAAA;IACF,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC;KAC7C,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAGnD,MAAM,QAAQ,GAAG,CAAC,IAAkB,EAAU,EAAE;IAC9C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACnE,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACvE,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;IAC5B,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC,GAAG,CAAA;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IAC9C,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,GAAG,EAAE,EAAE,CAAA;AACjC,CAAC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,IAAkB,EAAoB,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,aAAa,EAAE,CAAA;IACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAA;IAC1C,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,oBAAoB,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IACrE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IACpC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAC9B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;QAC/C,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3D,CAAC,CAAA;IACF,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC;KAC7C,CAAA;AACH,CAAC,CAAA"}
@@ -120,6 +120,13 @@ export interface PageHelper {
120
120
  * what the dialog is gating — but it is best-effort: an app without the widget, or one where the
121
121
  * visitor already decided, simply has no dialog and this returns at once.
122
122
  *
123
+ * It waits for the consent store to SETTLE (`<html data-consent>`), so a visitor still being
124
+ * located is waited out — the transparent overlay is up then and would take the click — and a
125
+ * visitor decided for automatically (outside the consent countries) returns `false` the moment
126
+ * the decision lands, rather than after the whole `timeout`. Whichever surface asks — the bottom
127
+ * bar or the preferences window — carries `[data-consent-dialog]` and `[data-consent-accept-all]`.
128
+ * Where the runner's own country would decide the outcome, pin it with `mockConsentGeo` first.
129
+ *
123
130
  * @returns whether a dialog was actually answered.
124
131
  */
125
132
  acceptConsent: (opts?: AcceptConsentOptions) => Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/page/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,6BAA6B;IAC5C;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;IAChC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kHAAkH;IAClH,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;IAClE;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CACrC;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;AAED,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,sBAAsB,EAAE,CAAC,IAAI,CAAC,EAAE,6BAA6B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAClF;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpG;;;;;OAKG;IACH,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D;;;;;;;;;;;;;;;OAeG;IACH,aAAa,EAAE,CAAC,IAAI,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAChE;;;;;OAKG;IACH,sBAAsB,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/page/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,6BAA6B;IAC5C;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;IAChC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kHAAkH;IAClH,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;IAClE;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CACrC;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;AAED,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,sBAAsB,EAAE,CAAC,IAAI,CAAC,EAAE,6BAA6B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAClF;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpG;;;;;OAKG;IACH,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,aAAa,EAAE,CAAC,IAAI,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAChE;;;;;OAKG;IACH,sBAAsB,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,OAAO,KAAK,EACmE,UAAU,EACxF,MAAM,iBAAiB,CAAA;AAExB,eAAO,MAAM,cAAc,SAAU,IAAI,KAAG,UA+K3C,CAAA"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAItC,OAAO,KAAK,EACmE,UAAU,EACxF,MAAM,iBAAiB,CAAA;AAExB,eAAO,MAAM,cAAc,SAAU,IAAI,KAAG,UAuL3C,CAAA"}
package/build/page.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdir } from 'node:fs/promises';
2
2
  import { join, resolve } from 'node:path';
3
- import { DEFAULT_MARKETING_CONSENT_TIMEOUT, DEFAULT_SUPERVISOR_PATH } from './consts.local.js';
3
+ import { CONSENT_SETTLED_WITHOUT_UI, DEFAULT_MARKETING_CONSENT_TIMEOUT, DEFAULT_SUPERVISOR_PATH, } from './consts.local.js';
4
4
  export const makePageHelper = (page) => {
5
5
  const answerMarketingConsent = async (opts) => {
6
6
  const timeout = opts?.timeout ?? DEFAULT_MARKETING_CONSENT_TIMEOUT;
@@ -107,12 +107,20 @@ export const makePageHelper = (page) => {
107
107
  };
108
108
  const acceptConsent = async (opts) => {
109
109
  const dialog = page.locator('[data-consent-dialog]');
110
+ // Wait until the consent store has SETTLED: a surface is up, or a decision exists without one
111
+ // (stored earlier, or made automatically outside the consent countries — `<html data-consent>`).
112
+ // Never while the visitor is still being located: the overlay is up then and a click would land
113
+ // on it. An application older than the attribute only ever settles by showing the dialog.
110
114
  try {
111
- await dialog.waitFor({ state: 'visible', timeout: opts?.timeout ?? 5_000 });
115
+ await page.locator(`[data-consent-dialog], ${CONSENT_SETTLED_WITHOUT_UI}`)
116
+ .first().waitFor({ state: 'visible', timeout: opts?.timeout ?? 5_000 });
112
117
  }
113
118
  catch {
114
119
  return false;
115
120
  }
121
+ if (!await dialog.first().isVisible()) {
122
+ return false;
123
+ }
116
124
  // Accept-all rather than essential-only: a test asserting a narrower decision should make that
117
125
  // decision itself, and silently choosing the minimum here would hide it from the spec.
118
126
  await page.locator('[data-consent-accept-all]').click();
package/build/page.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"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"}
package/build/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Page } from 'playwright';
2
+ import type { ConsentGeoMock } from './consent/types.js';
2
3
  export interface MountOptions {
3
4
  /**
4
5
  * URL the harness is reachable at. The consuming package is in charge
@@ -31,6 +32,11 @@ export interface MountOptions {
31
32
  waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle';
32
33
  /** Navigation timeout in ms. */
33
34
  timeout?: number;
35
+ /**
36
+ * Answer the page's Cloudflare trace with this — installed BEFORE the first navigation, so the
37
+ * consent geo gate sees it on the very first load. See `consentGeoTestHelper.mockConsentGeo`.
38
+ */
39
+ consentGeo?: ConsentGeoMock;
34
40
  }
35
41
  export interface Mounted {
36
42
  page: Page;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;IAClE,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAA;IACV,0EAA0E;IAC1E,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAA;IAClE,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,UAAU,CAAC,EAAE,cAAc,CAAA;CAC5B;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAA;IACV,0EAA0E;IAC1E,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/test-ui",
3
- "version": "0.1.18-rc.50",
3
+ "version": "0.1.18-rc.51",
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.40",
26
- "@owlmeans/basic-envelope": "^0.1.18-rc.47",
27
- "@owlmeans/basic-ids": "^0.1.18-rc.38",
28
- "@owlmeans/basic-keys": "^0.1.18-rc.49",
29
- "@owlmeans/test": "^0.1.18-rc.35",
30
- "@owlmeans/test-auth": "^0.1.18-rc.47",
25
+ "@owlmeans/auth": "^0.1.18-rc.41",
26
+ "@owlmeans/basic-envelope": "^0.1.18-rc.48",
27
+ "@owlmeans/basic-ids": "^0.1.18-rc.39",
28
+ "@owlmeans/basic-keys": "^0.1.18-rc.50",
29
+ "@owlmeans/test": "^0.1.18-rc.36",
30
+ "@owlmeans/test-auth": "^0.1.18-rc.48",
31
31
  "playwright": "^1.63.0"
32
32
  },
33
33
  "peerDependencies": {
@@ -0,0 +1,38 @@
1
+ import type { BrowserContext, Page } from 'playwright'
2
+
3
+ /**
4
+ * A fake answer from Cloudflare's trace endpoint: a country code (`'US'`), or
5
+ * `{ country?, fail?, delayMs?, gpc? }`.
6
+ */
7
+ export type ConsentGeoMock = string | ConsentGeoMockOptions
8
+
9
+ export interface ConsentGeoMockOptions {
10
+ /** The `loc=` the fake trace answers with. Defaults to `PL` — a consent country. */
11
+ country?: string
12
+ /** `true` answers 404 (the host is not behind Cloudflare); `'hang'` never answers. */
13
+ fail?: boolean | 'hang'
14
+ /** Answer only after this many ms — long enough to see the spinner. */
15
+ delayMs?: number
16
+ /** Send Global Privacy Control (`navigator.globalPrivacyControl`). */
17
+ gpc?: boolean
18
+ /** The trace path the page asks. Defaults to `/cdn-cgi/trace`. */
19
+ path?: string
20
+ }
21
+
22
+ /**
23
+ * Pinning where a visitor is, so a consent test does not depend on where its runner happens to be:
24
+ * every app behind Cloudflare answers the trace with the RUNNER's country.
25
+ */
26
+ export interface ConsentGeoTestHelper {
27
+ /**
28
+ * Answer the page's same-origin trace request from inside the page: an init script that wraps
29
+ * `window.fetch` before any page script runs, and counts the calls in
30
+ * `window.__consentTraceCalls`. Never `page.route` — interception disables the cache for every
31
+ * request, and a vite-served application stalls on its hundreds of unbundled modules.
32
+ *
33
+ * Applies to the NEXT navigation of the page or context it is installed on.
34
+ */
35
+ mockConsentGeo: (target: Page | BrowserContext, geo: ConsentGeoMock) => Promise<void>
36
+ /** How many trace requests the page made since it loaded (`mockConsentGeo` only). */
37
+ traceCalls: (page: Page) => Promise<number>
38
+ }
package/src/consent.ts ADDED
@@ -0,0 +1,48 @@
1
+ import type { BrowserContext, Page } from 'playwright'
2
+ import { DEFAULT_CONSENT_GEO_COUNTRY, DEFAULT_CONSENT_TRACE_PATH } from './consts.local.js'
3
+ import type { ConsentGeoMock, ConsentGeoMockOptions, ConsentGeoTestHelper } from './consent/types.js'
4
+
5
+ export const createConsentGeoTestHelper = (): ConsentGeoTestHelper => {
6
+ const mockConsentGeo = async (target: Page | BrowserContext, geo: ConsentGeoMock): Promise<void> => {
7
+ const options: ConsentGeoMockOptions & { country: string, delayMs: number, path: string } = {
8
+ country: typeof geo === 'string' ? geo : geo.country ?? DEFAULT_CONSENT_GEO_COUNTRY,
9
+ delayMs: typeof geo === 'string' ? 0 : geo.delayMs ?? 0,
10
+ path: typeof geo === 'string' ? DEFAULT_CONSENT_TRACE_PATH : geo.path ?? DEFAULT_CONSENT_TRACE_PATH,
11
+ ...(typeof geo !== 'string' && geo.fail != null ? { fail: geo.fail } : {}),
12
+ ...(typeof geo !== 'string' && geo.gpc != null ? { gpc: geo.gpc } : {}),
13
+ }
14
+
15
+ // A string, not a function: it runs in the page, and this package compiles without DOM types.
16
+ await target.addInitScript({ content: `(function(arg){
17
+ if (arg.gpc != null) {
18
+ Object.defineProperty(Navigator.prototype, 'globalPrivacyControl', { get: function () { return arg.gpc }, configurable: true })
19
+ }
20
+ window.__consentTraceCalls = 0
21
+ var original = window.fetch.bind(window)
22
+ window.fetch = function (input, init) {
23
+ var href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url
24
+ var url = new URL(href, location.href)
25
+ if (url.origin !== location.origin || url.pathname !== arg.path) return original(input, init)
26
+ window.__consentTraceCalls += 1
27
+ var answer = function () {
28
+ if (arg.fail === 'hang') return new Promise(function () {})
29
+ if (arg.fail === true) return Promise.resolve(new Response('not found', { status: 404 }))
30
+ return Promise.resolve(new Response(
31
+ 'fl=0f0\\nh=' + location.hostname + '\\nip=203.0.113.7\\nts=0\\nvisit_scheme=https\\ncolo=TST\\nloc=' + arg.country + '\\n',
32
+ { status: 200, headers: { 'content-type': 'text/plain' } }
33
+ ))
34
+ }
35
+ return arg.delayMs > 0
36
+ ? new Promise(function (resolve) { setTimeout(resolve, arg.delayMs) }).then(answer)
37
+ : answer()
38
+ }
39
+ })(${JSON.stringify(options)})` })
40
+ }
41
+
42
+ const traceCalls = async (page: Page): Promise<number> =>
43
+ await page.evaluate('window.__consentTraceCalls || 0') as number
44
+
45
+ return { mockConsentGeo, traceCalls }
46
+ }
47
+
48
+ export const consentGeoTestHelper = createConsentGeoTestHelper()
@@ -1,3 +1,12 @@
1
1
  export const DEFAULT_MARKETING_CONSENT_TIMEOUT = 30_000
2
2
 
3
3
  export const DEFAULT_SUPERVISOR_PATH = '/authentication/login/pk-supervisor'
4
+
5
+ /** A consent country: a mocked visitor is asked unless the spec says otherwise. */
6
+ export const DEFAULT_CONSENT_GEO_COUNTRY = 'PL'
7
+
8
+ /** `@owlmeans/consent`'s `CONSENT_TRACE_PATH`, repeated: this package depends on no consent package. */
9
+ export const DEFAULT_CONSENT_TRACE_PATH = '/cdn-cgi/trace'
10
+
11
+ /** What `acceptConsent` treats as "nothing to answer": the store settled on a decision without UI. */
12
+ export const CONSENT_SETTLED_WITHOUT_UI = 'html[data-consent="decided"], html[data-consent="idle"]'
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './browser.js'
2
+ export * from './consent.js'
3
+ export type * from './consent/types.js'
2
4
  export type * from './browser/types.js'
3
5
  export * from './marketing-consent.js'
4
6
  export type * from './types.js'
package/src/mount.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { browserHelper } from './browser.js'
2
+ import { consentGeoTestHelper } from './consent.js'
2
3
  import type { Mounted, MountOptions } from './types.js'
3
4
 
4
5
  const buildUrl = (opts: MountOptions): string => {
@@ -26,6 +27,9 @@ const buildUrl = (opts: MountOptions): string => {
26
27
  export const mountComponent = async (opts: MountOptions): Promise<Mounted> => {
27
28
  const browser = await browserHelper.launchBrowser()
28
29
  const context = await browser.newContext()
30
+ if (opts.consentGeo != null) {
31
+ await consentGeoTestHelper.mockConsentGeo(context, opts.consentGeo)
32
+ }
29
33
  const page = await context.newPage()
30
34
  await page.goto(buildUrl(opts), {
31
35
  waitUntil: opts.waitUntil ?? 'domcontentloaded',
package/src/page/types.ts CHANGED
@@ -124,6 +124,13 @@ export interface PageHelper {
124
124
  * what the dialog is gating — but it is best-effort: an app without the widget, or one where the
125
125
  * visitor already decided, simply has no dialog and this returns at once.
126
126
  *
127
+ * It waits for the consent store to SETTLE (`<html data-consent>`), so a visitor still being
128
+ * located is waited out — the transparent overlay is up then and would take the click — and a
129
+ * visitor decided for automatically (outside the consent countries) returns `false` the moment
130
+ * the decision lands, rather than after the whole `timeout`. Whichever surface asks — the bottom
131
+ * bar or the preferences window — carries `[data-consent-dialog]` and `[data-consent-accept-all]`.
132
+ * Where the runner's own country would decide the outcome, pin it with `mockConsentGeo` first.
133
+ *
127
134
  * @returns whether a dialog was actually answered.
128
135
  */
129
136
  acceptConsent: (opts?: AcceptConsentOptions) => Promise<boolean>
package/src/page.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { mkdir } from 'node:fs/promises'
2
2
  import { join, resolve } from 'node:path'
3
3
  import type { Page } from 'playwright'
4
- import { DEFAULT_MARKETING_CONSENT_TIMEOUT, DEFAULT_SUPERVISOR_PATH } from './consts.local.js'
4
+ import {
5
+ CONSENT_SETTLED_WITHOUT_UI, DEFAULT_MARKETING_CONSENT_TIMEOUT, DEFAULT_SUPERVISOR_PATH,
6
+ } from './consts.local.js'
5
7
  import type {
6
8
  AcceptConsentOptions, AnswerMarketingConsentOptions, DispatcherLoginOptions, PageHelper, SupervisorFormLoginOptions,
7
9
  } from './page/types.js'
@@ -126,11 +128,19 @@ export const makePageHelper = (page: Page): PageHelper => {
126
128
 
127
129
  const acceptConsent = async (opts?: AcceptConsentOptions): Promise<boolean> => {
128
130
  const dialog = page.locator('[data-consent-dialog]')
131
+ // Wait until the consent store has SETTLED: a surface is up, or a decision exists without one
132
+ // (stored earlier, or made automatically outside the consent countries — `<html data-consent>`).
133
+ // Never while the visitor is still being located: the overlay is up then and a click would land
134
+ // on it. An application older than the attribute only ever settles by showing the dialog.
129
135
  try {
130
- await dialog.waitFor({ state: 'visible', timeout: opts?.timeout ?? 5_000 })
136
+ await page.locator(`[data-consent-dialog], ${CONSENT_SETTLED_WITHOUT_UI}`)
137
+ .first().waitFor({ state: 'visible', timeout: opts?.timeout ?? 5_000 })
131
138
  } catch {
132
139
  return false
133
140
  }
141
+ if (!await dialog.first().isVisible()) {
142
+ return false
143
+ }
134
144
  // Accept-all rather than essential-only: a test asserting a narrower decision should make that
135
145
  // decision itself, and silently choosing the minimum here would hide it from the spec.
136
146
  await page.locator('[data-consent-accept-all]').click()
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Page } from 'playwright'
2
+ import type { ConsentGeoMock } from './consent/types.js'
2
3
 
3
4
  export interface MountOptions {
4
5
  /**
@@ -32,6 +33,11 @@ export interface MountOptions {
32
33
  waitUntil?: 'commit' | 'domcontentloaded' | 'load' | 'networkidle'
33
34
  /** Navigation timeout in ms. */
34
35
  timeout?: number
36
+ /**
37
+ * Answer the page's Cloudflare trace with this — installed BEFORE the first navigation, so the
38
+ * consent geo gate sees it on the very first load. See `consentGeoTestHelper.mockConsentGeo`.
39
+ */
40
+ consentGeo?: ConsentGeoMock
35
41
  }
36
42
 
37
43
  export interface Mounted {