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>Pixel Art Editor - Test Suite Runner</title>
7 <link rel="stylesheet" href="style.css">
8 <style>
9 body {
10 background-color: #0f172a;
11 color: #f8fafc;
12 font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
13 margin: 0;
14 padding: 2rem;
15 }
16 .test-container {
17 max-width: 900px;
18 margin: 0 auto;
19 }
20 .header-card {
21 background: rgba(30, 41, 59, 0.7);
22 backdrop-filter: blur(12px);
23 border: 1px solid rgba(255, 255, 255, 0.1);
24 border-radius: 16px;
25 padding: 1.5rem 2rem;
26 margin-bottom: 2rem;
27 box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
28 }
29 .header-card h1 {
30 margin: 0 0 0.5rem 0;
31 font-size: 1.8rem;
32 background: linear-gradient(135deg, #a5b4fc, #6366f1);
33 -webkit-background-clip: text;
34 -webkit-text-fill-color: transparent;
35 }
36 .summary-bar {
37 display: flex;
38 gap: 1.5rem;
39 margin-top: 1rem;
40 }
41 .stat-badge {
42 padding: 0.5rem 1rem;
43 border-radius: 8px;
44 font-weight: 600;
45 font-size: 0.9rem;
46 display: inline-flex;
47 align-items: center;
48 gap: 0.5rem;
49 }
50 .stat-passed { background: rgba(34, 197, 94, 0.15); color: #4ade80; border: 1px solid rgba(34, 197, 94, 0.3); }
51 .stat-failed { background: rgba(239, 68, 68, 0.15); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.3); }
52 .stat-total { background: rgba(99, 102, 241, 0.15); color: #818cf8; border: 1px solid rgba(99, 102, 241, 0.3); }
53
54 .suite-card {
55 background: rgba(30, 41, 59, 0.5);
56 border: 1px solid rgba(255, 255, 255, 0.08);
57 border-radius: 12px;
58 margin-bottom: 1.5rem;
59 overflow: hidden;
60 }
61 .suite-header {
62 background: rgba(15, 23, 42, 0.6);
63 padding: 1rem 1.5rem;
64 font-weight: 600;
65 font-size: 1.1rem;
66 border-bottom: 1px solid rgba(255, 255, 255, 0.08);
67 color: #cbd5e1;
68 }
69 .test-item {
70 padding: 0.85rem 1.5rem;
71 display: flex;
72 justify-content: space-between;
73 align-items: center;
74 border-bottom: 1px solid rgba(255, 255, 255, 0.04);
75 }
76 .test-item:last-child {
77 border-bottom: none;
78 }
79 .test-name {
80 display: flex;
81 align-items: center;
82 gap: 0.75rem;
83 }
84 .status-icon {
85 width: 22px;
86 height: 22px;
87 border-radius: 50%;
88 display: flex;
89 align-items: center;
90 justify-content: center;
91 font-size: 0.75rem;
92 font-weight: bold;
93 }
94 .status-icon.pass { background: #22c55e; color: #052e16; }
95 .status-icon.fail { background: #ef4444; color: #450a0a; }
96 .duration {
97 font-size: 0.8rem;
98 color: #64748b;
99 }
100 .error-box {
101 margin-top: 0.5rem;
102 padding: 0.75rem;
103 background: rgba(239, 68, 68, 0.1);
104 border-left: 3px solid #ef4444;
105 color: #fca5a5;
106 font-family: monospace;
107 font-size: 0.85rem;
108 border-radius: 4px;
109 }
110 .action-row {
111 margin-top: 1.5rem;
112 display: flex;
113 gap: 1rem;
114 }
115 .btn {
116 background: #6366f1;
117 color: white;
118 border: none;
119 padding: 0.6rem 1.2rem;
120 border-radius: 8px;
121 cursor: pointer;
122 font-weight: 600;
123 transition: background 0.2s;
124 text-decoration: none;
125 display: inline-block;
126 }
127 .btn:hover {
128 background: #4f46e5;
129 }
130 .btn-secondary {
131 background: rgba(255, 255, 255, 0.1);
132 color: #e2e8f0;
133 }
134 .btn-secondary:hover {
135 background: rgba(255, 255, 255, 0.2);
136 }
137 </style>
138</head>
139<body>
140 <div class="test-container">
141 <div class="header-card">
142 <h1>Pixel Art Editor Unit Test Suite</h1>
143 <p style="color: #94a3b8; margin: 0;">Automated visual test runner for Flood Fill, Undo Stack, and JSON Serializer logic.</p>
144
145 <div class="summary-bar" id="summaryBar">
146 <div class="stat-badge stat-total">Running tests...</div>
147 </div>
148
149 <div class="action-row">
150 <button class="btn" onclick="runAndRenderTests()">Re-run Tests</button>
151 <a href="index.html" class="btn btn-secondary">← Back to Editor</a>
152 </div>
153 </div>
154
155 <div id="testOutput"></div>
156 </div>
157
158 <script src="palette.js"></script>
159 <script src="editor.js"></script>
160 <script src="tests.js"></script>
161 <script>
162 function runAndRenderTests() {
163 const results = window.executeAllTests();
164 const outputEl = document.getElementById('testOutput');
165 const summaryEl = document.getElementById('summaryBar');
166 outputEl.innerHTML = '';
167
168 let passedCount = 0;
169 let failedCount = 0;
170
171
172 const suites = {};
173 results.forEach(r => {
174 if (!suites[r.suite]) suites[r.suite] = [];
175 suites[r.suite].push(r);
176 if (r.passed) passedCount++; else failedCount++;
177 });
178
179 summaryEl.innerHTML = `
180 <div class="stat-badge stat-passed">✓ ${passedCount} Passed</div>
181 <div class="stat-badge stat-failed">✗ ${failedCount} Failed</div>
182 <div class="stat-badge stat-total">Total: ${results.length}</div>
183 `;
184
185 Object.keys(suites).forEach(suiteName => {
186 const suiteCard = document.createElement('div');
187 suiteCard.className = 'suite-card';
188
189 const suiteHeader = document.createElement('div');
190 suiteHeader.className = 'suite-header';
191 suiteHeader.textContent = suiteName;
192 suiteCard.appendChild(suiteHeader);
193
194 suites[suiteName].forEach(test => {
195 const item = document.createElement('div');
196 item.className = 'test-item';
197
198 const nameDiv = document.createElement('div');
199 nameDiv.className = 'test-name';
200
201 const icon = document.createElement('div');
202 icon.className = `status-icon ${test.passed ? 'pass' : 'fail'}`;
203 icon.textContent = test.passed ? '✓' : '✕';
204
205 const title = document.createElement('span');
206 title.textContent = test.name;
207
208 nameDiv.appendChild(icon);
209 nameDiv.appendChild(title);
210
211 const dur = document.createElement('span');
212 dur.className = 'duration';
213 dur.textContent = `${test.duration}ms`;
214
215 item.appendChild(nameDiv);
216 item.appendChild(dur);
217
218 if (!test.passed && test.errorMsg) {
219 const errBox = document.createElement('div');
220 errBox.className = 'error-box';
221 errBox.textContent = `Error: ${test.errorMsg}`;
222 item.appendChild(errBox);
223 }
224
225 suiteCard.appendChild(item);
226 });
227
228 outputEl.appendChild(suiteCard);
229 });
230 }
231
232
233 window.addEventListener('DOMContentLoaded', runAndRenderTests);
234 </script>
235</body>
236</html>
237
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.