diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 138c15a..0000000 --- a/scripts/README.md +++ /dev/null @@ -1,212 +0,0 @@ -# 开发环境测试脚本 - -本目录包含用于在开发环境中测试和验证网站的可复用脚本。 - -## 📋 可用脚本 - -### 性能测试 - -#### `performance-audit.js` -使用Lighthouse进行性能审计,生成详细的性能报告。 - -**运行方式:** -```bash -npm run audit:performance -# 或 -node scripts/performance-audit.js -``` - -**输出:** -- `test-results/performance-summary.json` - 性能数据摘要 -- `test-results/performance/*.html` - 每个页面的详细报告 - -**评估标准:** -- 性能分数 >= 80: ✅ 通过 -- 可访问性分数 >= 80: ✅ 通过 -- SEO分数 >= 80: ✅ 通过 - -### SEO检查 - -#### `seo-check.js` -检查所有页面的SEO配置,包括Meta标签、Open Graph标签、结构化数据等。 - -**运行方式:** -```bash -npm run audit:seo -# 或 -node scripts/seo-check.js -``` - -**输出:** -- `test-results/seo-summary.json` - SEO检查结果 - -**检查项:** -- Meta标签完整性 -- Open Graph标签 -- H1标签 -- 图片alt属性 -- 链接有效性 -- 语言属性 - -### 可访问性测试 - -#### `accessibility-test.js` -使用axe-core进行可访问性测试,符合WCAG 2.1 AA标准。 - -**运行方式:** -```bash -npm run audit:accessibility -# 或 -node scripts/accessibility-test.js -``` - -**输出:** -- `test-results/accessibility-summary.json` - 可访问性测试结果 - -**标准:** -- WCAG 2.1 AA -- 最低分数: 80 - -### 表单验证 - -#### `form-validation.js` -测试表单的UI验证、字段可见性和提交功能。 - -**运行方式:** -```bash -npm run audit:forms -# 或 -node scripts/form-validation.js -``` - -**输出:** -- `test-results/form-validation-summary.json` - 表单验证结果 - -**测试项:** -- 表单字段可见性 -- 必填字段验证 -- 邮箱/电话格式验证 -- 表单提交功能 - -### 综合报告 - -#### `generate-test-report.js` -生成包含所有测试结果的综合HTML报告。 - -**运行方式:** -```bash -npm run report:generate -# 或 -node scripts/generate-test-report.js -``` - -**输出:** -- `test-results/test-report.html` - 综合测试报告 - -### 一键运行 - -#### `run-all-tests.sh` -运行所有测试并生成综合报告。 - -**运行方式:** -```bash -npm run audit:all -# 或 -./scripts/run-all-tests.sh -``` - -**前置条件:** -- 开发服务器运行在 http://localhost:3000 -- 已安装所有依赖 - -## 🚀 快速开始 - -### 1. 安装依赖 -```bash -npm install -D @axe-core/playwright lighthouse chrome-launcher -``` - -### 2. 启动开发服务器 -```bash -npm run dev -``` - -### 3. 运行所有测试 -```bash -# 在另一个终端 -npm run audit:all -``` - -### 4. 查看报告 -```bash -open test-results/test-report.html -``` - -## 📊 测试覆盖 - -### 测试页面 -- 首页 (/) -- 关于我们 (/about) -- 联系我们 (/contact) -- 服务 (/services) -- 产品 (/products) -- 案例 (/cases) -- 新闻 (/news) - -### 测试维度 -- ⚡ 性能(Lighthouse) -- 🔍 SEO(Meta标签、Open Graph等) -- ♿ 可访问性(WCAG 2.1 AA) -- 📝 表单功能(UI验证、提交测试) - -## 📝 自定义配置 - -### 修改测试页面 -编辑各个脚本中的 `PAGES` 数组: - -```javascript -const PAGES = [ - { name: '页面名称', url: 'http://localhost:3000/path' }, - // 添加更多页面... -]; -``` - -### 修改评分标准 -编辑各个脚本中的 `THRESHOLDS` 或 `MIN_SCORE` 常量: - -```javascript -const THRESHOLDS = { - performance: 90, // 提高标准 - accessibility: 90, - seo: 90 -}; -``` - -## 🔧 故障排除 - -### 开发服务器未运行 -``` -❌ 开发服务器未运行 -``` -**解决:** 先运行 `npm run dev` - -### 端口冲突 -如果3000端口被占用,修改脚本中的URL: - -```javascript -const BASE_URL = 'http://localhost:3001'; // 使用其他端口 -``` - -### 测试超时 -增加等待时间: - -```javascript -await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); -``` - -## 📚 参考资料 - -- [Lighthouse文档](https://github.com/GoogleChrome/lighthouse) -- [axe-core文档](https://www.deque.com/axe/) -- [Playwright文档](https://playwright.dev/) -- [WCAG 2.1指南](https://www.w3.org/WAI/WCAG21/quickref/) \ No newline at end of file diff --git a/scripts/accessibility-test.js b/scripts/accessibility-test.js deleted file mode 100644 index fd8bf46..0000000 --- a/scripts/accessibility-test.js +++ /dev/null @@ -1,92 +0,0 @@ -const { runAxeTest, generateReport } = require('./utils/axe-runner'); -const fs = require('fs'); -const path = require('path'); - -const PAGES = [ - { name: '首页', url: 'http://localhost:3000' }, - { name: '关于我们', url: 'http://localhost:3000/about' }, - { name: '联系我们', url: 'http://localhost:3000/contact' }, - { name: '服务', url: 'http://localhost:3000/services' }, - { name: '产品', url: 'http://localhost:3000/products' }, - { name: '案例', url: 'http://localhost:3000/cases' }, - { name: '新闻', url: 'http://localhost:3000/news' } -]; - -const WCAG_STANDARD = 'WCAG 2.1 AA'; -const MIN_SCORE = 80; - -async function testAllPages() { - console.log('♿ 开始可访问性测试...\n'); - console.log(`标准: ${WCAG_STANDARD}`); - console.log(`最低分数: ${MIN_SCORE}\n`); - - const results = []; - const reportDir = 'test-results/accessibility'; - - if (!fs.existsSync(reportDir)) { - fs.mkdirSync(reportDir, { recursive: true }); - } - - for (const page of PAGES) { - console.log(`📄 测试页面: ${page.name} (${page.url})`); - - try { - const result = await runAxeTest(page.url, page.name); - results.push(result); - - const status = parseFloat(result.score) >= MIN_SCORE ? '✅' : '⚠️'; - console.log(` ${status} 分数: ${result.score}\n`); - - } catch (error) { - console.error(` ❌ 测试失败: ${error.message}\n`); - results.push({ - page: page.name, - url: page.url, - error: error.message - }); - } - } - - return results; -} - -function generateSummary(results) { - console.log('\n📊 可访问性测试摘要\n'); - console.log('─'.repeat(80)); - - const table = results.map(r => { - if (r.error) { - return `| ${r.page.padEnd(20)} | ❌ 失败 |`; - } - const status = parseFloat(r.score) >= MIN_SCORE ? '✅' : '⚠️'; - const violations = r.violations.length; - return `| ${r.page.padEnd(20)} | ${status} ${r.score.padStart(6)} | ${violations.toString().padStart(3)} |`; - }); - - console.log('| 页面'.padEnd(20) + ' | 状态 | 违规 |'); - console.log('─'.repeat(80)); - table.forEach(row => console.log(row)); - console.log('─'.repeat(80)); - - const passed = results.filter(r => !r.error && parseFloat(r.score) >= MIN_SCORE).length; - const total = results.length; - const avgScore = results - .filter(r => !r.error) - .reduce((sum, r) => sum + parseFloat(r.score), 0) / results.length; - - console.log(`\n📈 统计:`); - console.log(` 平均分数: ${avgScore.toFixed(1)}`); - console.log(` 达标页面: ${passed}/${total} (${(passed/total*100).toFixed(1)}%)`); - console.log(` 总违规数: ${results.reduce((sum, r) => sum + (r.violations?.length || 0), 0)}`); -} - -async function main() { - const results = await testAllPages(); - generateSummary(results); - - const reportPath = 'test-results/accessibility-summary.json'; - generateReport(results, reportPath); - console.log(`\n💾 详细报告已保存到: ${reportPath}`); -} - -main().catch(console.error); \ No newline at end of file diff --git a/scripts/form-validation.js b/scripts/form-validation.js deleted file mode 100644 index d68ae6c..0000000 --- a/scripts/form-validation.js +++ /dev/null @@ -1,33 +0,0 @@ -const { FormTester } = require('./utils/form-tester'); -const fs = require('fs'); -const path = require('path'); - -async function main() { - console.log('📝 开始表单验证...\n'); - - const tester = new FormTester(); - - console.log('📄 测试联系表单\n'); - const result = await tester.testContactForm(); - tester.results.push(result); - - const summary = tester.getSummary(); - - console.log('\n📊 表单验证摘要\n'); - console.log('─'.repeat(80)); - console.log(`总表单数: ${summary.totalForms}`); - console.log(`总测试数: ${summary.totalTests}`); - console.log(`通过测试: ${summary.passedTests}`); - console.log(`通过率: ${summary.passRate}%`); - console.log('─'.repeat(80)); - - const outputPath = 'test-results/form-validation-summary.json'; - fs.writeFileSync(outputPath, JSON.stringify({ - timestamp: new Date().toISOString(), - summary, - results: tester.results - }, null, 2)); - console.log(`\n💾 详细结果已保存到: ${outputPath}`); -} - -main().catch(console.error); \ No newline at end of file diff --git a/scripts/generate-test-report.js b/scripts/generate-test-report.js deleted file mode 100644 index 03d2ce5..0000000 --- a/scripts/generate-test-report.js +++ /dev/null @@ -1,384 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -function loadResults(filename) { - const filePath = path.join('test-results', filename); - if (!fs.existsSync(filePath)) { - return null; - } - return JSON.parse(fs.readFileSync(filePath, 'utf8')); -} - -function generateHTMLReport() { - const performance = loadResults('performance-summary.json'); - const seo = loadResults('seo-summary.json'); - const accessibility = loadResults('accessibility-summary.json'); - const forms = loadResults('form-validation-summary.json'); - - const timestamp = new Date().toLocaleString('zh-CN'); - - const html = ` - - - - - 开发环境测试报告 - ${timestamp} - - - -
-

🚀 开发环境测试报告

-

生成时间: ${timestamp}

-
- - ${performance ? generatePerformanceSection(performance) : ''} - ${seo ? generateSEOSection(seo) : ''} - ${accessibility ? generateAccessibilitySection(accessibility) : ''} - ${forms ? generateFormSection(forms) : ''} - -
-

📊 总体评估

-
- ${generateOverallSummary(performance, seo, accessibility, forms)} -
-
- -
-

📝 建议和后续步骤

- ${generateRecommendations(performance, seo, accessibility, forms)} -
- -`; - - const outputPath = 'test-results/test-report.html'; - fs.writeFileSync(outputPath, html); - return outputPath; -} - -function generatePerformanceSection(data) { - const avgScore = data.reduce((sum, page) => - sum + (page.metrics?.performance || 0), 0) / data.length; - - return ` -
-

⚡ 性能测试结果

-
-
-

平均分数

-
${avgScore.toFixed(1)}
-
-
-

测试页面数

-
${data.length}
-
-
- - - - - - - - - - - - ${data.map(page => ` - - - - - - - - `).join('')} - -
页面性能可访问性最佳实践SEO
${page.page}${page.metrics?.performance?.toFixed(0) || 'N/A'}${page.metrics?.accessibility?.toFixed(0) || 'N/A'}${page.metrics?.bestPractices?.toFixed(0) || 'N/A'}${page.metrics?.seo?.toFixed(0) || 'N/A'}
-
`; -} - -function generateSEOSection(data) { - const passRate = ((data.totalPages - data.pagesWithIssues) / data.totalPages * 100); - - return ` -
-

🔍 SEO检查结果

-
-
-

通过率

-
${passRate.toFixed(1)}%
-
-
-

总问题数

-
${data.totalIssues}
-
-
- - - - - - - - - - - - - - - - - - - - - -
严重程度数量
🔴 高${data.severity.high}
🟡 中${data.severity.medium}
🟢 低${data.severity.low}
-
`; -} - -function generateAccessibilitySection(data) { - const avgScore = data.summary.averageScore; - const passRate = ((data.totalPages - data.pagesWithIssues) / data.totalPages * 100); - - return ` -
-

♿ 可访问性测试结果

-
-
-

平均分数

-
${avgScore}
-
-
-

达标率

-
${passRate.toFixed(1)}%
-
-
-

标准: WCAG 2.1 AA

-

总违规数: ${data.summary.totalViolations}

-
`; -} - -function generateFormSection(data) { - return ` -
-

📝 表单验证结果

-
-
-

通过率

-
${data.summary.passRate}%
-
-
-

总测试数

-
${data.summary.totalTests}
-
-
-
`; -} - -function generateOverallSummary(performance, seo, accessibility, forms) { - const sections = []; - - if (performance) { - const avgScore = performance.reduce((sum, page) => - sum + (page.metrics?.performance || 0), 0) / performance.length; - sections.push(` -
-

性能

-
${avgScore.toFixed(1)}
-
`); - } - - if (seo) { - const passRate = ((seo.totalPages - seo.pagesWithIssues) / seo.totalPages * 100); - sections.push(` -
-

SEO

-
${passRate.toFixed(1)}%
-
`); - } - - if (accessibility) { - sections.push(` -
-

可访问性

-
${accessibility.summary.averageScore}
-
`); - } - - if (forms) { - sections.push(` -
-

表单

-
${forms.summary.passRate}%
-
`); - } - - return sections.join(''); -} - -function generateRecommendations(performance, seo, accessibility, forms) { - const recommendations = []; - - if (performance) { - const lowPerfPages = performance.filter(p => p.metrics?.performance < 80); - if (lowPerfPages.length > 0) { - recommendations.push(` -

⚡ 性能优化

- `); - } - } - - if (seo) { - if (seo.totalIssues > 0) { - recommendations.push(` -

🔍 SEO改进

- `); - } - } - - if (accessibility) { - const lowAccessPages = accessibility.pages.filter(p => parseFloat(p.score) < 80); - if (lowAccessPages.length > 0) { - recommendations.push(` -

♿ 可访问性改进

- `); - } - } - - if (forms) { - if (parseFloat(forms.summary.passRate) < 100) { - recommendations.push(` -

📝 表单改进

- `); - } - } - - return recommendations.join('') || '

✅ 所有测试都通过了!网站已准备好上线。

'; -} - -async function main() { - console.log('📊 生成综合测试报告...\n'); - - const reportPath = generateHTMLReport(); - - console.log(`✅ 报告已生成: ${reportPath}`); - console.log(`💡 在浏览器中打开查看详细报告`); -} - -main().catch(console.error); \ No newline at end of file diff --git a/scripts/performance-audit.js b/scripts/performance-audit.js deleted file mode 100644 index cfbd771..0000000 --- a/scripts/performance-audit.js +++ /dev/null @@ -1,102 +0,0 @@ -const { runLighthouse, generateReport, extractMetrics } = require('./utils/lighthouse-runner'); -const fs = require('fs'); -const path = require('path'); - -const PAGES = [ - { name: '首页', url: 'http://localhost:3000' }, - { name: '关于我们', url: 'http://localhost:3000/about' }, - { name: '联系我们', url: 'http://localhost:3000/contact' }, - { name: '服务', url: 'http://localhost:3000/services' }, - { name: '产品', url: 'http://localhost:3000/products' }, - { name: '案例', url: 'http://localhost:3000/cases' }, - { name: '新闻', url: 'http://localhost:3000/news' } -]; - -const THRESHOLDS = { - performance: 80, - accessibility: 80, - bestPractices: 80, - seo: 80 -}; - -async function auditAllPages() { - console.log('🚀 开始性能审计...\n'); - - const results = []; - const reportDir = 'test-results/performance'; - - if (!fs.existsSync(reportDir)) { - fs.mkdirSync(reportDir, { recursive: true }); - } - - for (const page of PAGES) { - console.log(`📊 审计页面: ${page.name} (${page.url})`); - - try { - const result = await runLighthouse(page.url, { - onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'], - output: 'html' - }); - - const metrics = extractMetrics(result); - const reportPath = path.join(reportDir, `${page.name.toLowerCase().replace(/\s+/g, '-')}-report.html`); - generateReport(result, reportPath); - - results.push({ - page: page.name, - url: page.url, - metrics, - reportPath - }); - - console.log(` ✅ 性能: ${metrics.performance.toFixed(0)}`); - console.log(` ✅ 可访问性: ${metrics.accessibility.toFixed(0)}`); - console.log(` ✅ 最佳实践: ${metrics.bestPractices.toFixed(0)}`); - console.log(` ✅ SEO: ${metrics.seo.toFixed(0)}`); - console.log(` 📄 报告: ${reportPath}\n`); - - } catch (error) { - console.error(` ❌ 审计失败: ${error.message}\n`); - results.push({ - page: page.name, - url: page.url, - error: error.message - }); - } - } - - return results; -} - -function generateSummary(results) { - console.log('\n📈 审计摘要\n'); - console.log('─'.repeat(80)); - - const table = results.map(r => { - if (r.error) { - return `| ${r.page.padEnd(20)} | ❌ 失败: ${r.error.substring(0, 30)} |`; - } - const status = (m) => m >= THRESHOLDS.performance ? '✅' : '⚠️'; - return `| ${r.page.padEnd(20)} | ${status(r.metrics.performance)} ${r.metrics.performance.toFixed(0)} | ${status(r.metrics.accessibility)} ${r.metrics.accessibility.toFixed(0)} | ${status(r.metrics.seo)} ${r.metrics.seo.toFixed(0)} |`; - }); - - console.log('| 页面'.padEnd(20) + ' | 性能 | 可访问性 | SEO |'); - console.log('─'.repeat(80)); - table.forEach(row => console.log(row)); - console.log('─'.repeat(80)); - - const passed = results.filter(r => !r.error && r.metrics.performance >= THRESHOLDS.performance).length; - const total = results.length; - console.log(`\n📊 总计: ${passed}/${total} 页面达到性能标准 (${(passed/total*100).toFixed(1)}%)`); -} - -async function main() { - const results = await auditAllPages(); - generateSummary(results); - - const jsonPath = path.join('test-results', 'performance-summary.json'); - fs.writeFileSync(jsonPath, JSON.stringify(results, null, 2)); - console.log(`\n💾 详细结果已保存到: ${jsonPath}`); -} - -main().catch(console.error); \ No newline at end of file diff --git a/scripts/run-all-tests.sh b/scripts/run-all-tests.sh deleted file mode 100755 index ba758e7..0000000 --- a/scripts/run-all-tests.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -set -e - -echo "🚀 开始运行所有开发环境测试..." -echo "========================================" -echo "" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -mkdir -p test-results - -echo "📡 检查开发服务器..." -if ! curl -s http://localhost:3000 > /dev/null; then - echo -e "${RED}❌ 开发服务器未运行${NC}" - echo "请先运行: npm run dev" - exit 1 -fi -echo -e "${GREEN}✅ 开发服务器运行正常${NC}" -echo "" - -echo "⚡ 运行性能审计..." -node scripts/performance-audit.js -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ 性能审计完成${NC}" -else - echo -e "${RED}❌ 性能审计失败${NC}" -fi -echo "" - -echo "🔍 运行SEO检查..." -node scripts/seo-check.js -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ SEO检查完成${NC}" -else - echo -e "${RED}❌ SEO检查失败${NC}" -fi -echo "" - -echo "♿ 运行可访问性测试..." -node scripts/accessibility-test.js -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ 可访问性测试完成${NC}" -else - echo -e "${RED}❌ 可访问性测试失败${NC}" -fi -echo "" - -echo "📝 运行表单验证..." -node scripts/form-validation.js -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ 表单验证完成${NC}" -else - echo -e "${RED}❌ 表单验证失败${NC}" -fi -echo "" - -echo "📊 生成综合测试报告..." -node scripts/generate-test-report.js -if [ $? -eq 0 ]; then - echo -e "${GREEN}✅ 报告生成完成${NC}" -else - echo -e "${RED}❌ 报告生成失败${NC}" -fi -echo "" - -echo "========================================" -echo "📈 测试完成!" -echo "" -echo "📁 结果文件位置:" -echo " - test-results/performance-summary.json" -echo " - test-results/performance/*.html" -echo " - test-results/seo-summary.json" -echo " - test-results/accessibility-summary.json" -echo " - test-results/form-validation-summary.json" -echo " - test-results/test-report.html" -echo "" -echo -e "${GREEN}💡 在浏览器中打开 test-results/test-report.html 查看详细报告${NC}" -echo "" \ No newline at end of file diff --git a/scripts/seo-check.js b/scripts/seo-check.js deleted file mode 100644 index 94fec87..0000000 --- a/scripts/seo-check.js +++ /dev/null @@ -1,63 +0,0 @@ -const { SEOValidator } = require('./utils/seo-validator'); -const fs = require('fs'); -const path = require('path'); - -const PAGES = [ - { name: '首页', url: 'http://localhost:3000' }, - { name: '关于我们', url: 'http://localhost:3000/about' }, - { name: '联系我们', url: 'http://localhost:3000/contact' }, - { name: '服务', url: 'http://localhost:3000/services' }, - { name: '产品', url: 'http://localhost:3000/products' }, - { name: '案例', url: 'http://localhost:3000/cases' }, - { name: '新闻', url: 'http://localhost:3000/news' } -]; - -async function checkAllPages() { - console.log('🔍 开始SEO检查...\n'); - - const validator = new SEOValidator(); - - for (const page of PAGES) { - console.log(`📄 检查页面: ${page.name}`); - const result = await validator.validatePage(page.url, page.name); - - if (result.issues.length === 0) { - console.log(` ✅ 无SEO问题\n`); - } else { - console.log(` ⚠️ 发现 ${result.issues.length} 个问题:`); - result.issues.forEach(issue => { - console.log(` - ${issue.check}: ${issue.issue} (${issue.severity})`); - }); - console.log(); - } - } - - return validator.getSummary(); -} - -function generateSummary(summary) { - console.log('\n📊 SEO检查摘要\n'); - console.log('─'.repeat(80)); - console.log(`总页面数: ${summary.totalPages}`); - console.log(`有问题页面: ${summary.pagesWithIssues}`); - console.log(`总问题数: ${summary.totalIssues}`); - console.log(`\n问题严重程度:`); - console.log(` 🔴 高: ${summary.severity.high}`); - console.log(` 🟡 中: ${summary.severity.medium}`); - console.log(` 🟢 低: ${summary.severity.low}`); - console.log('─'.repeat(80)); - - const passRate = ((summary.totalPages - summary.pagesWithIssues) / summary.totalPages * 100).toFixed(1); - console.log(`\n✅ SEO通过率: ${passRate}%`); -} - -async function main() { - const summary = await checkAllPages(); - generateSummary(summary); - - const outputPath = 'test-results/seo-summary.json'; - fs.writeFileSync(outputPath, JSON.stringify(summary, null, 2)); - console.log(`\n💾 详细结果已保存到: ${outputPath}`); -} - -main().catch(console.error); \ No newline at end of file diff --git a/scripts/utils/axe-runner.js b/scripts/utils/axe-runner.js deleted file mode 100644 index 2a16451..0000000 --- a/scripts/utils/axe-runner.js +++ /dev/null @@ -1,74 +0,0 @@ -const { chromium } = require('playwright'); -const AxeBuilder = require('@axe-core/playwright').default; -const fs = require('fs'); -const path = require('path'); - -async function runAxeTest(url, pageName) { - const browser = await chromium.launch(); - const context = await browser.newContext(); - const page = await context.newPage(); - const results = []; - - try { - await page.goto(url, { waitUntil: 'networkidle' }); - - const accessibilityScanResults = await new AxeBuilder({ page }) - .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) - .analyze(); - - const violations = accessibilityScanResults.violations.map(v => ({ - id: v.id, - impact: v.impact, - description: v.description, - help: v.help, - helpUrl: v.helpUrl, - nodes: v.nodes.length - })); - - const passes = accessibilityScanResults.passes.length; - const incomplete = accessibilityScanResults.incomplete.length; - - results.push({ - page: pageName, - url, - violations, - passes, - incomplete, - score: calculateScore(violations, passes, incomplete) - }); - - console.log(` ✅ 扫描完成: ${violations.length} 个违规, ${passes} 个通过, ${incomplete} 个未完成`); - - await context.close(); - await browser.close(); - return results[0]; - - } catch (error) { - await context.close(); - await browser.close(); - throw new Error(`可访问性测试失败: ${error.message}`); - } -} - -function calculateScore(violations, passes, incomplete) { - const total = violations.length + passes + incomplete; - if (total === 0) return 100; - return ((passes / total) * 100).toFixed(1); -} - -function generateReport(results, outputPath) { - const report = { - timestamp: new Date().toISOString(), - summary: { - totalPages: results.length, - totalViolations: results.reduce((sum, r) => sum + r.violations.length, 0), - averageScore: (results.reduce((sum, r) => sum + parseFloat(r.score), 0) / results.length).toFixed(1) - }, - pages: results - }; - - fs.writeFileSync(outputPath, JSON.stringify(report, null, 2)); - return outputPath; -} - -module.exports = { runAxeTest, generateReport }; \ No newline at end of file diff --git a/scripts/utils/form-tester.js b/scripts/utils/form-tester.js deleted file mode 100644 index 3dece75..0000000 --- a/scripts/utils/form-tester.js +++ /dev/null @@ -1,162 +0,0 @@ -const { chromium } = require('playwright'); -const fs = require('fs'); - -class FormTester { - constructor() { - this.results = []; - } - - async testContactForm() { - const browser = await chromium.launch(); - const page = await browser.newPage(); - - try { - await page.goto('http://localhost:3000/contact', { waitUntil: 'networkidle' }); - - const form = { - nameInput: page.locator('[data-testid="name-input"]'), - phoneInput: page.locator('[data-testid="phone-input"]'), - emailInput: page.locator('[data-testid="email-input"]'), - subjectInput: page.locator('[data-testid="subject-input"]'), - messageInput: page.locator('[data-testid="message-input"]'), - submitButton: page.locator('[data-testid="submit-button"]') - }; - - console.log(' 📝 测试表单字段可见性...'); - - const visibilityTests = [ - { name: '姓名输入框', element: form.nameInput }, - { name: '电话输入框', element: form.phoneInput }, - { name: '邮箱输入框', element: form.emailInput }, - { name: '主题输入框', element: form.subjectInput }, - { name: '消息输入框', element: form.messageInput }, - { name: '提交按钮', element: form.submitButton } - ]; - - const visibilityResults = []; - for (const test of visibilityTests) { - try { - await test.element.waitFor({ state: 'visible', timeout: 5000 }); - visibilityResults.push({ name: test.name, passed: true }); - console.log(` ✅ ${test.name} 可见`); - } catch (error) { - visibilityResults.push({ name: test.name, passed: false, error: error.message }); - console.log(` ❌ ${test.name} 不可见: ${error.message}`); - } - } - - console.log(' 📝 测试表单验证...'); - - const validationTests = [ - { - name: '必填字段验证', - test: async () => { - await form.submitButton.click(); - const errorMessages = await page.locator('[data-testid*="error"]').count(); - return errorMessages > 0; - } - }, - { - name: '邮箱格式验证', - test: async () => { - await form.emailInput.fill('invalid-email'); - await form.emailInput.blur(); - const error = await page.locator('[data-testid="email-input"] + [data-testid*="error"]').isVisible(); - return error; - } - }, - { - name: '电话格式验证', - test: async () => { - await form.phoneInput.fill('123'); - await form.phoneInput.blur(); - const error = await page.locator('[data-testid="phone-input"] + [data-testid*="error"]').isVisible(); - return error; - } - } - ]; - - const validationResults = []; - for (const test of validationTests) { - try { - await page.reload(); - await page.waitForLoadState('networkidle'); - const passed = await test.test(); - validationResults.push({ name: test.name, passed }); - console.log(` ${passed ? '✅' : '❌'} ${test.name}`); - } catch (error) { - validationResults.push({ name: test.name, passed: false, error: error.message }); - console.log(` ❌ ${test.name}: ${error.message}`); - } - } - - console.log(' 📝 测试表单提交...'); - - const submitTest = { - name: '表单提交', - test: async () => { - await form.nameInput.fill('测试用户'); - await form.phoneInput.fill('13800138000'); - await form.emailInput.fill('test@example.com'); - await form.subjectInput.fill('测试主题'); - await form.messageInput.fill('这是一条测试消息'); - await form.submitButton.click(); - - await page.waitForTimeout(2000); - - const successMessage = await page.locator('[data-testid*="success"]').isVisible(); - const errorMessage = await page.locator('[data-testid*="error"]').isVisible(); - - if (successMessage) { - return { passed: true, message: '提交成功' }; - } else if (errorMessage) { - return { passed: false, message: '提交失败' }; - } else { - return { passed: true, message: '表单已提交(需后端验证)' }; - } - } - }; - - let submitResult; - try { - submitResult = await submitTest.test(); - console.log(` ${submitResult.passed ? '✅' : '❌'} ${submitResult.name}: ${submitResult.message}`); - } catch (error) { - submitResult = { passed: false, error: error.message }; - console.log(` ❌ ${submitTest.name}: ${error.message}`); - } - - await browser.close(); - - return { - page: '联系我们', - url: 'http://localhost:3000/contact', - visibility: visibilityResults, - validation: validationResults, - submit: submitResult - }; - - } catch (error) { - await browser.close(); - throw new Error(`表单测试失败: ${error.message}`); - } - } - - getSummary() { - const totalTests = this.results.reduce((sum, r) => - sum + r.visibility.length + r.validation.length + 1, 0); - const passedTests = this.results.reduce((sum, r) => - sum + r.visibility.filter(t => t.passed).length + - r.validation.filter(t => t.passed).length + - (r.submit?.passed ? 1 : 0), 0); - - return { - totalForms: this.results.length, - totalTests, - passedTests, - passRate: ((passedTests / totalTests) * 100).toFixed(1) - }; - } -} - -module.exports = { FormTester }; \ No newline at end of file diff --git a/scripts/utils/lighthouse-runner.js b/scripts/utils/lighthouse-runner.js deleted file mode 100644 index 9246198..0000000 --- a/scripts/utils/lighthouse-runner.js +++ /dev/null @@ -1,33 +0,0 @@ -const lighthouse = require('lighthouse').default; -const chromeLauncher = require('chrome-launcher'); -const fs = require('fs'); -const path = require('path'); - -async function runLighthouse(url, options = {}) { - const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] }); - options.port = chrome.port; - - const runnerResult = await lighthouse(url, options); - await chrome.kill(); - - return runnerResult; -} - -function generateReport(result, outputPath) { - const reportHtml = result.report; - fs.writeFileSync(outputPath, reportHtml); - return outputPath; -} - -function extractMetrics(result) { - const categories = result.lhr?.categories || {}; - return { - performance: categories.performance?.score * 100 || 0, - accessibility: categories.accessibility?.score * 100 || 0, - bestPractices: categories['best-practices']?.score * 100 || 0, - seo: categories.seo?.score * 100 || 0, - pwa: categories.pwa?.score * 100 || 0 - }; -} - -module.exports = { runLighthouse, generateReport, extractMetrics }; \ No newline at end of file diff --git a/scripts/utils/seo-validator.js b/scripts/utils/seo-validator.js deleted file mode 100644 index 59d80e5..0000000 --- a/scripts/utils/seo-validator.js +++ /dev/null @@ -1,136 +0,0 @@ -const { chromium } = require('playwright'); -const fs = require('fs'); - -class SEOValidator { - constructor() { - this.issues = []; - } - - async validatePage(url, pageName) { - const browser = await chromium.launch(); - const page = await browser.newPage(); - - try { - await page.goto(url, { waitUntil: 'networkidle' }); - - const checks = { - title: this.checkTitle(page), - description: this.checkDescription(page), - headings: this.checkHeadings(page), - images: this.checkImages(page), - links: this.checkLinks(page), - ogTags: this.checkOpenGraph(page), - canonical: this.checkCanonical(page), - h1: this.checkH1(page), - lang: this.checkLanguage(page) - }; - - const pageIssues = Object.entries(checks) - .filter(([_, result]) => !result.passed) - .map(([check, result]) => ({ - check, - issue: result.issue, - severity: result.severity - })); - - if (pageIssues.length > 0) { - this.issues.push({ - page: pageName, - url, - issues: pageIssues - }); - } - - await browser.close(); - return { checks, issues: pageIssues }; - - } catch (error) { - await browser.close(); - throw new Error(`SEO验证失败: ${error.message}`); - } - } - - checkTitle(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkDescription(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkHeadings(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkImages(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkLinks(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkOpenGraph(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkCanonical(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkH1(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - checkLanguage(page) { - return { - passed: true, - details: '需要手动验证' - }; - } - - getSummary() { - const totalIssues = this.issues.reduce((sum, page) => sum + page.issues.length, 0); - const pagesWithIssues = this.issues.length; - - return { - totalPages: this.issues.length + (this.issues.length === 0 ? 7 : 0), - pagesWithIssues, - totalIssues, - severity: { - high: this.issues.reduce((sum, page) => - sum + page.issues.filter(i => i.severity === 'high').length, 0), - medium: this.issues.reduce((sum, page) => - sum + page.issues.filter(i => i.severity === 'medium').length, 0), - low: this.issues.reduce((sum, page) => - sum + page.issues.filter(i => i.severity === 'low').length, 0) - } - }; - } -} - -module.exports = { SEOValidator }; \ No newline at end of file