mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-09 06:15:21 +02:00
Takes the four tenant onboarding steps to the design, and makes the model choice explicit. **Nothing is preselected on the connect step.** It arrived with a source already chosen, which made the row read as a confirmation rather than a question and let a customer pass the step having touched none of it. The step now opens unanswered and cannot advance until a source is selected in the visible row. Two defects fell out of that, both found by Greptile in review: - A saved draft can name an adapter the row does not show — one the registry dropped, or one in the advanced list. The gate asked `sourcePicked`, which only means "a draft named something", so Next stayed live on a step that had visibly asked nothing and would hire against the hidden name. It now asks `sourceSelected`, read off the visible row, and the snap that replaces an unofferable adapter clears the pick rather than presenting its replacement as chosen. - Cmd+Enter bypassed that gate entirely, because the condition was written out twice and drifted. Both paths now ask one predicate. Also: selection is a fill rather than a border; the input canvas opens only for a chosen source; the close control is gone from every step (nothing downstream of the connect step works until a model is connected); the arc draws to the design's 424px column with a filled 44px name field; the CTA reads "Next" through the arc and "Get started" at the end; the login spinner reads "Preparing..."; the OAuth panels number their fields and show the URL above the code. Storybook: the arc stories waited for a button named "Connect" and had been silently rendering the wrong step since the CTA was renamed. They now wait on the destination heading. Three e2e specs had the same fault and now select a source before advancing. One test was removed rather than replaced — `hydrates again when the same company comes back through onboarding` drove the close control, and three substitutes each passed against a wizard with the behaviour deleted. The reason is recorded where it stood. Known and not fixed here: SidebarCompanyMenu opens the wizard at step 1 for "create a new organization", and with no exit an existing user who changes their mind is trapped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
73 lines
3.1 KiB
TypeScript
73 lines
3.1 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
|
|
import { ONBOARDING_STORAGE_KEY } from "@/components/OnboardingWizard";
|
|
import {
|
|
ONBOARDING_ARC_ENTRY_STEP,
|
|
STORYBOOK_COMPANY_ID,
|
|
clearOnboardingDraft,
|
|
readOnboardingDraft,
|
|
seedOnboardingDraft,
|
|
} from "./onboardingDraft";
|
|
|
|
afterEach(() => {
|
|
window.localStorage.clear();
|
|
});
|
|
|
|
describe("storybook onboarding draft", () => {
|
|
// The leak this exists to stop: `localStorage` is per-origin and shared by
|
|
// every story in a session, so a seeded draft left behind makes the *next*
|
|
// story restore a saved step instead of the one it asked for. The reviewer
|
|
// then sees a screen they did not click on, which reads as a wizard bug.
|
|
it("leaves nothing behind once cleared", () => {
|
|
seedOnboardingDraft();
|
|
expect(readOnboardingDraft()).not.toBeNull();
|
|
|
|
clearOnboardingDraft();
|
|
expect(readOnboardingDraft()).toBeNull();
|
|
expect(window.localStorage.getItem(ONBOARDING_STORAGE_KEY)).toBeNull();
|
|
});
|
|
|
|
// The wizard captures `entryStep` from this draft once, at mount, and offers
|
|
// Back only while `currentStep > entryStep`. Seeding a later step is therefore
|
|
// not a shortcut to it — it is a step that can never show its Back button.
|
|
it("enters the arc at its first step, so later steps can be walked into", () => {
|
|
seedOnboardingDraft();
|
|
expect(readOnboardingDraft()?.step).toBe(ONBOARDING_ARC_ENTRY_STEP);
|
|
});
|
|
|
|
// `createdAgentId` is what `launchStateIncomplete` checks. Filling it in
|
|
// before the hire would paint over the guard step 5 is supposed to show when
|
|
// it is reached without an agent.
|
|
it("does not claim an agent exists before the hire", () => {
|
|
seedOnboardingDraft();
|
|
expect(readOnboardingDraft()?.createdAgentId).toBe("");
|
|
});
|
|
|
|
// A run standing on step 3 has not reached the connect step, so it cannot
|
|
// have chosen a source there. Seeding one is not a harmless head start: the
|
|
// step reads a saved `adapterType` as "already picked", and every arc story
|
|
// opened with Claude Code selected and its sign-in panel already showing —
|
|
// the preselection the step was changed to stop doing, restored by the
|
|
// fixture. Stories that need a source click one, the way a customer does.
|
|
it("does not claim a model source was chosen before the connect step", () => {
|
|
seedOnboardingDraft();
|
|
expect(readOnboardingDraft()).not.toHaveProperty("adapterType");
|
|
});
|
|
|
|
// `restoreOnboardingState` treats restoring as an authorization decision and
|
|
// throws the whole blob away when the saved company is not one the account
|
|
// owns. Seeding a company the fixtures do not report would silently restore
|
|
// nothing, and every story would quietly fall back to its `initialStep`.
|
|
it("names the company the fixtures report as owned", () => {
|
|
seedOnboardingDraft();
|
|
expect(readOnboardingDraft()?.createdCompanyId).toBe(STORYBOOK_COMPANY_ID);
|
|
});
|
|
|
|
it("survives a malformed value without throwing", () => {
|
|
window.localStorage.setItem(ONBOARDING_STORAGE_KEY, "{not json");
|
|
expect(readOnboardingDraft()).toBeNull();
|
|
});
|
|
});
|