Live UI proof screenshots for openclaw/openclaw#118217 (mock harness, synthetic data).
Created
August 2, 2026 22:18
-
-
Save steipete/b9922938687a045e3067940555401817 to your computer and use it in GitHub Desktop.
openclaw PR 118217 sidebar context-menu proof
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Live UI proof for openclaw PR #118217 — sidebar context menus + hideable catalogs. | |
| // Drives the repo mock harness (pnpm dev:ui:mock) with real Chromium via Playwright. | |
| import { chromium } from "playwright"; | |
| const BASE = process.env.PROOF_BASE ?? "http://127.0.0.1:5199"; | |
| const OUT = process.env.PROOF_OUT ?? "."; | |
| const results = []; | |
| const check = (name, ok, detail) => { | |
| results.push({ name, ok, detail }); | |
| console.log(`${ok ? "PASS" : "FAIL"} ${name}${detail ? ` — ${detail}` : ""}`); | |
| }; | |
| const browser = await chromium.launch(); | |
| const page = await browser.newPage({ viewport: { width: 1360, height: 900 } }); | |
| await page.goto(BASE, { waitUntil: "domcontentloaded", timeout: 120_000 }); | |
| await page.waitForSelector("aside.sidebar", { timeout: 120_000 }); | |
| // Catalog sections load through the idle-import boundary; wait for the header. | |
| await page.waitForSelector('[data-session-section="catalog:codex"]', { timeout: 60_000 }); | |
| // 1. Native context menu suppressed on plain sidebar chrome (footer). | |
| const footerPrevented = await page.$eval(".sidebar-shell__footer", (el) => { | |
| const ev = new MouseEvent("contextmenu", { bubbles: true, cancelable: true }); | |
| el.dispatchEvent(ev); | |
| return ev.defaultPrevented; | |
| }); | |
| check("native context menu suppressed on sidebar chrome", footerPrevented === true); | |
| // 2. Right-click on the agent card opens the agent menu (custom, not native). | |
| await page.click("openclaw-sidebar-agent-card", { button: "right" }); | |
| await page.waitForSelector(".sidebar-agent-menu[open]", { state: "attached", timeout: 10_000 }); | |
| check("agent card right-click opens agent menu", true); | |
| await page.waitForTimeout(500); | |
| await page.screenshot({ path: `${OUT}/1-agent-card-context-menu.png` }); | |
| await page.keyboard.press("Escape"); | |
| await page.waitForTimeout(300); | |
| // 3. Right-click on the Codex catalog header opens the catalog view menu with Hide. | |
| const codexHeader = page.locator( | |
| '[data-session-section="catalog:codex"] .sidebar-recent-sessions__head', | |
| ); | |
| await codexHeader.click({ button: "right" }); | |
| await page.waitForSelector(".sidebar-catalog-view-menu[open]", { state: "attached", timeout: 10_000 }); | |
| const hideItem = page.locator( | |
| '.sidebar-catalog-view-menu wa-dropdown-item[value="hide-catalog"]', | |
| ); | |
| check("catalog header right-click opens view menu with Hide item", (await hideItem.count()) === 1); | |
| await page.waitForTimeout(500); | |
| await page.screenshot({ path: `${OUT}/2-catalog-header-context-menu.png` }); | |
| // 4. Hide removes the Codex section and persists the preference. | |
| await hideItem.click({ force: true }); | |
| await page.waitForSelector('[data-session-section="catalog:codex"]', { | |
| state: "detached", | |
| timeout: 10_000, | |
| }); | |
| const stored = await page.evaluate(() => | |
| localStorage.getItem("openclaw:sidebar:sessions:hidden-catalogs"), | |
| ); | |
| check("Hide removes section and persists", stored === '["codex"]', `stored=${stored}`); | |
| await page.screenshot({ path: `${OUT}/3-codex-section-hidden.png` }); | |
| // 5. Settings → Appearance lists the hidden section with a Show control. | |
| await page.goto(`${BASE}/settings/appearance`, { waitUntil: "domcontentloaded" }); | |
| const heading = page.locator("h3", { hasText: "Hidden session sections" }); | |
| await heading.waitFor({ timeout: 60_000 }); | |
| const showRow = page.locator(".settings-row", { hasText: "codex" }); | |
| check("settings lists hidden section", (await showRow.count()) >= 1); | |
| await showRow.first().scrollIntoViewIfNeeded(); | |
| await page.screenshot({ path: `${OUT}/4-settings-hidden-sections.png` }); | |
| // 6. Show restores the section live (event-synced, no reload). | |
| await showRow.first().locator("button", { hasText: "Show" }).click({ force: true }); | |
| await page.waitForTimeout(300); | |
| const storedAfter = await page.evaluate(() => | |
| localStorage.getItem("openclaw:sidebar:sessions:hidden-catalogs"), | |
| ); | |
| check("Show clears preference", storedAfter === "[]", `stored=${storedAfter}`); | |
| // Settings replaces the app sidebar with its own workspace nav; return to the | |
| // app via SPA navigation (no reload) to observe the restored section. | |
| await page.getByText("Back to app").click(); | |
| await page.waitForSelector("aside.sidebar", { timeout: 30_000 }); | |
| await page.waitForSelector('[data-session-section="catalog:codex"]', { timeout: 30_000 }); | |
| check("Codex section restored in sidebar after SPA navigation, no reload", true); | |
| await page.screenshot({ path: `${OUT}/5-codex-section-restored.png` }); | |
| await browser.close(); | |
| const failed = results.filter((r) => !r.ok); | |
| console.log(JSON.stringify({ verdict: failed.length === 0 ? "PASS" : "FAIL", results }, null, 2)); | |
| process.exit(failed.length === 0 ? 0 : 1); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment




