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.
Files changed (37) hide show
  1. package/dist/cli.js +7 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/analyze.d.ts.map +1 -1
  4. package/dist/commands/analyze.js +6 -1
  5. package/dist/commands/analyze.js.map +1 -1
  6. package/dist/commands/init.d.ts +10 -1
  7. package/dist/commands/init.d.ts.map +1 -1
  8. package/dist/commands/init.js +227 -46
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/update.d.ts.map +1 -1
  11. package/dist/commands/update.js +2 -1
  12. package/dist/commands/update.js.map +1 -1
  13. package/dist/commands/verify.d.ts +6 -0
  14. package/dist/commands/verify.d.ts.map +1 -1
  15. package/dist/commands/verify.js +143 -0
  16. package/dist/commands/verify.js.map +1 -1
  17. package/dist/core/ui-verify/index.d.ts +8 -0
  18. package/dist/core/ui-verify/index.d.ts.map +1 -0
  19. package/dist/core/ui-verify/index.js +29 -0
  20. package/dist/core/ui-verify/index.js.map +1 -0
  21. package/dist/core/ui-verify/report-generator.d.ts +6 -0
  22. package/dist/core/ui-verify/report-generator.d.ts.map +1 -0
  23. package/dist/core/ui-verify/report-generator.js +209 -0
  24. package/dist/core/ui-verify/report-generator.js.map +1 -0
  25. package/dist/core/ui-verify/smoke-engine.d.ts +16 -0
  26. package/dist/core/ui-verify/smoke-engine.d.ts.map +1 -0
  27. package/dist/core/ui-verify/smoke-engine.js +297 -0
  28. package/dist/core/ui-verify/smoke-engine.js.map +1 -0
  29. package/dist/core/ui-verify/types.d.ts +100 -0
  30. package/dist/core/ui-verify/types.d.ts.map +1 -0
  31. package/dist/core/ui-verify/types.js +8 -0
  32. package/dist/core/ui-verify/types.js.map +1 -0
  33. package/dist/core/ui-verify/visual-engine.d.ts +21 -0
  34. package/dist/core/ui-verify/visual-engine.d.ts.map +1 -0
  35. package/dist/core/ui-verify/visual-engine.js +177 -0
  36. package/dist/core/ui-verify/visual-engine.js.map +1 -0
  37. 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":";AAAA;;GAEG;;AAMH,gDA4IC;AAhJD,+BAA4B;AAC5B,uCAAgD;AAGzC,KAAK,UAAU,kBAAkB,CACtC,MAAsB,EACtB,SAAiB;IAEjB,MAAM,IAAA,oBAAS,EAAC,SAAS,CAAC,CAAC;IAE3B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;QAClE,CAAC,CAAC,CAAC,CAAC;IAEN,MAAM,IAAI,GAAG;;;;;qBAKM,MAAM,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA2FpB,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,QAAQ,UAAU,SAAS,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;;;;;yBAKnG,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;;6BAE5C,QAAQ;wEACmC,QAAQ;;;;6BAInD,MAAM,CAAC,OAAO,CAAC,MAAM;;yBAEzB,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;;6BAE1C,MAAM,CAAC,OAAO,CAAC,MAAM;;;;6BAIrB,MAAM,CAAC,OAAO,CAAC,KAAK;;;;MAI3C,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;MACvD,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;;;QAGvD,CAAC;IAEP,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,aAAa,MAAM,CAAC,MAAM,OAAO,CAAC,CAAC;IACtE,MAAM,IAAA,oBAAS,EAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAsB;IAClD,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEhD,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9C,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC9B,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAClC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ;gBAClC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG;gBAC1C,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;YAClB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,+DAA+D,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5G,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,UAAU,KAAK,CAAC,CAAC,QAAQ,YAAY,KAAK,EAAE,CAAC;QACjH,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;oCAEyB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;kBAC5C,CAAC,CAAC,YAAY;6BACH,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;;;6BAGrD,KAAK;UACxB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC,UAAU,4EAA4E,CAAC,CAAC,CAAC,EAAE;;WAEtJ,CAAC;IACV,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;MAGH,SAAS;SACN,CAAC;AACV,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAsB;IACnD,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/C,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChC,qBAAqB,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,QAAQ,CACvF,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEX,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;;;oBAGpB,CAAC,CAAC,YAAY;;;;oBAId,CAAC,CAAC,WAAW;;;WAGtB,CAAC,CAAC,CAAC,EAAE,CAAC;QAEb,OAAO;;oCAEyB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;kBAC5C,CAAC,CAAC,YAAY;6BACH,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;;;mEAGf,CAAC,CAAC,QAAQ;UACnE,UAAU;UACV,MAAM,CAAC,CAAC,CAAC,uBAAuB,MAAM,QAAQ,CAAC,CAAC,CAAC,EAAE;;WAElD,CAAC;IACV,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;MAGH,SAAS;SACN,CAAC;AACV,CAAC"}
@@ -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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"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * UI 验证类型定义
4
+ *
5
+ * VERIFY_SPEC.yaml 结构定义
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ //# sourceMappingURL=types.js.map