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

test: add regression coverage for toggle logic and dark-scheme fallback paths

Add pinning test for the corrected toggleColorScheme script to prevent
silent reversion to the null-evaluating m&&m[1]==='light' pattern.

Add dark-scheme network exception test for resolveTheme to verify
fallbackForScheme() returns dark tokens on all fallback paths, not just
the !policyRes.ok path that was previously the only dark-scheme test.

+23
+13
apps/web/src/layouts/__tests__/base.test.tsx
··· 286 286 expect(html).toContain("SameSite=Lax"); 287 287 expect(html).toContain("path=/"); 288 288 }); 289 + 290 + it("toggleColorScheme script defaults to 'light' when no cookie present (first toggle must produce 'dark')", async () => { 291 + // Regression: the old script used `m&&m[1]==='light'` which evaluates to `null` 292 + // (not `false`) when no cookie exists, causing the first toggle to always produce 293 + // 'light' instead of 'dark'. The fix introduces a `current` variable that defaults 294 + // to 'light', ensuring `next` is always the opposite of the current scheme. 295 + const res = await app.request("/"); 296 + const html = await res.text(); 297 + // Verify the corrected pattern is present: `current` defaults to 'light' when no cookie 298 + expect(html).toContain("var current=m?m[1]:'light'"); 299 + // Verify `next` is derived from `current`, not from the raw regex match 300 + expect(html).toContain("current==='light'?'dark':'light'"); 301 + }); 289 302 }); 290 303 291 304 describe("favicon", () => {
+10
apps/web/src/lib/__tests__/theme-resolution.test.ts
··· 250 250 ); 251 251 }); 252 252 253 + it("returns dark fallback tokens when network exception occurs with dark cookie", async () => { 254 + // Regression: fallbackForScheme() must return dark tokens when the detected scheme is dark. 255 + // Previously, all fallback paths returned FALLBACK_THEME (light tokens) regardless of scheme. 256 + mockFetch.mockRejectedValueOnce(new Error("fetch failed")); 257 + const result = await resolveTheme(APPVIEW, "atbb-color-scheme=dark", undefined); 258 + expect(result.tokens).toEqual(fallbackForScheme("dark").tokens); 259 + expect(result.colorScheme).toBe("dark"); 260 + expect(result.tokens).not.toEqual(FALLBACK_THEME.tokens); 261 + }); 262 + 253 263 it("re-throws programming errors (TypeError) rather than swallowing them", async () => { 254 264 // A TypeError from a bug in the code should propagate, not be silently logged. 255 265 // This TypeError comes from the fetch() mock itself (not from .json()), so it