Skip to content

Commit bb649e2

Browse files
committed
feat: Working PDF report compare thing
1 parent 5b2b5aa commit bb649e2

File tree

5 files changed

+222
-216
lines changed

5 files changed

+222
-216
lines changed

test/report/generate-report-data.js

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,8 @@ const fs = require('fs');
22
const path = require('path');
33

44
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-
};
5+
// Pass through the test results directly since they're already in the correct format
6+
return testResults;
337
}
348

359
function writeReportData(data) {

test/report/index.html

Lines changed: 155 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,202 +1,194 @@
11
<!DOCTYPE html>
2-
<html lang="en">
2+
<html>
33
<head>
4-
<meta charset="UTF-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
64
<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>
125
<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-
426
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));
7+
margin: 0;
8+
font-family: system-ui, -apple-system, sans-serif;
469
}
47-
48-
.layout {
10+
.container {
4911
display: grid;
5012
grid-template-columns: 300px 1fr;
5113
height: 100vh;
5214
}
53-
5415
.sidebar {
55-
border-right: 1px solid hsl(var(--border));
56-
padding: 1rem;
16+
background: #f5f5f5;
17+
padding: 20px;
18+
border-right: 1px solid #ddd;
5719
overflow-y: auto;
5820
}
59-
6021
.main {
61-
padding: 1rem;
22+
padding: 20px;
6223
overflow-y: auto;
6324
}
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;
25+
.summary {
26+
margin-bottom: 20px;
27+
padding: 15px;
28+
background: white;
29+
border-radius: 8px;
30+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
31+
}
32+
.failure-item {
33+
padding: 10px;
34+
margin: 5px 0;
35+
background: white;
36+
border-radius: 4px;
7037
cursor: pointer;
38+
border: 1px solid #ddd;
7139
}
72-
73-
.test-item:hover {
74-
background-color: hsl(var(--muted));
40+
.failure-item:hover {
41+
background: #f0f0f0;
7542
}
76-
77-
.test-item.failed {
78-
border-color: hsl(var(--destructive));
43+
.failure-item.selected {
44+
background: #e3f2fd;
45+
border-color: #2196f3;
7946
}
80-
81-
.pdf-viewer {
82-
width: 100%;
83-
height: calc(100vh - 2rem);
84-
border: 1px solid hsl(var(--border));
85-
border-radius: var(--radius);
47+
.pdf-view {
48+
display: grid;
49+
grid-template-columns: 1fr 1fr;
50+
gap: 20px;
51+
margin-top: 20px;
8652
}
87-
88-
.viewer-controls {
89-
display: flex;
90-
gap: 1rem;
91-
margin-bottom: 1rem;
53+
.pdf-container {
54+
border: 1px solid #ddd;
55+
padding: 10px;
56+
border-radius: 4px;
9257
}
93-
94-
.button {
95-
padding: 0.5rem 1rem;
96-
background-color: hsl(var(--primary));
97-
color: hsl(var(--primary-foreground));
58+
.pdf-container h3 {
59+
margin-top: 0;
60+
}
61+
iframe {
62+
width: 100%;
63+
height: 800px;
9864
border: none;
99-
border-radius: var(--radius);
100-
cursor: pointer;
10165
}
102-
103-
.button:hover {
104-
opacity: 0.9;
66+
.differences {
67+
white-space: pre-wrap;
68+
font-family: monospace;
69+
background: #f8f8f8;
70+
padding: 10px;
71+
border-radius: 4px;
72+
margin-top: 20px;
10573
}
106-
107-
.button.secondary {
108-
background-color: hsl(var(--secondary));
109-
color: hsl(var(--secondary-foreground));
74+
.section {
75+
margin-bottom: 30px;
11076
}
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);
77+
.section h3 {
78+
margin-bottom: 10px;
11879
}
11980
</style>
12081
</head>
12182
<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);
83+
<div class="container">
84+
<div class="sidebar">
85+
<div class="summary section">
86+
<h2>Test Summary</h2>
87+
<div id="summary"></div>
88+
</div>
89+
<div class="section">
90+
<h3>Failed Tests</h3>
91+
<div id="failures"></div>
92+
</div>
93+
<div class="section">
94+
<h3>Actual PDFs</h3>
95+
<div id="pdfs"></div>
96+
</div>
97+
</div>
98+
<div class="main">
99+
<div id="details"></div>
100+
</div>
101+
</div>
102+
103+
<!-- Load test results -->
104+
<script src="test-results.js"></script>
128105

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-
))}
106+
<script>
107+
let selectedItem = null;
108+
109+
function formatSummary(results) {
110+
return `
111+
<div>Total: ${results.total}</div>
112+
<div style="color: #4caf50">Passed: ${results.passed}</div>
113+
<div style="color: #f44336">Failed: ${results.failed}</div>
114+
<div style="color: #ff9800">Skipped: ${results.skipped}</div>
115+
`;
116+
}
117+
118+
function showPdfComparison(item) {
119+
const details = document.getElementById('details');
120+
121+
// Update selected state in sidebar
122+
if (selectedItem) {
123+
document.getElementById(selectedItem).classList.remove('selected');
124+
}
125+
document.getElementById(item.id).classList.add('selected');
126+
selectedItem = item.id;
127+
128+
details.innerHTML = `
129+
<h2>${item.name}</h2>
130+
<div class="pdf-view">
131+
<div class="pdf-container">
132+
<h3>Actual PDF</h3>
133+
<iframe src="/${item.actualPdf}"></iframe>
152134
</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-
)}
135+
<div class="pdf-container">
136+
<h3>Reference PDF</h3>
137+
<iframe src="/${item.referencePdf}"></iframe>
193138
</div>
194139
</div>
195-
);
140+
${item.error ? `
141+
<div class="differences">
142+
<h3>Differences</h3>
143+
<pre>${item.error}</pre>
144+
</div>
145+
` : ''}
146+
`;
147+
}
148+
149+
async function init() {
150+
const results = window.TEST_RESULTS;
151+
152+
// Update summary
153+
document.getElementById('summary').innerHTML = formatSummary(results);
154+
155+
// Update failures list
156+
const failures = results.failures || [];
157+
const failuresHtml = failures.map(failure => `
158+
<div id="failure_${failure.name.replace(/[^a-zA-Z0-9]/g, '_')}" class="failure-item" onclick='showPdfComparison(${JSON.stringify({...failure, id: "failure_" + failure.name.replace(/[^a-zA-Z0-9]/g, '_')}).replace(/'/g, "&#39;")})'>
159+
${failure.name}
160+
</div>
161+
`).join('');
162+
document.getElementById('failures').innerHTML = failuresHtml || '<div class="failure-item">No failures</div>';
163+
164+
// Load and display actual PDFs
165+
try {
166+
const response = await fetch('/api/pdfs');
167+
const pdfs = await response.json();
168+
const pdfsHtml = pdfs.map(pdf => `
169+
<div id="pdf_${pdf.name.replace(/[^a-zA-Z0-9]/g, '_')}" class="failure-item" onclick='showPdfComparison(${JSON.stringify({...pdf, id: "pdf_" + pdf.name.replace(/[^a-zA-Z0-9]/g, '_')}).replace(/'/g, "&#39;")})'>
170+
${pdf.name}
171+
</div>
172+
`).join('');
173+
document.getElementById('pdfs').innerHTML = pdfsHtml || '<div class="failure-item">No PDFs found</div>';
174+
175+
// Show first failure by default, or first PDF if no failures
176+
if (failures.length > 0) {
177+
showPdfComparison({...failures[0], id: "failure_" + failures[0].name.replace(/[^a-zA-Z0-9]/g, '_')});
178+
} else if (pdfs.length > 0) {
179+
showPdfComparison({...pdfs[0], id: "pdf_" + pdfs[0].name.replace(/[^a-zA-Z0-9]/g, '_')});
180+
}
181+
} catch (error) {
182+
console.error('Failed to load PDFs:', error);
183+
document.getElementById('pdfs').innerHTML = `
184+
<div class="failure-item">
185+
Failed to load PDFs: ${error.message}
186+
</div>
187+
`;
188+
}
196189
}
197190

198-
const root = ReactDOM.createRoot(document.getElementById('root'));
199-
root.render(<App />);
191+
init();
200192
</script>
201193
</body>
202194
</html>

0 commit comments

Comments
 (0)