import { expect, test } from "@playwright/test" import { api, apiPage, deleteAll } from "./utils/api" /** * The global search: the only way to reach a node without knowing its flow. * * The load-bearing part is what the flat index turns back into — a node hit is * a flow address with that node in focus, which no list endpoint could have * answered. */ const flowName = `test_search_${Date.now().toString(36)}` test.use({ storageState: "playwright/.auth/user.json" }) test.describe.configure({ mode: "serial" }) test.beforeAll(async ({ browser }) => { const page = await apiPage(browser) await api(page, `/flows/${flowName}`, { method: "PUT", data: { name: flowName, title: "Searchable flow", nodes: [{ id: "findme", type: "python", title: "Find me" }], inputs: [{ spec: { name: "findme_input", dtype: "float" } }], }, }) await page.close() }) test.afterAll(async ({ browser }) => { await deleteAll(browser, [`/flows/${flowName}`]) }) test("finds a node by name and opens its flow on it", async ({ page }) => { await page.goto("/") await page.getByRole("button", { name: "Search", exact: true }).click() await page.getByTestId("global-search-input").fill("findme") await page.getByRole("option", { name: /Find me/ }).click() await expect(page).toHaveURL(new RegExp(`/flows/${flowName}\\?node=findme$`)) }) test("finds a flow input and reads it where its label sits", async ({ page, }) => { await page.goto("/") await page.getByRole("button", { name: "Search", exact: true }).click() await page.getByTestId("global-search-input").fill("findme_input") await page.getByRole("option", { name: /findme_input/ }).click() await expect(page).toHaveURL( new RegExp(`/flows/${flowName}\\?node=input%3A${flowName}\\.findme_input$`), ) // What clicking the label shows, and the settings one press on. await expect(page.getByTestId("input-inspector")).toBeVisible() await expect(page.getByTestId("flow-panel")).toBeHidden() await page.getByTestId("edit-input").click() await expect(page.getByTestId("flow-panel")).toBeVisible() }) test("the label popover rides along when the canvas is panned", async ({ page, }) => { const id = `input:${flowName}.findme_input` await page.goto(`/flows/${flowName}?node=${encodeURIComponent(id)}`) const popover = page.getByTestId("input-inspector") const label = page.locator(`.react-flow__node[data-id="${id}"]`) await expect(popover).toBeVisible() // Its own entrance moves it too, which would read as part of the offset. await expect .poll(() => popover.evaluate((el) => el.getAnimations().length)) .toBe(0) // Where the popover hangs off the label, which a pan must not change. const offset = async () => { const [from, to] = [await label.boundingBox(), await popover.boundingBox()] return [Math.round(to!.x - from!.x), Math.round(to!.y - from!.y)] } const [before, at] = [await offset(), await label.boundingBox()] const { width, height } = page.viewportSize() ?? { width: 0, height: 0 } await page.mouse.move(width * 0.8, height * 0.2) await page.mouse.down() await page.mouse.move(width * 0.8 - 90, height * 0.2 + 50, { steps: 6 }) await page.mouse.up() await expect.poll(async () => (await label.boundingBox())!.x).not.toBe(at!.x) await expect(popover).toBeVisible() await expect.poll(offset).toEqual(before) }) test("opens on the keyboard from anywhere", async ({ page }) => { await page.goto("/dashboards") // The binding lives in the sidebar, so wait for it to be there to press at. await expect( page.getByRole("button", { name: "Search", exact: true }), ).toBeVisible() await page.keyboard.press("ControlOrMeta+k") await expect(page.getByTestId("global-search-input")).toBeFocused() })