tangled
alpha
login
or
join now
malpercio.dev
/
atbb
5
fork
atom
WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node
typescript
hono
htmx
atproto
5
fork
atom
overview
issues
pulls
pipelines
test: settings preview endpoint and HTMX attribute tests
malpercio.dev
1 day ago
477f47ce
ba73403e
+139
1 changed file
expand all
collapse all
unified
split
apps
web
src
routes
__tests__
settings.test.tsx
+139
apps/web/src/routes/__tests__/settings.test.tsx
···
162
162
expect(html).toContain("Neobrutal Dark");
163
163
});
164
164
165
165
+
it("renders selects with hx-get attribute when allowUserChoice is true", async () => {
166
166
+
setupAuthenticatedSessionGet();
167
167
+
const routes = await loadSettingsRoutes();
168
168
+
const res = await routes.request("/settings", {
169
169
+
headers: { cookie: "atbb_session=token" },
170
170
+
});
171
171
+
expect(res.status).toBe(200);
172
172
+
const html = await res.text();
173
173
+
expect(html).toContain('hx-get="/settings/preview"');
174
174
+
});
175
175
+
165
176
it("GET /settings with ?saved=1 shows success banner", async () => {
166
177
setupAuthenticatedSessionGet();
167
178
const routes = await loadSettingsRoutes();
···
399
410
expect(res.status).toBe(302);
400
411
const cookies = res.headers.getSetCookie?.() ?? [];
401
412
expect(cookies.length).toBe(0);
413
413
+
});
414
414
+
415
415
+
// ── GET /settings/preview ────────────────────────────────────────────────────
416
416
+
417
417
+
describe("GET /settings/preview", () => {
418
418
+
it("returns empty fragment when no query params provided", async () => {
419
419
+
const routes = await loadSettingsRoutes();
420
420
+
const res = await routes.request("/settings/preview");
421
421
+
expect(res.status).toBe(200);
422
422
+
const html = await res.text();
423
423
+
expect(html).toBe('<div id="theme-preview"></div>');
424
424
+
// Verify no fetch was made
425
425
+
expect(mockFetch).not.toHaveBeenCalled();
426
426
+
});
427
427
+
428
428
+
it("returns empty fragment when lightThemeUri is malformed (no slash separators)", async () => {
429
429
+
const routes = await loadSettingsRoutes();
430
430
+
const res = await routes.request(
431
431
+
"/settings/preview?lightThemeUri=not-a-uri"
432
432
+
);
433
433
+
expect(res.status).toBe(200);
434
434
+
const html = await res.text();
435
435
+
expect(html).toBe('<div id="theme-preview"></div>');
436
436
+
// Verify no fetch was made for malformed URI
437
437
+
expect(mockFetch).not.toHaveBeenCalled();
438
438
+
});
439
439
+
440
440
+
it("returns swatch preview for valid lightThemeUri", async () => {
441
441
+
const themeResponse = {
442
442
+
name: "Clean Light",
443
443
+
colorScheme: "light",
444
444
+
tokens: {
445
445
+
"color-bg": "#f5f0e8",
446
446
+
"color-surface": "#fff",
447
447
+
"color-primary": "#ff5c00",
448
448
+
"color-text": "#1a1a1a",
449
449
+
"color-border": "#000",
450
450
+
},
451
451
+
};
452
452
+
mockFetch.mockResolvedValueOnce(mockResponse(themeResponse));
453
453
+
454
454
+
const routes = await loadSettingsRoutes();
455
455
+
const res = await routes.request(
456
456
+
"/settings/preview?lightThemeUri=at://did:plc:forum/space.atbb.forum.theme/3lbllight"
457
457
+
);
458
458
+
expect(res.status).toBe(200);
459
459
+
const html = await res.text();
460
460
+
expect(html).toContain('id="theme-preview"');
461
461
+
expect(html).toContain("Clean Light");
462
462
+
expect(html).toContain('class="theme-preview__swatch"');
463
463
+
// Verify the five swatch spans are present
464
464
+
const swatchCount = (html.match(/class="theme-preview__swatch"/g) || []).length;
465
465
+
expect(swatchCount).toBe(5);
466
466
+
});
467
467
+
468
468
+
it("returns swatch preview for valid darkThemeUri", async () => {
469
469
+
const themeResponse = {
470
470
+
name: "Neobrutal Dark",
471
471
+
colorScheme: "dark",
472
472
+
tokens: {
473
473
+
"color-bg": "#1a1a1a",
474
474
+
"color-surface": "#2a2a2a",
475
475
+
"color-primary": "#ff5c00",
476
476
+
"color-text": "#f5f0e8",
477
477
+
"color-border": "#3a3a3a",
478
478
+
},
479
479
+
};
480
480
+
mockFetch.mockResolvedValueOnce(mockResponse(themeResponse));
481
481
+
482
482
+
const routes = await loadSettingsRoutes();
483
483
+
const res = await routes.request(
484
484
+
"/settings/preview?darkThemeUri=at://did:plc:forum/space.atbb.forum.theme/3lbldark"
485
485
+
);
486
486
+
expect(res.status).toBe(200);
487
487
+
const html = await res.text();
488
488
+
expect(html).toContain('id="theme-preview"');
489
489
+
expect(html).toContain("Neobrutal Dark");
490
490
+
expect(html).toContain('class="theme-preview__swatch"');
491
491
+
});
492
492
+
493
493
+
it("returns empty fragment when /api/themes/:rkey returns non-ok status", async () => {
494
494
+
mockFetch.mockResolvedValueOnce(mockResponse({}, false, 404));
495
495
+
496
496
+
const routes = await loadSettingsRoutes();
497
497
+
const res = await routes.request(
498
498
+
"/settings/preview?lightThemeUri=at://did:plc:forum/space.atbb.forum.theme/unknown"
499
499
+
);
500
500
+
expect(res.status).toBe(200);
501
501
+
const html = await res.text();
502
502
+
expect(html).toBe('<div id="theme-preview"></div>');
503
503
+
});
504
504
+
505
505
+
it("returns empty fragment when fetch throws error (network failure)", async () => {
506
506
+
mockFetch.mockRejectedValueOnce(new Error("Network error"));
507
507
+
508
508
+
const routes = await loadSettingsRoutes();
509
509
+
const res = await routes.request(
510
510
+
"/settings/preview?lightThemeUri=at://did:plc:forum/space.atbb.forum.theme/3lbllight"
511
511
+
);
512
512
+
expect(res.status).toBe(200);
513
513
+
const html = await res.text();
514
514
+
expect(html).toBe('<div id="theme-preview"></div>');
515
515
+
});
516
516
+
517
517
+
it("includes swatch color values in style attribute", async () => {
518
518
+
const themeResponse = {
519
519
+
name: "Test Theme",
520
520
+
tokens: {
521
521
+
"color-bg": "#ffffff",
522
522
+
"color-surface": "#f0f0f0",
523
523
+
"color-primary": "#0066ff",
524
524
+
"color-text": "#000000",
525
525
+
"color-border": "#cccccc",
526
526
+
},
527
527
+
};
528
528
+
mockFetch.mockResolvedValueOnce(mockResponse(themeResponse));
529
529
+
530
530
+
const routes = await loadSettingsRoutes();
531
531
+
const res = await routes.request(
532
532
+
"/settings/preview?lightThemeUri=at://did:plc:forum/space.atbb.forum.theme/test"
533
533
+
);
534
534
+
expect(res.status).toBe(200);
535
535
+
const html = await res.text();
536
536
+
expect(html).toContain('style="background:#ffffff"');
537
537
+
expect(html).toContain('style="background:#0066ff"');
538
538
+
expect(html).toContain('title="color-bg"');
539
539
+
expect(html).toContain('title="color-primary"');
540
540
+
});
402
541
});
403
542
});