import { expect } from "playwright/test"; import { afterAll, describe, it } from "vitest"; import { BASE_URL, E2E_WORKSPACE_PATH } from "../helpers"; import { authedPage, closeBrowser } from "./browser"; import { clearDefaultWorkspaceOwnership, restoreDefaultWorkspaceOwnership, updateUser } from "../setup/db"; // The wizards restore their flags on completion; afterAll is a safety net so a mid-test failure can't leave the seeded user stranded in a wizard, or leave the default workspace without an Owner role for every other spec file that assumes one exists. afterAll(() => { updateUser({ first_login_done: 1 }); restoreDefaultWorkspaceOwnership(); return closeBrowser(); }); describe("onboarding wizards", () => { it("welcome wizard: opens at first sign-in, and finishing later continues into the app", async () => { updateUser({ first_login_done: 0 }); const page = await authedPage(`${E2E_WORKSPACE_PATH}/docs`); await page.waitForURL("**/wizard/welcome?step=profile"); await expect(page.getByRole("heading", { name: "Introduce yourself" })).toBeVisible(); await page.getByRole("button", { name: "Continue", exact: true }).click(); await page.waitForURL((url) => url.searchParams.get("step") !== "profile"); await page.getByRole("button", { name: "Finish later", exact: true }).click(); await page.waitForURL(`${BASE_URL}${E2E_WORKSPACE_PATH}`); await expect(page.getByRole("heading", { name: /docs, tickets, chat, and deploys/i })).toBeVisible(); }); it("owner wizard: first user sets up their workspace, connects tools, and finishes with no computer", async () => { updateUser({ first_login_done: 1 }); // global.ts's seed pre-creates the default workspace's Owner role/membership for every other spec's benefit, but tenancy.BindDefaultWorkspaceOwner always INSERTs a fresh one (idx_roles_owner_singleton), so this genuinely-fresh-owner flow 409s unless that pre-seeded row is cleared first. clearDefaultWorkspaceOwnership(); const page = await authedPage("/"); await page.waitForURL("**/onboarding/owner"); await page.getByRole("button", { name: "Continue", exact: true }).click(); // The workspace step names the workspace only; projects come from the project wizard. await expect(page.getByLabel("Workspace name", { exact: true })).toBeVisible(); await expect(page.getByLabel(/project/i)).toHaveCount(0); await page.getByRole("button", { name: "Continue", exact: true }).click(); // Connect your tools is the last step; no computer is needed to finish. Wait for it, or the click lands on the workspace step's busy Continue. await expect(page.getByRole("heading", { name: "Connect your tools" })).toBeVisible(); await page.getByRole("button", { name: "Continue", exact: true }).click(); await expect(page.getByRole("heading", { name: "Workspace ready" })).toBeVisible(); // The Welcome wizard takes over at its finish view, listing what is still the owner's to set up, past the profile the owner wizard already set. await page.waitForURL((url) => url.pathname === "/wizard/welcome" && url.searchParams.get("step") === "finish"); await expect(page.getByRole("heading", { name: "2 things left for later" })).toBeVisible(); await expect(page.getByRole("button", { name: "Open Connect GitHub" })).toBeVisible(); await expect(page.getByRole("button", { name: "Open Your computer" })).toBeVisible(); await page.getByRole("button", { name: "Finish later", exact: true }).click(); await page.waitForURL((url) => !url.pathname.startsWith("/wizard/welcome")); }); it("dns onboarding renders and can be skipped", async () => { const page = await authedPage("/wizard/onboarding/dns"); await page.waitForLoadState("networkidle"); await expect( page.getByRole("heading", { name: /set up dns/i }), ).toBeVisible(); await page.getByRole("button", { name: /skip for now/i }).click(); await page.waitForURL("**/"); }); });