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
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

test(web): write failing tests for POST /admin/themes/:rkey/reset-to-preset (ATB-59)

Malpercio 95cd1a23 21731ba9

+137
+137
apps/web/src/routes/__tests__/admin-themes.test.tsx
··· 540 540 expect(putBody.tokens["color-bg"]).toBe("#000000"); 541 541 }); 542 542 }); 543 + 544 + describe("createAdminThemeRoutes — POST /admin/themes/:rkey/reset-to-preset", () => { 545 + const MANAGE_THEMES = "space.atbb.permission.manageThemes"; 546 + 547 + beforeEach(() => { 548 + vi.stubGlobal("fetch", mockFetch); 549 + vi.stubEnv("APPVIEW_URL", "http://localhost:3000"); 550 + vi.resetModules(); 551 + }); 552 + 553 + afterEach(() => { 554 + vi.unstubAllGlobals(); 555 + vi.unstubAllEnvs(); 556 + mockFetch.mockReset(); 557 + }); 558 + 559 + function mockResponse(body: unknown, ok = true, status = 200) { 560 + return { 561 + ok, 562 + status, 563 + statusText: ok ? "OK" : "Error", 564 + json: () => Promise.resolve(body), 565 + }; 566 + } 567 + 568 + function setupAuthenticatedSession(permissions: string[]) { 569 + mockFetch.mockResolvedValueOnce( 570 + mockResponse({ authenticated: true, did: "did:plc:forum", handle: "admin.bsky.social" }) 571 + ); 572 + mockFetch.mockResolvedValueOnce(mockResponse({ permissions })); 573 + } 574 + 575 + async function loadThemeRoutes() { 576 + const { createAdminThemeRoutes } = await import("../admin-themes.js"); 577 + return createAdminThemeRoutes("http://localhost:3000"); 578 + } 579 + 580 + // ── Unauthenticated ──────────────────────────────────────────────────────── 581 + 582 + it("redirects unauthenticated users to /login", async () => { 583 + const routes = await loadThemeRoutes(); 584 + const res = await routes.request("/admin/themes/abc123/reset-to-preset", { 585 + method: "POST", 586 + headers: { "content-type": "application/x-www-form-urlencoded" }, 587 + body: new URLSearchParams({ preset: "neobrutal-light" }).toString(), 588 + }); 589 + expect(res.status).toBe(302); 590 + expect(res.headers.get("location")).toBe("/login"); 591 + }); 592 + 593 + // ── No manageThemes permission → 403 ─────────────────────────────────────── 594 + 595 + it("returns 403 for users without manageThemes permission", async () => { 596 + setupAuthenticatedSession([]); 597 + const routes = await loadThemeRoutes(); 598 + const res = await routes.request("/admin/themes/abc123/reset-to-preset", { 599 + method: "POST", 600 + headers: { 601 + "content-type": "application/x-www-form-urlencoded", 602 + cookie: "atbb_session=token", 603 + }, 604 + body: new URLSearchParams({ preset: "neobrutal-light" }).toString(), 605 + }); 606 + expect(res.status).toBe(403); 607 + }); 608 + 609 + // ── Valid presets redirect ────────────────────────────────────────────────── 610 + 611 + it("redirects to ?preset=neobrutal-light for valid preset", async () => { 612 + setupAuthenticatedSession([MANAGE_THEMES]); 613 + 614 + const routes = await loadThemeRoutes(); 615 + const res = await routes.request("/admin/themes/abc123/reset-to-preset", { 616 + method: "POST", 617 + headers: { 618 + "content-type": "application/x-www-form-urlencoded", 619 + cookie: "atbb_session=token", 620 + }, 621 + body: new URLSearchParams({ preset: "neobrutal-light" }).toString(), 622 + }); 623 + 624 + expect(res.status).toBe(302); 625 + expect(res.headers.get("location")).toBe("/admin/themes/abc123?preset=neobrutal-light"); 626 + }); 627 + 628 + it("redirects to ?preset=neobrutal-dark for dark preset", async () => { 629 + setupAuthenticatedSession([MANAGE_THEMES]); 630 + 631 + const routes = await loadThemeRoutes(); 632 + const res = await routes.request("/admin/themes/abc123/reset-to-preset", { 633 + method: "POST", 634 + headers: { 635 + "content-type": "application/x-www-form-urlencoded", 636 + cookie: "atbb_session=token", 637 + }, 638 + body: new URLSearchParams({ preset: "neobrutal-dark" }).toString(), 639 + }); 640 + 641 + expect(res.status).toBe(302); 642 + expect(res.headers.get("location")).toBe("/admin/themes/abc123?preset=neobrutal-dark"); 643 + }); 644 + 645 + it("redirects to ?preset=blank for blank preset", async () => { 646 + setupAuthenticatedSession([MANAGE_THEMES]); 647 + 648 + const routes = await loadThemeRoutes(); 649 + const res = await routes.request("/admin/themes/abc123/reset-to-preset", { 650 + method: "POST", 651 + headers: { 652 + "content-type": "application/x-www-form-urlencoded", 653 + cookie: "atbb_session=token", 654 + }, 655 + body: new URLSearchParams({ preset: "blank" }).toString(), 656 + }); 657 + 658 + expect(res.status).toBe(302); 659 + expect(res.headers.get("location")).toBe("/admin/themes/abc123?preset=blank"); 660 + }); 661 + 662 + // ── Invalid preset → 400 ─────────────────────────────────────────────────── 663 + 664 + it("returns 400 for unknown preset name", async () => { 665 + setupAuthenticatedSession([MANAGE_THEMES]); 666 + 667 + const routes = await loadThemeRoutes(); 668 + const res = await routes.request("/admin/themes/abc123/reset-to-preset", { 669 + method: "POST", 670 + headers: { 671 + "content-type": "application/x-www-form-urlencoded", 672 + cookie: "atbb_session=token", 673 + }, 674 + body: new URLSearchParams({ preset: "hacked" }).toString(), 675 + }); 676 + 677 + expect(res.status).toBe(400); 678 + }); 679 + });