import { describe, expect, it } from "vitest"; import { ONBOARDING_STEPS, relevantSteps } from "@/onboarding/onboarding-steps"; import { UI_AREA_KEYS, PRESETS } from "@/types/ui-preferences"; import en from "@/locales/en.json"; function lookup(key: string): unknown { return key .split(".") .reduce( (acc, part) => acc && typeof acc === "object" ? (acc as Record)[part] : undefined, en, ); } describe("ONBOARDING_STEPS", () => { it("has unique ids and real title translations", () => { const ids = ONBOARDING_STEPS.map((s) => s.id); expect(new Set(ids).size).toBe(ids.length); for (const step of ONBOARDING_STEPS) { expect(typeof lookup(step.titleKey), step.titleKey).toBe("string"); } }); it("opens with the preset picker right after the welcome", () => { expect(ONBOARDING_STEPS[0].id).toBe("welcome"); expect(ONBOARDING_STEPS[1].id).toBe("preset"); }); it("has no add-a-host step, which used to close onboarding mid-flow", () => { const ids = relevantSteps({ aiGloballyEnabled: false }).map((s) => s.id); expect(ids).not.toContain("first-host"); }); it("ends on the done step", () => { const ids = relevantSteps({ aiGloballyEnabled: false }).map((s) => s.id); expect(ids[ids.length - 1]).toBe("done"); }); it("only offers the AI step when an admin has enabled the assistant", () => { // A user on an instance with AI switched off should never be told the // feature exists, so the step has to disappear rather than render disabled. const off = relevantSteps({ aiGloballyEnabled: false }).map((s) => s.id); const on = relevantSteps({ aiGloballyEnabled: true }).map((s) => s.id); expect(off).not.toContain("ai"); expect(on).toContain("ai"); }); it("tours the feature set after the setup choices", () => { const ids = ONBOARDING_STEPS.map((s) => s.id); for (const id of ["features", "workflow", "security"]) { expect(ids).toContain(id); expect(ids.indexOf(id)).toBeGreaterThan(ids.indexOf("appearance")); } }); }); describe("onboarding step body translations", () => { const KEYS: Record = { welcome: ["hosts", "terminal", "files"], feature: [ "files", "desktop", "tunnels", "docker", "snippets", "automations", "metrics", ], workflow: ["palette", "split", "dock", "workspaces"], security: ["credentials", "twofa", "identity", "sharing"], done: ["settings", "rerun", "docs"], }; it("has a title and description for every card the steps render", () => { for (const [prefix, keys] of Object.entries(KEYS)) { for (const key of keys) { expect( lookup(`onboarding.${prefix}_${key}`), `${prefix}_${key}`, ).toBeTypeOf("string"); expect( lookup(`onboarding.${prefix}_${key}_desc`), `${prefix}_${key}_desc`, ).toBeTypeOf("string"); } } }); it("has the intro copy each step shows above its cards", () => { for (const key of [ "welcomeIntro", "presetIntro", "appearanceIntro", "featuresIntro", "workflowIntro", "securityIntro", "aiIntro", "aiEnableTitle", "aiEnableDesc", "aiSkipTitle", "aiSkipDesc", "aiNote", "doneDesc", ]) { expect(lookup(`onboarding.${key}`), key).toBeTypeOf("string"); } }); }); describe("interface settings translations", () => { it("has a label for every preset the picker offers", () => { for (const preset of ["simple", "balanced", "advanced"]) { expect( lookup(`newUi.sidebar.userProfile.preset_${preset}`), preset, ).toBeTypeOf("string"); expect( lookup(`newUi.sidebar.userProfile.preset_${preset}_desc`), preset, ).toBeTypeOf("string"); } }); it("has a label for every UI area the override list can show", () => { for (const area of UI_AREA_KEYS) { expect( lookup(`newUi.sidebar.userProfile.uiArea_${area}`), area, ).toBeTypeOf("string"); } }); it("covers every area key in each preset", () => { for (const preset of ["simple", "balanced", "advanced"] as const) { expect(Object.keys(PRESETS[preset]).sort()).toEqual( [...UI_AREA_KEYS].sort(), ); } }); });