eaglelandsonce commited on
Commit
5a84604
·
verified ·
1 Parent(s): fd61df2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +402 -18
index.html CHANGED
@@ -1,19 +1,403 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Microservices Testing TicTacToe</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ </head>
9
+ <body class="bg-gradient-to-br from-indigo-50 to-sky-100 min-h-screen flex items-center justify-center p-6">
10
+ <div class="w-full max-w-5xl bg-white rounded-2xl shadow-xl p-6">
11
+ <h1 class="text-3xl md:text-4xl font-extrabold text-center text-indigo-700">Microservices Testing TicTacToe</h1>
12
+ <p class="text-center text-sm text-gray-600 mt-2">
13
+ Source:
14
+ <a class="text-blue-600 underline" target="_blank" rel="noopener"
15
+ href="https://www.linkedin.com/pulse/automated-testing-microservices-from-concept-practice-michael-lively-gamte/">
16
+ Automated Testing in Microservices: From Concept to Practice — Michael Lively
17
+ </a>
18
+ </p>
19
+
20
+ <div id="banner" class="hidden bg-indigo-100 border border-indigo-300 text-indigo-900 font-semibold text-center py-2 rounded mt-4"></div>
21
+
22
+ <div class="mt-6 flex flex-col md:flex-row md:space-x-6 space-y-4 md:space-y-0">
23
+ <!-- Board -->
24
+ <div id="board" class="grid grid-cols-3 gap-2 p-2 bg-gray-50 rounded-xl border-4 border-gray-200 mx-auto"></div>
25
+
26
+ <!-- Question Panel -->
27
+ <div id="questionPanel" class="md:w-1/2 w-full bg-gray-50 rounded-xl shadow-inner p-4">
28
+ <h2 id="panelQuestion" class="text-lg md:text-xl font-semibold text-gray-800">
29
+ Select a square to view the question
30
+ </h2>
31
+ <ul id="panelChoices" class="mt-4 space-y-2"></ul>
32
+ <p id="panelHint" class="mt-3 text-sm text-gray-600 hidden"></p>
33
+ <div class="mt-4 flex items-center space-x-2">
34
+ <button id="hintBtn" class="px-3 py-1.5 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">Show Hint</button>
35
+ <button id="clearBtn" class="px-3 py-1.5 bg-white border rounded hover:bg-gray-100">Clear Panel</button>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between mt-6 space-y-3 md:space-y-0">
41
+ <p id="status" class="text-lg font-medium text-gray-800"></p>
42
+ <div class="flex items-center space-x-2">
43
+ <label class="text-sm text-gray-600">Mode:</label>
44
+ <select id="mode" class="border rounded px-2 py-1 text-sm">
45
+ <option value="two">Two Players (X vs O)</option>
46
+ <option value="solo">Solo (You are X)</option>
47
+ </select>
48
+ <button id="restartBtn" class="px-4 py-2 bg-indigo-700 text-white rounded hover:bg-indigo-800">Restart</button>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <script>
54
+ // --- Utilities ---
55
+ const randShuffle = (arr) => arr
56
+ .map(x => ({ x, r: Math.random() }))
57
+ .sort((a,b) => a.r - b.r)
58
+ .map(o => o.x);
59
+
60
+ // --- Question bank (answers identified by 'answer' index BEFORE shuffle) ---
61
+ // Built from your provided content: automation benefits, test layers, CI/CD, tools, trends, and best practices.
62
+ const QUESTION_BANK = [
63
+ {
64
+ question: 'Why is manual testing insufficient for microservices at scale?',
65
+ choices: [
66
+ 'Microservices never change once deployed',
67
+ 'Manual testing is slow, error-prone, and can’t keep pace with highly distributed systems',
68
+ 'Because UIs don’t need testing',
69
+ 'It costs more than writing the entire system again'
70
+ ],
71
+ answer: 2,
72
+ hint: 'Think speed + distribution + repeatability.'
73
+ },
74
+ {
75
+ question: 'A core benefit of automated testing is that tests are…',
76
+ choices: ['Randomized', 'Deterministic and reproducible', 'UI-only', 'Manual by default'],
77
+ answer: 2,
78
+ hint: 'Same inputs → same results.'
79
+ },
80
+ {
81
+ question: 'Which layer validates a single small unit like a function or method?',
82
+ choices: ['Integration testing', 'End-to-end testing', 'Unit testing', 'Contract testing'],
83
+ answer: 3,
84
+ hint: 'Foundation of the pyramid.'
85
+ },
86
+ {
87
+ question: 'Which testing ensures services communicate correctly together?',
88
+ choices: ['Integration testing', 'Accessibility testing', 'Static analysis', 'Exploratory testing'],
89
+ answer: 1,
90
+ hint: 'Service-to-service behavior.'
91
+ },
92
+ {
93
+ question: 'Which approach guarantees a service still fulfills expectations when it changes?',
94
+ choices: ['Contract testing', 'Chaos testing', 'Snapshot testing', 'Golden master testing'],
95
+ answer: 1,
96
+ hint: 'Inputs/outputs pact.'
97
+ },
98
+ {
99
+ question: 'Which is best described as simulating user interactions across the system, including dependencies?',
100
+ choices: ['Functional testing', 'Linting', 'Schema migration testing', 'Fuzzing only'],
101
+ answer: 1,
102
+ hint: 'User-focused workflows.'
103
+ },
104
+ {
105
+ question: 'Black-box validation of the whole architecture against business goals after deployment is…',
106
+ choices: ['End-to-end testing', 'Mutation testing', 'Smoke testing', 'Code coverage'],
107
+ answer: 1,
108
+ hint: 'System-level validation.'
109
+ },
110
+ {
111
+ question: 'Where does automation fit in a modern pipeline?',
112
+ choices: [
113
+ 'Only after production',
114
+ 'Embedded in CI/CD with automated gates and rapid feedback',
115
+ 'Only on developer laptops',
116
+ 'Only in long quarterly test cycles'
117
+ ],
118
+ answer: 2,
119
+ hint: 'Shift earlier, fail faster.'
120
+ },
121
+ {
122
+ question: 'Which tool is commonly used for load/performance testing in microservices?',
123
+ choices: ['Selenium', 'Apache JMeter or Gatling', 'Pa11y', 'Axe-core'],
124
+ answer: 2,
125
+ hint: 'Think throughput and latency.'
126
+ },
127
+ {
128
+ question: 'Which tool focuses on distributed tracing for service interactions?',
129
+ choices: ['Jaeger', 'Pact', 'Hoverfly', 'Testcontainers'],
130
+ answer: 1,
131
+ hint: 'Trace requests across services.'
132
+ },
133
+ {
134
+ question: 'Which tool supports contract testing for APIs and messaging?',
135
+ choices: ['Pact', 'Jaeger', 'Axe-core', 'Katalon'],
136
+ answer: 1,
137
+ hint: 'Consumer–provider agreements.'
138
+ },
139
+ {
140
+ question: 'Which helps create ephemeral, repeatable dependencies (DBs, queues) for tests via containers?',
141
+ choices: ['Hoverfly', 'Testcontainers', 'Selenium Grid', 'Pa11y'],
142
+ answer: 2,
143
+ hint: 'Spin up what you need on demand.'
144
+ },
145
+ {
146
+ question: 'Which helps with service virtualization and integration testing when dependencies are unavailable?',
147
+ choices: ['Hoverfly', 'SBOM', 'Gitleaks', 'Snyk Code'],
148
+ answer: 1,
149
+ hint: 'Simulate services.'
150
+ },
151
+ {
152
+ question: 'Which category accelerates test creation without heavy scripting?',
153
+ choices: ['Codeless platforms (e.g., ACCELQ, Katalon)', 'Shell scripts only', 'Makefiles only', 'SQL migrations'],
154
+ answer: 1,
155
+ hint: 'Bridge dev and non-dev testers.'
156
+ },
157
+ {
158
+ question: 'AI-driven testing in 2025 commonly does what?',
159
+ choices: [
160
+ 'Ignores failing tests',
161
+ 'Prioritizes cases, generates scripts, and self-heals tests',
162
+ 'Removes CI entirely',
163
+ 'Eliminates the need for version control'
164
+ ],
165
+ answer: 2,
166
+ hint: 'Agentic assist + resilience.'
167
+ },
168
+ {
169
+ question: 'Which supports accessibility automation against WCAG?',
170
+ choices: ['Axe / Pa11y', 'curl / jq', 'Terraform', 'Prometheus'],
171
+ answer: 1,
172
+ hint: 'Inclusive UX checks.'
173
+ },
174
+ {
175
+ question: 'Shift-left security in pipelines commonly includes:',
176
+ choices: [
177
+ 'SAST/DAST, SBOM generation, and image signing',
178
+ 'Only manual penetration tests',
179
+ 'Turning off TLS for speed',
180
+ 'Ignoring dependency vulnerabilities'
181
+ ],
182
+ answer: 1,
183
+ hint: 'Security as code, early.'
184
+ },
185
+ {
186
+ question: 'Why use ephemeral environments for testing?',
187
+ choices: [
188
+ 'They are slower but cheaper',
189
+ 'They ensure clean states per run and avoid “works on my machine”',
190
+ 'They replace production entirely',
191
+ 'They require manual setup every time'
192
+ ],
193
+ answer: 2,
194
+ hint: 'Spin up, test, tear down.'
195
+ },
196
+ {
197
+ question: 'A key best practice to avoid tight coupling in tests is to…',
198
+ choices: [
199
+ 'Always test with every real dependency',
200
+ 'Mock or virtualize dependencies when appropriate',
201
+ 'Skip integration tests',
202
+ 'Use only UI tests'
203
+ ],
204
+ answer: 2,
205
+ hint: 'Minimize brittle cross-service reliance.'
206
+ },
207
+ {
208
+ question: 'Prioritization guidance in complex systems suggests testing…',
209
+ choices: [
210
+ 'Low-value edge features first',
211
+ 'Only the UI',
212
+ 'High-risk, business-critical services first',
213
+ 'Only the database layer'
214
+ ],
215
+ answer: 3,
216
+ hint: 'Risk-driven focus.'
217
+ },
218
+ {
219
+ question: 'Which tool pairing is most appropriate: performance metrics store?',
220
+ choices: ['InfluxDB', 'Selenium IDE', 'Kibana only', 'Cypress'],
221
+ answer: 1,
222
+ hint: 'Time-series for perf data.'
223
+ },
224
+ {
225
+ question: 'Functional automation frameworks like Selenium are strongest for…',
226
+ choices: ['API contract validation', 'UI interaction simulation', 'Tracing spans', 'Generating SBOMs'],
227
+ answer: 2,
228
+ hint: 'Clicks, forms, flows.'
229
+ },
230
+ {
231
+ question: 'Scalable fuzzing in microservices helps by…',
232
+ choices: [
233
+ 'Generating random inputs to uncover edge cases across services',
234
+ 'Replacing unit tests',
235
+ 'Eliminating the need for logs',
236
+ 'Forcing 100% code coverage automatically'
237
+ ],
238
+ answer: 1,
239
+ hint: 'Probe the unknowns.'
240
+ },
241
+ {
242
+ question: 'In CI, a typical sequence is:',
243
+ choices: [
244
+ 'Deploy to prod → write tests → run tests',
245
+ 'Build → test (unit/integration/contract) → gate → stage deploy',
246
+ 'Write docs → ship',
247
+ 'Only manual QA after release'
248
+ ],
249
+ answer: 2,
250
+ hint: 'Feedback before promotion.'
251
+ }
252
+ ];
253
+
254
+ // --- Game State ---
255
+ let currentPlayer, boardState, selectedCell, cellQuestions, mode;
256
+ const boardEl = document.getElementById('board');
257
+ const statusEl = document.getElementById('status');
258
+ const bannerEl = document.getElementById('banner');
259
+ const hintBtn = document.getElementById('hintBtn');
260
+ const clearBtn = document.getElementById('clearBtn');
261
+ const panelQuestion = document.getElementById('panelQuestion');
262
+ const panelChoices = document.getElementById('panelChoices');
263
+ const panelHint = document.getElementById('panelHint');
264
+ const restartBtn = document.getElementById('restartBtn');
265
+ const modeSel = document.getElementById('mode');
266
+
267
+ function initGame() {
268
+ mode = modeSel.value;
269
+ currentPlayer = 'X';
270
+ boardState = Array(9).fill('');
271
+ bannerEl.classList.add('hidden');
272
+ statusEl.innerText = '';
273
+ selectedCell = null;
274
+
275
+ // Pick 9 questions at random, then for each, shuffle choices and remap the correct answer index
276
+ const picked = randShuffle(QUESTION_BANK).slice(0, 9).map(q => {
277
+ const original = q.choices.map((c, i) => ({ text: c, idx: i + 1 }));
278
+ const shuffled = randShuffle(original);
279
+ const newChoices = shuffled.map(o => o.text);
280
+ const newAnswerIndex = shuffled.findIndex(o => o.idx === q.answer) + 1;
281
+ return {
282
+ question: q.question,
283
+ choices: newChoices,
284
+ answer: newAnswerIndex,
285
+ hint: q.hint || ''
286
+ };
287
+ });
288
+
289
+ cellQuestions = picked;
290
+ panelQuestion.innerText = 'Select a square to view the question';
291
+ panelChoices.innerHTML = '';
292
+ panelHint.classList.add('hidden');
293
+ panelHint.innerText = '';
294
+
295
+ renderBoard();
296
+ }
297
+
298
+ function renderBoard() {
299
+ boardEl.innerHTML = '';
300
+ boardState.forEach((mark, idx) => {
301
+ const btn = document.createElement('button');
302
+ let cls = 'bg-white h-24 w-24 md:h-28 md:w-28 flex items-center justify-center text-3xl font-extrabold rounded-xl shadow hover:bg-gray-100 transition';
303
+ if (mark === 'X') cls += ' text-indigo-700';
304
+ if (mark === 'O') cls += ' text-sky-700';
305
+ btn.className = cls;
306
+ btn.innerText = mark;
307
+ btn.disabled = mark !== '';
308
+ btn.addEventListener('click', () => openQuestion(idx));
309
+ boardEl.appendChild(btn);
310
+ });
311
+ statusEl.innerText = `Current: ${currentPlayer}`;
312
+ }
313
+
314
+ function openQuestion(idx) {
315
+ selectedCell = idx;
316
+ const q = cellQuestions[idx];
317
+ panelQuestion.innerText = q.question;
318
+ panelChoices.innerHTML = '';
319
+ panelHint.classList.add('hidden');
320
+ panelHint.innerText = q.hint || '';
321
+ q.choices.forEach((c, i) => {
322
+ const li = document.createElement('li');
323
+ const btn = document.createElement('button');
324
+ btn.innerText = c;
325
+ btn.className = 'w-full text-left px-4 py-2 bg-gray-100 rounded hover:bg-gray-200';
326
+ btn.addEventListener('click', () => handleAnswer(i + 1));
327
+ li.appendChild(btn);
328
+ panelChoices.appendChild(li);
329
+ });
330
+ }
331
+
332
+ function handleAnswer(choice) {
333
+ const q = cellQuestions[selectedCell];
334
+ if (choice === q.answer) {
335
+ boardState[selectedCell] = currentPlayer;
336
+ renderBoard();
337
+ if (checkWin(currentPlayer)) return endGame(`${currentPlayer} wins!`);
338
+ if (boardState.every(c => c)) return endGame('Stalemate!');
339
+ currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
340
+ statusEl.innerText = `Current: ${currentPlayer}`;
341
+ if (mode === 'solo' && currentPlayer === 'O') {
342
+ setTimeout(aiMove, 450);
343
+ }
344
+ } else {
345
+ alert('Incorrect! Turn missed.');
346
+ currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
347
+ statusEl.innerText = `Current: ${currentPlayer}`;
348
+ if (mode === 'solo' && currentPlayer === 'O') {
349
+ setTimeout(aiMove, 450);
350
+ }
351
+ }
352
+ }
353
+
354
+ function checkWin(player) {
355
+ const wins = [
356
+ [0,1,2],[3,4,5],[6,7,8],
357
+ [0,3,6],[1,4,7],[2,5,8],
358
+ [0,4,8],[2,4,6]
359
+ ];
360
+ return wins.some(combo => combo.every(i => boardState[i] === player));
361
+ }
362
+
363
+ function endGame(msg) {
364
+ bannerEl.innerText = `🎉 ${msg}`;
365
+ bannerEl.classList.remove('hidden');
366
+ document.querySelectorAll('#board button').forEach(b => b.disabled = true);
367
+ }
368
+
369
+ // Simple AI: pick a random open square; 55% chance to "answer" correctly.
370
+ function aiMove() {
371
+ const open = boardState.map((v,i) => v === '' ? i : null).filter(v => v !== null);
372
+ if (open.length === 0) return;
373
+ const pick = open[Math.floor(Math.random() * open.length)];
374
+ const q = cellQuestions[pick];
375
+ const aiCorrect = Math.random() < 0.55;
376
+ if (aiCorrect) {
377
+ boardState[pick] = 'O';
378
+ renderBoard();
379
+ if (checkWin('O')) return endGame('O wins!');
380
+ if (boardState.every(c => c)) return endGame('Stalemate!');
381
+ currentPlayer = 'X';
382
+ statusEl.innerText = `Current: ${currentPlayer}`;
383
+ } else {
384
+ currentPlayer = 'X';
385
+ statusEl.innerText = `Current: ${currentPlayer}`;
386
+ }
387
+ }
388
+
389
+ hintBtn.addEventListener('click', () => panelHint.classList.toggle('hidden'));
390
+ clearBtn.addEventListener('click', () => {
391
+ panelQuestion.innerText = 'Select a square to view the question';
392
+ panelChoices.innerHTML = '';
393
+ panelHint.classList.add('hidden');
394
+ panelHint.innerText = '';
395
+ });
396
+ restartBtn.addEventListener('click', initGame);
397
+ modeSel.addEventListener('change', initGame);
398
+
399
+ // Start
400
+ initGame();
401
+ </script>
402
+ </body>
403
  </html>