@@ -25,221 +25,157 @@ export const template = ({
2525 numTests,
2626 numPassed,
2727 numFailed,
28- numTodo,
28+ numTodo = 0 ,
2929 results,
3030} ) => {
31+ const percent = ( v ) => ( numTests === 0 ? 0 : Math . round ( ( v / numTests ) * 100 ) )
32+
3133 return `
3234<!DOCTYPE html>
3335<html>
3436<head>
35- <title>Test Results </title>
37+ <title>Test Report </title>
3638 <style>
37- body {
38- font-family: system-ui, -apple-system, sans-serif;
39- line-height: 1.5;
40- padding: 2rem;
41- margin: 0;
42- background: #f5f5f5;
43- }
44- .container {
45- max-width: 1200px;
46- margin: 0 auto;
47- background: white;
48- padding: 2rem;
49- border-radius: 8px;
50- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
51- }
52- .header {
53- display: flex;
54- justify-content: space-between;
55- align-items: center;
56- margin-bottom: 2rem;
57- padding-bottom: 1rem;
58- border-bottom: 1px solid #eee;
59- }
60- .header-controls {
61- display: flex;
62- gap: 1rem;
63- align-items: center;
64- }
65- .summary {
66- display: flex;
67- gap: 2rem;
68- margin-bottom: 2rem;
69- }
70- .stat {
71- padding: 1rem;
72- border-radius: 6px;
73- min-width: 120px;
74- }
39+ body { font-family: system-ui, -apple-system, sans-serif; background: #f5f5f5; margin: 0; padding: 2rem; }
40+ .container { max-width: 1200px; margin: 0 auto; background: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.08); padding: 2rem; }
41+ .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }
42+ .main-flex { display: flex; align-items: center; gap: 3rem; margin-bottom: 2.5rem; }
43+ .pie-chart-container { position: relative; width: 200px; height: 200px; background: #fff; border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.07); display: flex; align-items: center; justify-content: center; }
44+ .pie-legend { display: flex; flex-direction: column; gap: 0.7rem; margin-top: 1.5rem; font-size: 1.1rem; }
45+ .pie-legend-item { display: flex; align-items: center; gap: 0.6rem; }
46+ .dot { width: 18px; height: 18px; border-radius: 50%; display: inline-block; }
47+ .pie-passed { background: #4caf50; }
48+ .pie-failed { background: #f44336; }
49+ .pie-todo { background: #ff9800; }
50+ .pie-stat-label { font-weight: 600; min-width: 60px; display: inline-block; }
51+ .pie-stat-value { font-weight: 700; margin-left: 0.5em; }
52+ .summary-tiles { display: flex; flex-direction: row; gap: 1.5rem; align-items: center; height: 200px; }
53+ .stat { padding: 1.2rem 1.5rem; border-radius: 12px; min-width: 120px; background: #f5f5f5; box-shadow: 0 1px 2px rgba(0,0,0,0.03); display: flex; flex-direction: column; align-items: center; justify-content: center; height: 120px; }
7554 .stat.total { background: #e3f2fd; }
7655 .stat.passed { background: #e8f5e9; }
7756 .stat.failed { background: #ffebee; }
78- .stat h3 { margin: 0; }
79- .stat p { margin: 0.5rem 0 0; font-size: 1.5rem; font-weight: bold; }
80- .test-case {
81- padding: 0.75rem 1rem;
82- border-bottom: 1px solid #eee;
83- }
84- .test-case:last-child {
85- border-bottom: none;
86- }
87- .test-name {
88- display: flex;
89- align-items: center;
90- gap: 0.5rem;
91- }
92- .test-name.passed::before {
93- content: '✓';
94- color: #4caf50;
95- }
96- .test-name.failed::before {
97- content: '✗';
98- color: #f44336;
99- }
100- .error-details {
101- margin-top: 1rem;
102- padding: 1rem;
103- background: #fff8f8;
104- border-radius: 4px;
105- display: none;
106- }
107- .error-details.show {
108- display: block;
109- }
110- .toggle-error {
111- color: #f44336;
112- text-decoration: underline;
113- cursor: pointer;
114- margin-top: 0.5rem;
115- display: inline-block;
116- }
117- pre {
118- margin: 0.5rem 0;
119- padding: 1rem;
120- background: #f5f5f5;
121- border-radius: 4px;
122- overflow-x: auto;
123- }
124-
125- .test-name.todo::before {
126- content: '◦';
127- color: #ff9800;
128- }
129- .test-name.todo {
130- color: #ff9800;
131- font-style: italic;
132- }
133-
13457 .stat.todo { background: #fff3e0; }
58+ .stat h3 { margin: 0 0 0.3em 0; font-size: 1.1em; font-weight: 600; }
59+ .stat p { margin: 0; font-size: 2rem; font-weight: bold; }
60+ .stat.passed h3, .stat.passed p { color: #4caf50; }
61+ .stat.failed h3, .stat.failed p { color: #f44336; }
13562 .stat.todo h3, .stat.todo p { color: #ff9800; }
136- .describe-group {
137- margin: 0.5rem 0;
138- padding: 0 1rem;
139- }
140- .describe-header {
141- padding: 0.75rem 1rem;
142- background: #f3f4f6;
143- margin: 0.5rem 0;
144- font-weight: 600;
145- border-radius: 4px;
146- cursor: pointer;
147- }
148- .describe-content {
149- display: block;
150- padding: 0.5rem 0;
151- }
152- .describe-content.hide {
153- display: none;
154- }
155- .api-details {
156- margin-top: 1rem;
157- padding: 1rem;
158- background: #f8f9fa;
159- border-radius: 4px;
160- display: none;
161- }
162- .api-details.show {
163- display: block;
164- }
165- .toggle-api {
166- color: #2196f3;
167- text-decoration: underline;
168- cursor: pointer;
169- margin-top: 0.5rem;
170- display: inline-block;
171- margin-left: 1rem;
172- }
173- .api-section {
174- margin: 1rem 0;
175- padding: 1rem;
176- background: #fff;
177- border: 1px solid #e0e0e0;
178- border-radius: 4px;
179- }
180- .api-section h4 {
181- margin: 0 0 0.5rem 0;
182- color: #555;
183- }
184- .api-table {
185- width: 100%;
186- border-collapse: collapse;
187- margin: 0.5rem 0;
188- }
189- .api-table th, .api-table td {
190- text-align: left;
191- padding: 0.5rem;
192- border: 1px solid #e0e0e0;
193- }
194- .api-table th {
195- background: #f3f4f6;
196- font-weight: 600;
197- }
198- pre.api-data {
199- margin: 0.5rem 0;
200- padding: 0.5rem;
201- background: #f5f5f5;
202- border-radius: 4px;
203- max-height: 200px;
204- overflow-y: auto;
205- white-space: pre-wrap;
206- }
63+ .test-case { padding: 0.75rem 1rem; border-bottom: 1px solid #eee; }
64+ .test-case:last-child { border-bottom: none; }
65+ .test-name { display: flex; align-items: center; gap: 0.5rem; }
66+ .test-name.passed::before { content: '✓'; color: #4caf50; }
67+ .test-name.failed::before { content: '✗'; color: #f44336; }
68+ .test-name.todo::before { content: '◦'; color: #ff9800; }
69+ .test-name.todo { color: #ff9800; font-style: italic; }
70+ .error-details { margin-top: 1rem; padding: 1rem; background: #fff8f8; border-radius: 4px; display: none; }
71+ .error-details.show { display: block; }
72+ .toggle-error { color: #f44336; text-decoration: underline; cursor: pointer; margin-top: 0.5rem; display: inline-block; }
73+ pre { margin: 0.5rem 0; padding: 1rem; background: #f5f5f5; border-radius: 4px; overflow-x: auto; }
74+ .describe-group { margin: 0.5rem 0; padding: 0 1rem; }
75+ .describe-header { padding: 0.75rem 1rem; background: #f3f4f6; margin: 0.5rem 0; font-weight: 600; border-radius: 4px; cursor: pointer; }
76+ .describe-content { display: block; padding: 0.5rem 0; }
77+ .describe-content.hide { display: none; }
78+ .api-details { margin-top: 1rem; padding: 1rem; background: #f8f9fa; border-radius: 4px; display: none; }
79+ .api-details.show { display: block; }
80+ .toggle-api { color: #2196f3; text-decoration: underline; cursor: pointer; margin-top: 0.5rem; display: inline-block; margin-left: 1rem; }
81+ .api-section { margin: 1rem 0; padding: 1rem; background: #fff; border: 1px solid #e0e0e0; border-radius: 4px; }
82+ .api-section h4 { margin: 0 0 0.5rem 0; color: #555; }
83+ .api-table { width: 100%; border-collapse: collapse; margin: 0.5rem 0; }
84+ .api-table th, .api-table td { text-align: left; padding: 0.5rem; border: 1px solid #e0e0e0; }
85+ .api-table th { background: #f3f4f6; font-weight: 600; }
86+ pre.api-data { margin: 0.5rem 0; padding: 0.5rem; background: #f5f5f5; border-radius: 4px; max-height: 200px; overflow-y: auto; white-space: pre-wrap; }
20787 </style>
20888</head>
20989<body>
21090 <div class="container">
21191 <div class="header">
212- <h1>Test Results</h1>
213- <div class="header-controls">
214- <div>${ new Date ( ) . toLocaleString ( ) } </div>
215- </div>
92+ <h1>Test Report</h1>
93+ <div>${ new Date ( ) . toLocaleString ( ) } </div>
21694 </div>
217-
218- <div class="summary">
219- <div class="stat total">
220- <h3>Total Tests</h3>
221- <p>${ numTests } </p>
222- </div>
223- <div class="stat passed">
224- <h3>Passed</h3>
225- <p>${ numPassed } </p>
226- </div>
227- <div class="stat failed">
228- <h3>Failed</h3>
229- <p>${ numFailed } </p>
95+ <div class="main-flex">
96+ <div>
97+ <div class="pie-chart-container">
98+ <canvas id="pieChart" width="200" height="200"></canvas>
99+ </div>
100+ <div class="pie-legend">
101+ <div class="pie-legend-item">
102+ <span class="dot pie-passed"></span>
103+ <span class="pie-stat-label">Passed</span>
104+ <span class="pie-stat-value" id="percent-passed">${ percent (
105+ numPassed
106+ ) } %</span>
107+ </div>
108+ <div class="pie-legend-item">
109+ <span class="dot pie-failed"></span>
110+ <span class="pie-stat-label">Failed</span>
111+ <span class="pie-stat-value" id="percent-failed">${ percent (
112+ numFailed
113+ ) } %</span>
114+ </div>
115+ <div class="pie-legend-item">
116+ <span class="dot pie-todo"></span>
117+ <span class="pie-stat-label">Todo</span>
118+ <span class="pie-stat-value" id="percent-todo">${ percent (
119+ numTodo
120+ ) } %</span>
121+ </div>
122+ </div>
230123 </div>
231- <div class="stat todo">
232- <h3>Todo</h3>
233- <p>${ numTodo } </p>
124+ <div class="summary-tiles">
125+ <div class="stat total">
126+ <h3>Total</h3>
127+ <p>${ numTests } </p>
128+ </div>
129+ <div class="stat passed">
130+ <h3>Passed</h3>
131+ <p>${ numPassed } </p>
132+ </div>
133+ <div class="stat failed">
134+ <h3>Failed</h3>
135+ <p>${ numFailed } </p>
136+ </div>
137+ <div class="stat todo">
138+ <h3>Todo</h3>
139+ <p>${ numTodo } </p>
140+ </div>
234141 </div>
235142 </div>
236-
237143 <div class="results">
238- ${ renderDescribeGroup ( groupByDescribe ( results ) ) }
144+ ${ renderDescribeGroup ( groupByDescribe ( results ) ) }
239145 </div>
240146 </div>
241-
242147 <script>
148+ (function() {
149+ const data = [
150+ { value: ${ numPassed } , color: '#4caf50' },
151+ { value: ${ numFailed } , color: '#f44336' },
152+ { value: ${ numTodo } , color: '#ff9800' }
153+ ];
154+ const total = ${ numTests } ;
155+ const canvas = document.getElementById('pieChart');
156+ if (!canvas) return;
157+ const ctx = canvas.getContext('2d');
158+ const centerX = canvas.width / 2;
159+ const centerY = canvas.height / 2;
160+ const radius = 90;
161+ let startAngle = -0.5 * Math.PI;
162+ const totalValue = data.reduce((sum, d) => sum + d.value, 0);
163+ data.forEach((d) => {
164+ if (d.value > 0) {
165+ const slice = (d.value / totalValue) * 2 * Math.PI;
166+ ctx.beginPath();
167+ ctx.moveTo(centerX, centerY);
168+ ctx.arc(centerX, centerY, radius, startAngle, startAngle + slice);
169+ ctx.closePath();
170+ ctx.fillStyle = d.color;
171+ ctx.globalAlpha = 0.92;
172+ ctx.fill();
173+ ctx.globalAlpha = 1;
174+ startAngle += slice;
175+ }
176+ });
177+ })();
178+
243179 function toggleDescribeContent(element) {
244180 const content = element.nextElementSibling;
245181 content.classList.toggle('hide');
@@ -258,12 +194,6 @@ export const template = ({
258194 details.classList.toggle('show');
259195 element.textContent = isShowing ? 'Show API details' : 'Hide API details';
260196 }
261-
262- document.addEventListener('DOMContentLoaded', () => {
263- const failedTests = document.querySelectorAll('.test-name.failed');
264- failedTests.forEach(test => {
265- });
266- });
267197 </script>
268198</body>
269199</html>
0 commit comments