@sungen/driver-ui 3.2.34 → 3.3.0-alpha.2
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/dist/app-profile-presets.d.ts +12 -0
- package/dist/app-profile-presets.d.ts.map +1 -0
- package/dist/app-profile-presets.js +85 -0
- package/dist/app-profile-presets.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/patterns/assertion-patterns.d.ts +13 -0
- package/dist/patterns/assertion-patterns.d.ts.map +1 -0
- package/dist/patterns/assertion-patterns.js +903 -0
- package/dist/patterns/assertion-patterns.js.map +1 -0
- package/dist/patterns/capture-patterns.d.ts +21 -0
- package/dist/patterns/capture-patterns.d.ts.map +1 -0
- package/dist/patterns/capture-patterns.js +174 -0
- package/dist/patterns/capture-patterns.js.map +1 -0
- package/dist/patterns/focus-patterns.d.ts +22 -0
- package/dist/patterns/focus-patterns.d.ts.map +1 -0
- package/dist/patterns/focus-patterns.js +79 -0
- package/dist/patterns/focus-patterns.js.map +1 -0
- package/dist/patterns/form-patterns.d.ts +6 -0
- package/dist/patterns/form-patterns.d.ts.map +1 -0
- package/dist/patterns/form-patterns.js +239 -0
- package/dist/patterns/form-patterns.js.map +1 -0
- package/dist/patterns/interaction-patterns.d.ts +20 -0
- package/dist/patterns/interaction-patterns.d.ts.map +1 -0
- package/dist/patterns/interaction-patterns.js +633 -0
- package/dist/patterns/interaction-patterns.js.map +1 -0
- package/dist/patterns/keyboard-patterns.d.ts +7 -0
- package/dist/patterns/keyboard-patterns.d.ts.map +1 -0
- package/dist/patterns/keyboard-patterns.js +57 -0
- package/dist/patterns/keyboard-patterns.js.map +1 -0
- package/dist/patterns/navigation-patterns.d.ts +6 -0
- package/dist/patterns/navigation-patterns.d.ts.map +1 -0
- package/dist/patterns/navigation-patterns.js +185 -0
- package/dist/patterns/navigation-patterns.js.map +1 -0
- package/dist/patterns/scope-patterns.d.ts +7 -0
- package/dist/patterns/scope-patterns.d.ts.map +1 -0
- package/dist/patterns/scope-patterns.js +36 -0
- package/dist/patterns/scope-patterns.js.map +1 -0
- package/dist/patterns/scroll-patterns.d.ts +7 -0
- package/dist/patterns/scroll-patterns.d.ts.map +1 -0
- package/dist/patterns/scroll-patterns.js +25 -0
- package/dist/patterns/scroll-patterns.js.map +1 -0
- package/dist/patterns/setup-patterns.d.ts +6 -0
- package/dist/patterns/setup-patterns.d.ts.map +1 -0
- package/dist/patterns/setup-patterns.js +75 -0
- package/dist/patterns/setup-patterns.js.map +1 -0
- package/dist/patterns/storage-patterns.d.ts +26 -0
- package/dist/patterns/storage-patterns.d.ts.map +1 -0
- package/dist/patterns/storage-patterns.js +142 -0
- package/dist/patterns/storage-patterns.js.map +1 -0
- package/dist/patterns/table-pagination-patterns.d.ts +42 -0
- package/dist/patterns/table-pagination-patterns.d.ts.map +1 -0
- package/dist/patterns/table-pagination-patterns.js +93 -0
- package/dist/patterns/table-pagination-patterns.js.map +1 -0
- package/dist/patterns/table-patterns.d.ts +19 -0
- package/dist/patterns/table-patterns.d.ts.map +1 -0
- package/dist/patterns/table-patterns.js +594 -0
- package/dist/patterns/table-patterns.js.map +1 -0
- package/dist/trace-identity/aria-snapshot-head.d.ts +23 -0
- package/dist/trace-identity/aria-snapshot-head.d.ts.map +1 -0
- package/dist/trace-identity/aria-snapshot-head.js +26 -0
- package/dist/trace-identity/aria-snapshot-head.js.map +1 -0
- package/dist/trace-identity/playwright-identity-resolver.d.ts +10 -0
- package/dist/trace-identity/playwright-identity-resolver.d.ts.map +1 -0
- package/dist/trace-identity/playwright-identity-resolver.js +130 -0
- package/dist/trace-identity/playwright-identity-resolver.js.map +1 -0
- package/dist/trace-identity/trace-run-record-reporter.d.ts +41 -0
- package/dist/trace-identity/trace-run-record-reporter.d.ts.map +1 -0
- package/dist/trace-identity/trace-run-record-reporter.js +113 -0
- package/dist/trace-identity/trace-run-record-reporter.js.map +1 -0
- package/dist/utils/column-header.d.ts +37 -0
- package/dist/utils/column-header.d.ts.map +1 -0
- package/dist/utils/column-header.js +46 -0
- package/dist/utils/column-header.js.map +1 -0
- package/dist/utils/row-index.d.ts +18 -0
- package/dist/utils/row-index.d.ts.map +1 -0
- package/dist/utils/row-index.js +39 -0
- package/dist/utils/row-index.js.map +1 -0
- package/dist/utils/url-path-regex.d.ts +45 -0
- package/dist/utils/url-path-regex.d.ts.map +1 -0
- package/dist/utils/url-path-regex.js +68 -0
- package/dist/utils/url-path-regex.js.map +1 -0
- package/package.json +7 -3
- package/src/index.ts +10 -1
- package/src/trace-identity/aria-snapshot-head.ts +33 -0
- package/src/trace-identity/playwright-identity-resolver.ts +123 -0
- package/src/trace-identity/trace-run-record-reporter.ts +105 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The head line of a Playwright `locator.ariaSnapshot()` → role, accessible name, heading level.
|
|
3
|
+
*
|
|
4
|
+
* Owned here on purpose (G5): the spike loaded Playwright's private aria-YAML parser by absolute
|
|
5
|
+
* path, which breaks on any internal move. We only ever need the FIRST node of a single element's
|
|
6
|
+
* snapshot, whose grammar is small and documented in Playwright's aria snapshot docs:
|
|
7
|
+
*
|
|
8
|
+
* - button "Login"
|
|
9
|
+
* - heading "My Account" [level=1]
|
|
10
|
+
* - textbox "Email": test@playground.dev (a value after the colon)
|
|
11
|
+
* - link "Products": (children on the next lines)
|
|
12
|
+
* - text: Hi, Jane (a bare text node — no role)
|
|
13
|
+
*
|
|
14
|
+
* More than one top-level node (a fragment) has no single identity.
|
|
15
|
+
*/
|
|
16
|
+
export interface AriaHead {
|
|
17
|
+
role: string;
|
|
18
|
+
name: string;
|
|
19
|
+
level?: number;
|
|
20
|
+
note?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function parseAriaHead(yaml: string): AriaHead;
|
|
23
|
+
//# sourceMappingURL=aria-snapshot-head.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aria-snapshot-head.d.ts","sourceRoot":"","sources":["../../src/trace-identity/aria-snapshot-head.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAQvF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CASpD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseAriaHead = parseAriaHead;
|
|
4
|
+
const LINE = /^- ([a-zA-Z]+)(?: "((?:[^"\\]|\\.)*)")?((?: \[[^\]]*\])*)(?::.*)?$/;
|
|
5
|
+
function unescape(raw) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(`"${raw}"`);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return raw.replace(/\\"/g, '"');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function parseAriaHead(yaml) {
|
|
14
|
+
const top = yaml.split(/\r?\n/).filter((l) => l.startsWith('- '));
|
|
15
|
+
if (top.length !== 1)
|
|
16
|
+
return { role: 'generic', name: '', note: `fragment of ${top.length}` };
|
|
17
|
+
const m = LINE.exec(top[0].trimEnd());
|
|
18
|
+
if (!m)
|
|
19
|
+
return { role: 'generic', name: '', note: `unparsed: ${top[0].slice(0, 60)}` };
|
|
20
|
+
const role = m[1];
|
|
21
|
+
if (role === 'text')
|
|
22
|
+
return { role: 'text', name: '', note: 'text node' };
|
|
23
|
+
const level = /\[level=(\d)\]/.exec(m[3] ?? '')?.[1];
|
|
24
|
+
return { role, name: m[2] === undefined ? '' : unescape(m[2]), ...(level ? { level: Number(level) } : {}) };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=aria-snapshot-head.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aria-snapshot-head.js","sourceRoot":"","sources":["../../src/trace-identity/aria-snapshot-head.ts"],"names":[],"mappings":";;AAuBA,sCASC;AAfD,MAAM,IAAI,GAAG,oEAAoE,CAAC;AAElF,SAAS,QAAQ,CAAC,GAAW;IAC3B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAW,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,CAAC;AAC7F,CAAC;AAED,SAAgB,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,eAAe,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;IAC9F,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;IACvF,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAClB,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAC1E,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9G,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CapabilityDescriptor } from '@sun-asterisk/sungen';
|
|
2
|
+
type Resolver = NonNullable<CapabilityDescriptor['traceIdentity']>;
|
|
3
|
+
type Input = Parameters<Resolver>[0];
|
|
4
|
+
type Output = Awaited<ReturnType<Resolver>>;
|
|
5
|
+
export declare const RESOLVER_ID = "driver-ui/playwright-aria@1";
|
|
6
|
+
/** At most this many snapshots per call — a bound on work, not a sampling. */
|
|
7
|
+
export declare const MAX_REQUESTS = 2000;
|
|
8
|
+
export declare function resolveTraceIdentities(input: Input): Promise<Output>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=playwright-identity-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-identity-resolver.d.ts","sourceRoot":"","sources":["../../src/trace-identity/playwright-identity-resolver.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAGjE,KAAK,QAAQ,GAAG,WAAW,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC,CAAC;AACnE,KAAK,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,KAAK,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AAM5C,eAAO,MAAM,WAAW,gCAAgC,CAAC;AAEzD,8EAA8E;AAC9E,eAAO,MAAM,YAAY,OAAO,CAAC;AAkEjC,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB1E"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_REQUESTS = exports.RESOLVER_ID = void 0;
|
|
4
|
+
exports.resolveTraceIdentities = resolveTraceIdentities;
|
|
5
|
+
/**
|
|
6
|
+
* `traceIdentity` for the UI capability (SPI §4.5, DOS-14.5): who a recorded action was aimed at,
|
|
7
|
+
* from Playwright's OWN accessibility engine on the replayed snapshot — the phase 07 method that
|
|
8
|
+
* agreed with the test's locator on 98/98 oracle-bearing actions over two real sites.
|
|
9
|
+
*
|
|
10
|
+
* replay the inert HTML core rendered, in a context with JavaScript disabled, offline,
|
|
11
|
+
* service workers blocked, every request routed to abort AND Chromium's network
|
|
12
|
+
* blocklist set to `*` (non-http schemes too) — nothing leaves the machine
|
|
13
|
+
* identity role + name from `ariaSnapshot()` of the element Playwright marked, re-resolved with
|
|
14
|
+
* `getByRole(role, { name, exact })`: it must land on exactly that element
|
|
15
|
+
* G3 if role+name matches several elements, look for a named scope (row, landmark,
|
|
16
|
+
* dialog, card…) inside which it is unique; none → `low` (ambiguous)
|
|
17
|
+
* low any reason at all: core's caps (missing CSS, broken refs), no semantic role, empty
|
|
18
|
+
* name, getByRole missing the target, ambiguity, a failed snapshot
|
|
19
|
+
* A missing browser is an ERROR, never a silent `low` for everything.
|
|
20
|
+
*/
|
|
21
|
+
const test_1 = require("@playwright/test");
|
|
22
|
+
const aria_snapshot_head_1 = require("./aria-snapshot-head");
|
|
23
|
+
const NAMELESS = new Set(['generic', 'none', 'presentation', 'text', 'fragment', 'paragraph']);
|
|
24
|
+
const SCOPE_ROLES = ['row', 'listitem', 'article', 'dialog', 'alertdialog', 'form', 'group', 'tabpanel', 'region', 'navigation', 'complementary', 'search', 'banner', 'contentinfo', 'main', 'menu', 'list', 'table', 'grid'];
|
|
25
|
+
exports.RESOLVER_ID = 'driver-ui/playwright-aria@1';
|
|
26
|
+
/** At most this many snapshots per call — a bound on work, not a sampling. */
|
|
27
|
+
exports.MAX_REQUESTS = 2000;
|
|
28
|
+
async function launch() {
|
|
29
|
+
try {
|
|
30
|
+
return await test_1.chromium.launch({ headless: true });
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
throw new Error(`trace identity needs Chromium for Playwright — run \`npx playwright install chromium\` (${e.message.split('\n')[0]})`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** A fresh page in a fresh isolated context: JS off, offline, SW blocked, every request aborted, CDP blocklist `*`. */
|
|
37
|
+
async function isolatedPage(browser, counter) {
|
|
38
|
+
const context = await browser.newContext({ javaScriptEnabled: false, offline: true, serviceWorkers: 'block' });
|
|
39
|
+
await context.route('**/*', (route) => route.abort('blockedbyclient'));
|
|
40
|
+
const page = await context.newPage();
|
|
41
|
+
page.on('request', () => { counter.blocked++; });
|
|
42
|
+
const cdp = await context.newCDPSession(page);
|
|
43
|
+
await cdp.send('Network.enable');
|
|
44
|
+
await cdp.send('Network.setBlockedURLs', { urls: ['*'] });
|
|
45
|
+
return page;
|
|
46
|
+
}
|
|
47
|
+
const byRole = (root, role, name, level) => root.getByRole(role, { ...(name ? { name, exact: true } : {}), ...(level ? { level } : {}) });
|
|
48
|
+
/** A named container in which role+name is unique and lands on the target (G3). */
|
|
49
|
+
async function scopeFor(page, target, role, name, level) {
|
|
50
|
+
for (const scopeRole of SCOPE_ROLES) {
|
|
51
|
+
const holders = byRole(page, scopeRole, '').filter({ has: target });
|
|
52
|
+
const n = await holders.count();
|
|
53
|
+
if (!n)
|
|
54
|
+
continue;
|
|
55
|
+
const head = (0, aria_snapshot_head_1.parseAriaHead)((await holders.nth(n - 1).ariaSnapshot({ timeout: 3000 }).catch(() => '')).split('\n')[0]);
|
|
56
|
+
if (!head.name)
|
|
57
|
+
continue;
|
|
58
|
+
const scoped = byRole(byRole(page, scopeRole, head.name), role, name, level);
|
|
59
|
+
if ((await scoped.count()) === 1 && (await scoped.and(target).count()) === 1)
|
|
60
|
+
return { role: scopeRole, name: head.name };
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const lowResult = (key, reasons) => ({ key, role: 'generic', name: '', identityConfidence: 'low', reasons });
|
|
65
|
+
async function identify(page, req) {
|
|
66
|
+
const low = (reasons) => lowResult(req.key, reasons);
|
|
67
|
+
await page.setViewportSize({ width: Math.max(1, req.viewport.width), height: Math.max(1, req.viewport.height) });
|
|
68
|
+
await page.setContent(req.html, { waitUntil: 'domcontentloaded', timeout: 10000 });
|
|
69
|
+
const target = page.locator(req.targetSelector);
|
|
70
|
+
const marked = await target.count();
|
|
71
|
+
if (marked !== 1)
|
|
72
|
+
return low([...req.capReasons, `marked-elements=${marked}`]);
|
|
73
|
+
let aria;
|
|
74
|
+
try {
|
|
75
|
+
aria = await target.ariaSnapshot({ timeout: 3000 });
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
return low([...req.capReasons, `ariaSnapshot-failed: ${e.message.split('\n')[0]}`]);
|
|
79
|
+
}
|
|
80
|
+
const head = (0, aria_snapshot_head_1.parseAriaHead)(aria);
|
|
81
|
+
const id = { role: head.role, name: head.name, ...(head.level ? { level: head.level } : {}) };
|
|
82
|
+
const reasons = [...req.capReasons];
|
|
83
|
+
if (NAMELESS.has(head.role))
|
|
84
|
+
reasons.push(`no-semantic-role(${head.role})`);
|
|
85
|
+
if (!head.name)
|
|
86
|
+
reasons.push('empty-name');
|
|
87
|
+
let scope;
|
|
88
|
+
if (!NAMELESS.has(head.role)) {
|
|
89
|
+
const all = byRole(page, head.role, head.name, head.level);
|
|
90
|
+
const count = await all.count();
|
|
91
|
+
if ((await all.and(target).count()) !== 1)
|
|
92
|
+
reasons.push('getByRole-misses-target');
|
|
93
|
+
else if (count > 1) {
|
|
94
|
+
scope = head.name ? await scopeFor(page, target, head.role, head.name, head.level) : undefined;
|
|
95
|
+
if (!scope)
|
|
96
|
+
reasons.push(`ambiguous(${count})`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return reasons.length ? { key: req.key, ...id, identityConfidence: 'low', reasons } : { key: req.key, ...id, ...(scope ? { scope } : {}), identityConfidence: 'high', reasons: [] };
|
|
100
|
+
}
|
|
101
|
+
async function resolveTraceIdentities(input) {
|
|
102
|
+
const requests = Array.isArray(input?.requests) ? input.requests : [];
|
|
103
|
+
if (requests.length > exports.MAX_REQUESTS)
|
|
104
|
+
throw new Error(`trace identity: ${requests.length} snapshots in one call (max ${exports.MAX_REQUESTS})`);
|
|
105
|
+
const browser = await launch();
|
|
106
|
+
try {
|
|
107
|
+
const counter = { blocked: 0 };
|
|
108
|
+
let page = await isolatedPage(browser, counter);
|
|
109
|
+
const results = [];
|
|
110
|
+
for (const req of requests) {
|
|
111
|
+
try {
|
|
112
|
+
results.push(await identify(page, req));
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
// One bad snapshot is a `low` answer for that snapshot, never a crash of the whole check.
|
|
116
|
+
const why = e.message.split('\n')[0].slice(0, 200);
|
|
117
|
+
results.push(lowResult(req.key, [`replay-failed: ${why}`]));
|
|
118
|
+
if (page.isClosed() || /closed/i.test(why)) {
|
|
119
|
+
await page.context().close().catch(() => undefined);
|
|
120
|
+
page = await isolatedPage(browser, counter);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { resolver: exports.RESOLVER_ID, browser: `chromium ${browser.version()}`, results, blockedRequests: counter.blocked };
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await browser.close();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=playwright-identity-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-identity-resolver.js","sourceRoot":"","sources":["../../src/trace-identity/playwright-identity-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright reporter that records sungen's own RUN RECORD for trace conformance (DOS-14.6, G5).
|
|
3
|
+
*
|
|
4
|
+
* A Playwright trace carries no run id and no hash of the code that produced it, so a good trace of
|
|
5
|
+
* another test — or of the same test before the code was weakened — could be swapped in. This
|
|
6
|
+
* reporter writes, next to the traces it vouches for (`<outputDir>/sungen-run-record[.shard-i-of-n].json`):
|
|
7
|
+
*
|
|
8
|
+
* runId SUNGEN_RUN_ID when set (one id across CI shards), else a fresh UUID
|
|
9
|
+
* specs sha256 of every test file at run START (the code that ran)
|
|
10
|
+
* attempts per test attempt: testId, retry, status, window, and the sha256 of its trace.zip
|
|
11
|
+
* report where the JSON reporter wrote its report — traces are mapped THROUGH that report
|
|
12
|
+
* (merge-reports renames trace files; their content, and so the sha256, stays)
|
|
13
|
+
*
|
|
14
|
+
* The outputDir is wiped at the start of every Playwright run, so a record and its traces always
|
|
15
|
+
* come from the same run. Enable it for free / impl:code tests together with `trace: 'on'`, in a
|
|
16
|
+
* config with its OWN outputDir — the compiled suite clears `test-results/` on every run (#941):
|
|
17
|
+
*
|
|
18
|
+
* outputDir: 'test-results-free',
|
|
19
|
+
* reporter: [['json', { outputFile: 'test-results-free/results.json' }],
|
|
20
|
+
* ['@sungen/driver-ui/dist/trace-identity/trace-run-record-reporter.js']],
|
|
21
|
+
* (the compiled file — Playwright does not transpile TypeScript inside node_modules; `prepack` builds it)
|
|
22
|
+
* use: { trace: 'on' },
|
|
23
|
+
*/
|
|
24
|
+
import type { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
|
|
25
|
+
declare class TraceRunRecordReporter implements Reporter {
|
|
26
|
+
private config?;
|
|
27
|
+
private root;
|
|
28
|
+
private runId;
|
|
29
|
+
private startedAt;
|
|
30
|
+
private specs;
|
|
31
|
+
private results;
|
|
32
|
+
printsToStdio(): boolean;
|
|
33
|
+
onBegin(config: FullConfig, suite: Suite): void;
|
|
34
|
+
onTestEnd(test: TestCase, result: TestResult): void;
|
|
35
|
+
onEnd(): Promise<void>;
|
|
36
|
+
/** The JSON reporter's output file, as the reporter itself resolves it (option, then env). */
|
|
37
|
+
private jsonReportPath;
|
|
38
|
+
private rel;
|
|
39
|
+
}
|
|
40
|
+
export default TraceRunRecordReporter;
|
|
41
|
+
//# sourceMappingURL=trace-run-record-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-run-record-reporter.d.ts","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAenG,cAAM,sBAAuB,YAAW,QAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,OAAO,CAAqD;IAEpE,aAAa,IAAI,OAAO;IAExB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAS/C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAI7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5B,8FAA8F;IAC9F,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,GAAG;CAIZ;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const node_crypto_1 = require("node:crypto");
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const sha256File = (file) => {
|
|
40
|
+
try {
|
|
41
|
+
return (0, node_crypto_1.createHash)('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const posix = (p) => p.split(path.sep).join('/');
|
|
48
|
+
class TraceRunRecordReporter {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.root = process.cwd();
|
|
51
|
+
this.runId = process.env.SUNGEN_RUN_ID || (0, node_crypto_1.randomUUID)();
|
|
52
|
+
this.startedAt = new Date().toISOString();
|
|
53
|
+
this.specs = {};
|
|
54
|
+
this.results = [];
|
|
55
|
+
}
|
|
56
|
+
printsToStdio() { return false; }
|
|
57
|
+
onBegin(config, suite) {
|
|
58
|
+
this.config = config;
|
|
59
|
+
if (config.configFile)
|
|
60
|
+
this.root = path.dirname(config.configFile);
|
|
61
|
+
for (const test of suite.allTests()) {
|
|
62
|
+
const rel = this.rel(test.location.file);
|
|
63
|
+
if (this.specs[rel] === undefined)
|
|
64
|
+
this.specs[rel] = sha256File(test.location.file) ?? '';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
onTestEnd(test, result) {
|
|
68
|
+
this.results.push({ test, result });
|
|
69
|
+
}
|
|
70
|
+
async onEnd() {
|
|
71
|
+
if (!this.config)
|
|
72
|
+
return;
|
|
73
|
+
const attempts = this.results.map(({ test, result }) => {
|
|
74
|
+
const trace = result.attachments.find((a) => a.name === 'trace' && a.path);
|
|
75
|
+
const sha = trace?.path ? sha256File(trace.path) : null;
|
|
76
|
+
return {
|
|
77
|
+
testId: test.id, file: this.rel(test.location.file), title: test.title, retry: result.retry, status: result.status,
|
|
78
|
+
startTime: result.startTime.toISOString(), duration: result.duration, trace: sha ? { sha256: sha } : null,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
const shard = this.config.shard;
|
|
82
|
+
const record = {
|
|
83
|
+
schema: 1, tool: 'sungen-trace-run-record', runId: this.runId, startedAt: this.startedAt,
|
|
84
|
+
playwrightVersion: this.config.version, shard: shard ? `${shard.current}/${shard.total}` : null,
|
|
85
|
+
report: this.jsonReportPath(), specs: this.specs, attempts,
|
|
86
|
+
};
|
|
87
|
+
const outDir = this.config.projects[0]?.outputDir ?? path.join(this.root, 'test-results');
|
|
88
|
+
const name = shard ? `sungen-run-record.shard-${shard.current}-of-${shard.total}.json` : 'sungen-run-record.json';
|
|
89
|
+
try {
|
|
90
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
91
|
+
fs.writeFileSync(path.join(outDir, name), JSON.stringify(record, null, 2));
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
// A missing record makes every free / impl:code test `unproven` — say so instead of hiding it.
|
|
95
|
+
console.error(`[sungen] could not write the trace run record: ${e instanceof Error ? e.message : String(e)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** The JSON reporter's output file, as the reporter itself resolves it (option, then env). */
|
|
99
|
+
jsonReportPath() {
|
|
100
|
+
const json = this.config?.reporter.find(([name]) => name === 'json');
|
|
101
|
+
const opt = json && typeof json[1]?.outputFile === 'string' ? json[1].outputFile : undefined;
|
|
102
|
+
const fromEnv = process.env.PLAYWRIGHT_JSON_OUTPUT_FILE
|
|
103
|
+
?? (process.env.PLAYWRIGHT_JSON_OUTPUT_NAME ? path.join(process.env.PLAYWRIGHT_JSON_OUTPUT_DIR ?? '', process.env.PLAYWRIGHT_JSON_OUTPUT_NAME) : undefined);
|
|
104
|
+
const chosen = opt ? path.resolve(this.root, opt) : fromEnv ? path.resolve(process.cwd(), fromEnv) : undefined;
|
|
105
|
+
return chosen ? this.rel(chosen) : null;
|
|
106
|
+
}
|
|
107
|
+
rel(file) {
|
|
108
|
+
const r = path.relative(this.root, file);
|
|
109
|
+
return posix(r.startsWith('..') || path.isAbsolute(r) ? path.basename(file) : r);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.default = TraceRunRecordReporter;
|
|
113
|
+
//# sourceMappingURL=trace-run-record-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-run-record-reporter.js","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PatternContext } from '@sun-asterisk/sungen';
|
|
2
|
+
/** The `columns:` map as it reaches a pattern from a resolved TABLE entry. */
|
|
3
|
+
interface ColumnsCarrier {
|
|
4
|
+
columns?: Record<string, {
|
|
5
|
+
index?: number;
|
|
6
|
+
header?: string;
|
|
7
|
+
} | undefined>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The DOM accessible name of a table COLUMN HEADER (issue #674).
|
|
11
|
+
*
|
|
12
|
+
* `see [Category] column in [Article List] table` used to bake the bracket label straight into
|
|
13
|
+
* `getByRole('columnheader', { name: … })`. Only the TABLE went through resolveSelector; the column
|
|
14
|
+
* never did. On a Japanese DOM the header reads 記事分類, so the step could not pass and there was
|
|
15
|
+
* no override — QA's only escape was to rename the Gherkin reference to the Japanese header text.
|
|
16
|
+
* That reference is what ships in the delivered test case, and it has to stay readable in the
|
|
17
|
+
* project's working language: the DOM text is a property of the PAGE, the label is a property of
|
|
18
|
+
* the TEST. They are allowed to differ, so both need a home.
|
|
19
|
+
*
|
|
20
|
+
* Most specific declaration first:
|
|
21
|
+
* 1. the table entry's `columns: { <col>: { header: … } }` — scoped to this one table, and the
|
|
22
|
+
* same map the row-scoped cell steps already index by, so a column is described in one place;
|
|
23
|
+
* 2. a `<col>--columnheader` entry's `name:` — the column key's own selector, which is already
|
|
24
|
+
* how `click [Name] columnheader` and `[Name] columnheader is sorted …` resolve. Reading it
|
|
25
|
+
* here is what makes the whole columnheader surface behave the same way;
|
|
26
|
+
* 3. the bracket label verbatim — the historical behaviour, so an undeclared column compiles
|
|
27
|
+
* byte-identically to before.
|
|
28
|
+
*
|
|
29
|
+
* (2) accepts the entry ONLY when it actually resolved to a columnheader role. resolveSelector
|
|
30
|
+
* falls back to the BASE key when no `--columnheader` key matches, so an unrelated `category`
|
|
31
|
+
* combobox on the same screen would otherwise hand its accessible name to the column step — a
|
|
32
|
+
* silent wrong locator, worse than the bug being fixed. Auto-infer reaches this branch too, but it
|
|
33
|
+
* names the role after the label, so it returns exactly what (3) would.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveColumnHeaderName(columnLabel: string, context: PatternContext, tableResolved?: ColumnsCarrier): string;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=column-header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-header.d.ts","sourceRoot":"","sources":["../../src/utils/column-header.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAqB,MAAM,sBAAsB,CAAC;AAEzE,8EAA8E;AAC9E,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,EACvB,aAAa,CAAC,EAAE,cAAc,GAC7B,MAAM,CAgBR"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveColumnHeaderName = resolveColumnHeaderName;
|
|
4
|
+
const sungen_1 = require("@sun-asterisk/sungen");
|
|
5
|
+
/**
|
|
6
|
+
* The DOM accessible name of a table COLUMN HEADER (issue #674).
|
|
7
|
+
*
|
|
8
|
+
* `see [Category] column in [Article List] table` used to bake the bracket label straight into
|
|
9
|
+
* `getByRole('columnheader', { name: … })`. Only the TABLE went through resolveSelector; the column
|
|
10
|
+
* never did. On a Japanese DOM the header reads 記事分類, so the step could not pass and there was
|
|
11
|
+
* no override — QA's only escape was to rename the Gherkin reference to the Japanese header text.
|
|
12
|
+
* That reference is what ships in the delivered test case, and it has to stay readable in the
|
|
13
|
+
* project's working language: the DOM text is a property of the PAGE, the label is a property of
|
|
14
|
+
* the TEST. They are allowed to differ, so both need a home.
|
|
15
|
+
*
|
|
16
|
+
* Most specific declaration first:
|
|
17
|
+
* 1. the table entry's `columns: { <col>: { header: … } }` — scoped to this one table, and the
|
|
18
|
+
* same map the row-scoped cell steps already index by, so a column is described in one place;
|
|
19
|
+
* 2. a `<col>--columnheader` entry's `name:` — the column key's own selector, which is already
|
|
20
|
+
* how `click [Name] columnheader` and `[Name] columnheader is sorted …` resolve. Reading it
|
|
21
|
+
* here is what makes the whole columnheader surface behave the same way;
|
|
22
|
+
* 3. the bracket label verbatim — the historical behaviour, so an undeclared column compiles
|
|
23
|
+
* byte-identically to before.
|
|
24
|
+
*
|
|
25
|
+
* (2) accepts the entry ONLY when it actually resolved to a columnheader role. resolveSelector
|
|
26
|
+
* falls back to the BASE key when no `--columnheader` key matches, so an unrelated `category`
|
|
27
|
+
* combobox on the same screen would otherwise hand its accessible name to the column step — a
|
|
28
|
+
* silent wrong locator, worse than the bug being fixed. Auto-infer reaches this branch too, but it
|
|
29
|
+
* names the role after the label, so it returns exactly what (3) would.
|
|
30
|
+
*/
|
|
31
|
+
function resolveColumnHeaderName(columnLabel, context, tableResolved) {
|
|
32
|
+
const declared = tableResolved?.columns?.[(0, sungen_1.normalizeRefLabel)(columnLabel)]?.header;
|
|
33
|
+
if (declared && declared.trim())
|
|
34
|
+
return declared.trim();
|
|
35
|
+
try {
|
|
36
|
+
const resolved = context.selectorResolver.resolveSelector(columnLabel, context.featureName, 'column', 0);
|
|
37
|
+
if (resolved.role === 'columnheader' && resolved.name && resolved.name.trim()) {
|
|
38
|
+
return resolved.name.trim();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// No entry for the column key and auto-infer declined — the label is the header.
|
|
43
|
+
}
|
|
44
|
+
return columnLabel;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=column-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-header.js","sourceRoot":"","sources":["../../src/utils/column-header.ts"],"names":[],"mappings":";;AAiCA,0DAoBC;AArDD,iDAAyE;AAOzE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,uBAAuB,CACrC,WAAmB,EACnB,OAAuB,EACvB,aAA8B;IAE9B,MAAM,QAAQ,GAAG,aAAa,EAAE,OAAO,EAAE,CAAC,IAAA,0BAAiB,EAAC,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC;IAClF,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,eAAe,CACvD,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,CAC9C,CAAC;QACF,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9E,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iFAAiF;IACnF,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PatternContext } from '@sun-asterisk/sungen';
|
|
2
|
+
/**
|
|
3
|
+
* Positional-row index expression for `… table row {{n}}` steps (FR2, issue #468).
|
|
4
|
+
*
|
|
5
|
+
* QA counts rows from `conventions.table.row_base` (default 1) and the header <tr>
|
|
6
|
+
* usually carries role=row at getByRole('row') index 0 (`header_is_row: false`), so:
|
|
7
|
+
* nth index = n + (header_is_row ? 0 : 1) - row_base
|
|
8
|
+
*
|
|
9
|
+
* `rowToken` is the literal number or `{{var}}` from the step text. A runtime marker
|
|
10
|
+
* becomes `Number(testData.get('var'))` via the runtime-data transform, so the offset
|
|
11
|
+
* is emitted as ` + k` / ` - k` around it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function rowIndexExpr(rowToken: string, context: PatternContext, opts?: {
|
|
14
|
+
headerInRows?: boolean;
|
|
15
|
+
}): string;
|
|
16
|
+
/** Matches `table row 2` / `table row {{n}}` and captures the row token. */
|
|
17
|
+
export declare const TABLE_ROW_NTH: RegExp;
|
|
18
|
+
//# sourceMappingURL=row-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row-index.d.ts","sourceRoot":"","sources":["../../src/utils/row-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/E,MAAM,CAmBR;AAED,4EAA4E;AAC5E,eAAO,MAAM,aAAa,QAAyC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TABLE_ROW_NTH = void 0;
|
|
4
|
+
exports.rowIndexExpr = rowIndexExpr;
|
|
5
|
+
/**
|
|
6
|
+
* Positional-row index expression for `… table row {{n}}` steps (FR2, issue #468).
|
|
7
|
+
*
|
|
8
|
+
* QA counts rows from `conventions.table.row_base` (default 1) and the header <tr>
|
|
9
|
+
* usually carries role=row at getByRole('row') index 0 (`header_is_row: false`), so:
|
|
10
|
+
* nth index = n + (header_is_row ? 0 : 1) - row_base
|
|
11
|
+
*
|
|
12
|
+
* `rowToken` is the literal number or `{{var}}` from the step text. A runtime marker
|
|
13
|
+
* becomes `Number(testData.get('var'))` via the runtime-data transform, so the offset
|
|
14
|
+
* is emitted as ` + k` / ` - k` around it.
|
|
15
|
+
*/
|
|
16
|
+
function rowIndexExpr(rowToken, context, opts = {}) {
|
|
17
|
+
const rowBase = context.appProfile?.tableRowBase ?? 1;
|
|
18
|
+
// `headerInRows: false` — the table declares a row SHAPE (`rows: 'div.job-row'`, issue #688), and a
|
|
19
|
+
// shape never matches the header, so `header_is_row` has nothing to correct for.
|
|
20
|
+
const headerInRows = opts.headerInRows ?? true;
|
|
21
|
+
const headerOffset = headerInRows && !context.appProfile?.tableHeaderIsRow ? 1 : 0;
|
|
22
|
+
const offset = headerOffset - rowBase;
|
|
23
|
+
const varMatch = rowToken.match(/^\{\{([^}]+)\}\}$/);
|
|
24
|
+
if (!varMatch)
|
|
25
|
+
return String(Number(rowToken) + offset);
|
|
26
|
+
// No fallback to the placeholder text. This value is spliced RAW into `.nth({{{rowIndexExpr}}})`,
|
|
27
|
+
// so `${n}` landed as a bare expression outside any template literal — a syntax error that took
|
|
28
|
+
// the whole spec file at parse time, not just the step, and named nothing useful. The throw
|
|
29
|
+
// reaches StepMapper.mapStep and becomes SG-E038 against this step alone (#781).
|
|
30
|
+
const expr = context.dataResolver.resolveData(varMatch[1].trim(), context.featureName);
|
|
31
|
+
if (/^\d+$/.test(expr))
|
|
32
|
+
return String(Number(expr) + offset); // inline-data mode
|
|
33
|
+
if (offset === 0)
|
|
34
|
+
return expr;
|
|
35
|
+
return offset > 0 ? `${expr} + ${offset}` : `${expr} - ${-offset}`;
|
|
36
|
+
}
|
|
37
|
+
/** Matches `table row 2` / `table row {{n}}` and captures the row token. */
|
|
38
|
+
exports.TABLE_ROW_NTH = /\btable\s+row\s+(\d+|\{\{[^}]+\}\})/i;
|
|
39
|
+
//# sourceMappingURL=row-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row-index.js","sourceRoot":"","sources":["../../src/utils/row-index.ts"],"names":[],"mappings":";;;AAaA,oCAqBC;AAhCD;;;;;;;;;;GAUG;AACH,SAAgB,YAAY,CAC1B,QAAgB,EAAE,OAAuB,EAAE,OAAmC,EAAE;IAEhF,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,YAAY,IAAI,CAAC,CAAC;IACtD,oGAAoG;IACpG,iFAAiF;IACjF,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;IAC/C,MAAM,YAAY,GAAG,YAAY,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC;IAEtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC;IAExD,kGAAkG;IAClG,gGAAgG;IAChG,4FAA4F;IAC5F,iFAAiF;IACjF,MAAM,IAAI,GAAW,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/F,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,mBAAmB;IACjF,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;AACrE,CAAC;AAED,4EAA4E;AAC/D,QAAA,aAAa,GAAG,sCAAsC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL-path → RegExp-source conversion for page URL assertions (`toHaveURL`) and
|
|
3
|
+
* navigation waits (`waitForURL`).
|
|
4
|
+
*
|
|
5
|
+
* A selector's `value` may carry dynamic route placeholders (`:id`, `:receptionId`,
|
|
6
|
+
* …), e.g. `/cn22/view/so/ocr/results/:id`. A live URL populates those segments with
|
|
7
|
+
* real values (`…/results/rec-dummy-000017`), so a fully-escaped literal pattern would
|
|
8
|
+
* never match. Placeholder segments become a single-segment wildcard (`[^/]+`); every
|
|
9
|
+
* other segment is escaped so its regex metacharacters stay literal.
|
|
10
|
+
*/
|
|
11
|
+
/** Escape a literal string for safe use inside a RegExp source. */
|
|
12
|
+
export declare function escapeRegex(text: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Convert a URL path into a RegExp source string, turning dynamic route
|
|
15
|
+
* placeholders (`:name`) into `[^/]+` wildcards while escaping literal segments.
|
|
16
|
+
* `/` is emitted as the escaped `\/` so the result drops straight into `/…/`. A query or
|
|
17
|
+
* fragment in the input is dropped — see the comment in the body.
|
|
18
|
+
*/
|
|
19
|
+
export declare function pathToRegexSource(path: string): string;
|
|
20
|
+
/** A page value split into the two patterns the runtime matches SEPARATELY. */
|
|
21
|
+
export interface UrlPatternParts {
|
|
22
|
+
/** Pattern for `u.pathname`. Always present. */
|
|
23
|
+
pathRegex: string;
|
|
24
|
+
/** Pattern for `u.hostname` — set only when the page value named an absolute URL. */
|
|
25
|
+
hostRegex?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Split a page value into the host and path patterns the runtime matches separately.
|
|
29
|
+
*
|
|
30
|
+
* `u.hostname` never appears in `u.pathname`, so one concatenated regex cannot carry both — it
|
|
31
|
+
* only ever "worked" while the match ran unanchored across the whole URL, which is the looseness
|
|
32
|
+
* that let a wait resolve on a transient URL (#677). A page value that names an absolute URL
|
|
33
|
+
* therefore yields two patterns; a relative one yields the path pattern alone, exactly as before.
|
|
34
|
+
*
|
|
35
|
+
* Every caller that builds a pathname pattern for a URL assertion or wait goes through here, so
|
|
36
|
+
* the host half cannot be honoured on one step form and silently dropped on another: before this,
|
|
37
|
+
* only `wait for [X] page` split the host, while both assertion forms matched the path against
|
|
38
|
+
* ANY host — `see [X] page` with `https://admin.example.com/jobs` passed on the public site.
|
|
39
|
+
*
|
|
40
|
+
* Dynamic `:id` segments still wildcard; the host stays a literal. A value that starts like an
|
|
41
|
+
* absolute URL but does not parse falls back to the path-only reading rather than throwing — the
|
|
42
|
+
* pattern is then wrong in a way the author can read, which beats losing the step.
|
|
43
|
+
*/
|
|
44
|
+
export declare function urlToPatternParts(path: string): UrlPatternParts;
|
|
45
|
+
//# sourceMappingURL=url-path-regex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-path-regex.d.ts","sourceRoot":"","sources":["../../src/utils/url-path-regex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CActD;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAQ/D"}
|