Colour tile tests: the disc on the shared widgets panel for every look's screenshot, and a key's release sending rgb and hex
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WgBfkm4rCmtd1T4vHWjEPx
This commit is contained in:
@@ -12,7 +12,8 @@ import { api, apiPage, deleteAll } from "./utils/api"
|
||||
*
|
||||
* The arithmetic behind the wheel is checked in `color.check.ts`, which needs
|
||||
* no browser. What needs one is the gesture: how many messages a drag sends,
|
||||
* and whether the one it does send is where the pointer was let go.
|
||||
* and whether the one it does send is where the pointer was let go. And the
|
||||
* wiring: a tile set to rgb or hex reads and sends that form, not the wheel's.
|
||||
*/
|
||||
const flowName = `test_color_${Date.now().toString(36)}`
|
||||
const dashboard = `${flowName}_d`
|
||||
@@ -36,7 +37,11 @@ test.beforeAll(async ({ browser }) => {
|
||||
{
|
||||
id: "lamp",
|
||||
type: "python",
|
||||
provides: [{ name: "tint", dtype: "list", item: "float" }],
|
||||
provides: [
|
||||
{ name: "tint", dtype: "list", item: "float" },
|
||||
{ name: "rgb", dtype: "list", item: "int" },
|
||||
{ name: "hex", dtype: "str" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -48,6 +53,16 @@ test.beforeAll(async ({ browser }) => {
|
||||
method: "POST",
|
||||
data: { version: flow.definition.version },
|
||||
})
|
||||
// The same amber in either form, as a flow speaking it would set it.
|
||||
for (const [name, value] of [
|
||||
["rgb", [204, 150, 57]],
|
||||
["hex", "#cc9639"],
|
||||
] as const) {
|
||||
await api(page, `/messages/${flowName}.${name}`, {
|
||||
method: "POST",
|
||||
data: { value },
|
||||
})
|
||||
}
|
||||
|
||||
// A leftover from a run that failed before its teardown would answer the
|
||||
// create with a version conflict.
|
||||
@@ -66,6 +81,20 @@ test.beforeAll(async ({ browser }) => {
|
||||
layout: { lg: { x: 0, y: 0, w: 6, h: 4 } },
|
||||
config: { target, dtype: "list", format: "hsv" },
|
||||
},
|
||||
{
|
||||
id: "rgb",
|
||||
type: "color",
|
||||
title: "RGB",
|
||||
layout: { lg: { x: 6, y: 0, w: 3, h: 4 } },
|
||||
config: { target: `${flowName}.rgb`, dtype: "list", format: "rgb" },
|
||||
},
|
||||
{
|
||||
id: "hex",
|
||||
type: "color",
|
||||
title: "Hex",
|
||||
layout: { lg: { x: 9, y: 0, w: 3, h: 4 } },
|
||||
config: { target: `${flowName}.hex`, dtype: "str", format: "hex" },
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
@@ -87,10 +116,13 @@ test.afterAll(async ({ browser }) => {
|
||||
await deleteAll(browser, [`/dashboards/${dashboard}`, `/flows/${flowName}`])
|
||||
})
|
||||
|
||||
/** Open the panel, settled, and collect every publish the widget makes. */
|
||||
async function openBoard(page: Page) {
|
||||
/** Open the panel, settled, and collect every publish made to `name`. */
|
||||
async function openBoard<Value = [number, number, number]>(
|
||||
page: Page,
|
||||
name = target,
|
||||
) {
|
||||
await page.goto(`/view/${dashboard}`)
|
||||
await page.getByTestId("color-wheel").waitFor({ timeout: 20000 })
|
||||
await page.getByTestId("color-wheel").first().waitFor({ timeout: 20000 })
|
||||
// A tile fades and scales in, so where the disc is drawn is only settled
|
||||
// once that has finished — and the gestures below are aimed in pixels.
|
||||
await page.waitForFunction(
|
||||
@@ -104,11 +136,11 @@ async function openBoard(page: Page) {
|
||||
|
||||
// Every publish this panel makes, as the browser sends it. The setup above
|
||||
// talks to the API out of band, so nothing but the widget is counted here.
|
||||
const sent: [number, number, number][] = []
|
||||
const sent: Value[] = []
|
||||
page.on("request", (request) => {
|
||||
if (
|
||||
request.method() === "POST" &&
|
||||
request.url().includes(`/messages/${target}`)
|
||||
request.url().includes(`/messages/${name}`)
|
||||
)
|
||||
sent.push(request.postDataJSON()?.value)
|
||||
})
|
||||
@@ -117,7 +149,7 @@ async function openBoard(page: Page) {
|
||||
|
||||
test("a dragged disc publishes once, where it was let go", async ({ page }) => {
|
||||
const sent = await openBoard(page)
|
||||
const disc = page.getByTestId("color-wheel")
|
||||
const disc = page.getByRole("slider", { name: "Tint hue and saturation" })
|
||||
const box = (await disc.boundingBox())!
|
||||
const middle = { x: box.x + box.width / 2, y: box.y + box.height / 2 }
|
||||
|
||||
@@ -211,3 +243,34 @@ test("a dragged brightness column publishes where it was let go", async ({
|
||||
.toBe(1)
|
||||
expect(sent[0][2], "the release sent another brightness").toBe(letGo)
|
||||
})
|
||||
|
||||
for (const { title, sends } of [
|
||||
{ title: "RGB", sends: [204, 162, 57] },
|
||||
{ title: "Hex", sends: "#cca239" },
|
||||
]) {
|
||||
test(`a key's release publishes the colour as ${title}`, async ({ page }) => {
|
||||
const sent = await openBoard<unknown>(
|
||||
page,
|
||||
`${flowName}.${title.toLowerCase()}`,
|
||||
)
|
||||
const disc = page.getByRole("slider", {
|
||||
name: `${title} hue and saturation`,
|
||||
})
|
||||
// The amber the setup published, read back from the tile's own form.
|
||||
await expect(disc).toHaveAttribute(
|
||||
"aria-valuetext",
|
||||
"38 degrees, 72% saturated",
|
||||
)
|
||||
|
||||
// A key moves the hue five degrees on the way down and commits on the
|
||||
// way up, so one press is one message.
|
||||
await disc.press("ArrowRight")
|
||||
await expect
|
||||
.poll(() => sent.length, {
|
||||
timeout: 10000,
|
||||
message: "letting go of the key published nothing",
|
||||
})
|
||||
.toBe(1)
|
||||
expect(sent[0], "the key sent another colour").toEqual(sends)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ const lockedName = `${flowName}_locked`
|
||||
const w = (name: string) => `${flowName}.${name}`
|
||||
|
||||
/** Every tile the dashboard carries, including the deliberately unbound one. */
|
||||
const TILES = 11
|
||||
const TILES = 12
|
||||
|
||||
test.use({ storageState: "playwright/.auth/user.json" })
|
||||
|
||||
@@ -59,6 +59,7 @@ test.beforeAll(async ({ browser }) => {
|
||||
{ name: "lamp", dtype: "bool" },
|
||||
{ name: "setpoint", dtype: "float" },
|
||||
{ name: "idle", dtype: "float" },
|
||||
{ name: "tint", dtype: "list", item: "float" },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -78,6 +79,8 @@ test.beforeAll(async ({ browser }) => {
|
||||
await publish(page, w("condition"), "sun")
|
||||
await publish(page, w("setpoint"), 21.5)
|
||||
await publish(page, w("idle"), 0)
|
||||
// Amber, so the disc's handle and shade have something to show.
|
||||
await publish(page, w("tint"), [38, 72, 80])
|
||||
await publish(page, w("days"), [
|
||||
{ label: "Mon", icon: "sun", value: "21°" },
|
||||
{ label: "Tue", icon: "cloudy", value: "18°" },
|
||||
@@ -214,6 +217,13 @@ test.beforeAll(async ({ browser }) => {
|
||||
layout: { lg: { x: 6, y: 4, w: 6, h: 3 } },
|
||||
config: { series: [{ message: w("level"), dtype: "float" }] },
|
||||
},
|
||||
{
|
||||
id: "tint",
|
||||
type: "color",
|
||||
title: "Tint",
|
||||
layout: { lg: { x: 6, y: 7, w: 3, h: 3 } },
|
||||
config: { target: w("tint"), dtype: "list", format: "hsv" },
|
||||
},
|
||||
]
|
||||
const draft = await (
|
||||
await api(page, `/dashboards/${dashboardName}`, {
|
||||
|
||||
Reference in New Issue
Block a user