feat(FN-1937): track skipped onboarding steps in modal progress
- Add skippedSteps persistence to onboarding state with new markStepSkipped/getSkippedSteps helpers and legacy defaults - Update onboarding navigation so skip actions mark steps as skipped, while completed actions clear skipped state - Render skipped progress indicators as clickable steps with a dash marker and dedicated skipped styling - Expand onboarding state/modal tests and update App test mocks for the new onboarding-state exports
This commit is contained in:
@@ -107,7 +107,9 @@ const mockSaveOnboardingState = vi.fn();
|
||||
const mockClearOnboardingState = vi.fn();
|
||||
const mockIsOnboardingCompleted = vi.fn();
|
||||
const mockMarkOnboardingCompleted = vi.fn();
|
||||
const mockMarkStepSkipped = vi.fn();
|
||||
const mockGetOnboardingCompletedAt = vi.fn();
|
||||
const mockGetSkippedSteps = vi.fn();
|
||||
const mockGetStepData = vi.fn();
|
||||
|
||||
vi.mock("../../components/model-onboarding-state", () => ({
|
||||
@@ -118,7 +120,9 @@ vi.mock("../../components/model-onboarding-state", () => ({
|
||||
clearOnboardingState: (...args: unknown[]) => mockClearOnboardingState(...args),
|
||||
isOnboardingCompleted: (...args: unknown[]) => mockIsOnboardingCompleted(...args),
|
||||
markOnboardingCompleted: (...args: unknown[]) => mockMarkOnboardingCompleted(...args),
|
||||
markStepSkipped: (...args: unknown[]) => mockMarkStepSkipped(...args),
|
||||
getOnboardingCompletedAt: (...args: unknown[]) => mockGetOnboardingCompletedAt(...args),
|
||||
getSkippedSteps: (...args: unknown[]) => mockGetSkippedSteps(...args),
|
||||
getStepData: (...args: unknown[]) => mockGetStepData(...args),
|
||||
}));
|
||||
|
||||
@@ -262,8 +266,13 @@ beforeEach(() => {
|
||||
mockIsOnboardingCompleted.mockReset();
|
||||
mockIsOnboardingCompleted.mockReturnValue(false);
|
||||
mockMarkOnboardingCompleted.mockReset();
|
||||
mockMarkStepSkipped.mockReset();
|
||||
mockGetOnboardingCompletedAt.mockReset();
|
||||
mockGetOnboardingCompletedAt.mockReturnValue(null);
|
||||
mockGetSkippedSteps.mockReset();
|
||||
mockGetSkippedSteps.mockReturnValue([]);
|
||||
mockGetStepData.mockReset();
|
||||
mockGetStepData.mockReturnValue(null);
|
||||
});
|
||||
|
||||
describe("App deep link handling", () => {
|
||||
|
||||
@@ -44,6 +44,8 @@ const mockGetOnboardingState = vi.fn();
|
||||
const mockSaveOnboardingState = vi.fn();
|
||||
const mockClearOnboardingState = vi.fn();
|
||||
const mockMarkOnboardingCompleted = vi.fn();
|
||||
const mockMarkStepSkipped = vi.fn();
|
||||
const mockGetSkippedSteps = vi.fn();
|
||||
const mockGetStepData = vi.fn();
|
||||
|
||||
vi.mock("../model-onboarding-state", () => ({
|
||||
@@ -51,6 +53,8 @@ vi.mock("../model-onboarding-state", () => ({
|
||||
saveOnboardingState: (...args: unknown[]) => mockSaveOnboardingState(...args),
|
||||
clearOnboardingState: (...args: unknown[]) => mockClearOnboardingState(...args),
|
||||
markOnboardingCompleted: (...args: unknown[]) => mockMarkOnboardingCompleted(...args),
|
||||
markStepSkipped: (...args: unknown[]) => mockMarkStepSkipped(...args),
|
||||
getSkippedSteps: (...args: unknown[]) => mockGetSkippedSteps(...args),
|
||||
getStepData: (...args: unknown[]) => mockGetStepData(...args),
|
||||
}));
|
||||
|
||||
@@ -123,6 +127,8 @@ beforeEach(() => {
|
||||
mockSaveOnboardingState.mockImplementation(() => {});
|
||||
mockClearOnboardingState.mockImplementation(() => {});
|
||||
mockMarkOnboardingCompleted.mockImplementation(() => {});
|
||||
mockMarkStepSkipped.mockImplementation(() => {});
|
||||
mockGetSkippedSteps.mockReturnValue([]);
|
||||
mockGetStepData.mockReturnValue(null);
|
||||
// Reset mockFetchAuthStatus to default - use mockImplementation for clear control
|
||||
mockFetchAuthStatus.mockReset();
|
||||
@@ -189,6 +195,155 @@ describe("ModelOnboardingModal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("skip vs complete step tracking (FN-1937)", () => {
|
||||
it("does not mark AI Setup step as completed when Skip setup is clicked", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Skip setup →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Skip setup →" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Connect GitHub")).toBeTruthy();
|
||||
});
|
||||
|
||||
const aiSetupIndicator = screen.getByRole("button", { name: "Go back to AI Setup" });
|
||||
expect(aiSetupIndicator).toHaveClass("skipped");
|
||||
expect(aiSetupIndicator).not.toHaveClass("done");
|
||||
expect(aiSetupIndicator.querySelector('[data-testid="icon-check-circle"]')).toBeNull();
|
||||
});
|
||||
|
||||
it("marks AI Setup step as completed when Next is clicked", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Next →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Next →" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Connect GitHub")).toBeTruthy();
|
||||
});
|
||||
|
||||
const aiSetupIndicator = screen.getByRole("button", { name: "Go back to AI Setup" });
|
||||
expect(aiSetupIndicator).toHaveClass("done");
|
||||
expect(aiSetupIndicator).not.toHaveClass("skipped");
|
||||
});
|
||||
|
||||
it("does not add step to completedSteps when skipped", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Skip setup →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
mockSaveOnboardingState.mockClear();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Skip setup →" }));
|
||||
|
||||
await waitFor(() => {
|
||||
const hasSkipPersistence = mockSaveOnboardingState.mock.calls.some((call) => {
|
||||
const options = call[1] as { completedSteps?: string[]; skippedSteps?: string[] } | undefined;
|
||||
return !options?.completedSteps?.includes("ai-setup") && options?.skippedSteps?.includes("ai-setup");
|
||||
});
|
||||
expect(hasSkipPersistence).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("shows dash icon for skipped step in progress indicator", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Skip setup →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Skip setup →" }));
|
||||
|
||||
await waitFor(() => {
|
||||
const skipMark = document.querySelector(".onboarding-step-skip-mark");
|
||||
expect(skipMark).toBeTruthy();
|
||||
expect(skipMark).toHaveTextContent("–");
|
||||
});
|
||||
});
|
||||
|
||||
it("allows going back to a skipped step", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Skip setup →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Skip setup →" }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Connect GitHub")).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "← Back" }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Set Up AI")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("removes skipped status when step is completed after going back", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Skip setup →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Skip setup →" }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Connect GitHub")).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Go back to AI Setup" }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Set Up AI")).toBeTruthy();
|
||||
});
|
||||
|
||||
mockSaveOnboardingState.mockClear();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Next →" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Connect GitHub")).toBeTruthy();
|
||||
});
|
||||
|
||||
const aiSetupIndicator = screen.getByRole("button", { name: "Go back to AI Setup" });
|
||||
expect(aiSetupIndicator).toHaveClass("done");
|
||||
expect(aiSetupIndicator).not.toHaveClass("skipped");
|
||||
|
||||
const hasCompletedPersistence = mockSaveOnboardingState.mock.calls.some((call) => {
|
||||
const options = call[1] as { completedSteps?: string[]; skippedSteps?: string[] } | undefined;
|
||||
return options?.completedSteps?.includes("ai-setup") && !options?.skippedSteps?.includes("ai-setup");
|
||||
});
|
||||
expect(hasCompletedPersistence).toBe(true);
|
||||
});
|
||||
|
||||
it("skipped step is clickable in progress indicator", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Skip setup →" })).toBeTruthy();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Skip setup →" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Connect GitHub")).toBeTruthy();
|
||||
});
|
||||
|
||||
const aiSetupIndicator = screen.getByRole("button", { name: "Go back to AI Setup" });
|
||||
expect(aiSetupIndicator).toHaveClass("skipped");
|
||||
|
||||
fireEvent.click(aiSetupIndicator);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Set Up AI")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AI Setup step", () => {
|
||||
it("shows OAuth providers with Login button", async () => {
|
||||
render(<ModelOnboardingModal onComplete={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
isOnboardingCompleted,
|
||||
getOnboardingCompletedAt,
|
||||
markStepCompleted,
|
||||
markStepSkipped,
|
||||
getCompletedSteps,
|
||||
getSkippedSteps,
|
||||
getStepData,
|
||||
ONBOARDING_STEP_LABELS,
|
||||
} from "../model-onboarding-state";
|
||||
@@ -72,6 +74,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "ai-setup",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -86,6 +89,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "unknown-step",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -114,6 +118,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "github",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -125,6 +130,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup", "github"] as const,
|
||||
skippedSteps: [],
|
||||
dismissed: true,
|
||||
completed: false,
|
||||
stepData: { "ai-setup": { someData: "value" } },
|
||||
@@ -231,6 +237,7 @@ describe("model-onboarding-state", () => {
|
||||
const parsed = JSON.parse(stored);
|
||||
expect(parsed.currentStep).toBe("ai-setup");
|
||||
expect(parsed.completedSteps).toEqual(["ai-setup"]);
|
||||
expect(parsed.skippedSteps).toEqual([]);
|
||||
expect(parsed.dismissed).toBe(false);
|
||||
expect(parsed.completed).toBe(false);
|
||||
});
|
||||
@@ -258,6 +265,27 @@ describe("model-onboarding-state", () => {
|
||||
const parsed = JSON.parse(stored);
|
||||
expect(parsed.completedSteps).toEqual(["ai-setup", "github", "first-task"]);
|
||||
});
|
||||
|
||||
it("markStepCompleted removes step from skippedSteps", () => {
|
||||
markStepSkipped("ai-setup");
|
||||
markStepCompleted("ai-setup");
|
||||
|
||||
const state = getOnboardingState();
|
||||
expect(state?.completedSteps).toContain("ai-setup");
|
||||
expect(state?.skippedSteps).not.toContain("ai-setup");
|
||||
});
|
||||
});
|
||||
|
||||
describe("markStepSkipped", () => {
|
||||
it("markStepSkipped adds step to skippedSteps", () => {
|
||||
markStepSkipped("ai-setup");
|
||||
expect(getOnboardingState()?.skippedSteps).toContain("ai-setup");
|
||||
});
|
||||
|
||||
it("markStepSkipped does not add to completedSteps", () => {
|
||||
markStepSkipped("ai-setup");
|
||||
expect(getOnboardingState()?.completedSteps).not.toContain("ai-setup");
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearOnboardingState", () => {
|
||||
@@ -266,6 +294,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "ai-setup" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup"],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -280,6 +309,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "github" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup", "github"] as const,
|
||||
skippedSteps: [],
|
||||
dismissed: true,
|
||||
completed: false,
|
||||
stepData: { "ai-setup": { someData: "value" } },
|
||||
@@ -314,6 +344,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "ai-setup" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup"],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -336,6 +367,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -349,6 +381,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "complete" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup", "github", "first-task"],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: true,
|
||||
stepData: {},
|
||||
@@ -363,6 +396,7 @@ describe("model-onboarding-state", () => {
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedAt: "2024-01-02T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -377,6 +411,7 @@ describe("model-onboarding-state", () => {
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedAt: "2024-01-02T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -390,6 +425,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
stepData: {},
|
||||
// Note: no completed field
|
||||
@@ -404,6 +440,7 @@ describe("model-onboarding-state", () => {
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedAt: "",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -437,6 +474,26 @@ describe("model-onboarding-state", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSkippedSteps", () => {
|
||||
it("getSkippedSteps returns persisted skipped steps", () => {
|
||||
saveOnboardingState("github", { skippedSteps: ["ai-setup"] });
|
||||
expect(getSkippedSteps()).toEqual(["ai-setup"]);
|
||||
});
|
||||
|
||||
it("skippedSteps defaults to empty array for legacy state", () => {
|
||||
const legacyState = {
|
||||
currentStep: "github" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup"],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
};
|
||||
mockStore[STORAGE_KEY] = JSON.stringify(legacyState);
|
||||
expect(getSkippedSteps()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getStepData", () => {
|
||||
it("returns null when no state exists", () => {
|
||||
expect(getStepData("ai-setup")).toBeNull();
|
||||
@@ -459,6 +516,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
// Note: no stepData field
|
||||
@@ -478,6 +536,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "complete" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -507,6 +566,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup", "github"],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: true,
|
||||
stepData: {},
|
||||
@@ -521,6 +581,7 @@ describe("model-onboarding-state", () => {
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedAt: "2024-01-02T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -534,6 +595,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "github" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup"],
|
||||
skippedSteps: [],
|
||||
dismissed: true,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -554,6 +616,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "complete" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -616,6 +679,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: ["ai-setup", "github"],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: true,
|
||||
stepData: {},
|
||||
@@ -630,6 +694,7 @@ describe("model-onboarding-state", () => {
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedAt: "2024-01-02T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -643,6 +708,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "custom-step" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -661,6 +727,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "my-custom-step" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -675,6 +742,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "my_custom_step" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -752,6 +820,7 @@ describe("model-onboarding-state", () => {
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedAt: timestamp,
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: true,
|
||||
stepData: {},
|
||||
@@ -765,6 +834,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "first-task" as const,
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
@@ -788,6 +858,7 @@ describe("model-onboarding-state", () => {
|
||||
currentStep: "github",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
completedSteps: [],
|
||||
skippedSteps: [],
|
||||
dismissed: false,
|
||||
completed: false,
|
||||
stepData: {},
|
||||
|
||||
Reference in New Issue
Block a user