speccore 8.3.45 → 8.3.47
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/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +6 -1
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/init.d.ts +10 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +227 -46
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +2 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/verify.d.ts +6 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +143 -0
- package/dist/commands/verify.js.map +1 -1
- package/dist/core/ui-verify/index.d.ts +8 -0
- package/dist/core/ui-verify/index.d.ts.map +1 -0
- package/dist/core/ui-verify/index.js +29 -0
- package/dist/core/ui-verify/index.js.map +1 -0
- package/dist/core/ui-verify/report-generator.d.ts +6 -0
- package/dist/core/ui-verify/report-generator.d.ts.map +1 -0
- package/dist/core/ui-verify/report-generator.js +209 -0
- package/dist/core/ui-verify/report-generator.js.map +1 -0
- package/dist/core/ui-verify/smoke-engine.d.ts +16 -0
- package/dist/core/ui-verify/smoke-engine.d.ts.map +1 -0
- package/dist/core/ui-verify/smoke-engine.js +297 -0
- package/dist/core/ui-verify/smoke-engine.js.map +1 -0
- package/dist/core/ui-verify/types.d.ts +100 -0
- package/dist/core/ui-verify/types.d.ts.map +1 -0
- package/dist/core/ui-verify/types.js +8 -0
- package/dist/core/ui-verify/types.js.map +1 -0
- package/dist/core/ui-verify/visual-engine.d.ts +21 -0
- package/dist/core/ui-verify/visual-engine.d.ts.map +1 -0
- package/dist/core/ui-verify/visual-engine.js +177 -0
- package/dist/core/ui-verify/visual-engine.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* UI Verify Report Generator — HTML 报告生成器
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateHtmlReport = generateHtmlReport;
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const fs_extra_1 = require("fs-extra");
|
|
9
|
+
async function generateHtmlReport(report, outputDir) {
|
|
10
|
+
await (0, fs_extra_1.ensureDir)(outputDir);
|
|
11
|
+
const timestamp = new Date(report.timestamp).toLocaleString('zh-CN');
|
|
12
|
+
const passRate = report.summary.total > 0
|
|
13
|
+
? Math.round((report.summary.passed / report.summary.total) * 100)
|
|
14
|
+
: 0;
|
|
15
|
+
const html = `<!DOCTYPE html>
|
|
16
|
+
<html lang="zh-CN">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8">
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
20
|
+
<title>UI 验证报告 — ${report.taskId}</title>
|
|
21
|
+
<style>
|
|
22
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
23
|
+
body {
|
|
24
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
25
|
+
background: #f5f5f5;
|
|
26
|
+
color: #333;
|
|
27
|
+
line-height: 1.6;
|
|
28
|
+
}
|
|
29
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
30
|
+
.header {
|
|
31
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
32
|
+
color: white;
|
|
33
|
+
padding: 30px;
|
|
34
|
+
border-radius: 12px;
|
|
35
|
+
margin-bottom: 24px;
|
|
36
|
+
}
|
|
37
|
+
.header h1 { font-size: 24px; margin-bottom: 8px; }
|
|
38
|
+
.header .meta { opacity: 0.9; font-size: 14px; }
|
|
39
|
+
.summary {
|
|
40
|
+
display: grid;
|
|
41
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
42
|
+
gap: 16px;
|
|
43
|
+
margin-bottom: 24px;
|
|
44
|
+
}
|
|
45
|
+
.card {
|
|
46
|
+
background: white;
|
|
47
|
+
padding: 20px;
|
|
48
|
+
border-radius: 8px;
|
|
49
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
50
|
+
}
|
|
51
|
+
.card .label { font-size: 12px; color: #666; text-transform: uppercase; }
|
|
52
|
+
.card .value { font-size: 28px; font-weight: bold; margin-top: 4px; }
|
|
53
|
+
.card.success { border-left: 4px solid #22c55e; }
|
|
54
|
+
.card.fail { border-left: 4px solid #ef4444; }
|
|
55
|
+
.card.warn { border-left: 4px solid #f59e0b; }
|
|
56
|
+
.section { background: white; border-radius: 8px; padding: 24px; margin-bottom: 16px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
57
|
+
.section h2 { font-size: 18px; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
|
|
58
|
+
.scenario { border: 1px solid #e5e5e5; border-radius: 8px; margin-bottom: 16px; overflow: hidden; }
|
|
59
|
+
.scenario-header {
|
|
60
|
+
padding: 12px 16px;
|
|
61
|
+
background: #fafafa;
|
|
62
|
+
display: flex;
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
align-items: center;
|
|
65
|
+
}
|
|
66
|
+
.scenario-header.pass { border-left: 4px solid #22c55e; }
|
|
67
|
+
.scenario-header.fail { border-left: 4px solid #ef4444; }
|
|
68
|
+
.scenario-body { padding: 16px; }
|
|
69
|
+
.screenshot-comparison { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 12px; }
|
|
70
|
+
.screenshot-box { text-align: center; }
|
|
71
|
+
.screenshot-box img { max-width: 100%; border-radius: 4px; border: 1px solid #ddd; }
|
|
72
|
+
.screenshot-box .label { font-size: 12px; color: #666; margin-top: 4px; }
|
|
73
|
+
.steps { margin-top: 12px; }
|
|
74
|
+
.step { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 14px; }
|
|
75
|
+
.step.pass { color: #22c55e; }
|
|
76
|
+
.step.fail { color: #ef4444; }
|
|
77
|
+
.issues { margin-top: 12px; }
|
|
78
|
+
.issue { padding: 8px 12px; border-radius: 4px; margin-bottom: 8px; font-size: 14px; }
|
|
79
|
+
.issue.error { background: #fef2f2; color: #991b1b; }
|
|
80
|
+
.issue.warning { background: #fffbeb; color: #92400e; }
|
|
81
|
+
.issue.info { background: #eff6ff; color: #1e40af; }
|
|
82
|
+
.badge {
|
|
83
|
+
display: inline-block;
|
|
84
|
+
padding: 2px 8px;
|
|
85
|
+
border-radius: 12px;
|
|
86
|
+
font-size: 12px;
|
|
87
|
+
font-weight: 500;
|
|
88
|
+
}
|
|
89
|
+
.badge.pass { background: #dcfce7; color: #166534; }
|
|
90
|
+
.badge.fail { background: #fee2e2; color: #991b1b; }
|
|
91
|
+
.badge.warn { background: #fef3c7; color: #92400e; }
|
|
92
|
+
.status-bar {
|
|
93
|
+
height: 8px;
|
|
94
|
+
background: #e5e5e5;
|
|
95
|
+
border-radius: 4px;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
margin-top: 8px;
|
|
98
|
+
}
|
|
99
|
+
.status-bar .fill {
|
|
100
|
+
height: 100%;
|
|
101
|
+
background: linear-gradient(90deg, #22c55e 0%, #22c55e var(--pass-rate), #ef4444 var(--pass-rate), #ef4444 100%);
|
|
102
|
+
transition: width 0.3s ease;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
105
|
+
</head>
|
|
106
|
+
<body>
|
|
107
|
+
<div class="container">
|
|
108
|
+
<div class="header">
|
|
109
|
+
<h1>UI 验证报告</h1>
|
|
110
|
+
<div class="meta">
|
|
111
|
+
任务: ${report.taskId} | 平台: ${report.platform} | 时间: ${timestamp} | 耗时: ${(report.summary.duration / 1000).toFixed(1)}s
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="summary">
|
|
116
|
+
<div class="card ${report.summary.failed === 0 ? 'success' : 'fail'}">
|
|
117
|
+
<div class="label">通过率</div>
|
|
118
|
+
<div class="value">${passRate}%</div>
|
|
119
|
+
<div class="status-bar"><div class="fill" style="--pass-rate: ${passRate}%; width: 100%;"></div></div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="card success">
|
|
122
|
+
<div class="label">通过</div>
|
|
123
|
+
<div class="value">${report.summary.passed}</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="card ${report.summary.failed > 0 ? 'fail' : 'success'}">
|
|
126
|
+
<div class="label">失败</div>
|
|
127
|
+
<div class="value">${report.summary.failed}</div>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="card">
|
|
130
|
+
<div class="label">场景总数</div>
|
|
131
|
+
<div class="value">${report.summary.total}</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
${report.smokeEnabled ? generateSmokeSection(report) : ''}
|
|
136
|
+
${report.visualEnabled ? generateVisualSection(report) : ''}
|
|
137
|
+
</div>
|
|
138
|
+
</body>
|
|
139
|
+
</html>`;
|
|
140
|
+
const reportPath = (0, path_1.join)(outputDir, `ui-verify-${report.taskId}.html`);
|
|
141
|
+
await (0, fs_extra_1.writeFile)(reportPath, html, 'utf-8');
|
|
142
|
+
return reportPath;
|
|
143
|
+
}
|
|
144
|
+
function generateSmokeSection(report) {
|
|
145
|
+
if (report.smokeResults.length === 0)
|
|
146
|
+
return '';
|
|
147
|
+
const scenarios = report.smokeResults.map((r) => {
|
|
148
|
+
const steps = r.steps.map((s) => {
|
|
149
|
+
const icon = s.passed ? '✅' : '❌';
|
|
150
|
+
const actionDesc = s.action.selector
|
|
151
|
+
? `${s.action.type}(${s.action.selector})`
|
|
152
|
+
: s.action.type;
|
|
153
|
+
const error = s.error ? `<div style="color:#ef4444;font-size:12px;margin-left:24px;">${s.error}</div>` : '';
|
|
154
|
+
return `<div class="step ${s.passed ? 'pass' : 'fail'}">${icon} ${actionDesc} (${s.duration}ms)</div>${error}`;
|
|
155
|
+
}).join('');
|
|
156
|
+
return `
|
|
157
|
+
<div class="scenario">
|
|
158
|
+
<div class="scenario-header ${r.passed ? 'pass' : 'fail'}">
|
|
159
|
+
<strong>${r.scenarioName}</strong>
|
|
160
|
+
<span class="badge ${r.passed ? 'pass' : 'fail'}">${r.passed ? '通过' : '失败'}</span>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="scenario-body">
|
|
163
|
+
<div class="steps">${steps}</div>
|
|
164
|
+
${r.screenshot ? `<div style="margin-top:12px;"><img src="${r.screenshot}" style="max-width:100%;border-radius:4px;border:1px solid #ddd;" /></div>` : ''}
|
|
165
|
+
</div>
|
|
166
|
+
</div>`;
|
|
167
|
+
}).join('');
|
|
168
|
+
return `
|
|
169
|
+
<div class="section">
|
|
170
|
+
<h2>🔥 冒烟测试结果</h2>
|
|
171
|
+
${scenarios}
|
|
172
|
+
</div>`;
|
|
173
|
+
}
|
|
174
|
+
function generateVisualSection(report) {
|
|
175
|
+
if (report.visualResults.length === 0)
|
|
176
|
+
return '';
|
|
177
|
+
const scenarios = report.visualResults.map((r) => {
|
|
178
|
+
const issues = r.issues.map((i) => `<div class="issue ${i.severity}">${i.severity.toUpperCase()}: ${i.description}</div>`).join('');
|
|
179
|
+
const comparison = r.baselinePath ? `
|
|
180
|
+
<div class="screenshot-comparison">
|
|
181
|
+
<div class="screenshot-box">
|
|
182
|
+
<img src="${r.baselinePath}" alt="基准图" />
|
|
183
|
+
<div class="label">基准图</div>
|
|
184
|
+
</div>
|
|
185
|
+
<div class="screenshot-box">
|
|
186
|
+
<img src="${r.currentPath}" alt="当前截图" />
|
|
187
|
+
<div class="label">当前截图</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>` : '';
|
|
190
|
+
return `
|
|
191
|
+
<div class="scenario">
|
|
192
|
+
<div class="scenario-header ${r.passed ? 'pass' : 'warn'}">
|
|
193
|
+
<strong>${r.scenarioName}</strong>
|
|
194
|
+
<span class="badge ${r.passed ? 'pass' : 'warn'}">${r.passed ? '通过' : '警告'}</span>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="scenario-body">
|
|
197
|
+
<p style="font-size:14px;color:#666;margin-bottom:12px;">${r.analysis}</p>
|
|
198
|
+
${comparison}
|
|
199
|
+
${issues ? `<div class="issues">${issues}</div>` : ''}
|
|
200
|
+
</div>
|
|
201
|
+
</div>`;
|
|
202
|
+
}).join('');
|
|
203
|
+
return `
|
|
204
|
+
<div class="section">
|
|
205
|
+
<h2>👁️ 视觉检查结果</h2>
|
|
206
|
+
${scenarios}
|
|
207
|
+
</div>`;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=report-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-generator.js","sourceRoot":"","sources":["../../../src/core/ui-verify/report-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke Test Engine — Playwright 执行引擎
|
|
3
|
+
*
|
|
4
|
+
* 按 VERIFY_SPEC.yaml 执行结构化流程测试
|
|
5
|
+
*/
|
|
6
|
+
import { VerifySpec, SmokeResult } from './types';
|
|
7
|
+
interface SmokeOptions {
|
|
8
|
+
headless?: boolean;
|
|
9
|
+
browser?: 'chromium' | 'firefox' | 'webkit';
|
|
10
|
+
device?: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
screenshotDir: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function runSmokeTest(spec: VerifySpec, options: SmokeOptions): Promise<SmokeResult[]>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=smoke-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke-engine.d.ts","sourceRoot":"","sources":["../../../src/core/ui-verify/smoke-engine.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EACL,UAAU,EAIV,WAAW,EAEZ,MAAM,SAAS,CAAC;AAEjB,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CA+BxB"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Smoke Test Engine — Playwright 执行引擎
|
|
4
|
+
*
|
|
5
|
+
* 按 VERIFY_SPEC.yaml 执行结构化流程测试
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.runSmokeTest = runSmokeTest;
|
|
9
|
+
const playwright_1 = require("playwright");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const fs_extra_1 = require("fs-extra");
|
|
12
|
+
const logger_1 = require("../../utils/logger");
|
|
13
|
+
async function runSmokeTest(spec, options) {
|
|
14
|
+
const browserType = options.browser || 'chromium';
|
|
15
|
+
const headless = options.headless !== false;
|
|
16
|
+
const timeout = options.timeout || 30000;
|
|
17
|
+
logger_1.logger.info(`🎭 启动 ${browserType} (headless=${headless})`);
|
|
18
|
+
const browser = await launchBrowser(browserType, headless);
|
|
19
|
+
const context = await browser.newContext({
|
|
20
|
+
viewport: getViewport(options.device),
|
|
21
|
+
userAgent: getUserAgent(options.device),
|
|
22
|
+
});
|
|
23
|
+
const results = [];
|
|
24
|
+
try {
|
|
25
|
+
for (const scenario of spec.scenarios) {
|
|
26
|
+
const page = await context.newPage();
|
|
27
|
+
page.setDefaultTimeout(timeout);
|
|
28
|
+
const result = await runScenario(page, scenario, spec, options);
|
|
29
|
+
results.push(result);
|
|
30
|
+
await page.close();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await context.close();
|
|
35
|
+
await browser.close();
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
async function launchBrowser(type, headless) {
|
|
40
|
+
switch (type) {
|
|
41
|
+
case 'firefox':
|
|
42
|
+
return playwright_1.firefox.launch({ headless });
|
|
43
|
+
case 'webkit':
|
|
44
|
+
return playwright_1.webkit.launch({ headless });
|
|
45
|
+
default:
|
|
46
|
+
return playwright_1.chromium.launch({ headless });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function runScenario(page, scenario, spec, options) {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
const steps = [];
|
|
52
|
+
let screenshot;
|
|
53
|
+
logger_1.logger.info(` 📋 场景: ${scenario.name}`);
|
|
54
|
+
try {
|
|
55
|
+
// 导航到初始 URL
|
|
56
|
+
const fullUrl = spec.baseUrl
|
|
57
|
+
? new URL(spec.url, spec.baseUrl).toString()
|
|
58
|
+
: spec.url;
|
|
59
|
+
await page.goto(fullUrl, { waitUntil: 'networkidle' });
|
|
60
|
+
// 执行操作序列
|
|
61
|
+
for (const action of scenario.actions) {
|
|
62
|
+
const stepStart = Date.now();
|
|
63
|
+
try {
|
|
64
|
+
await executeAction(page, action);
|
|
65
|
+
steps.push({
|
|
66
|
+
action,
|
|
67
|
+
passed: true,
|
|
68
|
+
duration: Date.now() - stepStart,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
73
|
+
steps.push({
|
|
74
|
+
action,
|
|
75
|
+
passed: false,
|
|
76
|
+
error: message,
|
|
77
|
+
duration: Date.now() - stepStart,
|
|
78
|
+
});
|
|
79
|
+
// 操作失败,停止当前场景
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// 执行断言(只在所有操作都成功时)
|
|
84
|
+
const allActionsPassed = steps.every((s) => s.passed);
|
|
85
|
+
if (allActionsPassed) {
|
|
86
|
+
for (const assertion of scenario.assertions) {
|
|
87
|
+
if (assertion.type === 'visual') {
|
|
88
|
+
// 视觉断言在 visual-engine 中处理,这里跳过
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const assertStart = Date.now();
|
|
92
|
+
try {
|
|
93
|
+
await executeAssertion(page, assertion);
|
|
94
|
+
steps.push({
|
|
95
|
+
action: { type: 'wait', selector: assertion.selector },
|
|
96
|
+
passed: true,
|
|
97
|
+
duration: Date.now() - assertStart,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
102
|
+
steps.push({
|
|
103
|
+
action: { type: 'wait', selector: assertion.selector },
|
|
104
|
+
passed: false,
|
|
105
|
+
error: `断言失败: ${message}`,
|
|
106
|
+
duration: Date.now() - assertStart,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 截图
|
|
112
|
+
const safeName = scenario.name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
113
|
+
screenshot = (0, path_1.join)(options.screenshotDir, `${safeName}.png`);
|
|
114
|
+
await (0, fs_extra_1.ensureDir)(options.screenshotDir);
|
|
115
|
+
await page.screenshot({ path: screenshot, fullPage: true });
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
logger_1.logger.error(` ❌ 场景执行失败: ${message}`);
|
|
120
|
+
}
|
|
121
|
+
const passed = steps.every((s) => s.passed);
|
|
122
|
+
const duration = Date.now() - startTime;
|
|
123
|
+
logger_1.logger.info(` ${passed ? '✅' : '❌'} ${scenario.name} (${steps.filter((s) => s.passed).length}/${steps.length} 通过, ${duration}ms)`);
|
|
124
|
+
return {
|
|
125
|
+
scenarioName: scenario.name,
|
|
126
|
+
passed,
|
|
127
|
+
duration,
|
|
128
|
+
steps,
|
|
129
|
+
screenshot,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function executeAction(page, action) {
|
|
133
|
+
switch (action.type) {
|
|
134
|
+
case 'fill':
|
|
135
|
+
if (!action.selector)
|
|
136
|
+
throw new Error('fill 需要 selector');
|
|
137
|
+
await page.fill(action.selector, action.value || '');
|
|
138
|
+
break;
|
|
139
|
+
case 'click':
|
|
140
|
+
if (!action.selector)
|
|
141
|
+
throw new Error('click 需要 selector');
|
|
142
|
+
await page.click(action.selector);
|
|
143
|
+
break;
|
|
144
|
+
case 'select':
|
|
145
|
+
if (!action.selector)
|
|
146
|
+
throw new Error('select 需要 selector');
|
|
147
|
+
await page.selectOption(action.selector, action.value || '');
|
|
148
|
+
break;
|
|
149
|
+
case 'check':
|
|
150
|
+
if (!action.selector)
|
|
151
|
+
throw new Error('check 需要 selector');
|
|
152
|
+
await page.check(action.selector);
|
|
153
|
+
break;
|
|
154
|
+
case 'uncheck':
|
|
155
|
+
if (!action.selector)
|
|
156
|
+
throw new Error('uncheck 需要 selector');
|
|
157
|
+
await page.uncheck(action.selector);
|
|
158
|
+
break;
|
|
159
|
+
case 'hover':
|
|
160
|
+
if (!action.selector)
|
|
161
|
+
throw new Error('hover 需要 selector');
|
|
162
|
+
await page.hover(action.selector);
|
|
163
|
+
break;
|
|
164
|
+
case 'focus':
|
|
165
|
+
if (!action.selector)
|
|
166
|
+
throw new Error('focus 需要 selector');
|
|
167
|
+
await page.focus(action.selector);
|
|
168
|
+
break;
|
|
169
|
+
case 'blur':
|
|
170
|
+
if (!action.selector)
|
|
171
|
+
throw new Error('blur 需要 selector');
|
|
172
|
+
await page.evaluate(`(sel) => {
|
|
173
|
+
const el = document.querySelector(sel);
|
|
174
|
+
if (el && el.blur) el.blur();
|
|
175
|
+
}`, action.selector);
|
|
176
|
+
break;
|
|
177
|
+
case 'press':
|
|
178
|
+
if (!action.key)
|
|
179
|
+
throw new Error('press 需要 key');
|
|
180
|
+
await page.keyboard.press(action.key);
|
|
181
|
+
break;
|
|
182
|
+
case 'wait':
|
|
183
|
+
if (action.selector) {
|
|
184
|
+
await page.waitForSelector(action.selector, {
|
|
185
|
+
timeout: action.timeout || 10000,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
else if (action.delay) {
|
|
189
|
+
await page.waitForTimeout(action.delay);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
await page.waitForTimeout(1000);
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
case 'navigate':
|
|
196
|
+
if (!action.value)
|
|
197
|
+
throw new Error('navigate 需要 value (URL)');
|
|
198
|
+
await page.goto(action.value, { waitUntil: 'networkidle' });
|
|
199
|
+
break;
|
|
200
|
+
case 'screenshot':
|
|
201
|
+
// 截图在场景级别统一处理,这里只做等待
|
|
202
|
+
await page.waitForTimeout(500);
|
|
203
|
+
break;
|
|
204
|
+
default:
|
|
205
|
+
throw new Error(`未知操作类型: ${action.type}`);
|
|
206
|
+
}
|
|
207
|
+
// 操作后等待
|
|
208
|
+
if (action.waitFor) {
|
|
209
|
+
await page.waitForSelector(action.waitFor, { timeout: action.timeout || 10000 });
|
|
210
|
+
}
|
|
211
|
+
// 操作间隔
|
|
212
|
+
if (action.delay && action.delay > 0) {
|
|
213
|
+
await page.waitForTimeout(action.delay);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function executeAssertion(page, assertion) {
|
|
217
|
+
switch (assertion.type) {
|
|
218
|
+
case 'visible':
|
|
219
|
+
if (!assertion.selector)
|
|
220
|
+
throw new Error('visible 断言需要 selector');
|
|
221
|
+
await page.waitForSelector(assertion.selector, { state: 'visible' });
|
|
222
|
+
break;
|
|
223
|
+
case 'hidden':
|
|
224
|
+
if (!assertion.selector)
|
|
225
|
+
throw new Error('hidden 断言需要 selector');
|
|
226
|
+
await page.waitForSelector(assertion.selector, { state: 'hidden' });
|
|
227
|
+
break;
|
|
228
|
+
case 'text':
|
|
229
|
+
if (!assertion.selector)
|
|
230
|
+
throw new Error('text 断言需要 selector');
|
|
231
|
+
const text = await page.textContent(assertion.selector);
|
|
232
|
+
if (assertion.contains && !text?.includes(assertion.contains)) {
|
|
233
|
+
throw new Error(`文本不包含 "${assertion.contains}",实际: "${text}"`);
|
|
234
|
+
}
|
|
235
|
+
if (assertion.equals && text !== assertion.equals) {
|
|
236
|
+
throw new Error(`文本不匹配,期望: "${assertion.equals}",实际: "${text}"`);
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
case 'value':
|
|
240
|
+
if (!assertion.selector)
|
|
241
|
+
throw new Error('value 断言需要 selector');
|
|
242
|
+
const inputValue = await page.inputValue(assertion.selector);
|
|
243
|
+
if (assertion.value && inputValue !== assertion.value) {
|
|
244
|
+
throw new Error(`值不匹配,期望: "${assertion.value}",实际: "${inputValue}"`);
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
case 'url':
|
|
248
|
+
const url = page.url();
|
|
249
|
+
if (assertion.contains && !url.includes(assertion.contains)) {
|
|
250
|
+
throw new Error(`URL 不包含 "${assertion.contains}",实际: "${url}"`);
|
|
251
|
+
}
|
|
252
|
+
if (assertion.equals && url !== assertion.equals) {
|
|
253
|
+
throw new Error(`URL 不匹配,期望: "${assertion.equals}",实际: "${url}"`);
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
case 'count':
|
|
257
|
+
if (!assertion.selector)
|
|
258
|
+
throw new Error('count 断言需要 selector');
|
|
259
|
+
const count = await page.locator(assertion.selector).count();
|
|
260
|
+
if (assertion.count !== undefined && count !== assertion.count) {
|
|
261
|
+
throw new Error(`元素数量不匹配,期望: ${assertion.count},实际: ${count}`);
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
case 'attribute':
|
|
265
|
+
if (!assertion.selector || !assertion.attribute) {
|
|
266
|
+
throw new Error('attribute 断言需要 selector 和 attribute');
|
|
267
|
+
}
|
|
268
|
+
const attr = await page.getAttribute(assertion.selector, assertion.attribute);
|
|
269
|
+
if (assertion.value && attr !== assertion.value) {
|
|
270
|
+
throw new Error(`属性 ${assertion.attribute} 不匹配,期望: "${assertion.value}",实际: "${attr}"`);
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
throw new Error(`未知断言类型: ${assertion.type}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function getViewport(device) {
|
|
278
|
+
switch (device) {
|
|
279
|
+
case 'mobile':
|
|
280
|
+
return { width: 375, height: 812 };
|
|
281
|
+
case 'tablet':
|
|
282
|
+
return { width: 768, height: 1024 };
|
|
283
|
+
default:
|
|
284
|
+
return { width: 1280, height: 720 };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function getUserAgent(device) {
|
|
288
|
+
switch (device) {
|
|
289
|
+
case 'mobile':
|
|
290
|
+
return 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
|
|
291
|
+
case 'tablet':
|
|
292
|
+
return 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
|
|
293
|
+
default:
|
|
294
|
+
return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=smoke-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke-engine.js","sourceRoot":"","sources":["../../../src/core/ui-verify/smoke-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI 验证类型定义
|
|
3
|
+
*
|
|
4
|
+
* VERIFY_SPEC.yaml 结构定义
|
|
5
|
+
*/
|
|
6
|
+
export interface VerifySpec {
|
|
7
|
+
name: string;
|
|
8
|
+
platform: string;
|
|
9
|
+
url: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
scenarios: Scenario[];
|
|
12
|
+
}
|
|
13
|
+
export interface Scenario {
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
actions: Action[];
|
|
17
|
+
assertions: Assertion[];
|
|
18
|
+
}
|
|
19
|
+
export type ActionType = 'fill' | 'click' | 'select' | 'check' | 'uncheck' | 'hover' | 'focus' | 'blur' | 'press' | 'wait' | 'navigate' | 'screenshot';
|
|
20
|
+
export interface Action {
|
|
21
|
+
type: ActionType;
|
|
22
|
+
selector?: string;
|
|
23
|
+
value?: string;
|
|
24
|
+
key?: string;
|
|
25
|
+
delay?: number;
|
|
26
|
+
waitFor?: string;
|
|
27
|
+
timeout?: number;
|
|
28
|
+
}
|
|
29
|
+
export type AssertionType = 'visible' | 'hidden' | 'text' | 'value' | 'url' | 'count' | 'attribute' | 'style' | 'visual';
|
|
30
|
+
export interface Assertion {
|
|
31
|
+
type: AssertionType;
|
|
32
|
+
selector?: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
contains?: string;
|
|
35
|
+
equals?: string;
|
|
36
|
+
count?: number;
|
|
37
|
+
attribute?: string;
|
|
38
|
+
style?: string;
|
|
39
|
+
value?: string;
|
|
40
|
+
threshold?: 'strict' | 'normal' | 'loose';
|
|
41
|
+
}
|
|
42
|
+
export interface SmokeResult {
|
|
43
|
+
scenarioName: string;
|
|
44
|
+
passed: boolean;
|
|
45
|
+
duration: number;
|
|
46
|
+
steps: StepResult[];
|
|
47
|
+
screenshot?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface StepResult {
|
|
50
|
+
action: Action;
|
|
51
|
+
passed: boolean;
|
|
52
|
+
error?: string;
|
|
53
|
+
duration: number;
|
|
54
|
+
}
|
|
55
|
+
export interface VisualResult {
|
|
56
|
+
scenarioName: string;
|
|
57
|
+
passed: boolean;
|
|
58
|
+
baselinePath?: string;
|
|
59
|
+
currentPath: string;
|
|
60
|
+
diffPath?: string;
|
|
61
|
+
analysis: string;
|
|
62
|
+
issues: VisualIssue[];
|
|
63
|
+
}
|
|
64
|
+
export interface VisualIssue {
|
|
65
|
+
severity: 'error' | 'warning' | 'info';
|
|
66
|
+
description: string;
|
|
67
|
+
region?: {
|
|
68
|
+
x: number;
|
|
69
|
+
y: number;
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export interface UIVerifyReport {
|
|
75
|
+
taskId: string;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
platform: string;
|
|
78
|
+
url: string;
|
|
79
|
+
smokeEnabled: boolean;
|
|
80
|
+
visualEnabled: boolean;
|
|
81
|
+
smokeResults: SmokeResult[];
|
|
82
|
+
visualResults: VisualResult[];
|
|
83
|
+
summary: {
|
|
84
|
+
total: number;
|
|
85
|
+
passed: number;
|
|
86
|
+
failed: number;
|
|
87
|
+
duration: number;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export interface UIVerifyOptions {
|
|
91
|
+
smokeTest: boolean;
|
|
92
|
+
visualCheck: boolean;
|
|
93
|
+
devices?: string[];
|
|
94
|
+
browsers?: string[];
|
|
95
|
+
updateBaseline?: boolean;
|
|
96
|
+
threshold?: 'strict' | 'normal' | 'loose';
|
|
97
|
+
visualModel?: string;
|
|
98
|
+
timeout?: number;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/ui-verify/types.ts"],"names":[],"mappings":"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"}
|