@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
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
const ESM_TEMPLATE = `import { test as base } from '@playwright/test';
|
|
4
|
+
import { captureFailure, setupPageListeners } from '@muuktest/amikoo-playwright';
|
|
5
|
+
|
|
6
|
+
export const test = base.extend({
|
|
7
|
+
page: async ({ page }, use, testInfo) => {
|
|
8
|
+
const { consoleLogs, networkFailures } = setupPageListeners(page);
|
|
9
|
+
await use(page);
|
|
10
|
+
await captureFailure(page, testInfo, consoleLogs, networkFailures);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export { expect } from '@playwright/test';
|
|
15
|
+
`;
|
|
16
|
+
const CJS_TEMPLATE = `const { test: base } = require('@playwright/test');
|
|
17
|
+
const { captureFailure, setupPageListeners } = require('@muuktest/amikoo-playwright');
|
|
18
|
+
|
|
19
|
+
const test = base.extend({
|
|
20
|
+
page: async ({ page }, use, testInfo) => {
|
|
21
|
+
const { consoleLogs, networkFailures } = setupPageListeners(page);
|
|
22
|
+
await use(page);
|
|
23
|
+
await captureFailure(page, testInfo, consoleLogs, networkFailures);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const { expect } = require('@playwright/test');
|
|
28
|
+
module.exports = { test, expect };
|
|
29
|
+
`;
|
|
30
|
+
function addImportToContent(content, language, moduleSystem) {
|
|
31
|
+
if (moduleSystem === "esm") {
|
|
32
|
+
const importLine = `import { captureFailure, setupPageListeners } from '@muuktest/amikoo-playwright';`;
|
|
33
|
+
const lines = content.split("\n");
|
|
34
|
+
let lastImportIdx = -1;
|
|
35
|
+
for (let i = 0; i < lines.length; i++) {
|
|
36
|
+
if (/^import\s/.test(lines[i])) lastImportIdx = i;
|
|
37
|
+
}
|
|
38
|
+
if (lastImportIdx === -1) {
|
|
39
|
+
return importLine + "\n" + content;
|
|
40
|
+
}
|
|
41
|
+
lines.splice(lastImportIdx + 1, 0, importLine);
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
} else {
|
|
44
|
+
const requireLine = `const { captureFailure, setupPageListeners } = require('@muuktest/amikoo-playwright');`;
|
|
45
|
+
const lines = content.split("\n");
|
|
46
|
+
let lastRequireIdx = -1;
|
|
47
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48
|
+
if (/require\s*\(/.test(lines[i])) lastRequireIdx = i;
|
|
49
|
+
}
|
|
50
|
+
if (lastRequireIdx === -1) {
|
|
51
|
+
return requireLine + "\n" + content;
|
|
52
|
+
}
|
|
53
|
+
lines.splice(lastRequireIdx + 1, 0, requireLine);
|
|
54
|
+
return lines.join("\n");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function patchPageFixture(content) {
|
|
58
|
+
const pageFixtureRegex = /page:\s*async\s*\(\s*\{([^}]*)\}\s*,\s*(\w+)(?:\s*,\s*(\w+))?\s*\)/;
|
|
59
|
+
const match = pageFixtureRegex.exec(content);
|
|
60
|
+
if (!match) return content;
|
|
61
|
+
const matchStart = match.index;
|
|
62
|
+
const matchEnd = matchStart + match[0].length;
|
|
63
|
+
let newSignature = match[0];
|
|
64
|
+
const params = [match[1]?.trim(), match[2]?.trim(), match[3]?.trim()].filter(Boolean);
|
|
65
|
+
if (!params.includes("testInfo")) {
|
|
66
|
+
if (match[3]) {
|
|
67
|
+
newSignature = newSignature.replace(match[3], "testInfo");
|
|
68
|
+
} else if (match[2]) {
|
|
69
|
+
newSignature = newSignature.replace(match[2], `${match[2]}, testInfo`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let braceStart = content.indexOf("{", matchEnd);
|
|
73
|
+
if (braceStart === -1) return content;
|
|
74
|
+
let depth = 0;
|
|
75
|
+
let braceEnd = -1;
|
|
76
|
+
for (let i = braceStart; i < content.length; i++) {
|
|
77
|
+
if (content[i] === "{") depth++;
|
|
78
|
+
else if (content[i] === "}") {
|
|
79
|
+
depth--;
|
|
80
|
+
if (depth === 0) {
|
|
81
|
+
braceEnd = i;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (braceEnd === -1) return content;
|
|
87
|
+
const bodyContent = content.slice(braceStart + 1, braceEnd);
|
|
88
|
+
const setupLine = `
|
|
89
|
+
const { consoleLogs, networkFailures } = setupPageListeners(page);`;
|
|
90
|
+
const captureLine = `
|
|
91
|
+
await captureFailure(page, testInfo, consoleLogs, networkFailures);`;
|
|
92
|
+
const newBody = setupLine + bodyContent + captureLine + "\n ";
|
|
93
|
+
return content.slice(0, match.index) + newSignature + content.slice(matchEnd, braceStart + 1) + newBody + content.slice(braceEnd);
|
|
94
|
+
}
|
|
95
|
+
function planFixture(fixtureFile, projectRoot, language, moduleSystem) {
|
|
96
|
+
if (!fixtureFile) {
|
|
97
|
+
const ext = language === "ts" ? "ts" : "js";
|
|
98
|
+
const outPath = path.join(projectRoot, `fixtures.${ext}`);
|
|
99
|
+
return { action: "create", path: outPath };
|
|
100
|
+
}
|
|
101
|
+
const content = fs.readFileSync(fixtureFile, "utf8");
|
|
102
|
+
if (content.includes("captureFailure")) {
|
|
103
|
+
return { action: "skip", path: fixtureFile };
|
|
104
|
+
}
|
|
105
|
+
return { action: "update", path: fixtureFile };
|
|
106
|
+
}
|
|
107
|
+
function createOrUpdateFixture(fixtureFile, projectRoot, language, moduleSystem) {
|
|
108
|
+
if (!fixtureFile) {
|
|
109
|
+
const ext = language === "ts" ? "ts" : "js";
|
|
110
|
+
const outPath = path.join(projectRoot, `fixtures.${ext}`);
|
|
111
|
+
const template = moduleSystem === "cjs" ? CJS_TEMPLATE : ESM_TEMPLATE;
|
|
112
|
+
fs.writeFileSync(outPath, template, "utf8");
|
|
113
|
+
return { path: outPath, action: "created" };
|
|
114
|
+
}
|
|
115
|
+
let content = fs.readFileSync(fixtureFile, "utf8");
|
|
116
|
+
if (content.includes("captureFailure")) {
|
|
117
|
+
return { path: fixtureFile, action: "skipped" };
|
|
118
|
+
}
|
|
119
|
+
content = addImportToContent(content, language, moduleSystem);
|
|
120
|
+
content = patchPageFixture(content);
|
|
121
|
+
fs.writeFileSync(fixtureFile, content, "utf8");
|
|
122
|
+
return { path: fixtureFile, action: "updated" };
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
createOrUpdateFixture,
|
|
126
|
+
planFixture
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=fixture-creator.js.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":"AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAYjB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcrB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAerB,SAAS,mBAAmB,SAAiB,UAAuB,cAAqC;AACvG,MAAI,iBAAiB,OAAO;AAC1B,UAAM,aAAa;AAEnB,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,gBAAgB;AACpB,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAI,YAAY,KAAK,MAAM,CAAC,CAAC,EAAG,iBAAgB;AAAA,IAClD;AACA,QAAI,kBAAkB,IAAI;AACxB,aAAO,aAAa,OAAO;AAAA,IAC7B;AACA,UAAM,OAAO,gBAAgB,GAAG,GAAG,UAAU;AAC7C,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,OAAO;AACL,UAAM,cAAc;AACpB,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,iBAAiB;AACrB,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAI,eAAe,KAAK,MAAM,CAAC,CAAC,EAAG,kBAAiB;AAAA,IACtD;AACA,QAAI,mBAAmB,IAAI;AACzB,aAAO,cAAc,OAAO;AAAA,IAC9B;AACA,UAAM,OAAO,iBAAiB,GAAG,GAAG,WAAW;AAC/C,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AACF;AAEA,SAAS,iBAAiB,SAAyB;AAGjD,QAAM,mBAAmB;AACzB,QAAM,QAAQ,iBAAiB,KAAK,OAAO;AAC3C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,aAAa,MAAM;AACzB,QAAM,WAAW,aAAa,MAAM,CAAC,EAAE;AAGvC,MAAI,eAAe,MAAM,CAAC;AAC1B,QAAM,SAAS,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,EAAE,OAAO,OAAO;AACpF,MAAI,CAAC,OAAO,SAAS,UAAU,GAAG;AAEhC,QAAI,MAAM,CAAC,GAAG;AAEZ,qBAAe,aAAa,QAAQ,MAAM,CAAC,GAAG,UAAU;AAAA,IAC1D,WAAW,MAAM,CAAC,GAAG;AAEnB,qBAAe,aAAa,QAAQ,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,YAAY;AAAA,IACvE;AAAA,EACF;AAGA,MAAI,aAAa,QAAQ,QAAQ,KAAK,QAAQ;AAC9C,MAAI,eAAe,GAAI,QAAO;AAG9B,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,WAAS,IAAI,YAAY,IAAI,QAAQ,QAAQ,KAAK;AAChD,QAAI,QAAQ,CAAC,MAAM,IAAK;AAAA,aACf,QAAQ,CAAC,MAAM,KAAK;AAC3B;AACA,UAAI,UAAU,GAAG;AAAE,mBAAW;AAAG;AAAA,MAAO;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,aAAa,GAAI,QAAO;AAE5B,QAAM,cAAc,QAAQ,MAAM,aAAa,GAAG,QAAQ;AAG1D,QAAM,YAAY;AAAA;AAElB,QAAM,cAAc;AAAA;AAEpB,QAAM,UAAU,YAAY,cAAc,cAAc;AAExD,SACE,QAAQ,MAAM,GAAG,MAAM,KAAK,IAC5B,eACA,QAAQ,MAAM,UAAU,aAAa,CAAC,IACtC,UACA,QAAQ,MAAM,QAAQ;AAE1B;AAEO,SAAS,YACd,aACA,aACA,UACA,cACa;AACb,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,aAAa,OAAO,OAAO;AACvC,UAAM,UAAU,KAAK,KAAK,aAAa,YAAY,GAAG,EAAE;AACxD,WAAO,EAAE,QAAQ,UAAU,MAAM,QAAQ;AAAA,EAC3C;AACA,QAAM,UAAU,GAAG,aAAa,aAAa,MAAM;AACnD,MAAI,QAAQ,SAAS,gBAAgB,GAAG;AACtC,WAAO,EAAE,QAAQ,QAAQ,MAAM,YAAY;AAAA,EAC7C;AACA,SAAO,EAAE,QAAQ,UAAU,MAAM,YAAY;AAC/C;AAEO,SAAS,sBACd,aACA,aACA,UACA,cACe;AAEf,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,aAAa,OAAO,OAAO;AACvC,UAAM,UAAU,KAAK,KAAK,aAAa,YAAY,GAAG,EAAE;AACxD,UAAM,WAAW,iBAAiB,QAAQ,eAAe;AACzD,OAAG,cAAc,SAAS,UAAU,MAAM;AAC1C,WAAO,EAAE,MAAM,SAAS,QAAQ,UAAU;AAAA,EAC5C;AAGA,MAAI,UAAU,GAAG,aAAa,aAAa,MAAM;AAGjD,MAAI,QAAQ,SAAS,gBAAgB,GAAG;AACtC,WAAO,EAAE,MAAM,aAAa,QAAQ,UAAU;AAAA,EAChD;AAEA,YAAU,mBAAmB,SAAS,UAAU,YAAY;AAC5D,YAAU,iBAAiB,OAAO;AAClC,KAAG,cAAc,aAAa,SAAS,MAAM;AAC7C,SAAO,EAAE,MAAM,aAAa,QAAQ,UAAU;AAChD;","names":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
var import_readline = __toESM(require("readline"), 1);
|
|
26
|
+
var import_path = __toESM(require("path"), 1);
|
|
27
|
+
var import_scanner = require("./scanner.cjs");
|
|
28
|
+
var import_fixture_creator = require("./fixture-creator.cjs");
|
|
29
|
+
var import_test_updater = require("./test-updater.cjs");
|
|
30
|
+
var import_agent_setup = require("./agent-setup.cjs");
|
|
31
|
+
var import_mcp_setup = require("./mcp-setup.cjs");
|
|
32
|
+
const [, , command] = process.argv;
|
|
33
|
+
function askConfirmation(message) {
|
|
34
|
+
const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
rl.question(message, (answer) => {
|
|
37
|
+
rl.close();
|
|
38
|
+
resolve(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function displayPreview(fixturePlan, testPlan, projectRoot) {
|
|
43
|
+
console.log("\n[amikoo] Planned changes:\n");
|
|
44
|
+
const relFixture = import_path.default.relative(projectRoot, fixturePlan.path);
|
|
45
|
+
if (fixturePlan.action === "create") console.log(` CREATE ${relFixture}`);
|
|
46
|
+
else if (fixturePlan.action === "update") console.log(` UPDATE ${relFixture} \u2014 add captureFailure + setupPageListeners`);
|
|
47
|
+
else console.log(` SKIP ${relFixture} (already configured)`);
|
|
48
|
+
if (testPlan.toUpdate.length > 0) {
|
|
49
|
+
console.log(`
|
|
50
|
+
Test imports to update (${testPlan.toUpdate.length}):`);
|
|
51
|
+
for (const f of testPlan.toUpdate) console.log(` UPDATE ${import_path.default.relative(projectRoot, f)}`);
|
|
52
|
+
}
|
|
53
|
+
if (testPlan.toSkip.length > 0) {
|
|
54
|
+
console.log(`
|
|
55
|
+
Test imports already up to date: ${testPlan.toSkip.length} file(s)`);
|
|
56
|
+
}
|
|
57
|
+
console.log("");
|
|
58
|
+
}
|
|
59
|
+
async function runInit() {
|
|
60
|
+
const cwd = process.cwd();
|
|
61
|
+
console.log(`[amikoo] Scanning project at ${cwd}...`);
|
|
62
|
+
const { fixtureFile, testFiles, projectRoot } = await (0, import_scanner.scanProject)(cwd);
|
|
63
|
+
let language = "ts";
|
|
64
|
+
let moduleSystem = "esm";
|
|
65
|
+
const referenceFile = fixtureFile ?? testFiles[0] ?? null;
|
|
66
|
+
if (referenceFile) {
|
|
67
|
+
const info = (0, import_scanner.toFileInfo)(referenceFile, projectRoot);
|
|
68
|
+
language = info.language;
|
|
69
|
+
moduleSystem = info.moduleSystem;
|
|
70
|
+
}
|
|
71
|
+
console.log(`[amikoo] Project root: ${projectRoot}`);
|
|
72
|
+
console.log(`[amikoo] Fixture file: ${fixtureFile ?? "(none found \u2014 will create)"}`);
|
|
73
|
+
console.log(`[amikoo] Test files found: ${testFiles.length}`);
|
|
74
|
+
const fixturePlan = (0, import_fixture_creator.planFixture)(fixtureFile, projectRoot, language, moduleSystem);
|
|
75
|
+
const testPlan = testFiles.length > 0 ? (0, import_test_updater.planTestImports)(testFiles, fixturePlan.path, projectRoot) : { toUpdate: [], toSkip: [] };
|
|
76
|
+
const agentPlan = (0, import_agent_setup.planAgentFile)(projectRoot);
|
|
77
|
+
const mcpPlan = (0, import_mcp_setup.planMcpConfig)(projectRoot);
|
|
78
|
+
if (fixturePlan.action === "skip" && testPlan.toUpdate.length === 0 && agentPlan.action === "skip" && mcpPlan.action === "skip") {
|
|
79
|
+
console.log("\n[amikoo] Everything is already configured. Nothing to do.");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
displayPreview(fixturePlan, testPlan, projectRoot);
|
|
83
|
+
const relAgent = import_path.default.relative(projectRoot, agentPlan.path);
|
|
84
|
+
if (agentPlan.action === "create") console.log(` CREATE ${relAgent}`);
|
|
85
|
+
else console.log(` SKIP ${relAgent} (already exists)`);
|
|
86
|
+
const relMcp = import_path.default.relative(projectRoot, mcpPlan.path);
|
|
87
|
+
if (mcpPlan.action === "create") console.log(` CREATE ${relMcp}`);
|
|
88
|
+
else if (mcpPlan.action === "update") console.log(` UPDATE ${relMcp} \u2014 add amikoo-mcp server`);
|
|
89
|
+
else console.log(` SKIP ${relMcp} (already configured)`);
|
|
90
|
+
if (mcpPlan.showWarning) {
|
|
91
|
+
console.log("\n Note: Creating project-level .vscode/mcp.json will override user-level MCP settings.");
|
|
92
|
+
}
|
|
93
|
+
console.log("");
|
|
94
|
+
const confirmed = await askConfirmation("[amikoo] Proceed with these changes? (y/N) ");
|
|
95
|
+
if (!confirmed) {
|
|
96
|
+
console.log("[amikoo] Aborted.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (fixturePlan.action !== "skip") {
|
|
100
|
+
const fixtureResult = (0, import_fixture_creator.createOrUpdateFixture)(fixtureFile, projectRoot, language, moduleSystem);
|
|
101
|
+
console.log(`[amikoo] Fixture ${fixtureResult.action}: ${fixtureResult.path}`);
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`[amikoo] Fixture already configured: ${fixturePlan.path}`);
|
|
104
|
+
}
|
|
105
|
+
const agentResult = (0, import_agent_setup.createAgentFile)(projectRoot);
|
|
106
|
+
if (agentResult.action === "created") {
|
|
107
|
+
console.log(`[amikoo] Agent config created: ${import_path.default.relative(projectRoot, agentResult.path)}`);
|
|
108
|
+
}
|
|
109
|
+
const mcpResult = (0, import_mcp_setup.createOrUpdateMcpConfig)(projectRoot, mcpPlan);
|
|
110
|
+
if (mcpResult.action === "created") {
|
|
111
|
+
console.log(`[amikoo] MCP config created: ${import_path.default.relative(projectRoot, mcpResult.path)}`);
|
|
112
|
+
} else if (mcpResult.action === "updated") {
|
|
113
|
+
console.log(`[amikoo] MCP config updated: ${import_path.default.relative(projectRoot, mcpResult.path)}`);
|
|
114
|
+
}
|
|
115
|
+
if (testPlan.toUpdate.length > 0) {
|
|
116
|
+
const { updated } = (0, import_test_updater.updateTestImports)(testPlan.toUpdate, fixturePlan.path, projectRoot);
|
|
117
|
+
if (updated.length > 0) {
|
|
118
|
+
console.log(`[amikoo] Updated imports in ${updated.length} file(s):`);
|
|
119
|
+
for (const f of updated) console.log(` - ${f}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
console.log("\n[amikoo] Done! Re-run your tests \u2014 failures will now capture DOM state automatically.");
|
|
123
|
+
}
|
|
124
|
+
if (command === "init") {
|
|
125
|
+
runInit().catch((err) => {
|
|
126
|
+
console.error("[amikoo] Error:", err);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
console.error(`[amikoo] Unknown command: ${command ?? "(none)"}`);
|
|
131
|
+
console.error("Usage: npx @muuktest/amikoo-playwright init");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport readline from 'readline';\nimport path from 'path';\nimport { scanProject, toFileInfo } from './scanner.js';\nimport { createOrUpdateFixture, planFixture } from './fixture-creator.js';\nimport { updateTestImports, planTestImports } from './test-updater.js';\nimport { planAgentFile, createAgentFile } from './agent-setup.js';\nimport { planMcpConfig, createOrUpdateMcpConfig } from './mcp-setup.js';\n\nconst [,, command] = process.argv;\n\nfunction askConfirmation(message: string): Promise<boolean> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise(resolve => {\n rl.question(message, answer => {\n rl.close();\n resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');\n });\n });\n}\n\nfunction displayPreview(\n fixturePlan: { action: 'create' | 'update' | 'skip'; path: string },\n testPlan: { toUpdate: string[]; toSkip: string[] },\n projectRoot: string\n): void {\n console.log('\\n[amikoo] Planned changes:\\n');\n const relFixture = path.relative(projectRoot, fixturePlan.path);\n if (fixturePlan.action === 'create') console.log(` CREATE ${relFixture}`);\n else if (fixturePlan.action === 'update') console.log(` UPDATE ${relFixture} — add captureFailure + setupPageListeners`);\n else console.log(` SKIP ${relFixture} (already configured)`);\n\n if (testPlan.toUpdate.length > 0) {\n console.log(`\\n Test imports to update (${testPlan.toUpdate.length}):`);\n for (const f of testPlan.toUpdate) console.log(` UPDATE ${path.relative(projectRoot, f)}`);\n }\n if (testPlan.toSkip.length > 0) {\n console.log(`\\n Test imports already up to date: ${testPlan.toSkip.length} file(s)`);\n }\n console.log('');\n}\n\nasync function runInit() {\n const cwd = process.cwd();\n console.log(`[amikoo] Scanning project at ${cwd}...`);\n\n // PHASE 1: SCAN\n const { fixtureFile, testFiles, projectRoot } = await scanProject(cwd);\n\n let language: 'ts' | 'js' = 'ts';\n let moduleSystem: 'esm' | 'cjs' = 'esm';\n const referenceFile = fixtureFile ?? testFiles[0] ?? null;\n if (referenceFile) {\n const info = toFileInfo(referenceFile, projectRoot);\n language = info.language;\n moduleSystem = info.moduleSystem;\n }\n\n console.log(`[amikoo] Project root: ${projectRoot}`);\n console.log(`[amikoo] Fixture file: ${fixtureFile ?? '(none found — will create)'}`);\n console.log(`[amikoo] Test files found: ${testFiles.length}`);\n\n // PHASE 2: PLAN (read-only)\n const fixturePlan = planFixture(fixtureFile, projectRoot, language, moduleSystem);\n const testPlan = testFiles.length > 0\n ? planTestImports(testFiles, fixturePlan.path, projectRoot)\n : { toUpdate: [], toSkip: [] };\n const agentPlan = planAgentFile(projectRoot);\n const mcpPlan = planMcpConfig(projectRoot);\n\n // PHASE 3: EARLY EXIT IF NOTHING TO DO\n if (fixturePlan.action === 'skip' && testPlan.toUpdate.length === 0 && agentPlan.action === 'skip' && mcpPlan.action === 'skip') {\n console.log('\\n[amikoo] Everything is already configured. Nothing to do.');\n return;\n }\n\n // PHASE 4: PREVIEW\n displayPreview(fixturePlan, testPlan, projectRoot);\n const relAgent = path.relative(projectRoot, agentPlan.path);\n if (agentPlan.action === 'create') console.log(` CREATE ${relAgent}`);\n else console.log(` SKIP ${relAgent} (already exists)`);\n\n const relMcp = path.relative(projectRoot, mcpPlan.path);\n if (mcpPlan.action === 'create') console.log(` CREATE ${relMcp}`);\n else if (mcpPlan.action === 'update') console.log(` UPDATE ${relMcp} — add amikoo-mcp server`);\n else console.log(` SKIP ${relMcp} (already configured)`);\n\n if (mcpPlan.showWarning) {\n console.log('\\n Note: Creating project-level .vscode/mcp.json will override user-level MCP settings.');\n }\n console.log('');\n\n // PHASE 5: CONFIRM\n const confirmed = await askConfirmation('[amikoo] Proceed with these changes? (y/N) ');\n if (!confirmed) { console.log('[amikoo] Aborted.'); return; }\n\n // PHASE 6: EXECUTE\n if (fixturePlan.action !== 'skip') {\n const fixtureResult = createOrUpdateFixture(fixtureFile, projectRoot, language, moduleSystem);\n console.log(`[amikoo] Fixture ${fixtureResult.action}: ${fixtureResult.path}`);\n } else {\n console.log(`[amikoo] Fixture already configured: ${fixturePlan.path}`);\n }\n\n // PHASE 6 (continued): Create agent config file\n const agentResult = createAgentFile(projectRoot);\n if (agentResult.action === 'created') {\n console.log(`[amikoo] Agent config created: ${path.relative(projectRoot, agentResult.path)}`);\n }\n\n // PHASE 6 (continued): Create/update MCP config\n const mcpResult = createOrUpdateMcpConfig(projectRoot, mcpPlan);\n if (mcpResult.action === 'created') {\n console.log(`[amikoo] MCP config created: ${path.relative(projectRoot, mcpResult.path)}`);\n } else if (mcpResult.action === 'updated') {\n console.log(`[amikoo] MCP config updated: ${path.relative(projectRoot, mcpResult.path)}`);\n }\n\n // BUG FIX: Always update test imports (not gated on fixture action)\n if (testPlan.toUpdate.length > 0) {\n const { updated } = updateTestImports(testPlan.toUpdate, fixturePlan.path, projectRoot);\n if (updated.length > 0) {\n console.log(`[amikoo] Updated imports in ${updated.length} file(s):`);\n for (const f of updated) console.log(` - ${f}`);\n }\n }\n\n console.log('\\n[amikoo] Done! Re-run your tests — failures will now capture DOM state automatically.');\n}\n\nif (command === 'init') {\n runInit().catch(err => { console.error('[amikoo] Error:', err); process.exit(1); });\n} else {\n console.error(`[amikoo] Unknown command: ${command ?? '(none)'}`);\n console.error('Usage: npx @muuktest/amikoo-playwright init');\n process.exit(1);\n}\n"],"mappings":"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","names":["readline","path"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import readline from "readline";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { scanProject, toFileInfo } from "./scanner.js";
|
|
5
|
+
import { createOrUpdateFixture, planFixture } from "./fixture-creator.js";
|
|
6
|
+
import { updateTestImports, planTestImports } from "./test-updater.js";
|
|
7
|
+
import { planAgentFile, createAgentFile } from "./agent-setup.js";
|
|
8
|
+
import { planMcpConfig, createOrUpdateMcpConfig } from "./mcp-setup.js";
|
|
9
|
+
const [, , command] = process.argv;
|
|
10
|
+
function askConfirmation(message) {
|
|
11
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
rl.question(message, (answer) => {
|
|
14
|
+
rl.close();
|
|
15
|
+
resolve(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function displayPreview(fixturePlan, testPlan, projectRoot) {
|
|
20
|
+
console.log("\n[amikoo] Planned changes:\n");
|
|
21
|
+
const relFixture = path.relative(projectRoot, fixturePlan.path);
|
|
22
|
+
if (fixturePlan.action === "create") console.log(` CREATE ${relFixture}`);
|
|
23
|
+
else if (fixturePlan.action === "update") console.log(` UPDATE ${relFixture} \u2014 add captureFailure + setupPageListeners`);
|
|
24
|
+
else console.log(` SKIP ${relFixture} (already configured)`);
|
|
25
|
+
if (testPlan.toUpdate.length > 0) {
|
|
26
|
+
console.log(`
|
|
27
|
+
Test imports to update (${testPlan.toUpdate.length}):`);
|
|
28
|
+
for (const f of testPlan.toUpdate) console.log(` UPDATE ${path.relative(projectRoot, f)}`);
|
|
29
|
+
}
|
|
30
|
+
if (testPlan.toSkip.length > 0) {
|
|
31
|
+
console.log(`
|
|
32
|
+
Test imports already up to date: ${testPlan.toSkip.length} file(s)`);
|
|
33
|
+
}
|
|
34
|
+
console.log("");
|
|
35
|
+
}
|
|
36
|
+
async function runInit() {
|
|
37
|
+
const cwd = process.cwd();
|
|
38
|
+
console.log(`[amikoo] Scanning project at ${cwd}...`);
|
|
39
|
+
const { fixtureFile, testFiles, projectRoot } = await scanProject(cwd);
|
|
40
|
+
let language = "ts";
|
|
41
|
+
let moduleSystem = "esm";
|
|
42
|
+
const referenceFile = fixtureFile ?? testFiles[0] ?? null;
|
|
43
|
+
if (referenceFile) {
|
|
44
|
+
const info = toFileInfo(referenceFile, projectRoot);
|
|
45
|
+
language = info.language;
|
|
46
|
+
moduleSystem = info.moduleSystem;
|
|
47
|
+
}
|
|
48
|
+
console.log(`[amikoo] Project root: ${projectRoot}`);
|
|
49
|
+
console.log(`[amikoo] Fixture file: ${fixtureFile ?? "(none found \u2014 will create)"}`);
|
|
50
|
+
console.log(`[amikoo] Test files found: ${testFiles.length}`);
|
|
51
|
+
const fixturePlan = planFixture(fixtureFile, projectRoot, language, moduleSystem);
|
|
52
|
+
const testPlan = testFiles.length > 0 ? planTestImports(testFiles, fixturePlan.path, projectRoot) : { toUpdate: [], toSkip: [] };
|
|
53
|
+
const agentPlan = planAgentFile(projectRoot);
|
|
54
|
+
const mcpPlan = planMcpConfig(projectRoot);
|
|
55
|
+
if (fixturePlan.action === "skip" && testPlan.toUpdate.length === 0 && agentPlan.action === "skip" && mcpPlan.action === "skip") {
|
|
56
|
+
console.log("\n[amikoo] Everything is already configured. Nothing to do.");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
displayPreview(fixturePlan, testPlan, projectRoot);
|
|
60
|
+
const relAgent = path.relative(projectRoot, agentPlan.path);
|
|
61
|
+
if (agentPlan.action === "create") console.log(` CREATE ${relAgent}`);
|
|
62
|
+
else console.log(` SKIP ${relAgent} (already exists)`);
|
|
63
|
+
const relMcp = path.relative(projectRoot, mcpPlan.path);
|
|
64
|
+
if (mcpPlan.action === "create") console.log(` CREATE ${relMcp}`);
|
|
65
|
+
else if (mcpPlan.action === "update") console.log(` UPDATE ${relMcp} \u2014 add amikoo-mcp server`);
|
|
66
|
+
else console.log(` SKIP ${relMcp} (already configured)`);
|
|
67
|
+
if (mcpPlan.showWarning) {
|
|
68
|
+
console.log("\n Note: Creating project-level .vscode/mcp.json will override user-level MCP settings.");
|
|
69
|
+
}
|
|
70
|
+
console.log("");
|
|
71
|
+
const confirmed = await askConfirmation("[amikoo] Proceed with these changes? (y/N) ");
|
|
72
|
+
if (!confirmed) {
|
|
73
|
+
console.log("[amikoo] Aborted.");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (fixturePlan.action !== "skip") {
|
|
77
|
+
const fixtureResult = createOrUpdateFixture(fixtureFile, projectRoot, language, moduleSystem);
|
|
78
|
+
console.log(`[amikoo] Fixture ${fixtureResult.action}: ${fixtureResult.path}`);
|
|
79
|
+
} else {
|
|
80
|
+
console.log(`[amikoo] Fixture already configured: ${fixturePlan.path}`);
|
|
81
|
+
}
|
|
82
|
+
const agentResult = createAgentFile(projectRoot);
|
|
83
|
+
if (agentResult.action === "created") {
|
|
84
|
+
console.log(`[amikoo] Agent config created: ${path.relative(projectRoot, agentResult.path)}`);
|
|
85
|
+
}
|
|
86
|
+
const mcpResult = createOrUpdateMcpConfig(projectRoot, mcpPlan);
|
|
87
|
+
if (mcpResult.action === "created") {
|
|
88
|
+
console.log(`[amikoo] MCP config created: ${path.relative(projectRoot, mcpResult.path)}`);
|
|
89
|
+
} else if (mcpResult.action === "updated") {
|
|
90
|
+
console.log(`[amikoo] MCP config updated: ${path.relative(projectRoot, mcpResult.path)}`);
|
|
91
|
+
}
|
|
92
|
+
if (testPlan.toUpdate.length > 0) {
|
|
93
|
+
const { updated } = updateTestImports(testPlan.toUpdate, fixturePlan.path, projectRoot);
|
|
94
|
+
if (updated.length > 0) {
|
|
95
|
+
console.log(`[amikoo] Updated imports in ${updated.length} file(s):`);
|
|
96
|
+
for (const f of updated) console.log(` - ${f}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.log("\n[amikoo] Done! Re-run your tests \u2014 failures will now capture DOM state automatically.");
|
|
100
|
+
}
|
|
101
|
+
if (command === "init") {
|
|
102
|
+
runInit().catch((err) => {
|
|
103
|
+
console.error("[amikoo] Error:", err);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
console.error(`[amikoo] Unknown command: ${command ?? "(none)"}`);
|
|
108
|
+
console.error("Usage: npx @muuktest/amikoo-playwright init");
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport readline from 'readline';\nimport path from 'path';\nimport { scanProject, toFileInfo } from './scanner.js';\nimport { createOrUpdateFixture, planFixture } from './fixture-creator.js';\nimport { updateTestImports, planTestImports } from './test-updater.js';\nimport { planAgentFile, createAgentFile } from './agent-setup.js';\nimport { planMcpConfig, createOrUpdateMcpConfig } from './mcp-setup.js';\n\nconst [,, command] = process.argv;\n\nfunction askConfirmation(message: string): Promise<boolean> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise(resolve => {\n rl.question(message, answer => {\n rl.close();\n resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');\n });\n });\n}\n\nfunction displayPreview(\n fixturePlan: { action: 'create' | 'update' | 'skip'; path: string },\n testPlan: { toUpdate: string[]; toSkip: string[] },\n projectRoot: string\n): void {\n console.log('\\n[amikoo] Planned changes:\\n');\n const relFixture = path.relative(projectRoot, fixturePlan.path);\n if (fixturePlan.action === 'create') console.log(` CREATE ${relFixture}`);\n else if (fixturePlan.action === 'update') console.log(` UPDATE ${relFixture} — add captureFailure + setupPageListeners`);\n else console.log(` SKIP ${relFixture} (already configured)`);\n\n if (testPlan.toUpdate.length > 0) {\n console.log(`\\n Test imports to update (${testPlan.toUpdate.length}):`);\n for (const f of testPlan.toUpdate) console.log(` UPDATE ${path.relative(projectRoot, f)}`);\n }\n if (testPlan.toSkip.length > 0) {\n console.log(`\\n Test imports already up to date: ${testPlan.toSkip.length} file(s)`);\n }\n console.log('');\n}\n\nasync function runInit() {\n const cwd = process.cwd();\n console.log(`[amikoo] Scanning project at ${cwd}...`);\n\n // PHASE 1: SCAN\n const { fixtureFile, testFiles, projectRoot } = await scanProject(cwd);\n\n let language: 'ts' | 'js' = 'ts';\n let moduleSystem: 'esm' | 'cjs' = 'esm';\n const referenceFile = fixtureFile ?? testFiles[0] ?? null;\n if (referenceFile) {\n const info = toFileInfo(referenceFile, projectRoot);\n language = info.language;\n moduleSystem = info.moduleSystem;\n }\n\n console.log(`[amikoo] Project root: ${projectRoot}`);\n console.log(`[amikoo] Fixture file: ${fixtureFile ?? '(none found — will create)'}`);\n console.log(`[amikoo] Test files found: ${testFiles.length}`);\n\n // PHASE 2: PLAN (read-only)\n const fixturePlan = planFixture(fixtureFile, projectRoot, language, moduleSystem);\n const testPlan = testFiles.length > 0\n ? planTestImports(testFiles, fixturePlan.path, projectRoot)\n : { toUpdate: [], toSkip: [] };\n const agentPlan = planAgentFile(projectRoot);\n const mcpPlan = planMcpConfig(projectRoot);\n\n // PHASE 3: EARLY EXIT IF NOTHING TO DO\n if (fixturePlan.action === 'skip' && testPlan.toUpdate.length === 0 && agentPlan.action === 'skip' && mcpPlan.action === 'skip') {\n console.log('\\n[amikoo] Everything is already configured. Nothing to do.');\n return;\n }\n\n // PHASE 4: PREVIEW\n displayPreview(fixturePlan, testPlan, projectRoot);\n const relAgent = path.relative(projectRoot, agentPlan.path);\n if (agentPlan.action === 'create') console.log(` CREATE ${relAgent}`);\n else console.log(` SKIP ${relAgent} (already exists)`);\n\n const relMcp = path.relative(projectRoot, mcpPlan.path);\n if (mcpPlan.action === 'create') console.log(` CREATE ${relMcp}`);\n else if (mcpPlan.action === 'update') console.log(` UPDATE ${relMcp} — add amikoo-mcp server`);\n else console.log(` SKIP ${relMcp} (already configured)`);\n\n if (mcpPlan.showWarning) {\n console.log('\\n Note: Creating project-level .vscode/mcp.json will override user-level MCP settings.');\n }\n console.log('');\n\n // PHASE 5: CONFIRM\n const confirmed = await askConfirmation('[amikoo] Proceed with these changes? (y/N) ');\n if (!confirmed) { console.log('[amikoo] Aborted.'); return; }\n\n // PHASE 6: EXECUTE\n if (fixturePlan.action !== 'skip') {\n const fixtureResult = createOrUpdateFixture(fixtureFile, projectRoot, language, moduleSystem);\n console.log(`[amikoo] Fixture ${fixtureResult.action}: ${fixtureResult.path}`);\n } else {\n console.log(`[amikoo] Fixture already configured: ${fixturePlan.path}`);\n }\n\n // PHASE 6 (continued): Create agent config file\n const agentResult = createAgentFile(projectRoot);\n if (agentResult.action === 'created') {\n console.log(`[amikoo] Agent config created: ${path.relative(projectRoot, agentResult.path)}`);\n }\n\n // PHASE 6 (continued): Create/update MCP config\n const mcpResult = createOrUpdateMcpConfig(projectRoot, mcpPlan);\n if (mcpResult.action === 'created') {\n console.log(`[amikoo] MCP config created: ${path.relative(projectRoot, mcpResult.path)}`);\n } else if (mcpResult.action === 'updated') {\n console.log(`[amikoo] MCP config updated: ${path.relative(projectRoot, mcpResult.path)}`);\n }\n\n // BUG FIX: Always update test imports (not gated on fixture action)\n if (testPlan.toUpdate.length > 0) {\n const { updated } = updateTestImports(testPlan.toUpdate, fixturePlan.path, projectRoot);\n if (updated.length > 0) {\n console.log(`[amikoo] Updated imports in ${updated.length} file(s):`);\n for (const f of updated) console.log(` - ${f}`);\n }\n }\n\n console.log('\\n[amikoo] Done! Re-run your tests — failures will now capture DOM state automatically.');\n}\n\nif (command === 'init') {\n runInit().catch(err => { console.error('[amikoo] Error:', err); process.exit(1); });\n} else {\n console.error(`[amikoo] Unknown command: ${command ?? '(none)'}`);\n console.error('Usage: npx @muuktest/amikoo-playwright init');\n process.exit(1);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,116 @@
|
|
|
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 mcp_setup_exports = {};
|
|
30
|
+
__export(mcp_setup_exports, {
|
|
31
|
+
createOrUpdateMcpConfig: () => createOrUpdateMcpConfig,
|
|
32
|
+
planMcpConfig: () => planMcpConfig
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(mcp_setup_exports);
|
|
35
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
36
|
+
var import_path = __toESM(require("path"), 1);
|
|
37
|
+
const MCP_FILE_REL = import_path.default.join(".vscode", "mcp.json");
|
|
38
|
+
const AMIKOO_SERVER = {
|
|
39
|
+
command: "npx",
|
|
40
|
+
args: ["-y", "@muuktest/amikoo-mcp@latest"],
|
|
41
|
+
env: { MUUK_KEY: "${input:muuk_key}" }
|
|
42
|
+
};
|
|
43
|
+
const MUUK_KEY_INPUT = {
|
|
44
|
+
id: "muuk_key",
|
|
45
|
+
type: "promptString",
|
|
46
|
+
description: "MuukTest Key (available to download from MuukTest account)",
|
|
47
|
+
password: true
|
|
48
|
+
};
|
|
49
|
+
function checkExistingMcpConfig(projectDir) {
|
|
50
|
+
const filePath = import_path.default.join(projectDir, MCP_FILE_REL);
|
|
51
|
+
if (!import_fs.default.existsSync(filePath)) {
|
|
52
|
+
return { exists: false, hasServer: false, hasInput: false, config: null };
|
|
53
|
+
}
|
|
54
|
+
let config;
|
|
55
|
+
try {
|
|
56
|
+
config = JSON.parse(import_fs.default.readFileSync(filePath, "utf8"));
|
|
57
|
+
} catch {
|
|
58
|
+
console.warn(`[amikoo] Warning: ${MCP_FILE_REL} contains invalid JSON \u2014 skipping MCP setup.`);
|
|
59
|
+
return { exists: true, hasServer: false, hasInput: false, config: null };
|
|
60
|
+
}
|
|
61
|
+
const servers = config.servers;
|
|
62
|
+
const hasServer = !!servers?.["amikoo-mcp"];
|
|
63
|
+
const inputs = config.inputs;
|
|
64
|
+
const hasInput = Array.isArray(inputs) && inputs.some((i) => i.id === "muuk_key");
|
|
65
|
+
return { exists: true, hasServer, hasInput, config };
|
|
66
|
+
}
|
|
67
|
+
function planMcpConfig(projectDir) {
|
|
68
|
+
const filePath = import_path.default.join(projectDir, MCP_FILE_REL);
|
|
69
|
+
const existing = checkExistingMcpConfig(projectDir);
|
|
70
|
+
if (existing.hasServer || existing.hasInput) {
|
|
71
|
+
return { action: "skip", path: filePath, showWarning: false };
|
|
72
|
+
}
|
|
73
|
+
if (existing.exists && existing.config === null) {
|
|
74
|
+
return { action: "skip", path: filePath, showWarning: false };
|
|
75
|
+
}
|
|
76
|
+
if (!existing.exists) {
|
|
77
|
+
return { action: "create", path: filePath, showWarning: true };
|
|
78
|
+
}
|
|
79
|
+
return { action: "update", path: filePath, showWarning: false };
|
|
80
|
+
}
|
|
81
|
+
function createOrUpdateMcpConfig(projectDir, plan) {
|
|
82
|
+
const filePath = import_path.default.join(projectDir, MCP_FILE_REL);
|
|
83
|
+
if (plan.action === "skip") {
|
|
84
|
+
return { path: filePath, action: "skipped" };
|
|
85
|
+
}
|
|
86
|
+
if (plan.action === "create") {
|
|
87
|
+
import_fs.default.mkdirSync(import_path.default.dirname(filePath), { recursive: true });
|
|
88
|
+
const config2 = {
|
|
89
|
+
servers: { "amikoo-mcp": AMIKOO_SERVER },
|
|
90
|
+
inputs: [MUUK_KEY_INPUT]
|
|
91
|
+
};
|
|
92
|
+
import_fs.default.writeFileSync(filePath, JSON.stringify(config2, null, 2) + "\n", "utf8");
|
|
93
|
+
return { path: filePath, action: "created" };
|
|
94
|
+
}
|
|
95
|
+
const raw = import_fs.default.readFileSync(filePath, "utf8");
|
|
96
|
+
const config = JSON.parse(raw);
|
|
97
|
+
if (!config.servers || typeof config.servers !== "object") {
|
|
98
|
+
config.servers = {};
|
|
99
|
+
}
|
|
100
|
+
config.servers["amikoo-mcp"] = AMIKOO_SERVER;
|
|
101
|
+
if (!Array.isArray(config.inputs)) {
|
|
102
|
+
config.inputs = [];
|
|
103
|
+
}
|
|
104
|
+
const inputs = config.inputs;
|
|
105
|
+
if (!inputs.some((i) => i.id === "muuk_key")) {
|
|
106
|
+
inputs.push(MUUK_KEY_INPUT);
|
|
107
|
+
}
|
|
108
|
+
import_fs.default.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
109
|
+
return { path: filePath, action: "updated" };
|
|
110
|
+
}
|
|
111
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
112
|
+
0 && (module.exports = {
|
|
113
|
+
createOrUpdateMcpConfig,
|
|
114
|
+
planMcpConfig
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=mcp-setup.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/mcp-setup.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nconst MCP_FILE_REL = path.join('.vscode', 'mcp.json');\n\nconst AMIKOO_SERVER = {\n command: 'npx',\n args: ['-y', '@muuktest/amikoo-mcp@latest'],\n env: { MUUK_KEY: '${input:muuk_key}' },\n};\n\nconst MUUK_KEY_INPUT = {\n id: 'muuk_key',\n type: 'promptString',\n description: 'MuukTest Key (available to download from MuukTest account)',\n password: true,\n};\n\nexport interface McpConfigPlan {\n action: 'create' | 'update' | 'skip';\n path: string;\n showWarning: boolean;\n}\n\ninterface ExistingConfig {\n exists: boolean;\n hasServer: boolean;\n hasInput: boolean;\n config: Record<string, unknown> | null;\n}\n\nfunction checkExistingMcpConfig(projectDir: string): ExistingConfig {\n const filePath = path.join(projectDir, MCP_FILE_REL);\n if (!fs.existsSync(filePath)) {\n return { exists: false, hasServer: false, hasInput: false, config: null };\n }\n\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n } catch {\n console.warn(`[amikoo] Warning: ${MCP_FILE_REL} contains invalid JSON — skipping MCP setup.`);\n return { exists: true, hasServer: false, hasInput: false, config: null };\n }\n\n const servers = config.servers as Record<string, unknown> | undefined;\n const hasServer = !!servers?.['amikoo-mcp'];\n\n const inputs = config.inputs as Array<Record<string, unknown>> | undefined;\n const hasInput = Array.isArray(inputs) && inputs.some(i => i.id === 'muuk_key');\n\n return { exists: true, hasServer, hasInput, config };\n}\n\nexport function planMcpConfig(projectDir: string): McpConfigPlan {\n const filePath = path.join(projectDir, MCP_FILE_REL);\n const existing = checkExistingMcpConfig(projectDir);\n\n if (existing.hasServer || existing.hasInput) {\n return { action: 'skip', path: filePath, showWarning: false };\n }\n\n if (existing.exists && existing.config === null) {\n // File exists but invalid JSON — skip to avoid corruption\n return { action: 'skip', path: filePath, showWarning: false };\n }\n\n if (!existing.exists) {\n return { action: 'create', path: filePath, showWarning: true };\n }\n\n return { action: 'update', path: filePath, showWarning: false };\n}\n\nexport function createOrUpdateMcpConfig(\n projectDir: string,\n plan: McpConfigPlan,\n): { path: string; action: 'created' | 'updated' | 'skipped' } {\n const filePath = path.join(projectDir, MCP_FILE_REL);\n\n if (plan.action === 'skip') {\n return { path: filePath, action: 'skipped' };\n }\n\n if (plan.action === 'create') {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const config = {\n servers: { 'amikoo-mcp': AMIKOO_SERVER },\n inputs: [MUUK_KEY_INPUT],\n };\n fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + '\\n', 'utf8');\n return { path: filePath, action: 'created' };\n }\n\n // update — merge into existing config\n const raw = fs.readFileSync(filePath, 'utf8');\n const config = JSON.parse(raw) as Record<string, unknown>;\n\n if (!config.servers || typeof config.servers !== 'object') {\n config.servers = {};\n }\n (config.servers as Record<string, unknown>)['amikoo-mcp'] = AMIKOO_SERVER;\n\n if (!Array.isArray(config.inputs)) {\n config.inputs = [];\n }\n const inputs = config.inputs as Array<Record<string, unknown>>;\n if (!inputs.some(i => i.id === 'muuk_key')) {\n inputs.push(MUUK_KEY_INPUT);\n }\n\n fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + '\\n', 'utf8');\n return { path: filePath, action: 'updated' };\n}\n"],"mappings":"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","names":["path","fs","config"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface McpConfigPlan {
|
|
2
|
+
action: 'create' | 'update' | 'skip';
|
|
3
|
+
path: string;
|
|
4
|
+
showWarning: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function planMcpConfig(projectDir: string): McpConfigPlan;
|
|
7
|
+
declare function createOrUpdateMcpConfig(projectDir: string, plan: McpConfigPlan): {
|
|
8
|
+
path: string;
|
|
9
|
+
action: 'created' | 'updated' | 'skipped';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { type McpConfigPlan, createOrUpdateMcpConfig, planMcpConfig };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface McpConfigPlan {
|
|
2
|
+
action: 'create' | 'update' | 'skip';
|
|
3
|
+
path: string;
|
|
4
|
+
showWarning: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function planMcpConfig(projectDir: string): McpConfigPlan;
|
|
7
|
+
declare function createOrUpdateMcpConfig(projectDir: string, plan: McpConfigPlan): {
|
|
8
|
+
path: string;
|
|
9
|
+
action: 'created' | 'updated' | 'skipped';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { type McpConfigPlan, createOrUpdateMcpConfig, planMcpConfig };
|