@kkeve/suite 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/keve.js +2 -1
- package/dist/commands/clean.js +2 -2
- package/dist/commands/config.js +7 -7
- package/dist/commands/init.js +2 -2
- package/dist/commands/report.js +7 -7
- package/dist/commands/run.js +12 -11
- package/dist/commands/test.js +7 -7
- package/dist/config.js +7 -7
- package/dist/core/action-log-writer.js +4 -1
- package/dist/core/decorator/keve-decorators.d.ts +128 -0
- package/dist/core/decorator/keve-decorators.js +254 -0
- package/dist/core/decorator/keve-host.d.ts +40 -0
- package/dist/core/decorator/keve-host.js +33 -0
- package/dist/core/{keve-registry.d.ts → decorator/keve-registry.d.ts} +19 -0
- package/dist/core/{keve-registry.js → decorator/keve-registry.js} +13 -1
- package/dist/core/generateReport/report.d.ts +1 -1
- package/dist/core/generateReport/report.js +2 -2
- package/dist/core/generateReport/reportData.d.ts +3 -0
- package/dist/core/generateReport/reportData.js +49 -15
- package/dist/core/judge/confidence.d.ts +119 -0
- package/dist/core/judge/confidence.js +295 -0
- package/dist/core/judge/goal-heuristics.d.ts +32 -0
- package/dist/core/judge/goal-heuristics.js +128 -0
- package/dist/core/judge/pngCodec.d.ts +23 -0
- package/dist/core/judge/pngCodec.js +215 -0
- package/dist/core/judge/screenshotDiff.d.ts +112 -0
- package/dist/core/judge/screenshotDiff.js +420 -0
- package/dist/core/page-object/definePage.d.ts +95 -0
- package/dist/core/page-object/definePage.js +62 -0
- package/dist/core/page-object/elementResolver.d.ts +32 -0
- package/dist/core/page-object/elementResolver.js +236 -0
- package/dist/core/page-object/engine-types.d.ts +140 -0
- package/dist/core/page-object/engine-types.js +34 -0
- package/dist/core/sso-cookie.d.ts +18 -0
- package/dist/core/sso-cookie.js +10 -0
- package/dist/dsl/index.d.ts +15 -0
- package/dist/dsl/index.js +29 -0
- package/dist/dsl/locatorSource.d.ts +25 -0
- package/dist/dsl/locatorSource.js +323 -0
- package/dist/dsl/ops.d.ts +46 -0
- package/dist/dsl/ops.js +95 -0
- package/dist/dsl/toDecorator.d.ts +98 -0
- package/dist/dsl/toDecorator.js +747 -0
- package/dist/engine-cypress/adapter.d.ts +108 -0
- package/dist/engine-cypress/adapter.js +514 -0
- package/dist/engine-cypress/agent-page.d.ts +159 -0
- package/dist/engine-cypress/agent-page.js +566 -0
- package/dist/engine-cypress/agentDom.d.ts +22 -0
- package/dist/engine-cypress/agentDom.js +590 -0
- package/dist/engine-cypress/bridge.d.ts +39 -0
- package/dist/engine-cypress/bridge.js +598 -0
- package/dist/engine-cypress/cdp.d.ts +78 -0
- package/dist/engine-cypress/cdp.js +339 -0
- package/dist/engine-cypress/host.d.ts +22 -0
- package/dist/engine-cypress/host.js +480 -0
- package/dist/engine-cypress/index.d.ts +26 -0
- package/dist/engine-cypress/index.js +58 -0
- package/dist/engine-cypress/keveGoal.d.ts +43 -0
- package/dist/engine-cypress/keveGoal.js +265 -0
- package/dist/engine-cypress/pageOps.d.ts +41 -0
- package/dist/engine-cypress/pageOps.js +439 -0
- package/dist/engine-cypress/reporter.d.ts +20 -0
- package/dist/engine-cypress/reporter.js +128 -0
- package/dist/engine-cypress/setup.d.ts +21 -0
- package/dist/engine-cypress/setup.js +27 -0
- package/dist/engine-cypress/shotBuffer.d.ts +40 -0
- package/dist/engine-cypress/shotBuffer.js +41 -0
- package/dist/engine-cypress/state.d.ts +56 -0
- package/dist/engine-cypress/state.js +81 -0
- package/dist/engine-playwright/engineAdapter.d.ts +78 -0
- package/dist/engine-playwright/engineAdapter.js +371 -0
- package/dist/engine-playwright/global-setup.d.ts +28 -0
- package/dist/engine-playwright/global-setup.js +142 -0
- package/dist/{core → engine-playwright}/keve-report.d.ts +0 -1
- package/dist/engine-playwright/keve-report.js +137 -0
- package/dist/{core → engine-playwright}/keve-test.d.ts +10 -8
- package/dist/{core → engine-playwright}/keve-test.js +204 -200
- package/dist/{core → engine-playwright}/pw-config.js +8 -7
- package/dist/index.d.ts +13 -8
- package/dist/index.js +43 -28
- package/dist/page-agent/agent.d.ts +82 -7
- package/dist/page-agent/agent.js +252 -88
- package/dist/page-agent/diagnostics.d.ts +184 -0
- package/dist/page-agent/diagnostics.js +377 -0
- package/dist/page-agent/hooks.d.ts +3 -2
- package/dist/page-agent/hooks.js +4 -3
- package/dist/page-agent/page-like.d.ts +116 -0
- package/dist/page-agent/page-like.js +14 -0
- package/dist/page-agent/playwright-page.d.ts +20 -0
- package/dist/page-agent/playwright-page.js +98 -0
- package/dist/page-agent/system-prompt.d.ts +7 -0
- package/dist/page-agent/system-prompt.js +10 -0
- package/dist/page-agent/tools.d.ts +1 -1
- package/dist/page-agent/tools.js +144 -8
- package/package.json +24 -11
- package/scripts/gen-system-prompt.mjs +36 -0
- package/scripts/run-dsl-task.mjs +619 -0
- package/scripts/runtime/cypress-preprocessor.mjs +44 -0
- package/scripts/runtime/cypress.config.mjs +35 -0
- package/scripts/runtime/playwright.config.mjs +41 -0
- package/scripts/verify-shot-diff.mjs +243 -0
- package/dist/commands/auth-update.d.ts +0 -16
- package/dist/commands/auth-update.js +0 -88
- package/dist/core/auth.d.ts +0 -17
- package/dist/core/auth.js +0 -224
- package/dist/core/cdp-resolver.d.ts +0 -5
- package/dist/core/cdp-resolver.js +0 -93
- package/dist/core/global-setup.d.ts +0 -6
- package/dist/core/global-setup.js +0 -123
- package/dist/core/keve-decorators.d.ts +0 -51
- package/dist/core/keve-decorators.js +0 -95
- package/dist/core/keve-report.js +0 -376
- /package/dist/core/{keve-aspect.d.ts → decorator/keve-aspect.d.ts} +0 -0
- /package/dist/core/{keve-aspect.js → decorator/keve-aspect.js} +0 -0
- /package/dist/{core → engine-playwright}/pw-config.d.ts +0 -0
package/dist/bin/keve.js
CHANGED
|
@@ -47,10 +47,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
const commander_1 = require("commander");
|
|
49
49
|
const path = __importStar(require("node:path"));
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
50
51
|
// package.json is in the package root, not in dist/
|
|
51
52
|
// __dirname → dist/bin/ → go up 2 levels to package root
|
|
52
53
|
const pkgPath = path.resolve(__dirname, '..', '..', 'package.json');
|
|
53
|
-
const pkg =
|
|
54
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
54
55
|
const program = new commander_1.Command();
|
|
55
56
|
program
|
|
56
57
|
.name('keve')
|
package/dist/commands/clean.js
CHANGED
|
@@ -43,7 +43,7 @@ exports.clean = clean;
|
|
|
43
43
|
const fs = __importStar(require("node:fs"));
|
|
44
44
|
const path = __importStar(require("node:path"));
|
|
45
45
|
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
-
const
|
|
46
|
+
const config_js_1 = require("../config.js");
|
|
47
47
|
function removeDir(dir, dryRun) {
|
|
48
48
|
if (!fs.existsSync(dir))
|
|
49
49
|
return;
|
|
@@ -55,7 +55,7 @@ function removeDir(dir, dryRun) {
|
|
|
55
55
|
console.log(chalk_1.default.green(` ✓ Removed: ${path.relative(process.cwd(), dir)}`));
|
|
56
56
|
}
|
|
57
57
|
async function clean(options) {
|
|
58
|
-
const keveDir = (0,
|
|
58
|
+
const keveDir = (0, config_js_1.findKeveDir)();
|
|
59
59
|
if (!keveDir) {
|
|
60
60
|
console.error(chalk_1.default.red(' ✗ .keve/ directory not found'));
|
|
61
61
|
process.exit(1);
|
package/dist/commands/config.js
CHANGED
|
@@ -43,13 +43,13 @@ exports.config = config;
|
|
|
43
43
|
const fs = __importStar(require("node:fs"));
|
|
44
44
|
const path = __importStar(require("node:path"));
|
|
45
45
|
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
-
const
|
|
47
|
-
const
|
|
46
|
+
const config_js_1 = require("../config.js");
|
|
47
|
+
const pw_config_js_1 = require("../engine-playwright/pw-config.js");
|
|
48
48
|
async function config(options) {
|
|
49
|
-
const keveDir = (0,
|
|
50
|
-
const taskDir = (0,
|
|
49
|
+
const keveDir = (0, config_js_1.findKeveDir)();
|
|
50
|
+
const taskDir = (0, config_js_1.resolveTaskDir)(keveDir);
|
|
51
51
|
if (options.show) {
|
|
52
|
-
const configPath = (0,
|
|
52
|
+
const configPath = (0, pw_config_js_1.findExistingConfig)(keveDir, taskDir);
|
|
53
53
|
if (configPath) {
|
|
54
54
|
console.log(chalk_1.default.green(` Config: ${configPath}`));
|
|
55
55
|
console.log(fs.readFileSync(configPath, 'utf-8'));
|
|
@@ -69,14 +69,14 @@ async function config(options) {
|
|
|
69
69
|
timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
|
|
70
70
|
retries: options.retries ? parseInt(options.retries, 10) : undefined,
|
|
71
71
|
};
|
|
72
|
-
const content = (0,
|
|
72
|
+
const content = (0, pw_config_js_1.generatePwConfig)(pwOptions);
|
|
73
73
|
if (options.print) {
|
|
74
74
|
console.log(content);
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
const outputPath = options.output
|
|
78
78
|
? path.resolve(options.output)
|
|
79
|
-
: (0,
|
|
79
|
+
: (0, pw_config_js_1.resolveConfigOutputPath)();
|
|
80
80
|
console.log(chalk_1.default.cyan('\n⚙️ keve config\n'));
|
|
81
81
|
if (fs.existsSync(outputPath) && !options.force) {
|
|
82
82
|
console.log(chalk_1.default.yellow(` ⚠ ${path.relative(process.cwd(), outputPath)} already exists. Use --force to overwrite.`));
|
package/dist/commands/init.js
CHANGED
|
@@ -50,7 +50,7 @@ const fs = __importStar(require("node:fs"));
|
|
|
50
50
|
const os = __importStar(require("node:os"));
|
|
51
51
|
const path = __importStar(require("node:path"));
|
|
52
52
|
const chalk_1 = __importDefault(require("chalk"));
|
|
53
|
-
const
|
|
53
|
+
const pw_config_js_1 = require("../engine-playwright/pw-config.js");
|
|
54
54
|
// Skills bundled in this package — copied to global ~/.codeflicker/skills/
|
|
55
55
|
const KEVE_SKILLS = [
|
|
56
56
|
'keve-bddcase-gen',
|
|
@@ -254,7 +254,7 @@ async function init(options) {
|
|
|
254
254
|
webServer: !options.cdp,
|
|
255
255
|
};
|
|
256
256
|
if (!fs.existsSync(pwConfigPath) || options.force) {
|
|
257
|
-
fs.writeFileSync(pwConfigPath, (0,
|
|
257
|
+
fs.writeFileSync(pwConfigPath, (0, pw_config_js_1.generatePwConfig)(pwOptions), 'utf-8');
|
|
258
258
|
console.log(chalk_1.default.green(' ✓ Created keve-test.config.ts'));
|
|
259
259
|
console.log(chalk_1.default.gray(` baseURL: ${pwOptions.baseUrl || '(from env)'}`));
|
|
260
260
|
console.log(chalk_1.default.gray(` CDP: ${pwOptions.cdp ? 'enabled' : 'disabled'}`));
|
package/dist/commands/report.js
CHANGED
|
@@ -46,20 +46,20 @@ exports.report = report;
|
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
47
|
const path = __importStar(require("node:path"));
|
|
48
48
|
const chalk_1 = __importDefault(require("chalk"));
|
|
49
|
-
const
|
|
50
|
-
const
|
|
49
|
+
const config_js_1 = require("../config.js");
|
|
50
|
+
const index_js_1 = require("../index.js");
|
|
51
51
|
/**
|
|
52
52
|
* Resolve paths for report inputs
|
|
53
53
|
*/
|
|
54
54
|
function resolvePaths(options) {
|
|
55
|
-
const keveDir = (0,
|
|
55
|
+
const keveDir = (0, config_js_1.findKeveDir)();
|
|
56
56
|
// Determine round
|
|
57
57
|
let round = null;
|
|
58
58
|
if (options.round) {
|
|
59
59
|
round = parseInt(options.round, 10);
|
|
60
60
|
}
|
|
61
61
|
else if (keveDir) {
|
|
62
|
-
const branchDir = (0,
|
|
62
|
+
const branchDir = (0, config_js_1.getBranchDir)(keveDir);
|
|
63
63
|
const artifactsBase = branchDir ? path.join(branchDir, 'test-artifacts') : path.join(keveDir, 'test-artifacts');
|
|
64
64
|
if (fs.existsSync(artifactsBase)) {
|
|
65
65
|
const dirs = fs.readdirSync(artifactsBase)
|
|
@@ -73,7 +73,7 @@ function resolvePaths(options) {
|
|
|
73
73
|
// Resolve test-results.json
|
|
74
74
|
let resultsPath = options.results;
|
|
75
75
|
if (!resultsPath && keveDir && round !== null) {
|
|
76
|
-
const branchDir = (0,
|
|
76
|
+
const branchDir = (0, config_js_1.getBranchDir)(keveDir);
|
|
77
77
|
const resultBase = branchDir ? path.join(branchDir, 'test-artifacts', `round-${round}`) : path.join(keveDir, 'test-artifacts', `round-${round}`);
|
|
78
78
|
const candidate = path.join(resultBase, 'test-results.json');
|
|
79
79
|
if (fs.existsSync(candidate))
|
|
@@ -96,7 +96,7 @@ function resolvePaths(options) {
|
|
|
96
96
|
// Resolve confidence-data.jsonl
|
|
97
97
|
let confidencePath = '';
|
|
98
98
|
if (keveDir && round !== null) {
|
|
99
|
-
const branchDir = (0,
|
|
99
|
+
const branchDir = (0, config_js_1.getBranchDir)(keveDir);
|
|
100
100
|
const resultBase = branchDir ? path.join(branchDir, 'test-artifacts', `round-${round}`) : path.join(keveDir, 'test-artifacts', `round-${round}`);
|
|
101
101
|
const candidate = path.join(resultBase, 'confidence-data.jsonl');
|
|
102
102
|
if (fs.existsSync(candidate))
|
|
@@ -117,7 +117,7 @@ async function invokeReport(options) {
|
|
|
117
117
|
const projectRoot = keveDir || path.dirname(resultsPath);
|
|
118
118
|
console.log(chalk_1.default.cyan('\n📊 keve report\n'));
|
|
119
119
|
try {
|
|
120
|
-
const reportData = await (0,
|
|
120
|
+
const reportData = await (0, index_js_1.generateReportData)({
|
|
121
121
|
projectRoot,
|
|
122
122
|
resultsPath,
|
|
123
123
|
confidencePath: confidencePath || undefined,
|
package/dist/commands/run.js
CHANGED
|
@@ -50,9 +50,9 @@ const path = __importStar(require("node:path"));
|
|
|
50
50
|
const os = __importStar(require("node:os"));
|
|
51
51
|
const node_child_process_1 = require("node:child_process");
|
|
52
52
|
const chalk_1 = __importDefault(require("chalk"));
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
53
|
+
const config_js_1 = require("../config.js");
|
|
54
|
+
const report_js_1 = require("../core/generateReport/report.js");
|
|
55
|
+
const reportData_js_1 = require("../core/generateReport/reportData.js");
|
|
56
56
|
function isPlaywrightBrowserInstalled() {
|
|
57
57
|
const cacheDir = os.platform() === 'darwin'
|
|
58
58
|
? path.join(os.homedir(), 'Library/Caches/ms-playwright')
|
|
@@ -95,7 +95,7 @@ function findPlaywrightConfig(keveDir, optionsConfig) {
|
|
|
95
95
|
console.error(chalk_1.default.red(` ✗ Config not found: ${optionsConfig}`));
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
|
-
const taskDir = (0,
|
|
98
|
+
const taskDir = (0, config_js_1.resolveTaskDir)(keveDir);
|
|
99
99
|
const searchPaths = [
|
|
100
100
|
taskDir ? path.join(taskDir, 'keve-test.config.mjs') : null,
|
|
101
101
|
taskDir ? path.join(taskDir, 'keve-test.config.ts') : null,
|
|
@@ -139,7 +139,7 @@ class KeveRunError extends Error {
|
|
|
139
139
|
}
|
|
140
140
|
exports.KeveRunError = KeveRunError;
|
|
141
141
|
async function run(options) {
|
|
142
|
-
const keveDir = (0,
|
|
142
|
+
const keveDir = (0, config_js_1.findKeveDir)();
|
|
143
143
|
const cwd = process.cwd();
|
|
144
144
|
const configPath = findPlaywrightConfig(keveDir || '', options.config);
|
|
145
145
|
if (!configPath) {
|
|
@@ -150,12 +150,13 @@ async function run(options) {
|
|
|
150
150
|
pwConfig = await new Function('return import(' + JSON.stringify(configPath) + ')')();
|
|
151
151
|
}
|
|
152
152
|
catch (e) {
|
|
153
|
-
|
|
153
|
+
// 包含 config 路径和完整错误堆栈,便于定位 "Invalid left-hand side" 等语法错误
|
|
154
|
+
throw new KeveRunError(`Failed to import Playwright config (${configPath}): ${e.message}\n${e.stack || ''}`);
|
|
154
155
|
}
|
|
155
156
|
const configDefault = pwConfig?.default || pwConfig;
|
|
156
157
|
const taskDir = process.env.KEVE_TASK_DIR
|
|
157
158
|
? path.resolve(process.env.KEVE_TASK_DIR)
|
|
158
|
-
: (0,
|
|
159
|
+
: (0, config_js_1.resolveTaskDir)(keveDir) || cwd;
|
|
159
160
|
let resultDir = '';
|
|
160
161
|
let resultsFile = '';
|
|
161
162
|
if (configDefault?.reporter) {
|
|
@@ -173,7 +174,7 @@ async function run(options) {
|
|
|
173
174
|
round = parseInt(options.round, 10);
|
|
174
175
|
}
|
|
175
176
|
else {
|
|
176
|
-
round = (0,
|
|
177
|
+
round = (0, config_js_1.getNextRoundFromState)(taskDir);
|
|
177
178
|
}
|
|
178
179
|
resultDir = path.join(taskDir, 'test-artifacts', `round-${round}`);
|
|
179
180
|
resultsFile = path.join(resultDir, 'test-results.json');
|
|
@@ -265,7 +266,7 @@ async function run(options) {
|
|
|
265
266
|
console.log(chalk_1.default.green(`\n ✓ Round ${round} complete`));
|
|
266
267
|
console.log(chalk_1.default.gray(` Tests: ${stats.expected || 0} expected, ${stats.actual || 0} actual`));
|
|
267
268
|
console.log(chalk_1.default.gray(` Passed: ${passed}, Failed: ${failed}, Skipped: ${skipped}`));
|
|
268
|
-
(0,
|
|
269
|
+
(0, config_js_1.completeRound)(taskDir, round, { passed, failed, skipped });
|
|
269
270
|
stripBase64FromTestResults(resultsFile, resultDir);
|
|
270
271
|
// ── 生成 report-data.json:在 Playwright 进程结束后统一生成,避免 keve-reporter 的 onEnd 与 JSON reporter 的 race condition ──
|
|
271
272
|
// keve-reporter.ts 的 onEnd 在 Playwright 进程内部执行,此时 JSON reporter 可能尚未完全刷新 test-results.json,
|
|
@@ -275,7 +276,7 @@ async function run(options) {
|
|
|
275
276
|
try {
|
|
276
277
|
const confidencePath = path.join(resultDir, 'confidence-data.jsonl');
|
|
277
278
|
const casesPath = path.join(taskDir, 'cases', 'test-cases.yaml');
|
|
278
|
-
const reportData = await (0,
|
|
279
|
+
const reportData = await (0, reportData_js_1.generateReportData)({
|
|
279
280
|
projectRoot: taskDir,
|
|
280
281
|
resultsPath: resultsFile,
|
|
281
282
|
confidencePath: fs.existsSync(confidencePath) ? confidencePath : undefined,
|
|
@@ -399,7 +400,7 @@ async function runGenerateReport(resultDir, resultsFile, keveDir, taskDir) {
|
|
|
399
400
|
const confidenceFile = path.join(resultDir, 'confidence-data.jsonl');
|
|
400
401
|
console.log(chalk_1.default.gray(' Step 1: Generating report data...'));
|
|
401
402
|
try {
|
|
402
|
-
const { reportData } = await (0,
|
|
403
|
+
const { reportData } = await (0, report_js_1.generateReport)({
|
|
403
404
|
projectRoot,
|
|
404
405
|
resultsPath: resultsFile,
|
|
405
406
|
confidencePath: fs.existsSync(confidenceFile) ? confidenceFile : undefined,
|
package/dist/commands/test.js
CHANGED
|
@@ -44,12 +44,12 @@ exports.test = test;
|
|
|
44
44
|
const fs = __importStar(require("node:fs"));
|
|
45
45
|
const path = __importStar(require("node:path"));
|
|
46
46
|
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
47
|
+
const config_js_1 = require("../config.js");
|
|
48
|
+
const run_js_1 = require("./run.js");
|
|
49
|
+
const report_js_1 = require("./report.js");
|
|
50
50
|
async function test(options) {
|
|
51
51
|
const maxRounds = parseInt(options.maxRounds || '3', 10);
|
|
52
|
-
const keveDir = (0,
|
|
52
|
+
const keveDir = (0, config_js_1.findKeveDir)();
|
|
53
53
|
console.log(chalk_1.default.cyan('\n🔄 keve test — Full pipeline\n'));
|
|
54
54
|
console.log(chalk_1.default.gray(` Max rounds: ${maxRounds}`));
|
|
55
55
|
for (let round = 1; round <= maxRounds; round++) {
|
|
@@ -57,14 +57,14 @@ async function test(options) {
|
|
|
57
57
|
// Step 1: Run tests
|
|
58
58
|
console.log(chalk_1.default.yellow(' [1/2] Running tests...'));
|
|
59
59
|
try {
|
|
60
|
-
await (0,
|
|
60
|
+
await (0, run_js_1.run)({
|
|
61
61
|
config: options.config,
|
|
62
62
|
grep: options.grep,
|
|
63
63
|
verbose: options.verbose,
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
catch (e) {
|
|
67
|
-
if (e instanceof
|
|
67
|
+
if (e instanceof run_js_1.KeveRunError) {
|
|
68
68
|
console.error(chalk_1.default.red(` ✗ Run failed: ${e.message}`));
|
|
69
69
|
break;
|
|
70
70
|
}
|
|
@@ -72,7 +72,7 @@ async function test(options) {
|
|
|
72
72
|
}
|
|
73
73
|
// Step 2: Generate report
|
|
74
74
|
console.log(chalk_1.default.yellow(' [2/2] Generating report...'));
|
|
75
|
-
await (0,
|
|
75
|
+
await (0, report_js_1.report)({});
|
|
76
76
|
// Check if all passed — if so, we're done
|
|
77
77
|
if (keveDir) {
|
|
78
78
|
const summaryPath = path.join(keveDir, 'reports', 'latest', 'execution-summary.json');
|
package/dist/config.js
CHANGED
|
@@ -51,10 +51,10 @@ exports.readRunState = readRunState;
|
|
|
51
51
|
exports.writeRunState = writeRunState;
|
|
52
52
|
exports.getNextRoundFromState = getNextRoundFromState;
|
|
53
53
|
exports.completeRound = completeRound;
|
|
54
|
-
const fs = __importStar(require("fs"));
|
|
55
|
-
const path = __importStar(require("path"));
|
|
54
|
+
const fs = __importStar(require("node:fs"));
|
|
55
|
+
const path = __importStar(require("node:path"));
|
|
56
56
|
const yaml = __importStar(require("yaml"));
|
|
57
|
-
const
|
|
57
|
+
const node_child_process_1 = require("node:child_process");
|
|
58
58
|
const DEFAULT_CONFIG = {
|
|
59
59
|
project: {
|
|
60
60
|
name: '',
|
|
@@ -166,7 +166,7 @@ function getBranchDir(keveDir) {
|
|
|
166
166
|
*/
|
|
167
167
|
function getCurrentBranch() {
|
|
168
168
|
try {
|
|
169
|
-
return (0,
|
|
169
|
+
return (0, node_child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
|
|
170
170
|
}
|
|
171
171
|
catch {
|
|
172
172
|
return null;
|
|
@@ -177,7 +177,7 @@ function getCurrentBranch() {
|
|
|
177
177
|
*/
|
|
178
178
|
function getGitUserName() {
|
|
179
179
|
try {
|
|
180
|
-
return (0,
|
|
180
|
+
return (0, node_child_process_1.execSync)('git config user.name', { encoding: 'utf-8' }).trim();
|
|
181
181
|
}
|
|
182
182
|
catch {
|
|
183
183
|
return process.env.KEVE_USER_NAME || '';
|
|
@@ -188,7 +188,7 @@ function getGitUserName() {
|
|
|
188
188
|
*/
|
|
189
189
|
function getGitRepoName() {
|
|
190
190
|
try {
|
|
191
|
-
const url = (0,
|
|
191
|
+
const url = (0, node_child_process_1.execSync)('git remote get-url origin', { encoding: 'utf-8' }).trim();
|
|
192
192
|
const parts = url.replace(/\.git$/, '').split('/');
|
|
193
193
|
return parts[parts.length - 1] || '';
|
|
194
194
|
}
|
|
@@ -201,7 +201,7 @@ function getGitRepoName() {
|
|
|
201
201
|
*/
|
|
202
202
|
function getGitBranchName() {
|
|
203
203
|
try {
|
|
204
|
-
return (0,
|
|
204
|
+
return (0, node_child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
|
|
205
205
|
}
|
|
206
206
|
catch {
|
|
207
207
|
return '';
|
|
@@ -46,7 +46,10 @@ class ActionLogWriter {
|
|
|
46
46
|
filePath;
|
|
47
47
|
constructor(taskDir) {
|
|
48
48
|
const dir = taskDir || process.env.KEVE_TASK_DIR || '.keve';
|
|
49
|
-
|
|
49
|
+
// 优先写入 runner 注入的当前轮次目录;缺失时回退旧布局。
|
|
50
|
+
const logDir = process.env.KEVE_RESULT_DIR
|
|
51
|
+
? path.resolve(process.env.KEVE_RESULT_DIR)
|
|
52
|
+
: path.join(dir, 'test-artifacts', `round-${process.env.KEVE_ROUND || 'latest'}`);
|
|
50
53
|
fs.mkdirSync(logDir, { recursive: true });
|
|
51
54
|
this.filePath = path.join(logDir, 'action-log.jsonl');
|
|
52
55
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* keve-decorators — @keveModel + @keveScene 装饰器
|
|
3
|
+
*
|
|
4
|
+
* 使用 TC39 Stage 3 装饰器 API (Playwright 1.58+ 默认支持)
|
|
5
|
+
*
|
|
6
|
+
* 执行顺序(TC39 Stage 3 规范):
|
|
7
|
+
* 1. Method decorator 函数调用 → 注册 addInitializer
|
|
8
|
+
* 2. Class decorator 函数调用 → 注册 addInitializer
|
|
9
|
+
* 3. Method addInitializers 执行 → @keveScene 注册场景到 WeakMap
|
|
10
|
+
* 4. Class addInitializer 执行 → @keveModel 读取 WeakMap 并调用 test.describe
|
|
11
|
+
*
|
|
12
|
+
* 两层装饰器 + 函数调用:
|
|
13
|
+
* Model (模块) → test.describe → @keveModel(id, desc, { setup, teardown, precondition?, before? })
|
|
14
|
+
* Scene (场景) → test → @keveScene(id, desc, { tag?, precondition?, before?, afterHook? })
|
|
15
|
+
* Goal (步骤) → 步骤元数据 → keveGoal() 函数调用(在方法体内,支持 precondition)
|
|
16
|
+
*
|
|
17
|
+
* @keveGroup 已消除:
|
|
18
|
+
* - setup → @keveModel setup 选项(独立 Page,所有 Scene 前执行)
|
|
19
|
+
* - teardown → @keveModel teardown 选项(所有 Scene 后执行,扫描 E2E_ 前缀)
|
|
20
|
+
* - identity → Scene 内 keveGoal(fn) 中调用 globalThis.switchIdentity
|
|
21
|
+
* - afterHook → @keveScene afterHook 选项(Scene 后保证执行)
|
|
22
|
+
*/
|
|
23
|
+
import { type KeveGoalMeta } from './keve-registry.js';
|
|
24
|
+
import { type KeveHost } from './keve-host.js';
|
|
25
|
+
/**
|
|
26
|
+
* 注册宿主(推荐入口,引擎无关)。
|
|
27
|
+
*/
|
|
28
|
+
export declare function setKeveTestHost(host: KeveHost): void;
|
|
29
|
+
/**
|
|
30
|
+
* Playwright 兼容适配:把 @playwright/test 的 test 对象包成 KeveHost。
|
|
31
|
+
* 保留该名称是为了不破坏既有调用(keve-test.ts / 外部脚本)。
|
|
32
|
+
*/
|
|
33
|
+
export declare function setKeveTest(t: any): void;
|
|
34
|
+
/** Get the current keve test instance (Playwright 兼容查询;Cypress 下返回宿主) */
|
|
35
|
+
export declare function getKeveTest(): KeveHost;
|
|
36
|
+
/** 是否已注册宿主 */
|
|
37
|
+
export declare function hasKeveTest(): boolean;
|
|
38
|
+
export type { KeveGoalMeta, KeveEvalMeta, ErrorCategory } from './keve-registry.js';
|
|
39
|
+
export interface KeveModelOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Module 级 setup:在所有 Scene 之前执行一次。
|
|
42
|
+
* 拥有独立 Page(完成后销毁,不与 Scene 共享)。
|
|
43
|
+
* 用 keveGoal(fn) → API 成功零 AI 开销,失败 AI 自愈。
|
|
44
|
+
* 签名:async ({ page, keveGoal }) => { ... }
|
|
45
|
+
* Playwright 将其作为独立 test() 执行,因此可以解构 fixture。
|
|
46
|
+
*/
|
|
47
|
+
setup?: Function;
|
|
48
|
+
/**
|
|
49
|
+
* Module 级 teardown:在所有 Scene 之后执行一次。
|
|
50
|
+
* 保证执行(即使 Scene 失败/超时)。
|
|
51
|
+
* 无 Playwright fixture —— 纯异步函数,用于 API 清理、E2E_ 前缀扫描等。
|
|
52
|
+
* 签名:async () => { ... }
|
|
53
|
+
*/
|
|
54
|
+
teardown?: Function;
|
|
55
|
+
/**
|
|
56
|
+
* Module 级前置条件(声明式描述):进入任何 Scene 之前必须满足的条件列表。
|
|
57
|
+
* 仅作元数据声明,供 Reporter/平台侧展示与归因(blocked ≠ fail)。
|
|
58
|
+
* 需要可执行校验时用 before。
|
|
59
|
+
*/
|
|
60
|
+
precondition?: string[];
|
|
61
|
+
/**
|
|
62
|
+
* Module 级可执行前置校验:在所有 Scene 之前执行一次(beforeAll)。
|
|
63
|
+
* 返回 false 或抛错 → 整个 Model 下所有 Scene 跳过(skip,不是 fail)。
|
|
64
|
+
* 无 Playwright fixture —— 纯异步函数(可用自有 API client 做环境检查)。
|
|
65
|
+
* 签名:async () => boolean
|
|
66
|
+
*/
|
|
67
|
+
before?: () => Promise<boolean>;
|
|
68
|
+
}
|
|
69
|
+
export interface KeveSceneOptions {
|
|
70
|
+
/** 场景标签(可选,用于分类过滤) */
|
|
71
|
+
tag?: string;
|
|
72
|
+
/**
|
|
73
|
+
* 用例级前置条件(声明式描述):本 Scene 执行前必须满足的条件列表。
|
|
74
|
+
* 仅作元数据声明,供 Reporter/平台侧展示与归因(对应旧系统 stepGroup.extraInfo.precondition)。
|
|
75
|
+
* 需要可执行校验时用 before。
|
|
76
|
+
*/
|
|
77
|
+
precondition?: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Scene 级可执行前置校验:本 Scene 执行前调用一次(beforeEach,仅对本 Scene 生效)。
|
|
80
|
+
* 返回 false 或抛错 → 本 Scene 跳过(skip,不是 fail)。
|
|
81
|
+
* 无 Playwright fixture —— 纯异步函数。
|
|
82
|
+
* 签名:async () => boolean
|
|
83
|
+
*/
|
|
84
|
+
before?: () => Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Scene 级 afterHook:Scene 执行后保证调用(即使失败/超时)。
|
|
87
|
+
* 无 Playwright fixture —— 纯异步函数,用于 API 清理等。
|
|
88
|
+
* 签名:async () => { ... }
|
|
89
|
+
*/
|
|
90
|
+
afterHook?: Function;
|
|
91
|
+
}
|
|
92
|
+
export interface KeveSceneMeta {
|
|
93
|
+
id: string;
|
|
94
|
+
description: string;
|
|
95
|
+
title: string;
|
|
96
|
+
code: string;
|
|
97
|
+
goals: KeveGoalMeta[];
|
|
98
|
+
fn: Function;
|
|
99
|
+
/** 用例级前置条件描述列表 */
|
|
100
|
+
precondition?: string[];
|
|
101
|
+
/** 可执行前置校验 */
|
|
102
|
+
before?: () => Promise<boolean>;
|
|
103
|
+
afterHook?: Function;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* @keveModel(id, description, options?) — Class decorator (模块层)
|
|
107
|
+
* Uses context.addInitializer to defer test.describe() registration
|
|
108
|
+
* until AFTER all @keveScene addInitializers have run.
|
|
109
|
+
*
|
|
110
|
+
* options.setup: 第一个 test() 调用,拥有独立 Page + keveGoal
|
|
111
|
+
* options.teardown: test.afterAll 回调,保证执行
|
|
112
|
+
*/
|
|
113
|
+
export declare function keveModel(id: string, description: string, options?: KeveModelOptions): <T extends {
|
|
114
|
+
new (...args: any[]): {};
|
|
115
|
+
}>(ctor: T, context: ClassDecoratorContext) => T;
|
|
116
|
+
/**
|
|
117
|
+
* @keveScene(id, description, options?) — Method decorator (场景层, static methods only)
|
|
118
|
+
* Captures fn.toString() as the complete scene script.
|
|
119
|
+
* Registers scene metadata to both WeakMap and global Map.
|
|
120
|
+
* If options.afterHook is provided, registers it in sceneAfterHooks Map
|
|
121
|
+
* for afterEach consumption.
|
|
122
|
+
*
|
|
123
|
+
* Returns the original function unchanged — Playwright resolves fixture dependencies
|
|
124
|
+
* by parsing fn.toString() / AST. Wrapping would break this resolution.
|
|
125
|
+
*/
|
|
126
|
+
export declare function keveScene(id: string, description: string, options?: KeveSceneOptions): (fn: any, context: ClassMethodDecoratorContext) => any;
|
|
127
|
+
/** 获取 Model 下注册的所有 Scene */
|
|
128
|
+
export declare function getModelScenes(ctor: Function): KeveSceneMeta[] | null;
|