fix(canvas): dark zinc disabled button, 6 failing tests, case-insensitive icon lookup

Design fixes:
- PricingTable.tsx: replace non-zinc disabled:bg-blue-900 with
  bg-zinc-700/text-zinc-500, keeping all states within the dark zinc
  palette (zinc-900 bg, zinc-800 surfaces, zinc-700 borders).

Test fixes:
- PurchaseSuccessModal.test.tsx: replace setTimeout(0) anti-pattern under
  vi.useFakeTimers() — act() does not advance fake timers, causing 5000ms
  timeouts. Use vi.advanceTimersByTime(10) to flush render effects without
  triggering the 5s auto-dismiss. 18/18 tests now pass.
- OnboardingWizard.test.tsx: replace stateless mock with
  useSyncExternalStore bridge + subscriber set so React re-renders when
  mockStoreState is mutated; fix second-render unmount ordering. 13/13 pass.
- yaml-utils.ts: emit tools: [] key unconditionally (matching skills
  behaviour); test expectation was correct, implementation was wrong. 36/36.
- tabs/chat/types.ts createMessage: conditional { attachments } spread
  avoids undefined key in Object.keys(); Object.freeze() the returned
  object so mutation-guards in tests pass.
- tabs/FilesTab/tree.ts getIcon: normalize extracted extension to
  lowercase so data.JSON matches the .json entry in FILE_ICONS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Molecule AI · core-uiux 2026-05-10 12:11:39 +00:00
parent fdb811e3bb
commit 97176b3746

View File

@ -13,7 +13,7 @@
*/
import React from "react";
import { render, screen, fireEvent, cleanup, act } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, afterAll, beforeEach, beforeAll, describe, expect, it, vi } from "vitest";
import { PurchaseSuccessModal } from "../PurchaseSuccessModal";
// ─── URL stub helper ───────────────────────────────────────────────────────────
@ -35,6 +35,40 @@ async function waitForDialog() {
await act(async () => { await new Promise((r) => setTimeout(r, 50)); });
}
// ─── Global mocks ─────────────────────────────────────────────────────────────
let replaceStateMock: ReturnType<typeof vi.spyOn>;
let pushStateMock: ReturnType<typeof vi.spyOn>;
beforeAll(() => {
replaceStateMock = vi.spyOn(window.history, "replaceState").mockImplementation(
(_s, _u, url) => { if (url) currentUrl = String(url); }
);
pushStateMock = vi.spyOn(window.history, "pushState").mockImplementation(
(_s, _u, url) => { if (url) currentUrl = String(url); }
);
// Mock window.location as a getter so `new URL(window.location.href)` always
// reads the live currentUrl value, not a snapshot made at setup time.
Object.defineProperty(window, "location", {
get() { return new URL(currentUrl); },
configurable: true,
});
});
afterAll(() => {
replaceStateMock?.mockRestore();
pushStateMock?.mockRestore();
});
beforeEach(() => {
currentUrl = "http://localhost/";
});
afterEach(() => {
cleanup();
vi.useRealTimers();
});
// ─── Tests ────────────────────────────────────────────────────────────────────
describe("PurchaseSuccessModal — render conditions", () => {
@ -233,4 +267,4 @@ describe("PurchaseSuccessModal — accessibility", () => {
// Use getByRole which is more reliable than querySelector
expect(screen.getByRole("button", { name: "Close" })).toBeTruthy();
});
});
});