speccore 8.3.55 → 8.3.57
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 +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +24 -30
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/verify.d.ts +7 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +204 -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 -1
- package/dist/core/ui-verify/index.js +16 -1
- package/dist/core/ui-verify/index.js.map +1 -1
- package/dist/core/ui-verify/module-filter.d.ts +20 -0
- package/dist/core/ui-verify/module-filter.d.ts.map +1 -0
- package/dist/core/ui-verify/module-filter.js +79 -0
- package/dist/core/ui-verify/module-filter.js.map +1 -0
- package/dist/core/ui-verify/page-scanner.d.ts +22 -0
- package/dist/core/ui-verify/page-scanner.d.ts.map +1 -0
- package/dist/core/ui-verify/page-scanner.js +101 -0
- package/dist/core/ui-verify/page-scanner.js.map +1 -0
- package/dist/core/ui-verify/route-scanner.d.ts +26 -0
- package/dist/core/ui-verify/route-scanner.d.ts.map +1 -0
- package/dist/core/ui-verify/route-scanner.js +310 -0
- package/dist/core/ui-verify/route-scanner.js.map +1 -0
- package/dist/core/ui-verify/spec-generator.d.ts +17 -0
- package/dist/core/ui-verify/spec-generator.d.ts.map +1 -0
- package/dist/core/ui-verify/spec-generator.js +140 -0
- package/dist/core/ui-verify/spec-generator.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* page-scanner — 页面目录结构扫描
|
|
4
|
+
*
|
|
5
|
+
* 扫描 src/views/、src/pages/、src/screens/ 等目录
|
|
6
|
+
* 按目录层级推断模块划分,生成页面清单
|
|
7
|
+
*
|
|
8
|
+
* v8.3.57+ 新增
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.scanPages = scanPages;
|
|
12
|
+
const fs_extra_1 = require("fs-extra");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
// 常见的前端页面目录名
|
|
15
|
+
const PAGE_DIR_CANDIDATES = [
|
|
16
|
+
'src/views',
|
|
17
|
+
'src/pages',
|
|
18
|
+
'src/screens',
|
|
19
|
+
'src/routes',
|
|
20
|
+
'views',
|
|
21
|
+
'pages',
|
|
22
|
+
'screens',
|
|
23
|
+
];
|
|
24
|
+
// 页面文件扩展名
|
|
25
|
+
const PAGE_EXTENSIONS = ['.vue', '.tsx', '.jsx', '.ts', '.js'];
|
|
26
|
+
// ── 扫描页面目录 ──
|
|
27
|
+
async function scanPages(projectRoot) {
|
|
28
|
+
const rootDir = await findPageDir(projectRoot);
|
|
29
|
+
if (!rootDir)
|
|
30
|
+
return null;
|
|
31
|
+
const pages = [];
|
|
32
|
+
await scanDirRecursive(rootDir, rootDir, '', pages);
|
|
33
|
+
const modules = [...new Set(pages.map(p => p.module))].sort();
|
|
34
|
+
return { rootDir, pages, modules };
|
|
35
|
+
}
|
|
36
|
+
// 查找页面目录
|
|
37
|
+
async function findPageDir(projectRoot) {
|
|
38
|
+
for (const dir of PAGE_DIR_CANDIDATES) {
|
|
39
|
+
const full = (0, path_1.join)(projectRoot, dir);
|
|
40
|
+
if (await (0, fs_extra_1.pathExists)(full))
|
|
41
|
+
return full;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
// 递归扫描目录
|
|
46
|
+
async function scanDirRecursive(rootDir, currentDir, relativePath, pages) {
|
|
47
|
+
const entries = await (0, fs_extra_1.readdir)(currentDir, { withFileTypes: true });
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
const entryRelPath = relativePath ? (0, path_1.join)(relativePath, entry.name) : entry.name;
|
|
50
|
+
const entryFullPath = (0, path_1.join)(currentDir, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
// 跳过常见非页面目录
|
|
53
|
+
if (shouldSkipDir(entry.name))
|
|
54
|
+
continue;
|
|
55
|
+
await scanDirRecursive(rootDir, entryFullPath, entryRelPath, pages);
|
|
56
|
+
}
|
|
57
|
+
else if (entry.isFile()) {
|
|
58
|
+
const ext = PAGE_EXTENSIONS.find(e => entry.name.endsWith(e));
|
|
59
|
+
if (ext) {
|
|
60
|
+
const name = entry.name.slice(0, -ext.length);
|
|
61
|
+
// 模块名 = 相对路径的第一层目录(如果没有子目录,则用文件名作为模块)
|
|
62
|
+
const module = relativePath
|
|
63
|
+
? relativePath.split('/')[0]
|
|
64
|
+
: name;
|
|
65
|
+
const routePath = inferRoutePath(entryRelPath, name, ext);
|
|
66
|
+
pages.push({
|
|
67
|
+
name,
|
|
68
|
+
filePath: entryRelPath,
|
|
69
|
+
fullPath: entryFullPath,
|
|
70
|
+
module,
|
|
71
|
+
routePath,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 跳过不需要扫描的目录
|
|
78
|
+
function shouldSkipDir(name) {
|
|
79
|
+
const skipList = new Set([
|
|
80
|
+
'components', 'composables', 'hooks', 'utils', 'helpers',
|
|
81
|
+
'assets', 'styles', 'css', 'scss', 'less',
|
|
82
|
+
'__tests__', '__mocks__', 'test', 'tests', 'spec',
|
|
83
|
+
'node_modules', '.git', 'dist', 'build', 'public',
|
|
84
|
+
]);
|
|
85
|
+
return skipList.has(name.toLowerCase());
|
|
86
|
+
}
|
|
87
|
+
// 从文件路径推断路由路径
|
|
88
|
+
function inferRoutePath(relativePath, fileName, ext) {
|
|
89
|
+
const parts = relativePath.split('/');
|
|
90
|
+
// 如果文件叫 Index.vue / index.vue / Home.vue,使用目录名作为路径
|
|
91
|
+
if (/^(index|home)$/i.test(fileName) && parts.length > 1) {
|
|
92
|
+
return '/' + parts.slice(0, -1).join('/');
|
|
93
|
+
}
|
|
94
|
+
// 常规情况:目录 + 文件名(去掉扩展名)
|
|
95
|
+
const routeParts = [...parts];
|
|
96
|
+
routeParts[routeParts.length - 1] = fileName;
|
|
97
|
+
// 处理动态路由 [id].vue → :id
|
|
98
|
+
const normalized = routeParts.map(p => p.replace(/^\[(\.\.\.)?(.+?)\]$/, (match, rest, name) => rest ? `:${name}+` : `:${name}`));
|
|
99
|
+
return '/' + normalized.join('/');
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=page-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-scanner.js","sourceRoot":"","sources":["../../../src/core/ui-verify/page-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* route-scanner — 前端路由自动发现
|
|
3
|
+
*
|
|
4
|
+
* 支持 Vue Router / React Router 配置扫描
|
|
5
|
+
* 从源码中提取路由清单,自动生成测试规格
|
|
6
|
+
*
|
|
7
|
+
* v8.3.57+ 新增
|
|
8
|
+
*/
|
|
9
|
+
export interface DiscoveredRoute {
|
|
10
|
+
path: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
component?: string;
|
|
13
|
+
module?: string;
|
|
14
|
+
isLayout?: boolean;
|
|
15
|
+
children?: DiscoveredRoute[];
|
|
16
|
+
}
|
|
17
|
+
export interface RouteScanResult {
|
|
18
|
+
framework: 'vue' | 'react' | 'unknown';
|
|
19
|
+
routerFile: string;
|
|
20
|
+
routes: DiscoveredRoute[];
|
|
21
|
+
modules: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare function detectFramework(projectRoot: string): Promise<'vue' | 'react' | 'unknown'>;
|
|
24
|
+
export declare function scanRoutes(projectRoot: string): Promise<RouteScanResult>;
|
|
25
|
+
export declare function flattenRoutes(routes: DiscoveredRoute[]): DiscoveredRoute[];
|
|
26
|
+
//# sourceMappingURL=route-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-scanner.d.ts","sourceRoot":"","sources":["../../../src/core/ui-verify/route-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAwBD,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC,CAoB/F;AAGD,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAoB9E;AAoPD,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAU1E"}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* route-scanner — 前端路由自动发现
|
|
4
|
+
*
|
|
5
|
+
* 支持 Vue Router / React Router 配置扫描
|
|
6
|
+
* 从源码中提取路由清单,自动生成测试规格
|
|
7
|
+
*
|
|
8
|
+
* v8.3.57+ 新增
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.detectFramework = detectFramework;
|
|
12
|
+
exports.scanRoutes = scanRoutes;
|
|
13
|
+
exports.flattenRoutes = flattenRoutes;
|
|
14
|
+
const fs_extra_1 = require("fs-extra");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
// ── 路由器配置文件候选路径 ──
|
|
17
|
+
const VUE_ROUTER_CANDIDATES = [
|
|
18
|
+
'src/router/index.ts',
|
|
19
|
+
'src/router/index.js',
|
|
20
|
+
'src/router/routes.ts',
|
|
21
|
+
'src/router/routes.js',
|
|
22
|
+
'src/router.ts',
|
|
23
|
+
'src/router.js',
|
|
24
|
+
];
|
|
25
|
+
const REACT_ROUTER_CANDIDATES = [
|
|
26
|
+
'src/App.tsx',
|
|
27
|
+
'src/App.jsx',
|
|
28
|
+
'src/routes.tsx',
|
|
29
|
+
'src/routes.jsx',
|
|
30
|
+
'src/router.tsx',
|
|
31
|
+
'src/router.jsx',
|
|
32
|
+
'src/App.ts',
|
|
33
|
+
'src/App.js',
|
|
34
|
+
];
|
|
35
|
+
// ── 检测前端框架类型 ──
|
|
36
|
+
async function detectFramework(projectRoot) {
|
|
37
|
+
const pkgPath = (0, path_1.join)(projectRoot, 'package.json');
|
|
38
|
+
if (await (0, fs_extra_1.pathExists)(pkgPath)) {
|
|
39
|
+
try {
|
|
40
|
+
const pkg = JSON.parse(await (0, fs_extra_1.readFile)(pkgPath, 'utf-8'));
|
|
41
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
42
|
+
if (deps['vue-router'] || deps['vue'])
|
|
43
|
+
return 'vue';
|
|
44
|
+
if (deps['react-router-dom'] || deps['react-router'] || deps['react'])
|
|
45
|
+
return 'react';
|
|
46
|
+
}
|
|
47
|
+
catch { /* ignore */ }
|
|
48
|
+
}
|
|
49
|
+
// 根据文件存在性推断
|
|
50
|
+
for (const p of VUE_ROUTER_CANDIDATES) {
|
|
51
|
+
if (await (0, fs_extra_1.pathExists)((0, path_1.join)(projectRoot, p)))
|
|
52
|
+
return 'vue';
|
|
53
|
+
}
|
|
54
|
+
for (const p of REACT_ROUTER_CANDIDATES) {
|
|
55
|
+
if (await (0, fs_extra_1.pathExists)((0, path_1.join)(projectRoot, p)))
|
|
56
|
+
return 'react';
|
|
57
|
+
}
|
|
58
|
+
return 'unknown';
|
|
59
|
+
}
|
|
60
|
+
// ── 扫描路由(入口)──
|
|
61
|
+
async function scanRoutes(projectRoot) {
|
|
62
|
+
const framework = await detectFramework(projectRoot);
|
|
63
|
+
if (framework === 'vue') {
|
|
64
|
+
const routerFile = await findRouterFile(projectRoot, VUE_ROUTER_CANDIDATES);
|
|
65
|
+
if (routerFile) {
|
|
66
|
+
const routes = await parseVueRoutes(routerFile, projectRoot);
|
|
67
|
+
return { framework, routerFile, routes, modules: extractModules(routes) };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (framework === 'react') {
|
|
71
|
+
const routerFile = await findRouterFile(projectRoot, REACT_ROUTER_CANDIDATES);
|
|
72
|
+
if (routerFile) {
|
|
73
|
+
const routes = await parseReactRoutes(routerFile, projectRoot);
|
|
74
|
+
return { framework, routerFile, routes, modules: extractModules(routes) };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { framework: 'unknown', routerFile: '', routes: [], modules: [] };
|
|
78
|
+
}
|
|
79
|
+
// ── 查找路由配置文件 ──
|
|
80
|
+
async function findRouterFile(projectRoot, candidates) {
|
|
81
|
+
for (const p of candidates) {
|
|
82
|
+
const full = (0, path_1.join)(projectRoot, p);
|
|
83
|
+
if (await (0, fs_extra_1.pathExists)(full))
|
|
84
|
+
return full;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
// ═══════════════════════════════════════════════════════════
|
|
89
|
+
// Vue Router 解析
|
|
90
|
+
// ═══════════════════════════════════════════════════════════
|
|
91
|
+
async function parseVueRoutes(routerFile, projectRoot) {
|
|
92
|
+
const content = await (0, fs_extra_1.readFile)(routerFile, 'utf-8');
|
|
93
|
+
const routes = [];
|
|
94
|
+
// 策略1:直接提取路由数组字面量(最常用)
|
|
95
|
+
// const routes = [ { path: '...', component: ... }, ... ]
|
|
96
|
+
const arrayMatch = content.match(/(?:const|let|var)\s+routes\s*=\s*(\[[\s\S]*?\]);?\s*(?:export|const|function|new|createRouter|$)/);
|
|
97
|
+
if (arrayMatch) {
|
|
98
|
+
const routeArray = extractRouteObjects(arrayMatch[1], projectRoot);
|
|
99
|
+
routes.push(...routeArray);
|
|
100
|
+
}
|
|
101
|
+
// 策略2:提取从其他文件导入的路由
|
|
102
|
+
// import routes from './routes'
|
|
103
|
+
const importMatch = content.match(/import\s+routes\s+from\s+['"]([^'"]+)['"]/);
|
|
104
|
+
if (importMatch && routes.length === 0) {
|
|
105
|
+
const importedPath = resolveImportPath(importMatch[1], (0, path_1.dirname)(routerFile));
|
|
106
|
+
if (importedPath && await (0, fs_extra_1.pathExists)(importedPath)) {
|
|
107
|
+
const importedRoutes = await parseVueRoutes(importedPath, projectRoot);
|
|
108
|
+
routes.push(...importedRoutes);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 策略3:提取 createRouter({ routes: [...] }) 中的 routes
|
|
112
|
+
const createRouterMatch = content.match(/routes\s*:\s*(\[[\s\S]*?\])\s*[,}]/);
|
|
113
|
+
if (createRouterMatch && routes.length === 0) {
|
|
114
|
+
const routeArray = extractRouteObjects(createRouterMatch[1], projectRoot);
|
|
115
|
+
routes.push(...routeArray);
|
|
116
|
+
}
|
|
117
|
+
return routes;
|
|
118
|
+
}
|
|
119
|
+
// 从数组文本中提取路由对象
|
|
120
|
+
function extractRouteObjects(arrayText, projectRoot) {
|
|
121
|
+
const routes = [];
|
|
122
|
+
// 匹配单个路由对象:{ path: '...', ... }
|
|
123
|
+
const routeRegex = /\{\s*path\s*:\s*['"]([^'"]*?)['"]\s*,?/g;
|
|
124
|
+
let match;
|
|
125
|
+
while ((match = routeRegex.exec(arrayText)) !== null) {
|
|
126
|
+
const startIdx = match.index;
|
|
127
|
+
const path = match[1];
|
|
128
|
+
// 找到这个对象的结束位置(匹配花括号)
|
|
129
|
+
const endIdx = findObjectEnd(arrayText, startIdx);
|
|
130
|
+
const objText = arrayText.slice(startIdx, endIdx);
|
|
131
|
+
const route = parseRouteObject(objText, projectRoot);
|
|
132
|
+
if (route)
|
|
133
|
+
routes.push(route);
|
|
134
|
+
}
|
|
135
|
+
return routes;
|
|
136
|
+
}
|
|
137
|
+
// 解析单个路由对象文本
|
|
138
|
+
function parseRouteObject(objText, projectRoot) {
|
|
139
|
+
const pathMatch = objText.match(/path\s*:\s*['"]([^'"]*?)['"]/);
|
|
140
|
+
if (!pathMatch)
|
|
141
|
+
return null;
|
|
142
|
+
const path = pathMatch[1];
|
|
143
|
+
if (path === '*' || path === '' || path === '/') {
|
|
144
|
+
// 跳过通配符和根路由(通常重定向)
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const nameMatch = objText.match(/name\s*:\s*['"]([^'"]*?)['"]/);
|
|
148
|
+
const name = nameMatch ? nameMatch[1] : undefined;
|
|
149
|
+
// 提取 component(直接引用或动态导入)
|
|
150
|
+
let component;
|
|
151
|
+
const compMatch = objText.match(/component\s*:\s*([A-Za-z0-9_]+)/);
|
|
152
|
+
if (compMatch) {
|
|
153
|
+
component = compMatch[1];
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const lazyMatch = objText.match(/component\s*:\s*\(\)\s*=>\s*import\(['"]([^'"]+)['"]\)/);
|
|
157
|
+
if (lazyMatch) {
|
|
158
|
+
component = lazyMatch[1];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// 从 component 路径推断模块
|
|
162
|
+
let module;
|
|
163
|
+
if (component) {
|
|
164
|
+
module = inferModuleFromPath(component);
|
|
165
|
+
}
|
|
166
|
+
if (!module) {
|
|
167
|
+
module = inferModuleFromRoutePath(path);
|
|
168
|
+
}
|
|
169
|
+
const route = { path, name, component, module };
|
|
170
|
+
// 处理 children
|
|
171
|
+
const childrenMatch = objText.match(/children\s*:\s*(\[[\s\S]*?\])\s*[,}]/);
|
|
172
|
+
if (childrenMatch) {
|
|
173
|
+
route.children = extractRouteObjects(childrenMatch[1], projectRoot);
|
|
174
|
+
route.children.forEach(c => {
|
|
175
|
+
if (!c.module && module)
|
|
176
|
+
c.module = module;
|
|
177
|
+
// 子路由路径拼接
|
|
178
|
+
if (!c.path.startsWith('/')) {
|
|
179
|
+
c.path = path.replace(/\/$/, '') + '/' + c.path;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return route;
|
|
184
|
+
}
|
|
185
|
+
// ═══════════════════════════════════════════════════════════
|
|
186
|
+
// React Router 解析
|
|
187
|
+
// ═══════════════════════════════════════════════════════════
|
|
188
|
+
async function parseReactRoutes(routesFile, projectRoot) {
|
|
189
|
+
const content = await (0, fs_extra_1.readFile)(routesFile, 'utf-8');
|
|
190
|
+
const routes = [];
|
|
191
|
+
// 策略1:createBrowserRouter/createHashRouter 数组参数
|
|
192
|
+
const routerMatch = content.match(/create(?:Browser|Hash|Memory)Router\s*\((\[[\s\S]*?\])\s*\)/);
|
|
193
|
+
if (routerMatch) {
|
|
194
|
+
const routeArray = extractRouteObjects(routerMatch[1], projectRoot);
|
|
195
|
+
routes.push(...routeArray);
|
|
196
|
+
}
|
|
197
|
+
// 策略2:JSX Route 组件
|
|
198
|
+
// <Route path="..." element={<Xxx />} />
|
|
199
|
+
const jsxRouteRegex = /<Route\s+[^>]*path\s*=\s*['"]([^'"]*?)['"][^>]*>/g;
|
|
200
|
+
let jsxMatch;
|
|
201
|
+
while ((jsxMatch = jsxRouteRegex.exec(content)) !== null) {
|
|
202
|
+
const path = jsxMatch[1];
|
|
203
|
+
const fullTag = jsxMatch[0];
|
|
204
|
+
if (path === '*' || path === '')
|
|
205
|
+
continue;
|
|
206
|
+
const elementMatch = fullTag.match(/element\s*=\s*\{<([A-Za-z0-9_]+)/);
|
|
207
|
+
const lazyMatch = fullTag.match(/element\s*=\s*\{<(?:React\.)?Suspense[^>]*>[^<]*<([A-Za-z0-9_]+)/);
|
|
208
|
+
let component;
|
|
209
|
+
if (elementMatch)
|
|
210
|
+
component = elementMatch[1];
|
|
211
|
+
else if (lazyMatch)
|
|
212
|
+
component = lazyMatch[1];
|
|
213
|
+
const module = inferModuleFromRoutePath(path) || (component ? inferModuleFromPath(component) : undefined);
|
|
214
|
+
routes.push({ path, component, module });
|
|
215
|
+
}
|
|
216
|
+
return routes;
|
|
217
|
+
}
|
|
218
|
+
// ═══════════════════════════════════════════════════════════
|
|
219
|
+
// 辅助函数
|
|
220
|
+
// ═══════════════════════════════════════════════════════════
|
|
221
|
+
// 找到对象字面量的结束位置(匹配花括号)
|
|
222
|
+
function findObjectEnd(text, startIdx) {
|
|
223
|
+
let depth = 0;
|
|
224
|
+
let inString = null;
|
|
225
|
+
for (let i = startIdx; i < text.length; i++) {
|
|
226
|
+
const ch = text[i];
|
|
227
|
+
const prev = text[i - 1];
|
|
228
|
+
if (inString) {
|
|
229
|
+
if (ch === inString && prev !== '\\')
|
|
230
|
+
inString = null;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
234
|
+
inString = ch;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (ch === '{')
|
|
238
|
+
depth++;
|
|
239
|
+
if (ch === '}') {
|
|
240
|
+
depth--;
|
|
241
|
+
if (depth === 0)
|
|
242
|
+
return i + 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return text.length;
|
|
246
|
+
}
|
|
247
|
+
// 从 component 路径推断模块名
|
|
248
|
+
function inferModuleFromPath(componentPath) {
|
|
249
|
+
// 例如:@/views/booking/List.vue → booking
|
|
250
|
+
// 例如:../views/user/Profile.tsx → user
|
|
251
|
+
const parts = componentPath.split('/');
|
|
252
|
+
const viewsIdx = parts.findIndex(p => p === 'views' || p === 'pages' || p === 'screens');
|
|
253
|
+
if (viewsIdx >= 0 && viewsIdx + 1 < parts.length) {
|
|
254
|
+
return parts[viewsIdx + 1];
|
|
255
|
+
}
|
|
256
|
+
// 从父目录推断
|
|
257
|
+
if (parts.length >= 2) {
|
|
258
|
+
const dir = parts[parts.length - 2];
|
|
259
|
+
if (dir && dir !== '.' && dir !== '..')
|
|
260
|
+
return dir;
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
// 从路由路径推断模块名
|
|
265
|
+
function inferModuleFromRoutePath(routePath) {
|
|
266
|
+
const segments = routePath.split('/').filter(Boolean);
|
|
267
|
+
if (segments.length > 0) {
|
|
268
|
+
return segments[0];
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
// 提取所有模块名(去重)
|
|
273
|
+
function extractModules(routes) {
|
|
274
|
+
const modules = new Set();
|
|
275
|
+
function collect(r) {
|
|
276
|
+
for (const route of r) {
|
|
277
|
+
if (route.module)
|
|
278
|
+
modules.add(route.module);
|
|
279
|
+
if (route.children)
|
|
280
|
+
collect(route.children);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
collect(routes);
|
|
284
|
+
return Array.from(modules).sort();
|
|
285
|
+
}
|
|
286
|
+
// 解析 import 路径为绝对路径
|
|
287
|
+
function resolveImportPath(importPath, baseDir) {
|
|
288
|
+
if (importPath.startsWith('.')) {
|
|
289
|
+
return (0, path_1.join)(baseDir, importPath);
|
|
290
|
+
}
|
|
291
|
+
if (importPath.startsWith('@/')) {
|
|
292
|
+
// 简化为从 baseDir 的父目录查找 src/
|
|
293
|
+
return (0, path_1.join)(baseDir, '..', importPath.replace('@/', ''));
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
// ── 扁平化路由(把嵌套路由展开为列表)──
|
|
298
|
+
function flattenRoutes(routes) {
|
|
299
|
+
const flat = [];
|
|
300
|
+
function walk(r) {
|
|
301
|
+
for (const route of r) {
|
|
302
|
+
flat.push(route);
|
|
303
|
+
if (route.children)
|
|
304
|
+
walk(route.children);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
walk(routes);
|
|
308
|
+
return flat;
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=route-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-scanner.js","sourceRoot":"","sources":["../../../src/core/ui-verify/route-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec-generator — 从扫描结果生成 VERIFY_SPEC.yaml
|
|
3
|
+
*
|
|
4
|
+
* v8.3.57+ 新增
|
|
5
|
+
*/
|
|
6
|
+
import type { DiscoveredRoute } from './route-scanner';
|
|
7
|
+
import type { PageEntry } from './page-scanner';
|
|
8
|
+
import type { VerifySpec } from './types';
|
|
9
|
+
export interface SpecGenerationOptions {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
platform: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function generateSpecFromRoutes(routes: DiscoveredRoute[], options: SpecGenerationOptions): VerifySpec;
|
|
15
|
+
export declare function generateSpecFromPages(pages: PageEntry[], options: SpecGenerationOptions): VerifySpec;
|
|
16
|
+
export declare function writeVerifySpec(spec: VerifySpec, outputPath: string): Promise<string>;
|
|
17
|
+
//# sourceMappingURL=spec-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-generator.d.ts","sourceRoot":"","sources":["../../../src/core/ui-verify/spec-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAA+B,MAAM,SAAS,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,EAAE,EACzB,OAAO,EAAE,qBAAqB,GAC7B,UAAU,CA2BZ;AAGD,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,EAAE,EAClB,OAAO,EAAE,qBAAqB,GAC7B,UAAU,CA+BZ;AAwBD,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAMjB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* spec-generator — 从扫描结果生成 VERIFY_SPEC.yaml
|
|
4
|
+
*
|
|
5
|
+
* v8.3.57+ 新增
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.generateSpecFromRoutes = generateSpecFromRoutes;
|
|
9
|
+
exports.generateSpecFromPages = generateSpecFromPages;
|
|
10
|
+
exports.writeVerifySpec = writeVerifySpec;
|
|
11
|
+
const fs_extra_1 = require("fs-extra");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
// ── 从路由扫描结果生成规格 ──
|
|
14
|
+
function generateSpecFromRoutes(routes, options) {
|
|
15
|
+
const scenarios = [];
|
|
16
|
+
for (const route of routes) {
|
|
17
|
+
if (route.path.includes(':') || route.path.includes('*')) {
|
|
18
|
+
// 跳过动态路由和通配符(需要手动配置具体参数)
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const scenario = createScenarioForRoute(route, options.baseUrl);
|
|
22
|
+
if (scenario)
|
|
23
|
+
scenarios.push(scenario);
|
|
24
|
+
if (route.children) {
|
|
25
|
+
for (const child of route.children) {
|
|
26
|
+
if (child.path.includes(':') || child.path.includes('*'))
|
|
27
|
+
continue;
|
|
28
|
+
const childScenario = createScenarioForRoute(child, options.baseUrl);
|
|
29
|
+
if (childScenario)
|
|
30
|
+
scenarios.push(childScenario);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
name: options.name || `${options.platform} UI 验证`,
|
|
36
|
+
platform: options.platform,
|
|
37
|
+
url: options.baseUrl,
|
|
38
|
+
scenarios,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// ── 从页面扫描结果生成规格 ──
|
|
42
|
+
function generateSpecFromPages(pages, options) {
|
|
43
|
+
const scenarios = [];
|
|
44
|
+
for (const page of pages) {
|
|
45
|
+
if (!page.routePath || page.routePath.includes(':') || page.routePath.includes('*')) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const scenarioName = page.module
|
|
49
|
+
? `${page.module} - ${page.name}`
|
|
50
|
+
: page.name;
|
|
51
|
+
scenarios.push({
|
|
52
|
+
name: scenarioName,
|
|
53
|
+
description: `页面: ${page.filePath}`,
|
|
54
|
+
actions: [
|
|
55
|
+
{ type: 'navigate' },
|
|
56
|
+
{ type: 'wait', delay: 1000 },
|
|
57
|
+
],
|
|
58
|
+
assertions: [
|
|
59
|
+
{ type: 'visible', selector: 'body', description: '页面已加载' },
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
name: options.name || `${options.platform} UI 验证`,
|
|
65
|
+
platform: options.platform,
|
|
66
|
+
url: options.baseUrl,
|
|
67
|
+
scenarios,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// ── 为单个路由创建 scenario ──
|
|
71
|
+
function createScenarioForRoute(route, baseUrl) {
|
|
72
|
+
const path = route.path;
|
|
73
|
+
if (!path || path === '/')
|
|
74
|
+
return null;
|
|
75
|
+
const displayName = route.name || route.component || path;
|
|
76
|
+
const moduleName = route.module || 'default';
|
|
77
|
+
return {
|
|
78
|
+
name: `${moduleName} - ${displayName}`,
|
|
79
|
+
description: `路由: ${path}`,
|
|
80
|
+
actions: [
|
|
81
|
+
{ type: 'navigate' },
|
|
82
|
+
{ type: 'wait', delay: 1000 },
|
|
83
|
+
],
|
|
84
|
+
assertions: [
|
|
85
|
+
{ type: 'visible', selector: 'body', description: '页面已加载' },
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// ── 写入规格文件 ──
|
|
90
|
+
async function writeVerifySpec(spec, outputPath) {
|
|
91
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(outputPath, '..'));
|
|
92
|
+
const yaml = specToYaml(spec);
|
|
93
|
+
await (0, fs_extra_1.writeFile)(outputPath, yaml, 'utf-8');
|
|
94
|
+
return outputPath;
|
|
95
|
+
}
|
|
96
|
+
// ── VerifySpec → YAML 字符串 ──
|
|
97
|
+
function specToYaml(spec) {
|
|
98
|
+
const lines = [];
|
|
99
|
+
lines.push(`name: ${spec.name}`);
|
|
100
|
+
lines.push(`platform: ${spec.platform}`);
|
|
101
|
+
lines.push(`url: ${spec.url}`);
|
|
102
|
+
lines.push('');
|
|
103
|
+
lines.push('scenarios:');
|
|
104
|
+
for (const s of spec.scenarios) {
|
|
105
|
+
lines.push(` - name: ${s.name}`);
|
|
106
|
+
if (s.description)
|
|
107
|
+
lines.push(` description: ${s.description}`);
|
|
108
|
+
lines.push(' actions:');
|
|
109
|
+
for (const a of s.actions) {
|
|
110
|
+
lines.push(` - type: ${a.type}`);
|
|
111
|
+
if (a.selector)
|
|
112
|
+
lines.push(` selector: ${a.selector}`);
|
|
113
|
+
if (a.value)
|
|
114
|
+
lines.push(` value: ${a.value}`);
|
|
115
|
+
if (a.delay)
|
|
116
|
+
lines.push(` delay: ${a.delay}`);
|
|
117
|
+
if (a.key)
|
|
118
|
+
lines.push(` key: ${a.key}`);
|
|
119
|
+
if (a.waitFor)
|
|
120
|
+
lines.push(` waitFor: ${a.waitFor}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push(' assertions:');
|
|
123
|
+
for (const ass of s.assertions) {
|
|
124
|
+
lines.push(` - type: ${ass.type}`);
|
|
125
|
+
if (ass.selector)
|
|
126
|
+
lines.push(` selector: ${ass.selector}`);
|
|
127
|
+
if (ass.description)
|
|
128
|
+
lines.push(` description: ${ass.description}`);
|
|
129
|
+
if (ass.contains)
|
|
130
|
+
lines.push(` contains: ${ass.contains}`);
|
|
131
|
+
if (ass.equals)
|
|
132
|
+
lines.push(` equals: ${ass.equals}`);
|
|
133
|
+
if (ass.threshold)
|
|
134
|
+
lines.push(` threshold: ${ass.threshold}`);
|
|
135
|
+
}
|
|
136
|
+
lines.push('');
|
|
137
|
+
}
|
|
138
|
+
return lines.join('\n');
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=spec-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-generator.js","sourceRoot":"","sources":["../../../src/core/ui-verify/spec-generator.ts"],"names":[],"mappings":"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"}
|