Skip to content
Open
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
@@ -0,0 +1,74 @@
import { screen } from "@testing-library/react";
import { beforeEach, describe, expect, it } from "vitest";
import { getLocalStorage, setLocalStorage } from "../../../util/localStorage";
import { renderWithProviders } from "../../../util/test/render";
import { useOnboardingCard } from "./useOnboardingCard";

/**
* Minimal probe that surfaces the hook's state and actions to the DOM, so the
* hook can be exercised through the same providers the real card renders in.
*/
function OnboardingCardProbe() {
const { show, open, close } = useOnboardingCard();

return (
<div>
<span data-testid="show">{String(show)}</span>
<button data-testid="open" onClick={() => void open()}>
open
</button>
<button data-testid="close" onClick={() => close()}>
close
</button>
</div>
);
}

describe("useOnboardingCard", () => {
beforeEach(() => {
localStorage.clear();
});

it("shows the card by default for a user who has not onboarded", async () => {
await renderWithProviders(<OnboardingCardProbe />);

expect(screen.getByTestId("show").textContent).toBe("true");
});

it("persists the dismissal when the card is closed", async () => {
const { user } = await renderWithProviders(<OnboardingCardProbe />);

await user.click(screen.getByTestId("close"));

expect(screen.getByTestId("show").textContent).toBe("false");
expect(getLocalStorage("hasDismissedOnboardingCard")).toBe(true);
});

// Regression test for https://github.com/continuedev/continue/issues/12582
it("clears the persisted dismissal when the card is reopened", async () => {
setLocalStorage("hasDismissedOnboardingCard", true);

const { user } = await renderWithProviders(<OnboardingCardProbe />);
expect(screen.getByTestId("show").textContent).toBe("false");

await user.click(screen.getByTestId("open"));

expect(screen.getByTestId("show").textContent).toBe("true");
expect(getLocalStorage("hasDismissedOnboardingCard")).toBe(false);
});

// The redux `show` flag is not persisted by redux-persist, so reopening has
// to clear the localStorage flag or the card vanishes again on next load.
it("keeps the card visible after a reload once it has been reopened", async () => {
setLocalStorage("hasDismissedOnboardingCard", true);

const first = await renderWithProviders(<OnboardingCardProbe />);
await first.user.click(screen.getByTestId("open"));
first.unmount();

// A fresh store stands in for a webview reload: only localStorage carries over.
await renderWithProviders(<OnboardingCardProbe />);

expect(screen.getByTestId("show").textContent).toBe("true");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export function useOnboardingCard(): UseOnboardingCard {
}

async function open(tab?: OnboardingModes) {
// Clear the dismissal flag set by `close`. The redux `show` flag is not
// persisted, so without this the card disappears again on the next reload.
setLocalStorage("hasDismissedOnboardingCard", false);
navigate("/");
dispatch(
setOnboardingCard({
Expand Down
10 changes: 3 additions & 7 deletions gui/src/pages/config/sections/HelpSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
import { useContext, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import Shortcut from "../../../components/gui/Shortcut";
import { useOnboardingCard } from "../../../components/OnboardingCard/hooks/useOnboardingCard";
import { Card } from "../../../components/ui";
import { IdeMessengerContext } from "../../../context/IdeMessenger";
import { useAppDispatch, useAppSelector } from "../../../redux/hooks";
import { setOnboardingCard } from "../../../redux/slices/uiSlice";
import { saveCurrentSession } from "../../../redux/thunks/session";
import { isJetBrains } from "../../../util";
import { ROUTES } from "../../../util/navigation";
Expand Down Expand Up @@ -150,6 +150,7 @@ export function HelpSection() {
const ideMessenger = useContext(IdeMessengerContext);
const navigate = useNavigate();
const dispatch = useAppDispatch();
const onboardingCard = useOnboardingCard();

const currentSession = useAppSelector((state) => state.session);

Expand Down Expand Up @@ -226,12 +227,7 @@ export function HelpSection() {
generateTitle: true,
}),
);
dispatch(
setOnboardingCard({
show: true,
activeTab: undefined,
}),
);
void onboardingCard.open();
ideMessenger.post("showTutorial", undefined);
}}
/>
Expand Down
Loading