feat: 增加测试覆盖率并优化代码质量
test: 添加单元测试和端到端测试 refactor: 重构登录页面和上传模块 ci: 更新测试覆盖率阈值至42% build: 添加测试相关依赖 docs: 更新测试文档 style: 修复代码格式问题
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function generateSimpleReport() {
|
||||
const coveragePath = path.join(__dirname, 'coverage/e2e/coverage-data.json');
|
||||
|
||||
if (!fs.existsSync(coveragePath)) {
|
||||
console.error('Coverage data file not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
const coverageData = JSON.parse(fs.readFileSync(coveragePath, 'utf-8'));
|
||||
const outputDir = path.join(__dirname, 'coverage/e2e');
|
||||
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
let totalEntries = 0;
|
||||
let appEntries = 0;
|
||||
const fileStats = {};
|
||||
|
||||
console.log('\n=== E2E Coverage Collection Report ===\n');
|
||||
console.log('Pages covered:');
|
||||
|
||||
const pages = new Set();
|
||||
const scripts = new Set();
|
||||
|
||||
for (const entry of coverageData) {
|
||||
if (!entry.url) continue;
|
||||
|
||||
const url = entry.url;
|
||||
|
||||
if (url.includes('localhost:3000') || url.includes('_next')) {
|
||||
totalEntries++;
|
||||
|
||||
const urlObj = new URL(url);
|
||||
pages.add(urlObj.pathname);
|
||||
|
||||
const scriptUrl = entry.scriptId ? `script-${entry.scriptId}` : 'inline';
|
||||
if (!fileStats[scriptUrl]) {
|
||||
fileStats[scriptUrl] = { count: 0, sourceSize: 0 };
|
||||
}
|
||||
fileStats[scriptUrl].count++;
|
||||
if (entry.source) {
|
||||
fileStats[scriptUrl].sourceSize += entry.source.length;
|
||||
}
|
||||
|
||||
if (url.includes('novalon-website') || url.includes('/_next/')) {
|
||||
appEntries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nTotal JS bundles collected: ${totalEntries}`);
|
||||
console.log(`App-specific bundles: ${appEntries}`);
|
||||
console.log(`Unique pages visited: ${pages.size}`);
|
||||
console.log(`\nPages:`);
|
||||
pages.forEach(p => console.log(` - ${p}`));
|
||||
|
||||
const reportPath = path.join(outputDir, 'coverage-summary.json');
|
||||
const report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
totalEntries,
|
||||
appEntries,
|
||||
pagesVisited: Array.from(pages),
|
||||
fileStats,
|
||||
};
|
||||
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
console.log(`\nReport saved to: ${reportPath}`);
|
||||
|
||||
console.log('\n=== Coverage Summary ===');
|
||||
console.log(`✓ Playwright successfully collected JS coverage from ${totalEntries} bundles`);
|
||||
console.log(`✓ Covered ${pages.size} unique pages`);
|
||||
console.log(`\nNote: For Istanbul HTML report, run: npx playwright show-report`);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
generateSimpleReport();
|
||||
Generated
+35
-1
@@ -19,8 +19,10 @@
|
||||
"allure-playwright": "^3.5.0",
|
||||
"chrome-launcher": "^1.2.1",
|
||||
"glob": "^13.0.6",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"lighthouse": "^13.0.3",
|
||||
"typescript": "^5.3.0"
|
||||
"typescript": "^5.3.0",
|
||||
"v8-to-istanbul": "^9.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@axe-core/playwright": {
|
||||
@@ -3573,6 +3575,13 @@
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
||||
"integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
@@ -5093,6 +5102,16 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/istanbul-lib-coverage": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
|
||||
"integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-worker": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
|
||||
@@ -6566,6 +6585,21 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-to-istanbul": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
|
||||
"integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.12",
|
||||
"@types/istanbul-lib-coverage": "^2.0.1",
|
||||
"convert-source-map": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
|
||||
|
||||
+4
-1
@@ -19,6 +19,7 @@
|
||||
"test:allure:open": "allure open allure-report",
|
||||
"test:allure:serve": "allure serve allure-results",
|
||||
"test:all-with-progress": "node run-tests-with-progress.js",
|
||||
"test:coverage": "playwright test --config=playwright.coverage.config.ts && node coverage-reporter.js",
|
||||
"install": "playwright install --with-deps"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -29,8 +30,10 @@
|
||||
"allure-playwright": "^3.5.0",
|
||||
"chrome-launcher": "^1.2.1",
|
||||
"glob": "^13.0.6",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"lighthouse": "^13.0.3",
|
||||
"typescript": "^5.3.0"
|
||||
"typescript": "^5.3.0",
|
||||
"v8-to-istanbul": "^9.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/nextjs": "^10.42.0"
|
||||
|
||||
@@ -46,6 +46,15 @@ export default defineConfig({
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'chromium-coverage',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
browserName: 'chromium',
|
||||
},
|
||||
testMatch: /.*\.spec\.ts/,
|
||||
globalSetup: undefined,
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './src/tests',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 0,
|
||||
timeout: 30000,
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
trace: 'off',
|
||||
screenshot: 'off',
|
||||
video: 'off',
|
||||
headless: true,
|
||||
viewport: { width: 1280, height: 720 },
|
||||
actionTimeout: 15000,
|
||||
navigationTimeout: 30000,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
let coverageData: any[] = [];
|
||||
|
||||
async function startCoverage(page: Page) {
|
||||
await page.coverage.startJSCoverage({
|
||||
resetOnNavigation: true,
|
||||
reportAnonymousScripts: false,
|
||||
});
|
||||
}
|
||||
|
||||
async function stopCoverage(page: Page) {
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
coverageData = coverage;
|
||||
console.log(`Collected ${coverage.length} JS coverage entries`);
|
||||
}
|
||||
|
||||
async function saveCoverage() {
|
||||
const outputPath = path.join(__dirname, '../../coverage/e2e/coverage-data.json');
|
||||
const outputDir = path.dirname(outputPath);
|
||||
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify(coverageData, null, 2));
|
||||
console.log(`Coverage data saved to: ${outputPath}`);
|
||||
}
|
||||
|
||||
test.describe('E2E覆盖率测试', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await startCoverage(page);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await stopCoverage(page);
|
||||
});
|
||||
|
||||
test('首页应该正常加载', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000/');
|
||||
await expect(page).toHaveURL(/localhost:3000\//);
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test('关于页面应该正常加载', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000/about');
|
||||
await expect(page).toHaveURL(/about/);
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test('产品页面应该正常加载', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000/products');
|
||||
await expect(page).toHaveURL(/products/);
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test('服务页面应该正常加载', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000/services');
|
||||
await expect(page).toHaveURL(/services/);
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test('案例页面应该正常加载', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000/cases');
|
||||
await expect(page).toHaveURL(/cases/);
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test('新闻页面应该正常加载', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000/news');
|
||||
await expect(page).toHaveURL(/news/);
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await saveCoverage();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user