tamash-playwright 0.3.0 → 0.5.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/.env.example +5 -0
- package/README.md +53 -2
- package/dist/bindings/index.d.ts.map +1 -1
- package/dist/bindings/index.js +3 -0
- package/dist/bindings/index.js.map +1 -1
- package/dist/bindings/locator.binding.d.ts +4 -3
- package/dist/bindings/locator.binding.d.ts.map +1 -1
- package/dist/bindings/locator.binding.js +197 -7
- package/dist/bindings/locator.binding.js.map +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +126 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +26 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/scanDescribe.d.ts +11 -0
- package/dist/cli/scanDescribe.d.ts.map +1 -0
- package/dist/cli/scanDescribe.js +84 -0
- package/dist/cli/scanDescribe.js.map +1 -0
- package/dist/healer/action-recovery.d.ts +14 -0
- package/dist/healer/action-recovery.d.ts.map +1 -0
- package/dist/healer/action-recovery.js +102 -0
- package/dist/healer/action-recovery.js.map +1 -0
- package/dist/healer/index.d.ts +10 -2
- package/dist/healer/index.d.ts.map +1 -1
- package/dist/healer/index.js +297 -72
- package/dist/healer/index.js.map +1 -1
- package/dist/healer/providers/anthropic.d.ts.map +1 -1
- package/dist/healer/providers/anthropic.js +70 -0
- package/dist/healer/providers/anthropic.js.map +1 -1
- package/dist/healer/providers/gemini.d.ts.map +1 -1
- package/dist/healer/providers/gemini.js +99 -0
- package/dist/healer/providers/gemini.js.map +1 -1
- package/dist/healer/providers/ollama.d.ts.map +1 -1
- package/dist/healer/providers/ollama.js +113 -0
- package/dist/healer/providers/ollama.js.map +1 -1
- package/dist/healer/providers/openai.d.ts.map +1 -1
- package/dist/healer/providers/openai.js +99 -0
- package/dist/healer/providers/openai.js.map +1 -1
- package/dist/healer/providers/prompt.d.ts +7 -1
- package/dist/healer/providers/prompt.d.ts.map +1 -1
- package/dist/healer/providers/prompt.js +95 -1
- package/dist/healer/providers/prompt.js.map +1 -1
- package/dist/healer/providers/types.d.ts +36 -0
- package/dist/healer/providers/types.d.ts.map +1 -1
- package/dist/healer/providers/vision-models.d.ts +3 -0
- package/dist/healer/providers/vision-models.d.ts.map +1 -0
- package/dist/healer/providers/vision-models.js +40 -0
- package/dist/healer/providers/vision-models.js.map +1 -0
- package/dist/healer/replay-action.d.ts +3 -0
- package/dist/healer/replay-action.d.ts.map +1 -0
- package/dist/healer/replay-action.js +83 -0
- package/dist/healer/replay-action.js.map +1 -0
- package/dist/healer/vision.d.ts +25 -0
- package/dist/healer/vision.d.ts.map +1 -0
- package/dist/healer/vision.js +75 -0
- package/dist/healer/vision.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;;AACA,qCAAqC;AAErC,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5C,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,MAAM,IAAA,kBAAS,EAAC,IAAI,CAAC,CAAC;YACtB,MAAM;QACR,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,MAAM;QACR;YACE,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type LocatorOccurrence = {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
method: string;
|
|
5
|
+
snippet: string;
|
|
6
|
+
hasDescribe: boolean;
|
|
7
|
+
priority: 'high' | 'normal';
|
|
8
|
+
};
|
|
9
|
+
export declare function scanFileForLocators(filePath: string, content: string): LocatorOccurrence[];
|
|
10
|
+
export declare function scanTestDirectory(testDir: string, pattern?: RegExp): LocatorOccurrence[];
|
|
11
|
+
//# sourceMappingURL=scanDescribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanDescribe.d.ts","sourceRoot":"","sources":["../../src/cli/scanDescribe.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC7B,CAAC;AAmBF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA0B1F;AAsBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAA+B,GAAG,iBAAiB,EAAE,CAQhH"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.scanFileForLocators = scanFileForLocators;
|
|
7
|
+
exports.scanTestDirectory = scanTestDirectory;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const LOCATOR_FACTORY_METHODS = ['locator', 'getByRole', 'getByLabel', 'getByPlaceholder', 'getByText', 'getByAltText', 'getByTitle', 'getByTestId'];
|
|
11
|
+
const LOCATOR_FACTORY_PATTERN = new RegExp(`\\.(${LOCATOR_FACTORY_METHODS.join('|')})\\(`, 'g');
|
|
12
|
+
// `.locator()` (raw CSS/XPath) carries no semantic meaning on its own, so a missing .describe()
|
|
13
|
+
// hurts it the most — the other factories at least auto-derive a usable description from their
|
|
14
|
+
// own arguments (see describeFactoryCall in src/bindings/locator.binding.ts).
|
|
15
|
+
const HIGH_PRIORITY_METHODS = new Set(['locator']);
|
|
16
|
+
const SKIP_DIRECTORIES = new Set(['node_modules', 'dist', '.git', 'test-results', 'playwright-report']);
|
|
17
|
+
const STATEMENT_LOOKAHEAD_CHARS = 300;
|
|
18
|
+
function statementHasDescribe(content, matchIndex) {
|
|
19
|
+
const window = content.slice(matchIndex, matchIndex + STATEMENT_LOOKAHEAD_CHARS);
|
|
20
|
+
const semicolonIndex = window.indexOf(';');
|
|
21
|
+
const statement = semicolonIndex === -1 ? window : window.slice(0, semicolonIndex);
|
|
22
|
+
return statement.includes('.describe(');
|
|
23
|
+
}
|
|
24
|
+
function lineNumberAt(content, index) {
|
|
25
|
+
let line = 1;
|
|
26
|
+
for (let i = 0; i < index; i++) {
|
|
27
|
+
if (content[i] === '\n')
|
|
28
|
+
line++;
|
|
29
|
+
}
|
|
30
|
+
return line;
|
|
31
|
+
}
|
|
32
|
+
// A plain regex scan over source text, not a real TS parser — good enough to flag likely issues
|
|
33
|
+
// for a human (or an AI IDE) to review, not a strict/exhaustive linter.
|
|
34
|
+
function scanFileForLocators(filePath, content) {
|
|
35
|
+
const occurrences = [];
|
|
36
|
+
const lines = content.split('\n');
|
|
37
|
+
LOCATOR_FACTORY_PATTERN.lastIndex = 0;
|
|
38
|
+
let match;
|
|
39
|
+
while ((match = LOCATOR_FACTORY_PATTERN.exec(content))) {
|
|
40
|
+
const method = match[1];
|
|
41
|
+
const line = lineNumberAt(content, match.index);
|
|
42
|
+
const lineText = (lines[line - 1] ?? '').trim();
|
|
43
|
+
if (lineText.startsWith('//') || lineText.startsWith('*')) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
occurrences.push({
|
|
47
|
+
file: filePath,
|
|
48
|
+
line,
|
|
49
|
+
method,
|
|
50
|
+
snippet: lineText.slice(0, 100),
|
|
51
|
+
hasDescribe: statementHasDescribe(content, match.index),
|
|
52
|
+
priority: HIGH_PRIORITY_METHODS.has(method) ? 'high' : 'normal',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return occurrences;
|
|
56
|
+
}
|
|
57
|
+
function walk(dir, pattern, occurrences) {
|
|
58
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
if (SKIP_DIRECTORIES.has(entry.name))
|
|
61
|
+
continue;
|
|
62
|
+
walk(path_1.default.join(dir, entry.name), pattern, occurrences);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (pattern.test(entry.name)) {
|
|
66
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
67
|
+
const content = fs_1.default.readFileSync(fullPath, 'utf-8');
|
|
68
|
+
occurrences.push(...scanFileForLocators(fullPath, content));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Scans files matching `pattern` (test/spec files by default) for Playwright locators defined
|
|
73
|
+
// directly inline. Every occurrence returned here is, by definition, a locator written straight
|
|
74
|
+
// into a test file rather than inside a Page Object — that's what powers the page-object-usage
|
|
75
|
+
// recommendation in doctor.ts, on top of the per-locator missing-.describe() findings.
|
|
76
|
+
function scanTestDirectory(testDir, pattern = /\.(spec|test)\.tsx?$/) {
|
|
77
|
+
if (!fs_1.default.existsSync(testDir)) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const occurrences = [];
|
|
81
|
+
walk(testDir, pattern, occurrences);
|
|
82
|
+
return occurrences;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=scanDescribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanDescribe.js","sourceRoot":"","sources":["../../src/cli/scanDescribe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import type { ActionTactic, HealProvider, TokenUsage } from './providers/types';
|
|
3
|
+
export declare function applyActionTactic(tactic: Exclude<ActionTactic, 'none'>, locator: Locator, action: string, callArgs: unknown[]): Promise<unknown>;
|
|
4
|
+
export type ActionRecoveryOutcome = {
|
|
5
|
+
healing: {
|
|
6
|
+
provider: string;
|
|
7
|
+
warning: string;
|
|
8
|
+
result: unknown;
|
|
9
|
+
} | null;
|
|
10
|
+
usage?: TokenUsage;
|
|
11
|
+
stage?: 'action_recovery_declined' | 'action_recovery_failed';
|
|
12
|
+
};
|
|
13
|
+
export declare function tryActionRecovery(provider: HealProvider, locator: Locator, action: string, callArgs: unknown[], replayErrorMessage: string, timeoutMs: number | undefined): Promise<ActionRecoveryOutcome>;
|
|
14
|
+
//# sourceMappingURL=action-recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-recovery.d.ts","sourceRoot":"","sources":["../../src/healer/action-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAuDhF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBtJ;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,KAAK,CAAC,EAAE,UAAU,CAAC;IAMnB,KAAK,CAAC,EAAE,0BAA0B,GAAG,wBAAwB,CAAC;CAC/D,CAAC;AAMF,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EAAE,EACnB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAuBhC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyActionTactic = applyActionTactic;
|
|
4
|
+
exports.tryActionRecovery = tryActionRecovery;
|
|
5
|
+
const replay_action_1 = require("./replay-action");
|
|
6
|
+
// Verified directly against the installed playwright-core type definitions (not assumed) — these
|
|
7
|
+
// are the only Locator methods whose options accept `force`. `dragTo` also accepts it but isn't
|
|
8
|
+
// in the replayable action set at all, so it's irrelevant here.
|
|
9
|
+
const FORCE_CAPABLE_ACTIONS = new Set([
|
|
10
|
+
'check', 'clear', 'click', 'dblclick', 'fill', 'hover', 'selectOption', 'selectText', 'setChecked', 'tap', 'uncheck',
|
|
11
|
+
]);
|
|
12
|
+
// Deliberately small and explicit — anything not listed here declines the `dispatch` tactic
|
|
13
|
+
// rather than guessing at an event name that might not correspond to real browser behavior.
|
|
14
|
+
const DISPATCH_EVENT_MAP = {
|
|
15
|
+
click: 'click',
|
|
16
|
+
dblclick: 'dblclick',
|
|
17
|
+
focus: 'focus',
|
|
18
|
+
blur: 'blur',
|
|
19
|
+
hover: 'mouseover',
|
|
20
|
+
};
|
|
21
|
+
const WAIT_TACTIC_DELAY_MS = 500;
|
|
22
|
+
async function replayActionWithForce(locator, action, callArgs) {
|
|
23
|
+
const [firstArg] = callArgs;
|
|
24
|
+
switch (action) {
|
|
25
|
+
case 'click':
|
|
26
|
+
return locator.click({ force: true });
|
|
27
|
+
case 'dblclick':
|
|
28
|
+
return locator.dblclick({ force: true });
|
|
29
|
+
case 'tap':
|
|
30
|
+
return locator.tap({ force: true });
|
|
31
|
+
case 'hover':
|
|
32
|
+
return locator.hover({ force: true });
|
|
33
|
+
case 'check':
|
|
34
|
+
return locator.check({ force: true });
|
|
35
|
+
case 'uncheck':
|
|
36
|
+
return locator.uncheck({ force: true });
|
|
37
|
+
case 'fill':
|
|
38
|
+
return locator.fill(String(firstArg ?? ''), { force: true });
|
|
39
|
+
case 'clear':
|
|
40
|
+
return locator.clear({ force: true });
|
|
41
|
+
case 'selectOption':
|
|
42
|
+
return locator.selectOption(firstArg, { force: true });
|
|
43
|
+
case 'selectText':
|
|
44
|
+
return locator.selectText({ force: true });
|
|
45
|
+
case 'setChecked':
|
|
46
|
+
return locator.setChecked(Boolean(firstArg), { force: true });
|
|
47
|
+
default:
|
|
48
|
+
// Unreachable given the FORCE_CAPABLE_ACTIONS check in applyActionTactic() below, but kept
|
|
49
|
+
// as a real error (not a silent no-op) in case the two ever drift out of sync.
|
|
50
|
+
throw new Error(`Action "${action}" does not support a force retry.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function applyActionTactic(tactic, locator, action, callArgs) {
|
|
54
|
+
switch (tactic) {
|
|
55
|
+
case 'scroll':
|
|
56
|
+
await locator.scrollIntoViewIfNeeded();
|
|
57
|
+
return (0, replay_action_1.replayAction)(locator, action, callArgs);
|
|
58
|
+
case 'wait':
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, WAIT_TACTIC_DELAY_MS));
|
|
60
|
+
return (0, replay_action_1.replayAction)(locator, action, callArgs);
|
|
61
|
+
case 'force':
|
|
62
|
+
if (!FORCE_CAPABLE_ACTIONS.has(action)) {
|
|
63
|
+
throw new Error(`Action "${action}" does not support a force retry.`);
|
|
64
|
+
}
|
|
65
|
+
return replayActionWithForce(locator, action, callArgs);
|
|
66
|
+
case 'dispatch': {
|
|
67
|
+
const eventType = DISPATCH_EVENT_MAP[action];
|
|
68
|
+
if (!eventType) {
|
|
69
|
+
throw new Error(`Action "${action}" has no direct DOM event to dispatch.`);
|
|
70
|
+
}
|
|
71
|
+
return locator.dispatchEvent(eventType);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Strictly a second-order fallback: only ever called after a locator has already been
|
|
76
|
+
// successfully healed (by the text or vision path) but replaying the original action on it still
|
|
77
|
+
// failed. Bounded by design — the AI only ever picks among applyActionTactic()'s fixed menu, it
|
|
78
|
+
// never decides how to interact with the page on its own.
|
|
79
|
+
async function tryActionRecovery(provider, locator, action, callArgs, replayErrorMessage, timeoutMs) {
|
|
80
|
+
const result = await provider.suggestActionTactic({ action, errorMessage: replayErrorMessage, timeoutMs });
|
|
81
|
+
if (!result) {
|
|
82
|
+
return { healing: null, stage: 'action_recovery_failed' };
|
|
83
|
+
}
|
|
84
|
+
if (result.tactic === 'none') {
|
|
85
|
+
return { healing: null, usage: result.usage, stage: 'action_recovery_declined' };
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const tacticResult = await applyActionTactic(result.tactic, locator, action, callArgs);
|
|
89
|
+
return {
|
|
90
|
+
healing: {
|
|
91
|
+
provider: provider.name,
|
|
92
|
+
warning: `Recovered using ${provider.name} via action recovery (${result.tactic}).`,
|
|
93
|
+
result: tacticResult,
|
|
94
|
+
},
|
|
95
|
+
usage: result.usage,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return { healing: null, usage: result.usage, stage: 'action_recovery_failed' };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=action-recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-recovery.js","sourceRoot":"","sources":["../../src/healer/action-recovery.ts"],"names":[],"mappings":"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"}
|
package/dist/healer/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Frame, Locator, Page } from '@playwright/test';
|
|
1
|
+
import type { Frame, FrameLocator, Locator, Page } from '@playwright/test';
|
|
2
2
|
import type { TokenUsage } from './providers/types';
|
|
3
|
-
type BindingKind = 'locator' | 'page' | 'frame';
|
|
3
|
+
type BindingKind = 'locator' | 'page' | 'frame' | 'frameLocator';
|
|
4
4
|
export type SelfHealingReport = {
|
|
5
5
|
action: string;
|
|
6
6
|
kind: BindingKind;
|
|
@@ -11,7 +11,13 @@ export type SelfHealingReport = {
|
|
|
11
11
|
reason: string;
|
|
12
12
|
suggestedSelector?: string;
|
|
13
13
|
tokenUsage?: TokenUsage;
|
|
14
|
+
failureStage?: string;
|
|
15
|
+
usedVision: boolean;
|
|
16
|
+
usedActionRecovery: boolean;
|
|
17
|
+
sourceLocation?: string;
|
|
14
18
|
};
|
|
19
|
+
export declare function isHealingEnabled(): boolean;
|
|
20
|
+
export declare function isActionRecoveryEnabled(): boolean;
|
|
15
21
|
export declare function healActionFailure(context: {
|
|
16
22
|
action: string;
|
|
17
23
|
kind: BindingKind;
|
|
@@ -19,6 +25,8 @@ export declare function healActionFailure(context: {
|
|
|
19
25
|
error: unknown;
|
|
20
26
|
target?: Locator | Page | Frame;
|
|
21
27
|
callArgs?: unknown[];
|
|
28
|
+
frameScope?: FrameLocator;
|
|
29
|
+
sourceLocation?: string;
|
|
22
30
|
}): Promise<{
|
|
23
31
|
report: SelfHealingReport;
|
|
24
32
|
recovered: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/healer/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/healer/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI3E,OAAO,KAAK,EAAoC,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAOtF,KAAK,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IAKtB,UAAU,EAAE,OAAO,CAAC;IAIpB,kBAAkB,EAAE,OAAO,CAAC;IAK5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAsSF,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAMD,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AAmED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IAKrB,UAAU,CAAC,EAAE,YAAY,CAAC;IAG1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAyJ/E;AAED,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,CAEvD"}
|