Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,30 @@ describe("OnboardingPreviewCard", () => {
expect(screen.getByText("503 Service Unavailable")).toBeTruthy();
});

it("migrates a pre-rebrand dismissal stored under the legacy gittensory_ key (#7782)", async () => {
// A maintainer who dismissed this card before the rebrand has the flag under the OLD gittensory_-prefixed
// key. #5743's blanket rename had corrupted the legacyKey to equal the current key, so this value was
// silently dropped and the card reappeared. With the legacy key restored, the pre-rebrand dismissal is read
// forward: the card stays hidden and no demo API call runs.
window.localStorage.clear();
// useLocalStorage stores the `{ dismissed }` object shape, so the legacy value mirrors it.
window.localStorage.setItem(
"gittensory_maintainer_onboarding_preview_dismissed",
JSON.stringify({ dismissed: true }),
);
apiFetch.mockResolvedValue({ ok: true, data: preview() });

render(<OnboardingPreviewCard reviewability={REVIEWABILITY} />);
await waitFor(() =>
expect(window.localStorage.getItem("loopover_maintainer_onboarding_preview_dismissed")).toBe(
JSON.stringify({ dismissed: true }),
),
);
// The migrated legacy dismissal keeps the card hidden and skips the demo API call entirely.
expect(screen.queryByText(/Here's what LoopOver would have flagged/)).toBeNull();
expect(apiFetch).not.toHaveBeenCalled();
});

it("dismisses the card and keeps it hidden (and skips the API call) across a remount", async () => {
apiFetch.mockResolvedValue({ ok: true, data: preview() });
const { unmount } = render(<OnboardingPreviewCard reviewability={REVIEWABILITY} />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import { useLocalStorage } from "@/lib/use-local-storage";
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";
// One-time rebrand migration fallback -- see useLocalStorage's legacyKey param. This must stay the OLD
// gittensory_-prefixed literal; #5743's blanket gittensory->loopover rename corrupted it to equal DISMISS_KEY,
// silently dropping a pre-rebrand dismissal (#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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,27 @@ 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 opt-in stored under the legacy gittensory_ key (#7782)", () => {
// A maintainer who opted in before the rebrand has their preference under the OLD gittensory_-prefixed
// key. #5743's blanket rename had corrupted the legacyKey to equal the current key, so this value was
// silently dropped and the card defaulted back to "opt-in required". With the legacy key restored, the
// pre-rebrand opt-in is read forward and the pill shows "opt-in enabled".
window.localStorage.clear();
window.localStorage.setItem("gittensory_notification_opt_in", JSON.stringify(true));
useApiResource.mockReturnValue({
status: "ready",
data: notificationModelFixture,
error: null,
loadedAt: Date.now(),
reload: () => {},
});

render(<NotificationReadinessCard />);

expect(screen.getByText("opt-in enabled")).toBeTruthy();
// The legacy value was written forward to the current key (useLocalStorage's migration contract).
expect(window.localStorage.getItem("loopover_notification_opt_in")).toBe(JSON.stringify(true));
window.localStorage.clear();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export function NotificationReadinessCard() {
const [optIn, setOptIn] = useLocalStorage<boolean>(
"loopover_notification_opt_in",
false,
"loopover_notification_opt_in",
// Legacy fallback must stay the OLD gittensory_-prefixed literal; #5743's blanket gittensory->loopover
// rename corrupted it to equal the current key, silently dropping a pre-rebrand opt-in (#7782).
"gittensory_notification_opt_in",
);
const [busy, setBusy] = useState(false);

Expand Down
2 changes: 2 additions & 0 deletions scripts/branding-drift-baseline.json
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Loading