@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.
- 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 +652 -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,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pngCodec — 纯 Node PNG 编解码(零依赖,zlib 内置)
|
|
4
|
+
*
|
|
5
|
+
* 为什么不用 pngjs/pixelmatch:@kkeve/suite 依赖树里没有这两个包,
|
|
6
|
+
* 执行环境(keve-wiki / 调度节点)也不保证能现场安装 —— 而引擎侧截图对比
|
|
7
|
+
* 是核心链路,不能带可选依赖。Playwright 截图恒为 8-bit、非交错、
|
|
8
|
+
* colorType 2(RGB)/6(RGBA),覆盖这两类 + 灰度(0/4)即可覆盖全部引擎产物。
|
|
9
|
+
*
|
|
10
|
+
* 支持范围(超出范围抛错,不静默出错误结果):
|
|
11
|
+
* - bitDepth: 8
|
|
12
|
+
* - colorType: 0(灰度) / 2(RGB) / 4(灰+A) / 6(RGBA)
|
|
13
|
+
* - interlace: 0(Progressive PNG 抛错)
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.decodePng = decodePng;
|
|
50
|
+
exports.encodePng = encodePng;
|
|
51
|
+
const zlib = __importStar(require("node:zlib"));
|
|
52
|
+
// ── CRC32 ──
|
|
53
|
+
const CRC_TABLE = (() => {
|
|
54
|
+
const t = new Uint32Array(256);
|
|
55
|
+
for (let n = 0; n < 256; n++) {
|
|
56
|
+
let c = n;
|
|
57
|
+
for (let k = 0; k < 8; k++)
|
|
58
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
59
|
+
t[n] = c >>> 0;
|
|
60
|
+
}
|
|
61
|
+
return t;
|
|
62
|
+
})();
|
|
63
|
+
function crc32(buf) {
|
|
64
|
+
let c = 0xffffffff;
|
|
65
|
+
for (let i = 0; i < buf.length; i++)
|
|
66
|
+
c = CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
|
|
67
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
68
|
+
}
|
|
69
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
70
|
+
/** Paeth 预测器(PNG 规范 §6.4) */
|
|
71
|
+
function paeth(a, b, c) {
|
|
72
|
+
const p = a + b - c;
|
|
73
|
+
const pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
|
|
74
|
+
return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
|
|
75
|
+
}
|
|
76
|
+
/** 解码 PNG → RGBA。仅支持 8-bit / colorType 0,2,4,6 / 非交错 */
|
|
77
|
+
function decodePng(buf) {
|
|
78
|
+
if (buf.length < 8 || !buf.subarray(0, 8).equals(PNG_SIGNATURE)) {
|
|
79
|
+
throw new Error('pngCodec: 不是合法的 PNG 文件(签名不符)');
|
|
80
|
+
}
|
|
81
|
+
let width = 0, height = 0, bitDepth = 0, colorType = -1, interlace = 0;
|
|
82
|
+
const idatParts = [];
|
|
83
|
+
let pos = 8;
|
|
84
|
+
while (pos + 8 <= buf.length) {
|
|
85
|
+
const len = buf.readUInt32BE(pos);
|
|
86
|
+
const type = buf.toString('ascii', pos + 4, pos + 8);
|
|
87
|
+
const data = buf.subarray(pos + 8, pos + 8 + len);
|
|
88
|
+
pos += 12 + len; // length + type + data + crc
|
|
89
|
+
if (type === 'IHDR') {
|
|
90
|
+
width = data.readUInt32BE(0);
|
|
91
|
+
height = data.readUInt32BE(4);
|
|
92
|
+
bitDepth = data[8];
|
|
93
|
+
colorType = data[9];
|
|
94
|
+
interlace = data[12];
|
|
95
|
+
}
|
|
96
|
+
else if (type === 'IDAT') {
|
|
97
|
+
idatParts.push(Buffer.from(data));
|
|
98
|
+
}
|
|
99
|
+
else if (type === 'IEND') {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!width || !height)
|
|
104
|
+
throw new Error('pngCodec: IHDR 缺失或非法');
|
|
105
|
+
if (bitDepth !== 8)
|
|
106
|
+
throw new Error(`pngCodec: 不支持 bitDepth=${bitDepth}(仅支持 8,Playwright 截图恒为 8)`);
|
|
107
|
+
if (interlace !== 0)
|
|
108
|
+
throw new Error('pngCodec: 不支持交错 PNG(Progressive)');
|
|
109
|
+
const channelsByColor = { 0: 1, 2: 3, 4: 2, 6: 4 };
|
|
110
|
+
const channels = channelsByColor[colorType];
|
|
111
|
+
if (!channels)
|
|
112
|
+
throw new Error(`pngCodec: 不支持 colorType=${colorType}(仅支持 0/2/4/6)`);
|
|
113
|
+
const raw = zlib.inflateSync(Buffer.concat(idatParts));
|
|
114
|
+
const stride = width * channels;
|
|
115
|
+
if (raw.length < (stride + 1) * height) {
|
|
116
|
+
throw new Error(`pngCodec: IDAT 数据不足(期望 ${(stride + 1) * height} 字节,实际 ${raw.length})`);
|
|
117
|
+
}
|
|
118
|
+
// 反滤波(逐扫描线)
|
|
119
|
+
const out = new Uint8Array(width * height * 4);
|
|
120
|
+
const prev = new Uint8Array(stride);
|
|
121
|
+
const cur = new Uint8Array(stride);
|
|
122
|
+
for (let y = 0; y < height; y++) {
|
|
123
|
+
const rowStart = y * (stride + 1);
|
|
124
|
+
const filter = raw[rowStart];
|
|
125
|
+
for (let i = 0; i < stride; i++) {
|
|
126
|
+
const x = raw[rowStart + 1 + i];
|
|
127
|
+
const a = i >= channels ? cur[i - channels] : 0;
|
|
128
|
+
const b = prev[i];
|
|
129
|
+
const c = i >= channels ? prev[i - channels] : 0;
|
|
130
|
+
let v;
|
|
131
|
+
switch (filter) {
|
|
132
|
+
case 0:
|
|
133
|
+
v = x;
|
|
134
|
+
break;
|
|
135
|
+
case 1:
|
|
136
|
+
v = x + a;
|
|
137
|
+
break;
|
|
138
|
+
case 2:
|
|
139
|
+
v = x + b;
|
|
140
|
+
break;
|
|
141
|
+
case 3:
|
|
142
|
+
v = x + ((a + b) >> 1);
|
|
143
|
+
break;
|
|
144
|
+
case 4:
|
|
145
|
+
v = x + paeth(a, b, c);
|
|
146
|
+
break;
|
|
147
|
+
default: throw new Error(`pngCodec: 非法滤波类型 ${filter}(行 ${y})`);
|
|
148
|
+
}
|
|
149
|
+
cur[i] = v & 0xff;
|
|
150
|
+
}
|
|
151
|
+
// 展开为 RGBA
|
|
152
|
+
const o = y * width * 4;
|
|
153
|
+
for (let x = 0; x < width; x++) {
|
|
154
|
+
const s = x * channels;
|
|
155
|
+
if (colorType === 6) {
|
|
156
|
+
out[o + x * 4] = cur[s];
|
|
157
|
+
out[o + x * 4 + 1] = cur[s + 1];
|
|
158
|
+
out[o + x * 4 + 2] = cur[s + 2];
|
|
159
|
+
out[o + x * 4 + 3] = cur[s + 3];
|
|
160
|
+
}
|
|
161
|
+
else if (colorType === 2) {
|
|
162
|
+
out[o + x * 4] = cur[s];
|
|
163
|
+
out[o + x * 4 + 1] = cur[s + 1];
|
|
164
|
+
out[o + x * 4 + 2] = cur[s + 2];
|
|
165
|
+
out[o + x * 4 + 3] = 255;
|
|
166
|
+
}
|
|
167
|
+
else if (colorType === 0) {
|
|
168
|
+
out[o + x * 4] = out[o + x * 4 + 1] = out[o + x * 4 + 2] = cur[s];
|
|
169
|
+
out[o + x * 4 + 3] = 255;
|
|
170
|
+
}
|
|
171
|
+
else { // 4: 灰+alpha
|
|
172
|
+
out[o + x * 4] = out[o + x * 4 + 1] = out[o + x * 4 + 2] = cur[s];
|
|
173
|
+
out[o + x * 4 + 3] = cur[s + 1];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
prev.set(cur);
|
|
177
|
+
}
|
|
178
|
+
return { width, height, data: out };
|
|
179
|
+
}
|
|
180
|
+
// ── Encode ──
|
|
181
|
+
function chunk(type, data) {
|
|
182
|
+
const len = Buffer.alloc(4);
|
|
183
|
+
len.writeUInt32BE(data.length, 0);
|
|
184
|
+
const typeBuf = Buffer.from(type, 'ascii');
|
|
185
|
+
const crc = Buffer.alloc(4);
|
|
186
|
+
crc.writeUInt32BE(crc32(Buffer.concat([typeBuf, data])), 0);
|
|
187
|
+
return Buffer.concat([len, typeBuf, data, crc]);
|
|
188
|
+
}
|
|
189
|
+
/** RGBA → PNG(colorType 6,filter 0)。data 长度必须为 w*h*4 */
|
|
190
|
+
function encodePng(width, height, data) {
|
|
191
|
+
if (data.length !== width * height * 4) {
|
|
192
|
+
throw new Error(`pngCodec: encodePng 数据长度不符(期望 ${width * height * 4},实际 ${data.length})`);
|
|
193
|
+
}
|
|
194
|
+
const ihdr = Buffer.alloc(13);
|
|
195
|
+
ihdr.writeUInt32BE(width, 0);
|
|
196
|
+
ihdr.writeUInt32BE(height, 4);
|
|
197
|
+
ihdr[8] = 8; // bitDepth
|
|
198
|
+
ihdr[9] = 6; // colorType RGBA
|
|
199
|
+
ihdr[10] = 0; // compression
|
|
200
|
+
ihdr[11] = 0; // filter
|
|
201
|
+
ihdr[12] = 0; // interlace
|
|
202
|
+
// filter 0 逐行打包
|
|
203
|
+
const raw = Buffer.alloc((width * 4 + 1) * height);
|
|
204
|
+
for (let y = 0; y < height; y++) {
|
|
205
|
+
raw[y * (width * 4 + 1)] = 0;
|
|
206
|
+
Buffer.from(data.buffer, data.byteOffset + y * width * 4, width * 4)
|
|
207
|
+
.copy(raw, y * (width * 4 + 1) + 1);
|
|
208
|
+
}
|
|
209
|
+
return Buffer.concat([
|
|
210
|
+
PNG_SIGNATURE,
|
|
211
|
+
chunk('IHDR', ihdr),
|
|
212
|
+
chunk('IDAT', zlib.deflateSync(raw, { level: 6 })),
|
|
213
|
+
chunk('IEND', Buffer.alloc(0)),
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* screenshotDiff — 截图基线对比(平台「截图基线」断言的引擎侧实现)
|
|
3
|
+
*
|
|
4
|
+
* 判定语义(对齐《keve 统一用例工作台与场景插件完整方案》§2 视觉优先修正 + §9.5 首跑闭环):
|
|
5
|
+
* 无基线 → status 'baseline_required':保存候选基线(state=candidate),
|
|
6
|
+
* 首跑**不算通过**,差异率为 null,需人工审核转正后再次运行才做正式比对
|
|
7
|
+
* 表里已有候选 → status 'baseline_required':未审核候选不得作为正式有效基线,
|
|
8
|
+
* 不做像素比对、不判 matched/exceeded(差异率 null)
|
|
9
|
+
* 截图尺寸变化 → status 'size_mismatch':不得因 viewport 变化自动重新自举来掩盖变化;
|
|
10
|
+
* 按原 key+env 定位既有基线并明确报告尺寸差异
|
|
11
|
+
* diffRatio ≤ 阈值 → status 'matched':静默通过,仅记录证据(仅对 state=active 的基线)
|
|
12
|
+
* diffRatio > 阈值 → status 'exceeded':抛 ScreenshotDiffError(确定性事实,AI 不可翻案;
|
|
13
|
+
* keveGoal 对该错误短路跳过 Agent Re-Act —— diff 不是探索能修复的)
|
|
14
|
+
*
|
|
15
|
+
* 基线存储:<KEVE_BASELINE_DIR|cwd/baselines>/{key}.{env}.{w}x{h}.png + .meta.json
|
|
16
|
+
* meta: { version, state: 'candidate'|'active', sourceType, threshold, masks, updatedAt }
|
|
17
|
+
* key 建议沿用老平台基线 id(如 step-24497),可回溯迁移来源。
|
|
18
|
+
*
|
|
19
|
+
* 证据产物:actual 截图与 diff 热区图落盘 test-artifacts,路径写入结果,
|
|
20
|
+
* 由 keveGoalResult attachment 流到平台 stepsDetail 回收(evidence.screenshotDiff)。
|
|
21
|
+
*/
|
|
22
|
+
export interface ShotDiffRect {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
w: number;
|
|
26
|
+
h: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ShotDiffRegion extends ShotDiffRect {
|
|
29
|
+
/** 区块内差异像素占区块像素比例(%) */
|
|
30
|
+
ratio: number;
|
|
31
|
+
}
|
|
32
|
+
export interface ShotDiffResult {
|
|
33
|
+
key: string;
|
|
34
|
+
/** matched=比对一致(active 基线)|exceeded=超阈值(抛错)|baseline_required=无基线/候选未审核(不算通过)|size_mismatch=尺寸变化不自举 */
|
|
35
|
+
status: 'matched' | 'exceeded' | 'baseline_required' | 'size_mismatch';
|
|
36
|
+
/** 差异率(%,全图像素口径);baseline_required / size_mismatch 时为 null(不是 0) */
|
|
37
|
+
diffRatio: number | null;
|
|
38
|
+
threshold: number;
|
|
39
|
+
baselineVersion: number;
|
|
40
|
+
baselineState?: 'candidate' | 'active';
|
|
41
|
+
baselinePath: string;
|
|
42
|
+
/** 当前实际截图(绝对路径) */
|
|
43
|
+
actualPath: string;
|
|
44
|
+
/** 差异热区图(绝对路径,status=created 时与 actualPath 相同) */
|
|
45
|
+
diffPath: string;
|
|
46
|
+
env: string;
|
|
47
|
+
viewport: string;
|
|
48
|
+
regions: ShotDiffRegion[];
|
|
49
|
+
/** 供报告/AI 复核的一句话结论 */
|
|
50
|
+
message: string;
|
|
51
|
+
comparedAt: string;
|
|
52
|
+
}
|
|
53
|
+
export interface ExpectScreenshotOptions {
|
|
54
|
+
/** 差异率阈值(%),缺省取 KEVE_SHOT_DIFF_THRESHOLD(默认 1.0) */
|
|
55
|
+
threshold?: number;
|
|
56
|
+
/** 全页截图(默认视口截图) */
|
|
57
|
+
fullPage?: boolean;
|
|
58
|
+
/** 临时掩码(本次生效;持久掩码在基线 meta.masks) */
|
|
59
|
+
mask?: ShotDiffRect[];
|
|
60
|
+
}
|
|
61
|
+
/** 超阈值错误:携带完整对比结果,keveGoal 据此短路并透传 attachment */
|
|
62
|
+
export declare class ScreenshotDiffError extends Error {
|
|
63
|
+
shotDiff: ShotDiffResult;
|
|
64
|
+
constructor(result: ShotDiffResult);
|
|
65
|
+
}
|
|
66
|
+
export declare function baselineDir(): string;
|
|
67
|
+
export interface BaselinePaths {
|
|
68
|
+
pngPath: string;
|
|
69
|
+
metaPath: string;
|
|
70
|
+
env: string;
|
|
71
|
+
viewport: string;
|
|
72
|
+
width: number;
|
|
73
|
+
height: number;
|
|
74
|
+
}
|
|
75
|
+
/** 基线路径 = {safeKey}.{env}.{w}x{h} —— env×viewport 进 key,避免跨环境/跨视口假差异 */
|
|
76
|
+
export declare function resolveBaselinePaths(key: string, width: number, height: number): BaselinePaths;
|
|
77
|
+
/**
|
|
78
|
+
* 按 key+env 查找已存在基线(任意视口)。
|
|
79
|
+
* 命中表示「该目标曾建档」——此时若精确视口不存在,属于**截图尺寸变化**,
|
|
80
|
+
* 不允许重新自举成新候选来掩盖变化(方案 §9.4 / A20)。
|
|
81
|
+
*/
|
|
82
|
+
export declare function findBaselineByKeyEnv(key: string, preferWidth?: number, preferHeight?: number): BaselinePaths | null;
|
|
83
|
+
export interface BaselineMeta {
|
|
84
|
+
version: number;
|
|
85
|
+
state: 'candidate' | 'active';
|
|
86
|
+
sourceType?: 'first-run' | 'imported' | 'manual';
|
|
87
|
+
sourceUrl?: string;
|
|
88
|
+
threshold: number;
|
|
89
|
+
masks: ShotDiffRect[];
|
|
90
|
+
updatedAt: string;
|
|
91
|
+
}
|
|
92
|
+
/** 无基线:保存候选基线,返回 created(判 PASS) */
|
|
93
|
+
export declare function createBaseline(key: string, png: Buffer, opts?: {
|
|
94
|
+
sourceUrl?: string;
|
|
95
|
+
threshold?: number;
|
|
96
|
+
}): ShotDiffResult;
|
|
97
|
+
/**
|
|
98
|
+
* 有基线:像素对比 → matched(≤阈值,pass)/ exceeded(>阈值,抛 ScreenshotDiffError)。
|
|
99
|
+
* 尺寸不一致按重叠区对比并在 message 中注明(视口不变的常规场景不受影响)。
|
|
100
|
+
*/
|
|
101
|
+
export declare function compareWithBaseline(key: string, png: Buffer, opts?: {
|
|
102
|
+
threshold?: number;
|
|
103
|
+
mask?: ShotDiffRect[];
|
|
104
|
+
}): ShotDiffResult;
|
|
105
|
+
/** keveGoal 执行 fn 前调用:开始收集本 goal 内的对比结果 */
|
|
106
|
+
export declare function beginGoalShotDiffCapture(): void;
|
|
107
|
+
/** 当前是否处于 goal 收集期(engine 判断是否需要自开临时缓冲) */
|
|
108
|
+
export declare function isGoalShotDiffCaptureActive(): boolean;
|
|
109
|
+
/** keveGoal fn 结束(无论成败)后调用:取出本 goal 的全部对比结果 */
|
|
110
|
+
export declare function drainGoalShotDiff(): ShotDiffResult[];
|
|
111
|
+
/** engine.expectScreenshot 内部调用:记录到当前 goal 缓冲(若在收集期) */
|
|
112
|
+
export declare function recordShotDiff(r: ShotDiffResult): void;
|