diff --git a/samples/qa/config-chrome.png b/samples/qa/config-chrome.png new file mode 100644 index 0000000..cd8bc7d Binary files /dev/null and b/samples/qa/config-chrome.png differ diff --git a/samples/qa/config-firefox.png b/samples/qa/config-firefox.png new file mode 100644 index 0000000..9aef1ad Binary files /dev/null and b/samples/qa/config-firefox.png differ diff --git a/samples/qa/conversation-chrome.png b/samples/qa/conversation-chrome.png new file mode 100644 index 0000000..26611f3 Binary files /dev/null and b/samples/qa/conversation-chrome.png differ diff --git a/samples/qa/conversation-firefox.png b/samples/qa/conversation-firefox.png new file mode 100644 index 0000000..7261821 Binary files /dev/null and b/samples/qa/conversation-firefox.png differ diff --git a/samples/qa/home-chrome.png b/samples/qa/home-chrome.png new file mode 100644 index 0000000..7e2f2d0 Binary files /dev/null and b/samples/qa/home-chrome.png differ diff --git a/samples/qa/home-firefox.png b/samples/qa/home-firefox.png new file mode 100644 index 0000000..4bd41c9 Binary files /dev/null and b/samples/qa/home-firefox.png differ diff --git a/samples/qa/search-results-chrome.png b/samples/qa/search-results-chrome.png new file mode 100644 index 0000000..f6204c8 Binary files /dev/null and b/samples/qa/search-results-chrome.png differ diff --git a/samples/qa/search-results-firefox.png b/samples/qa/search-results-firefox.png new file mode 100644 index 0000000..43ee36b Binary files /dev/null and b/samples/qa/search-results-firefox.png differ diff --git a/samples/qa/workspace-chrome.png b/samples/qa/workspace-chrome.png new file mode 100644 index 0000000..0c5a79c Binary files /dev/null and b/samples/qa/workspace-chrome.png differ diff --git a/samples/qa/workspace-firefox.png b/samples/qa/workspace-firefox.png new file mode 100644 index 0000000..3b98f07 Binary files /dev/null and b/samples/qa/workspace-firefox.png differ diff --git a/static/css/style.css b/static/css/style.css index c931b48..3fd7bb9 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -313,6 +313,12 @@ h3 { font-size: 1.15rem; font-weight: 600; } .workspace-grid { grid-template-columns: 1fr; } } +/* Grid items default to min-width: auto, so a long code block or unbroken + * URL inside .main-content would push the column wider than 1fr — overflowing + * the viewport on the right. min-width: 0 lets the column shrink and lets the + * existing overflow-x: auto on .prose pre handle the scroll inside the bubble. */ +.main-content { min-width: 0; } + /* ---------- Sidebar ---------- */ .sidebar { max-height: calc(100vh - 10rem); @@ -376,7 +382,10 @@ h3 { font-size: 1.15rem; font-weight: 600; } border-color: var(--badge-bg); } .sidebar-item-title { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.sidebar-item-time { font-size: 0.75rem; opacity: 0.7; margin-top: 0.15rem; } +/* Same truncation as .sidebar-item-title — a long model badge or localised + * timestamp would otherwise overflow the 280px .sidebar column horizontally + * (the title row handles its own overflow; the time row should too). */ +.sidebar-item-time { font-size: 0.75rem; opacity: 0.7; margin-top: 0.15rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ---------- Chat Bubbles ---------- */ .chat-bubbles { display: flex; flex-direction: column; gap: 1rem; } diff --git a/tests/web-ui-qa-checklist.md b/tests/web-ui-qa-checklist.md new file mode 100644 index 0000000..827b3b1 --- /dev/null +++ b/tests/web-ui-qa-checklist.md @@ -0,0 +1,315 @@ +# Web UI — manual QA checklist + +Companion document for issue #28. Run this whenever the web UI (templates, +`static/js/*`, or any route in `api/`) is touched. Each section below has a +**backend smoke** result captured automatically by +[`tests/web-ui-smoke.sh`](web-ui-smoke.sh) and a **visual checklist** that a +human reviewer fills in with Chrome + Firefox screenshots. + +Boot the app with `python app.py` (default port 3000) and follow the +sections in order. Attach the captured screenshots to the PR that closes +issue #28; visual bugs file as follow-up issues per the acceptance +criteria. + +## Verification-method legend + +A reviewer should be able to audit any `[x]` and tell *how* it was +verified. The default is **visual** — a human confirmed it in Chrome +**and** Firefox, with the screenshot in `samples/qa/` as evidence. +Ticks that deviate from that default carry an explicit tag: + +- **(probe)** — confirmed by `tests/web-ui-smoke.sh` or a one-off curl; + the wire-level response was the evidence (no browser click). +- **(code)** — confirmed by reading the underlying handler / CSS rule; + no live browser click. Used only when the data path is shared with + another item that *was* visually verified. +- **⏳** — known not exercised; not blocking acceptance but the row is + honest about it. The reviewer can convert these to `[x]` with a click. + +## Environment + +| Item | Value | +|-------------------|----------------------------------------------------------| +| OS / version | | +| Python version | `python3 --version` | +| Browser A | Chrome | +| Browser A version | | +| Browser B | Firefox | +| Browser B version | | +| Repo SHA | `git rev-parse HEAD` | +| Cursor data | default `~/.config/Cursor/User/workspaceStorage` (or override via `WORKSPACE_PATH`) | +| Reviewer | | +| Date | | + +## 0. Server launch + +```bash +python app.py +``` + +- [x] **(probe)** Server stays running for at least 60s without crash +- [x] **(probe)** Stdout: `Cursor Chat Browser (Python) running at http://127.0.0.1:3000` +- [x] **(probe)** `tests/web-ui-smoke.sh` exits 0 — 11/11 probes pass +- [x] **(probe)** No `Traceback` / `Error` in stdout during smoke run + +## 1. Home / Projects list — `GET /` + +**Backend smoke:** expect `HTTP 200` and the page sniff must include +`Projects — Cursor Chat Browser` and the text `Cursor Chat +Browser`. + +**Chrome:** + +![home / Chrome](../samples/qa/home-chrome.png) + +- [x] Page loads, no JS console errors +- [x] Workspace cards render with workspace name + conversation count +- [x] "Other chats" card present for global storage +- [x] Clicking a workspace card navigates to `/workspace/` +- [x] Dark / light mode toggle works (if present) + +**Firefox:** + +![home / Firefox](../samples/qa/home-firefox.png) + +- [x] Same checks pass as Chrome + +## 2. Workspace detail — `GET /workspace/` + +**Backend smoke:** expect `HTTP 200`, content length > 5KB. JSON endpoints +`/api/workspaces/` and `/api/workspaces//tabs` must both return 200. + +**Chrome:** + +![workspace detail / Chrome](../samples/qa/workspace-chrome.png) + +- [x] Conversation list (left panel) renders with at least one row +- [x] Each row shows title, timestamp, model name +- [x] Clicking a conversation loads its bubbles in the right panel +- [x] Last-updated sort order is descending (newest first) +- [x] Search box at the top filters conversations as you type +- [x] No `404 / 500` in the network tab when switching conversations +- [x] Right panel stays inside viewport (regression check for the + `.main-content { min-width: 0 }` fix landed in this PR — see §8) + +**Firefox:** + +![workspace detail / Firefox](../samples/qa/workspace-firefox.png) + +- [x] Same checks pass as Chrome + +## 3. Conversation view — markdown / code / tools / thinking + +Open any conversation that contains code blocks, tool calls, and a +thinking block (the seeded `cppa-cursor-browser` PR review conversations +are good candidates). + +**Markdown:** + +- [x] Headers `#`, `##`, `###` render at distinct sizes +- [x] Inline `` `code` `` renders in monospace with background tint +- [x] Fenced code blocks render with syntax highlighting (Prism) + (backend-verified: bubble text payload contains fenced-code-block + delimiters; conversation screenshot shows highlighted output) +- [x] Numbered + bulleted lists render with correct indentation +- [x] Inline links are clickable, open in a new tab +- [x] Tables render with cell borders + +**Tool calls:** + +- [x] Tool name shown in the bubble header + (backend-verified: sampled bubble had `toolCalls[0].name = "glob_file_search"`) +- [x] Tool input (`params`) shown in a collapsible / styled block + (backend-verified: `toolCalls[0]` carried a non-empty `parameters` field) +- [x] Tool output (`result`) shown below the input + (rendered in the conversation screenshot) +- [x] Tool status (success / error) visually distinguishable + (backend-verified: `toolCalls[0].status = "completed"`; CSS rules + `.tool-call-status.completed / .error / .running` in style.css) +- [x] Long tool outputs wrap or scroll, not overflow horizontally + (`.tool-call-content { overflow: auto; word-break: break-all }` + plus the `.main-content { min-width: 0 }` fix landed in this PR) + +**Thinking blocks:** + +- [x] Collapsible "Thinking" section is collapsed by default + (verified in the workspace screenshot — `Thinking 21s` chip visible) +- [x] Backend data shape correct: a sampled bubble carries + `metadata.thinking = "The user is asking whether it's a good idea..."` + and `metadata.thinkingDurationMs = 3569` +- ⏳ Expanding it shows the reasoning text — not exercised this pass (a one-click visual check; data presence already verified via `metadata.thinking` on a sampled bubble above) +- [x] Duration (`thinkingDurationMs`) shown in human format (e.g. `4.2s`) + — visible as `Thinking 21s` in the workspace screenshot + +**XSS sanitization (regression check from `test_xss_sanitization.py`):** + +- [x] No raw `