@muuktest/amikoo-playwright 2.0.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/README.md +26 -0
- package/dist/capture.cjs +57 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +6 -0
- package/dist/capture.d.ts +6 -0
- package/dist/capture.js +23 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli/agent-setup.cjs +68 -0
- package/dist/cli/agent-setup.cjs.map +1 -0
- package/dist/cli/agent-setup.d.cts +11 -0
- package/dist/cli/agent-setup.d.ts +11 -0
- package/dist/cli/agent-setup.js +31 -0
- package/dist/cli/agent-setup.js.map +1 -0
- package/dist/cli/amikoo-playwright-agent.md +82 -0
- package/dist/cli/fixture-creator.cjs +163 -0
- package/dist/cli/fixture-creator.cjs.map +1 -0
- package/dist/cli/fixture-creator.d.cts +12 -0
- package/dist/cli/fixture-creator.d.ts +12 -0
- package/dist/cli/fixture-creator.js +128 -0
- package/dist/cli/fixture-creator.js.map +1 -0
- package/dist/cli/index.cjs +134 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +111 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-setup.cjs +116 -0
- package/dist/cli/mcp-setup.cjs.map +1 -0
- package/dist/cli/mcp-setup.d.cts +12 -0
- package/dist/cli/mcp-setup.d.ts +12 -0
- package/dist/cli/mcp-setup.js +81 -0
- package/dist/cli/mcp-setup.js.map +1 -0
- package/dist/cli/scanner.cjs +137 -0
- package/dist/cli/scanner.cjs.map +1 -0
- package/dist/cli/scanner.d.cts +16 -0
- package/dist/cli/scanner.d.ts +16 -0
- package/dist/cli/scanner.js +100 -0
- package/dist/cli/scanner.js.map +1 -0
- package/dist/cli/test-updater.cjs +131 -0
- package/dist/cli/test-updater.cjs.map +1 -0
- package/dist/cli/test-updater.d.cts +12 -0
- package/dist/cli/test-updater.d.ts +12 -0
- package/dist/cli/test-updater.js +96 -0
- package/dist/cli/test-updater.js.map +1 -0
- package/dist/dom/buildDomTree.js +1760 -0
- package/dist/helpers/dom-extractor.cjs +344 -0
- package/dist/helpers/dom-extractor.cjs.map +1 -0
- package/dist/helpers/dom-extractor.d.cts +9 -0
- package/dist/helpers/dom-extractor.d.ts +9 -0
- package/dist/helpers/dom-extractor.js +318 -0
- package/dist/helpers/dom-extractor.js.map +1 -0
- package/dist/helpers/dom-service.cjs +365 -0
- package/dist/helpers/dom-service.cjs.map +1 -0
- package/dist/helpers/dom-service.d.cts +82 -0
- package/dist/helpers/dom-service.d.ts +82 -0
- package/dist/helpers/dom-service.js +338 -0
- package/dist/helpers/dom-service.js.map +1 -0
- package/dist/helpers/failure-analyzer.cjs +276 -0
- package/dist/helpers/failure-analyzer.cjs.map +1 -0
- package/dist/helpers/failure-analyzer.d.cts +100 -0
- package/dist/helpers/failure-analyzer.d.ts +100 -0
- package/dist/helpers/failure-analyzer.js +241 -0
- package/dist/helpers/failure-analyzer.js.map +1 -0
- package/dist/index.cjs +32 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @muuktest/amikoo-playwright
|
|
2
|
+
|
|
3
|
+
Automatically captures DOM state and screenshots when Playwright tests fail. Configures the MCP server and creates or modifies your Playwright fixture so test errors can be analyzed using the MCP agent.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @muuktest/amikoo-playwright
|
|
9
|
+
npx @muuktest/amikoo-playwright init
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The `init` command sets up the fixture and MCP configuration automatically.
|
|
13
|
+
|
|
14
|
+
## Configure the Agent in VS Code
|
|
15
|
+
|
|
16
|
+
1. Open VS Code.
|
|
17
|
+
2. Open your Playwright project folder.
|
|
18
|
+
3. In the left sidebar, open the Agent panel.
|
|
19
|
+
4. Click **Configure Custom Agents…**
|
|
20
|
+
5. In the list of available agents, select `amikoo-playwright-agent`.
|
|
21
|
+
6. Close the configuration panel.
|
|
22
|
+
7. Verify that `amikoo-playwright-agent` appears as the active agent at the top of the Agent panel.
|
|
23
|
+
|
|
24
|
+
## Learn More
|
|
25
|
+
|
|
26
|
+
See the [MCP server documentation](https://www.npmjs.com/package/@muuktest/amikoo-mcp?activeTab=readme) for details on the agent and available tools.
|
package/dist/capture.cjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var capture_exports = {};
|
|
30
|
+
__export(capture_exports, {
|
|
31
|
+
captureFailure: () => captureFailure
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(capture_exports);
|
|
34
|
+
var import_path = __toESM(require("path"), 1);
|
|
35
|
+
var import_dom_extractor = require("./helpers/dom-extractor.cjs");
|
|
36
|
+
var import_failure_analyzer = require("./helpers/failure-analyzer.cjs");
|
|
37
|
+
async function captureFailure(page, testInfo, consoleLogs, networkFailures) {
|
|
38
|
+
if (testInfo.status === testInfo.expectedStatus) return;
|
|
39
|
+
console.log("\n[Test Failure] Capturing DOM state before trace...");
|
|
40
|
+
try {
|
|
41
|
+
const testName = testInfo.title.replace(/\s+/g, "_").toLowerCase();
|
|
42
|
+
const { jsonPath, screenshotPath } = await (0, import_dom_extractor.extractAndSaveElements)(page, `${testName}_failure`);
|
|
43
|
+
await testInfo.attach("dom-elements", { path: jsonPath, contentType: "application/json" });
|
|
44
|
+
await testInfo.attach("failure-screenshot", { path: screenshotPath, contentType: "image/png" });
|
|
45
|
+
const failureInfoPath = import_path.default.join(import_path.default.dirname(jsonPath), "failure_info.json");
|
|
46
|
+
await (0, import_failure_analyzer.analyzeFailure)(page, testInfo, consoleLogs, networkFailures, { jsonPath, screenshotPath, failureInfoPath });
|
|
47
|
+
await testInfo.attach("failure-info", { path: failureInfoPath, contentType: "application/json" });
|
|
48
|
+
console.log("[Test Failure] DOM capture completed successfully");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("[Test Failure] Error capturing DOM state:", error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
54
|
+
0 && (module.exports = {
|
|
55
|
+
captureFailure
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=capture.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/capture.ts"],"sourcesContent":["import type { Page, TestInfo } from '@playwright/test';\nimport path from 'path';\nimport { extractAndSaveElements } from './helpers/dom-extractor.js';\nimport { analyzeFailure } from './helpers/failure-analyzer.js';\nimport type { ConsoleEntry, NetworkFailure } from './helpers/failure-analyzer.js';\n\nexport async function captureFailure(\n page: Page,\n testInfo: TestInfo,\n consoleLogs: ConsoleEntry[],\n networkFailures: NetworkFailure[]\n): Promise<void> {\n if (testInfo.status === testInfo.expectedStatus) return;\n console.log('\\n[Test Failure] Capturing DOM state before trace...');\n try {\n const testName = testInfo.title.replace(/\\s+/g, '_').toLowerCase();\n const { jsonPath, screenshotPath } = await extractAndSaveElements(page, `${testName}_failure`);\n await testInfo.attach('dom-elements', { path: jsonPath, contentType: 'application/json' });\n await testInfo.attach('failure-screenshot', { path: screenshotPath, contentType: 'image/png' });\n const failureInfoPath = path.join(path.dirname(jsonPath), 'failure_info.json');\n await analyzeFailure(page, testInfo, consoleLogs, networkFailures, { jsonPath, screenshotPath, failureInfoPath });\n await testInfo.attach('failure-info', { path: failureInfoPath, contentType: 'application/json' });\n console.log('[Test Failure] DOM capture completed successfully');\n } catch (error) {\n console.error('[Test Failure] Error capturing DOM state:', error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,kBAAiB;AACjB,2BAAuC;AACvC,8BAA+B;AAG/B,eAAsB,eACpB,MACA,UACA,aACA,iBACe;AACf,MAAI,SAAS,WAAW,SAAS,eAAgB;AACjD,UAAQ,IAAI,sDAAsD;AAClE,MAAI;AACF,UAAM,WAAW,SAAS,MAAM,QAAQ,QAAQ,GAAG,EAAE,YAAY;AACjE,UAAM,EAAE,UAAU,eAAe,IAAI,UAAM,6CAAuB,MAAM,GAAG,QAAQ,UAAU;AAC7F,UAAM,SAAS,OAAO,gBAAgB,EAAE,MAAM,UAAU,aAAa,mBAAmB,CAAC;AACzF,UAAM,SAAS,OAAO,sBAAsB,EAAE,MAAM,gBAAgB,aAAa,YAAY,CAAC;AAC9F,UAAM,kBAAkB,YAAAA,QAAK,KAAK,YAAAA,QAAK,QAAQ,QAAQ,GAAG,mBAAmB;AAC7E,cAAM,wCAAe,MAAM,UAAU,aAAa,iBAAiB,EAAE,UAAU,gBAAgB,gBAAgB,CAAC;AAChH,UAAM,SAAS,OAAO,gBAAgB,EAAE,MAAM,iBAAiB,aAAa,mBAAmB,CAAC;AAChG,YAAQ,IAAI,mDAAmD;AAAA,EACjE,SAAS,OAAO;AACd,YAAQ,MAAM,6CAA6C,KAAK;AAAA,EAClE;AACF;","names":["path"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Page, TestInfo } from '@playwright/test';
|
|
2
|
+
import { ConsoleEntry, NetworkFailure } from './helpers/failure-analyzer.cjs';
|
|
3
|
+
|
|
4
|
+
declare function captureFailure(page: Page, testInfo: TestInfo, consoleLogs: ConsoleEntry[], networkFailures: NetworkFailure[]): Promise<void>;
|
|
5
|
+
|
|
6
|
+
export { captureFailure };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Page, TestInfo } from '@playwright/test';
|
|
2
|
+
import { ConsoleEntry, NetworkFailure } from './helpers/failure-analyzer.js';
|
|
3
|
+
|
|
4
|
+
declare function captureFailure(page: Page, testInfo: TestInfo, consoleLogs: ConsoleEntry[], networkFailures: NetworkFailure[]): Promise<void>;
|
|
5
|
+
|
|
6
|
+
export { captureFailure };
|
package/dist/capture.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { extractAndSaveElements } from "./helpers/dom-extractor.js";
|
|
3
|
+
import { analyzeFailure } from "./helpers/failure-analyzer.js";
|
|
4
|
+
async function captureFailure(page, testInfo, consoleLogs, networkFailures) {
|
|
5
|
+
if (testInfo.status === testInfo.expectedStatus) return;
|
|
6
|
+
console.log("\n[Test Failure] Capturing DOM state before trace...");
|
|
7
|
+
try {
|
|
8
|
+
const testName = testInfo.title.replace(/\s+/g, "_").toLowerCase();
|
|
9
|
+
const { jsonPath, screenshotPath } = await extractAndSaveElements(page, `${testName}_failure`);
|
|
10
|
+
await testInfo.attach("dom-elements", { path: jsonPath, contentType: "application/json" });
|
|
11
|
+
await testInfo.attach("failure-screenshot", { path: screenshotPath, contentType: "image/png" });
|
|
12
|
+
const failureInfoPath = path.join(path.dirname(jsonPath), "failure_info.json");
|
|
13
|
+
await analyzeFailure(page, testInfo, consoleLogs, networkFailures, { jsonPath, screenshotPath, failureInfoPath });
|
|
14
|
+
await testInfo.attach("failure-info", { path: failureInfoPath, contentType: "application/json" });
|
|
15
|
+
console.log("[Test Failure] DOM capture completed successfully");
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.error("[Test Failure] Error capturing DOM state:", error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
captureFailure
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/capture.ts"],"sourcesContent":["import type { Page, TestInfo } from '@playwright/test';\nimport path from 'path';\nimport { extractAndSaveElements } from './helpers/dom-extractor.js';\nimport { analyzeFailure } from './helpers/failure-analyzer.js';\nimport type { ConsoleEntry, NetworkFailure } from './helpers/failure-analyzer.js';\n\nexport async function captureFailure(\n page: Page,\n testInfo: TestInfo,\n consoleLogs: ConsoleEntry[],\n networkFailures: NetworkFailure[]\n): Promise<void> {\n if (testInfo.status === testInfo.expectedStatus) return;\n console.log('\\n[Test Failure] Capturing DOM state before trace...');\n try {\n const testName = testInfo.title.replace(/\\s+/g, '_').toLowerCase();\n const { jsonPath, screenshotPath } = await extractAndSaveElements(page, `${testName}_failure`);\n await testInfo.attach('dom-elements', { path: jsonPath, contentType: 'application/json' });\n await testInfo.attach('failure-screenshot', { path: screenshotPath, contentType: 'image/png' });\n const failureInfoPath = path.join(path.dirname(jsonPath), 'failure_info.json');\n await analyzeFailure(page, testInfo, consoleLogs, networkFailures, { jsonPath, screenshotPath, failureInfoPath });\n await testInfo.attach('failure-info', { path: failureInfoPath, contentType: 'application/json' });\n console.log('[Test Failure] DOM capture completed successfully');\n } catch (error) {\n console.error('[Test Failure] Error capturing DOM state:', error);\n }\n}\n"],"mappings":"AACA,OAAO,UAAU;AACjB,SAAS,8BAA8B;AACvC,SAAS,sBAAsB;AAG/B,eAAsB,eACpB,MACA,UACA,aACA,iBACe;AACf,MAAI,SAAS,WAAW,SAAS,eAAgB;AACjD,UAAQ,IAAI,sDAAsD;AAClE,MAAI;AACF,UAAM,WAAW,SAAS,MAAM,QAAQ,QAAQ,GAAG,EAAE,YAAY;AACjE,UAAM,EAAE,UAAU,eAAe,IAAI,MAAM,uBAAuB,MAAM,GAAG,QAAQ,UAAU;AAC7F,UAAM,SAAS,OAAO,gBAAgB,EAAE,MAAM,UAAU,aAAa,mBAAmB,CAAC;AACzF,UAAM,SAAS,OAAO,sBAAsB,EAAE,MAAM,gBAAgB,aAAa,YAAY,CAAC;AAC9F,UAAM,kBAAkB,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG,mBAAmB;AAC7E,UAAM,eAAe,MAAM,UAAU,aAAa,iBAAiB,EAAE,UAAU,gBAAgB,gBAAgB,CAAC;AAChH,UAAM,SAAS,OAAO,gBAAgB,EAAE,MAAM,iBAAiB,aAAa,mBAAmB,CAAC;AAChG,YAAQ,IAAI,mDAAmD;AAAA,EACjE,SAAS,OAAO;AACd,YAAQ,MAAM,6CAA6C,KAAK;AAAA,EAClE;AACF;","names":[]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var agent_setup_exports = {};
|
|
30
|
+
__export(agent_setup_exports, {
|
|
31
|
+
createAgentFile: () => createAgentFile,
|
|
32
|
+
planAgentFile: () => planAgentFile
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(agent_setup_exports);
|
|
35
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
36
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
37
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
38
|
+
var import_path = __toESM(require("path"), 1);
|
|
39
|
+
var import_url = require("url");
|
|
40
|
+
const _currentDir = import_path.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
|
|
41
|
+
const AGENT_FILE_REL = import_path.default.join(".github", "agents", "amikoo-playwright-agent.md");
|
|
42
|
+
function getTemplate(workspacePath) {
|
|
43
|
+
const raw = import_fs.default.readFileSync(import_path.default.join(_currentDir, "amikoo-playwright-agent.md"), "utf8");
|
|
44
|
+
const cleaned = raw.replace(/\[\d{2}:\d{2}\][^\n]*/g, "").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
45
|
+
return cleaned.replaceAll('"workspace_path": ""', `"workspace_path": "${workspacePath}"`);
|
|
46
|
+
}
|
|
47
|
+
function planAgentFile(projectRoot) {
|
|
48
|
+
const filePath = import_path.default.join(projectRoot, AGENT_FILE_REL);
|
|
49
|
+
return {
|
|
50
|
+
action: import_fs.default.existsSync(filePath) ? "skip" : "create",
|
|
51
|
+
path: filePath
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function createAgentFile(projectRoot) {
|
|
55
|
+
const filePath = import_path.default.join(projectRoot, AGENT_FILE_REL);
|
|
56
|
+
if (import_fs.default.existsSync(filePath)) {
|
|
57
|
+
return { path: filePath, action: "skipped" };
|
|
58
|
+
}
|
|
59
|
+
import_fs.default.mkdirSync(import_path.default.dirname(filePath), { recursive: true });
|
|
60
|
+
import_fs.default.writeFileSync(filePath, getTemplate(projectRoot), "utf8");
|
|
61
|
+
return { path: filePath, action: "created" };
|
|
62
|
+
}
|
|
63
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
64
|
+
0 && (module.exports = {
|
|
65
|
+
createAgentFile,
|
|
66
|
+
planAgentFile
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=agent-setup.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/agent-setup.ts","../../node_modules/tsup/assets/cjs_shims.js"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst _currentDir = path.dirname(fileURLToPath(import.meta.url));\nconst AGENT_FILE_REL = path.join('.github', 'agents', 'amikoo-playwright-agent.md');\n\nfunction getTemplate(workspacePath: string): string {\n const raw = fs.readFileSync(path.join(_currentDir, 'amikoo-playwright-agent.md'), 'utf8');\n // Strip chat-message artifacts like [12:39]... embedded in the source file\n const cleaned = raw.replace(/\\[\\d{2}:\\d{2}\\][^\\n]*/g, '').replace(/\\n{3,}/g, '\\n\\n').trimEnd() + '\\n';\n // Fill in workspace_path (all occurrences)\n return cleaned.replaceAll('\"workspace_path\": \"\"', `\"workspace_path\": \"${workspacePath}\"`);\n}\n\nexport interface AgentFilePlan {\n action: 'create' | 'skip';\n path: string;\n}\n\nexport function planAgentFile(projectRoot: string): AgentFilePlan {\n const filePath = path.join(projectRoot, AGENT_FILE_REL);\n return {\n action: fs.existsSync(filePath) ? 'skip' : 'create',\n path: filePath,\n };\n}\n\nexport function createAgentFile(projectRoot: string): { path: string; action: 'created' | 'skipped' } {\n const filePath = path.join(projectRoot, AGENT_FILE_REL);\n if (fs.existsSync(filePath)) {\n return { path: filePath, action: 'skipped' };\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, getTemplate(projectRoot), 'utf8');\n return { path: filePath, action: 'created' };\n}\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACKA,IAAM,mBAAmB,MACvB,OAAO,aAAa,cAChB,IAAI,IAAI,QAAQ,UAAU,EAAE,EAAE,OAC7B,SAAS,iBAAiB,SAAS,cAAc,QAAQ,YAAY,MAAM,WAC1E,SAAS,cAAc,MACvB,IAAI,IAAI,WAAW,SAAS,OAAO,EAAE;AAEtC,IAAM,gBAAgC,iCAAiB;ADZ9D,gBAAe;AACf,kBAAiB;AACjB,iBAA8B;AAE9B,MAAM,cAAc,YAAAA,QAAK,YAAQ,0BAAc,aAAe,CAAC;AAC/D,MAAM,iBAAiB,YAAAA,QAAK,KAAK,WAAW,UAAU,4BAA4B;AAElF,SAAS,YAAY,eAA+B;AAClD,QAAM,MAAM,UAAAC,QAAG,aAAa,YAAAD,QAAK,KAAK,aAAa,4BAA4B,GAAG,MAAM;AAExF,QAAM,UAAU,IAAI,QAAQ,0BAA0B,EAAE,EAAE,QAAQ,WAAW,MAAM,EAAE,QAAQ,IAAI;AAEjG,SAAO,QAAQ,WAAW,wBAAwB,sBAAsB,aAAa,GAAG;AAC1F;AAOO,SAAS,cAAc,aAAoC;AAChE,QAAM,WAAW,YAAAA,QAAK,KAAK,aAAa,cAAc;AACtD,SAAO;AAAA,IACL,QAAQ,UAAAC,QAAG,WAAW,QAAQ,IAAI,SAAS;AAAA,IAC3C,MAAM;AAAA,EACR;AACF;AAEO,SAAS,gBAAgB,aAAsE;AACpG,QAAM,WAAW,YAAAD,QAAK,KAAK,aAAa,cAAc;AACtD,MAAI,UAAAC,QAAG,WAAW,QAAQ,GAAG;AAC3B,WAAO,EAAE,MAAM,UAAU,QAAQ,UAAU;AAAA,EAC7C;AACA,YAAAA,QAAG,UAAU,YAAAD,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,YAAAC,QAAG,cAAc,UAAU,YAAY,WAAW,GAAG,MAAM;AAC3D,SAAO,EAAE,MAAM,UAAU,QAAQ,UAAU;AAC7C;","names":["path","fs"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface AgentFilePlan {
|
|
2
|
+
action: 'create' | 'skip';
|
|
3
|
+
path: string;
|
|
4
|
+
}
|
|
5
|
+
declare function planAgentFile(projectRoot: string): AgentFilePlan;
|
|
6
|
+
declare function createAgentFile(projectRoot: string): {
|
|
7
|
+
path: string;
|
|
8
|
+
action: 'created' | 'skipped';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { type AgentFilePlan, createAgentFile, planAgentFile };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface AgentFilePlan {
|
|
2
|
+
action: 'create' | 'skip';
|
|
3
|
+
path: string;
|
|
4
|
+
}
|
|
5
|
+
declare function planAgentFile(projectRoot: string): AgentFilePlan;
|
|
6
|
+
declare function createAgentFile(projectRoot: string): {
|
|
7
|
+
path: string;
|
|
8
|
+
action: 'created' | 'skipped';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { type AgentFilePlan, createAgentFile, planAgentFile };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const _currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const AGENT_FILE_REL = path.join(".github", "agents", "amikoo-playwright-agent.md");
|
|
6
|
+
function getTemplate(workspacePath) {
|
|
7
|
+
const raw = fs.readFileSync(path.join(_currentDir, "amikoo-playwright-agent.md"), "utf8");
|
|
8
|
+
const cleaned = raw.replace(/\[\d{2}:\d{2}\][^\n]*/g, "").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
9
|
+
return cleaned.replaceAll('"workspace_path": ""', `"workspace_path": "${workspacePath}"`);
|
|
10
|
+
}
|
|
11
|
+
function planAgentFile(projectRoot) {
|
|
12
|
+
const filePath = path.join(projectRoot, AGENT_FILE_REL);
|
|
13
|
+
return {
|
|
14
|
+
action: fs.existsSync(filePath) ? "skip" : "create",
|
|
15
|
+
path: filePath
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function createAgentFile(projectRoot) {
|
|
19
|
+
const filePath = path.join(projectRoot, AGENT_FILE_REL);
|
|
20
|
+
if (fs.existsSync(filePath)) {
|
|
21
|
+
return { path: filePath, action: "skipped" };
|
|
22
|
+
}
|
|
23
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
24
|
+
fs.writeFileSync(filePath, getTemplate(projectRoot), "utf8");
|
|
25
|
+
return { path: filePath, action: "created" };
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
createAgentFile,
|
|
29
|
+
planAgentFile
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=agent-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/agent-setup.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst _currentDir = path.dirname(fileURLToPath(import.meta.url));\nconst AGENT_FILE_REL = path.join('.github', 'agents', 'amikoo-playwright-agent.md');\n\nfunction getTemplate(workspacePath: string): string {\n const raw = fs.readFileSync(path.join(_currentDir, 'amikoo-playwright-agent.md'), 'utf8');\n // Strip chat-message artifacts like [12:39]... embedded in the source file\n const cleaned = raw.replace(/\\[\\d{2}:\\d{2}\\][^\\n]*/g, '').replace(/\\n{3,}/g, '\\n\\n').trimEnd() + '\\n';\n // Fill in workspace_path (all occurrences)\n return cleaned.replaceAll('\"workspace_path\": \"\"', `\"workspace_path\": \"${workspacePath}\"`);\n}\n\nexport interface AgentFilePlan {\n action: 'create' | 'skip';\n path: string;\n}\n\nexport function planAgentFile(projectRoot: string): AgentFilePlan {\n const filePath = path.join(projectRoot, AGENT_FILE_REL);\n return {\n action: fs.existsSync(filePath) ? 'skip' : 'create',\n path: filePath,\n };\n}\n\nexport function createAgentFile(projectRoot: string): { path: string; action: 'created' | 'skipped' } {\n const filePath = path.join(projectRoot, AGENT_FILE_REL);\n if (fs.existsSync(filePath)) {\n return { path: filePath, action: 'skipped' };\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, getTemplate(projectRoot), 'utf8');\n return { path: filePath, action: 'created' };\n}\n"],"mappings":"AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,MAAM,cAAc,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC/D,MAAM,iBAAiB,KAAK,KAAK,WAAW,UAAU,4BAA4B;AAElF,SAAS,YAAY,eAA+B;AAClD,QAAM,MAAM,GAAG,aAAa,KAAK,KAAK,aAAa,4BAA4B,GAAG,MAAM;AAExF,QAAM,UAAU,IAAI,QAAQ,0BAA0B,EAAE,EAAE,QAAQ,WAAW,MAAM,EAAE,QAAQ,IAAI;AAEjG,SAAO,QAAQ,WAAW,wBAAwB,sBAAsB,aAAa,GAAG;AAC1F;AAOO,SAAS,cAAc,aAAoC;AAChE,QAAM,WAAW,KAAK,KAAK,aAAa,cAAc;AACtD,SAAO;AAAA,IACL,QAAQ,GAAG,WAAW,QAAQ,IAAI,SAAS;AAAA,IAC3C,MAAM;AAAA,EACR;AACF;AAEO,SAAS,gBAAgB,aAAsE;AACpG,QAAM,WAAW,KAAK,KAAK,aAAa,cAAc;AACtD,MAAI,GAAG,WAAW,QAAQ,GAAG;AAC3B,WAAO,EAAE,MAAM,UAAU,QAAQ,UAAU;AAAA,EAC7C;AACA,KAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,KAAG,cAAc,UAAU,YAAY,WAAW,GAAG,MAAM;AAC3D,SAAO,EAAE,MAAM,UAAU,QAAQ,UAAU;AAC7C;","names":[]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: amikoo-playwright-agent
|
|
3
|
+
description: Orchestrates the analysis and repair of failing Playwright (TypeScript) tests using DOM failure artifacts and tools.
|
|
4
|
+
tools: ['execute', 'read', 'edit', 'search', 'amikoo-mcp/*']
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a maintenance agent for failing Playwright (TypeScript) tests.
|
|
8
|
+
|
|
9
|
+
Your responsibility is to analyze a Playwright test failure requested by the user and apply the correct fixes using the provieded tools, following a strict and verifiable workflow. You should only try to fix ine test at a time.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Preconditions (MANDATORY)
|
|
14
|
+
|
|
15
|
+
Before attempting any analysis or fix, you MUST:
|
|
16
|
+
|
|
17
|
+
1. Locate the failure data directory:
|
|
18
|
+
- `./test-results/dom-failures`
|
|
19
|
+
|
|
20
|
+
2. Verify that **all** the following files exist:
|
|
21
|
+
- `dom_elements.json`
|
|
22
|
+
- `failure_info.json`
|
|
23
|
+
- multiple screenshot files (e.g. `failure_screenshot_1.png`, `failure_screenshot_2.png`, etc.) that capture the state of the DOM at the time of failure
|
|
24
|
+
|
|
25
|
+
If **any** of these files are missing:
|
|
26
|
+
- STOP immediately
|
|
27
|
+
- Inform the user that the failure artifacts are required to proceed
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Failure Ownership Validation (MANDATORY)
|
|
32
|
+
|
|
33
|
+
Before analyzing or fixing anything, you MUST:
|
|
34
|
+
|
|
35
|
+
1. Read `failure_info.json`
|
|
36
|
+
2. Validate that the `location` field contains the name/path of the test file the user explicitly asked to fix
|
|
37
|
+
|
|
38
|
+
If the `location` does NOT correspond to the requested test:
|
|
39
|
+
- STOP immediately
|
|
40
|
+
- Inform the user that the failure data belongs to a different test and cannot be used
|
|
41
|
+
|
|
42
|
+
This validation prevents using tools based on unrelated failure data.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Analysis Workflow
|
|
47
|
+
|
|
48
|
+
Once all preconditions are satisfied you must strictly follow this analysis and fix workflow:
|
|
49
|
+
|
|
50
|
+
1. Read and deeply analyze:
|
|
51
|
+
- `dom_elements.json` from the failure data path
|
|
52
|
+
- `failure_info.json` from the failure data path
|
|
53
|
+
- The failing Playwright test script and any other related scripts or files as sometimes the failure might be caused by issues in related scripts/files, not only the test script itself.
|
|
54
|
+
This will help you check the main cause of the failure.
|
|
55
|
+
|
|
56
|
+
2. Call the tool `amikoo-mcp/call_repair_agent` with the required parameters. Do NOT attempt to fix the issue yourself. The tool will return a structured suggestion based on the failure artifacts. You can include multiple files in the `test_files_path` parameter if your analysis determines that multiple files are involved in the failure.
|
|
57
|
+
|
|
58
|
+
3. Based on the suggestions retrieved from the tool, apply the fix to the test code.
|
|
59
|
+
|
|
60
|
+
4. With the terminal, run the test script with the fix applied to check the issue is resolved. use the command: npx playwright test <script-file-path> --project=chromium --headed when doing so.
|
|
61
|
+
|
|
62
|
+
5. If the test script still fails, repeat steps 1-4 until the test passes.
|
|
63
|
+
|
|
64
|
+
Your analysis must be evidence-based and derived from the failure artifacts and test code.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Tool Usage Rules
|
|
69
|
+
|
|
70
|
+
Once the root cause is identified, you MUST call the appropriate tool **before** applying any code changes.
|
|
71
|
+
|
|
72
|
+
When calling any tool, ALWAYS use the following parameters:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"workspace_path": "",
|
|
77
|
+
"test_files_path": "[array of 1 to n file paths related to the test failure in case the cause analysis determines multiple files are involved to the issue]",
|
|
78
|
+
"failure_data_path": "./test-results/dom-failures"
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
You MUST NOT attempt to fix the test failure without first consulting the appropriate tool.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var fixture_creator_exports = {};
|
|
30
|
+
__export(fixture_creator_exports, {
|
|
31
|
+
createOrUpdateFixture: () => createOrUpdateFixture,
|
|
32
|
+
planFixture: () => planFixture
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(fixture_creator_exports);
|
|
35
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
36
|
+
var import_path = __toESM(require("path"), 1);
|
|
37
|
+
const ESM_TEMPLATE = `import { test as base } from '@playwright/test';
|
|
38
|
+
import { captureFailure, setupPageListeners } from '@muuktest/amikoo-playwright';
|
|
39
|
+
|
|
40
|
+
export const test = base.extend({
|
|
41
|
+
page: async ({ page }, use, testInfo) => {
|
|
42
|
+
const { consoleLogs, networkFailures } = setupPageListeners(page);
|
|
43
|
+
await use(page);
|
|
44
|
+
await captureFailure(page, testInfo, consoleLogs, networkFailures);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export { expect } from '@playwright/test';
|
|
49
|
+
`;
|
|
50
|
+
const CJS_TEMPLATE = `const { test: base } = require('@playwright/test');
|
|
51
|
+
const { captureFailure, setupPageListeners } = require('@muuktest/amikoo-playwright');
|
|
52
|
+
|
|
53
|
+
const test = base.extend({
|
|
54
|
+
page: async ({ page }, use, testInfo) => {
|
|
55
|
+
const { consoleLogs, networkFailures } = setupPageListeners(page);
|
|
56
|
+
await use(page);
|
|
57
|
+
await captureFailure(page, testInfo, consoleLogs, networkFailures);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const { expect } = require('@playwright/test');
|
|
62
|
+
module.exports = { test, expect };
|
|
63
|
+
`;
|
|
64
|
+
function addImportToContent(content, language, moduleSystem) {
|
|
65
|
+
if (moduleSystem === "esm") {
|
|
66
|
+
const importLine = `import { captureFailure, setupPageListeners } from '@muuktest/amikoo-playwright';`;
|
|
67
|
+
const lines = content.split("\n");
|
|
68
|
+
let lastImportIdx = -1;
|
|
69
|
+
for (let i = 0; i < lines.length; i++) {
|
|
70
|
+
if (/^import\s/.test(lines[i])) lastImportIdx = i;
|
|
71
|
+
}
|
|
72
|
+
if (lastImportIdx === -1) {
|
|
73
|
+
return importLine + "\n" + content;
|
|
74
|
+
}
|
|
75
|
+
lines.splice(lastImportIdx + 1, 0, importLine);
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
} else {
|
|
78
|
+
const requireLine = `const { captureFailure, setupPageListeners } = require('@muuktest/amikoo-playwright');`;
|
|
79
|
+
const lines = content.split("\n");
|
|
80
|
+
let lastRequireIdx = -1;
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
if (/require\s*\(/.test(lines[i])) lastRequireIdx = i;
|
|
83
|
+
}
|
|
84
|
+
if (lastRequireIdx === -1) {
|
|
85
|
+
return requireLine + "\n" + content;
|
|
86
|
+
}
|
|
87
|
+
lines.splice(lastRequireIdx + 1, 0, requireLine);
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function patchPageFixture(content) {
|
|
92
|
+
const pageFixtureRegex = /page:\s*async\s*\(\s*\{([^}]*)\}\s*,\s*(\w+)(?:\s*,\s*(\w+))?\s*\)/;
|
|
93
|
+
const match = pageFixtureRegex.exec(content);
|
|
94
|
+
if (!match) return content;
|
|
95
|
+
const matchStart = match.index;
|
|
96
|
+
const matchEnd = matchStart + match[0].length;
|
|
97
|
+
let newSignature = match[0];
|
|
98
|
+
const params = [match[1]?.trim(), match[2]?.trim(), match[3]?.trim()].filter(Boolean);
|
|
99
|
+
if (!params.includes("testInfo")) {
|
|
100
|
+
if (match[3]) {
|
|
101
|
+
newSignature = newSignature.replace(match[3], "testInfo");
|
|
102
|
+
} else if (match[2]) {
|
|
103
|
+
newSignature = newSignature.replace(match[2], `${match[2]}, testInfo`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
let braceStart = content.indexOf("{", matchEnd);
|
|
107
|
+
if (braceStart === -1) return content;
|
|
108
|
+
let depth = 0;
|
|
109
|
+
let braceEnd = -1;
|
|
110
|
+
for (let i = braceStart; i < content.length; i++) {
|
|
111
|
+
if (content[i] === "{") depth++;
|
|
112
|
+
else if (content[i] === "}") {
|
|
113
|
+
depth--;
|
|
114
|
+
if (depth === 0) {
|
|
115
|
+
braceEnd = i;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (braceEnd === -1) return content;
|
|
121
|
+
const bodyContent = content.slice(braceStart + 1, braceEnd);
|
|
122
|
+
const setupLine = `
|
|
123
|
+
const { consoleLogs, networkFailures } = setupPageListeners(page);`;
|
|
124
|
+
const captureLine = `
|
|
125
|
+
await captureFailure(page, testInfo, consoleLogs, networkFailures);`;
|
|
126
|
+
const newBody = setupLine + bodyContent + captureLine + "\n ";
|
|
127
|
+
return content.slice(0, match.index) + newSignature + content.slice(matchEnd, braceStart + 1) + newBody + content.slice(braceEnd);
|
|
128
|
+
}
|
|
129
|
+
function planFixture(fixtureFile, projectRoot, language, moduleSystem) {
|
|
130
|
+
if (!fixtureFile) {
|
|
131
|
+
const ext = language === "ts" ? "ts" : "js";
|
|
132
|
+
const outPath = import_path.default.join(projectRoot, `fixtures.${ext}`);
|
|
133
|
+
return { action: "create", path: outPath };
|
|
134
|
+
}
|
|
135
|
+
const content = import_fs.default.readFileSync(fixtureFile, "utf8");
|
|
136
|
+
if (content.includes("captureFailure")) {
|
|
137
|
+
return { action: "skip", path: fixtureFile };
|
|
138
|
+
}
|
|
139
|
+
return { action: "update", path: fixtureFile };
|
|
140
|
+
}
|
|
141
|
+
function createOrUpdateFixture(fixtureFile, projectRoot, language, moduleSystem) {
|
|
142
|
+
if (!fixtureFile) {
|
|
143
|
+
const ext = language === "ts" ? "ts" : "js";
|
|
144
|
+
const outPath = import_path.default.join(projectRoot, `fixtures.${ext}`);
|
|
145
|
+
const template = moduleSystem === "cjs" ? CJS_TEMPLATE : ESM_TEMPLATE;
|
|
146
|
+
import_fs.default.writeFileSync(outPath, template, "utf8");
|
|
147
|
+
return { path: outPath, action: "created" };
|
|
148
|
+
}
|
|
149
|
+
let content = import_fs.default.readFileSync(fixtureFile, "utf8");
|
|
150
|
+
if (content.includes("captureFailure")) {
|
|
151
|
+
return { path: fixtureFile, action: "skipped" };
|
|
152
|
+
}
|
|
153
|
+
content = addImportToContent(content, language, moduleSystem);
|
|
154
|
+
content = patchPageFixture(content);
|
|
155
|
+
import_fs.default.writeFileSync(fixtureFile, content, "utf8");
|
|
156
|
+
return { path: fixtureFile, action: "updated" };
|
|
157
|
+
}
|
|
158
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
159
|
+
0 && (module.exports = {
|
|
160
|
+
createOrUpdateFixture,
|
|
161
|
+
planFixture
|
|
162
|
+
});
|
|
163
|
+
//# sourceMappingURL=fixture-creator.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/fixture-creator.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface FixtureResult {\n path: string;\n action: 'created' | 'updated' | 'skipped';\n}\n\nexport interface FixturePlan {\n action: 'create' | 'update' | 'skip';\n path: string;\n}\n\nconst ESM_TEMPLATE = `import { test as base } from '@playwright/test';\nimport { captureFailure, setupPageListeners } from '@muuktest/amikoo-playwright';\n\nexport const test = base.extend({\n page: async ({ page }, use, testInfo) => {\n const { consoleLogs, networkFailures } = setupPageListeners(page);\n await use(page);\n await captureFailure(page, testInfo, consoleLogs, networkFailures);\n }\n});\n\nexport { expect } from '@playwright/test';\n`;\n\nconst CJS_TEMPLATE = `const { test: base } = require('@playwright/test');\nconst { captureFailure, setupPageListeners } = require('@muuktest/amikoo-playwright');\n\nconst test = base.extend({\n page: async ({ page }, use, testInfo) => {\n const { consoleLogs, networkFailures } = setupPageListeners(page);\n await use(page);\n await captureFailure(page, testInfo, consoleLogs, networkFailures);\n }\n});\n\nconst { expect } = require('@playwright/test');\nmodule.exports = { test, expect };\n`;\n\nfunction addImportToContent(content: string, language: 'ts' | 'js', moduleSystem: 'esm' | 'cjs'): string {\n if (moduleSystem === 'esm') {\n const importLine = `import { captureFailure, setupPageListeners } from '@muuktest/amikoo-playwright';`;\n // Find last import line\n const lines = content.split('\\n');\n let lastImportIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n if (/^import\\s/.test(lines[i])) lastImportIdx = i;\n }\n if (lastImportIdx === -1) {\n return importLine + '\\n' + content;\n }\n lines.splice(lastImportIdx + 1, 0, importLine);\n return lines.join('\\n');\n } else {\n const requireLine = `const { captureFailure, setupPageListeners } = require('@muuktest/amikoo-playwright');`;\n const lines = content.split('\\n');\n let lastRequireIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n if (/require\\s*\\(/.test(lines[i])) lastRequireIdx = i;\n }\n if (lastRequireIdx === -1) {\n return requireLine + '\\n' + content;\n }\n lines.splice(lastRequireIdx + 1, 0, requireLine);\n return lines.join('\\n');\n }\n}\n\nfunction patchPageFixture(content: string): string {\n // Find `page:` fixture definition — match the async function body\n // Strategy: find `page:` followed by `async`, locate its opening { and closing }\n const pageFixtureRegex = /page:\\s*async\\s*\\(\\s*\\{([^}]*)\\}\\s*,\\s*(\\w+)(?:\\s*,\\s*(\\w+))?\\s*\\)/;\n const match = pageFixtureRegex.exec(content);\n if (!match) return content;\n\n const matchStart = match.index;\n const matchEnd = matchStart + match[0].length;\n\n // Ensure testInfo is in parameters\n let newSignature = match[0];\n const params = [match[1]?.trim(), match[2]?.trim(), match[3]?.trim()].filter(Boolean);\n if (!params.includes('testInfo')) {\n // Add testInfo as third param\n if (match[3]) {\n // already has 3 params — replace third\n newSignature = newSignature.replace(match[3], 'testInfo');\n } else if (match[2]) {\n // has use, add testInfo\n newSignature = newSignature.replace(match[2], `${match[2]}, testInfo`);\n }\n }\n\n // Find the opening { of the async function body after the signature\n let braceStart = content.indexOf('{', matchEnd);\n if (braceStart === -1) return content;\n\n // Find matching closing }\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < content.length; i++) {\n if (content[i] === '{') depth++;\n else if (content[i] === '}') {\n depth--;\n if (depth === 0) { braceEnd = i; break; }\n }\n }\n if (braceEnd === -1) return content;\n\n const bodyContent = content.slice(braceStart + 1, braceEnd);\n\n // Insert setupPageListeners after opening brace (after first newline or at start)\n const setupLine = `\\n const { consoleLogs, networkFailures } = setupPageListeners(page);`;\n // Insert captureFailure before closing brace\n const captureLine = `\\n await captureFailure(page, testInfo, consoleLogs, networkFailures);`;\n\n const newBody = setupLine + bodyContent + captureLine + '\\n ';\n\n return (\n content.slice(0, match.index) +\n newSignature +\n content.slice(matchEnd, braceStart + 1) +\n newBody +\n content.slice(braceEnd)\n );\n}\n\nexport function planFixture(\n fixtureFile: string | null,\n projectRoot: string,\n language: 'ts' | 'js',\n moduleSystem: 'esm' | 'cjs'\n): FixturePlan {\n if (!fixtureFile) {\n const ext = language === 'ts' ? 'ts' : 'js';\n const outPath = path.join(projectRoot, `fixtures.${ext}`);\n return { action: 'create', path: outPath };\n }\n const content = fs.readFileSync(fixtureFile, 'utf8');\n if (content.includes('captureFailure')) {\n return { action: 'skip', path: fixtureFile };\n }\n return { action: 'update', path: fixtureFile };\n}\n\nexport function createOrUpdateFixture(\n fixtureFile: string | null,\n projectRoot: string,\n language: 'ts' | 'js',\n moduleSystem: 'esm' | 'cjs'\n): FixtureResult {\n // CREATE path\n if (!fixtureFile) {\n const ext = language === 'ts' ? 'ts' : 'js';\n const outPath = path.join(projectRoot, `fixtures.${ext}`);\n const template = moduleSystem === 'cjs' ? CJS_TEMPLATE : ESM_TEMPLATE;\n fs.writeFileSync(outPath, template, 'utf8');\n return { path: outPath, action: 'created' };\n }\n\n // UPDATE path\n let content = fs.readFileSync(fixtureFile, 'utf8');\n\n // Idempotency guard\n if (content.includes('captureFailure')) {\n return { path: fixtureFile, action: 'skipped' };\n }\n\n content = addImportToContent(content, language, moduleSystem);\n content = patchPageFixture(content);\n fs.writeFileSync(fixtureFile, content, 'utf8');\n return { path: fixtureFile, action: 'updated' };\n}\n"],"mappings":"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","names":["path","fs"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface FixtureResult {
|
|
2
|
+
path: string;
|
|
3
|
+
action: 'created' | 'updated' | 'skipped';
|
|
4
|
+
}
|
|
5
|
+
interface FixturePlan {
|
|
6
|
+
action: 'create' | 'update' | 'skip';
|
|
7
|
+
path: string;
|
|
8
|
+
}
|
|
9
|
+
declare function planFixture(fixtureFile: string | null, projectRoot: string, language: 'ts' | 'js', moduleSystem: 'esm' | 'cjs'): FixturePlan;
|
|
10
|
+
declare function createOrUpdateFixture(fixtureFile: string | null, projectRoot: string, language: 'ts' | 'js', moduleSystem: 'esm' | 'cjs'): FixtureResult;
|
|
11
|
+
|
|
12
|
+
export { type FixturePlan, type FixtureResult, createOrUpdateFixture, planFixture };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface FixtureResult {
|
|
2
|
+
path: string;
|
|
3
|
+
action: 'created' | 'updated' | 'skipped';
|
|
4
|
+
}
|
|
5
|
+
interface FixturePlan {
|
|
6
|
+
action: 'create' | 'update' | 'skip';
|
|
7
|
+
path: string;
|
|
8
|
+
}
|
|
9
|
+
declare function planFixture(fixtureFile: string | null, projectRoot: string, language: 'ts' | 'js', moduleSystem: 'esm' | 'cjs'): FixturePlan;
|
|
10
|
+
declare function createOrUpdateFixture(fixtureFile: string | null, projectRoot: string, language: 'ts' | 'js', moduleSystem: 'esm' | 'cjs'): FixtureResult;
|
|
11
|
+
|
|
12
|
+
export { type FixturePlan, type FixtureResult, createOrUpdateFixture, planFixture };
|