Skip to content

feat(installment): 增加分期计划金额与期数编辑及自动重算功能 #140

feat(installment): 增加分期计划金额与期数编辑及自动重算功能

feat(installment): 增加分期计划金额与期数编辑及自动重算功能 #140

Workflow file for this run

name: E2E Tests
on:
push:
branches: [main, release]
pull_request:
branches: [main, release]
workflow_dispatch:
env:
NODE_VERSION: '20.x'
PNPM_VERSION: '10'
jobs:
e2e:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: qmx
POSTGRES_PASSWORD: qmx_password
POSTGRES_DB: qmx_test
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U qmx -d qmx_test"
--health-interval=10s
--health-timeout=5s
--health-retries=5
env:
DATABASE_URL: postgresql://qmx:qmx_password@localhost:5432/qmx_test
REDIS_ENABLED: 'false'
strategy:
matrix:
# CI环境只运行chromium,本地可配置多浏览器
browser: [chromium]
timeout-minutes: 25
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
timeout-minutes: 10
- name: Setup pnpm ${{ env.PNPM_VERSION }}
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
# 多层缓存
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Setup Playwright browser cache
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright
~/.cache/ms-edge
key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Setup Vite cache
uses: actions/cache@v4
with:
path: |
node_modules/.vite
node_modules/.vitest
key: ${{ runner.os }}-vite-vitest-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-vite-vitest-
# 依赖安装(带重试)
- name: Install dependencies
shell: bash
run: |
set -e
MAX_RETRIES=3
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if pnpm install --frozen-lockfile; then
echo "✓ Dependencies installed successfully"
exit 0
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "⚠️ Install failed, retrying ($RETRY_COUNT/$MAX_RETRIES)..."
sleep 5
fi
done
echo "❌ Install failed after $MAX_RETRIES attempts"
exit 1
env:
CYPRESS_INSTALL_BINARY: 0
- name: Sync backend schema
run: pnpm run --filter qmx-backend db:push
timeout-minutes: 5
# 安装Playwright浏览器及系统依赖
- name: Install Playwright browsers and dependencies
shell: bash
run: |
pnpx playwright install --with-deps chromium
timeout-minutes: 10
# 设置测试环境
- name: Setup test environment
shell: bash
run: |
cp .env.test .env
echo "NODE_ENV=test" >> $GITHUB_ENV
echo "CI=true" >> $GITHUB_ENV
echo "TZ=UTC" >> $GITHUB_ENV
echo "BASE_URL=http://localhost:1420" >> $GITHUB_ENV
echo "VITE_API_BASE_URL=http://localhost:3001/api/v1" >> $GITHUB_ENV
echo "DATABASE_URL=postgresql://qmx:qmx_password@localhost:5432/qmx_test" >> $GITHUB_ENV
echo "REDIS_ENABLED=false" >> $GITHUB_ENV
# 启动服务(后端和前端)
- name: Start backend and frontend services
shell: bash
run: |
echo "Starting services..."
pnpm run dev:full > services.log 2>&1 &
SERVICES_PID=$!
echo "SERVICES_PID=$SERVICES_PID" >> $GITHUB_ENV
echo "Waiting for services to be ready..."
# 后端就绪检测(指数退避重试)
RETRY_COUNT=0
MAX_RETRIES=60
WAIT_TIME=1
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if curl -sf http://localhost:3001/api/v1/health 2>/dev/null || \
curl -sf http://localhost:3001/api/v1/ 2>/dev/null; then
echo "✓ Backend service is ready"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "❌ Backend service failed to start"
tail -50 services.log
exit 1
fi
echo "Waiting for backend... ($RETRY_COUNT/$MAX_RETRIES, wait ${WAIT_TIME}s)"
sleep $WAIT_TIME
# 指数退避,最多等待2秒
WAIT_TIME=$((WAIT_TIME < 2 ? WAIT_TIME * 2 : 2))
done
# 前端就绪检测
RETRY_COUNT=0
MAX_RETRIES=60
WAIT_TIME=1
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if curl -sf http://localhost:1420 2>/dev/null; then
echo "✓ Frontend service is ready"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "❌ Frontend service failed to start"
tail -50 services.log
exit 1
fi
echo "Waiting for frontend... ($RETRY_COUNT/$MAX_RETRIES, wait ${WAIT_TIME}s)"
sleep $WAIT_TIME
WAIT_TIME=$((WAIT_TIME < 2 ? WAIT_TIME * 2 : 2))
done
env:
CI: true
NODE_ENV: test
TZ: UTC
DATABASE_URL: postgresql://qmx:qmx_password@localhost:5432/qmx_test
REDIS_ENABLED: 'false'
# 测试数据初始化
- name: Initialize test data
shell: bash
run: |
echo "Initializing test data..."
RETRY_COUNT=0
MAX_RETRIES=3
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if curl -X POST http://localhost:3001/api/v1/test/seed 2>/dev/null; then
echo "✓ Test data initialized"
exit 0
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "⚠️ Test data init failed, retrying ($RETRY_COUNT/$MAX_RETRIES)..."
sleep 2
fi
done
echo "⚠️ Test data init failed after $MAX_RETRIES attempts, continuing anyway"
# 运行E2E测试(带重试)
- name: Run E2E tests
shell: bash
run: |
pnpm run e2e \
--project=${{ matrix.browser }} \
--reporter=html,json
timeout-minutes: 12
env:
CI: true
NODE_ENV: test
TZ: UTC
BASE_URL: http://localhost:1420
VITE_API_BASE_URL: http://localhost:3001/api/v1
continue-on-error: true
# 失败诊断:检查服务日志
- name: Check services logs on failure
if: failure()
shell: bash
run: |
echo "=== Last 100 lines of services.log ==="
tail -100 services.log || echo "No services.log found"
echo ""
echo "=== Process information ==="
ps aux | grep -E "node|vite|playwright|mongo" | grep -v grep || echo "No processes found"
echo ""
echo "=== Network connections ==="
netstat -tlnp 2>/dev/null | grep -E "3001|1420|27017" || ss -tlnp 2>/dev/null | grep -E "3001|1420|27017" || echo "No network info available"
echo ""
echo "=== MongoDB logs ==="
docker logs mongodb-test 2>/dev/null | tail -20 || echo "MongoDB logs not available"
# 上传测试结果
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-test-results-${{ matrix.browser }}
path: test-results/
retention-days: 7
# 上传Playwright报告
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-playwright-report-${{ matrix.browser }}
path: playwright-report/
retention-days: 7
# 上传服务日志
- name: Upload services log
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-services-log-${{ matrix.browser }}
path: services.log
retention-days: 7
# 清理资源
- name: Cleanup
if: always()
shell: bash
run: |
echo "Cleaning up..."
pkill -f "pnpm run dev:full" 2>/dev/null || true
pkill -f "vite" 2>/dev/null || true
echo "Cleanup completed"