@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
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* keveGoal — Cypress 版的唯一操作原语
|
|
4
|
+
*
|
|
5
|
+
* 与 Playwright 的 keveGoal fixture(core/keve-test.ts)**语义同构**:
|
|
6
|
+
* - 记录 goal 元数据到 sceneGoalsMap(报告归因用)
|
|
7
|
+
* - fn 前 / fn 后各截一张图,落盘后写进 attachment
|
|
8
|
+
* - fn 成功/失败都进入 Agent Re-Act,由观察真实页面后给最终结论;
|
|
9
|
+
* 仅截图差异超阈值、环境阻断这两类确定性事实短路
|
|
10
|
+
* - 产物是 `keveGoalResult` attachment,字段与 Playwright 完全一致
|
|
11
|
+
*
|
|
12
|
+
* 唯一的实现差异:截图与落盘必须经 HTTP 桥(cy.task 在 async cy.then 内不可 await)。
|
|
13
|
+
* 所以这里不 import 任何 Node 模块,也不 import @playwright/test。
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.setCyGoalExplorer = setCyGoalExplorer;
|
|
17
|
+
exports.createCyKeveGoal = createCyKeveGoal;
|
|
18
|
+
const keve_registry_js_1 = require("../core/decorator/keve-registry.js");
|
|
19
|
+
const keve_aspect_js_1 = require("../core/decorator/keve-aspect.js");
|
|
20
|
+
const goal_heuristics_js_1 = require("../core/judge/goal-heuristics.js");
|
|
21
|
+
const shotBuffer_js_1 = require("./shotBuffer.js");
|
|
22
|
+
const state_js_1 = require("./state.js");
|
|
23
|
+
/** 由引擎入口注册的探索器(P0-3:Cypress 侧 AI Re-Act) */
|
|
24
|
+
let explorer = null;
|
|
25
|
+
function setCyGoalExplorer(fn) {
|
|
26
|
+
explorer = fn;
|
|
27
|
+
}
|
|
28
|
+
/** 与 Playwright captureScreenshot 的 safeName 规则保持一致,保证两端截图文件名同形。 */
|
|
29
|
+
function safeStepName(step) {
|
|
30
|
+
return String(step || 'step').replace(/[^a-zA-Z0-9一-鿿]/g, '_').slice(0, 30);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 同时输出到 Cypress 控制台与用例日志缓冲。
|
|
34
|
+
*
|
|
35
|
+
* spec 内的 console.log 只留在浏览器控制台,不会进 Node 进程输出,因此
|
|
36
|
+
* Playwright 报告中那条 `[keveGoal]` stdout 轨迹在 Cypress 下会整段缺失。
|
|
37
|
+
* 这里双写,让 stdout 字段两端同构。
|
|
38
|
+
*/
|
|
39
|
+
function logLine(line) {
|
|
40
|
+
console.log(line);
|
|
41
|
+
(0, state_js_1.addLog)(line);
|
|
42
|
+
}
|
|
43
|
+
function createCyKeveGoal(engine) {
|
|
44
|
+
return async (options, fn) => {
|
|
45
|
+
const state = (0, state_js_1.requireTestState)();
|
|
46
|
+
const order = (0, state_js_1.nextGoalOrder)();
|
|
47
|
+
const goalMeta = {
|
|
48
|
+
precondition: options.precondition,
|
|
49
|
+
step: options.step,
|
|
50
|
+
expected: options.expected,
|
|
51
|
+
order,
|
|
52
|
+
};
|
|
53
|
+
if (!keve_registry_js_1.sceneGoalsMap.has(state.title))
|
|
54
|
+
keve_registry_js_1.sceneGoalsMap.set(state.title, []);
|
|
55
|
+
keve_registry_js_1.sceneGoalsMap.get(state.title).push(goalMeta);
|
|
56
|
+
const ctx = {
|
|
57
|
+
// Cypress 下没有 Playwright Page;探索层用 engine.rawPage() 拿 CDP 驱动
|
|
58
|
+
page: engine.rawPage(),
|
|
59
|
+
engine,
|
|
60
|
+
step: options.step,
|
|
61
|
+
expected: options.expected,
|
|
62
|
+
precondition: options.precondition,
|
|
63
|
+
order,
|
|
64
|
+
testTitle: state.title,
|
|
65
|
+
specFilePath: state.file,
|
|
66
|
+
};
|
|
67
|
+
await keve_aspect_js_1.keveAspect.runPhase('before', ctx);
|
|
68
|
+
// ── goal-before 截图(动作发生前的页面原始状态) ──
|
|
69
|
+
let goalScreenshotBefore = '';
|
|
70
|
+
try {
|
|
71
|
+
goalScreenshotBefore = await engine.captureArtifact(`goal-before-${order}-${safeStepName(options.step)}`);
|
|
72
|
+
}
|
|
73
|
+
catch { /* 截图非关键路径 */ }
|
|
74
|
+
let fnError;
|
|
75
|
+
let fnErrorRaw;
|
|
76
|
+
let fnResult;
|
|
77
|
+
let fnBlocked = false;
|
|
78
|
+
const fnSource = fn?.toString() || undefined;
|
|
79
|
+
const owned = !(0, shotBuffer_js_1.isGoalShotDiffCaptureActive)();
|
|
80
|
+
if (owned)
|
|
81
|
+
(0, shotBuffer_js_1.beginGoalShotDiffCapture)();
|
|
82
|
+
if (fn) {
|
|
83
|
+
try {
|
|
84
|
+
fnResult = await fn();
|
|
85
|
+
logLine(`[keveGoal] "${options.step}" fn executed`);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
fnError = err?.message || String(err);
|
|
89
|
+
fnErrorRaw = err;
|
|
90
|
+
fnBlocked = (0, goal_heuristics_js_1.isEnvironmentBlockedError)(err);
|
|
91
|
+
logLine(`[keveGoal] "${options.step}" fn error: ${fnError}`);
|
|
92
|
+
// 页面级权限阻断:fn 报的是定位失败,根因是访问被拒 → 按 blocked 归因
|
|
93
|
+
if (!fnBlocked && fnError) {
|
|
94
|
+
try {
|
|
95
|
+
const blockedUrl = await engine.currentUrl();
|
|
96
|
+
if (/\/forbidden|\/unauthorized|\/403|\/no-permission/i.test(blockedUrl)) {
|
|
97
|
+
fnBlocked = true;
|
|
98
|
+
fnError = `页面访问被阻断(当前 URL=${blockedUrl}),目标元素未渲染 — 权限或数据问题,非用例缺陷`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch { /* URL 读不到则保持原错误 */ }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const shotDiffs = owned ? (0, shotBuffer_js_1.drainGoalShotDiff)() : [];
|
|
106
|
+
// ── fn-after 截图(执行后的页面状态) ──
|
|
107
|
+
let fnAfterScreenshot = '';
|
|
108
|
+
if (fn) {
|
|
109
|
+
try {
|
|
110
|
+
fnAfterScreenshot = await engine.captureArtifact(`goal-after-fn-${order}-${safeStepName(options.step)}`);
|
|
111
|
+
}
|
|
112
|
+
catch { /* 非关键 */ }
|
|
113
|
+
}
|
|
114
|
+
// ── 判定 ──
|
|
115
|
+
let reactResult;
|
|
116
|
+
if (fnBlocked) {
|
|
117
|
+
reactResult = {
|
|
118
|
+
expectedMet: false,
|
|
119
|
+
conclusion: 'blocked',
|
|
120
|
+
actions: [{
|
|
121
|
+
action: { tool: 'done', verdict: 'blocked', text: `Environment blocked: ${fnError}` },
|
|
122
|
+
toolOutput: '',
|
|
123
|
+
result: 'ok',
|
|
124
|
+
}],
|
|
125
|
+
agentScreenshots: [],
|
|
126
|
+
};
|
|
127
|
+
console.log(`[keveGoal] 🚫 "${options.step}" BLOCKED — 环境错误,跳过 Agent Re-Act`);
|
|
128
|
+
}
|
|
129
|
+
else if (fnErrorRaw?.name === 'ScreenshotDiffError') {
|
|
130
|
+
reactResult = {
|
|
131
|
+
expectedMet: false,
|
|
132
|
+
conclusion: 'fail',
|
|
133
|
+
actions: [{
|
|
134
|
+
action: { tool: 'done', verdict: 'fail', text: fnError },
|
|
135
|
+
toolOutput: fnErrorRaw.shotDiff?.message || '',
|
|
136
|
+
result: 'ok',
|
|
137
|
+
}],
|
|
138
|
+
agentScreenshots: [],
|
|
139
|
+
};
|
|
140
|
+
console.log(`[keveGoal] 📊 "${options.step}" 截图差异超阈值 — 确定性失败`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// fn 成功 / 失败都交给探索器(若引擎入口注册了 AI Re-Act)。
|
|
144
|
+
// fn 只是前置确定性校验,@keveGoal 的预期是否真正达成必须由 Agent
|
|
145
|
+
// 观察真实页面后给出最终结论 —— 即便 fn 已通过,也要产出推理轨迹与
|
|
146
|
+
// 执行后截图,否则报告里会缺少 AI 推理日志。
|
|
147
|
+
let explored;
|
|
148
|
+
if (explorer) {
|
|
149
|
+
try {
|
|
150
|
+
logLine(fnError
|
|
151
|
+
? `[keveGoal] 🤖 "${options.step}" fn failed — entering Agent Re-Act`
|
|
152
|
+
: `[keveGoal] 🤖 "${options.step}" fn succeeded — entering Agent Re-Act for final verdict`);
|
|
153
|
+
explored = await explorer(options, {
|
|
154
|
+
fnSource,
|
|
155
|
+
fnError,
|
|
156
|
+
goalScreenshotBefore: goalScreenshotBefore || undefined,
|
|
157
|
+
fnAfterScreenshot: fnAfterScreenshot || undefined,
|
|
158
|
+
specFilePath: state.file || undefined,
|
|
159
|
+
testTitle: state.title,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
// Cypress 下浏览器控制台不回流 Node stdout,必须同时写用例日志,否则
|
|
164
|
+
// 探索层异常在 CI 里完全不可见(报告只剩一个没有归因的 fail)。
|
|
165
|
+
logLine(`[keveGoal] ⚠️ "${options.step}" 探索异常:${err?.message || err}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
logLine(`[keveGoal] ℹ️ "${options.step}" 未注册探索器,跳过 Agent Re-Act`);
|
|
170
|
+
}
|
|
171
|
+
reactResult = explored || {
|
|
172
|
+
expectedMet: false,
|
|
173
|
+
conclusion: (0, goal_heuristics_js_1.isAssertionFailure)(fnError) ? 'fail' : 'fail',
|
|
174
|
+
actions: [{
|
|
175
|
+
action: { tool: 'done', verdict: 'fail', text: fnError },
|
|
176
|
+
toolOutput: '',
|
|
177
|
+
result: 'ok',
|
|
178
|
+
}],
|
|
179
|
+
agentScreenshots: [],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// ── 确定性覆盖:fn 断言失败不允许被 Agent 判成 pass ──
|
|
183
|
+
if (fnError && !fnBlocked && reactResult.expectedMet && (0, goal_heuristics_js_1.isAssertionFailure)(fnError)) {
|
|
184
|
+
console.log(`[keveGoal] ⚠️ "${options.step}" Agent 判 PASS 但 fn 断言失败 — 覆盖为 FAIL`);
|
|
185
|
+
reactResult.expectedMet = false;
|
|
186
|
+
reactResult.conclusion = 'fail';
|
|
187
|
+
}
|
|
188
|
+
const conclusion = reactResult.expectedMet ? 'pass' : (reactResult.conclusion || 'fail');
|
|
189
|
+
const success = reactResult.expectedMet;
|
|
190
|
+
const errorMessage = success
|
|
191
|
+
? undefined
|
|
192
|
+
: conclusion === 'blocked'
|
|
193
|
+
? `Blocked: ${fnBlocked ? fnError : (reactResult.actions || []).filter((a) => a.action?.tool === 'done').pop()?.action?.text || 'Agent blocked'}`
|
|
194
|
+
: fnErrorRaw?.name === 'ScreenshotDiffError'
|
|
195
|
+
? fnError
|
|
196
|
+
: fnError && (0, goal_heuristics_js_1.isAssertionFailure)(fnError)
|
|
197
|
+
? `Assertion failed: ${fnError}`
|
|
198
|
+
: `Expected not achieved: ${options.expected}`;
|
|
199
|
+
// 与 Playwright keve-test.ts 的终局日志同形,保证两端执行轨迹可逐行对照
|
|
200
|
+
logLine(success
|
|
201
|
+
? `[keveGoal] ✅ "${options.step}" PASSED`
|
|
202
|
+
: `[keveGoal] ❌ "${options.step}" FAILED (${conclusion}) — ${errorMessage}`);
|
|
203
|
+
// ── goal-after:优先取 agent done 时刻的截图,否则用 fn-after ──
|
|
204
|
+
let goalScreenshotAfter = '';
|
|
205
|
+
try {
|
|
206
|
+
const lastWithShot = [...(reactResult.actions || [])].reverse().find((a) => a.screenshotPath);
|
|
207
|
+
if (lastWithShot?.screenshotPath)
|
|
208
|
+
goalScreenshotAfter = String(lastWithShot.screenshotPath);
|
|
209
|
+
}
|
|
210
|
+
catch { /* 非关键 */ }
|
|
211
|
+
const goalResultPayload = {
|
|
212
|
+
step: options.step,
|
|
213
|
+
expected: options.expected,
|
|
214
|
+
precondition: options.precondition,
|
|
215
|
+
order,
|
|
216
|
+
success,
|
|
217
|
+
actions: (reactResult.actions || []).map((a) => ({
|
|
218
|
+
tool: a.action?.tool,
|
|
219
|
+
role: a.action?.role,
|
|
220
|
+
name: a.action?.name,
|
|
221
|
+
url: a.action?.url,
|
|
222
|
+
text: a.action?.text,
|
|
223
|
+
toolOutput: typeof a.toolOutput === 'string' ? a.toolOutput.slice(0, 500) : undefined,
|
|
224
|
+
reason: a.action?.reason,
|
|
225
|
+
success: a.action?.success,
|
|
226
|
+
verdict: a.action?.verdict,
|
|
227
|
+
conclusion: (0, goal_heuristics_js_1.deriveActionConclusion)(reactResult.conclusion, a.action),
|
|
228
|
+
result: a.result,
|
|
229
|
+
error: a.error,
|
|
230
|
+
evaluation: typeof a.evaluation === 'string' ? a.evaluation.slice(0, 300) : undefined,
|
|
231
|
+
memory: typeof a.memory === 'string' ? a.memory.slice(0, 200) : undefined,
|
|
232
|
+
nextGoal: typeof a.nextGoal === 'string' ? a.nextGoal.slice(0, 200) : undefined,
|
|
233
|
+
screenshotPath: a.screenshotPath,
|
|
234
|
+
})),
|
|
235
|
+
finalSnapshot: reactResult.finalSnapshot ? String(reactResult.finalSnapshot).slice(0, 500) : undefined,
|
|
236
|
+
diagnosticHints: reactResult.diagnosticHints || [],
|
|
237
|
+
diagnostics: reactResult.diagnostics,
|
|
238
|
+
goalScreenshotBefore: goalScreenshotBefore || undefined,
|
|
239
|
+
goalScreenshotAfter: goalScreenshotAfter || undefined,
|
|
240
|
+
// 注意不能写成 `|| [...]`:空数组是 truthy,会把 fn-after 兜底截图短路丢弃。
|
|
241
|
+
agentScreenshots: reactResult.agentScreenshots?.length
|
|
242
|
+
? reactResult.agentScreenshots
|
|
243
|
+
: (fnAfterScreenshot ? [fnAfterScreenshot] : []),
|
|
244
|
+
screenshotDiff: shotDiffs.length ? shotDiffs : undefined,
|
|
245
|
+
conclusion,
|
|
246
|
+
};
|
|
247
|
+
(0, state_js_1.addAttachment)({
|
|
248
|
+
name: 'keveGoalResult',
|
|
249
|
+
contentType: 'application/json',
|
|
250
|
+
// base64 编码:与 Playwright JSON reporter 的 attachment body 序列化格式一致
|
|
251
|
+
body: (0, state_js_1.encodeAttachmentBody)(JSON.stringify(goalResultPayload)),
|
|
252
|
+
});
|
|
253
|
+
const result = {
|
|
254
|
+
success,
|
|
255
|
+
conclusion,
|
|
256
|
+
actions: reactResult.actions || [],
|
|
257
|
+
finalSnapshot: reactResult.finalSnapshot,
|
|
258
|
+
error: success ? undefined : new Error(errorMessage),
|
|
259
|
+
};
|
|
260
|
+
await keve_aspect_js_1.keveAspect.runPhase('after', ctx, result, result.error);
|
|
261
|
+
if (!success)
|
|
262
|
+
throw result.error || new Error(`keveGoal "${options.step}" failed`);
|
|
263
|
+
return fnResult;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pageOps — 注入 AUT 的「操作原语 + 网络记录」源码
|
|
3
|
+
*
|
|
4
|
+
* 与 locatorSource.ts 同样以「自包含源码字符串」形式注入:isolated world 里的
|
|
5
|
+
* 函数无法闭包引用 bundle 变量,只能整段送过去求值。
|
|
6
|
+
*
|
|
7
|
+
* 职责边界:
|
|
8
|
+
* locatorSource → 定位(ElementRef / hop 链 → 元素集合)
|
|
9
|
+
* pageOps → 动作与读取(点击/输入/拖拽/文本/可见性/属性/滚动)
|
|
10
|
+
* netLog → fetch/XHR 记录,支撑 waitForResponse 的软等待门
|
|
11
|
+
*
|
|
12
|
+
* 交互走原生 DOM 事件(探针已验证 React 合成事件能正常响应),
|
|
13
|
+
* 与 Playwright 的 locator click 在「触发应用逻辑」这一层语义等价。
|
|
14
|
+
*/
|
|
15
|
+
export declare function pageOpsSource(): string;
|
|
16
|
+
/**
|
|
17
|
+
* 网络记录:waitForResponse 的就绪门依赖它。
|
|
18
|
+
* 在 AUT 里最早注入,之后所有 fetch/XHR 都会留下 {url, method, status, at}。
|
|
19
|
+
* 只记录元数据不记录 body —— 读 body 会消费流,影响被测应用自身逻辑。
|
|
20
|
+
*/
|
|
21
|
+
export declare function netLogSource(): string;
|
|
22
|
+
/** 三项注入合并(幂等,重复调用无副作用) */
|
|
23
|
+
export declare function bootstrapSource(locatorSrc: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* 运行诊断采集(AUT 主世界)。
|
|
26
|
+
*
|
|
27
|
+
* Cypress 侧不能订阅 CDP 域事件(`Cypress.automation` 只有 request/response,
|
|
28
|
+
* 没有 domain event 通道),所以这里在**主世界**劫持 console / 全局错误 /
|
|
29
|
+
* fetch / XHR,并用 PerformanceObserver 采集 LCP 与 longtask。
|
|
30
|
+
*
|
|
31
|
+
* 采集结果写入 `document.documentElement.dataset.keveDiagnostics`,因为
|
|
32
|
+
* isolated world 与主世界共享 DOM、但不共享 JS 全局,DOM 属性是唯一稳定的
|
|
33
|
+
* 跨世界只读通道。Agent 结束后由 isolated world 读一次快照即可。
|
|
34
|
+
*
|
|
35
|
+
* 约束:
|
|
36
|
+
* - 必须调用原始 console / fetch / XHR,避免递归采集;
|
|
37
|
+
* - 不读 body(会消费流,影响被测应用);
|
|
38
|
+
* - 文本与样本都做截断/限量,避免诊断本身撑爆报告;
|
|
39
|
+
* - 只写 DOM 属性,不改 `window.__keveNetLog`(waitForResponse 依赖它)。
|
|
40
|
+
*/
|
|
41
|
+
export declare function diagnosticsSource(): string;
|