diff --git a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx index 4a6beac325..16f8932b2a 100644 --- a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx +++ b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.test.tsx @@ -145,4 +145,20 @@ describe("OnboardingPreviewCard", () => { expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull(); expect(apiFetch).not.toHaveBeenCalled(); }); + + it("migrates a pre-rebrand gittensory_ dismiss flag so the card stays hidden (#7782)", async () => { + window.localStorage.setItem( + "gittensory_maintainer_onboarding_preview_dismissed", + JSON.stringify({ dismissed: true }), + ); + apiFetch.mockResolvedValue({ ok: true, data: preview() }); + render(); + await waitFor(() => + expect(window.localStorage.getItem("loopover_maintainer_onboarding_preview_dismissed")).toBe( + JSON.stringify({ dismissed: true }), + ), + ); + expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull(); + expect(apiFetch).not.toHaveBeenCalled(); + }); }); diff --git a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx index 7e754f20e3..3315794de9 100644 --- a/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx +++ b/apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx @@ -19,7 +19,8 @@ type ReviewabilityRow = { pr: string; title: string; reason: string }; const DISMISS_KEY = "loopover_maintainer_onboarding_preview_dismissed"; // One-time rebrand migration fallback -- see useLocalStorage's legacyKey param. -const LEGACY_DISMISS_KEY = "loopover_maintainer_onboarding_preview_dismissed"; +// Must stay as the pre-rebrand `gittensory_...` literal (distinct from DISMISS_KEY); #7782. +const LEGACY_DISMISS_KEY = "gittensory_maintainer_onboarding_preview_dismissed"; /** Builds a settings-preview form from a REAL cached PR (title, and a linked-issue number scraped from * `reason` when present) — everything else (author identity, labels, body) isn't in the reviewability diff --git a/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx b/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx index c5a42c53a1..a57253a523 100644 --- a/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx +++ b/apps/loopover-ui/src/components/site/notification-readiness-card.test.tsx @@ -1,5 +1,5 @@ -import { fireEvent, render, screen } from "@testing-library/react"; -import { describe, expect, it, vi } from "vitest"; +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; // #6985: a real fetch failure used to render the same generic text as "still loading" — these tests // pin the three render paths (loading / error / success) now that LoadingState/ErrorState replace it. @@ -31,6 +31,10 @@ const notificationModelFixture = { }; describe("NotificationReadinessCard loading/error states (#6985)", () => { + beforeEach(() => { + window.localStorage.clear(); + }); + it("shows a LoadingState (not the generic spinner-free text) while the model loads", () => { useApiResource.mockReturnValue({ status: "loading", @@ -94,4 +98,20 @@ describe("NotificationReadinessCard loading/error states (#6985)", () => { expect(container.textContent).toContain("No content leaves the browser without consent."); expect(screen.queryByText("Loading notification model…")).toBeNull(); }); + + it("migrates a pre-rebrand gittensory_ opt-in flag so the pill shows enabled (#7782)", async () => { + window.localStorage.setItem("gittensory_notification_opt_in", JSON.stringify(true)); + useApiResource.mockReturnValue({ + status: "ready", + data: notificationModelFixture, + error: null, + loadedAt: Date.now(), + reload: () => {}, + }); + + render(); + await waitFor(() => expect(screen.getByText("opt-in enabled")).toBeTruthy()); + expect(window.localStorage.getItem("loopover_notification_opt_in")).toBe(JSON.stringify(true)); + expect(screen.queryByText("opt-in required")).toBeNull(); + }); }); diff --git a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx index 36459045c2..d9a0698b1b 100644 --- a/apps/loopover-ui/src/components/site/notification-readiness-card.tsx +++ b/apps/loopover-ui/src/components/site/notification-readiness-card.tsx @@ -30,10 +30,11 @@ export function NotificationReadinessCard() { "/v1/app/notification-model", "Notification model", ); + // Legacy key must stay as the pre-rebrand `gittensory_...` literal (distinct from the current key); #7782. const [optIn, setOptIn] = useLocalStorage( "loopover_notification_opt_in", false, - "loopover_notification_opt_in", + "gittensory_notification_opt_in", ); const [busy, setBusy] = useState(false); diff --git a/scripts/branding-drift-baseline.json b/scripts/branding-drift-baseline.json index b161c01de0..704b782d0c 100644 --- a/scripts/branding-drift-baseline.json +++ b/scripts/branding-drift-baseline.json @@ -1,5 +1,7 @@ { "apps/loopover-ui/src/components/site/api/try-it.tsx": 1, + "apps/loopover-ui/src/components/site/app-panels/onboarding-preview-card.tsx": 2, + "apps/loopover-ui/src/components/site/notification-readiness-card.tsx": 2, "apps/loopover-ui/src/routes/app.index.tsx": 1, "apps/loopover-ui/src/routes/app.runs.tsx": 1, "apps/loopover-ui/src/routes/app.workbench.tsx": 1,