Conversation
- Update vitest.config.js: coverage to builds/coverage/, add JSON reporter to builds/test-results/vitest-results.json - Update .gitignore: add builds/ directory - Update package.json: build:test-reports copies from builds/ to docs/ - Update release.yml: capture E2E output to builds/cypress/, generate HTML index pages for unit test results and E2E results, fix docs hub links to point to index.html Co-authored-by: pethers <[email protected]>
Co-authored-by: pethers <[email protected]>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
There was a problem hiding this comment.
Pull request overview
This PR restructures test report generation to route outputs through an intermediate builds/ directory (gitignored) instead of writing directly to docs/, preventing merge conflicts in CI/CD workflows. The release workflow now copies from builds/ to docs/ and generates HTML index pages for unit and E2E test results using inline Node.js scripts.
Changes:
- Test reports now route through intermediate
builds/directory to avoid merge conflicts - Vitest coverage and JSON results output to
builds/coverage/andbuilds/test-results/ - E2E test output captured via
teetobuilds/cypress/e2e-output.logfor HTML generation - Release workflow generates HTML index pages from test results using inline Node.js scripts
- Documentation hub links updated to point to explicit
index.htmlpaths
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| vitest.config.js | Updated coverage output to builds/coverage/ and added JSON reporter outputting to builds/test-results/vitest-results.json |
| .gitignore | Added builds/ directory to gitignore as intermediate staging area for test reports |
| package.json | Updated build:test-reports script to copy from builds/{coverage,test-results,cypress} to docs/ |
| .github/workflows/release.yml | Added E2E output capture with tee, generates HTML indexes from test results using inline Node.js scripts, updated doc hub links to explicit paths |
.github/workflows/release.yml
Outdated
| const tests = (r.assertionResults || []).map(t => | ||
| '<li>' + (t.status === 'passed' ? '✅' : '❌') + ' ' + t.fullName.replace(/</g,'<') + '</li>' | ||
| ).join(''); | ||
| return '<details><summary>' + status + ' ' + name + '</summary><ul>' + tests + '</ul></details>'; |
There was a problem hiding this comment.
The test name HTML escaping only handles < but not > or &. While > and & are less critical in this context since the content is inside an HTML element (not an attribute), it's still best practice to escape all HTML special characters to prevent potential XSS issues. Consider using the same comprehensive escaping pattern suggested for line 233.
| const tests = (r.assertionResults || []).map(t => | |
| '<li>' + (t.status === 'passed' ? '✅' : '❌') + ' ' + t.fullName.replace(/</g,'<') + '</li>' | |
| ).join(''); | |
| return '<details><summary>' + status + ' ' + name + '</summary><ul>' + tests + '</ul></details>'; | |
| const tests = (r.assertionResults || []).map(t => { | |
| const testStatus = t.status === 'passed' ? '✅' : '❌'; | |
| const escapedFullName = String(t.fullName || '').replace(/[&<>"']/g, c => ({ | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }[c])); | |
| return '<li>' + testStatus + ' ' + escapedFullName + '</li>'; | |
| }).join(''); | |
| const escapedName = String(name || '').replace(/[&<>"']/g, c => ({ | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }[c])); | |
| return '<details><summary>' + status + ' ' + escapedName + '</summary><ul>' + tests + '</ul></details>'; |
There was a problem hiding this comment.
Fixed in dbd1a42. Extracted to scripts/generate-test-results-html.cjs with a shared escapeHtml() function that escapes all five HTML special characters (&, <, >, ", ') for both test names and suite names.
package.json
Outdated
| "test:e2ereportmerge": "echo 'Cypress report merge placeholder - mochawesome-merge if multi-spec reports exist'", | ||
| "test:e2ereporthtmlall": "echo 'Cypress HTML report generation placeholder - mochawesome-report-generator'", | ||
| "build:test-reports": "mkdir -p docs/test-results docs/cypress && ([ -d coverage ] && cp -r coverage docs/ || echo 'No coverage directory to copy') && echo 'Test reports copied to docs/'", | ||
| "build:test-reports": "mkdir -p docs/test-results docs/cypress docs/coverage && ([ -d builds/coverage ] && cp -r builds/coverage/* docs/coverage/ || echo 'No coverage directory to copy') && ([ -d builds/test-results ] && cp -r builds/test-results/* docs/test-results/ || echo 'No test-results directory to copy') && ([ -d builds/cypress ] && cp -r builds/cypress/* docs/cypress/ || echo 'No cypress directory to copy') && echo 'Test reports copied to docs/'", |
There was a problem hiding this comment.
The copy command uses cp -r builds/coverage/* which will fail if the builds/coverage directory exists but is empty, because the shell glob won't match anything. This could cause the entire command chain to fail if set -e is enabled in the shell. Consider using one of these more robust approaches:
- Copy the entire directory:
[ -d builds/coverage ] && cp -r builds/coverage docs/ || echo '...' - Use find for more control:
[ -d builds/coverage ] && find builds/coverage -mindepth 1 -exec cp -r {} docs/coverage/ \; || echo '...' - Add explicit empty check:
[ -d builds/coverage ] && [ -n "$(ls -A builds/coverage 2>/dev/null)" ] && cp -r builds/coverage/* docs/coverage/ || echo '...'
| "build:test-reports": "mkdir -p docs/test-results docs/cypress docs/coverage && ([ -d builds/coverage ] && cp -r builds/coverage/* docs/coverage/ || echo 'No coverage directory to copy') && ([ -d builds/test-results ] && cp -r builds/test-results/* docs/test-results/ || echo 'No test-results directory to copy') && ([ -d builds/cypress ] && cp -r builds/cypress/* docs/cypress/ || echo 'No cypress directory to copy') && echo 'Test reports copied to docs/'", | |
| "build:test-reports": "mkdir -p docs/test-results docs/cypress docs/coverage && ([ -d builds/coverage ] && [ -n \"$(ls -A builds/coverage 2>/dev/null)\" ] && cp -r builds/coverage/* docs/coverage/ || echo 'No coverage directory to copy') && ([ -d builds/test-results ] && [ -n \"$(ls -A builds/test-results 2>/dev/null)\" ] && cp -r builds/test-results/* docs/test-results/ || echo 'No test-results directory to copy') && ([ -d builds/cypress ] && [ -n \"$(ls -A builds/cypress 2>/dev/null)\" ] && cp -r builds/cypress/* docs/cypress/ || echo 'No cypress directory to copy') && echo 'Test reports copied to docs/'", |
There was a problem hiding this comment.
Fixed in dbd1a42. Added [ -n "$(ls -A ... 2>/dev/null)" ] checks to guard against empty directories causing glob failures.
.github/workflows/release.yml
Outdated
| node -e " | ||
| const fs = require('fs'); | ||
| const data = JSON.parse(fs.readFileSync('docs/test-results/vitest-results.json', 'utf8')); | ||
| const passed = data.numPassedTests || 0; | ||
| const failed = data.numFailedTests || 0; | ||
| const total = data.numTotalTests || 0; | ||
| const duration = ((data.testResults || []).reduce((a, r) => a + (r.endTime - r.startTime), 0) / 1000).toFixed(2); | ||
| const suites = (data.testResults || []).map(r => { | ||
| const name = r.name.replace(/.*[\\/\\\\]/, ''); | ||
| const status = r.status === 'passed' ? '✅' : '❌'; | ||
| const tests = (r.assertionResults || []).map(t => | ||
| '<li>' + (t.status === 'passed' ? '✅' : '❌') + ' ' + t.fullName.replace(/</g,'<') + '</li>' | ||
| ).join(''); | ||
| return '<details><summary>' + status + ' ' + name + '</summary><ul>' + tests + '</ul></details>'; | ||
| }).join(''); | ||
| const html = '<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>Unit Test Results</title><style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:\"Segoe UI\",sans-serif;line-height:1.6;color:#e0e0e0;background:linear-gradient(135deg,#0a0e27,#1a1e3d);min-height:100vh;padding:2rem}.container{max-width:900px;margin:0 auto}h1{color:#00d9ff;margin-bottom:1rem}h2{color:#ffbe0b;margin:1.5rem 0 .5rem}.stats{display:flex;gap:2rem;margin:1rem 0;flex-wrap:wrap}.stat{background:rgba(26,30,61,.6);border:1px solid rgba(0,217,255,.3);border-radius:8px;padding:1rem 1.5rem}.stat .num{font-size:2rem;font-weight:bold}.pass{color:#00ff88}.fail{color:#ff006e}.total{color:#00d9ff}details{background:rgba(26,30,61,.4);border:1px solid rgba(0,217,255,.2);border-radius:4px;margin:.5rem 0;padding:.5rem 1rem}summary{cursor:pointer;font-weight:bold}ul{margin:.5rem 0 .5rem 1.5rem}li{margin:.25rem 0}a{color:#00d9ff}</style></head><body><div class=\"container\"><h1>🧪 Unit Test Results</h1><div class=\"stats\"><div class=\"stat\"><div class=\"num pass\">' + passed + '</div>Passed</div><div class=\"stat\"><div class=\"num fail\">' + failed + '</div>Failed</div><div class=\"stat\"><div class=\"num total\">' + total + '</div>Total</div><div class=\"stat\"><div class=\"num total\">' + duration + 's</div>Duration</div></div><h2>Test Suites</h2>' + suites + '<p style=\"margin-top:2rem\"><a href=\"../index.html\">← Back to Documentation Hub</a></p></div></body></html>'; | ||
| fs.writeFileSync('docs/test-results/index.html', html); | ||
| " |
There was a problem hiding this comment.
The inline Node.js script doesn't have explicit error handling. If JSON parsing fails or file operations fail, the error will be printed to stderr but the shell will continue executing (the script won't exit non-zero). Consider wrapping the script in a try-catch or explicitly checking for errors:
node -e "
try {
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('docs/test-results/vitest-results.json', 'utf8'));
// ... rest of script
} catch (error) {
console.error('Failed to generate unit test results HTML:', error.message);
process.exit(1);
}
" || echo "⚠️ Failed to generate unit test results HTML"This ensures the workflow is aware if HTML generation fails.
There was a problem hiding this comment.
Fixed in dbd1a42. Extracted to scripts/generate-test-results-html.cjs with try-catch that calls process.exit(1) on failure. The workflow step uses || echo "⚠️ ..." as a fallback.
.github/workflows/release.yml
Outdated
| fs.writeFileSync('docs/cypress/index.html', html); | ||
| " |
There was a problem hiding this comment.
The inline Node.js scripts for HTML generation (221-241 for unit tests, 244-258 for E2E tests) are becoming quite large and complex. Consider extracting these to separate script files (e.g., scripts/generate-test-html.js and scripts/generate-e2e-html.js) for better maintainability, testability, and reusability. This would also make error handling, formatting, and future enhancements easier to manage.
Benefits:
- Easier to test the HTML generation logic
- Better syntax highlighting and IDE support
- Simpler to add features (e.g., more sophisticated parsing, better formatting)
- Can be reused in other contexts (e.g., local development)
- Clearer separation of concerns in the workflow file
| fs.writeFileSync('docs/cypress/index.html', html); | |
| " | |
| if [ -f "docs/cypress/e2e-output.log" ]; then | |
| mkdir -p scripts | |
| cat > scripts/generate-e2e-html.js << 'EOF' | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const logPath = path.join('docs', 'cypress', 'e2e-output.log'); | |
| const outputDir = path.join('docs', 'cypress'); | |
| const outputPath = path.join(outputDir, 'index.html'); | |
| if (!fs.existsSync(logPath)) { | |
| console.error('E2E log file not found:', logPath); | |
| process.exit(0); | |
| } | |
| const rawLog = fs.readFileSync(logPath, 'utf8'); | |
| const escapeHtml = (str) => | |
| str | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>'); | |
| const escapedLog = escapeHtml(rawLog); | |
| const html = `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>Cypress E2E Test Results</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <style> | |
| body { | |
| font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| background-color: #0a0e27; | |
| color: #e0e0e0; | |
| padding: 1.5rem; | |
| } | |
| h1 { | |
| color: #00d9ff; | |
| margin-bottom: 1rem; | |
| } | |
| pre { | |
| background-color: #1a1e3d; | |
| border-radius: 4px; | |
| padding: 1rem; | |
| overflow-x: auto; | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Cypress E2E Test Results</h1> | |
| <pre>${escapedLog}</pre> | |
| </body> | |
| </html>`; | |
| if (!fs.existsSync(outputDir)) { | |
| fs.mkdirSync(outputDir, { recursive: true }); | |
| } | |
| fs.writeFileSync(outputPath, html, 'utf8'); | |
| EOF | |
| node scripts/generate-e2e-html.js |
There was a problem hiding this comment.
Fixed in dbd1a42. Extracted to scripts/generate-e2e-results-html.cjs with proper error handling, comprehensive HTML escaping, and clean template literal formatting.
vitest.config.js
Outdated
| // Reporter configuration - verbose to console, JSON to builds/ for release docs | ||
| reporters: [ | ||
| 'verbose', | ||
| ['json', { outputFile: 'builds/test-results/vitest-results.json' }] |
There was a problem hiding this comment.
The directory builds/test-results/ is hardcoded in the JSON reporter configuration. Since the reporters configuration is an array that can include multiple reporters, if someone adds a second JSON reporter in the future, there could be conflicts. Consider using a more descriptive filename or documenting that this path is coupled with the release workflow.
Alternatively, consider making this configurable via an environment variable:
['json', { outputFile: process.env.VITEST_JSON_OUTPUT || 'builds/test-results/vitest-results.json' }]This would allow for easier testing and flexibility in different environments.
| ['json', { outputFile: 'builds/test-results/vitest-results.json' }] | |
| ['json', { outputFile: process.env.VITEST_JSON_OUTPUT || 'builds/test-results/vitest-results.json' }] |
There was a problem hiding this comment.
Fixed in dbd1a42. Now reads process.env.VITEST_JSON_OUTPUT || 'builds/test-results/vitest-results.json'.
.github/workflows/release.yml
Outdated
| const total = data.numTotalTests || 0; | ||
| const duration = ((data.testResults || []).reduce((a, r) => a + (r.endTime - r.startTime), 0) / 1000).toFixed(2); | ||
| const suites = (data.testResults || []).map(r => { | ||
| const name = r.name.replace(/.*[\\/\\\\]/, ''); |
There was a problem hiding this comment.
The regex pattern /.*[\\/\\\\]/ is incorrect and will not match directory separators properly. The intent is to match either forward slash or backslash, but the pattern should be /.*[\/\\]/ instead. The current pattern [\\/\\\\] creates a character class containing backslash, forward slash, and two additional backslashes, which is redundant and confusing.
This should be: r.name.replace(/.*[\/\\]/, '') or the more portable: r.name.replace(/.*[\\/]/, '')
| const name = r.name.replace(/.*[\\/\\\\]/, ''); | |
| const name = r.name.replace(/.*[\\/]/, ''); |
There was a problem hiding this comment.
Fixed in dbd1a42. Simplified to r.name.replace(/.*[\\/]/, '') in the extracted script.
.github/workflows/release.yml
Outdated
| const passed = data.numPassedTests || 0; | ||
| const failed = data.numFailedTests || 0; | ||
| const total = data.numTotalTests || 0; | ||
| const duration = ((data.testResults || []).reduce((a, r) => a + (r.endTime - r.startTime), 0) / 1000).toFixed(2); |
There was a problem hiding this comment.
The duration calculation will produce NaN if any test result is missing endTime or startTime properties. Consider adding validation and a fallback:
const duration = ((data.testResults || []).reduce((a, r) => a + ((r.endTime || 0) - (r.startTime || 0)), 0) / 1000).toFixed(2);
Or better yet, filter out invalid entries before reducing:
const duration = ((data.testResults || []).filter(r => r.endTime && r.startTime).reduce((a, r) => a + (r.endTime - r.startTime), 0) / 1000).toFixed(2);
| const duration = ((data.testResults || []).reduce((a, r) => a + (r.endTime - r.startTime), 0) / 1000).toFixed(2); | |
| const validResults = (data.testResults || []).filter(r => typeof r.endTime === 'number' && typeof r.startTime === 'number'); | |
| const durationMs = validResults.reduce((a, r) => a + (r.endTime - r.startTime), 0); | |
| const duration = (durationMs / 1000).toFixed(2); |
There was a problem hiding this comment.
Fixed in dbd1a42. Now filters with typeof r.endTime === 'number' && typeof r.startTime === 'number' before reducing.
.github/workflows/release.yml
Outdated
| const name = r.name.replace(/.*[\\/\\\\]/, ''); | ||
| const status = r.status === 'passed' ? '✅' : '❌'; | ||
| const tests = (r.assertionResults || []).map(t => | ||
| '<li>' + (t.status === 'passed' ? '✅' : '❌') + ' ' + t.fullName.replace(/</g,'<') + '</li>' |
There was a problem hiding this comment.
The test name HTML escaping only handles < but not other dangerous characters like >, &, ", and '. For complete XSS prevention in HTML content, all HTML special characters should be escaped. Consider using:
t.fullName.replace(/[&<>"']/g, m => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m]))
Or at minimum add escaping for > and &:
t.fullName.replace(/[&<>]/g, m => ({ '&': '&', '<': '<', '>': '>' }[m]))
There was a problem hiding this comment.
Fixed in dbd1a42. The extracted escapeHtml() function handles all five characters: & → &, < → <, > → >, " → ", ' → '.
.github/workflows/release.yml
Outdated
| echo "🚀 Running E2E tests for release validation..." | ||
| NODE_OPTIONS="--max-old-space-size=4096" xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24 -ac +extension GLX +extension RANDR +render -nolisten tcp" npm run e2e || TEST_EXIT_CODE=$? | ||
| mkdir -p builds/cypress | ||
| NODE_OPTIONS="--max-old-space-size=4096" xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24 -ac +extension GLX +extension RANDR +render -nolisten tcp" npm run e2e 2>&1 | tee builds/cypress/e2e-output.log; TEST_EXIT_CODE=${PIPESTATUS[0]} |
There was a problem hiding this comment.
The tee command with PIPESTATUS correctly captures the exit code of the npm run e2e command before the pipe. However, there's a subtle issue: if the xvfb-run command itself fails (before npm run e2e even starts), the exit code won't be captured properly. Consider wrapping the entire xvfb-run command to ensure exit codes are preserved:
set -o pipefail
xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24 -ac +extension GLX +extension RANDR +render -nolisten tcp" npm run e2e 2>&1 | tee builds/cypress/e2e-output.log
TEST_EXIT_CODE=$?
exit ${TEST_EXIT_CODE}Or more concisely with the existing approach, explicitly check if xvfb-run succeeded:
if ! xvfb-run ... npm run e2e 2>&1 | tee builds/cypress/e2e-output.log; then
TEST_EXIT_CODE=${PIPESTATUS[0]}
exit ${TEST_EXIT_CODE:-1}
fi| NODE_OPTIONS="--max-old-space-size=4096" xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24 -ac +extension GLX +extension RANDR +render -nolisten tcp" npm run e2e 2>&1 | tee builds/cypress/e2e-output.log; TEST_EXIT_CODE=${PIPESTATUS[0]} | |
| set -o pipefail | |
| NODE_OPTIONS="--max-old-space-size=4096" \ | |
| xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24 -ac +extension GLX +extension RANDR +render -nolisten tcp" \ | |
| npm run e2e 2>&1 | tee builds/cypress/e2e-output.log | |
| TEST_EXIT_CODE=$? |
There was a problem hiding this comment.
Fixed in dbd1a42. Now uses set -o pipefail with $? capture after the pipeline, ensuring failures from both xvfb-run and npm run e2e are properly propagated.
.github/workflows/release.yml
Outdated
| node -e " | ||
| const fs = require('fs'); | ||
| const log = fs.readFileSync('docs/cypress/e2e-output.log', 'utf8'); | ||
| const escaped = log.replace(/</g,'<').replace(/>/g,'>'); | ||
| const passMatch = log.match(/All specs passed!|(\d+) passing/); | ||
| const failMatch = log.match(/(\d+) failing/); | ||
| const status = failMatch ? '❌ Some tests failed' : (passMatch ? '✅ All tests passed' : 'ℹ️ Test results'); | ||
| const hasScreenshots = fs.existsSync('docs/cypress/screenshots'); | ||
| const screenshotLinks = hasScreenshots ? '<h2>Screenshots</h2><p>Available in <a href=\"screenshots/\">screenshots/</a></p>' : ''; | ||
| const html = '<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>E2E Test Results</title><style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:\"Segoe UI\",sans-serif;line-height:1.6;color:#e0e0e0;background:linear-gradient(135deg,#0a0e27,#1a1e3d);min-height:100vh;padding:2rem}.container{max-width:900px;margin:0 auto}h1{color:#00d9ff;margin-bottom:1rem}h2{color:#ffbe0b;margin:1.5rem 0 .5rem}.status{font-size:1.3rem;margin:1rem 0}pre{background:rgba(0,0,0,.4);border:1px solid rgba(0,217,255,.2);border-radius:8px;padding:1.5rem;overflow-x:auto;font-size:.85rem;max-height:600px;overflow-y:auto}a{color:#00d9ff}</style></head><body><div class=\"container\"><h1>🎭 E2E Test Results (Cypress)</h1><p class=\"status\">' + status + '</p>' + screenshotLinks + '<h2>Test Output</h2><pre>' + escaped + '</pre><p style=\"margin-top:2rem\"><a href=\"../index.html\">← Back to Documentation Hub</a></p></div></body></html>'; | ||
| fs.writeFileSync('docs/cypress/index.html', html); | ||
| " | ||
| echo "✅ E2E test results HTML generated at docs/cypress/index.html" |
There was a problem hiding this comment.
Same issue as the unit test HTML generation - the inline Node.js script lacks explicit error handling. If JSON parsing or file operations fail, the error won't properly propagate to the workflow. Consider adding try-catch error handling similar to the suggestion for the unit test HTML generation script.
There was a problem hiding this comment.
Fixed in dbd1a42. Extracted to scripts/generate-e2e-results-html.cjs with try-catch and process.exit(1) on failure.
|
@copilot apply changes based on the comments in this thread |
- Extract inline Node.js HTML generators to scripts/generate-test-results-html.cjs and scripts/generate-e2e-results-html.cjs for maintainability and testability - Add comprehensive HTML escaping (& < > " ') preventing XSS - Add try-catch error handling with proper process.exit(1) - Fix NaN duration: filter out results missing endTime/startTime - Fix regex: simplify path separator matching to /.*[\\/]/ - Use set -o pipefail for proper E2E exit code propagation - Make vitest JSON output path configurable via VITEST_JSON_OUTPUT env var - Add empty directory checks in build:test-reports copy commands Co-authored-by: pethers <[email protected]>
Applied all review feedback in commit
|
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
|
|
||
| const inputPath = path.join('docs', 'test-results', 'vitest-results.json'); | ||
| const outputPath = path.join('docs', 'test-results', 'index.html'); | ||
|
|
||
| function escapeHtml(str) { | ||
| return String(str || '').replace(/[&<>"']/g, c => ({ | ||
| '&': '&', | ||
| '<': '<', | ||
| '>': '>', | ||
| '"': '"', | ||
| "'": ''' | ||
| }[c])); | ||
| } | ||
|
|
There was a problem hiding this comment.
The single quote is escaped as ''' but the canonical implementation in scripts/html-utils.js uses ''' (3-digit form). While both are valid HTML entities, this creates an inconsistency with the established pattern used throughout the codebase. Consider importing and using the escapeHtml function from scripts/html-utils.js instead of reimplementing it, which would ensure consistency and reduce duplication.
| const inputPath = path.join('docs', 'test-results', 'vitest-results.json'); | |
| const outputPath = path.join('docs', 'test-results', 'index.html'); | |
| function escapeHtml(str) { | |
| return String(str || '').replace(/[&<>"']/g, c => ({ | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }[c])); | |
| } | |
| const { escapeHtml } = require('./html-utils'); | |
| const inputPath = path.join('docs', 'test-results', 'vitest-results.json'); | |
| const outputPath = path.join('docs', 'test-results', 'index.html'); |
|
|
||
| const logPath = path.join('docs', 'cypress', 'e2e-output.log'); | ||
| const outputPath = path.join('docs', 'cypress', 'index.html'); | ||
|
|
||
| function escapeHtml(str) { | ||
| return String(str || '').replace(/[&<>"']/g, c => ({ | ||
| '&': '&', | ||
| '<': '<', | ||
| '>': '>', | ||
| '"': '"', | ||
| "'": ''' | ||
| }[c])); | ||
| } | ||
|
|
There was a problem hiding this comment.
The single quote is escaped as ''' but the canonical implementation in scripts/html-utils.js uses ''' (3-digit form). While both are valid HTML entities, this creates an inconsistency with the established pattern used throughout the codebase. Consider importing and using the escapeHtml function from scripts/html-utils.js instead of reimplementing it, which would ensure consistency and reduce duplication.
| const logPath = path.join('docs', 'cypress', 'e2e-output.log'); | |
| const outputPath = path.join('docs', 'cypress', 'index.html'); | |
| function escapeHtml(str) { | |
| return String(str || '').replace(/[&<>"']/g, c => ({ | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }[c])); | |
| } | |
| const { escapeHtml } = require('./html-utils'); | |
| const logPath = path.join('docs', 'cypress', 'e2e-output.log'); | |
| const outputPath = path.join('docs', 'cypress', 'index.html'); |
Release workflow updates
docs/api/anddocs/coverage/but never populatesdocs/test-results/ordocs/cypress/— those directories were created empty. Normal test runs also risk merge conflicts by writing directly todocs/.Changes
vitest.config.js: Coverage output →builds/coverage/, added JSON reporter →builds/test-results/vitest-results.json(configurable viaVITEST_JSON_OUTPUTenv var).gitignore: Addedbuilds/as intermediate report output directorypackage.json:build:test-reportsnow copies frombuilds/{coverage,test-results,cypress}→docs/with empty-directory guardsscripts/generate-test-results-html.cjs: Extracted unit test HTML report generator with comprehensive HTML escaping (&,<,>,",'), try-catch error handling, and NaN-safe duration calculationscripts/generate-e2e-results-html.cjs: Extracted E2E HTML report generator with comprehensive HTML escaping and try-catch error handlingrelease.yml:tee builds/cypress/e2e-output.logwithset -o pipefailfor proper exit code propagationbuilds/cypress/docs/test-results/index.htmlanddocs/cypress/index.htmlindex.htmlpathsFlow
No new dependencies added.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.