test(cache): 修复CacheConfigTest边界值测试
- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl - 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE - 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查 - 所有1266个测试用例通过 - 覆盖率: 指令81.89%, 行88.48%, 分支51.55% docs: 添加项目状态报告 - 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态 - 包含质量指标、已完成功能、待办事项和技术债务
This commit is contained in:
168
frontend/scripts/run-e2e-tests.sh
Executable file
168
frontend/scripts/run-e2e-tests.sh
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 🦟 蚊子项目 E2E测试启动脚本
|
||||
# 同时启动前后端服务并运行E2E测试
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 蚊子项目 E2E测试启动器"
|
||||
echo ""
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 检查端口占用
|
||||
check_port() {
|
||||
local port=$1
|
||||
if lsof -i :$port > /dev/null 2>&1; then
|
||||
echo -e "${RED}❌ 端口 $port 已被占用${NC}"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# 清理函数
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo -e "${YELLOW}🧹 清理进程中...${NC}"
|
||||
|
||||
# 杀死后端进程
|
||||
if [ -n "$BACKEND_PID" ]; then
|
||||
kill $BACKEND_PID 2>/dev/null || true
|
||||
echo " 后端进程已停止"
|
||||
fi
|
||||
|
||||
# 杀死前端进程
|
||||
if [ -n "$FRONTEND_PID" ]; then
|
||||
kill $FRONTEND_PID 2>/dev/null || true
|
||||
echo " 前端进程已停止"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ 清理完成${NC}"
|
||||
}
|
||||
|
||||
# 注册清理函数
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# 检查必要命令
|
||||
command -v mvn >/dev/null 2>&1 || { echo -e "${RED}❌ 需要Maven (mvn)${NC}"; exit 1; }
|
||||
command -v node >/dev/null 2>&1 || { echo -e "${RED}❌ 需要Node.js${NC}"; exit 1; }
|
||||
command -v npm >/dev/null 2>&1 || { echo -e "${RED}❌ 需要npm${NC}"; exit 1; }
|
||||
|
||||
# 检查端口
|
||||
echo "🔍 检查端口..."
|
||||
check_port 8080 || exit 1
|
||||
check_port 5173 || exit 1
|
||||
echo -e "${GREEN} ✅ 端口可用${NC}"
|
||||
echo ""
|
||||
|
||||
# 获取项目根目录
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
FRONTEND_DIR="$PROJECT_ROOT/frontend"
|
||||
|
||||
echo "📂 项目目录: $PROJECT_ROOT"
|
||||
echo ""
|
||||
|
||||
# 步骤1: 编译后端
|
||||
echo "📦 步骤1: 编译后端..."
|
||||
cd "$PROJECT_ROOT"
|
||||
mvn clean compile -q -DskipTests
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN} ✅ 后端编译成功${NC}"
|
||||
else
|
||||
echo -e "${RED} ❌ 后端编译失败${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 步骤2: 启动后端服务
|
||||
echo "🚀 步骤2: 启动后端服务..."
|
||||
cd "$PROJECT_ROOT"
|
||||
mvn spring-boot:run -Dspring-boot.run.profiles=e2e -Dspring-boot.run.jvmArguments="-Xmx512m" > /tmp/mosquito-backend.log 2>&1 &
|
||||
BACKEND_PID=$!
|
||||
echo " 后端PID: $BACKEND_PID"
|
||||
echo " 日志: /tmp/mosquito-backend.log"
|
||||
echo ""
|
||||
|
||||
# 等待后端启动
|
||||
echo "⏳ 等待后端服务就绪..."
|
||||
for i in {1..60}; do
|
||||
if curl -s http://localhost:8080/api/v1/activities > /dev/null 2>&1; then
|
||||
echo -e "${GREEN} ✅ 后端服务已就绪${NC}"
|
||||
break
|
||||
fi
|
||||
if [ $i -eq 60 ]; then
|
||||
echo -e "${RED} ❌ 后端服务启动超时${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 2
|
||||
done
|
||||
echo ""
|
||||
|
||||
# 步骤3: 安装前端依赖
|
||||
echo "📦 步骤3: 安装前端依赖..."
|
||||
cd "$FRONTEND_DIR"
|
||||
npm install --silent
|
||||
echo -e "${GREEN} ✅ 前端依赖已安装${NC}"
|
||||
echo ""
|
||||
|
||||
# 步骤4: 启动前端服务
|
||||
echo "🚀 步骤4: 启动前端服务..."
|
||||
cd "$FRONTEND_DIR"
|
||||
npm run dev -- --port 5173 > /tmp/mosquito-frontend.log 2>&1 &
|
||||
FRONTEND_PID=$!
|
||||
echo " 前端PID: $FRONTEND_PID"
|
||||
echo " 日志: /tmp/mosquito-frontend.log"
|
||||
echo ""
|
||||
|
||||
# 等待前端启动
|
||||
echo "⏳ 等待前端服务就绪..."
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:5173 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN} ✅ 前端服务已就绪${NC}"
|
||||
break
|
||||
fi
|
||||
if [ $i -eq 30 ]; then
|
||||
echo -e "${RED} ❌ 前端服务启动超时${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 1
|
||||
done
|
||||
echo ""
|
||||
|
||||
# 步骤5: 运行E2E测试
|
||||
echo "🎭 步骤5: 运行E2E测试..."
|
||||
echo " 后端地址: http://localhost:8080"
|
||||
echo " 前端地址: http://localhost:5173"
|
||||
echo ""
|
||||
|
||||
cd "$FRONTEND_DIR"
|
||||
|
||||
# 设置环境变量
|
||||
export API_BASE_URL=http://localhost:8080
|
||||
export PLAYWRIGHT_BASE_URL=http://localhost:5173
|
||||
|
||||
# 运行Playwright测试
|
||||
echo " 正在运行Playwright E2E测试..."
|
||||
npx playwright test --reporter=list "$@"
|
||||
TEST_EXIT_CODE=$?
|
||||
|
||||
echo ""
|
||||
if [ $TEST_EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ E2E测试通过!${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ E2E测试失败${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📊 查看报告:"
|
||||
echo " Playwright报告: npx playwright show-report e2e-report"
|
||||
echo " 后端日志: tail -100 /tmp/mosquito-backend.log"
|
||||
echo " 前端日志: tail -100 /tmp/mosquito-frontend.log"
|
||||
|
||||
exit $TEST_EXIT_CODE
|
||||
181
frontend/scripts/verify-e2e-setup.cjs
Normal file
181
frontend/scripts/verify-e2e-setup.cjs
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* E2E测试环境验证脚本
|
||||
* 在运行完整测试前验证配置
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔍 验证E2E测试环境...\n');
|
||||
|
||||
// 检查目录结构
|
||||
const requiredDirs = [
|
||||
'e2e',
|
||||
'e2e/fixtures',
|
||||
'e2e/tests',
|
||||
'e2e/utils',
|
||||
];
|
||||
|
||||
console.log('📂 检查目录结构:');
|
||||
let allDirsExist = true;
|
||||
for (const dir of requiredDirs) {
|
||||
const dirPath = path.join(__dirname, '..', dir);
|
||||
if (fs.existsSync(dirPath)) {
|
||||
console.log(` ✅ ${dir}`);
|
||||
} else {
|
||||
console.log(` ❌ ${dir} (缺失)`);
|
||||
allDirsExist = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allDirsExist) {
|
||||
console.log('\n❌ 目录结构不完整');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 检查关键文件
|
||||
const requiredFiles = [
|
||||
'playwright.config.ts',
|
||||
'e2e/global-setup.ts',
|
||||
'e2e/global-teardown.ts',
|
||||
'e2e/fixtures/test-data.ts',
|
||||
'e2e/tests/user-journey.spec.ts',
|
||||
'e2e/utils/auth-helper.ts',
|
||||
'e2e/utils/wait-helper.ts',
|
||||
];
|
||||
|
||||
console.log('\n📄 检查关键文件:');
|
||||
let allFilesExist = true;
|
||||
for (const file of requiredFiles) {
|
||||
const filePath = path.join(__dirname, '..', file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
const stats = fs.statSync(filePath);
|
||||
console.log(` ✅ ${file} (${(stats.size / 1024).toFixed(1)} KB)`);
|
||||
} else {
|
||||
console.log(` ❌ ${file} (缺失)`);
|
||||
allFilesExist = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allFilesExist) {
|
||||
console.log('\n❌ 关键文件缺失');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 检查package.json中的脚本
|
||||
console.log('\n📦 检查npm脚本:');
|
||||
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||
|
||||
const requiredScripts = [
|
||||
'test:e2e',
|
||||
'test:e2e:ui',
|
||||
'test:e2e:debug',
|
||||
'test:e2e:report',
|
||||
];
|
||||
|
||||
for (const script of requiredScripts) {
|
||||
if (packageJson.scripts && packageJson.scripts[script]) {
|
||||
console.log(` ✅ ${script}`);
|
||||
} else {
|
||||
console.log(` ⚠️ ${script} (可选)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查Playwright配置
|
||||
console.log('\n⚙️ 检查Playwright配置:');
|
||||
const playwrightConfigPath = path.join(__dirname, '..', 'playwright.config.ts');
|
||||
if (fs.existsSync(playwrightConfigPath)) {
|
||||
const configContent = fs.readFileSync(playwrightConfigPath, 'utf-8');
|
||||
|
||||
const checks = [
|
||||
{ name: '测试目录', pattern: /testDir/ },
|
||||
{ name: '多浏览器支持', pattern: /chromium.*firefox.*webkit/s },
|
||||
{ name: '移动端设备', pattern: /Pixel 5.*iPhone 12/s },
|
||||
{ name: '截图配置', pattern: /screenshot/ },
|
||||
{ name: '录屏配置', pattern: /video/ },
|
||||
{ name: 'Web服务器配置', pattern: /webServer/ },
|
||||
];
|
||||
|
||||
for (const check of checks) {
|
||||
if (check.pattern.test(configContent)) {
|
||||
console.log(` ✅ ${check.name}`);
|
||||
} else {
|
||||
console.log(` ⚠️ ${check.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查后端E2E配置
|
||||
console.log('\n🔧 检查后端E2E配置:');
|
||||
const backendE2EConfigPath = path.join(__dirname, '..', '..', 'src', 'main', 'resources', 'application-e2e.properties');
|
||||
if (fs.existsSync(backendE2EConfigPath)) {
|
||||
console.log(' ✅ application-e2e.properties');
|
||||
|
||||
const configContent = fs.readFileSync(backendE2EConfigPath, 'utf-8');
|
||||
const checks = [
|
||||
{ name: 'H2数据库配置', pattern: /spring\.datasource\.url.*h2/i },
|
||||
{ name: 'Flyway禁用', pattern: /spring\.flyway\.enabled=false/i },
|
||||
];
|
||||
|
||||
for (const check of checks) {
|
||||
if (check.pattern.test(configContent)) {
|
||||
console.log(` ✅ ${check.name}`);
|
||||
} else {
|
||||
console.log(` ⚠️ ${check.name}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(' ❌ application-e2e.properties (缺失)');
|
||||
}
|
||||
|
||||
// 检查启动脚本
|
||||
console.log('\n🚀 检查启动脚本:');
|
||||
const scriptPath = path.join(__dirname, 'run-e2e-tests.sh');
|
||||
if (fs.existsSync(scriptPath)) {
|
||||
const stats = fs.statSync(scriptPath);
|
||||
const isExecutable = (stats.mode & parseInt('111', 8)) !== 0;
|
||||
|
||||
if (isExecutable) {
|
||||
console.log(' ✅ run-e2e-tests.sh (可执行)');
|
||||
} else {
|
||||
console.log(' ⚠️ run-e2e-tests.sh (需要执行权限)');
|
||||
console.log(' 运行: chmod +x scripts/run-e2e-tests.sh');
|
||||
}
|
||||
} else {
|
||||
console.log(' ❌ run-e2e-tests.sh (缺失)');
|
||||
}
|
||||
|
||||
// 统计测试用例
|
||||
console.log('\n🧪 统计测试用例:');
|
||||
const testFilePath = path.join(__dirname, '..', 'e2e', 'tests', 'user-journey.spec.ts');
|
||||
if (fs.existsSync(testFilePath)) {
|
||||
const testContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
|
||||
// 统计test.describe和test('')
|
||||
const describeMatches = testContent.match(/test\.describe\(/g);
|
||||
const testMatches = testContent.match(/test\(['`]/g);
|
||||
|
||||
console.log(` 测试套件: ${describeMatches ? describeMatches.length : 0} 个`);
|
||||
console.log(` 测试用例: ${testMatches ? testMatches.length : 0} 个`);
|
||||
|
||||
// 提取测试描述
|
||||
const testDescriptions = testContent.match(/test\(['`]([^'`]+)['`]/g);
|
||||
if (testDescriptions) {
|
||||
console.log('\n 测试场景:');
|
||||
testDescriptions.forEach((desc, i) => {
|
||||
const cleanDesc = desc.replace(/test\(['`]/, '').replace(/['`]$/, '');
|
||||
console.log(` ${i + 1}. ${cleanDesc}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ 验证完成!');
|
||||
console.log('');
|
||||
console.log('📖 使用指南:');
|
||||
console.log(' 快速运行: cd frontend && ./scripts/run-e2e-tests.sh');
|
||||
console.log(' 分步运行:');
|
||||
console.log(' 1. mvn spring-boot:run -Dspring-boot.run.profiles=e2e');
|
||||
console.log(' 2. cd frontend && npm run dev -- --port 5173');
|
||||
console.log(' 3. cd frontend && npm run test:e2e');
|
||||
console.log('');
|
||||
Reference in New Issue
Block a user