diff --git a/src/components/settings/integrations/__tests__/garmin-card.test.tsx b/src/components/settings/integrations/__tests__/garmin-card.test.tsx new file mode 100644 index 000000000..c9beb65c5 --- /dev/null +++ b/src/components/settings/integrations/__tests__/garmin-card.test.tsx @@ -0,0 +1,165 @@ +/** + * Garmin on the shared integration card. + * + * 1. Not connected: the key form, the link to Garmin Connect's key page, + * the validity choice and Connect, inside the shared card. + * 2. Connected: the same menu as Nightscout, a refused key opens the form, + * and disconnect asks first and invalidates the envelope. + * 3. The key's end: a sentence in the body while it is close, past or + * unknown, and the date in the details while it is comfortably valid. + */ +import { describe, expect, it, vi } from "vitest"; +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; + +import { queryKeys } from "@/lib/query-keys"; + +const invalidateSpy = vi.fn(); +const capturedDisconnectOnSuccess: { fn: (() => void) | null } = { fn: null }; + +vi.mock("@tanstack/react-query", () => ({ + useMutation: ({ onSuccess }: { onSuccess?: () => void }) => { + capturedDisconnectOnSuccess.fn = onSuccess ?? null; + return { mutate: vi.fn(), isPending: false }; + }, + useQueryClient: () => ({ invalidateQueries: invalidateSpy }), +})); + +let lastCardProps: IntegrationCardProps | null = null; +vi.mock("../integration-card", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + IntegrationCard: (props: IntegrationCardProps) => { + lastCardProps = props; + return createElement(actual.IntegrationCard, props); + }, + }; +}); + +import { I18nProvider } from "@/lib/i18n/context"; +import type { GarminKeyExpiry } from "@/lib/garmin/key-expiry"; +import type { IntegrationCardProps } from "../integration-card"; +import { GarminCard, GARMIN_KEY_SETTINGS_URL } from "../garmin-card"; +import type { IntegrationStatusViewModel } from "../shared"; +import { healthFixture } from "./health-fixture"; + +function render(viewModel?: Partial) { + return renderToStaticMarkup( + + + , + ); +} + +function connected(keyExpiry: GarminKeyExpiry | null) { + return render({ + connected: true, + configured: true, + health: healthFixture(), + keyExpiry, + metricFreshness: [], + }); +} + +describe("GarminCard on the shared card", () => { + it("shows the key form inside the shared card while not connected", () => { + const html = render({ connected: false, configured: false, health: null }); + expect(html).toContain('data-slot="integration-card"'); + expect(html).toContain('data-state="not_connected"'); + expect(html).toContain('id="garmin-api-key"'); + expect(html).toContain('id="garmin-key-validity"'); + expect(html).toContain(`href="${GARMIN_KEY_SETTINGS_URL}"`); + expect(html).toContain('data-testid="garmin-connect"'); + expect(html).toContain( + 'href="https://docs.healthlog.dev/guides/connect-garmin/"', + ); + // 30, 90, 180 and 365 days, plus "I don't know". + expect(html.match(/