diff --git a/src/components/balance/PriceFetchControl.test.tsx b/src/components/balance/PriceFetchControl.test.tsx index d70ca60..f4deb51 100644 --- a/src/components/balance/PriceFetchControl.test.tsx +++ b/src/components/balance/PriceFetchControl.test.tsx @@ -11,6 +11,10 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; // Mocks — declared before imports to satisfy vi.mock hoisting // --------------------------------------------------------------------------- +// React's hooks from the shared harness (#409): the consent dialog opens on a +// click (internal state), so the render test below presets its slot (#437). +vi.mock("react", async (orig) => (await import("../../test/hookHarness")).fakeReact(await orig())); + vi.mock("../../hooks/useIsPremium", () => ({ useIsPremium: vi.fn(), })); @@ -40,6 +44,7 @@ vi.mock("lucide-react", () => ({ // Imports (after mock declarations) // --------------------------------------------------------------------------- +import type { ReactNode } from "react"; import { useIsPremium } from "../../hooks/useIsPremium"; import { prices } from "../../services/balance.service"; import type { PriceResult } from "../../services/balance.service"; @@ -48,6 +53,7 @@ import { setPreference, } from "../../services/userPreferenceService"; import { renderToStaticMarkup } from "react-dom/server"; +import { hookHarness } from "../../test/hookHarness"; import PriceFetchControl, { __resetBestEffortDismissForTests, } from "./PriceFetchControl"; @@ -56,6 +62,7 @@ import PriceFetchControl, { // Helpers // --------------------------------------------------------------------------- +const harness = hookHarness(); const mockUseIsPremium = vi.mocked(useIsPremium); const mockFetchPrice = vi.mocked(prices.fetchPrice); const mockGetPreference = vi.mocked(getPreference); @@ -363,18 +370,35 @@ describe("PriceFetchControl — displayed texts", () => { __resetBestEffortDismissForTests(); vi.resetAllMocks(); setPremium(true); + harness.clear(); }); - it("names the fetch button and the stock best-effort notice through t", () => { - const html = renderToStaticMarkup( - , + /** + * One render of a stock's control, its hooks from the harness. `consentOpen` + * presets slot 0, the control's first hook (`showConsentModal`). The slots + * follow the order of the control's hook calls: a hook of any kind added + * before it shifts them without a word, and the preset lands on another hook + * (one slot per hook call: the `slot` helper of `fakeReact`, + * src/test/hookHarness.ts). + */ + function markup(consentOpen = false): string { + if (consentOpen) harness.slots[0] = true; + return renderToStaticMarkup( + harness.render( + () => + PriceFetchControl({ + symbol: "AAPL", + date: "2026-04-25", + categoryKind: "priced", + assetType: "stock", + onPriceFetched: vi.fn(), + }) as ReactNode, + ), ); + } + + it("names the fetch button and the stock best-effort notice through t", () => { + const html = markup(); expect(html).toContain('aria-label="t(balance.priceFetching.button)"'); expect(html).toContain(">t(balance.priceFetching.button)<"); expect(html).toContain(">t(balance.priceFetching.bestEffortNotice)<"); @@ -382,4 +406,15 @@ describe("PriceFetchControl — displayed texts", () => { expect(html).not.toContain(">best-effort<"); expect(html).toContain('aria-label="t(common.close)"'); }); + + it("names the consent dialog through t once it is open (#437)", () => { + expect(markup()).not.toContain('role="dialog"'); + harness.clear(); + const html = markup(true); + expect(html).toContain('role="dialog"'); + expect(html).toContain(">t(balance.priceFetching.consent.title)<"); + expect(html).toContain(">t(balance.priceFetching.consent.body)<"); + expect(html).toContain(">t(balance.priceFetching.consent.decline)<"); + expect(html).toContain(">t(balance.priceFetching.consent.accept)<"); + }); }); diff --git a/src/components/settings/LicenseKeyForm.render.test.tsx b/src/components/settings/LicenseKeyForm.render.test.tsx index c28ba59..6ecacc1 100644 --- a/src/components/settings/LicenseKeyForm.render.test.tsx +++ b/src/components/settings/LicenseKeyForm.render.test.tsx @@ -1,5 +1,6 @@ // Render tests for the key entry form of the License card (#347). No jsdom needed: -// react-dom/server renders in vitest's node environment, and `t` echoes its key. +// react-dom/server renders in vitest's node environment, and the marked `t` of +// src/test/i18nMock.ts renders `t(key)`. import { describe, it, expect, vi } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; @@ -44,4 +45,10 @@ describe("LicenseKeyForm", () => { expect(render(null, true)).toMatch(/