|
| 1 | +import { act, cleanup, fireEvent, screen, waitFor, within } from "@testing-library/react"; |
| 2 | +import { afterEach, beforeEach, describe, expect, test } from "vitest"; |
| 3 | +import { FakeBotloft } from "../../lib/fake"; |
| 4 | +import { crewOpened, openBot, renderApp, sidebar } from "../../test/app"; |
| 5 | + |
| 6 | +beforeEach(() => localStorage.clear()); |
| 7 | +afterEach(cleanup); |
| 8 | + |
| 9 | +/** A crew "Ops" with @scout and @writer, idle. */ |
| 10 | +function crew() { |
| 11 | + const fake = new FakeBotloft(); |
| 12 | + const ops = fake.addCrew("Ops"); |
| 13 | + const scout = fake.addBot(ops.id, "Scout", "Finds sources"); |
| 14 | + const writer = fake.addBot(ops.id, "Writer", "Writes the notes"); |
| 15 | + fake.setBotState(scout.id, "idle"); |
| 16 | + fake.setBotState(writer.id, "idle"); |
| 17 | + return { fake, scout, writer }; |
| 18 | +} |
| 19 | + |
| 20 | +async function open(name: string) { |
| 21 | + openBot(name); |
| 22 | + await screen.findByRole("region", { name: `Chat with ${name}` }); |
| 23 | +} |
| 24 | + |
| 25 | +async function started(fake: FakeBotloft) { |
| 26 | + renderApp(fake); |
| 27 | + await crewOpened("Ops"); |
| 28 | + await open("Scout"); |
| 29 | +} |
| 30 | + |
| 31 | +const panel = (name: string) => screen.queryByRole("complementary", { name }); |
| 32 | +const button = (name: RegExp) => screen.getByRole("button", { name }); |
| 33 | + |
| 34 | +describe("the panel beside a bot's chat", () => { |
| 35 | + test("comes back with the bot, each bot with its own", async () => { |
| 36 | + const { fake, scout } = crew(); |
| 37 | + await started(fake); |
| 38 | + fireEvent.click(button(/^Show browser/)); |
| 39 | + await waitFor(() => expect(fake.browser.watching).toBe(scout.id)); |
| 40 | + |
| 41 | + await open("Writer"); |
| 42 | + expect(panel("Scout's browser")).toBeNull(); |
| 43 | + expect(panel("Writer's browser")).toBeNull(); |
| 44 | + fireEvent.click(button(/^Show files/)); |
| 45 | + expect(panel("Files from Writer")).not.toBeNull(); |
| 46 | + |
| 47 | + await open("Scout"); |
| 48 | + const back = panel("Scout's browser"); |
| 49 | + expect(back).not.toBeNull(); |
| 50 | + // It is there at once: only a panel opened by hand or by the bot slides. |
| 51 | + expect(back?.className).not.toContain("panel-opening"); |
| 52 | + await waitFor(() => expect(fake.browser.watching).toBe(scout.id)); |
| 53 | + expect(panel("Files from Scout")).toBeNull(); |
| 54 | + |
| 55 | + await open("Writer"); |
| 56 | + expect(panel("Files from Writer")).not.toBeNull(); |
| 57 | + // Another one opened now slides, from the width of the one it replaces. |
| 58 | + fireEvent.click(button(/^Show details/)); |
| 59 | + expect(panel("About Writer")?.className).toContain("panel-opening"); |
| 60 | + }); |
| 61 | + |
| 62 | + test("closed by the owner, it stays closed when they come back", async () => { |
| 63 | + const { fake, scout } = crew(); |
| 64 | + await started(fake); |
| 65 | + // The bot starts browsing: its browser opens by itself. |
| 66 | + act(() => { |
| 67 | + fake.browser.open(scout.id, "https://example.com/", "Example"); |
| 68 | + }); |
| 69 | + await waitFor(() => expect(panel("Scout's browser")).not.toBeNull()); |
| 70 | + fireEvent.click(button(/^Hide browser/)); |
| 71 | + |
| 72 | + await open("Writer"); |
| 73 | + await open("Scout"); |
| 74 | + expect(panel("Scout's browser")).toBeNull(); |
| 75 | + expect(button(/^Show browser/).getAttribute("aria-label")).toBe( |
| 76 | + "Show browser: Scout is using the browser", |
| 77 | + ); |
| 78 | + }); |
| 79 | + |
| 80 | + test("one that opened by itself comes back too, and the crew's page in between changes nothing", async () => { |
| 81 | + const { fake, scout } = crew(); |
| 82 | + await started(fake); |
| 83 | + act(() => { |
| 84 | + fake.screens.draft(scout.id, "C:\\Work\\site\\home.html", "<h1>Home"); |
| 85 | + }); |
| 86 | + await waitFor(() => expect(panel("Scout's screens")).not.toBeNull()); |
| 87 | + |
| 88 | + fireEvent.click(within(sidebar()).getByRole("button", { name: "Ops" })); |
| 89 | + await crewOpened("Ops"); |
| 90 | + expect(panel("Scout's screens")).toBeNull(); |
| 91 | + await open("Scout"); |
| 92 | + expect(panel("Scout's screens")).not.toBeNull(); |
| 93 | + }); |
| 94 | + |
| 95 | + test("what the bot did while the owner was away stays behind its button, beside the panel that came back", async () => { |
| 96 | + const { fake, scout } = crew(); |
| 97 | + await started(fake); |
| 98 | + fireEvent.click(button(/^Show files/)); |
| 99 | + await open("Writer"); |
| 100 | + act(() => { |
| 101 | + fake.browser.open(scout.id, "https://example.com/", "Example"); |
| 102 | + }); |
| 103 | + |
| 104 | + await open("Scout"); |
| 105 | + expect(panel("Files from Scout")).not.toBeNull(); |
| 106 | + expect(panel("Scout's browser")).toBeNull(); |
| 107 | + expect(button(/^Show browser/).getAttribute("aria-label")).toBe( |
| 108 | + "Show browser: Scout is using the browser", |
| 109 | + ); |
| 110 | + // What it starts from now on takes the place of that panel, as always. |
| 111 | + act(() => { |
| 112 | + fake.screens.draft(scout.id, "C:\\Work\\site\\home.html", "<h1>Home"); |
| 113 | + }); |
| 114 | + await waitFor(() => expect(panel("Scout's screens")).not.toBeNull()); |
| 115 | + expect(panel("Files from Scout")).toBeNull(); |
| 116 | + }); |
| 117 | +}); |
0 commit comments