···294294295295 expect(res.status).toBe(200);
296296 const html = await res.text();
297297+ // The injected declaration must not appear
297298 expect(html).not.toContain("--injected");
299299+ // The entire dirty value must be dropped — not just the injected suffix
300300+ // (a partial-strip bug would output '--color-bg: red' which looks safe)
301301+ expect(html).not.toContain("--color-bg");
298302 });
299303300304 it("drops token values containing '}' (CSS block-escape injection prevention)", async () => {
···317321 const html = await res.text();
318322 // The injected block-escape value must not appear
319323 expect(html).not.toContain("red} body");
324324+ });
325325+326326+ it("returns a fallback HTML fragment when no tokens are submitted (does not crash)", async () => {
327327+ setupAuthenticatedSession([MANAGE_THEMES]);
328328+329329+ const routes = await loadThemeRoutes();
330330+ // POST with no body — parseBody() returns {} which produces an empty token map
331331+ const res = await routes.request("/admin/themes/abc123/preview", {
332332+ method: "POST",
333333+ headers: {
334334+ "content-type": "application/x-www-form-urlencoded",
335335+ cookie: "atbb_session=token",
336336+ },
337337+ });
338338+339339+ // Must not crash — returns a valid HTML fragment
340340+ expect(res.status).toBe(200);
341341+ const html = await res.text();
342342+ expect(html).not.toContain("<html");
343343+ expect(html).toContain(".preview-pane-inner");
320344 });
321345});