Skip to content

Commit 5b2b5aa

Browse files
committed
feat: Work-in-progress test report
1 parent fa8db93 commit 5b2b5aa

File tree

7 files changed

+1898
-235
lines changed

7 files changed

+1898
-235
lines changed

package-lock.json

Lines changed: 1571 additions & 230 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"docdash": "^1.2.0",
5353
"eslint": "^7.4.0",
5454
"eslint-plugin-jasmine": "^4.1.1",
55+
"express": "^4.18.2",
5556
"folder-delete": "^1.0.4",
5657
"inquirer": "^6.5.2",
5758
"jasmine": "3.5.0",
@@ -64,8 +65,9 @@
6465
"karma-coverage": "2.0.2",
6566
"karma-firefox-launcher": "1.3.0",
6667
"karma-ie-launcher": "1.0.0",
67-
"karma-jasmine": "3.3.1",
68+
"karma-jasmine": "^3.3.1",
6869
"karma-jasmine-matchers": "4.0.2",
70+
"karma-mocha": "^2.0.1",
6971
"karma-mocha-reporter": "2.2.5",
7072
"karma-rollup-preprocessor": "^7.0.7",
7173
"karma-sauce-launcher": "4.1.5",
@@ -85,6 +87,7 @@
8587
"rollup-plugin-preprocess": "0.0.4",
8688
"rollup-plugin-terser": "^6.1.0",
8789
"typescript": "^5.6.2",
90+
"webpack": "^5.97.1",
8891
"yarpm": "^0.2.1"
8992
},
9093
"scripts": {
@@ -107,7 +110,8 @@
107110
"prettier": "prettier --write \"*.{js,ts,md,css,json}\" \"{spec,examples,src,types}/**/*.{js,ts,md,css,json}\"",
108111
"lint": "prettier --check \"*.{js,ts,md,css,json}\" \"{spec,examples,src,types}/**/*.{js,ts,md,css,json}\"",
109112
"pregenerate-docs": "node deletedocs.js",
110-
"generate-docs": "jsdoc -c jsdoc.json --readme README.md"
113+
"generate-docs": "jsdoc -c jsdoc.json --readme README.md",
114+
"test-report": "node test/report/server.js"
111115
},
112116
"browserslist": [
113117
"last 2 versions",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
function generateReportData(testResults) {
5+
const summary = {
6+
total: testResults.total,
7+
passed: testResults.passed,
8+
skipped: testResults.skipped,
9+
failed: testResults.failed
10+
};
11+
12+
const failures = testResults.failures.map(failure => ({
13+
name: failure.name,
14+
actualPdf: failure.actualPdf,
15+
referencePdf: failure.referencePdf,
16+
differences: {
17+
total: failure.differences.total,
18+
patterns: failure.differences.patterns.map(pattern => ({
19+
type: pattern.type,
20+
count: pattern.count,
21+
sample: {
22+
expected: pattern.sample.expected,
23+
actual: pattern.sample.actual
24+
}
25+
}))
26+
}
27+
}));
28+
29+
return {
30+
summary,
31+
failures
32+
};
33+
}
34+
35+
function writeReportData(data) {
36+
const reportDir = path.join(__dirname);
37+
const dataFile = path.join(reportDir, 'test-results.js');
38+
39+
const content = `window.TEST_RESULTS = ${JSON.stringify(data, null, 2)};`;
40+
41+
fs.writeFileSync(dataFile, content);
42+
}
43+
44+
module.exports = {
45+
generateReportData,
46+
writeReportData
47+
};

test/report/index.html

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>jsPDF Test Report</title>
7+
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
8+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
9+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@radix-ui/[email protected]/styles.css">
11+
<script src="test-results.js"></script>
12+
<style>
13+
:root {
14+
--background: 0 0% 100%;
15+
--foreground: 222.2 84% 4.9%;
16+
--muted: 210 40% 96.1%;
17+
--muted-foreground: 215.4 16.3% 46.9%;
18+
--popover: 0 0% 100%;
19+
--popover-foreground: 222.2 84% 4.9%;
20+
--card: 0 0% 100%;
21+
--card-foreground: 222.2 84% 4.9%;
22+
--border: 214.3 31.8% 91.4%;
23+
--input: 214.3 31.8% 91.4%;
24+
--primary: 222.2 47.4% 11.2%;
25+
--primary-foreground: 210 40% 98%;
26+
--secondary: 210 40% 96.1%;
27+
--secondary-foreground: 222.2 47.4% 11.2%;
28+
--accent: 210 40% 96.1%;
29+
--accent-foreground: 222.2 47.4% 11.2%;
30+
--destructive: 0 84.2% 60.2%;
31+
--destructive-foreground: 210 40% 98%;
32+
--ring: 215 20.2% 65.1%;
33+
--radius: 0.5rem;
34+
}
35+
36+
* {
37+
margin: 0;
38+
padding: 0;
39+
box-sizing: border-box;
40+
}
41+
42+
body {
43+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
44+
background-color: hsl(var(--background));
45+
color: hsl(var(--foreground));
46+
}
47+
48+
.layout {
49+
display: grid;
50+
grid-template-columns: 300px 1fr;
51+
height: 100vh;
52+
}
53+
54+
.sidebar {
55+
border-right: 1px solid hsl(var(--border));
56+
padding: 1rem;
57+
overflow-y: auto;
58+
}
59+
60+
.main {
61+
padding: 1rem;
62+
overflow-y: auto;
63+
}
64+
65+
.test-item {
66+
padding: 0.75rem;
67+
border: 1px solid hsl(var(--border));
68+
border-radius: var(--radius);
69+
margin-bottom: 0.5rem;
70+
cursor: pointer;
71+
}
72+
73+
.test-item:hover {
74+
background-color: hsl(var(--muted));
75+
}
76+
77+
.test-item.failed {
78+
border-color: hsl(var(--destructive));
79+
}
80+
81+
.pdf-viewer {
82+
width: 100%;
83+
height: calc(100vh - 2rem);
84+
border: 1px solid hsl(var(--border));
85+
border-radius: var(--radius);
86+
}
87+
88+
.viewer-controls {
89+
display: flex;
90+
gap: 1rem;
91+
margin-bottom: 1rem;
92+
}
93+
94+
.button {
95+
padding: 0.5rem 1rem;
96+
background-color: hsl(var(--primary));
97+
color: hsl(var(--primary-foreground));
98+
border: none;
99+
border-radius: var(--radius);
100+
cursor: pointer;
101+
}
102+
103+
.button:hover {
104+
opacity: 0.9;
105+
}
106+
107+
.button.secondary {
108+
background-color: hsl(var(--secondary));
109+
color: hsl(var(--secondary-foreground));
110+
}
111+
112+
.hex-view {
113+
font-family: monospace;
114+
white-space: pre-wrap;
115+
padding: 1rem;
116+
background-color: hsl(var(--muted));
117+
border-radius: var(--radius);
118+
}
119+
</style>
120+
</head>
121+
<body>
122+
<div id="root"></div>
123+
<script type="text/babel">
124+
function App() {
125+
const [selectedTest, setSelectedTest] = React.useState(null);
126+
const [viewMode, setViewMode] = React.useState('actual');
127+
const [testResults, setTestResults] = React.useState(window.TEST_RESULTS);
128+
129+
return (
130+
<div className="layout">
131+
<div className="sidebar">
132+
<h2>Test Summary</h2>
133+
<div style={{ margin: '1rem 0' }}>
134+
<div>Total: {testResults.summary.total}</div>
135+
<div style={{ color: 'green' }}>Passed: {testResults.summary.passed}</div>
136+
<div style={{ color: 'orange' }}>Skipped: {testResults.summary.skipped}</div>
137+
<div style={{ color: 'red' }}>Failed: {testResults.summary.failed}</div>
138+
</div>
139+
<h3>Failed Tests</h3>
140+
{testResults.failures.map((test, index) => (
141+
<div
142+
key={index}
143+
className="test-item failed"
144+
onClick={() => setSelectedTest(test)}
145+
>
146+
<div>{test.name}</div>
147+
<div style={{ fontSize: '0.875rem', color: 'hsl(var(--muted-foreground))' }}>
148+
{test.differences.total} differences
149+
</div>
150+
</div>
151+
))}
152+
</div>
153+
<div className="main">
154+
{selectedTest ? (
155+
<>
156+
<div className="viewer-controls">
157+
<button
158+
className={`button ${viewMode === 'actual' ? '' : 'secondary'}`}
159+
onClick={() => setViewMode('actual')}
160+
>
161+
Actual
162+
</button>
163+
<button
164+
className={`button ${viewMode === 'reference' ? '' : 'secondary'}`}
165+
onClick={() => setViewMode('reference')}
166+
>
167+
Reference
168+
</button>
169+
<button
170+
className={`button ${viewMode === 'hex' ? '' : 'secondary'}`}
171+
onClick={() => setViewMode('hex')}
172+
>
173+
Hex Diff
174+
</button>
175+
</div>
176+
{viewMode === 'hex' ? (
177+
<div className="hex-view">
178+
{/* Placeholder for hex diff */}
179+
Hex diff view coming soon...
180+
</div>
181+
) : (
182+
<iframe
183+
className="pdf-viewer"
184+
src={viewMode === 'actual' ? selectedTest.actualPdf : selectedTest.referencePdf}
185+
/>
186+
)}
187+
</>
188+
) : (
189+
<div style={{ padding: '2rem', textAlign: 'center' }}>
190+
Select a test from the sidebar to view details
191+
</div>
192+
)}
193+
</div>
194+
</div>
195+
);
196+
}
197+
198+
const root = ReactDOM.createRoot(document.getElementById('root'));
199+
root.render(<App />);
200+
</script>
201+
</body>
202+
</html>

test/report/server.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const express = require('express');
2+
const path = require('path');
3+
const app = express();
4+
5+
// Serve static files from the report directory
6+
app.use(express.static(__dirname));
7+
8+
// Serve PDFs from the test directory
9+
app.use('/test', express.static(path.join(__dirname, '..')));
10+
11+
const port = process.env.PORT || 3000;
12+
app.listen(port, () => {
13+
console.log(`Test report server running at http://localhost:${port}`);
14+
});

test/report/test-results.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
window.TEST_RESULTS = {
2+
"summary": {
3+
"passed": 488,
4+
"skipped": 7,
5+
"failed": 8
6+
},
7+
"failures": []
8+
};

test/unit/karma.conf.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,43 @@
22
"use strict";
33
const karmaConfig = require("../karma.common.conf.js");
44
const resolve = require("rollup-plugin-node-resolve");
5+
const { generateReportData, writeReportData } = require('../report/generate-report-data');
6+
7+
const TestReporter = function(baseReporterDecorator) {
8+
baseReporterDecorator(this);
9+
10+
this.onRunComplete = function(browsers, results) {
11+
const testResults = {
12+
total: results.total,
13+
passed: results.success,
14+
failed: results.failed,
15+
skipped: results.skipped,
16+
failures: []
17+
};
18+
19+
// Process failures
20+
browsers.forEach(browser => {
21+
if (browser.lastResult && browser.lastResult.failed) {
22+
Object.values(browser.lastResult.failedSpecs || {}).forEach(failure => {
23+
testResults.failures.push({
24+
name: failure.suite.join(' ') + ' - ' + failure.description,
25+
actualPdf: failure.log[0]?.match(/Actual PDF saved to: (.+\.pdf)/)?.[1],
26+
referencePdf: 'test/reference/' + failure.log[0]?.match(/Actual PDF saved to: test\/actual\/(.+\.pdf)/)?.[1],
27+
differences: {
28+
total: failure.log[0]?.match(/Total differences: (\d+)/)?.[1] || 0,
29+
patterns: []
30+
}
31+
});
32+
});
33+
}
34+
});
35+
36+
const reportData = generateReportData(testResults);
37+
writeReportData(reportData);
38+
};
39+
};
40+
41+
TestReporter.$inject = ['baseReporterDecorator'];
542

643
module.exports = config => {
744
config.set({
@@ -48,17 +85,27 @@ module.exports = config => {
4885
}
4986
},
5087

51-
browsers: ["Chrome", "Firefox"],
88+
browsers: ["Chrome"],
89+
5290
// test results reporter to use
5391
// possible values: 'dots', 'progress'
5492
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
55-
reporters: ["mocha", "coverage"],
93+
reporters: ["mocha", "coverage", "test-report"],
5694

5795
// level of logging
5896
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
5997
logLevel: config.LOG_INFO,
6098

6199
// enable / disable watching file and executing tests whenever any file changes
62-
autoWatch: true
100+
autoWatch: true,
101+
102+
plugins: [
103+
'karma-chrome-launcher',
104+
'karma-coverage',
105+
'karma-jasmine',
106+
'karma-mocha-reporter',
107+
'karma-rollup-preprocessor',
108+
{'reporter:test-report': ['type', TestReporter]}
109+
]
63110
});
64111
};

0 commit comments

Comments
 (0)