@yule-h2o/web_auto_flow 0.0.5
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/LICENSE +0 -0
- package/README.md +156 -0
- package/dist/core/ai-engine/AITest.d.ts +27 -0
- package/dist/core/ai-engine/assertions/anomaly.d.ts +6 -0
- package/dist/core/ai-engine/assertions/extraction.d.ts +6 -0
- package/dist/core/ai-engine/assertions/quality.d.ts +6 -0
- package/dist/core/ai-engine/assertions/sentiment.d.ts +6 -0
- package/dist/core/ai-engine/llm-client.d.ts +24 -0
- package/dist/core/ai-engine/prompts.d.ts +3 -0
- package/dist/core/ai-engine/types.d.ts +102 -0
- package/dist/core/rule-engine/RuleTest.d.ts +36 -0
- package/dist/core/rule-engine/assertions/accessibility.d.ts +9 -0
- package/dist/core/rule-engine/assertions/content.d.ts +9 -0
- package/dist/core/rule-engine/assertions/element.d.ts +2 -0
- package/dist/core/rule-engine/assertions/navigation.d.ts +13 -0
- package/dist/core/rule-engine/assertions/network.d.ts +9 -0
- package/dist/core/rule-engine/assertions/performance.d.ts +11 -0
- package/dist/core/rule-engine/selectors.d.ts +17 -0
- package/dist/core/rule-engine/types.d.ts +208 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +2491 -0
- package/dist/share/bootstrap.d.ts +14 -0
- package/dist/share/compose.d.ts +131 -0
- package/dist/share/html-reporter.d.ts +2 -0
- package/dist/share/network.d.ts +7 -0
- package/dist/share/reporter.d.ts +13 -0
- package/dist/share/types.d.ts +51 -0
- package/dist/share/user-config.d.ts +33 -0
- package/package.json +46 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Stagehand } from "@browserbasehq/stagehand";
|
|
2
|
+
import type { PlanDefinition } from "./compose";
|
|
3
|
+
export interface BrowserOptions {
|
|
4
|
+
stagehand?: {
|
|
5
|
+
model?: string;
|
|
6
|
+
headless?: boolean;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export declare function createChromeProfile(baseDir: string): string;
|
|
10
|
+
export declare function createBrowser(baseDir: string, options?: BrowserOptions): Promise<{
|
|
11
|
+
stagehand: Stagehand;
|
|
12
|
+
page: import("playwright-core").Page;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function runPlan(plan: PlanDefinition, options?: BrowserOptions): Promise<import("./types").TestReport>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { PageLike } from "../core/rule-engine/types";
|
|
2
|
+
import type { RuleResult } from "../core/rule-engine/types";
|
|
3
|
+
import type { AIResult } from "../core/ai-engine/types";
|
|
4
|
+
import type { TestReport, TestContextOptions } from "./types";
|
|
5
|
+
import { RuleTest } from "../core/rule-engine/RuleTest";
|
|
6
|
+
import { AITest } from "../core/ai-engine/AITest";
|
|
7
|
+
/**
|
|
8
|
+
* 可自动执行的 RuleTest
|
|
9
|
+
*
|
|
10
|
+
* 用法:
|
|
11
|
+
* await check("名称").element("input").performance("FCP")
|
|
12
|
+
* // ↑ 链式构建 ↑
|
|
13
|
+
* // ↑ await 触发 .run(page)
|
|
14
|
+
*/
|
|
15
|
+
declare class AutoRuleTest extends RuleTest {
|
|
16
|
+
private _page;
|
|
17
|
+
private _onResult;
|
|
18
|
+
constructor(name: string, page: PageLike, onResult: (r: RuleResult) => void);
|
|
19
|
+
element: (selector: string, opts?: import("../core/rule-engine/types").ElementAssertionOptions) => this;
|
|
20
|
+
text: (selector: string, expected: string | RegExp, opts?: import("../core/rule-engine/types").TextAssertionOptions) => this;
|
|
21
|
+
attribute: (selector: string, attribute: string, expected: string | RegExp, opts?: import("../core/rule-engine/types").AttributeAssertionOptions) => this;
|
|
22
|
+
url: (expected: string | RegExp, opts?: import("../core/rule-engine/types").NavigationAssertionOptions) => this;
|
|
23
|
+
title: (expected: string | RegExp, opts?: import("../core/rule-engine/types").NavigationAssertionOptions) => this;
|
|
24
|
+
meta: (metaName: string, expected: string | RegExp, opts?: import("../core/rule-engine/types").NavigationAssertionOptions) => this;
|
|
25
|
+
status: (urlOrPattern: string | RegExp, expectedStatus: number, opts?: import("../core/rule-engine/types").NetworkAssertionOptions) => this;
|
|
26
|
+
performance: (metric: import("../core/rule-engine/assertions/performance").PerfMetric, opts?: import("../core/rule-engine/types").PerformanceAssertionOptions, customValue?: number) => this;
|
|
27
|
+
accessibility: (selector: string, opts?: import("../core/rule-engine/types").AccessibilityAssertionOptions) => this;
|
|
28
|
+
custom: (label: string, fn: (page: PageLike) => Promise<Omit<import("./types").AssertionResult, "type">>) => this;
|
|
29
|
+
then<TResult1 = RuleResult, TResult2 = never>(onfulfilled?: ((value: RuleResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 可自动执行的 AITest
|
|
33
|
+
*
|
|
34
|
+
* 用法:
|
|
35
|
+
* await inspect("名称").anomaly().sentiment(".msg", "positive")
|
|
36
|
+
*/
|
|
37
|
+
declare class AutoAITest extends AITest {
|
|
38
|
+
private _page;
|
|
39
|
+
private _onResult;
|
|
40
|
+
constructor(name: string, page: PageLike, onResult: (r: AIResult) => void, stagehand: any);
|
|
41
|
+
anomaly: (opts?: import("../core/ai-engine/types").AnomalyOptions) => this;
|
|
42
|
+
sentiment: (selector: string, expected: "positive" | "negative" | "neutral", criteria?: string) => this;
|
|
43
|
+
quality: (selector: string, opts: import("../core/ai-engine/types").QualityOptions) => this;
|
|
44
|
+
then<TResult1 = AIResult, TResult2 = never>(onfulfilled?: ((value: AIResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
45
|
+
}
|
|
46
|
+
/** 网络抓包器 */
|
|
47
|
+
export interface CapturedResponse {
|
|
48
|
+
url: string;
|
|
49
|
+
status: number;
|
|
50
|
+
headers: Record<string, string>;
|
|
51
|
+
body: string;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
}
|
|
54
|
+
export interface NetworkRecorder {
|
|
55
|
+
getCalls: () => CapturedResponse[];
|
|
56
|
+
filter: (urlPattern: string | RegExp) => CapturedResponse[];
|
|
57
|
+
assertCall: (urlPattern: string | RegExp, opts?: {
|
|
58
|
+
status?: number;
|
|
59
|
+
bodySchema?: Record<string, string>;
|
|
60
|
+
bodyContains?: string;
|
|
61
|
+
}) => {
|
|
62
|
+
passed: boolean;
|
|
63
|
+
message: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export interface TestContext {
|
|
67
|
+
/** 计划名称 */
|
|
68
|
+
name: string;
|
|
69
|
+
/** Playwright Page */
|
|
70
|
+
page: PageLike;
|
|
71
|
+
/** Stagehand 实例 */
|
|
72
|
+
stagehand: any;
|
|
73
|
+
/** 元素是否在页面上 */
|
|
74
|
+
has: (selector: string) => Promise<boolean>;
|
|
75
|
+
/** 一组选择器哪个在页面上,返回索引;都不在返回 -1 */
|
|
76
|
+
which: (selectors: string[]) => Promise<number>;
|
|
77
|
+
/** 点击第一个/第 n 个可见元素 */
|
|
78
|
+
click: (selector: string, nth?: number) => Promise<void>;
|
|
79
|
+
/** 填充输入框 */
|
|
80
|
+
fill: (selector: string, value: string) => Promise<void>;
|
|
81
|
+
/** 截图(自动编号) */
|
|
82
|
+
screenshot: (label: string) => Promise<void>;
|
|
83
|
+
/** 等待 */
|
|
84
|
+
sleep: (ms: number) => Promise<void>;
|
|
85
|
+
/** AI 交互(act + anomaly),失败自动降级 */
|
|
86
|
+
ai: (instruction: string) => Promise<void>;
|
|
87
|
+
/** 等待元素出现(超时 10s) */
|
|
88
|
+
wait: (selector: string, timeout?: number) => Promise<void>;
|
|
89
|
+
/** Rule Engine 断言 */
|
|
90
|
+
check: (name: string) => AutoRuleTest;
|
|
91
|
+
/** 抓包 */
|
|
92
|
+
network: () => NetworkRecorder;
|
|
93
|
+
/** 编排任务:原生 + AI Fallback */
|
|
94
|
+
task: (label: string, fn: (page: PageLike) => Promise<void>, aiFallback?: string) => Promise<boolean>;
|
|
95
|
+
inspect: (name: string) => AutoAITest;
|
|
96
|
+
}
|
|
97
|
+
export declare function createTestContext(page: PageLike, options?: TestContextOptions): TestContext;
|
|
98
|
+
export interface PlanDefinition {
|
|
99
|
+
/** 运行测试,返回报告 */
|
|
100
|
+
run: (page: PageLike, options?: {
|
|
101
|
+
stagehand?: any;
|
|
102
|
+
outputDir?: string;
|
|
103
|
+
}) => Promise<TestReport>;
|
|
104
|
+
/** 计划名称 */
|
|
105
|
+
name: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 定义测试计划
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* export default definePlan("PWA 登录", async ({ page, has, click, fill, screenshot, check, ai }) => {
|
|
113
|
+
* await page.goto("https://example.com/login");
|
|
114
|
+
*
|
|
115
|
+
* await check("登录页元素")
|
|
116
|
+
* .element("input", { minCount: 2 })
|
|
117
|
+
* .performance("FCP", { lt: 3000 });
|
|
118
|
+
*
|
|
119
|
+
* await fill('input[type="email"]', "test@test.com");
|
|
120
|
+
* await click('[type="submit"]');
|
|
121
|
+
* await screenshot("登录后");
|
|
122
|
+
*
|
|
123
|
+
* await check("登录结果")
|
|
124
|
+
* .url("/dashboard");
|
|
125
|
+
* });
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function definePlan(name: string, setup: (ctx: TestContext) => Promise<void>): PlanDefinition;
|
|
129
|
+
/** 判断当前文件是否作为入口直接运行 */
|
|
130
|
+
export declare function isMain(metaUrl: string): boolean;
|
|
131
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RuleResult, AIResult, TestReport } from "./types";
|
|
2
|
+
export declare class Reporter {
|
|
3
|
+
private planName;
|
|
4
|
+
private startedAt;
|
|
5
|
+
private screenshots;
|
|
6
|
+
constructor(planName: string);
|
|
7
|
+
/** 构建报告 */
|
|
8
|
+
build(ruleResults: RuleResult[], aiResults: AIResult[], actionTotal: number, actionFailed: number): TestReport;
|
|
9
|
+
private buildRuleLayer;
|
|
10
|
+
private buildAILayer;
|
|
11
|
+
}
|
|
12
|
+
/** 在控制台打印报告摘要 */
|
|
13
|
+
export declare function printReport(report: TestReport): void;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { RuleResult, AssertionResult, PageLike } from "../core/rule-engine/types";
|
|
2
|
+
import type { AIResult, AIAssertionResult } from "../core/ai-engine/types";
|
|
3
|
+
export type { RuleResult, AssertionResult, AIResult, AIAssertionResult, PageLike };
|
|
4
|
+
/** 单层报告摘要 */
|
|
5
|
+
export interface LayerSummary {
|
|
6
|
+
passed: number;
|
|
7
|
+
failed: number;
|
|
8
|
+
skipped: number;
|
|
9
|
+
total: number;
|
|
10
|
+
passRate: number;
|
|
11
|
+
duration: number;
|
|
12
|
+
}
|
|
13
|
+
/** 失败项 */
|
|
14
|
+
export interface FailureItem {
|
|
15
|
+
layer: "rule" | "ai" | "action";
|
|
16
|
+
testName: string;
|
|
17
|
+
assertionName: string;
|
|
18
|
+
message: string;
|
|
19
|
+
details?: string;
|
|
20
|
+
}
|
|
21
|
+
/** 测试报告 */
|
|
22
|
+
export interface TestReport {
|
|
23
|
+
planName: string;
|
|
24
|
+
startedAt: string;
|
|
25
|
+
summary: {
|
|
26
|
+
total: number;
|
|
27
|
+
passed: number;
|
|
28
|
+
failed: number;
|
|
29
|
+
skipped: number;
|
|
30
|
+
duration: number;
|
|
31
|
+
costEstimate: number;
|
|
32
|
+
};
|
|
33
|
+
layers: {
|
|
34
|
+
rule: LayerSummary;
|
|
35
|
+
ai: LayerSummary | null;
|
|
36
|
+
actions: {
|
|
37
|
+
total: number;
|
|
38
|
+
failed: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
failures: FailureItem[];
|
|
42
|
+
artifacts: {
|
|
43
|
+
screenshots: string[];
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** 上下文配置 */
|
|
47
|
+
export interface TestContextOptions {
|
|
48
|
+
stagehand?: any;
|
|
49
|
+
outputDir?: string;
|
|
50
|
+
planName?: string;
|
|
51
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface H2OUserConfig {
|
|
2
|
+
/** AI 配置 */
|
|
3
|
+
ai?: {
|
|
4
|
+
/** API Key */
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
/** 模型标识 */
|
|
7
|
+
model?: string;
|
|
8
|
+
/** 请求超时 (ms) */
|
|
9
|
+
timeout?: number;
|
|
10
|
+
/** 温度 */
|
|
11
|
+
temperature?: number;
|
|
12
|
+
/** 最大 token */
|
|
13
|
+
maxTokens?: number;
|
|
14
|
+
};
|
|
15
|
+
/** 输出配置 */
|
|
16
|
+
output?: {
|
|
17
|
+
/** 日志/截图/报告 输出根目录,默认 "./logs" */
|
|
18
|
+
dir?: string;
|
|
19
|
+
};
|
|
20
|
+
/** 浏览器配置 */
|
|
21
|
+
browser?: {
|
|
22
|
+
/** 无头模式 */
|
|
23
|
+
headless?: boolean;
|
|
24
|
+
/** 视口 */
|
|
25
|
+
viewport?: {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** 定义配置(纯类型助手,不做运行时处理) */
|
|
32
|
+
export declare function defineConfig(config: H2OUserConfig): H2OUserConfig;
|
|
33
|
+
export declare function loadUserConfig(): Promise<H2OUserConfig>;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yule-h2o/web_auto_flow",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Playwright + Stagehand AI 端到端自动化测试框架",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"testing",
|
|
8
|
+
"e2e",
|
|
9
|
+
"playwright",
|
|
10
|
+
"stagehand",
|
|
11
|
+
"ai",
|
|
12
|
+
"automation"
|
|
13
|
+
],
|
|
14
|
+
"author": "820125507@qq.com",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "rollup -c",
|
|
32
|
+
"prepublishOnly": "pnpm build"
|
|
33
|
+
},
|
|
34
|
+
"packageManager": "pnpm@10.28.2",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@browserbasehq/stagehand": "^3.5.0",
|
|
37
|
+
"playwright-core": "^1.61.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
41
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
42
|
+
"rollup": "^4.62.2",
|
|
43
|
+
"tslib": "^2.8.1",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
45
|
+
}
|
|
46
|
+
}
|