@qa-ai-stlc/explorer 0.2.0 → 0.4.0

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.
@@ -0,0 +1,51 @@
1
+ import { type BrowserLauncher, type Clock, type ViewportSize } from '@qa-ai-stlc/core';
2
+ import { type PageModelSet, type SelectorElement, type SelectorRegistry } from '@qa-ai-stlc/schemas';
3
+ import { type ExplorerIdentity } from './identity.js';
4
+ import { type LocatorPolicy } from './synthesize-locators.js';
5
+ export interface BuildSelectorRegistryOptions {
6
+ readonly pageModelSet: PageModelSet;
7
+ readonly browserLauncher: BrowserLauncher;
8
+ /** Signs in before re-navigating to score candidates. Omit to score anonymously. */
9
+ readonly identity?: ExplorerIdentity;
10
+ readonly policy?: LocatorPolicy;
11
+ readonly viewports?: readonly ViewportSize[];
12
+ readonly clock?: Clock;
13
+ }
14
+ export interface BuildSelectorRegistryResult {
15
+ readonly registry: SelectorRegistry;
16
+ /** Every non-GET request safe mode intercepted and cancelled; always 0 unless something is broken. */
17
+ readonly blockedRequestCount: number;
18
+ }
19
+ /**
20
+ * Builds a `SelectorRegistry` from a `PageModelSet` produced by `analyzePages()` (development
21
+ * plan section 6.3 steps 4-6): synthesizes locator candidates for every interactive element under
22
+ * the configured policy, scores the first (primary) candidate's stability in a fresh live pass
23
+ * over the same URLs, and assigns each element a stable id. Every entry's `source` is `'crawl'` -
24
+ * the only source this pipeline produces (`static`/`manual` come from other tasks).
25
+ */
26
+ export declare function buildSelectorRegistry(options: BuildSelectorRegistryOptions): Promise<BuildSelectorRegistryResult>;
27
+ /**
28
+ * Merges a freshly built registry onto a previously stored one, keeping history (development plan
29
+ * section 6.3.6): every element the fresh crawl found replaces its previous entry outright
30
+ * (un-deprecating it, if it had been). An element the fresh crawl did not find is kept rather than
31
+ * deleted, with `deprecatedAt` set to `generatedAt` the first time it goes missing; once
32
+ * deprecated, its `deprecatedAt` is never overwritten by a later run that still does not find it.
33
+ */
34
+ export declare function mergeSelectorRegistry(previous: SelectorRegistry, fresh: SelectorRegistry, generatedAt: string): SelectorRegistry;
35
+ export interface DegradedSelectorElement {
36
+ readonly elementId: string;
37
+ readonly previousScore: number;
38
+ readonly currentScore: number;
39
+ }
40
+ export interface SelectorRegistryDiff {
41
+ readonly added: readonly SelectorElement[];
42
+ readonly removed: readonly SelectorElement[];
43
+ readonly degraded: readonly DegradedSelectorElement[];
44
+ }
45
+ /**
46
+ * Compares two crawls' registries by `elementId`: elements only the current crawl found, elements
47
+ * only the previous crawl found, and elements both found where the current stability score is
48
+ * lower than before.
49
+ */
50
+ export declare function diffSelectorRegistry(previous: SelectorRegistry, current: SelectorRegistry): SelectorRegistryDiff;
51
+ //# sourceMappingURL=build-selector-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-selector-registry.d.ts","sourceRoot":"","sources":["../src/build-selector-registry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC9G,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAG3E,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE3F,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,sGAAsG;IACtG,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAoBD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,2BAA2B,CAAC,CAoDtC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,gBAAgB,EACvB,WAAW,EAAE,MAAM,GAClB,gBAAgB,CAalB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;CACvD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,gBAAgB,GACxB,oBAAoB,CAoBtB"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-selector-registry.js","sourceRoot":"","sources":["../src/build-selector-registry.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './accessibility-tree.js';
2
2
  export * from './allowlist.js';
3
3
  export * from './analyze-page.js';
4
4
  export * from './analyze-pages.js';
5
+ export * from './build-selector-registry.js';
5
6
  export * from './crawl.js';
6
7
  export * from './extract-links.js';
7
8
  export * from './identity.js';
@@ -9,4 +10,6 @@ export * from './normalize.js';
9
10
  export * from './page-elements.js';
10
11
  export * from './request-log.js';
11
12
  export * from './safe-mode.js';
13
+ export * from './stability-scoring.js';
14
+ export * from './synthesize-locators.js';
12
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export * from './accessibility-tree.js';
4
4
  export * from './allowlist.js';
5
5
  export * from './analyze-page.js';
6
6
  export * from './analyze-pages.js';
7
+ export * from './build-selector-registry.js';
7
8
  export * from './crawl.js';
8
9
  export * from './extract-links.js';
9
10
  export * from './identity.js';
@@ -11,4 +12,6 @@ export * from './normalize.js';
11
12
  export * from './page-elements.js';
12
13
  export * from './request-log.js';
13
14
  export * from './safe-mode.js';
15
+ export * from './stability-scoring.js';
16
+ export * from './synthesize-locators.js';
14
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"page-elements.d.ts","sourceRoot":"","sources":["../src/page-elements.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EACV,MAAM,EACN,IAAI,EAEJ,kBAAkB,EAElB,KAAK,EACN,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAqC9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAoJD,qGAAqG;AACrG,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAuCxG"}
1
+ {"version":3,"file":"page-elements.d.ts","sourceRoot":"","sources":["../src/page-elements.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EACV,MAAM,EACN,IAAI,EAEJ,kBAAkB,EAElB,KAAK,EACN,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2C9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAkPD,qGAAqG;AACrG,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAuCxG"}
@@ -17,7 +17,7 @@ function isInteractiveElementKind(value) {
17
17
  * globals unavailable to the rest of this package.
18
18
  */
19
19
  async function readRawPageElements(page) {
20
- /* v8 ignore next 60 -- runs in the browser's own V8 instance, invisible to Node coverage */
20
+ /* v8 ignore next 90 -- runs in the browser's own V8 instance, invisible to Node coverage */
21
21
  const result = await page.evaluate(() => {
22
22
  function accessibleName(element) {
23
23
  const ariaLabel = element.getAttribute('aria-label');
@@ -27,6 +27,63 @@ async function readRawPageElements(page) {
27
27
  const text = element.textContent.trim();
28
28
  return text.length > 0 ? text : undefined;
29
29
  }
30
+ // A small, explicit table rather than full ARIA role computation: locator synthesis only
31
+ // needs a role plausible enough for `getByRole`, and the five kinds below are the only ones
32
+ // this extractor recognizes in the first place.
33
+ function computeRole(element, kind) {
34
+ const explicit = element.getAttribute('role');
35
+ if (explicit !== null && explicit.trim().length > 0) {
36
+ return explicit;
37
+ }
38
+ if (kind === 'textarea') {
39
+ return 'textbox';
40
+ }
41
+ if (kind === 'select') {
42
+ return 'combobox';
43
+ }
44
+ if (kind === 'input') {
45
+ const type = (element.getAttribute('type') ?? 'text').toLowerCase();
46
+ if (type === 'checkbox' || type === 'radio') {
47
+ return type;
48
+ }
49
+ if (type === 'button' || type === 'submit' || type === 'reset') {
50
+ return 'button';
51
+ }
52
+ return 'textbox';
53
+ }
54
+ return kind === 'button' || kind === 'link' ? kind : undefined;
55
+ }
56
+ // `for`/`id` association first, then an ancestor `<label>` that wraps the field.
57
+ function labelText(element) {
58
+ const id = element.getAttribute('id');
59
+ if (id !== null && id.length > 0) {
60
+ const associated = document.querySelector(`label[for="${CSS.escape(id)}"]`);
61
+ const text = associated?.textContent.trim();
62
+ if (text !== undefined && text.length > 0) {
63
+ return text;
64
+ }
65
+ }
66
+ const wrapping = element.closest('label')?.textContent.trim();
67
+ return wrapping !== undefined && wrapping.length > 0 ? wrapping : undefined;
68
+ }
69
+ // Position among same-tag siblings, so a CSS fallback candidate can be built without a live
70
+ // page: `tagName:nth-of-type(nthOfType)` scoped to the element's own parent.
71
+ function nthOfType(element) {
72
+ const parent = element.parentElement;
73
+ if (parent === null) {
74
+ return 1;
75
+ }
76
+ let index = 0;
77
+ for (const sibling of parent.children) {
78
+ if (sibling.tagName === element.tagName) {
79
+ index += 1;
80
+ if (sibling === element) {
81
+ return index;
82
+ }
83
+ }
84
+ }
85
+ return 1;
86
+ }
30
87
  const kindByTagName = {
31
88
  a: 'link',
32
89
  button: 'button',
@@ -34,11 +91,21 @@ async function readRawPageElements(page) {
34
91
  select: 'select',
35
92
  textarea: 'textarea',
36
93
  };
37
- const interactiveElements = Array.from(document.querySelectorAll('button, a[href], input, select, textarea')).map((element) => ({
38
- kind: kindByTagName[element.tagName.toLowerCase()] ?? element.tagName.toLowerCase(),
39
- accessibleName: accessibleName(element),
40
- testId: element.getAttribute('data-testid') ?? undefined,
41
- }));
94
+ const interactiveElements = Array.from(document.querySelectorAll('button, a[href], input, select, textarea')).map((element) => {
95
+ const tagName = element.tagName.toLowerCase();
96
+ const kind = kindByTagName[tagName] ?? tagName;
97
+ return {
98
+ kind,
99
+ accessibleName: accessibleName(element),
100
+ testId: element.getAttribute('data-testid') ?? undefined,
101
+ role: computeRole(element, kind),
102
+ label: labelText(element),
103
+ placeholder: element.getAttribute('placeholder') ?? undefined,
104
+ htmlId: element.getAttribute('id') ?? undefined,
105
+ tagName,
106
+ nthOfType: nthOfType(element),
107
+ };
108
+ });
42
109
  const forms = Array.from(document.querySelectorAll('form')).map((form) => ({
43
110
  action: form.getAttribute('action') ?? undefined,
44
111
  method: (form.getAttribute('method') ?? 'get').toLowerCase(),
@@ -74,7 +141,7 @@ function normalizeInteractiveElement(raw, limits, onTruncated) {
74
141
  if (!isInteractiveElementKind(raw.kind)) {
75
142
  return undefined;
76
143
  }
77
- const element = { kind: raw.kind };
144
+ const element = { kind: raw.kind, tagName: raw.tagName, nthOfType: raw.nthOfType };
78
145
  if (raw.accessibleName !== undefined) {
79
146
  const { text, truncated } = truncateText(raw.accessibleName, limits);
80
147
  element.accessibleName = text;
@@ -85,6 +152,30 @@ function normalizeInteractiveElement(raw, limits, onTruncated) {
85
152
  if (raw.testId !== undefined) {
86
153
  element.testId = raw.testId;
87
154
  }
155
+ if (raw.role !== undefined) {
156
+ element.role = raw.role;
157
+ }
158
+ if (raw.label !== undefined) {
159
+ const { text, truncated } = truncateText(raw.label, limits);
160
+ element.label = text;
161
+ if (truncated) {
162
+ onTruncated();
163
+ }
164
+ }
165
+ if (raw.placeholder !== undefined) {
166
+ const { text, truncated } = truncateText(raw.placeholder, limits);
167
+ element.placeholder = text;
168
+ if (truncated) {
169
+ onTruncated();
170
+ }
171
+ }
172
+ if (raw.htmlId !== undefined) {
173
+ const { text, truncated } = truncateText(raw.htmlId, limits);
174
+ element.htmlId = text;
175
+ if (truncated) {
176
+ onTruncated();
177
+ }
178
+ }
88
179
  return element;
89
180
  }
90
181
  function normalizeForm(raw) {
@@ -1 +1 @@
1
- {"version":3,"file":"page-elements.js","sourceRoot":"","sources":["../src/page-elements.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"page-elements.js","sourceRoot":"","sources":["../src/page-elements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { AuthPage, PageLocator, ViewportSize } from '@qa-ai-stlc/core';
2
+ import type { LocatorCandidate } from '@qa-ai-stlc/schemas';
3
+ export interface StabilityScoringOptions {
4
+ /** Viewport sizes checked after uniqueness and reload survival; defaults to desktop/tablet/mobile. */
5
+ readonly viewports?: readonly ViewportSize[];
6
+ }
7
+ export declare const DEFAULT_VIEWPORTS: readonly ViewportSize[];
8
+ /** Resolves one of P1-08's `LocatorCandidate` values to a live `PageLocator`, by strategy. */
9
+ export declare function resolveCandidateLocator(page: AuthPage, candidate: LocatorCandidate): PageLocator;
10
+ /**
11
+ * Verifies one locator candidate against the current live page: uniqueness now, then survival
12
+ * across a reload and across each configured viewport size (development plan section 6.3.5).
13
+ * Uniqueness is a hard gate — a candidate that does not resolve to exactly one element right now
14
+ * cannot be called stable, so reload/viewport are never checked against a candidate that has
15
+ * already failed. The page's original viewport size is restored before returning, so scoring one
16
+ * element's candidate does not affect the next.
17
+ */
18
+ export declare function scoreLocatorStability(page: AuthPage, candidate: LocatorCandidate, options?: StabilityScoringOptions): Promise<number>;
19
+ //# sourceMappingURL=stability-scoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stability-scoring.d.ts","sourceRoot":"","sources":["../src/stability-scoring.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,sGAAsG;IACtG,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CAC9C;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,YAAY,EAIpD,CAAC;AAkCF,8FAA8F;AAC9F,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,GAAG,WAAW,CAmBhG;AAOD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,gBAAgB,EAC3B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,MAAM,CAAC,CA0BjB"}
@@ -0,0 +1,82 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { QaError } from '@qa-ai-stlc/core';
4
+ export const DEFAULT_VIEWPORTS = [
5
+ { width: 1280, height: 720 },
6
+ { width: 768, height: 1024 },
7
+ { width: 375, height: 667 },
8
+ ];
9
+ function parseRoleCandidateValue(value) {
10
+ let parsed;
11
+ try {
12
+ parsed = JSON.parse(value);
13
+ }
14
+ catch (cause) {
15
+ throw new QaError('locator-candidate-invalid', `Role locator candidate value is not valid JSON: "${value}".`, { cause });
16
+ }
17
+ if (typeof parsed !== 'object' ||
18
+ parsed === null ||
19
+ typeof parsed.role !== 'string') {
20
+ throw new QaError('locator-candidate-invalid', `Role locator candidate value has no "role" string: "${value}".`);
21
+ }
22
+ const record = parsed;
23
+ return typeof record.name === 'string'
24
+ ? { role: record.role, name: record.name }
25
+ : { role: record.role };
26
+ }
27
+ /** Resolves one of P1-08's `LocatorCandidate` values to a live `PageLocator`, by strategy. */
28
+ export function resolveCandidateLocator(page, candidate) {
29
+ switch (candidate.strategy) {
30
+ case 'role': {
31
+ const { role, name } = parseRoleCandidateValue(candidate.value);
32
+ return page.getByRole(role, name === undefined ? undefined : { name });
33
+ }
34
+ case 'testId':
35
+ return page.getByTestId(candidate.value);
36
+ case 'label':
37
+ return page.getByLabel(candidate.value);
38
+ case 'placeholder':
39
+ return page.getByPlaceholder(candidate.value);
40
+ case 'text':
41
+ return page.getByText(candidate.value);
42
+ case 'css':
43
+ return page.locator(candidate.value);
44
+ default:
45
+ throw new QaError('locator-strategy-unknown', `Unknown locator strategy "${candidate.strategy}".`);
46
+ }
47
+ }
48
+ async function isUniqueMatch(page, candidate) {
49
+ const count = await resolveCandidateLocator(page, candidate).count();
50
+ return count === 1;
51
+ }
52
+ /**
53
+ * Verifies one locator candidate against the current live page: uniqueness now, then survival
54
+ * across a reload and across each configured viewport size (development plan section 6.3.5).
55
+ * Uniqueness is a hard gate — a candidate that does not resolve to exactly one element right now
56
+ * cannot be called stable, so reload/viewport are never checked against a candidate that has
57
+ * already failed. The page's original viewport size is restored before returning, so scoring one
58
+ * element's candidate does not affect the next.
59
+ */
60
+ export async function scoreLocatorStability(page, candidate, options = {}) {
61
+ if (!(await isUniqueMatch(page, candidate))) {
62
+ return 0;
63
+ }
64
+ const viewports = options.viewports ?? DEFAULT_VIEWPORTS;
65
+ const originalViewport = page.viewportSize();
66
+ let survivedChecks = 0;
67
+ await page.reload();
68
+ if (await isUniqueMatch(page, candidate)) {
69
+ survivedChecks += 1;
70
+ }
71
+ for (const viewport of viewports) {
72
+ await page.setViewportSize(viewport);
73
+ if (await isUniqueMatch(page, candidate)) {
74
+ survivedChecks += 1;
75
+ }
76
+ }
77
+ if (originalViewport !== null) {
78
+ await page.setViewportSize(originalViewport);
79
+ }
80
+ return survivedChecks / (1 + viewports.length);
81
+ }
82
+ //# sourceMappingURL=stability-scoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stability-scoring.js","sourceRoot":"","sources":["../src/stability-scoring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { InteractiveElement, LocatorCandidate } from '@qa-ai-stlc/schemas';
2
+ export declare const LOCATOR_POLICIES: readonly ["playwright-default", "testid-first", "strict-no-css"];
3
+ export type LocatorPolicy = (typeof LOCATOR_POLICIES)[number];
4
+ /** Generates locator candidates for one interactive element, ordered by the given policy. */
5
+ export declare function synthesizeLocatorCandidates(element: InteractiveElement, policy: LocatorPolicy): LocatorCandidate[];
6
+ //# sourceMappingURL=synthesize-locators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesize-locators.d.ts","sourceRoot":"","sources":["../src/synthesize-locators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,eAAO,MAAM,gBAAgB,kEAAmE,CAAC;AACjG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAyE9D,6FAA6F;AAC7F,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,aAAa,GACpB,gBAAgB,EAAE,CAapB"}
@@ -0,0 +1,74 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export const LOCATOR_POLICIES = ['playwright-default', 'testid-first', 'strict-no-css'];
4
+ // A CSS `id` is untrusted, page-derived text: it can contain anything, including characters that
5
+ // are not valid unescaped in a CSS identifier. Rather than escaping, an id outside this safe
6
+ // pattern falls back to the nth-of-type candidate below.
7
+ const CSS_SAFE_ID = /^[A-Za-z_][A-Za-z0-9_-]*$/;
8
+ function buildRoleCandidate(element) {
9
+ const name = element.accessibleName ?? element.label;
10
+ if (element.role === undefined || name === undefined) {
11
+ return undefined;
12
+ }
13
+ return { strategy: 'role', value: JSON.stringify({ role: element.role, name }), fragile: false };
14
+ }
15
+ function buildTestIdCandidate(element) {
16
+ return element.testId === undefined
17
+ ? undefined
18
+ : { strategy: 'testId', value: element.testId, fragile: false };
19
+ }
20
+ function buildLabelCandidate(element) {
21
+ return element.label === undefined
22
+ ? undefined
23
+ : { strategy: 'label', value: element.label, fragile: false };
24
+ }
25
+ function buildPlaceholderCandidate(element) {
26
+ return element.placeholder === undefined
27
+ ? undefined
28
+ : { strategy: 'placeholder', value: element.placeholder, fragile: false };
29
+ }
30
+ function buildTextCandidate(element) {
31
+ return element.accessibleName === undefined
32
+ ? undefined
33
+ : { strategy: 'text', value: element.accessibleName, fragile: false };
34
+ }
35
+ // The last-resort candidate: always present, always flagged fragile, since a CSS selector breaks
36
+ // under refactors that Playwright's role/label/testid locators survive (development plan 6.3.4).
37
+ function buildCssCandidate(element) {
38
+ const value = element.htmlId !== undefined && CSS_SAFE_ID.test(element.htmlId)
39
+ ? `#${element.htmlId}`
40
+ : `${element.tagName}:nth-of-type(${String(element.nthOfType)})`;
41
+ return { strategy: 'css', value, fragile: true };
42
+ }
43
+ const NON_CSS_BUILDERS = {
44
+ role: buildRoleCandidate,
45
+ testId: buildTestIdCandidate,
46
+ label: buildLabelCandidate,
47
+ placeholder: buildPlaceholderCandidate,
48
+ text: buildTextCandidate,
49
+ };
50
+ // Order follows Playwright's own guidance (development plan 6.3.4): role with an accessible name
51
+ // first, then test ID, then label/placeholder, then text, with CSS always last. `testid-first`
52
+ // swaps the first two for teams that mandate stable test IDs; `strict-no-css` drops the fragile
53
+ // fallback entirely, so an element with none of the other signals gets zero candidates.
54
+ const POLICY_ORDER = {
55
+ 'playwright-default': ['role', 'testId', 'label', 'placeholder', 'text', 'css'],
56
+ 'testid-first': ['testId', 'role', 'label', 'placeholder', 'text', 'css'],
57
+ 'strict-no-css': ['role', 'testId', 'label', 'placeholder', 'text'],
58
+ };
59
+ /** Generates locator candidates for one interactive element, ordered by the given policy. */
60
+ export function synthesizeLocatorCandidates(element, policy) {
61
+ const candidates = [];
62
+ for (const strategy of POLICY_ORDER[policy]) {
63
+ if (strategy === 'css') {
64
+ candidates.push(buildCssCandidate(element));
65
+ continue;
66
+ }
67
+ const candidate = NON_CSS_BUILDERS[strategy](element);
68
+ if (candidate !== undefined) {
69
+ candidates.push(candidate);
70
+ }
71
+ }
72
+ return candidates;
73
+ }
74
+ //# sourceMappingURL=synthesize-locators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesize-locators.js","sourceRoot":"","sources":["../src/synthesize-locators.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qa-ai-stlc/explorer",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "The crawler: route discovery in safe mode, redacted request logging, and the route map every later explorer phase builds on.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -28,8 +28,8 @@
28
28
  "clean": "node ../../scripts/clean-dir.mjs dist"
29
29
  },
30
30
  "dependencies": {
31
- "@qa-ai-stlc/core": "0.2.0",
32
- "@qa-ai-stlc/schemas": "0.2.0"
31
+ "@qa-ai-stlc/core": "0.3.0",
32
+ "@qa-ai-stlc/schemas": "0.4.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@qa-ai-stlc/test-utils": "0.0.0"