mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-03 23:03:22 +00:00
Imported from andrewyng/aisuite@1b4bbf303e (contents of its platform/ directory, hoisted to the repo root). Development history prior to this commit lives in that repository. Co-authored-by: Devika <devikaverma11@gmail.com>
157 lines
8.1 KiB
TypeScript
157 lines
8.1 KiB
TypeScript
// First-run onboarding (UX-DECISIONS §24 → §29 → §39): model → your tools → go.
|
||
// §39: step 1 is a provider GALLERY (cards wear their own state; a card opens its key
|
||
// form inside a fixed-height swap region; Test verifies, SAVES, and returns) and step 2
|
||
// is a two-state tools page (why-paragraph + sign-in → mini connector gallery with live
|
||
// one-click connects). Entered here via the REPLAY path (Settings ▸ Appearance ▸ "Run
|
||
// setup again") — which is itself under test.
|
||
import { expect } from "@playwright/test";
|
||
import { test } from "./fixtures";
|
||
|
||
async function openOnboarding(page) {
|
||
await page.goto("/");
|
||
await page.getByTestId("account-row").click();
|
||
await page.getByTestId("account-menu").getByRole("button", { name: "Settings" }).click();
|
||
await page.getByRole("button", { name: "Run setup again" }).click();
|
||
await expect(page.getByTestId("ob-step-model")).toBeVisible();
|
||
}
|
||
|
||
test("provider gallery: cards wear their state; Next arms off stored credentials", async ({
|
||
page,
|
||
}) => {
|
||
await openOnboarding(page);
|
||
|
||
// Every card carries its own status with zero clicks (the 2026-07-16 confusion —
|
||
// "is OpenAI already connected?" — is answered by the gallery itself).
|
||
await expect(page.getByTestId("ob-provider-openai")).toContainText("✓ Connected");
|
||
await expect(page.getByTestId("ob-provider-anthropic")).toContainText("✓ Connected");
|
||
await expect(page.getByTestId("ob-provider-zai")).toContainText("Not set up");
|
||
await expect(page.getByTestId("ob-provider-ollama")).toContainText("No key needed");
|
||
// Recognition-first order: anthropic before openai before the OpenAI-compat tail.
|
||
const names = await page
|
||
.getByTestId("ob-provider-gallery")
|
||
.locator("[data-testid^=ob-provider-]")
|
||
.evaluateAll((els) => els.map((e) => e.getAttribute("data-testid")));
|
||
expect(names.indexOf("ob-provider-anthropic")).toBeLessThan(names.indexOf("ob-provider-openai"));
|
||
expect(names.indexOf("ob-provider-openai")).toBeLessThan(names.indexOf("ob-provider-zai"));
|
||
|
||
// A configured provider already arms Next — no form visit required.
|
||
await expect(page.getByTestId("ob-continue")).toBeEnabled();
|
||
await page.getByTestId("ob-continue").click();
|
||
await expect(page.getByTestId("ob-step-tools")).toBeVisible();
|
||
});
|
||
|
||
test("key form: Test verifies, saves, and returns to the gallery with the ✓", async ({
|
||
page,
|
||
}) => {
|
||
await openOnboarding(page);
|
||
|
||
await page.getByTestId("ob-provider-zai").click();
|
||
// The header stays put (§39 fixed frame): the welcome headline is still on screen.
|
||
await expect(page.getByRole("heading", { name: "Welcome to OpenWorker" })).toBeVisible();
|
||
// Optional endpoint is a quiet disclosure with no explainer copy (owner call 2026-07-18).
|
||
await expect(page.getByTestId("ob-field-base_url")).toHaveCount(0);
|
||
await page.getByTestId("ob-endpoint-link").click();
|
||
await expect(page.getByTestId("ob-field-base_url")).toHaveValue(/api\.z\.ai/);
|
||
|
||
// Bad key: the error is a line, not a navigation.
|
||
await page.getByTestId("ob-field-api_key").fill("bad-key");
|
||
await page.getByTestId("ob-test").click();
|
||
await expect(page.getByText("Invalid API key.")).toBeVisible();
|
||
|
||
// Good key: state lands IN the field ("✓ Tested & saved" pill), then the form
|
||
// auto-returns to the gallery where the Z AI card now wears its ✓.
|
||
await page.getByTestId("ob-field-api_key").fill("zk-good");
|
||
await page.getByTestId("ob-test").click();
|
||
await expect(page.getByTestId("ob-saved-pill")).toBeVisible();
|
||
await expect(page.getByTestId("ob-provider-zai")).toContainText("✓ Connected", {
|
||
timeout: 5_000,
|
||
});
|
||
await expect(page.getByTestId("ob-continue")).toBeEnabled();
|
||
});
|
||
|
||
test("key form: revisiting a connected provider shows the in-field saved state; drafts survive switching", async ({
|
||
page,
|
||
}) => {
|
||
await openOnboarding(page);
|
||
|
||
// Revisit a configured provider: green in-field pill + masked placeholder — the old
|
||
// empty-password-field-reads-as-not-set-up trap (owner complaint 2026-07-16) is gone.
|
||
await page.getByTestId("ob-provider-openai").click();
|
||
await expect(page.getByTestId("ob-saved-pill")).toBeVisible();
|
||
await expect(page.getByTestId("ob-field-api_key")).toHaveAttribute("placeholder", "••••••••");
|
||
|
||
// Typed-but-unsaved input survives a peek at another provider (drafts).
|
||
await page.getByTestId("ob-back").click();
|
||
await page.getByTestId("ob-provider-zai").click();
|
||
await page.getByTestId("ob-field-api_key").fill("zk-draft");
|
||
await page.getByTestId("ob-back").click();
|
||
await page.getByTestId("ob-provider-openai").click();
|
||
await expect(page.getByTestId("ob-saved-pill")).toBeVisible();
|
||
await page.getByTestId("ob-back").click();
|
||
await page.getByTestId("ob-provider-zai").click();
|
||
await expect(page.getByTestId("ob-field-api_key")).toHaveValue("zk-draft");
|
||
|
||
// Next from a dirty form auto-verifies and saves first (2026-07-12: no hidden
|
||
// Test-then-Continue two-step), then advances.
|
||
await page.getByTestId("ob-field-api_key").fill("zk-good");
|
||
await page.getByTestId("ob-continue").click();
|
||
await expect(page.getByTestId("ob-step-tools")).toBeVisible();
|
||
});
|
||
|
||
test("tools page: sign-in morphs the page into the connector gallery; a card connects one-click", async ({
|
||
page,
|
||
}) => {
|
||
await openOnboarding(page);
|
||
await page.getByTestId("ob-continue").click();
|
||
await expect(page.getByTestId("ob-step-tools")).toBeVisible();
|
||
|
||
// Pre-sign-in (§41): the benefit rows are already there (no Connect buttons yet),
|
||
// the combined Google row says Coming soon, the band asks for sign-in, and the one
|
||
// footer button is the quiet "Continue without sign-in".
|
||
await expect(page.getByText("Chat can only advise")).toBeVisible();
|
||
await expect(page.getByTestId("ob-tool-outlook")).toContainText("Stay on top of email");
|
||
await expect(page.getByTestId("ob-tool-outlook").getByRole("button")).toHaveCount(0);
|
||
await expect(page.getByTestId("ob-tool-attio")).toContainText("Track every relationship");
|
||
await expect(page.getByTestId("ob-tool-google-soon")).toContainText("Coming soon");
|
||
await expect(page.getByText("Sign in for one-click connections")).toBeVisible();
|
||
await expect(page.getByTestId("ob-tools-skip")).toContainText("Continue without sign-in");
|
||
|
||
// Sign-in lands out-of-band; the band's SLOT stays put and flips to the congrats
|
||
// (zero layout shift), and every row grows its Connect pill.
|
||
await page.getByTestId("ob-cloud-signin").click();
|
||
await expect(page.getByTestId("ob-tools-signedin")).toBeVisible({ timeout: 10_000 });
|
||
await expect(page.getByTestId("ob-tools-signedin")).toContainText("You’re signed in");
|
||
await expect(
|
||
page.getByTestId("ob-tool-attio").getByRole("button", { name: "Connect" }),
|
||
).toBeVisible();
|
||
await expect(page.getByTestId("ob-tool-google-soon").getByRole("button")).toHaveCount(0);
|
||
|
||
// One-click connect: the consent completes in the (mock) browser; the poll flips the
|
||
// row to ✓ Connected. Next was armed the whole time — connecting is optional.
|
||
await page.getByTestId("ob-tool-outlook").getByRole("button", { name: "Connect" }).click();
|
||
await expect(page.getByTestId("ob-tool-outlook")).toContainText("✓ Connected", {
|
||
timeout: 10_000,
|
||
});
|
||
await expect(page.getByTestId("ob-continue-tools")).toBeEnabled();
|
||
await page.getByTestId("ob-continue-tools").click();
|
||
|
||
// Done step: the automation CTA lands on the Automations quickstart.
|
||
await expect(page.getByTestId("ob-step-done")).toBeVisible();
|
||
await page.getByTestId("ob-cta-automation").click();
|
||
await expect(page.getByTestId("onboarding")).toHaveCount(0);
|
||
await expect(page.getByRole("heading", { name: "Automations" })).toBeVisible();
|
||
});
|
||
|
||
test("tools page skips cleanly; Start working lands in a session with the panel open", async ({
|
||
page,
|
||
}) => {
|
||
await openOnboarding(page);
|
||
await page.getByTestId("ob-continue").click();
|
||
await page.getByTestId("ob-tools-skip").click();
|
||
await expect(page.getByTestId("ob-step-done")).toBeVisible();
|
||
await page.getByTestId("ob-start").click();
|
||
await expect(page.getByTestId("onboarding")).toHaveCount(0);
|
||
// §32: "Start working" lands with the rail's Access section expanded (the drawer is gone).
|
||
await expect(page.getByRole("region", { name: "Session access" })).toBeVisible();
|
||
});
|