@ia-qa/self-healing 1.5.7 → 1.6.1

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,235 @@
1
+ "use strict";
2
+ /**
3
+ * Static inventory of **name-anchored** locators — the other half of `ingest`.
4
+ *
5
+ * `usage.selectors` answers "which CSS selectors does the suite use?". That was
6
+ * only ever half the question: a suite written the modern way does not hold CSS
7
+ * at all. `page.getByRole('button', { name: 'Save' })`, `cy.contains('Save')` and
8
+ * `By.linkText('Save')` are anchored on the **accessible name**, so they survive
9
+ * every selector rewrite this tool can perform and break on the one kind of drift
10
+ * it used to classify as harmless: a rename.
11
+ *
12
+ * Same discipline as `ingest`: quoted literals in known call shapes, no
13
+ * execution, no AST, no network. A name built by concatenation or holding `${…}`
14
+ * is invisible here — and invisible to the rewrite below too, which is why it is
15
+ * skipped rather than guessed at.
16
+ *
17
+ * Framework parity is the point (guardrail: Playwright / Cypress / Selenium must
18
+ * all be first-class). Every shape below carries what the contract needs to check
19
+ * it: a name, and a role when the call states one.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.AUTHORITATIVE_SHAPES = exports.CONTRACT_ROLES = exports.SHAPE_ROLES = exports.normalizeName = void 0;
23
+ exports.judgeable = judgeable;
24
+ exports.quoteLiteral = quoteLiteral;
25
+ exports.scanNameLocators = scanNameLocators;
26
+ exports.candidateRoles = candidateRoles;
27
+ const normalizeName = (s) => (s == null ? '' : String(s)).toLowerCase().trim().replace(/\s+/g, ' ');
28
+ exports.normalizeName = normalizeName;
29
+ /**
30
+ * The roles a name-anchored call can be checked against.
31
+ *
32
+ * `getByLabel` / `getByPlaceholder` do not name a role, but `extractInPage`
33
+ * derives an accessible name from `<label>`, then `placeholder`, then `name` —
34
+ * so both resolve against form-control roles and nothing else.
35
+ */
36
+ exports.SHAPE_ROLES = {
37
+ 'playwright-label': ['textbox', 'combobox', 'checkbox', 'radio', 'slider'],
38
+ 'playwright-placeholder': ['textbox', 'combobox'],
39
+ 'selenium-linktext': ['link'],
40
+ };
41
+ /**
42
+ * Every role `extractInPage` can emit — the complete vocabulary of a contract.
43
+ *
44
+ * A locator asking for anything else is asking about a part of the page the
45
+ * mapping never claimed to hold. `getByRole('heading', …)` is the common case:
46
+ * headings are not interactive, so no contract has ever contained one, and
47
+ * "absent from the contract" would mean "absent from the app" only if the two
48
+ * were the same thing. They are not, and a checker that forgets it reports every
49
+ * heading in the suite as broken.
50
+ */
51
+ exports.CONTRACT_ROLES = new Set([
52
+ 'button', 'link', 'textbox', 'combobox', 'checkbox', 'radio', 'menuitem', 'tab', 'slider', 'generic',
53
+ ]);
54
+ /**
55
+ * Shapes whose miss is *evidence* that a test targets nothing.
56
+ *
57
+ * The contract maps interactive elements only. `getByText('Terms of service')`
58
+ * may well point at a paragraph the mapping never saw, so its absence proves
59
+ * nothing — reporting it would be a false positive, and one false positive costs
60
+ * more than ten honest "inconclusive". A stated role, a label or a placeholder,
61
+ * on the other hand, can designate something the contract holds — provided the
62
+ * role is one the contract speaks (see `judgeable` below).
63
+ */
64
+ exports.AUTHORITATIVE_SHAPES = new Set([
65
+ 'playwright-role',
66
+ 'playwright-label',
67
+ 'playwright-placeholder',
68
+ 'selenium-linktext',
69
+ ]);
70
+ /**
71
+ * Can this occurrence's absence from a contract be read as a finding?
72
+ *
73
+ * Both halves must hold: a shape that names an element the contract enumerates,
74
+ * AND a role the contract can actually contain.
75
+ */
76
+ function judgeable(site) {
77
+ if (!exports.AUTHORITATIVE_SHAPES.has(site.shape))
78
+ return false;
79
+ const roles = candidateRoles(site);
80
+ return roles !== null && roles.some((r) => exports.CONTRACT_ROLES.has(r));
81
+ }
82
+ const Q = "(['\"`])";
83
+ /** Body of a quoted literal: no unescaped closing quote, no interpolation. */
84
+ const BODY = '((?:(?!\\1)[^\\\\]|\\\\.)*?)';
85
+ /**
86
+ * A literal we can read *and* rewrite. A template hole (`${…}`) makes the runtime
87
+ * value unknowable, and a regex argument (`{ name: /save/i }`) is not a literal at
88
+ * all — both are left to the human rather than guessed at.
89
+ */
90
+ function literalName(raw) {
91
+ if (raw == null)
92
+ return null;
93
+ if (raw.includes('${'))
94
+ return null;
95
+ return raw.replace(/\\(['"`\\])/g, '$1');
96
+ }
97
+ /** Wrap `name` in a quote it does not contain; escape as a last resort. */
98
+ function quoteLiteral(name) {
99
+ for (const q of ["'", '"', '`']) {
100
+ if (name.indexOf(q) === -1 && !(q === '`' && name.includes('${')))
101
+ return q + name + q;
102
+ }
103
+ return "'" + name.replace(/(['\\])/g, '\\$1') + "'";
104
+ }
105
+ /** Replace the `name: '…'` value inside an already-matched `getByRole(…)` call. */
106
+ function renameRoleCall(call, newName) {
107
+ return call.replace(/(\bname\s*:\s*)(['"`])((?:(?!\2)[^\\]|\\.)*?)\2/, (_m, key) => key + quoteLiteral(newName));
108
+ }
109
+ /** Replace the Nth quoted literal of an already-matched call. */
110
+ function renameNthLiteral(call, newName, nth) {
111
+ let seen = 0;
112
+ return call.replace(/(['"`])((?:(?!\1)[^\\]|\\.)*?)\1/g, (m, _q, _body) => {
113
+ seen++;
114
+ return seen === nth ? quoteLiteral(newName) : m;
115
+ });
116
+ }
117
+ /**
118
+ * Playwright and Testing Library are the same call under two spellings, and a
119
+ * Cypress suite reaches role-anchored locating only through the second
120
+ * (`@testing-library/cypress` → `cy.findByRole`). Matching the whole family
121
+ * rather than Playwright's spelling alone is what keeps the three frameworks at
122
+ * parity here — otherwise a Cypress suite would have no rewritable locator at
123
+ * all, and "parity" would mean "we read your file and did nothing".
124
+ *
125
+ * `getBy` / `findBy` / `queryBy`, with or without `All`.
126
+ */
127
+ const V = '(?:get|find|query)(?:All)?By';
128
+ /**
129
+ * Every needle starts at the `.` so the single-pass rewriter in `fixEngine` — which
130
+ * only probes at `.` and quote characters — can match it. That covers
131
+ * `page.getByRole`, `screen.findByRole`, `cy.findByRole`, `cy.contains`,
132
+ * `By.linkText` and any chained form. A bare destructured `getByText('…')` (React
133
+ * Testing Library) is inventoried but not rewritten: an accepted, reported miss.
134
+ */
135
+ const SHAPES = [
136
+ {
137
+ shape: 'playwright-role',
138
+ re: new RegExp(`\\.${V}Role\\(\\s*${Q}([a-z]+)\\1\\s*,\\s*\\{[^{}]*?\\bname\\s*:\\s*(['"\`])((?:(?!\\3)[^\\\\]|\\\\.)*?)\\3[^{}]*\\}\\s*\\)`, 'g'),
139
+ nameGroup: 4,
140
+ roleGroup: 2,
141
+ literalIndex: 2,
142
+ role: true,
143
+ },
144
+ { shape: 'playwright-label', re: new RegExp(`\\.${V}Label(?:Text)?\\(\\s*${Q}${BODY}\\1`, 'g'), nameGroup: 2, literalIndex: 1 },
145
+ { shape: 'playwright-placeholder', re: new RegExp(`\\.${V}Placeholder(?:Text)?\\(\\s*${Q}${BODY}\\1`, 'g'), nameGroup: 2, literalIndex: 1 },
146
+ { shape: 'playwright-alt', re: new RegExp(`\\.${V}AltText\\(\\s*${Q}${BODY}\\1`, 'g'), nameGroup: 2, literalIndex: 1 },
147
+ { shape: 'playwright-title', re: new RegExp(`\\.${V}Title\\(\\s*${Q}${BODY}\\1`, 'g'), nameGroup: 2, literalIndex: 1 },
148
+ // `By` + `Text(` exactly, so `.getByLabelText(` and `.getByAltText(` are not
149
+ // candidates for it and the shapes above stay mutually exclusive.
150
+ { shape: 'playwright-text', re: new RegExp(`\\.${V}Text\\(\\s*${Q}${BODY}\\1`, 'g'), nameGroup: 2, literalIndex: 1 },
151
+ // `cy.contains(selector, text)` — the text is the SECOND literal. Checked before
152
+ // the one-argument form so the two-argument call is never read as `contains(text)`.
153
+ {
154
+ shape: 'cypress-contains',
155
+ re: new RegExp(`\\.contains\\(\\s*${Q}${BODY}\\1\\s*,\\s*(['"\`])((?:(?!\\3)[^\\\\]|\\\\.)*?)\\3`, 'g'),
156
+ nameGroup: 4,
157
+ literalIndex: 2,
158
+ },
159
+ { shape: 'cypress-contains', re: new RegExp(`\\.contains\\(\\s*${Q}${BODY}\\1\\s*\\)`, 'g'), nameGroup: 2, literalIndex: 1 },
160
+ { shape: 'selenium-linktext', re: new RegExp(`\\.(?:linkText|partialLinkText)\\(\\s*${Q}${BODY}\\1`, 'g'), nameGroup: 2, literalIndex: 1 },
161
+ // Selenium's constant form, shared by the Java / Python / C# bindings:
162
+ // `find_element(By.LINK_TEXT, "Save")`.
163
+ {
164
+ shape: 'selenium-linktext',
165
+ re: new RegExp(`\\.(?:LINK_TEXT|PARTIAL_LINK_TEXT)\\s*,\\s*${Q}${BODY}\\1`, 'g'),
166
+ nameGroup: 2,
167
+ literalIndex: 1,
168
+ },
169
+ ];
170
+ /**
171
+ * Find every name-anchored locator in `text`.
172
+ *
173
+ * Overlapping shapes are resolved by source position: the first definition that
174
+ * claims a character range wins, which is why the two-argument `cy.contains` is
175
+ * declared before the one-argument form.
176
+ */
177
+ function scanNameLocators(text, file) {
178
+ const lines = text.split('\n');
179
+ const lineStart = [];
180
+ let acc = 0;
181
+ for (const l of lines) {
182
+ lineStart.push(acc);
183
+ acc += l.length + 1;
184
+ }
185
+ const lineOf = (index) => {
186
+ let lo = 0;
187
+ let hi = lineStart.length - 1;
188
+ while (lo < hi) {
189
+ const mid = (lo + hi + 1) >> 1;
190
+ if (lineStart[mid] <= index)
191
+ lo = mid;
192
+ else
193
+ hi = mid - 1;
194
+ }
195
+ return lo + 1;
196
+ };
197
+ const claimed = [];
198
+ const out = [];
199
+ for (const def of SHAPES) {
200
+ def.re.lastIndex = 0;
201
+ let m;
202
+ while ((m = def.re.exec(text)) !== null) {
203
+ const start = m.index;
204
+ const end = start + m[0].length;
205
+ if (claimed.some(([s, e]) => start < e && end > s))
206
+ continue;
207
+ const name = literalName(m[def.nameGroup]);
208
+ if (name == null || name.trim() === '')
209
+ continue;
210
+ claimed.push([start, end]);
211
+ const call = m[0];
212
+ const shape = def.shape;
213
+ const role = def.roleGroup ? m[def.roleGroup] : undefined;
214
+ const literalIndex = def.literalIndex;
215
+ out.push({
216
+ name,
217
+ role,
218
+ shape,
219
+ site: { file, line: lineOf(start), shape, ...(role ? { role } : {}), text: name },
220
+ call,
221
+ rename: (newName) => shape === 'playwright-role'
222
+ ? renameRoleCall(call, newName)
223
+ : renameNthLiteral(call, newName, literalIndex),
224
+ });
225
+ }
226
+ }
227
+ return out.sort((a, b) => a.site.line - b.site.line);
228
+ }
229
+ /** Roles a hit can legitimately resolve to, or `null` when it constrains nothing. */
230
+ function candidateRoles(hit) {
231
+ if (hit.role)
232
+ return [hit.role];
233
+ return exports.SHAPE_ROLES[hit.shape] ?? null;
234
+ }
235
+ //# sourceMappingURL=nameLocators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nameLocators.js","sourceRoot":"","sources":["../src/nameLocators.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AA8FH,8BAIC;AAkBD,oCAKC;AA4FD,4CAqDC;AAGD,wCAGC;AAvOM,MAAM,aAAa,GAAG,CAAC,CAAU,EAAU,EAAE,CAClD,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAD5D,QAAA,aAAa,iBAC+C;AAEzE;;;;;;GAMG;AACU,QAAA,WAAW,GAAyC;IAC/D,kBAAkB,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC;IAC1E,wBAAwB,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IACjD,mBAAmB,EAAE,CAAC,MAAM,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;GASG;AACU,QAAA,cAAc,GAAG,IAAI,GAAG,CAAS;IAC5C,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS;CACrG,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACU,QAAA,oBAAoB,GAAG,IAAI,GAAG,CAAY;IACrD,iBAAiB;IACjB,kBAAkB;IAClB,wBAAwB;IACxB,mBAAmB;CACpB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,IAAyC;IACjE,IAAI,CAAC,4BAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,GAAG,UAAU,CAAC;AACrB,8EAA8E;AAC9E,MAAM,IAAI,GAAG,8BAA8B,CAAC;AAE5C;;;;GAIG;AACH,SAAS,WAAW,CAAC,GAAuB;IAC1C,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED,2EAA2E;AAC3E,SAAgB,YAAY,CAAC,IAAY;IACvC,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;AACtD,CAAC;AAED,mFAAmF;AACnF,SAAS,cAAc,CAAC,IAAY,EAAE,OAAe;IACnD,OAAO,IAAI,CAAC,OAAO,CACjB,iDAAiD,EACjD,CAAC,EAAE,EAAE,GAAW,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CACjD,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,SAAS,gBAAgB,CAAC,IAAY,EAAE,OAAe,EAAE,GAAW;IAClE,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,OAAO,IAAI,CAAC,OAAO,CAAC,mCAAmC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;QACxE,IAAI,EAAE,CAAC;QACP,OAAO,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC;AAaD;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,8BAA8B,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,MAAM,GAAe;IACzB;QACE,KAAK,EAAE,iBAAiB;QACxB,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,uGAAuG,EAAE,GAAG,CAAC;QAClJ,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;QACf,IAAI,EAAE,IAAI;KACX;IACD,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IAC/H,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,8BAA8B,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IAC3I,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IACtH,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IACtH,6EAA6E;IAC7E,kEAAkE;IAClE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IACpH,iFAAiF;IACjF,oFAAoF;IACpF;QACE,KAAK,EAAE,kBAAkB;QACzB,EAAE,EAAE,IAAI,MAAM,CAAC,qBAAqB,CAAC,GAAG,IAAI,qDAAqD,EAAE,GAAG,CAAC;QACvG,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,qBAAqB,CAAC,GAAG,IAAI,YAAY,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IAC5H,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,yCAAyC,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE;IAC1I,uEAAuE;IACvE,wCAAwC;IACxC;QACE,KAAK,EAAE,mBAAmB;QAC1B,EAAE,EAAE,IAAI,MAAM,CAAC,8CAA8C,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC;QAChF,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;CACF,CAAC;AAEF;;;;;;GAMG;AACH,SAAgB,gBAAgB,CAAC,IAAY,EAAE,IAAY;IACzD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,KAAa,EAAU,EAAE;QACvC,IAAI,EAAE,GAAG,CAAC,CAAC;QACX,IAAI,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9B,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,KAAK;gBAAE,EAAE,GAAG,GAAG,CAAC;;gBACjC,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,EAAE,GAAG,CAAC,CAAC;IAChB,CAAC,CAAC;IAEF,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,MAAM,GAAG,GAAc,EAAE,CAAC;IAE1B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC;QACrB,IAAI,CAAyB,CAAC;QAC9B,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;YACtB,MAAM,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAChC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;gBAAE,SAAS;YAE7D,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YAC3C,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,SAAS;YAEjD,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YACxB,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;YACtC,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI;gBACJ,IAAI;gBACJ,KAAK;gBACL,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE;gBACjF,IAAI;gBACJ,MAAM,EAAE,CAAC,OAAe,EAAE,EAAE,CAC1B,KAAK,KAAK,iBAAiB;oBACzB,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC;oBAC/B,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC;aACpD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC;AAED,qFAAqF;AACrF,SAAgB,cAAc,CAAC,GAAwC;IACrE,IAAI,GAAG,CAAC,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,mBAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ia-qa/self-healing",
3
- "version": "1.5.7",
3
+ "version": "1.6.1",
4
4
  "description": "Local-first self-healing for UI tests: a local MCP server + CLI that map your app's pages to a role/name/selector contract, diff selector drift (PASS/FIX/BLOCK), and apply deterministic fixes to Cypress/Playwright/Selenium tests. Runs on your machine — nothing leaves it.",
5
5
  "keywords": [
6
6
  "self-healing",