pageqa 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -3
- package/dist/agent.d.ts +10 -0
- package/dist/agent.js +95 -106
- package/dist/agent.js.map +1 -1
- package/dist/bsk/tools.d.ts +35 -0
- package/dist/bsk/tools.js +210 -165
- package/dist/bsk/tools.js.map +1 -1
- package/dist/config.js +12 -3
- package/dist/config.js.map +1 -1
- package/dist/index.js +208 -13
- package/dist/index.js.map +1 -1
- package/dist/llm.js +23 -1
- package/dist/llm.js.map +1 -1
- package/dist/locator.d.ts +91 -0
- package/dist/locator.js +255 -0
- package/dist/locator.js.map +1 -0
- package/dist/record.d.ts +50 -0
- package/dist/record.js +167 -0
- package/dist/record.js.map +1 -0
- package/dist/replay.d.ts +190 -0
- package/dist/replay.js +548 -0
- package/dist/replay.js.map +1 -0
- package/dist/report.d.ts +40 -2
- package/dist/report.js +136 -3
- package/dist/report.js.map +1 -1
- package/dist/vars.d.ts +25 -0
- package/dist/vars.js +32 -0
- package/dist/vars.js.map +1 -1
- package/package.json +4 -4
package/dist/locator.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 元素定位符(Locator):把「这次点的是哪个元素」表达成跨快照可复用的语义描述。
|
|
3
|
+
*
|
|
4
|
+
* 背景:bsk 的 `@eN` 引用只在**产生它的那次快照**内有效,下一次 snapshot 编号会重排,
|
|
5
|
+
* 因此回放脚本不能直接存 `@eN`。bsk 快照文本是一棵缩进的 aria 树,每个可交互节点形如:
|
|
6
|
+
*
|
|
7
|
+
* @e1 link "Learn more" [→ iana.org]
|
|
8
|
+
*
|
|
9
|
+
* 其中的「角色 + 可访问名」(role + name)才是稳定的语义信息:页面微调后仍能重新命中。
|
|
10
|
+
* 本模块负责在**录制时**把 target 解析成定位符,并在**回放时**用新的快照把它解析回 `@eN`。
|
|
11
|
+
*
|
|
12
|
+
* 定位符不保证唯一:同名元素可能有多个(列表每行都有「操作」按钮、页面级与区域级各有一个
|
|
13
|
+
* 「操作」下拉)。因此还记录两样东西:
|
|
14
|
+
* - `nth`:录制时在所有同名候选中排第几;
|
|
15
|
+
* - `path`:录制时该节点的**祖先路径**(只保留有名字的祖先)。
|
|
16
|
+
* 回放时优先用祖先路径消歧——「同名第几个」在页面里同类元素数量变化后会指错,
|
|
17
|
+
* 而「在哪个区域下」是更强、更抗漂移的信号。
|
|
18
|
+
*/
|
|
19
|
+
/** 是否为 bsk 快照引用(`@e1` 或 `e1`)。回放时引用必须重新解析,CSS 选择器则可直接用。 */
|
|
20
|
+
export function isRef(target) {
|
|
21
|
+
return /^@?e\d+$/.test(target.trim());
|
|
22
|
+
}
|
|
23
|
+
/** 归一化引用写法:`e1` -> `@e1`。 */
|
|
24
|
+
export function normalizeRef(target) {
|
|
25
|
+
const t = target.trim();
|
|
26
|
+
return t.startsWith("@") ? t : "@" + t;
|
|
27
|
+
}
|
|
28
|
+
/** 缩进宽度(tab 按 2 空格计,bsk 用空格缩进,这里只是保险)。 */
|
|
29
|
+
function indentWidth(line) {
|
|
30
|
+
let n = 0;
|
|
31
|
+
for (const ch of line) {
|
|
32
|
+
if (ch === " ")
|
|
33
|
+
n += 1;
|
|
34
|
+
else if (ch === "\t")
|
|
35
|
+
n += 2;
|
|
36
|
+
else
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
return n;
|
|
40
|
+
}
|
|
41
|
+
/** 取出 `role "name"` 形式的角色与可访问名。 */
|
|
42
|
+
function parseRoleName(text) {
|
|
43
|
+
const roleMatch = text.match(/^([A-Za-z][A-Za-z0-9_-]*)/);
|
|
44
|
+
const nameMatch = text.match(/"([^"]*)"/);
|
|
45
|
+
return {
|
|
46
|
+
role: roleMatch ? roleMatch[1] : "",
|
|
47
|
+
name: nameMatch ? nameMatch[1] : "",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 路径条目的两个边界。
|
|
52
|
+
*
|
|
53
|
+
* 有些节点的可访问名是**整页文本**(如 `main "Dropdown 下拉菜单 将动作或菜单折叠到…"`),
|
|
54
|
+
* 直接存进脚本既撑大体积,又因为页面改一个字就失配而毫无消歧价值。
|
|
55
|
+
* 因此名字截断、层数只留最深的几层——深层(menu / tabpanel / dialog)才是真正区分区域的信息。
|
|
56
|
+
* 截断在录制与回放两侧执行同一套规则,所以比较依旧有效。
|
|
57
|
+
*/
|
|
58
|
+
const PATH_NAME_MAX = 60;
|
|
59
|
+
const PATH_DEPTH_MAX = 3;
|
|
60
|
+
/** 路径节点(有名字的祖先)的可读写法。 */
|
|
61
|
+
function pathLabel(role, name) {
|
|
62
|
+
const shown = name.length > PATH_NAME_MAX
|
|
63
|
+
? name.slice(0, PATH_NAME_MAX - 3) + "..."
|
|
64
|
+
: name;
|
|
65
|
+
return `${role || "?"} ${JSON.stringify(shown)}`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 解析 bsk 快照文本里所有带引用的行,得到「引用 -> 角色/可访问名/祖先路径」。
|
|
69
|
+
*
|
|
70
|
+
* 缩进即层级:维护一个缩进栈,遇到更浅或同级的新行就弹出更深/同级的节点,
|
|
71
|
+
* 因此每个引用都能拿到「它在哪个区域(tabpanel / dialog / 某个标题)之下」。
|
|
72
|
+
* `@vom` / `@view` / `@layers` / `L1 page` 等元信息行不参与树。
|
|
73
|
+
*/
|
|
74
|
+
export function parseSnapshotRefs(snapshotText) {
|
|
75
|
+
const refs = [];
|
|
76
|
+
const stack = [];
|
|
77
|
+
for (const line of snapshotText.split(/\r?\n/)) {
|
|
78
|
+
const body = line.trim();
|
|
79
|
+
if (!body)
|
|
80
|
+
continue;
|
|
81
|
+
if (body.startsWith("@vom") ||
|
|
82
|
+
body.startsWith("@view") ||
|
|
83
|
+
body.startsWith("@layers") ||
|
|
84
|
+
/^L\d+\b/.test(body)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const indent = indentWidth(line);
|
|
88
|
+
const refMatch = body.match(/^@e(\d+)\b\s*(.*)$/);
|
|
89
|
+
const { role, name } = parseRoleName(refMatch ? refMatch[2] : body);
|
|
90
|
+
// 弹出所有「缩进 >= 当前」的节点,剩下的就是本行的祖先
|
|
91
|
+
while (stack.length > 0 && stack[stack.length - 1].indent >= indent) {
|
|
92
|
+
stack.pop();
|
|
93
|
+
}
|
|
94
|
+
if (refMatch) {
|
|
95
|
+
refs.push({
|
|
96
|
+
ref: `@e${refMatch[1]}`,
|
|
97
|
+
role,
|
|
98
|
+
name,
|
|
99
|
+
path: stack
|
|
100
|
+
.map((e) => e.label)
|
|
101
|
+
.filter((l) => l !== null)
|
|
102
|
+
.slice(-PATH_DEPTH_MAX),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
stack.push({
|
|
106
|
+
indent,
|
|
107
|
+
label: name ? pathLabel(role, name) : null,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return refs;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 录制时构造定位符:从当时的快照里查出该引用对应的角色/可访问名、同名序号与祖先路径。
|
|
114
|
+
*
|
|
115
|
+
* target 是 CSS 选择器(或快照里查不到的引用)时,定位符退化为「只有 target 兜底」,
|
|
116
|
+
* 回放时直接按原 target 操作。
|
|
117
|
+
*/
|
|
118
|
+
export function buildLocator(target, snapshotText) {
|
|
119
|
+
const original = target.trim();
|
|
120
|
+
const bare = {
|
|
121
|
+
role: "",
|
|
122
|
+
name: "",
|
|
123
|
+
nth: -1,
|
|
124
|
+
target: original,
|
|
125
|
+
path: [],
|
|
126
|
+
};
|
|
127
|
+
if (!isRef(original))
|
|
128
|
+
return bare;
|
|
129
|
+
const ref = normalizeRef(original);
|
|
130
|
+
const refs = parseSnapshotRefs(snapshotText);
|
|
131
|
+
const self = refs.find((r) => r.ref === ref);
|
|
132
|
+
// 快照里查不到(例如模型用了一次更早快照的引用):没有语义信息可用,只能留 target 兜底
|
|
133
|
+
if (!self)
|
|
134
|
+
return bare;
|
|
135
|
+
if (!self.role && !self.name)
|
|
136
|
+
return bare;
|
|
137
|
+
const same = refs.filter((r) => r.role === self.role && r.name === self.name);
|
|
138
|
+
const nth = same.findIndex((r) => r.ref === ref);
|
|
139
|
+
return {
|
|
140
|
+
role: self.role,
|
|
141
|
+
name: self.name,
|
|
142
|
+
nth,
|
|
143
|
+
target: original,
|
|
144
|
+
path: self.path,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 祖先路径的匹配度:从最深一级往上数,连续相同的层数。
|
|
149
|
+
* 只比尾部——页面结构微调通常发生在中间层,根节点往往没变。
|
|
150
|
+
*/
|
|
151
|
+
function pathScore(want, got) {
|
|
152
|
+
let score = 0;
|
|
153
|
+
const depth = Math.min(want.length, got.length);
|
|
154
|
+
for (let i = 1; i <= depth; i++) {
|
|
155
|
+
if (want[want.length - i] !== got[got.length - i])
|
|
156
|
+
break;
|
|
157
|
+
score += 1;
|
|
158
|
+
}
|
|
159
|
+
return score;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 回放时用**新的**快照把定位符解析回 `@eN`。
|
|
163
|
+
*
|
|
164
|
+
* 匹配策略逐级放宽,都不中则返回 null(由调用方决定退回 target 还是报错):
|
|
165
|
+
* 1. role + name 全等(最常见,页面没变时必然命中)
|
|
166
|
+
* 2. role 相同且 name 包含(文案被加了前后缀,如「操作」→「操作 更多」)
|
|
167
|
+
* 3. 只看 name(角色被前端改过,如 button 变 a)
|
|
168
|
+
*
|
|
169
|
+
* 命中多个候选时,先按**祖先路径**消歧(「结构」tab 下的「操作」与产品级的「操作」
|
|
170
|
+
* 名字完全一样,只有所属区域能区分),路径也无法区分时才退回录制的同名序号 `nth`。
|
|
171
|
+
*/
|
|
172
|
+
export function resolveLocator(locator, snapshotText) {
|
|
173
|
+
const name = locator.name;
|
|
174
|
+
const role = locator.role;
|
|
175
|
+
if (!name && !role)
|
|
176
|
+
return null;
|
|
177
|
+
const refs = parseSnapshotRefs(snapshotText);
|
|
178
|
+
const exact = refs.filter((r) => r.role === role && r.name === name);
|
|
179
|
+
const fuzzy = exact.length
|
|
180
|
+
? exact
|
|
181
|
+
: refs.filter((r) => r.role === role && name.length > 0 && r.name.includes(name));
|
|
182
|
+
const pool = fuzzy.length
|
|
183
|
+
? fuzzy
|
|
184
|
+
: refs.filter((r) => name.length > 0 && r.name === name);
|
|
185
|
+
if (!pool.length)
|
|
186
|
+
return null;
|
|
187
|
+
if (pool.length === 1)
|
|
188
|
+
return pool[0].ref;
|
|
189
|
+
const path = locator.path ?? [];
|
|
190
|
+
if (path.length > 0) {
|
|
191
|
+
const scored = pool
|
|
192
|
+
.map((r) => ({ ref: r.ref, score: pathScore(path, r.path) }))
|
|
193
|
+
.filter((c) => c.score > 0)
|
|
194
|
+
.sort((a, b) => b.score - a.score);
|
|
195
|
+
if (scored.length > 0) {
|
|
196
|
+
const best = scored[0].score;
|
|
197
|
+
const top = scored.filter((c) => c.score === best);
|
|
198
|
+
if (top.length === 1)
|
|
199
|
+
return top[0].ref;
|
|
200
|
+
return top[locator.nth >= 0 && locator.nth < top.length ? locator.nth : 0]
|
|
201
|
+
.ref;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const idx = locator.nth >= 0 && locator.nth < pool.length ? locator.nth : 0;
|
|
205
|
+
return pool[idx].ref;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* 定位不到时给出「当前页面里有什么」的线索。
|
|
209
|
+
*
|
|
210
|
+
* 「元素未找到」本身是条死路,用户只知道某步挂了。区分三种情况后,报告能直接回答
|
|
211
|
+
* 「是弹窗没打开」「是菜单点错了一个」还是「元素改名了」,用户据此就能判断该改脚本还是改用例。
|
|
212
|
+
*/
|
|
213
|
+
export function locatorHint(locator, snapshotText, limit = 3) {
|
|
214
|
+
const refs = parseSnapshotRefs(snapshotText);
|
|
215
|
+
const sameRole = refs.filter((r) => r.role === locator.role);
|
|
216
|
+
const head = locator.name.slice(0, 2);
|
|
217
|
+
const similar = head
|
|
218
|
+
? sameRole.filter((r) => r.name.includes(head))
|
|
219
|
+
: [];
|
|
220
|
+
if (similar.length > 0) {
|
|
221
|
+
return {
|
|
222
|
+
kind: "similar-name",
|
|
223
|
+
items: similar.slice(0, limit).map((r) => pathLabel(r.role, r.name)),
|
|
224
|
+
roleCount: sameRole.length,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (sameRole.length === 0) {
|
|
228
|
+
return { kind: "no-role", items: [], roleCount: 0 };
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
kind: "role-only",
|
|
232
|
+
items: sameRole
|
|
233
|
+
.filter((r) => r.name)
|
|
234
|
+
.slice(0, limit)
|
|
235
|
+
.map((r) => pathLabel(r.role, r.name)),
|
|
236
|
+
roleCount: sameRole.length,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/** 定位符的可读描述,用于报告与日志(说明「当时点的是什么、在哪个区域」)。 */
|
|
240
|
+
export function describeLocator(locator) {
|
|
241
|
+
if (!locator)
|
|
242
|
+
return "无定位符";
|
|
243
|
+
const parts = [];
|
|
244
|
+
if (locator.role)
|
|
245
|
+
parts.push(`role=${locator.role}`);
|
|
246
|
+
if (locator.name)
|
|
247
|
+
parts.push(`name=${JSON.stringify(locator.name)}`);
|
|
248
|
+
if (locator.nth > 0)
|
|
249
|
+
parts.push(`第 ${locator.nth + 1} 个同名元素`);
|
|
250
|
+
const area = locator.path?.at(-1);
|
|
251
|
+
if (area)
|
|
252
|
+
parts.push(`位于 ${area} 之下`);
|
|
253
|
+
return parts.length ? parts.join(" ") : `target=${locator.target}`;
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../src/locator.ts"],"names":[],"mappings":"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"}
|
package/dist/record.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReplayStep } from "./replay.js";
|
|
2
|
+
import { type RunVarValue } from "./vars.js";
|
|
3
|
+
/** 工具层上报的一次执行(ok=false 表示 bsk 报错,录制时会被忽略)。 */
|
|
4
|
+
export interface ToolExecEvent {
|
|
5
|
+
/** 工具名:navigate / snapshot / click / fill / upload / hover / scroll / wait / assert_text。 */
|
|
6
|
+
name: string;
|
|
7
|
+
/** 模型给出的入参。 */
|
|
8
|
+
params: Record<string, unknown>;
|
|
9
|
+
ok: boolean;
|
|
10
|
+
/** 本次操作前页面最后一次快照文本(用于构造语义定位符)。 */
|
|
11
|
+
lastSnapshot: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RecorderOptions {
|
|
14
|
+
/**
|
|
15
|
+
* 本次带模型的运行是否启用了 Jev 语义断言。
|
|
16
|
+
*
|
|
17
|
+
* 需要记下来:Jev 允许模型用「标题包含 Example」这种语义化措辞做断言,
|
|
18
|
+
* 而回放默认是字符串包含匹配,这类断言在回放时会误报失败。
|
|
19
|
+
* 脚本里标记该断言是语义录制的,回放失败时才能给出「加 --semantic 重试」的提示。
|
|
20
|
+
*/
|
|
21
|
+
semantic?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare class Recorder {
|
|
24
|
+
private readonly vars;
|
|
25
|
+
private readonly steps;
|
|
26
|
+
private lastCompletedStep;
|
|
27
|
+
/** 整场运行是否解析到过步骤自述;一次都没解析到时不给步骤号(见 recorded)。 */
|
|
28
|
+
private sawNarration;
|
|
29
|
+
/** 自述的尾部缓冲,用于跨 delta 匹配(流式输出会把「完成」切开)。 */
|
|
30
|
+
private tail;
|
|
31
|
+
private readonly semantic;
|
|
32
|
+
constructor(vars?: RunVarValue[], options?: RecorderOptions);
|
|
33
|
+
/**
|
|
34
|
+
* 喂入模型输出的文本增量,跟踪「已完成到第几步」。
|
|
35
|
+
*
|
|
36
|
+
* 模型是先调用工具、再自述「第 k 步完成:…」,因此某个工具调用归属哪一步只能用
|
|
37
|
+
* 「它之前最后一次自述 + 1」来近似——映射不准时退化为 null,不影响回放正确性。
|
|
38
|
+
*/
|
|
39
|
+
noteText(delta: string): void;
|
|
40
|
+
/** 记录一次工具执行。仅成功的操作会进入脚本;`snapshot` 不记录(回放不需要「看」)。 */
|
|
41
|
+
noteTool(event: ToolExecEvent): void;
|
|
42
|
+
/**
|
|
43
|
+
* 当前已录制的步骤快照。
|
|
44
|
+
*
|
|
45
|
+
* 整场运行一次都没解析到步骤自述时,把步骤号一律留空:
|
|
46
|
+
* 全部指向「第 1 步」是自信的错误,报告会指着用例第一行说「卡在这里」,
|
|
47
|
+
* 比如实留空更误导。
|
|
48
|
+
*/
|
|
49
|
+
get recorded(): ReplayStep[];
|
|
50
|
+
}
|
package/dist/record.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 录制层:把一次带模型的运行里**成功执行过的浏览器操作**记成可回放的步骤。
|
|
3
|
+
*
|
|
4
|
+
* 两个关键判断:
|
|
5
|
+
* - 只记成功的操作。失败的尝试是模型探索过程的一部分(它随后会重试别的元素),
|
|
6
|
+
* 记下来只会让回放照着踩坑。
|
|
7
|
+
* - 记语义定位符而不是 `@eN`。`@eN` 只在当时那次快照内有效(见 locator.ts)。
|
|
8
|
+
*/
|
|
9
|
+
import { buildLocator } from "./locator.js";
|
|
10
|
+
import { restorePlaceholders } from "./vars.js";
|
|
11
|
+
/**
|
|
12
|
+
* 从模型输出的自述里取「第 k 步完成」,用于把操作映射回用例步骤号。
|
|
13
|
+
*
|
|
14
|
+
* 模型写法并不统一,实测见过「第 3 步完成」「第3步已完成」「步骤 3 完成」「步骤 3:完成」。
|
|
15
|
+
* 只认第一种会让整场运行的步骤映射全部落到第 1 步——比映射不出来更糟(报告会指错用例行)。
|
|
16
|
+
*/
|
|
17
|
+
const STEP_DONE_PATTERNS = [
|
|
18
|
+
/第\s*(\d+)\s*步\s*(?:已)?\s*完\s*成/g,
|
|
19
|
+
/步\s*骤\s*(\d+)\s*(?:已)?\s*完\s*成/g,
|
|
20
|
+
];
|
|
21
|
+
/** 自述可能被流式输出切在「完/成」之间,保留一小段尾巴拼回来再匹配。 */
|
|
22
|
+
const NARRATION_TAIL = 64;
|
|
23
|
+
export class Recorder {
|
|
24
|
+
vars;
|
|
25
|
+
steps = [];
|
|
26
|
+
lastCompletedStep = 0;
|
|
27
|
+
/** 整场运行是否解析到过步骤自述;一次都没解析到时不给步骤号(见 recorded)。 */
|
|
28
|
+
sawNarration = false;
|
|
29
|
+
/** 自述的尾部缓冲,用于跨 delta 匹配(流式输出会把「完成」切开)。 */
|
|
30
|
+
tail = "";
|
|
31
|
+
semantic;
|
|
32
|
+
constructor(vars = [], options = {}) {
|
|
33
|
+
this.vars = vars;
|
|
34
|
+
this.semantic = options.semantic ?? false;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 喂入模型输出的文本增量,跟踪「已完成到第几步」。
|
|
38
|
+
*
|
|
39
|
+
* 模型是先调用工具、再自述「第 k 步完成:…」,因此某个工具调用归属哪一步只能用
|
|
40
|
+
* 「它之前最后一次自述 + 1」来近似——映射不准时退化为 null,不影响回放正确性。
|
|
41
|
+
*/
|
|
42
|
+
noteText(delta) {
|
|
43
|
+
// 在「上一段尾巴 + 本次增量」上匹配:只取已完成的步骤号(取最大值),
|
|
44
|
+
// 因此重叠窗口造成的重复命中无害。
|
|
45
|
+
const window = this.tail + delta;
|
|
46
|
+
for (const pattern of STEP_DONE_PATTERNS) {
|
|
47
|
+
pattern.lastIndex = 0;
|
|
48
|
+
let m;
|
|
49
|
+
while ((m = pattern.exec(window)) !== null) {
|
|
50
|
+
this.sawNarration = true;
|
|
51
|
+
this.lastCompletedStep = Math.max(this.lastCompletedStep, Number(m[1]));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this.tail = window.slice(-NARRATION_TAIL);
|
|
55
|
+
}
|
|
56
|
+
/** 记录一次工具执行。仅成功的操作会进入脚本;`snapshot` 不记录(回放不需要「看」)。 */
|
|
57
|
+
noteTool(event) {
|
|
58
|
+
if (!event.ok)
|
|
59
|
+
return;
|
|
60
|
+
const step = this.lastCompletedStep + 1;
|
|
61
|
+
const p = event.params ?? {};
|
|
62
|
+
const text = (key) => String(p[key] ?? "");
|
|
63
|
+
const restore = (value) => restorePlaceholders(value, this.vars);
|
|
64
|
+
/** target + 定位符(target 为空时不做定位符解析)。 */
|
|
65
|
+
const targetOf = () => {
|
|
66
|
+
const target = restore(text("target").trim());
|
|
67
|
+
const locator = target
|
|
68
|
+
? buildLocator(target, event.lastSnapshot)
|
|
69
|
+
: null;
|
|
70
|
+
return {
|
|
71
|
+
target,
|
|
72
|
+
// 定位符的「可访问名」同样要还原占位符:列表里点的是「刚刚创建的那条」
|
|
73
|
+
// (如 `自动化测试产品${timestamp}`),名字里若写死录制当次的时间戳,
|
|
74
|
+
// 回放时新时间戳对不上,这一步必然定位失败。
|
|
75
|
+
locator: locator
|
|
76
|
+
? { ...locator, name: restore(locator.name) }
|
|
77
|
+
: null,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
switch (event.name) {
|
|
81
|
+
case "navigate":
|
|
82
|
+
this.steps.push({
|
|
83
|
+
kind: "navigate",
|
|
84
|
+
step,
|
|
85
|
+
url: restore(text("url")),
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
case "click":
|
|
89
|
+
case "hover":
|
|
90
|
+
case "scroll": {
|
|
91
|
+
const { target, locator } = targetOf();
|
|
92
|
+
if (!target)
|
|
93
|
+
return;
|
|
94
|
+
this.steps.push({ kind: event.name, step, target, locator });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
case "fill": {
|
|
98
|
+
const { target, locator } = targetOf();
|
|
99
|
+
if (!target)
|
|
100
|
+
return;
|
|
101
|
+
const raw = text("value");
|
|
102
|
+
const value = restore(raw);
|
|
103
|
+
this.steps.push({
|
|
104
|
+
kind: "fill",
|
|
105
|
+
step,
|
|
106
|
+
target,
|
|
107
|
+
value,
|
|
108
|
+
// 占位符被还原时留一份「录制当次真正填了什么」,便于人工核对
|
|
109
|
+
...(value !== raw ? { recordedValue: raw } : {}),
|
|
110
|
+
locator,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
case "upload": {
|
|
115
|
+
const { target, locator } = targetOf();
|
|
116
|
+
this.steps.push({
|
|
117
|
+
kind: "upload",
|
|
118
|
+
step,
|
|
119
|
+
file: restore(text("file")),
|
|
120
|
+
...(target ? { target, locator } : { locator: null }),
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
case "wait": {
|
|
125
|
+
const ms = Number(p["ms"]);
|
|
126
|
+
this.steps.push({
|
|
127
|
+
kind: "wait",
|
|
128
|
+
step,
|
|
129
|
+
ms: Number.isFinite(ms) && ms > 0 ? ms : 1000,
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
case "assert_text": {
|
|
134
|
+
const raw = text("expectation");
|
|
135
|
+
if (!raw)
|
|
136
|
+
return;
|
|
137
|
+
const expectation = restore(raw);
|
|
138
|
+
this.steps.push({
|
|
139
|
+
kind: "assert_text",
|
|
140
|
+
step,
|
|
141
|
+
expectation,
|
|
142
|
+
...(expectation !== raw ? { recordedExpectation: raw } : {}),
|
|
143
|
+
// 语义录制的断言在字符串匹配下可能误报,标记出来供回放给出提示
|
|
144
|
+
...(this.semantic ? { semantic: true } : {}),
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
default:
|
|
149
|
+
// snapshot 以及未知工具:不进入回放脚本
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 当前已录制的步骤快照。
|
|
155
|
+
*
|
|
156
|
+
* 整场运行一次都没解析到步骤自述时,把步骤号一律留空:
|
|
157
|
+
* 全部指向「第 1 步」是自信的错误,报告会指着用例第一行说「卡在这里」,
|
|
158
|
+
* 比如实留空更误导。
|
|
159
|
+
*/
|
|
160
|
+
get recorded() {
|
|
161
|
+
const steps = [...this.steps];
|
|
162
|
+
if (this.sawNarration)
|
|
163
|
+
return steps;
|
|
164
|
+
return steps.map((s) => ({ ...s, step: null }));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"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"}
|