feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
import { execSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { testMapping, moduleToTests } from '../config/test-mapping.config';
|
||||
|
||||
export interface TestSelectionResult {
|
||||
selectedTests: string[];
|
||||
affectedModules: string[];
|
||||
changedFiles: string[];
|
||||
analysisReport: string;
|
||||
}
|
||||
|
||||
export class SmartTestSelector {
|
||||
private projectRoot: string;
|
||||
|
||||
constructor(projectRoot: string = process.cwd()) {
|
||||
this.projectRoot = projectRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码变更选择测试用例
|
||||
*/
|
||||
selectTestsByChanges(
|
||||
changedFiles: string[],
|
||||
options: {
|
||||
includeRelated?: boolean;
|
||||
priority?: 'high' | 'medium' | 'low' | 'all';
|
||||
testLevel?: 'smoke' | 'functional' | 'all';
|
||||
} = {}
|
||||
): TestSelectionResult {
|
||||
const {
|
||||
includeRelated = true,
|
||||
priority = 'all',
|
||||
testLevel = 'all',
|
||||
} = options;
|
||||
|
||||
const selectedTests = new Set<string>();
|
||||
const affectedModules = new Set<string>();
|
||||
|
||||
// 分析每个变更文件
|
||||
for (const file of changedFiles) {
|
||||
const normalizedPath = this.normalizePath(file);
|
||||
const mapping = this.findMapping(normalizedPath);
|
||||
|
||||
if (mapping) {
|
||||
// 添加直接关联的测试
|
||||
mapping.tests.forEach(test => selectedTests.add(test));
|
||||
mapping.modules.forEach(module => affectedModules.add(module));
|
||||
|
||||
// 如果启用关联分析,添加相关模块的测试
|
||||
if (includeRelated) {
|
||||
this.addRelatedTests(mapping.modules, selectedTests, affectedModules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据优先级过滤
|
||||
const filteredTests = this.filterByPriority(
|
||||
Array.from(selectedTests),
|
||||
priority
|
||||
);
|
||||
|
||||
// 根据测试级别过滤
|
||||
const finalTests = this.filterByTestLevel(filteredTests, testLevel);
|
||||
|
||||
// 生成分析报告
|
||||
const analysisReport = this.generateAnalysisReport({
|
||||
changedFiles,
|
||||
affectedModules: Array.from(affectedModules),
|
||||
selectedTests: finalTests,
|
||||
});
|
||||
|
||||
return {
|
||||
selectedTests: finalTests,
|
||||
affectedModules: Array.from(affectedModules),
|
||||
changedFiles,
|
||||
analysisReport,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Git获取变更文件
|
||||
*/
|
||||
getChangedFilesFromGit(
|
||||
baseBranch: string = 'origin/main',
|
||||
headBranch: string = 'HEAD'
|
||||
): string[] {
|
||||
try {
|
||||
const output = execSync(
|
||||
`git diff --name-only ${baseBranch}...${headBranch}`,
|
||||
{ encoding: 'utf-8', cwd: this.projectRoot }
|
||||
);
|
||||
return output
|
||||
.split('\n')
|
||||
.filter(file => file.trim() && this.isSourceFile(file));
|
||||
} catch (error) {
|
||||
console.error('Failed to get changed files from git:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化文件路径
|
||||
*/
|
||||
private normalizePath(filePath: string): string {
|
||||
return filePath.replace(/\\/g, '/').replace(/^\.\//, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找文件对应的测试映射
|
||||
*/
|
||||
private findMapping(normalizedPath: string) {
|
||||
// 精确匹配
|
||||
if (testMapping[normalizedPath]) {
|
||||
return testMapping[normalizedPath];
|
||||
}
|
||||
|
||||
// 模糊匹配(支持通配符)
|
||||
for (const [pattern, mapping] of Object.entries(testMapping)) {
|
||||
if (this.matchPattern(normalizedPath, pattern)) {
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的模式匹配
|
||||
*/
|
||||
private matchPattern(path: string, pattern: string): boolean {
|
||||
const regex = new RegExp(
|
||||
'^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$'
|
||||
);
|
||||
return regex.test(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加相关测试
|
||||
*/
|
||||
private addRelatedTests(
|
||||
modules: string[],
|
||||
selectedTests: Set<string>,
|
||||
affectedModules: Set<string>
|
||||
): void {
|
||||
for (const module of modules) {
|
||||
const relatedTests = moduleToTests[module];
|
||||
if (relatedTests) {
|
||||
relatedTests.forEach(test => selectedTests.add(test));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据优先级过滤测试
|
||||
*/
|
||||
private filterByPriority(
|
||||
tests: string[],
|
||||
priority: 'high' | 'medium' | 'low' | 'all'
|
||||
): string[] {
|
||||
if (priority === 'all') {
|
||||
return tests;
|
||||
}
|
||||
|
||||
const priorityMap = {
|
||||
high: ['@p0', '@smoke'],
|
||||
medium: ['@p1', '@functional'],
|
||||
low: ['@p2', '@edge'],
|
||||
};
|
||||
|
||||
const targetTags = priorityMap[priority];
|
||||
return tests.filter(test =>
|
||||
targetTags.some(tag => test.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据测试级别过滤
|
||||
*/
|
||||
private filterByTestLevel(
|
||||
tests: string[],
|
||||
level: 'smoke' | 'functional' | 'all'
|
||||
): string[] {
|
||||
if (level === 'all') {
|
||||
return tests;
|
||||
}
|
||||
|
||||
const levelMap = {
|
||||
smoke: '@smoke',
|
||||
functional: '@functional',
|
||||
};
|
||||
|
||||
const targetTag = levelMap[level];
|
||||
return tests.filter(test => test.includes(targetTag));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为源代码文件
|
||||
*/
|
||||
private isSourceFile(filePath: string): boolean {
|
||||
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.vue', '.java'];
|
||||
return extensions.some(ext => filePath.endsWith(ext));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成分析报告
|
||||
*/
|
||||
private generateAnalysisReport(data: {
|
||||
changedFiles: string[];
|
||||
affectedModules: string[];
|
||||
selectedTests: string[];
|
||||
}): string {
|
||||
return `
|
||||
# 智能测试选择分析报告
|
||||
|
||||
## 变更文件 (${data.changedFiles.length}个)
|
||||
${data.changedFiles.map(f => `- ${f}`).join('\n')}
|
||||
|
||||
## 受影响模块 (${data.affectedModules.length}个)
|
||||
${data.affectedModules.map(m => `- ${m}`).join('\n')}
|
||||
|
||||
## 选中测试用例 (${data.selectedTests.length}个)
|
||||
${data.selectedTests.map(t => `- ${t}`).join('\n')}
|
||||
|
||||
## 执行建议
|
||||
- 优先执行冒烟测试(@smoke标签)
|
||||
- 然后执行功能测试(@functional标签)
|
||||
- 最后执行边缘场景测试(@edge标签)
|
||||
`.trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user