// The pointer: improvise-7ybz (a click resolves in Rust) and improvise-wsaw // (the sidebar obeys its flag). History: context/test-evidence/clients.md. import { test, expect } from "@playwright/test"; async function openApp(page) { await page.goto("/app/index.html"); await expect(page.locator("#grid table")).toBeVisible({ timeout: 20_000 }); await page.locator("#grid").focus(); } /// Open the category panel the way a user does, and leave the keyboard in /// Normal — so a later click has a focus change to make. async function openPanelThenLeave(page) { await page.keyboard.press("Shift+C"); await expect(page.locator("#categories")).toBeVisible(); await page.keyboard.press("Escape"); await expect(page.locator("#mode")).toHaveText("NORMAL"); await expect(page.locator("#categories")).toBeVisible(); } test.describe("the category sidebar obeys category_panel_open", () => { // improvise-wsaw. The flag existed from the start and app.js never read it, // so the sidebar was drawn always and `C` changed only the mode label. test("the sidebar is hidden until C, and C hides it again", async ({ page }) => { await openApp(page); await expect(page.locator("#categories")).toBeHidden(); await page.keyboard.press("Shift+C"); await expect(page.locator("#categories")).toBeVisible(); await expect(page.locator("#mode")).toHaveText("CATEGORIES"); await expect(page.locator("#categories li").first()).toBeVisible(); // The same key closes it, because `toggle-panel-and-focus` closes only // when the panel is already both open AND focused. await page.keyboard.press("Shift+C"); await expect(page.locator("#categories")).toBeHidden(); await expect(page.locator("#mode")).toHaveText("NORMAL"); }); }); test.describe("clicking the category panel", () => { // improvise-7ybz. One click means three things — focus the panel, move its // cursor, activate the row — and all three are resolved in Rust. test("a click focuses the panel, moves the cursor and expands the row", async ({ page, }) => { await openApp(page); await openPanelThenLeave(page); const product = page.locator("#categories li").filter({ hasText: "Product" }); await expect(product).toHaveText(/▶ Product \(4\)/); await expect(page.locator("#categories li.selected")).toHaveText(/_Index/); await product.click(); // All three, and the third is what the user reported as impossible. await expect(page.locator("#mode")).toHaveText("CATEGORIES"); await expect(page.locator("#categories li.selected")).toHaveText(/Product/); await expect(product).toHaveText(/▼ Product \(4\)/); await expect(page.locator("#categories li.item")).toHaveText([ "Espresso", "Latte", "Cold Brew", "Pastry", ]); // Clicking it again collapses: the click mirrors Enter, which is a toggle. await product.click(); await expect(page.locator("#categories li.item")).toHaveCount(0); }); // The other arm of `filter-to-item`: on an ITEM row the same click filters, // because the rule is "do what Enter does here" rather than "expand". test("a click on an item row filters, as Enter does", async ({ page }) => { await openApp(page); await openPanelThenLeave(page); await page.locator("#categories li").filter({ hasText: "Product" }).click(); await page.locator("#categories li.item").filter({ hasText: "Latte" }).click(); await expect(page.locator("#status")).toContainText("Filter: Product = Latte"); await expect(page.locator("#categories li.selected")).toHaveText("Latte"); }); }); test.describe("clicking a tile", () => { // The tile bar is keyboard-reachable only through `T` plus arrows; a click // is the whole interaction, so it selects AND cycles. test("a click selects the tile and cycles its axis", async ({ page }) => { await openApp(page); const product = page.locator("#tiles .tile").filter({ hasText: "Product" }); await expect(product).toHaveText("Product Row"); await product.click(); await expect(page.locator("#mode")).toHaveText("TILES"); await expect(product).toHaveClass(/selected/); // Row → Column is `Axis::next`; the page does not spell the rotation. await expect(product).toHaveText("Product Col", { timeout: 10_000 }); // And the grid moved with it, not just the label. await expect( page.locator("#grid thead th").filter({ hasText: "Espresso" }).first(), ).toBeVisible(); }); }); test.describe("clicking a grid cell", () => { // Before improvise-7ybz a click reached only `edit()`, so the selection — // which every keyboard command reads — stayed wherever it had been. test("a click moves the selection, not only the editor", async ({ page }) => { await openApp(page); const cells = page.locator("#grid tbody td"); // The cursor starts at the origin, so any other cell is a real move. await expect(cells.nth(0)).toHaveClass(/selected/); await cells.nth(4).click(); // demo.improv is all Records, so the click also opened an editor; close it // without committing and the selection must have followed the pointer. await page.keyboard.press("Escape"); await expect(cells.nth(4)).toHaveClass(/selected/); await expect(page.locator("#grid tbody td.selected")).toHaveCount(1); }); // The selection a click leaves is the one the keyboard then moves from — // the property that makes the two input paths one cursor rather than two. test("the keyboard continues from where the pointer left the cursor", async ({ page, }) => { await openApp(page); const cells = page.locator("#grid tbody td"); await cells.nth(4).click(); await page.keyboard.press("Escape"); await expect(cells.nth(4)).toHaveClass(/selected/); await page.keyboard.press("ArrowLeft"); await expect(cells.nth(3)).toHaveClass(/selected/); }); });