@kkeve/suite 0.0.15 → 0.0.18

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.
Files changed (115) hide show
  1. package/dist/bin/keve.js +2 -1
  2. package/dist/commands/clean.js +2 -2
  3. package/dist/commands/config.js +7 -7
  4. package/dist/commands/init.js +2 -2
  5. package/dist/commands/report.js +7 -7
  6. package/dist/commands/run.js +12 -11
  7. package/dist/commands/test.js +7 -7
  8. package/dist/config.js +7 -7
  9. package/dist/core/action-log-writer.js +4 -1
  10. package/dist/core/decorator/keve-decorators.d.ts +128 -0
  11. package/dist/core/decorator/keve-decorators.js +254 -0
  12. package/dist/core/decorator/keve-host.d.ts +40 -0
  13. package/dist/core/decorator/keve-host.js +33 -0
  14. package/dist/core/{keve-registry.d.ts → decorator/keve-registry.d.ts} +19 -0
  15. package/dist/core/{keve-registry.js → decorator/keve-registry.js} +13 -1
  16. package/dist/core/generateReport/report.d.ts +1 -1
  17. package/dist/core/generateReport/report.js +2 -2
  18. package/dist/core/generateReport/reportData.d.ts +3 -0
  19. package/dist/core/generateReport/reportData.js +49 -15
  20. package/dist/core/judge/confidence.d.ts +119 -0
  21. package/dist/core/judge/confidence.js +295 -0
  22. package/dist/core/judge/goal-heuristics.d.ts +32 -0
  23. package/dist/core/judge/goal-heuristics.js +128 -0
  24. package/dist/core/judge/pngCodec.d.ts +23 -0
  25. package/dist/core/judge/pngCodec.js +215 -0
  26. package/dist/core/judge/screenshotDiff.d.ts +112 -0
  27. package/dist/core/judge/screenshotDiff.js +420 -0
  28. package/dist/core/page-object/definePage.d.ts +95 -0
  29. package/dist/core/page-object/definePage.js +62 -0
  30. package/dist/core/page-object/elementResolver.d.ts +32 -0
  31. package/dist/core/page-object/elementResolver.js +236 -0
  32. package/dist/core/page-object/engine-types.d.ts +140 -0
  33. package/dist/core/page-object/engine-types.js +34 -0
  34. package/dist/core/sso-cookie.d.ts +18 -0
  35. package/dist/core/sso-cookie.js +10 -0
  36. package/dist/dsl/index.d.ts +15 -0
  37. package/dist/dsl/index.js +29 -0
  38. package/dist/dsl/locatorSource.d.ts +25 -0
  39. package/dist/dsl/locatorSource.js +323 -0
  40. package/dist/dsl/ops.d.ts +46 -0
  41. package/dist/dsl/ops.js +95 -0
  42. package/dist/dsl/toDecorator.d.ts +98 -0
  43. package/dist/dsl/toDecorator.js +747 -0
  44. package/dist/engine-cypress/adapter.d.ts +108 -0
  45. package/dist/engine-cypress/adapter.js +514 -0
  46. package/dist/engine-cypress/agent-page.d.ts +159 -0
  47. package/dist/engine-cypress/agent-page.js +566 -0
  48. package/dist/engine-cypress/agentDom.d.ts +22 -0
  49. package/dist/engine-cypress/agentDom.js +590 -0
  50. package/dist/engine-cypress/bridge.d.ts +39 -0
  51. package/dist/engine-cypress/bridge.js +598 -0
  52. package/dist/engine-cypress/cdp.d.ts +78 -0
  53. package/dist/engine-cypress/cdp.js +339 -0
  54. package/dist/engine-cypress/host.d.ts +22 -0
  55. package/dist/engine-cypress/host.js +480 -0
  56. package/dist/engine-cypress/index.d.ts +26 -0
  57. package/dist/engine-cypress/index.js +58 -0
  58. package/dist/engine-cypress/keveGoal.d.ts +43 -0
  59. package/dist/engine-cypress/keveGoal.js +265 -0
  60. package/dist/engine-cypress/pageOps.d.ts +41 -0
  61. package/dist/engine-cypress/pageOps.js +439 -0
  62. package/dist/engine-cypress/reporter.d.ts +20 -0
  63. package/dist/engine-cypress/reporter.js +128 -0
  64. package/dist/engine-cypress/setup.d.ts +21 -0
  65. package/dist/engine-cypress/setup.js +27 -0
  66. package/dist/engine-cypress/shotBuffer.d.ts +40 -0
  67. package/dist/engine-cypress/shotBuffer.js +41 -0
  68. package/dist/engine-cypress/state.d.ts +56 -0
  69. package/dist/engine-cypress/state.js +81 -0
  70. package/dist/engine-playwright/engineAdapter.d.ts +78 -0
  71. package/dist/engine-playwright/engineAdapter.js +371 -0
  72. package/dist/engine-playwright/global-setup.d.ts +28 -0
  73. package/dist/engine-playwright/global-setup.js +142 -0
  74. package/dist/{core → engine-playwright}/keve-report.d.ts +0 -1
  75. package/dist/engine-playwright/keve-report.js +137 -0
  76. package/dist/{core → engine-playwright}/keve-test.d.ts +10 -8
  77. package/dist/{core → engine-playwright}/keve-test.js +204 -200
  78. package/dist/{core → engine-playwright}/pw-config.js +8 -7
  79. package/dist/index.d.ts +13 -8
  80. package/dist/index.js +43 -28
  81. package/dist/page-agent/agent.d.ts +82 -7
  82. package/dist/page-agent/agent.js +252 -88
  83. package/dist/page-agent/diagnostics.d.ts +184 -0
  84. package/dist/page-agent/diagnostics.js +377 -0
  85. package/dist/page-agent/hooks.d.ts +3 -2
  86. package/dist/page-agent/hooks.js +4 -3
  87. package/dist/page-agent/page-like.d.ts +116 -0
  88. package/dist/page-agent/page-like.js +14 -0
  89. package/dist/page-agent/playwright-page.d.ts +20 -0
  90. package/dist/page-agent/playwright-page.js +98 -0
  91. package/dist/page-agent/system-prompt.d.ts +7 -0
  92. package/dist/page-agent/system-prompt.js +10 -0
  93. package/dist/page-agent/tools.d.ts +1 -1
  94. package/dist/page-agent/tools.js +144 -8
  95. package/package.json +24 -11
  96. package/scripts/gen-system-prompt.mjs +36 -0
  97. package/scripts/run-dsl-task.mjs +652 -0
  98. package/scripts/runtime/cypress-preprocessor.mjs +44 -0
  99. package/scripts/runtime/cypress.config.mjs +35 -0
  100. package/scripts/runtime/playwright.config.mjs +41 -0
  101. package/scripts/verify-shot-diff.mjs +243 -0
  102. package/dist/commands/auth-update.d.ts +0 -16
  103. package/dist/commands/auth-update.js +0 -88
  104. package/dist/core/auth.d.ts +0 -17
  105. package/dist/core/auth.js +0 -224
  106. package/dist/core/cdp-resolver.d.ts +0 -5
  107. package/dist/core/cdp-resolver.js +0 -93
  108. package/dist/core/global-setup.d.ts +0 -6
  109. package/dist/core/global-setup.js +0 -123
  110. package/dist/core/keve-decorators.d.ts +0 -51
  111. package/dist/core/keve-decorators.js +0 -95
  112. package/dist/core/keve-report.js +0 -376
  113. /package/dist/core/{keve-aspect.d.ts → decorator/keve-aspect.d.ts} +0 -0
  114. /package/dist/core/{keve-aspect.js → decorator/keve-aspect.js} +0 -0
  115. /package/dist/{core → engine-playwright}/pw-config.d.ts +0 -0
package/dist/core/auth.js DELETED
@@ -1,224 +0,0 @@
1
- "use strict";
2
- /**
3
- * Auth utilities — acquire storage state for Playwright tests
4
- *
5
- * 1. Existing valid storage-state.json → skip
6
- * 2. CDP connect to user Chrome → reuse login state
7
- * 3. Manual browser login → fallback
8
- */
9
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- var desc = Object.getOwnPropertyDescriptor(m, k);
12
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
- desc = { enumerable: true, get: function() { return m[k]; } };
14
- }
15
- Object.defineProperty(o, k2, desc);
16
- }) : (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- o[k2] = m[k];
19
- }));
20
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
- Object.defineProperty(o, "default", { enumerable: true, value: v });
22
- }) : function(o, v) {
23
- o["default"] = v;
24
- });
25
- var __importStar = (this && this.__importStar) || (function () {
26
- var ownKeys = function(o) {
27
- ownKeys = Object.getOwnPropertyNames || function (o) {
28
- var ar = [];
29
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
- return ar;
31
- };
32
- return ownKeys(o);
33
- };
34
- return function (mod) {
35
- if (mod && mod.__esModule) return mod;
36
- var result = {};
37
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
- __setModuleDefault(result, mod);
39
- return result;
40
- };
41
- })();
42
- Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.acquireStorageState = acquireStorageState;
44
- const fs = __importStar(require("node:fs"));
45
- const os = __importStar(require("node:os"));
46
- const path = __importStar(require("node:path"));
47
- const node_child_process_1 = require("node:child_process"); // getChromeWsUrl 需要用 lsof 检查端口
48
- const node_module_1 = require("node:module");
49
- // ─── Playwright Chromium resolver ───────────────────────────────────────────
50
- async function getChromium() {
51
- const cwdRequire = (0, node_module_1.createRequire)(path.resolve(process.cwd(), 'node_modules'));
52
- const pwAbsolutePath = cwdRequire.resolve('@playwright/test');
53
- const pwModule = await Promise.resolve(`${pwAbsolutePath}`).then(s => __importStar(require(s)));
54
- const chromium = pwModule.chromium || pwModule.default?.chromium;
55
- if (!chromium)
56
- throw new Error('Failed to resolve chromium from @playwright/test');
57
- return chromium;
58
- }
59
- // ─── Chrome CDP helpers ─────────────────────────────────────────────────────
60
- function getChromeUserDataDir() {
61
- const p = os.platform();
62
- const h = os.homedir();
63
- if (p === 'darwin')
64
- return path.join(h, 'Library', 'Application Support', 'Google', 'Chrome');
65
- if (p === 'win32')
66
- return path.join(h, 'AppData', 'Local', 'Google', 'Chrome', 'User Data');
67
- return path.join(h, '.config', 'google-chrome');
68
- }
69
- /** 读取 DevToolsActivePort 获取 CDP WebSocket URL(端口须正在监听) */
70
- function getChromeWsUrl() {
71
- const portFile = path.join(getChromeUserDataDir(), 'DevToolsActivePort');
72
- if (!fs.existsSync(portFile))
73
- return null;
74
- try {
75
- const [portStr, wsPath] = fs.readFileSync(portFile, 'utf8').trim().split('\n').map(l => l.trim()).filter(Boolean);
76
- const port = parseInt(portStr, 10);
77
- if (!port || !wsPath)
78
- return null;
79
- try {
80
- (0, node_child_process_1.execSync)(`lsof -P -i TCP:${port} 2>/dev/null | grep LISTEN`, { encoding: 'utf8' });
81
- }
82
- catch {
83
- return null;
84
- }
85
- return `ws://127.0.0.1:${port}${wsPath}`;
86
- }
87
- catch {
88
- return null;
89
- }
90
- }
91
- /** CDP 连接用户 Chrome → 打开目标页 → 保存 storage-state → 断开
92
- * 仅连接已运行的 Chrome(keve run 已确保 Chrome 启动 + debug 端口就绪)
93
- */
94
- async function authViaCDP(targetUrl, storageStatePath, prefix) {
95
- const wsUrl = getChromeWsUrl();
96
- if (!wsUrl) {
97
- console.log(`${prefix} Chrome CDP not available`);
98
- return false;
99
- }
100
- console.log(`${prefix} Chrome CDP available, connecting...`);
101
- try {
102
- const chromium = await getChromium();
103
- const browser = await chromium.connectOverCDP(wsUrl, { timeout: 10000 });
104
- const context = browser.contexts()[0];
105
- if (!context) {
106
- browser.close();
107
- return false;
108
- }
109
- // 访问目标页面,等登录重定向完成
110
- const page = await context.newPage();
111
- await page.goto(targetUrl, { waitUntil: 'networkidle', timeout: 60000 });
112
- await waitForLoginRedirect(page);
113
- const onLoginPage = /login|auth|signin/i.test(page.url());
114
- if (onLoginPage) {
115
- console.log(`${prefix} CDP connected but target requires login`);
116
- await page.close();
117
- browser.close();
118
- return false;
119
- }
120
- // 保存 storage-state
121
- const cookies = await context.cookies();
122
- const state = { cookies, origins: [] };
123
- fs.writeFileSync(storageStatePath, JSON.stringify(state, null, 2));
124
- await page.close();
125
- browser.close();
126
- console.log(`${prefix} CDP auth success (${cookies.length} cookies) → ${storageStatePath}`);
127
- return cookies.length > 0;
128
- }
129
- catch (e) {
130
- console.warn(`${prefix} CDP connect failed: ${e.message}`);
131
- return false;
132
- }
133
- }
134
- function isStorageStateValid(statePath, targetDomain) {
135
- if (!fs.existsSync(statePath))
136
- return false;
137
- try {
138
- const s = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
139
- if (!s.cookies?.length)
140
- return false;
141
- if (targetDomain && !s.cookies.some(c => c.domain?.includes(targetDomain)))
142
- return false;
143
- const now = Date.now() / 1000;
144
- return s.cookies.some(c => !c.expires || c.expires === -1 || c.expires > now);
145
- }
146
- catch {
147
- return false;
148
- }
149
- }
150
- function extractDomain(url) {
151
- try {
152
- return new URL(url).hostname;
153
- }
154
- catch {
155
- return undefined;
156
- }
157
- }
158
- async function waitForLoginRedirect(page, maxMs = 30000) {
159
- for (let i = 0; i < maxMs / 500; i++) {
160
- if (!/login|auth|signin/i.test(page.url()))
161
- break;
162
- await page.waitForTimeout(500);
163
- }
164
- await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => { });
165
- return !/login|auth|signin|sign-in/i.test(page.url());
166
- }
167
- /** 手动登录 → 保存 storage-state */
168
- async function authViaManualLogin(targetUrl, storageStatePath, prefix) {
169
- const loginUrl = targetUrl.replace(/\/?$/, '/login');
170
- console.log(`${prefix} Opening browser for manual login: ${loginUrl}`);
171
- console.log(`${prefix} Please login. Waiting up to 2 minutes...`);
172
- try {
173
- const browser = await (await getChromium()).launch({ headless: false });
174
- const context = await browser.newContext();
175
- const page = await context.newPage();
176
- await page.goto(loginUrl, { waitUntil: 'domcontentloaded', timeout: 15000 })
177
- .catch(() => page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 15000 }));
178
- let loginDetected = false;
179
- for (let i = 0; i < 120; i++) {
180
- if (!/login|auth|signin|sign-in/i.test(page.url())) {
181
- loginDetected = true;
182
- break;
183
- }
184
- await page.waitForTimeout(1000);
185
- }
186
- if (!loginDetected)
187
- console.log(`${prefix} Timeout (2min), saving current state...`);
188
- await waitForLoginRedirect(page);
189
- await context.storageState({ path: storageStatePath });
190
- const c = JSON.parse(fs.readFileSync(storageStatePath, 'utf-8')).cookies?.length || 0;
191
- console.log(`${prefix} Auth saved (${c} cookies) → ${storageStatePath}`);
192
- await browser.close();
193
- return c > 0;
194
- }
195
- catch (e) {
196
- console.error(`${prefix} Manual login error: ${e.message}`);
197
- return false;
198
- }
199
- }
200
- async function acquireStorageState(options = {}) {
201
- const targetUrl = options.targetUrl || process.env.PWGEN_TARGET_URL || 'http://localhost:3000';
202
- const storageStatePath = options.storageStatePath || path.resolve(process.cwd(), '.auth/storage-state.json');
203
- const prefix = options.logPrefix || '[keve]';
204
- const targetDomain = extractDomain(targetUrl);
205
- const authDir = path.dirname(storageStatePath);
206
- if (!fs.existsSync(authDir))
207
- fs.mkdirSync(authDir, { recursive: true });
208
- // Skip mode
209
- if (process.env.PWGEN_SKIP_AUTH === '1' || process.env.PWGEN_SKIP_AUTH === 'true') {
210
- fs.writeFileSync(storageStatePath, JSON.stringify({ cookies: [], origins: [] }, null, 2));
211
- console.log(`${prefix} PWGEN_SKIP_AUTH=1 → empty storage state saved`);
212
- return true;
213
- }
214
- // Step 1: 已有有效 storage-state
215
- if (isStorageStateValid(storageStatePath, targetDomain)) {
216
- console.log(`${prefix} Storage state valid → ${storageStatePath}`);
217
- return true;
218
- }
219
- // Step 2: CDP 连接用户 Chrome
220
- if (await authViaCDP(targetUrl, storageStatePath, prefix))
221
- return true;
222
- // Step 3: 手动登录
223
- return await authViaManualLogin(targetUrl, storageStatePath, prefix);
224
- }
@@ -1,5 +0,0 @@
1
- export interface CdpResolverOptions {
2
- endpoint?: string;
3
- wsEndpoint?: string;
4
- }
5
- export declare function resolveCdpWsUrl(options: CdpResolverOptions): Promise<string | null>;
@@ -1,93 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.resolveCdpWsUrl = resolveCdpWsUrl;
37
- const fs = __importStar(require("node:fs"));
38
- const os = __importStar(require("node:os"));
39
- const path = __importStar(require("node:path"));
40
- const node_child_process_1 = require("node:child_process");
41
- // TODO: consolidate doc-reading logic into keve-suite/spec
42
- function getChromeUserDataDir() {
43
- const p = os.platform();
44
- const h = os.homedir();
45
- if (p === 'darwin')
46
- return path.join(h, 'Library', 'Application Support', 'Google', 'Chrome');
47
- if (p === 'win32')
48
- return path.join(h, 'AppData', 'Local', 'Google', 'Chrome', 'User Data');
49
- return path.join(h, '.config', 'google-chrome');
50
- }
51
- function getChromeWsUrlFromPort() {
52
- const portFile = path.join(getChromeUserDataDir(), 'DevToolsActivePort');
53
- if (!fs.existsSync(portFile))
54
- return null;
55
- try {
56
- const [portStr, wsPath] = fs.readFileSync(portFile, 'utf8').trim().split('\n').map(l => l.trim()).filter(Boolean);
57
- const port = parseInt(portStr, 10);
58
- if (!port || !wsPath)
59
- return null;
60
- try {
61
- (0, node_child_process_1.execSync)(`lsof -P -i TCP:${port} 2>/dev/null | grep LISTEN`, { encoding: 'utf8' });
62
- }
63
- catch {
64
- return null;
65
- }
66
- return `ws://127.0.0.1:${port}${wsPath}`;
67
- }
68
- catch {
69
- return null;
70
- }
71
- }
72
- async function fetchWsUrlFromHttpEndpoint(endpoint) {
73
- try {
74
- const url = endpoint.replace(/\/$/, '') + '/json/version';
75
- const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
76
- const data = await res.json();
77
- return data.webSocketDebuggerUrl || null;
78
- }
79
- catch (e) {
80
- console.warn(`[cdp-resolver] Failed to fetch from ${endpoint}: ${e.message}`);
81
- return null;
82
- }
83
- }
84
- async function resolveCdpWsUrl(options) {
85
- if (options.wsEndpoint)
86
- return options.wsEndpoint;
87
- if (options.endpoint) {
88
- const wsUrl = await fetchWsUrlFromHttpEndpoint(options.endpoint);
89
- if (wsUrl)
90
- return wsUrl;
91
- }
92
- return getChromeWsUrlFromPort();
93
- }
@@ -1,6 +0,0 @@
1
- declare global {
2
- var __keve_cdp_browser: import('@playwright/test').Browser | undefined;
3
- }
4
- export declare function getCdpBrowser(): Promise<import('@playwright/test').Browser | null>;
5
- export declare function isCdpMode(): boolean;
6
- export default function (): Promise<void>;
@@ -1,123 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.getCdpBrowser = getCdpBrowser;
37
- exports.isCdpMode = isCdpMode;
38
- exports.default = default_1;
39
- const fs = __importStar(require("node:fs"));
40
- const path = __importStar(require("node:path"));
41
- const auth_1 = require("./auth");
42
- async function getCdpBrowser() {
43
- // 如果已有共享的 browser 对象,直接复用
44
- if (globalThis.__keve_cdp_browser && globalThis.__keve_cdp_browser.isConnected()) {
45
- return globalThis.__keve_cdp_browser;
46
- }
47
- const { chromium } = await Promise.resolve().then(() => __importStar(require('@playwright/test')));
48
- let wsUrl = process.env.KEVE_CDP_WS_ENDPOINT;
49
- // 如果只提供了 HTTP endpoint,自动获取 WebSocket URL
50
- if (!wsUrl && process.env.KEVE_CDP_ENDPOINT) {
51
- const httpUrl = process.env.KEVE_CDP_ENDPOINT.replace(/\/$/, '');
52
- try {
53
- const resp = await fetch(`${httpUrl}/json/version`, { signal: AbortSignal.timeout(5000) });
54
- const data = await resp.json();
55
- wsUrl = data.webSocketDebuggerUrl;
56
- console.log(`[keve CDP] Resolved WS endpoint: ${wsUrl}`);
57
- }
58
- catch (e) {
59
- console.warn(`[keve CDP] Failed to resolve CDP WebSocket URL from ${httpUrl}: ${e.message} — falling back to non-CDP mode`);
60
- return null;
61
- }
62
- }
63
- if (!wsUrl) {
64
- console.warn(`[keve CDP] No CDP endpoint configured — falling back to non-CDP mode`);
65
- return null;
66
- }
67
- try {
68
- const browser = await chromium.connectOverCDP(wsUrl, { timeout: 15000 });
69
- console.log(`[keve CDP] Connected via ${wsUrl}`);
70
- globalThis.__keve_cdp_browser = browser;
71
- return browser;
72
- }
73
- catch (connectErr) {
74
- console.warn(`[keve CDP] connectOverCDP failed: ${connectErr.message} — falling back to non-CDP mode`);
75
- return null;
76
- }
77
- }
78
- function isCdpMode() {
79
- return !!process.env.KEVE_CDP_WS_ENDPOINT || !!process.env.KEVE_CDP_ENDPOINT;
80
- }
81
- async function default_1() {
82
- const projectRoot = path.resolve(process.cwd());
83
- const storageStatePath = path.resolve(projectRoot, '.auth/storage-state.json');
84
- const cdpEndpoint = process.env.KEVE_CDP_ENDPOINT || process.env.KEVE_CDP_WS_ENDPOINT;
85
- if (cdpEndpoint) {
86
- console.log(`[keve global-setup] CDP endpoint detected: ${cdpEndpoint}`);
87
- // CDP 模式:在 global-setup 中提前连接一次,缓存 Browser 对象
88
- // getCdpBrowser() 失败时返回 null 并清除 CDP 环境变量 → 后续 fixture 自动 fallback
89
- const browser = await getCdpBrowser();
90
- if (browser) {
91
- const contexts = browser.contexts();
92
- const pages = contexts.length > 0 ? contexts[0].pages() : [];
93
- console.log(`[keve global-setup] CDP connected. contexts=${contexts.length}, pages=${pages.length}`);
94
- if (pages.length > 0) {
95
- console.log(`[keve global-setup] Existing tabs: ${pages.map(p => p.url()).join(', ')}`);
96
- }
97
- }
98
- else {
99
- console.warn(`[keve global-setup] CDP connection failed — tests will use non-CDP fallback (chromium.launch)`);
100
- }
101
- if (!fs.existsSync(storageStatePath)) {
102
- const authDir = path.dirname(storageStatePath);
103
- if (!fs.existsSync(authDir))
104
- fs.mkdirSync(authDir, { recursive: true });
105
- fs.writeFileSync(storageStatePath, JSON.stringify({ cookies: [], origins: [] }));
106
- }
107
- return;
108
- }
109
- console.log(`[keve global-setup] Acquiring fresh auth state...`);
110
- const success = await (0, auth_1.acquireStorageState)({
111
- logPrefix: '[keve global-setup]',
112
- });
113
- if (!success) {
114
- console.warn(`[keve global-setup] Auth acquisition failed. Tests may fail due to auth issues.`);
115
- if (!fs.existsSync(storageStatePath)) {
116
- const authDir = path.dirname(storageStatePath);
117
- if (!fs.existsSync(authDir))
118
- fs.mkdirSync(authDir, { recursive: true });
119
- fs.writeFileSync(storageStatePath, JSON.stringify({ cookies: [], origins: [] }));
120
- console.log(`[keve global-setup] Created empty storage state → ${storageStatePath}`);
121
- }
122
- }
123
- }
@@ -1,51 +0,0 @@
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
14
- * Scene (场景) → test → @keveScene (fn.toString() 捕获完整脚本)
15
- * Goal (步骤) → 步骤元数据 → keveGoal() 函数调用(在方法体内)
16
- */
17
- import { type KeveGoalMeta } from './keve-registry';
18
- /** Called by keve-test.ts to register the extended test instance */
19
- export declare function setKeveTest(t: any): void;
20
- /** Get the current keve test instance (extended with fixtures) */
21
- export declare function getKeveTest(): any;
22
- export type { KeveGoalMeta, KeveEvalMeta, ErrorCategory } from './keve-registry';
23
- export interface KeveSceneMeta {
24
- id: string;
25
- description: string;
26
- title: string;
27
- code: string;
28
- goals: KeveGoalMeta[];
29
- fn: Function;
30
- }
31
- /**
32
- * @keveModel(name) — Class decorator (模块层)
33
- * Uses context.addInitializer to defer test.describe() registration
34
- * until AFTER all @keveScene addInitializers have run.
35
- */
36
- export declare function keveModel(id: string, description: string): <T extends {
37
- new (...args: any[]): {};
38
- }>(ctor: T, context: ClassDecoratorContext) => T;
39
- /**
40
- * @keveScene(title) — Method decorator (场景层, static methods only)
41
- * Captures fn.toString() as the complete scene script.
42
- * Wraps fn with try-catch-finally to classify errors and decide skipAI.
43
- * Registers scene metadata to both WeakMap and global Map.
44
- *
45
- * The wrapper preserves the original function's destructuring signature by
46
- * extracting the parameter text from fn.toString() and constructing a wrapper
47
- * via new Function(), so Playwright can correctly resolve fixture dependencies.
48
- */
49
- export declare function keveScene(id: string, description: string): (fn: Function, context: ClassMethodDecoratorContext) => Function;
50
- /** 获取 Model 下注册的所有 Scene */
51
- export declare function getModelScenes(ctor: Function): KeveSceneMeta[] | null;
@@ -1,95 +0,0 @@
1
- "use strict";
2
- /**
3
- * keve-decorators — @keveModel + @keveScene 装饰器
4
- *
5
- * 使用 TC39 Stage 3 装饰器 API (Playwright 1.58+ 默认支持)
6
- *
7
- * 执行顺序(TC39 Stage 3 规范):
8
- * 1. Method decorator 函数调用 → 注册 addInitializer
9
- * 2. Class decorator 函数调用 → 注册 addInitializer
10
- * 3. Method addInitializers 执行 → @keveScene 注册场景到 WeakMap
11
- * 4. Class addInitializer 执行 → @keveModel 读取 WeakMap 并调用 test.describe
12
- *
13
- * 两层装饰器 + 函数调用:
14
- * Model (模块) → test.describe → @keveModel
15
- * Scene (场景) → test → @keveScene (fn.toString() 捕获完整脚本)
16
- * Goal (步骤) → 步骤元数据 → keveGoal() 函数调用(在方法体内)
17
- */
18
- Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.setKeveTest = setKeveTest;
20
- exports.getKeveTest = getKeveTest;
21
- exports.keveModel = keveModel;
22
- exports.keveScene = keveScene;
23
- exports.getModelScenes = getModelScenes;
24
- const test_1 = require("@playwright/test");
25
- const keve_registry_1 = require("./keve-registry");
26
- // ─── Configurable test reference ────────────────────────────────────
27
- // keveModel needs to call test.describe / test — but it must use the EXTENDED test
28
- // (with keveGoal/keveAssert fixtures), not the base @playwright/test.
29
- // keve-test.ts calls setKeveTest() after creating the extended test.
30
- let _keveTest = test_1.test;
31
- /** Called by keve-test.ts to register the extended test instance */
32
- function setKeveTest(t) { _keveTest = t; }
33
- /** Get the current keve test instance (extended with fixtures) */
34
- function getKeveTest() { return _keveTest; }
35
- // ─── Internal Registry (WeakMap per Model constructor) ─────────────
36
- /** Model → Scene 列表:每个 @keveModel 类下注册的 @keveScene 列表 */
37
- const modelScenesMap = new WeakMap();
38
- // ─── Error Classification ──────────────────────────────────────────
39
- // Error classification moved to keve-report.ts onTestEnd —
40
- // onTestEnd has access to result.error and result.status, which is more reliable
41
- // than try-catch in the decorator (async rejections, test-level timeouts can bypass decorator catch).
42
- // ─── TC39 Stage 3 Decorators ──────────────────────────────────────
43
- /**
44
- * @keveModel(name) — Class decorator (模块层)
45
- * Uses context.addInitializer to defer test.describe() registration
46
- * until AFTER all @keveScene addInitializers have run.
47
- */
48
- function keveModel(id, description) {
49
- return (ctor, context) => {
50
- context.addInitializer(function () {
51
- const ctor = this;
52
- const scenes = modelScenesMap.get(ctor) || [];
53
- _keveTest.describe(`${id}: ${description}`, () => {
54
- for (const scene of scenes) {
55
- _keveTest(scene.title, scene.fn);
56
- }
57
- });
58
- });
59
- return ctor;
60
- };
61
- }
62
- /**
63
- * @keveScene(title) — Method decorator (场景层, static methods only)
64
- * Captures fn.toString() as the complete scene script.
65
- * Wraps fn with try-catch-finally to classify errors and decide skipAI.
66
- * Registers scene metadata to both WeakMap and global Map.
67
- *
68
- * The wrapper preserves the original function's destructuring signature by
69
- * extracting the parameter text from fn.toString() and constructing a wrapper
70
- * via new Function(), so Playwright can correctly resolve fixture dependencies.
71
- */
72
- function keveScene(id, description) {
73
- return (fn, context) => {
74
- const code = fn.toString(); // Capture original code BEFORE wrapping
75
- const title = `${id}: ${description}`;
76
- // Register the original function directly — Playwright resolves fixture dependencies
77
- // by parsing fn.toString() / AST. Wrapping with new Function() breaks this resolution.
78
- // Error classification is handled in onTestEnd (keve-report.ts), not here.
79
- context.addInitializer(function () {
80
- const ctor = this;
81
- if (!modelScenesMap.has(ctor))
82
- modelScenesMap.set(ctor, []);
83
- const scene = { id, description, title, code, goals: [], fn };
84
- modelScenesMap.get(ctor).push(scene);
85
- // Register to global maps for Reporter access
86
- keve_registry_1.sceneCodeMap.set(title, code); // Stores ORIGINAL code, not wrapped
87
- });
88
- return fn; // Return original function unchanged — preserves fixture parameter signature
89
- };
90
- }
91
- // ─── Utility ───────────────────────────────────────────────────────
92
- /** 获取 Model 下注册的所有 Scene */
93
- function getModelScenes(ctor) {
94
- return modelScenesMap.get(ctor) || null;
95
- }