* chore: remove legacy orchestrator, trading and one-off script leftovers The repository still carried the pre-evOo prototype: a Python orchestrator, the old HTML "evoo-platform" cockpit (whose test ran in the root unit suite), an apex-trading package, the hot-reload deploy.sh for it, a permissions manifest, an architecture note with local file links, agent handoff notes, a Steve Jobs persona template and about ninety one-off scripts that probe a developer database with hard-coded local URLs. None of it is used by the product, CI or the real deploy in ops/evo-deploy. Delete those files, drop the evoo-platform test glob from the root vitest config, the Cockpit section from .env.example and the dead biome and gitignore entries, and update the docs that listed the removed tools. scripts/start-tmux.sh no longer starts the removed sentinel and resolves the checkout from its own location instead of a machine-specific path; scripts/seed-ollama.ts requires DATABASE_URL instead of falling back to a local URL with a username. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * fix(web): drop trading and persona prompts from the conversation shell The web shell still showed leftovers from the trading prototype: composer chips with German trading and "Steve Jobs" prompts, trading suggestion chips under the empty conversation line, a German trading hero for the empty transcript, and a German "data protection" status panel in the sidebar. They suggested actions the product does not have and added persistent chrome. Remove the composer chips and their helper, the empty-state chips and the hero (the empty conversation keeps its existing one-line start hint), the sidebar trading indicator and the data trust panel. The new-bot E2E now asserts the plain empty state; the sidebar spec no longer expects the trading status, and the placeholder Steve Jobs spec with no assertions is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * refactor: remove the trading bot status integration With the sidebar trading indicator gone, the only remaining consumer of the trading bot status was a banner in General settings that reused the same component. evOo is a personal assistant; a status proxy and poller for one external trading service, configured through service-specific TRADING_BOT_URL and VITE_TRADING_BOT_URL variables, does not belong in the core product. Remove the settings banner and its prop plumbing, the web poller, the indicator component, the /api/trading-bot/status proxy, the shared core contract, the env loading and both variables from .env.example, together with their tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * chore: remove one-off database scripts and a stale review note packages/db carried four hand-run scripts outside the package build that query or seed a developer database with a hard-coded connection string, one of them for a specific account. docs/VOICE-FIRST-REVIEW.md was a one-time verification note for a workflow that no longer exists and named private machines and local paths. Nothing references them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * chore: replace machine-specific paths and addresses with placeholders ORIGIN.md pinned the intended checkout to an absolute path under a personal home directory, docs/LOCAL-SETUP.md listed folders on one developer's machine, and two tests used what looks like a real tailnet address. Use a home-relative path, drop the machine-specific folder list, and use 100.100.100.100 in the tests; it is still inside 100.64.0.0/10, so both tests check the same thing as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * test(web): drop the trading status from the shell contrast test main's contrast test now waits for the sidebar trading status before measuring; this branch removes that status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
191 lines
9.1 KiB
TypeScript
191 lines
9.1 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
captureScreenshot,
|
|
completeOnboarding,
|
|
createBotFromPicker,
|
|
openNewBot,
|
|
rpc,
|
|
signup,
|
|
} from "./helpers";
|
|
|
|
test("create opens form, then empty chat; picker lists bots; sidebar collapses", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `new-bot-ux-${stamp}@rakazo.test`, "password12", "New Bot UX");
|
|
await completeOnboarding(page);
|
|
await page.goto("/app");
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
|
|
await page.getByTestId("create-menu-trigger").click();
|
|
const picker = page.getByTestId("bot-create-picker");
|
|
await expect(picker).toBeVisible();
|
|
await expect(picker.getByPlaceholder("Search")).toBeVisible();
|
|
await expect(picker.getByTestId("create-new-bot")).toBeVisible();
|
|
await expect(picker.getByText("Chief", { exact: true })).toBeVisible();
|
|
await captureScreenshot(page, testInfo, "plus-picker-bots");
|
|
|
|
await picker.getByTestId("create-new-bot").click();
|
|
const form = page.getByTestId("create-bot-form");
|
|
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "create");
|
|
await expect(form).toBeVisible();
|
|
await expect(form.locator("label:has-text('Name') input")).toBeVisible();
|
|
await expect(form.locator("label:has-text('Title') input")).toBeVisible();
|
|
await expect(form.locator("label:has-text('Description') textarea")).toBeVisible();
|
|
await expect(form.getByTestId("create-bot-computer")).toBeVisible();
|
|
await expect(form.getByTestId("create-bot-team")).toBeVisible();
|
|
await expect(form.getByTestId("create-bot-private")).toBeVisible();
|
|
await captureScreenshot(page, testInfo, "create-bot-form");
|
|
|
|
await form.locator("label:has-text('Name') input").fill("New Bot");
|
|
await form.getByRole("button", { name: "Create", exact: true }).click();
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
|
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "closed");
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0);
|
|
await expect(
|
|
page.getByTestId("transcript").getByText("Start the conversation — New Bot is on stage above."),
|
|
).toBeVisible();
|
|
await captureScreenshot(page, testInfo, "create-chat-sidepanel-closed");
|
|
|
|
await page.getByTestId("toggle-bots-sidebar").click();
|
|
await expect(page.getByTestId("bots-sidebar")).toHaveAttribute("data-collapsed", "true");
|
|
const edge = page.getByTestId("bots-sidebar-edge");
|
|
await expect(edge).toBeVisible();
|
|
const restore = page.getByTestId("toggle-bots-sidebar");
|
|
await expect(restore).toBeVisible();
|
|
await captureScreenshot(page, testInfo, "bots-sidebar-collapsed");
|
|
|
|
await restore.click();
|
|
await expect(page.getByTestId("bots-sidebar")).toHaveAttribute("data-collapsed", "false");
|
|
await expect(page.getByRole("button", { name: "Minimize bots", exact: true })).toBeVisible();
|
|
|
|
await page.getByTestId("toggle-bots-sidebar").click();
|
|
await expect(page.getByTestId("bots-sidebar")).toHaveAttribute("data-collapsed", "true");
|
|
|
|
const box = await edge.boundingBox();
|
|
expect(box).toBeTruthy();
|
|
await page.mouse.move(box!.x + box!.width / 2, box!.y + box!.height / 2);
|
|
await page.mouse.down();
|
|
await page.mouse.move(box!.x + 80, box!.y + box!.height / 2, { steps: 8 });
|
|
await page.mouse.up();
|
|
await expect(page.getByTestId("bots-sidebar")).toHaveAttribute("data-collapsed", "false");
|
|
await captureScreenshot(page, testInfo, "bots-sidebar-expanded");
|
|
});
|
|
|
|
test("picker rows explain groups and spaces", async ({ page }, testInfo) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `picker-info-${stamp}@rakazo.test`, "password12", "Picker Info");
|
|
await completeOnboarding(page);
|
|
await page.goto("/app");
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
|
|
await page.getByTestId("create-menu-trigger").click();
|
|
const picker = page.getByTestId("bot-create-picker");
|
|
await expect(picker).toBeVisible();
|
|
|
|
await picker.getByTestId("create-new-group").hover();
|
|
const groupInfo = picker.getByTestId("picker-info-group");
|
|
await expect.poll(() => groupInfo.evaluate((el) => getComputedStyle(el).opacity)).toBe("1");
|
|
await captureScreenshot(page, testInfo, "picker-group-info-hover");
|
|
await groupInfo.click();
|
|
const dialog = page.getByTestId("picker-info-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog.getByText("Groups", { exact: true })).toBeVisible();
|
|
await expect(dialog).toContainText("same thread");
|
|
await expect(page.getByTestId("side-panel")).not.toHaveAttribute("data-panel", "create-group");
|
|
await captureScreenshot(page, testInfo, "picker-group-info-dialog");
|
|
await dialog.getByRole("button", { name: "Close" }).click();
|
|
await expect(dialog).toBeHidden();
|
|
|
|
await page.getByTestId("create-menu-trigger").click();
|
|
const spaceInfo = picker.getByTestId("picker-info-space");
|
|
await expect.poll(() => spaceInfo.evaluate((el) => getComputedStyle(el).opacity)).toBe("0");
|
|
await picker.getByTestId("create-new-space").hover();
|
|
await expect.poll(() => spaceInfo.evaluate((el) => getComputedStyle(el).opacity)).toBe("1");
|
|
await spaceInfo.click();
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog.getByText("Spaces", { exact: true })).toBeVisible();
|
|
await expect(dialog).toContainText("own bots and groups");
|
|
await captureScreenshot(page, testInfo, "picker-space-info-dialog");
|
|
await page.keyboard.press("Escape");
|
|
await expect(dialog).toBeHidden();
|
|
});
|
|
|
|
test("later bot waits before showing the focus card; sending cancels it", async ({ page }) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `focus-delay-${stamp}@rakazo.test`, "password12", "Focus Delay");
|
|
await completeOnboarding(page, undefined, { mainConversation: true });
|
|
// The main bot shows the focus card immediately in the main conversation.
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toBeVisible();
|
|
|
|
await page.clock.install();
|
|
await createBotFromPicker(page);
|
|
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0);
|
|
|
|
await page.clock.fastForward(9_000);
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0);
|
|
await page.clock.fastForward(1_500);
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toBeVisible();
|
|
|
|
await createBotFromPicker(page, { name: "Later Bot" });
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0);
|
|
const composer = page.getByPlaceholder(/Message/);
|
|
await composer.fill("I'll set this up myself");
|
|
// Send must finish before the delay is advanced: cancel runs after a successful
|
|
// RPC, and promptFocus will still post if the clock fires while send is in flight.
|
|
const sent = page.waitForResponse(
|
|
(response) => response.url().includes("/rpc/threads/send") && response.ok(),
|
|
);
|
|
await page.keyboard.press("Enter");
|
|
await sent;
|
|
await expect(
|
|
page.getByTestId("transcript").getByText("I'll set this up myself").first(),
|
|
).toBeVisible();
|
|
await page.clock.fastForward(12_000);
|
|
await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0);
|
|
});
|
|
|
|
test("plus picker can create a Private computer bot", async ({ page }, testInfo) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `new-bot-private-${stamp}@rakazo.test`, "password12", "New Bot Private");
|
|
await completeOnboarding(page);
|
|
await page.goto("/app");
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
|
|
await createBotFromPicker(page, { computerMode: "dedicated" });
|
|
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
|
await captureScreenshot(page, testInfo, "create-private-computer-bot");
|
|
|
|
const botId = page.url().split("/").pop()!;
|
|
const bots = await rpc<Array<{ id: string; computerMode: string }>>(page, "bots/list", {});
|
|
expect(bots.find((bot) => bot.id === botId)?.computerMode).toBe("dedicated");
|
|
});
|
|
|
|
test("second bot from plus opens create form before persist", async ({ page }, testInfo) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `second-bot-form-${stamp}@rakazo.test`, "password12", "Second Bot Form");
|
|
await completeOnboarding(page);
|
|
await page.goto("/app");
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
|
|
await openNewBot(page);
|
|
const form = page.getByTestId("create-bot-form");
|
|
await expect(form).toBeVisible();
|
|
await form.locator("label:has-text('Name') input").fill("Researcher");
|
|
await form.locator("label:has-text('Title') input").fill("Finds sources");
|
|
await form.locator("label:has-text('Description') textarea").fill("Briefs from the web.");
|
|
await captureScreenshot(page, testInfo, "second-bot-create-form");
|
|
|
|
const create = page.waitForResponse(
|
|
(response) => response.url().includes("/rpc/bots/create") && response.ok(),
|
|
);
|
|
await form.getByRole("button", { name: "Create", exact: true }).click();
|
|
await create;
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
|
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "closed");
|
|
await captureScreenshot(page, testInfo, "second-bot-created");
|
|
});
|