pageqa 0.6.0 → 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 +57 -99
- package/dist/agent.js.map +1 -1
- package/dist/bsk/tools.d.ts +35 -0
- package/dist/bsk/tools.js +130 -79
- package/dist/bsk/tools.js.map +1 -1
- package/dist/index.js +176 -9
- package/dist/index.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 +31 -1
- package/dist/report.js +120 -1
- 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 +2 -2
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { type BskOps } from "./bsk/tools.js";
|
|
2
|
+
import { type Locator } from "./locator.js";
|
|
3
|
+
import { type AssertionResult, type TestReport, type TokenUsage } from "./report.js";
|
|
4
|
+
export declare const REPLAY_FORMAT = "pageqa-replay";
|
|
5
|
+
export declare const REPLAY_VERSION = 1;
|
|
6
|
+
/** 回放步骤的类型。`snapshot` 不在其中:它只服务于模型的「观察」,回放不需要。 */
|
|
7
|
+
export type ReplayStepKind = "navigate" | "click" | "fill" | "upload" | "hover" | "scroll" | "wait" | "assert_text";
|
|
8
|
+
/** 需要定位元素的步骤(click/hover/scroll 共用)。 */
|
|
9
|
+
export interface ReplayTargetStep {
|
|
10
|
+
kind: "click" | "hover" | "scroll";
|
|
11
|
+
/** 尽力映射到的用例步骤号(1 起);映射不到为 null。 */
|
|
12
|
+
step: number | null;
|
|
13
|
+
/** 录制时模型给出的 target(`@eN` 或 CSS)。 */
|
|
14
|
+
target: string;
|
|
15
|
+
/** 语义定位符;target 是 CSS 或快照中查不到时为 null。 */
|
|
16
|
+
locator: Locator | null;
|
|
17
|
+
}
|
|
18
|
+
export interface ReplayFillStep {
|
|
19
|
+
kind: "fill";
|
|
20
|
+
step: number | null;
|
|
21
|
+
target: string;
|
|
22
|
+
/** 填入值(已把运行时变量还原成占位符写法)。 */
|
|
23
|
+
value: string;
|
|
24
|
+
/** 占位符被还原时,记录录制当次真正填入的具体值,便于人工核对。 */
|
|
25
|
+
recordedValue?: string;
|
|
26
|
+
locator: Locator | null;
|
|
27
|
+
}
|
|
28
|
+
export interface ReplayUploadStep {
|
|
29
|
+
kind: "upload";
|
|
30
|
+
step: number | null;
|
|
31
|
+
file: string;
|
|
32
|
+
target?: string;
|
|
33
|
+
locator: Locator | null;
|
|
34
|
+
}
|
|
35
|
+
export interface ReplayNavigateStep {
|
|
36
|
+
kind: "navigate";
|
|
37
|
+
step: number | null;
|
|
38
|
+
url: string;
|
|
39
|
+
}
|
|
40
|
+
export interface ReplayWaitStep {
|
|
41
|
+
kind: "wait";
|
|
42
|
+
step: number | null;
|
|
43
|
+
ms: number;
|
|
44
|
+
}
|
|
45
|
+
export interface ReplayAssertStep {
|
|
46
|
+
kind: "assert_text";
|
|
47
|
+
step: number | null;
|
|
48
|
+
/** 断言期望(已把运行时变量还原成占位符写法)。 */
|
|
49
|
+
expectation: string;
|
|
50
|
+
/** 占位符被还原时,记录录制当次实际用的字面量。 */
|
|
51
|
+
recordedExpectation?: string;
|
|
52
|
+
/** 录制时该断言由 Jev 语义判断得出(字符串匹配可能误报,回放失败时会给出提示)。 */
|
|
53
|
+
semantic?: boolean;
|
|
54
|
+
}
|
|
55
|
+
export type ReplayStep = ReplayTargetStep | ReplayFillStep | ReplayUploadStep | ReplayNavigateStep | ReplayWaitStep | ReplayAssertStep;
|
|
56
|
+
/** 一个场景的录制结果(套件模式下每个 `## 场景` 一条)。 */
|
|
57
|
+
export interface ScenarioRecording {
|
|
58
|
+
name: string;
|
|
59
|
+
/** 用例原文步骤(忽略 `#`/`>` 行),用于把回放步骤映回「用例第 k 步」。 */
|
|
60
|
+
caseSteps: string[];
|
|
61
|
+
steps: ReplayStep[];
|
|
62
|
+
}
|
|
63
|
+
/** 回放脚本文件结构。 */
|
|
64
|
+
export interface ReplayScript {
|
|
65
|
+
format: typeof REPLAY_FORMAT;
|
|
66
|
+
version: number;
|
|
67
|
+
recordedAt: string;
|
|
68
|
+
source: {
|
|
69
|
+
/** 源用例文件路径;内联文本时为 null。 */
|
|
70
|
+
path: string | null;
|
|
71
|
+
/** 源用例内容哈希,用于回放时提示「源用例已变更」。 */
|
|
72
|
+
hash: string | null;
|
|
73
|
+
};
|
|
74
|
+
scenarios: ReplayScenario[];
|
|
75
|
+
}
|
|
76
|
+
export interface ReplayScenario {
|
|
77
|
+
name: string;
|
|
78
|
+
caseSteps: string[];
|
|
79
|
+
steps: ReplayStep[];
|
|
80
|
+
}
|
|
81
|
+
/** 源用例内容哈希(sha256 前 16 位十六进制,够用且便于人眼比对)。 */
|
|
82
|
+
export declare function scriptHash(text: string): string;
|
|
83
|
+
/** 把录制结果组装成脚本对象(不含文件 IO,便于单测)。 */
|
|
84
|
+
export declare function buildReplayScript(recordings: ScenarioRecording[], meta?: {
|
|
85
|
+
sourcePath?: string | null;
|
|
86
|
+
sourceText?: string | null;
|
|
87
|
+
}): ReplayScript;
|
|
88
|
+
/** 写入回放脚本文件(自动创建父目录)。 */
|
|
89
|
+
export declare function writeReplayScript(path: string, script: ReplayScript): void;
|
|
90
|
+
/** 读取并校验回放脚本文件;结构不合法时抛出可读错误(而不是回放中途才崩)。 */
|
|
91
|
+
export declare function loadReplayScript(path: string): ReplayScript;
|
|
92
|
+
/**
|
|
93
|
+
* 把脚本里**写死的录制取值**还原成占位符,返回实际发生的替换(供日志)。
|
|
94
|
+
*
|
|
95
|
+
* 为什么需要:`locator.name`(列表里点「刚创建的那条」)与 `caseSteps` 里都可能残留
|
|
96
|
+
* 录制当次的具体时间戳,回放时新时间戳对不上,这一步必然定位失败。
|
|
97
|
+
* 新录制的脚本已经不会再写死(录制时就还原了),但**旧脚本不必重跑 LLM 重录**——
|
|
98
|
+
* 脚本自己带着 `recorded*` 证据,加载时即可就地修好。
|
|
99
|
+
*
|
|
100
|
+
* 只改写已知会承载「动态名称」的字段:用例原文、定位符名、填入值、断言期望。
|
|
101
|
+
* 刻意不动 `file`(本地文件路径必须原样存在)与 `target`/`url`(可能是有意的字面量)。
|
|
102
|
+
*/
|
|
103
|
+
export declare function normalizePlaceholderLiterals(script: ReplayScript): string[];
|
|
104
|
+
/**
|
|
105
|
+
* 检查源用例是否已变更:变更时返回提示文本(由调用方决定如何处理)。
|
|
106
|
+
* 只警告不失败——用例文案变了不代表页面行为变了,硬失败会天天误报。
|
|
107
|
+
*/
|
|
108
|
+
export declare function sourceDriftNotice(script: ReplayScript): string | null;
|
|
109
|
+
export interface ReplayOptions {
|
|
110
|
+
session?: string;
|
|
111
|
+
/** 启用 Jev 语义断言(默认关闭,保证回放不调用任何模型)。 */
|
|
112
|
+
semantic?: boolean;
|
|
113
|
+
debug?: boolean;
|
|
114
|
+
/** 脚本路径,写进报告便于追溯。 */
|
|
115
|
+
scriptPath?: string;
|
|
116
|
+
/** 回放时刻,用于重新展开 `${...}` 占位符(同一次回放共用一个时刻)。 */
|
|
117
|
+
now?: Date;
|
|
118
|
+
/** 任一失败(含元素未找到)即停止该场景,不跑完剩余步骤。 */
|
|
119
|
+
failFast?: boolean;
|
|
120
|
+
}
|
|
121
|
+
export interface ReplayRunResult {
|
|
122
|
+
report: TestReport;
|
|
123
|
+
text: string;
|
|
124
|
+
json: string;
|
|
125
|
+
transcript: string;
|
|
126
|
+
usage: TokenUsage;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 当前页面里找不到要操作的元素。
|
|
130
|
+
*
|
|
131
|
+
* 单独区分出来,是因为它与「操作失败」不是一回事:元素不存在说明**当前页面状态下这一步不需要**
|
|
132
|
+
* ——最典型的是录制期模型顺手点的「取 消」这类补救/清理动作(提交后弹窗没关,模型点取消补一下),
|
|
133
|
+
* 回放时页面更顺利、弹窗早已关闭,那个按钮根本不存在。
|
|
134
|
+
* 这类步骤按「跳过」处理并继续,而不是让一条 65 步的长流程在第 12 步整条报废。
|
|
135
|
+
*/
|
|
136
|
+
export declare class LocatorMissError extends Error {
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 场景结论:**任一断言不成立、任一失败步骤、或中途中止** → fail。
|
|
140
|
+
*
|
|
141
|
+
* 单独抽成纯函数是因为它曾经写错过一次:只统计「抛出的步骤失败」而漏掉
|
|
142
|
+
* 「断言返回不成立」,结果一条带 FAIL 断言的用例报了 PASS 与非零之外的退出码——
|
|
143
|
+
* 假通过比报错危险得多,这里用单测钉死。
|
|
144
|
+
* 跳过(元素未找到)不影响结论:页面真的退化时,后续步骤或断言会暴露出来。
|
|
145
|
+
*/
|
|
146
|
+
export declare function replayScenarioStatus(outcome: {
|
|
147
|
+
failed: number;
|
|
148
|
+
aborted: string | null;
|
|
149
|
+
assertions: AssertionResult[];
|
|
150
|
+
}): "pass" | "fail";
|
|
151
|
+
/** 单场景的步骤执行结果(纯执行结果,不含 session 生命周期与报告组装)。 */
|
|
152
|
+
export interface ReplayStepOutcome {
|
|
153
|
+
trace: string[];
|
|
154
|
+
assertions: AssertionResult[];
|
|
155
|
+
outputs: string[];
|
|
156
|
+
/** 因「元素未找到」被跳过的步骤描述(不算失败,但必须让人看见)。 */
|
|
157
|
+
skipped: string[];
|
|
158
|
+
/** 已成功执行的最后一步序号。 */
|
|
159
|
+
executed: number;
|
|
160
|
+
/** 失败(不含跳过)的步骤数。 */
|
|
161
|
+
failed: number;
|
|
162
|
+
/** 中止时的步骤标签;未中止为 null。 */
|
|
163
|
+
aborted: string | null;
|
|
164
|
+
}
|
|
165
|
+
export interface ReplayStepOptions {
|
|
166
|
+
/** 展开 `${...}` 占位符。 */
|
|
167
|
+
expand: (text: string) => string;
|
|
168
|
+
/** 本次回放是否启用了 Jev 语义断言。 */
|
|
169
|
+
jevActive: boolean;
|
|
170
|
+
/** 任一失败即停止该场景(默认关:跑完才能给出完整健康报告)。 */
|
|
171
|
+
failFast?: boolean;
|
|
172
|
+
/** 单步重试次数(默认 3)。 */
|
|
173
|
+
attempts?: number;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 按顺序执行一个场景的全部步骤(不碰 session 生命周期,便于单测)。
|
|
177
|
+
*
|
|
178
|
+
* 失败语义:
|
|
179
|
+
* - **元素未找到** → 记为「跳过」并继续。这是「当前页面状态下这一步不需要」,
|
|
180
|
+
* 而非页面退化的证据;真正的退化会由后续步骤或断言暴露出来。
|
|
181
|
+
* - **其它失败**(元素找到了但操作报错、断言不成立)→ 记为失败并继续,跑完给出全貌。
|
|
182
|
+
* - **navigate 失败** → 后续步骤已无意义,直接中止。
|
|
183
|
+
* 失败一律不会被吞掉:都会变成报告里的一条 FAIL 断言 + 非零退出码。
|
|
184
|
+
*/
|
|
185
|
+
export declare function executeReplaySteps(ops: BskOps, scenario: ReplayScenario, opts: ReplayStepOptions): Promise<ReplayStepOutcome>;
|
|
186
|
+
/**
|
|
187
|
+
* 回放整个脚本:单场景按单份报告输出;多场景逐个回放(各自独立 session/窗口)并汇总,
|
|
188
|
+
* 汇总语义与 `--suite` 一致——任一场景失败则整体失败、退出码非零。
|
|
189
|
+
*/
|
|
190
|
+
export declare function runReplayScript(script: ReplayScript, opts?: ReplayOptions): Promise<ReplayRunResult>;
|