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 : 1 rem ;
22+ padding : 20 px ;
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 - z A - Z 0 - 9 ] / g, '_' ) } " class="failure-item" onclick='showPdfComparison(${ JSON . stringify ( { ...failure , id : "failure_" + failure . name . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) } ) . replace ( / ' / g, "'" ) } )'>
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 - z A - Z 0 - 9 ] / g, '_' ) } " class="failure-item" onclick='showPdfComparison(${ JSON . stringify ( { ...pdf , id : "pdf_" + pdf . name . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) } ) . replace ( / ' / g, "'" ) } )'>
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 - z A - Z 0 - 9 ] / g, '_' ) } ) ;
178+ } else if ( pdfs . length > 0 ) {
179+ showPdfComparison ( { ...pdfs [ 0 ] , id : "pdf_" + pdfs [ 0 ] . name . replace ( / [ ^ a - z A - Z 0 - 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