93 lines
2.9 KiB
TypeScript
93 lines
2.9 KiB
TypeScript
import { expect, test } from "@playwright/test"
|
|
import { api, apiPage, createFlow, deleteAll } from "./utils/api"
|
|
|
|
/**
|
|
* One socket serves the whole shell, and it has to survive the routes under it
|
|
* coming and going.
|
|
*
|
|
* It used to belong to whichever component's effect ran first, which on Home is
|
|
* the brain graph rather than the shell around it. Leaving Home closed the
|
|
* socket while the shell held the reference count above zero, and the page was
|
|
* deaf for the rest of its life: no live values, no pulses, and nothing that
|
|
* would ever reconnect.
|
|
*/
|
|
|
|
const flowName = `test_live_${Date.now().toString(36)}`
|
|
|
|
test.use({ storageState: "playwright/.auth/user.json" })
|
|
|
|
test.beforeAll(async ({ browser }) => {
|
|
const page = await apiPage(browser)
|
|
await createFlow(page, {
|
|
name: flowName,
|
|
title: "Live",
|
|
nodes: [
|
|
{
|
|
id: "source",
|
|
type: "python",
|
|
provides: [{ name: "reading", dtype: "float" }],
|
|
},
|
|
{
|
|
id: "sink",
|
|
type: "python",
|
|
requires: [{ name: "reading", dtype: "float" }],
|
|
},
|
|
],
|
|
})
|
|
const detail = await (await api(page, `/flows/${flowName}`)).json()
|
|
await api(page, `/flows/${flowName}/publish`, {
|
|
method: "POST",
|
|
data: { version: detail.definition.version },
|
|
})
|
|
await page.close()
|
|
})
|
|
|
|
test.afterAll(async ({ browser }) => {
|
|
await deleteAll(browser, [`/flows/${flowName}`])
|
|
})
|
|
|
|
test("the live socket survives leaving Home and coming back", async ({
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(() => {
|
|
const Native = window.WebSocket
|
|
const opened: WebSocket[] = []
|
|
;(window as unknown as { __sockets: WebSocket[] }).__sockets = opened
|
|
window.WebSocket = class extends Native {
|
|
constructor(url: string | URL, protocols?: string | string[]) {
|
|
super(url, protocols)
|
|
opened.push(this)
|
|
}
|
|
}
|
|
})
|
|
|
|
await page.goto("/")
|
|
const neuron = page.locator(".brain-cell").first()
|
|
await neuron.waitFor()
|
|
|
|
// The full-bleed canvas is its own shell, so a trip through it takes the
|
|
// sidebar shell down to nothing: coming back mounts the shell and the brain
|
|
// graph in the same commit, which is the order that used to pick an owner.
|
|
await page.goto(`/flows/${flowName}`)
|
|
const bar = page.locator('[data-sidebar="sidebar"]')
|
|
await bar.getByRole("link", { name: "Home", exact: true }).click()
|
|
await neuron.waitFor()
|
|
|
|
// And this is the navigation that used to end it: a sibling route in the
|
|
// same shell, which unmounts the graph but not the shell.
|
|
await bar.getByRole("link", { name: "Flows", exact: true }).click()
|
|
await page.waitForURL(/\/flows$/)
|
|
await bar.getByRole("link", { name: "Home", exact: true }).click()
|
|
await neuron.waitFor()
|
|
|
|
await expect
|
|
.poll(() =>
|
|
page.evaluate(() =>
|
|
(window as unknown as { __sockets: WebSocket[] }).__sockets.some(
|
|
(socket) => socket.readyState === 1,
|
|
),
|
|
),
|
|
)
|
|
.toBe(true)
|
|
})
|