@aplaytest/core 0.1.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.
Files changed (112) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/config/schema.d.ts +247 -0
  3. package/dist/config/schema.d.ts.map +1 -0
  4. package/dist/config/schema.js +279 -0
  5. package/dist/config/schema.js.map +1 -0
  6. package/dist/evidence/id.d.ts +17 -0
  7. package/dist/evidence/id.d.ts.map +1 -0
  8. package/dist/evidence/id.js +20 -0
  9. package/dist/evidence/id.js.map +1 -0
  10. package/dist/evidence/redact.d.ts +22 -0
  11. package/dist/evidence/redact.d.ts.map +1 -0
  12. package/dist/evidence/redact.js +97 -0
  13. package/dist/evidence/redact.js.map +1 -0
  14. package/dist/evidence/store.d.ts +51 -0
  15. package/dist/evidence/store.d.ts.map +1 -0
  16. package/dist/evidence/store.js +148 -0
  17. package/dist/evidence/store.js.map +1 -0
  18. package/dist/evidence/types.d.ts +183 -0
  19. package/dist/evidence/types.d.ts.map +1 -0
  20. package/dist/evidence/types.js +21 -0
  21. package/dist/evidence/types.js.map +1 -0
  22. package/dist/history/ingest-playwright.d.ts +26 -0
  23. package/dist/history/ingest-playwright.d.ts.map +1 -0
  24. package/dist/history/ingest-playwright.js +73 -0
  25. package/dist/history/ingest-playwright.js.map +1 -0
  26. package/dist/history/ingest.d.ts +29 -0
  27. package/dist/history/ingest.d.ts.map +1 -0
  28. package/dist/history/ingest.js +86 -0
  29. package/dist/history/ingest.js.map +1 -0
  30. package/dist/history/memory-index.d.ts +88 -0
  31. package/dist/history/memory-index.d.ts.map +1 -0
  32. package/dist/history/memory-index.js +236 -0
  33. package/dist/history/memory-index.js.map +1 -0
  34. package/dist/history/playwright-report.d.ts +102 -0
  35. package/dist/history/playwright-report.d.ts.map +1 -0
  36. package/dist/history/playwright-report.js +274 -0
  37. package/dist/history/playwright-report.js.map +1 -0
  38. package/dist/history/store.d.ts +73 -0
  39. package/dist/history/store.d.ts.map +1 -0
  40. package/dist/history/store.js +243 -0
  41. package/dist/history/store.js.map +1 -0
  42. package/dist/history/types.d.ts +69 -0
  43. package/dist/history/types.d.ts.map +1 -0
  44. package/dist/history/types.js +18 -0
  45. package/dist/history/types.js.map +1 -0
  46. package/dist/history/url.d.ts +79 -0
  47. package/dist/history/url.d.ts.map +1 -0
  48. package/dist/history/url.js +121 -0
  49. package/dist/history/url.js.map +1 -0
  50. package/dist/index.d.ts +39 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +31 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/locator/stability.d.ts +63 -0
  55. package/dist/locator/stability.d.ts.map +1 -0
  56. package/dist/locator/stability.js +127 -0
  57. package/dist/locator/stability.js.map +1 -0
  58. package/dist/taxonomy/classify.d.ts +63 -0
  59. package/dist/taxonomy/classify.d.ts.map +1 -0
  60. package/dist/taxonomy/classify.js +344 -0
  61. package/dist/taxonomy/classify.js.map +1 -0
  62. package/dist/taxonomy/kinds.d.ts +49 -0
  63. package/dist/taxonomy/kinds.d.ts.map +1 -0
  64. package/dist/taxonomy/kinds.js +139 -0
  65. package/dist/taxonomy/kinds.js.map +1 -0
  66. package/dist/taxonomy/parse-error.d.ts +85 -0
  67. package/dist/taxonomy/parse-error.d.ts.map +1 -0
  68. package/dist/taxonomy/parse-error.js +179 -0
  69. package/dist/taxonomy/parse-error.js.map +1 -0
  70. package/dist/version.d.ts +10 -0
  71. package/dist/version.d.ts.map +1 -0
  72. package/dist/version.js +10 -0
  73. package/dist/version.js.map +1 -0
  74. package/dist-cjs/.tsbuildinfo +1 -0
  75. package/dist-cjs/config/schema.js +283 -0
  76. package/dist-cjs/config/schema.js.map +1 -0
  77. package/dist-cjs/evidence/id.js +24 -0
  78. package/dist-cjs/evidence/id.js.map +1 -0
  79. package/dist-cjs/evidence/redact.js +103 -0
  80. package/dist-cjs/evidence/redact.js.map +1 -0
  81. package/dist-cjs/evidence/store.js +156 -0
  82. package/dist-cjs/evidence/store.js.map +1 -0
  83. package/dist-cjs/evidence/types.js +25 -0
  84. package/dist-cjs/evidence/types.js.map +1 -0
  85. package/dist-cjs/history/ingest-playwright.js +76 -0
  86. package/dist-cjs/history/ingest-playwright.js.map +1 -0
  87. package/dist-cjs/history/ingest.js +89 -0
  88. package/dist-cjs/history/ingest.js.map +1 -0
  89. package/dist-cjs/history/memory-index.js +242 -0
  90. package/dist-cjs/history/memory-index.js.map +1 -0
  91. package/dist-cjs/history/playwright-report.js +277 -0
  92. package/dist-cjs/history/playwright-report.js.map +1 -0
  93. package/dist-cjs/history/store.js +247 -0
  94. package/dist-cjs/history/store.js.map +1 -0
  95. package/dist-cjs/history/types.js +23 -0
  96. package/dist-cjs/history/types.js.map +1 -0
  97. package/dist-cjs/history/url.js +127 -0
  98. package/dist-cjs/history/url.js.map +1 -0
  99. package/dist-cjs/index.js +88 -0
  100. package/dist-cjs/index.js.map +1 -0
  101. package/dist-cjs/locator/stability.js +134 -0
  102. package/dist-cjs/locator/stability.js.map +1 -0
  103. package/dist-cjs/package.json +3 -0
  104. package/dist-cjs/taxonomy/classify.js +348 -0
  105. package/dist-cjs/taxonomy/classify.js.map +1 -0
  106. package/dist-cjs/taxonomy/kinds.js +145 -0
  107. package/dist-cjs/taxonomy/kinds.js.map +1 -0
  108. package/dist-cjs/taxonomy/parse-error.js +186 -0
  109. package/dist-cjs/taxonomy/parse-error.js.map +1 -0
  110. package/dist-cjs/version.js +13 -0
  111. package/dist-cjs/version.js.map +1 -0
  112. package/package.json +39 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Deterministic failure classification.
3
+ *
4
+ * No model. An ordered rule list, first match wins, most specific first.
5
+ * Every classification records WHICH rule fired and WHICH signals it saw, so a
6
+ * wrong verdict is debuggable rather than mysterious — the whole system routes
7
+ * on this decision, and an opaque router is an unfixable router.
8
+ *
9
+ * ORDERING IS LOAD-BEARING. Two ordering choices in particular:
10
+ *
11
+ * 1. `infra` is checked first. A crashed browser surfaces as a timeout or a
12
+ * missing element; classifying it as `locator_not_found` would send a
13
+ * dead environment to the healing engine.
14
+ *
15
+ * 2. `schema_violation` and `app_error` are checked BEFORE any locator rule.
16
+ * If the app threw an uncaught exception and, as a consequence, an element
17
+ * never rendered, the root cause is the exception. Healing the selector
18
+ * would paper over a real bug — the exact outcome the NEVER_HEAL set
19
+ * exists to prevent.
20
+ */
21
+ import type { ClassifiableFailure } from '../evidence/types.js';
22
+ import type { FailureKind } from './kinds.js';
23
+ export type Confidence = 'high' | 'medium' | 'low';
24
+ export interface Classification {
25
+ readonly kind: FailureKind;
26
+ /** Name of the rule that fired — appears in the CLI and the evidence bundle. */
27
+ readonly rule: string;
28
+ readonly confidence: Confidence;
29
+ /** Human-readable reasons this rule matched. */
30
+ readonly signals: readonly string[];
31
+ }
32
+ /**
33
+ * Classify a Playwright failure into a {@link FailureKind}.
34
+ *
35
+ * First matching rule wins. The returned `rule` and `signals` are what make a
36
+ * wrong verdict debuggable — the rest of the system routes on this decision.
37
+ *
38
+ * @param input - Message, stack, matcher name, timeout flag, and optional
39
+ * console/network evidence.
40
+ * @returns The kind, the rule that fired, a confidence, and the signals it saw.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const verdict = classify({
45
+ * message: "Error: locator('getByTestId('gym-card-name')') resolved to 0 elements",
46
+ * stack: '',
47
+ * timedOut: true,
48
+ * });
49
+ * // verdict.kind === 'locator_not_found'
50
+ * ```
51
+ */
52
+ export declare function classify(input: ClassifiableFailure): Classification;
53
+ /** Exposed for the CLI's `aplaytest doctor --rules` and for prompt-corpus tooling. */
54
+ /**
55
+ * The ordered rule list, for diagnostics and `aplaytest doctor`.
56
+ *
57
+ * @returns Rule name and kind, in evaluation order (first match wins).
58
+ */
59
+ export declare function listRules(): ReadonlyArray<{
60
+ name: string;
61
+ kind: FailureKind;
62
+ }>;
63
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/taxonomy/classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAoTD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc,CA0BnE;AAED,sFAAsF;AACtF;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAE9E"}
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Deterministic failure classification.
3
+ *
4
+ * No model. An ordered rule list, first match wins, most specific first.
5
+ * Every classification records WHICH rule fired and WHICH signals it saw, so a
6
+ * wrong verdict is debuggable rather than mysterious — the whole system routes
7
+ * on this decision, and an opaque router is an unfixable router.
8
+ *
9
+ * ORDERING IS LOAD-BEARING. Two ordering choices in particular:
10
+ *
11
+ * 1. `infra` is checked first. A crashed browser surfaces as a timeout or a
12
+ * missing element; classifying it as `locator_not_found` would send a
13
+ * dead environment to the healing engine.
14
+ *
15
+ * 2. `schema_violation` and `app_error` are checked BEFORE any locator rule.
16
+ * If the app threw an uncaught exception and, as a consequence, an element
17
+ * never rendered, the root cause is the exception. Healing the selector
18
+ * would paper over a real bug — the exact outcome the NEVER_HEAL set
19
+ * exists to prevent.
20
+ */
21
+ import { stripCodeFrame } from './parse-error.js';
22
+ const has = (haystack, needle) => haystack.toLowerCase().includes(needle.toLowerCase());
23
+ const anyOf = (haystack, needles) => needles.filter(n => has(haystack, n));
24
+ const matcherIs = (f, ...names) => f.matcher !== null && names.some(n => f.matcher?.toLowerCase().includes(n.toLowerCase()) === true);
25
+ /** Signals that the application itself threw, rather than the test mis-looking. */
26
+ const UNCAUGHT_PATTERNS = [
27
+ 'Uncaught',
28
+ 'Unhandled',
29
+ 'Unhandled Promise Rejection',
30
+ 'TypeError:',
31
+ 'ReferenceError:',
32
+ 'The above error occurred in',
33
+ 'React will try to recreate',
34
+ ];
35
+ const RULES = [
36
+ // ── 1. Infrastructure ────────────────────────────────────────────────────
37
+ // First, always. A dead browser must never reach the healing engine.
38
+ {
39
+ name: 'infra.browser-closed',
40
+ kind: 'infra',
41
+ confidence: 'high',
42
+ match: f => {
43
+ const hits = anyOf(f.message + f.stack, [
44
+ 'Target page, context or browser has been closed',
45
+ 'Browser has been closed',
46
+ 'browserType.launch',
47
+ "Executable doesn't exist",
48
+ 'Protocol error',
49
+ 'Target crashed',
50
+ 'SIGSEGV',
51
+ 'out of memory',
52
+ ]);
53
+ return hits.length > 0 ? hits : null;
54
+ },
55
+ },
56
+ // ── 2. Contract violations — never healable ──────────────────────────────
57
+ {
58
+ name: 'contract.schema-violation',
59
+ kind: 'schema_violation',
60
+ confidence: 'high',
61
+ match: f => {
62
+ const hits = anyOf(f.message + f.stack, [
63
+ 'ZodError',
64
+ 'Validation error:',
65
+ 'invalid_type',
66
+ 'unrecognized_keys',
67
+ 'invalid_union',
68
+ 'Expected object, received',
69
+ 'Required at ',
70
+ ]);
71
+ const fromNetwork = f.failedRequests
72
+ .filter(r => r.schemaError !== null)
73
+ .map(r => `schema error on ${r.method} ${r.url}`);
74
+ const all = [...hits, ...fromNetwork];
75
+ return all.length > 0 ? all : null;
76
+ },
77
+ },
78
+ {
79
+ name: 'contract.app-error',
80
+ kind: 'app_error',
81
+ confidence: 'medium',
82
+ match: f => {
83
+ // Deliberately requires an UNCAUGHT signal, not merely the presence of
84
+ // console output. A stray console.warn must not veto a legitimate heal.
85
+ const consoleHits = f.consoleErrors.filter(e => UNCAUGHT_PATTERNS.some(p => has(e, p)));
86
+ const messageHits = anyOf(f.message, ['pageerror', 'page.on(\'pageerror\')']);
87
+ const all = [
88
+ ...consoleHits.map(e => `uncaught in console: ${e.slice(0, 120)}`),
89
+ ...messageHits,
90
+ ];
91
+ return all.length > 0 ? all : null;
92
+ },
93
+ },
94
+ // ── 3. Transport ─────────────────────────────────────────────────────────
95
+ {
96
+ name: 'transport.network-error',
97
+ kind: 'network_error',
98
+ confidence: 'high',
99
+ match: f => {
100
+ const hits = anyOf(f.message + f.stack, [
101
+ 'net::ERR_CONNECTION_REFUSED',
102
+ 'net::ERR_CONNECTION_RESET',
103
+ 'net::ERR_NAME_NOT_RESOLVED',
104
+ 'net::ERR_INTERNET_DISCONNECTED',
105
+ 'ECONNREFUSED',
106
+ 'ETIMEDOUT',
107
+ 'socket hang up',
108
+ 'fetch failed',
109
+ ]);
110
+ const fromRequests = f.failedRequests
111
+ .filter(r => r.failureText !== null)
112
+ .map(r => `request failed: ${r.method} ${r.url}`);
113
+ const all = [...hits, ...fromRequests];
114
+ return all.length > 0 ? all : null;
115
+ },
116
+ },
117
+ {
118
+ name: 'transport.navigation-failure',
119
+ kind: 'navigation_failure',
120
+ confidence: 'high',
121
+ match: f => {
122
+ const hits = anyOf(f.message, [
123
+ 'page.goto',
124
+ 'Navigation timeout',
125
+ 'navigation failed',
126
+ 'net::ERR_ABORTED',
127
+ ]);
128
+ return hits.length > 0 ? hits : null;
129
+ },
130
+ },
131
+ {
132
+ name: 'transport.http-status',
133
+ kind: 'http_status',
134
+ confidence: 'high',
135
+ match: f => {
136
+ if (matcherIs(f, 'toBeOK'))
137
+ return ['matcher toBeOK failed'];
138
+ const hits = anyOf(f.message, [
139
+ 'Unexpected status',
140
+ 'expected status',
141
+ 'toBeOK',
142
+ 'Response status code',
143
+ ]);
144
+ return hits.length > 0 ? hits : null;
145
+ },
146
+ },
147
+ // ── 4. Snapshot comparisons ──────────────────────────────────────────────
148
+ {
149
+ name: 'snapshot.visual-diff',
150
+ kind: 'visual_diff',
151
+ confidence: 'high',
152
+ match: f => {
153
+ if (matcherIs(f, 'toHaveScreenshot'))
154
+ return ['matcher toHaveScreenshot'];
155
+ const hits = anyOf(f.message, [
156
+ 'Screenshot comparison failed',
157
+ 'toHaveScreenshot',
158
+ 'pixels (ratio',
159
+ 'A snapshot doesn\'t exist at',
160
+ ]);
161
+ return hits.length > 0 ? hits : null;
162
+ },
163
+ },
164
+ {
165
+ name: 'snapshot.aria-diff',
166
+ kind: 'aria_diff',
167
+ confidence: 'high',
168
+ match: f => {
169
+ if (matcherIs(f, 'toMatchAriaSnapshot'))
170
+ return ['matcher toMatchAriaSnapshot'];
171
+ const hits = anyOf(f.message, ['toMatchAriaSnapshot', 'aria snapshot']);
172
+ return hits.length > 0 ? hits : null;
173
+ },
174
+ },
175
+ // ── 5. Locator resolution ────────────────────────────────────────────────
176
+ {
177
+ name: 'locator.ambiguous',
178
+ kind: 'locator_ambiguous',
179
+ confidence: 'high',
180
+ match: f => {
181
+ const hits = anyOf(f.message, ['strict mode violation', 'resolved to 2 elements']);
182
+ if (hits.length > 0)
183
+ return hits;
184
+ // "resolved to N elements" for any N > 1
185
+ return /resolved to (\d+) elements/.test(f.message) &&
186
+ !/resolved to 0 elements/.test(f.message)
187
+ ? ['strict mode: multiple matches']
188
+ : null;
189
+ },
190
+ },
191
+ {
192
+ name: 'locator.not-actionable',
193
+ kind: 'locator_not_actionable',
194
+ confidence: 'high',
195
+ match: f => {
196
+ const hits = anyOf(f.message, [
197
+ 'element is not visible',
198
+ 'element is not enabled',
199
+ 'element is not stable',
200
+ 'element is not editable',
201
+ 'intercepts pointer events',
202
+ 'element is outside of the viewport',
203
+ 'waiting for element to be visible, enabled and stable',
204
+ ]);
205
+ return hits.length > 0 ? hits : null;
206
+ },
207
+ },
208
+ {
209
+ name: 'locator.not-found',
210
+ kind: 'locator_not_found',
211
+ confidence: 'high',
212
+ match: f => {
213
+ const hits = anyOf(f.message, [
214
+ 'resolved to 0 elements',
215
+ // Playwright renders a failed visibility assertion on a missing
216
+ // element as `Received: <element(s) not found>`. That is a LOCATOR
217
+ // problem (the address is gone), not a visibility problem — and the
218
+ // distinction decides whether healing is even applicable, so this
219
+ // rule must stay ahead of assertion.visibility.
220
+ 'element(s) not found',
221
+ 'locator resolved to hidden',
222
+ 'no element matching',
223
+ 'Element is not attached to the DOM',
224
+ ]);
225
+ // Playwright's call log says "waiting for getByTestId('…')" or
226
+ // "waiting for locator('…')" depending on the API used.
227
+ const waiting = /waiting for (locator|getBy|frameLocator)/i.test(f.message)
228
+ ? ['call log: waiting for an element that never appeared']
229
+ : [];
230
+ // An action that timed out while resolving a locator is a not-found,
231
+ // not a generic test timeout.
232
+ const actionTimeout = f.timedOut && /locator\.\w+:|frameLocator|\.click\(|\.fill\(/.test(f.message)
233
+ ? ['action timed out resolving its locator']
234
+ : [];
235
+ const all = [...hits, ...waiting, ...actionTimeout];
236
+ return all.length > 0 ? all : null;
237
+ },
238
+ },
239
+ // ── 6. Assertions ────────────────────────────────────────────────────────
240
+ {
241
+ name: 'assertion.visibility',
242
+ kind: 'assertion_visibility',
243
+ confidence: 'high',
244
+ match: f => {
245
+ if (matcherIs(f, 'toBeVisible', 'toBeHidden', 'toBeAttached', 'toHaveCount')) {
246
+ return [`matcher ${f.matcher ?? ''} timed out`];
247
+ }
248
+ const hits = anyOf(f.message, ['toBeVisible', 'toBeHidden', 'toBeAttached', 'toHaveCount']);
249
+ return hits.length > 0 ? hits : null;
250
+ },
251
+ },
252
+ {
253
+ name: 'assertion.value-mismatch',
254
+ kind: 'assertion_value_mismatch',
255
+ confidence: 'high',
256
+ match: f => {
257
+ if (matcherIs(f, 'toHaveText', 'toContainText', 'toHaveValue', 'toHaveAttribute', 'toMatchObject', 'toEqual', 'toBe', 'toHaveURL', 'toHaveTitle')) {
258
+ return [`matcher ${f.matcher ?? ''} value mismatch`];
259
+ }
260
+ const hasExpectedReceived = (has(f.message, 'Expected') && has(f.message, 'Received')) ||
261
+ has(f.message, 'Expected string') ||
262
+ has(f.message, 'Expected pattern');
263
+ return hasExpectedReceived ? ['expected/received mismatch in message'] : null;
264
+ },
265
+ },
266
+ ];
267
+ /** Fallback used when a test timed out but nothing more specific matched. */
268
+ const TEST_TIMEOUT_RULE = {
269
+ name: 'timeout.test-level',
270
+ kind: 'assertion_visibility',
271
+ confidence: 'low',
272
+ match: f => f.timedOut || f.budgetUsedRatio >= 0.95
273
+ ? [`test consumed ${(f.budgetUsedRatio * 100).toFixed(0)}% of its time budget`]
274
+ : null,
275
+ };
276
+ /**
277
+ * Every rule below matches against `message` and `stack`, so the code frame
278
+ * Playwright embeds in a message — the user's own source, quoted with context —
279
+ * is matched against too. Strip it here, at the single choke point, rather than
280
+ * at each call site: there are two of those today (the reporter and the
281
+ * Playwright-JSON adapter), a caller who forgets gets silently wrong verdicts,
282
+ * and "silently wrong" is the one failure mode a router must not have.
283
+ */
284
+ function forMatching(failure) {
285
+ return {
286
+ ...failure,
287
+ message: stripCodeFrame(failure.message),
288
+ stack: stripCodeFrame(failure.stack),
289
+ };
290
+ }
291
+ /**
292
+ * Classify a Playwright failure into a {@link FailureKind}.
293
+ *
294
+ * First matching rule wins. The returned `rule` and `signals` are what make a
295
+ * wrong verdict debuggable — the rest of the system routes on this decision.
296
+ *
297
+ * @param input - Message, stack, matcher name, timeout flag, and optional
298
+ * console/network evidence.
299
+ * @returns The kind, the rule that fired, a confidence, and the signals it saw.
300
+ *
301
+ * @example
302
+ * ```ts
303
+ * const verdict = classify({
304
+ * message: "Error: locator('getByTestId('gym-card-name')') resolved to 0 elements",
305
+ * stack: '',
306
+ * timedOut: true,
307
+ * });
308
+ * // verdict.kind === 'locator_not_found'
309
+ * ```
310
+ */
311
+ export function classify(input) {
312
+ const failure = forMatching(input);
313
+ for (const rule of RULES) {
314
+ const signals = rule.match(failure);
315
+ if (signals !== null && signals.length > 0) {
316
+ return { kind: rule.kind, rule: rule.name, confidence: rule.confidence, signals };
317
+ }
318
+ }
319
+ const timeoutSignals = TEST_TIMEOUT_RULE.match(failure);
320
+ if (timeoutSignals !== null && timeoutSignals.length > 0) {
321
+ return {
322
+ kind: TEST_TIMEOUT_RULE.kind,
323
+ rule: TEST_TIMEOUT_RULE.name,
324
+ confidence: TEST_TIMEOUT_RULE.confidence,
325
+ signals: timeoutSignals,
326
+ };
327
+ }
328
+ return {
329
+ kind: 'unknown',
330
+ rule: 'none',
331
+ confidence: 'low',
332
+ signals: ['no rule matched — this is a taxonomy gap worth reporting'],
333
+ };
334
+ }
335
+ /** Exposed for the CLI's `aplaytest doctor --rules` and for prompt-corpus tooling. */
336
+ /**
337
+ * The ordered rule list, for diagnostics and `aplaytest doctor`.
338
+ *
339
+ * @returns Rule name and kind, in evaluation order (first match wins).
340
+ */
341
+ export function listRules() {
342
+ return [...RULES, TEST_TIMEOUT_RULE].map(r => ({ name: r.name, kind: r.kind }));
343
+ }
344
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/taxonomy/classify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The failure taxonomy.
3
+ *
4
+ * Classification is deterministic — string and structure matching over the
5
+ * Playwright error plus features of the evidence bundle. No model is involved.
6
+ * The taxonomy exists to ROUTE a failure: to healing, to flaky analysis, or to
7
+ * "stop, this is a real bug".
8
+ */
9
+ export declare const FAILURE_KINDS: readonly ["locator_not_found", "locator_ambiguous", "locator_not_actionable", "assertion_value_mismatch", "assertion_visibility", "visual_diff", "aria_diff", "navigation_failure", "network_error", "http_status", "schema_violation", "app_error", "infra", "unknown"];
10
+ export type FailureKind = (typeof FAILURE_KINDS)[number];
11
+ /**
12
+ * Kinds that must NEVER produce a heal proposal, at any aggressiveness level.
13
+ *
14
+ * This is the most important guard in the system and it is code, not policy.
15
+ * A suite's Zod schemas and console-error assertions exist so that drift fails
16
+ * LOUDLY; a framework that "helpfully" repaired a `schema_violation` would
17
+ * destroy the most valuable signal the suite produces.
18
+ */
19
+ export declare const NEVER_HEAL: ReadonlySet<FailureKind>;
20
+ /** How far a heal may go for a given kind. */
21
+ export type HealEligibility = 'full' | 'propose-only' | 'never';
22
+ /** Whether a kind should feed the flaky engine, and how strongly. */
23
+ export type FlakeRelevance = 'strong' | 'weak' | 'excluded';
24
+ export interface KindRouting {
25
+ readonly heal: HealEligibility;
26
+ readonly flake: FlakeRelevance;
27
+ /** Short, user-facing reason shown in the CLI when a heal is refused. */
28
+ readonly note: string;
29
+ }
30
+ export declare const ROUTING: Readonly<Record<FailureKind, KindRouting>>;
31
+ /**
32
+ * How far a heal may go for this kind. `NEVER_HEAL` wins over `ROUTING`.
33
+ *
34
+ * @param kind - A classified failure kind.
35
+ * @returns `'never'`, `'propose-only'`, or `'full'`.
36
+ */
37
+ export declare function healEligibility(kind: FailureKind): HealEligibility;
38
+ /**
39
+ * @param kind - A classified failure kind.
40
+ * @returns `true` when a heal may be proposed (not necessarily auto-applied).
41
+ */
42
+ export declare function isHealable(kind: FailureKind): boolean;
43
+ /**
44
+ * @param kind - A classified failure kind.
45
+ * @returns `false` for infra, HTTP status, and schema violations — those are
46
+ * not evidence of flakiness.
47
+ */
48
+ export declare function countsTowardFlakeStats(kind: FailureKind): boolean;
49
+ //# sourceMappingURL=kinds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kinds.d.ts","sourceRoot":"","sources":["../../src/taxonomy/kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,aAAa,0QAehB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,WAAW,CAO9C,CAAC;AAEH,8CAA8C;AAC9C,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,cAAc,GACd,OAAO,CAAC;AAEZ,qEAAqE;AACrE,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,MAAM,GACN,UAAU,CAAC;AAEf,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAuE9D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CAGlE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The failure taxonomy.
3
+ *
4
+ * Classification is deterministic — string and structure matching over the
5
+ * Playwright error plus features of the evidence bundle. No model is involved.
6
+ * The taxonomy exists to ROUTE a failure: to healing, to flaky analysis, or to
7
+ * "stop, this is a real bug".
8
+ */
9
+ export const FAILURE_KINDS = [
10
+ 'locator_not_found', // strict mode: 0 matches
11
+ 'locator_ambiguous', // strict mode violation: >1 match
12
+ 'locator_not_actionable', // found, but intercepted / disabled / unstable
13
+ 'assertion_value_mismatch', // toHaveText / toMatchObject / toHaveValue …
14
+ 'assertion_visibility', // toBeVisible / toBeHidden timed out
15
+ 'visual_diff', // toHaveScreenshot
16
+ 'aria_diff', // toMatchAriaSnapshot
17
+ 'navigation_failure',
18
+ 'network_error', // request failed / connection refused
19
+ 'http_status', // unexpected status in an API spec
20
+ 'schema_violation', // Zod parse failed — the wire contract broke
21
+ 'app_error', // console error / error boundary rendered
22
+ 'infra', // browser crash, OOM, port-forward down
23
+ 'unknown',
24
+ ];
25
+ /**
26
+ * Kinds that must NEVER produce a heal proposal, at any aggressiveness level.
27
+ *
28
+ * This is the most important guard in the system and it is code, not policy.
29
+ * A suite's Zod schemas and console-error assertions exist so that drift fails
30
+ * LOUDLY; a framework that "helpfully" repaired a `schema_violation` would
31
+ * destroy the most valuable signal the suite produces.
32
+ */
33
+ export const NEVER_HEAL = new Set([
34
+ 'schema_violation',
35
+ 'app_error',
36
+ 'http_status',
37
+ 'network_error',
38
+ 'navigation_failure',
39
+ 'infra',
40
+ ]);
41
+ export const ROUTING = {
42
+ locator_not_found: {
43
+ heal: 'full',
44
+ flake: 'weak',
45
+ note: 'Element address is gone. The bread-and-butter heal.',
46
+ },
47
+ locator_ambiguous: {
48
+ heal: 'full',
49
+ flake: 'weak',
50
+ note: 'More than one match — often a genuine duplication in the app.',
51
+ },
52
+ locator_not_actionable: {
53
+ heal: 'propose-only',
54
+ flake: 'strong',
55
+ note: 'Found but not interactable. Usually timing or animation, not a wrong selector.',
56
+ },
57
+ assertion_value_mismatch: {
58
+ heal: 'propose-only',
59
+ flake: 'weak',
60
+ note: 'Changing an assertion changes what the test proves. Human decides.',
61
+ },
62
+ assertion_visibility: {
63
+ heal: 'propose-only',
64
+ flake: 'strong',
65
+ note: 'Most common genuine flake.',
66
+ },
67
+ visual_diff: {
68
+ heal: 'propose-only',
69
+ flake: 'weak',
70
+ note: 'Route to the snapshot workflow, not to selector healing.',
71
+ },
72
+ aria_diff: {
73
+ heal: 'propose-only',
74
+ flake: 'weak',
75
+ note: 'A semantic change — nearly always a real change.',
76
+ },
77
+ navigation_failure: {
78
+ heal: 'never',
79
+ flake: 'strong',
80
+ note: 'Environment or app. Not a selector problem.',
81
+ },
82
+ network_error: {
83
+ heal: 'never',
84
+ flake: 'strong',
85
+ note: 'Environment or app. Not a selector problem.',
86
+ },
87
+ http_status: {
88
+ heal: 'never',
89
+ flake: 'weak',
90
+ note: 'The API behaved differently. That is the finding.',
91
+ },
92
+ schema_violation: {
93
+ heal: 'never',
94
+ flake: 'excluded',
95
+ note: 'The wire contract broke. This IS the bug — never repair it.',
96
+ },
97
+ app_error: {
98
+ heal: 'never',
99
+ flake: 'weak',
100
+ note: 'The application errored. This IS the bug — never repair it.',
101
+ },
102
+ infra: {
103
+ heal: 'never',
104
+ flake: 'excluded',
105
+ note: 'Browser crash or environment failure. Not evidence about the test.',
106
+ },
107
+ unknown: {
108
+ heal: 'never',
109
+ flake: 'weak',
110
+ note: 'Unclassified. Every occurrence is a taxonomy gap worth reporting.',
111
+ },
112
+ };
113
+ /**
114
+ * How far a heal may go for this kind. `NEVER_HEAL` wins over `ROUTING`.
115
+ *
116
+ * @param kind - A classified failure kind.
117
+ * @returns `'never'`, `'propose-only'`, or `'full'`.
118
+ */
119
+ export function healEligibility(kind) {
120
+ if (NEVER_HEAL.has(kind))
121
+ return 'never';
122
+ return ROUTING[kind].heal;
123
+ }
124
+ /**
125
+ * @param kind - A classified failure kind.
126
+ * @returns `true` when a heal may be proposed (not necessarily auto-applied).
127
+ */
128
+ export function isHealable(kind) {
129
+ return healEligibility(kind) !== 'never';
130
+ }
131
+ /**
132
+ * @param kind - A classified failure kind.
133
+ * @returns `false` for infra, HTTP status, and schema violations — those are
134
+ * not evidence of flakiness.
135
+ */
136
+ export function countsTowardFlakeStats(kind) {
137
+ return ROUTING[kind].flake !== 'excluded';
138
+ }
139
+ //# sourceMappingURL=kinds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kinds.js","sourceRoot":"","sources":["../../src/taxonomy/kinds.ts"],"names":[],"mappings":"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"}