@ia-qa/self-healing 1.15.0 → 1.16.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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/listChurn.d.ts +63 -0
- package/dist/listChurn.js +92 -0
- package/dist/listChurn.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ export type { AuditOutcome, AuditSuccess } from './cli/audit';
|
|
|
46
46
|
export { detectTestCommand } from './cli/init';
|
|
47
47
|
export { groupNumericLabelDrift, maskDigits } from './nameHint';
|
|
48
48
|
export type { NameDriftGroup, RenamedRowLike } from './nameHint';
|
|
49
|
+
export { groupListChurn, listShape } from './listChurn';
|
|
50
|
+
export type { ListChurnGroup, ChurnElementLike, ChurnRowLike } from './listChurn';
|
|
49
51
|
export { planSetup, writeSetup, readSitemap, isWebAddress } from './ui/setup';
|
|
50
52
|
export type { SetupRequest, SetupPlan, SetupWrite, SitemapAnswer, LoginMode } from './ui/setup';
|
|
51
53
|
export type { CheckReport, CheckVerdict } from './checks';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.captureDir = exports.unionElements = exports.mergeCaptures = exports.globToRegex = exports.readTestDepth = exports.collectUnattributable = exports.computePageDepth = exports.computeTestDepth = exports.saveExercised = exports.EXERCISED_FILENAME = exports.contractPageNames = exports.exercisedPath = exports.loadExercised = exports.usagePath = exports.usageFiles = exports.loadUsage = exports.saveUsage = exports.ingestPaths = exports.scanText = exports.crawlApp = exports.sitemapsFromRobots = exports.isSitemapIndex = exports.extractLocs = exports.discoverFromSitemap = exports.UNSAFE_URL_RE = exports.mergePages = exports.toCandidate = exports.normalizedPath = exports.isSafeCandidateUrl = exports.resolvePageName = exports.pageNameFromUrl = exports.mapUrl = exports.markdownPath = exports.saveMarkdown = exports.renderPageContract = exports.saveMapping = exports.loadMapping = exports.extractInteractiveElements = exports.renderModelList = exports.defaultKeyEnv = exports.findProvider = exports.AI_PROVIDERS = exports.parseModelJson = exports.buildPrompt = exports.prefilter = exports.aiResolve = exports.createAiResolver = exports.llmResolverStub = exports.aiFill = exports.aiClick = void 0;
|
|
4
|
-
exports.NAVIGATION_SVG_FILENAME = exports.NAVIGATION_FILENAME = exports.OVERVIEW_FILENAME = exports.navigationSvgPath = exports.navigationPath = exports.overviewPath = exports.writeOverview = exports.navigationGraph = exports.layoutGraph = exports.renderNavigationSvg = exports.renderNavigation = exports.renderOverview = exports.buildOverview = exports.minPagesForLayout = exports.stripLayoutFromPages = exports.extractLayout = exports.isWebAddress = exports.readSitemap = exports.writeSetup = exports.planSetup = exports.maskDigits = exports.groupNumericLabelDrift = exports.detectTestCommand = exports.auditProject = exports.runFix = exports.checkCounts = exports.checkHeadline = exports.checkVerdict = exports.checkProject = exports.dirDiffAggregate = exports.runBaseline = exports.LoginWallError = exports.runMap = exports.baselineDir = exports.mappingDir = exports.setBaseDir = exports.LAYOUT_DIRNAME = exports.layoutPath = exports.layoutDir = exports.resolveSecret = exports.loadConfig = exports.replaceQuoted = exports.collectFiles = exports.applyRewrites = exports.computeRewrites = exports.launchBrowser = exports.CAPTURE_DIRNAME = void 0;
|
|
4
|
+
exports.NAVIGATION_SVG_FILENAME = exports.NAVIGATION_FILENAME = exports.OVERVIEW_FILENAME = exports.navigationSvgPath = exports.navigationPath = exports.overviewPath = exports.writeOverview = exports.navigationGraph = exports.layoutGraph = exports.renderNavigationSvg = exports.renderNavigation = exports.renderOverview = exports.buildOverview = exports.minPagesForLayout = exports.stripLayoutFromPages = exports.extractLayout = exports.isWebAddress = exports.readSitemap = exports.writeSetup = exports.planSetup = exports.listShape = exports.groupListChurn = exports.maskDigits = exports.groupNumericLabelDrift = exports.detectTestCommand = exports.auditProject = exports.runFix = exports.checkCounts = exports.checkHeadline = exports.checkVerdict = exports.checkProject = exports.dirDiffAggregate = exports.runBaseline = exports.LoginWallError = exports.runMap = exports.baselineDir = exports.mappingDir = exports.setBaseDir = exports.LAYOUT_DIRNAME = exports.layoutPath = exports.layoutDir = exports.resolveSecret = exports.loadConfig = exports.replaceQuoted = exports.collectFiles = exports.applyRewrites = exports.computeRewrites = exports.launchBrowser = exports.CAPTURE_DIRNAME = void 0;
|
|
5
5
|
var healer_1 = require("./playwright/healer");
|
|
6
6
|
Object.defineProperty(exports, "aiClick", { enumerable: true, get: function () { return healer_1.aiClick; } });
|
|
7
7
|
Object.defineProperty(exports, "aiFill", { enumerable: true, get: function () { return healer_1.aiFill; } });
|
|
@@ -118,6 +118,10 @@ Object.defineProperty(exports, "detectTestCommand", { enumerable: true, get: fun
|
|
|
118
118
|
var nameHint_1 = require("./nameHint");
|
|
119
119
|
Object.defineProperty(exports, "groupNumericLabelDrift", { enumerable: true, get: function () { return nameHint_1.groupNumericLabelDrift; } });
|
|
120
120
|
Object.defineProperty(exports, "maskDigits", { enumerable: true, get: function () { return nameHint_1.maskDigits; } });
|
|
121
|
+
// Lists whose items are data — noticed here, so every surface groups them the same way.
|
|
122
|
+
var listChurn_1 = require("./listChurn");
|
|
123
|
+
Object.defineProperty(exports, "groupListChurn", { enumerable: true, get: function () { return listChurn_1.groupListChurn; } });
|
|
124
|
+
Object.defineProperty(exports, "listShape", { enumerable: true, get: function () { return listChurn_1.listShape; } });
|
|
121
125
|
// The setup door `ui` uses: preview and write through one plan, never overwriting, no secret field.
|
|
122
126
|
var setup_1 = require("./ui/setup");
|
|
123
127
|
Object.defineProperty(exports, "planSetup", { enumerable: true, get: function () { return setup_1.planSetup; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-list churn — the noise class two field reports named first.
|
|
3
|
+
*
|
|
4
|
+
* A catalogue, a feed, a results table: the list is UI, its items are data. When the data
|
|
5
|
+
* changes, the contract diff is right row by row and useless as a whole — measured on the
|
|
6
|
+
* witness app, one list of eight products rotating half its items produced 5 ambiguous,
|
|
7
|
+
* 1 lost, 6 rebound, 4 healable and 6 added, and a real lost button next to it was one line
|
|
8
|
+
* among eighteen.
|
|
9
|
+
*
|
|
10
|
+
* This module only *notices*, like `nameHint.ts`: the rows, the counts and the verdict are
|
|
11
|
+
* untouched. What a caller does with a group is its own policy.
|
|
12
|
+
*
|
|
13
|
+
* A group is claimed on three facts, and each one exists because a narrower rule was wrong:
|
|
14
|
+
* - **a shape** — elements of one role whose selector differs only by a position
|
|
15
|
+
* (`li:nth-of-type(n)`) or whose link differs only by an id-like path segment
|
|
16
|
+
* (`/item/101`). Shape alone is not enough: inserting one button above three others shifts
|
|
17
|
+
* every position too, and that is the `rebound` this package exists to catch.
|
|
18
|
+
* - **a list, not a toolbar** — at least `MIN_MEMBERS` elements of that shape in the baseline.
|
|
19
|
+
* - **replacement both ways** — at least `MIN_REPLACED` names gone *and* as many new names
|
|
20
|
+
* arrived. An insertion keeps every old name; an emptied list brings no new one. Both stay
|
|
21
|
+
* loud: an empty catalogue is a regression, not data.
|
|
22
|
+
*/
|
|
23
|
+
export interface ChurnElementLike {
|
|
24
|
+
role: string;
|
|
25
|
+
name: string;
|
|
26
|
+
selector: string;
|
|
27
|
+
href?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ChurnRowLike {
|
|
30
|
+
status: string;
|
|
31
|
+
role: string;
|
|
32
|
+
name: string;
|
|
33
|
+
selector: string;
|
|
34
|
+
href?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ListChurnGroup {
|
|
37
|
+
role: string;
|
|
38
|
+
/** The shape, readable: `a[href="/item/:id"]` or `#latest > li:nth-of-type(*) > button`. */
|
|
39
|
+
shape: string;
|
|
40
|
+
/** Baseline members whose name is nowhere in the current list. */
|
|
41
|
+
gone: string[];
|
|
42
|
+
/** Current members whose name was nowhere in the baseline list. */
|
|
43
|
+
appeared: string[];
|
|
44
|
+
/** Members present on both sides. */
|
|
45
|
+
kept: number;
|
|
46
|
+
/** Baseline selectors of the rows this group accounts for — every non-`ok` row of the shape. */
|
|
47
|
+
selectors: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare const MIN_MEMBERS = 4;
|
|
50
|
+
export declare const MIN_REPLACED = 2;
|
|
51
|
+
/**
|
|
52
|
+
* The shape an element belongs to, or `null` when it has none — most elements.
|
|
53
|
+
* A link is shaped by its path with id-like segments collapsed; anything else by its
|
|
54
|
+
* selector with positions collapsed. A link whose path holds no id-like segment is not
|
|
55
|
+
* shaped at all: `/products` and `/cart` are navigation, never a list.
|
|
56
|
+
*/
|
|
57
|
+
export declare function listShape(el: {
|
|
58
|
+
role: string;
|
|
59
|
+
selector: string;
|
|
60
|
+
href?: string;
|
|
61
|
+
}): string | null;
|
|
62
|
+
/** Lists on one page whose items were replaced. Pure; empty on an ordinary page. */
|
|
63
|
+
export declare function groupListChurn(baseline: ChurnElementLike[], current: ChurnElementLike[], rows: ChurnRowLike[]): ListChurnGroup[];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Data-list churn — the noise class two field reports named first.
|
|
4
|
+
*
|
|
5
|
+
* A catalogue, a feed, a results table: the list is UI, its items are data. When the data
|
|
6
|
+
* changes, the contract diff is right row by row and useless as a whole — measured on the
|
|
7
|
+
* witness app, one list of eight products rotating half its items produced 5 ambiguous,
|
|
8
|
+
* 1 lost, 6 rebound, 4 healable and 6 added, and a real lost button next to it was one line
|
|
9
|
+
* among eighteen.
|
|
10
|
+
*
|
|
11
|
+
* This module only *notices*, like `nameHint.ts`: the rows, the counts and the verdict are
|
|
12
|
+
* untouched. What a caller does with a group is its own policy.
|
|
13
|
+
*
|
|
14
|
+
* A group is claimed on three facts, and each one exists because a narrower rule was wrong:
|
|
15
|
+
* - **a shape** — elements of one role whose selector differs only by a position
|
|
16
|
+
* (`li:nth-of-type(n)`) or whose link differs only by an id-like path segment
|
|
17
|
+
* (`/item/101`). Shape alone is not enough: inserting one button above three others shifts
|
|
18
|
+
* every position too, and that is the `rebound` this package exists to catch.
|
|
19
|
+
* - **a list, not a toolbar** — at least `MIN_MEMBERS` elements of that shape in the baseline.
|
|
20
|
+
* - **replacement both ways** — at least `MIN_REPLACED` names gone *and* as many new names
|
|
21
|
+
* arrived. An insertion keeps every old name; an emptied list brings no new one. Both stay
|
|
22
|
+
* loud: an empty catalogue is a regression, not data.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.MIN_REPLACED = exports.MIN_MEMBERS = void 0;
|
|
26
|
+
exports.listShape = listShape;
|
|
27
|
+
exports.groupListChurn = groupListChurn;
|
|
28
|
+
exports.MIN_MEMBERS = 4;
|
|
29
|
+
exports.MIN_REPLACED = 2;
|
|
30
|
+
const ID_SEGMENT = /\d/;
|
|
31
|
+
function hrefOf(el) {
|
|
32
|
+
if (el.href)
|
|
33
|
+
return el.href;
|
|
34
|
+
const m = /^a\[href="([^"]*)"\]$/i.exec(el.selector.trim());
|
|
35
|
+
return m ? m[1] : undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The shape an element belongs to, or `null` when it has none — most elements.
|
|
39
|
+
* A link is shaped by its path with id-like segments collapsed; anything else by its
|
|
40
|
+
* selector with positions collapsed. A link whose path holds no id-like segment is not
|
|
41
|
+
* shaped at all: `/products` and `/cart` are navigation, never a list.
|
|
42
|
+
*/
|
|
43
|
+
function listShape(el) {
|
|
44
|
+
const href = hrefOf(el);
|
|
45
|
+
if (href !== undefined) {
|
|
46
|
+
const pathOnly = href.split(/[?#]/)[0];
|
|
47
|
+
const segments = pathOnly.split('/');
|
|
48
|
+
if (!segments.some((s) => ID_SEGMENT.test(s)))
|
|
49
|
+
return null;
|
|
50
|
+
return `${el.role} a[href="${segments.map((s) => (ID_SEGMENT.test(s) ? ':id' : s)).join('/')}"]`;
|
|
51
|
+
}
|
|
52
|
+
if (!/:nth-of-type\(\d+\)/.test(el.selector))
|
|
53
|
+
return null;
|
|
54
|
+
return `${el.role} ${el.selector.replace(/:nth-of-type\(\d+\)/g, ':nth-of-type(*)')}`;
|
|
55
|
+
}
|
|
56
|
+
function byShape(elements) {
|
|
57
|
+
const out = new Map();
|
|
58
|
+
for (const el of elements) {
|
|
59
|
+
const shape = listShape(el);
|
|
60
|
+
if (!shape)
|
|
61
|
+
continue;
|
|
62
|
+
out.set(shape, [...(out.get(shape) ?? []), el]);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/** Lists on one page whose items were replaced. Pure; empty on an ordinary page. */
|
|
67
|
+
function groupListChurn(baseline, current, rows) {
|
|
68
|
+
const before = byShape(baseline);
|
|
69
|
+
const after = byShape(current);
|
|
70
|
+
const groups = [];
|
|
71
|
+
for (const [shape, members] of before) {
|
|
72
|
+
if (members.length < exports.MIN_MEMBERS)
|
|
73
|
+
continue;
|
|
74
|
+
const oldNames = new Set(members.map((m) => m.name));
|
|
75
|
+
const newNames = new Set((after.get(shape) ?? []).map((m) => m.name));
|
|
76
|
+
const gone = [...oldNames].filter((n) => !newNames.has(n));
|
|
77
|
+
const appeared = [...newNames].filter((n) => !oldNames.has(n));
|
|
78
|
+
if (gone.length < exports.MIN_REPLACED || appeared.length < exports.MIN_REPLACED)
|
|
79
|
+
continue;
|
|
80
|
+
const selectors = rows.filter((r) => r.status !== 'ok' && listShape(r) === shape).map((r) => r.selector);
|
|
81
|
+
groups.push({
|
|
82
|
+
role: members[0].role,
|
|
83
|
+
shape: shape.slice(members[0].role.length + 1),
|
|
84
|
+
gone,
|
|
85
|
+
appeared,
|
|
86
|
+
kept: [...oldNames].filter((n) => newNames.has(n)).length,
|
|
87
|
+
selectors,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return groups;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=listChurn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listChurn.js","sourceRoot":"","sources":["../src/listChurn.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ia-qa/self-healing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Your Playwright, Cypress or Selenium tests break when a selector moves — this finds the element again and rewrites the test. Deterministic: no LLM decides whether your build passes. Runs entirely on your machine, with a local MCP server for agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"self-healing",
|