Files
claude-desktop-debian/docs/testing/cases/code-tab-foundations.md
Aaddrick 3506c14918 test(harness): add Linux compatibility test harness (#579)
Build out a Playwright-based regression-detection harness covering
the compat-matrix surfaces (KDE-W, KDE-X, GNOME, Sway, i3, Niri,
packaging formats). Adds:

- Planning + decision docs under docs/testing/ — README, matrix,
  runbook, automation, cases/ (11 case files), quick-entry-closeout
- Playwright scaffolding (config, tsconfig)
- 78 spec runners under tools/test-harness/src/runners/ — T## case-
  doc runners and S## distribution/smoke runners
- Substrate primitives in tools/test-harness/src/lib/: AX-tree
  loader (snapshotAx + waitForAxNode + axTreeToSnapshot), focus-
  shifter, eipc-registry, niri-native bridge, drag-drop bridge,
  electron-mocks, claudeai page-objects, inspector client

S03 (DEB Depends declared) and S04 (RPM Requires declared) ship
marked test.fail() — they're regression detectors for the case-doc
gap (deb.sh emits no Depends:, rpm.sh sets AutoReqProv: no), and
the expected-failure shape lets them report green on every host
until upstream packaging starts declaring runtime deps.

127 files, no runtime changes; harness is opt-in via
'cd tools/test-harness && npx playwright test'.

Co-authored-by: Claude <claude@anthropic.com>
2026-05-04 23:17:37 -04:00

198 lines
9.9 KiB
Markdown

# Code Tab — Foundations
Tests covering Code-tab availability on Linux (officially unsupported per upstream docs), sign-in flow, folder picker, drag-and-drop, and the basic editing surfaces (terminal, file pane). See [`../matrix.md`](../matrix.md) for status.
## T15 — Sign-in completes in the embedded webview
> **Drift in build 1.5354.0** — Sign-in is an in-app `mainView.webContents.loadURL` flow, not an `xdg-open` browser handoff. Claude.ai/login renders inside the embedded BrowserView; the resulting `sessionKey` cookie is then exchanged at `${apiHost}/v1/oauth/${org}/authorize` with redirect URI `https://claude.ai/desktop/callback`. No system browser is involved.
**Severity:** Smoke
**Surface:** Auth / embedded webview
**Applies to:** All rows
**Issues:**
**Steps:**
1. Launch a fresh app instance (signed-out state).
2. Click **Sign in**. Observe claude.ai/login rendering inside the app.
3. Authenticate. Observe the in-app navigation completing back to the
workspace.
**Expected:** Sign-in stays inside the embedded webview (`will-navigate`
handler `Ihr` keeps `/login/` paths in-app). After auth the
`sessionKey` cookie is captured and silently exchanged for an OAuth
token via the `desktop/callback` redirect. Account dropdown populates;
no auth banner remains.
**Diagnostics on failure:** DevTools console for the `mainView`
BrowserView, network captures of the `/v1/oauth/{org}/authorize` and
`/v1/oauth/token` calls, launcher log, cookie jar inspection
(`sessionKey` on `.claude.ai`).
**References:** [Code tab auth troubleshooting](https://code.claude.com/docs/en/desktop#403-or-authentication-errors-in-the-code-tab)
**Code anchors:**
- `build-reference/app-extracted/.vite/build/index.js:141996` — desktop
OAuth redirect URI `https://claude.ai/desktop/callback`
- `build-reference/app-extracted/.vite/build/index.js:142431` — POST to
`${apiHost}/v1/oauth/${org}/authorize` with `Bearer ${sessionKey}`
- `build-reference/app-extracted/.vite/build/index.js:216565``Ihr`
treats `/login/` paths as in-app (not external)
- `build-reference/app-extracted/.vite/build/index.js:141316`
`mainView.webContents.loadURL(...)` drives the embedded sign-in
## T16 — Code tab loads
**Severity:** Smoke
**Surface:** Code tab — top-level UI
**Applies to:** All rows
**Issues:**
**Steps:**
1. After sign-in, click the **Code** tab at the top center.
2. Wait a few seconds.
**Expected:** Code tab renders the session UI (sidebar, prompt area, environment dropdown). Per upstream docs the Code tab is "not supported" on Linux — the patched build under this project should render the UI normally or surface a clear, actionable message. Not a blank screen, infinite spinner, or `Error 403: Forbidden`.
**Diagnostics on failure:** Screenshot, DevTools console, network captures (auth/feature-flag responses), launcher log, the active patch set in `scripts/patches/`.
**References:** [Use Claude Code Desktop](https://code.claude.com/docs/en/desktop), [Get started with the desktop app](https://code.claude.com/docs/en/desktop-quickstart)
**Code anchors:**
- `build-reference/app-extracted/.vite/build/index.js:525066`
`sidebarMode === "code"` rewrites the BrowserView path to `/epitaxy`
- `build-reference/app-extracted/.vite/build/index.js:496066` — Code
deeplinks (`claude://code?...`) navigate to `/epitaxy?...`
- `build-reference/app-extracted/.vite/build/index.js:105273``IHi`
recognises `/epitaxy` and `/epitaxy/...` as the Code-tab path
- `build-reference/app-extracted/.vite/build/index.js:105346`
`sidebarMode` enum contains `"code"`
**Inventory anchor:** `…tablist.tab-by-name.code` (role `tab`, label
`Code`) — confirms the Code tab is reachable from the new-chat tablist
in the captured idle state.
## T17 — Folder picker opens
**Severity:** Smoke
**Surface:** Code tab → Environment selection
**Applies to:** All rows
**Issues:**
**Runner:** [`tools/test-harness/src/runners/T17_folder_picker.spec.ts`](../../../tools/test-harness/src/runners/T17_folder_picker.spec.ts) — runtime-attach via SIGUSR1 + main-process `dialog.showOpenDialog` mock + `webContents.executeJavaScript` to drive the renderer. Click chain to reach the folder-picker button awaits selector tuning
**Steps:**
1. In the Code tab, click the environment pill → **Local****Select folder**.
2. Choose a project directory.
**Expected:** Native file chooser opens. On Wayland sessions the chooser is `xdg-desktop-portal`-backed (verify with `busctl --user tree org.freedesktop.portal.Desktop`). On X11 sessions the GTK/Qt native picker fires. Selected path appears in the env pill.
**Diagnostics on failure:** `systemctl --user status xdg-desktop-portal`, `XDG_SESSION_TYPE`, the portal backend in use (`xdg-desktop-portal-kde`, `xdg-desktop-portal-gnome`, `xdg-desktop-portal-wlr`), launcher log.
**References:** [Local sessions](https://code.claude.com/docs/en/desktop#local-sessions)
**Code anchors:**
- `build-reference/app-extracted/.vite/build/index.js:66403` — IPC
channel `claude.web_FileSystem_browseFolder` (renderer → main)
- `build-reference/app-extracted/.vite/build/index.js:509188`
`browseFolder` impl calls `dialog.showOpenDialog` with
`properties: ["openDirectory", "createDirectory"]`
- `build-reference/app-extracted/.vite/build/index.js:450534`
`grantViaPicker` (Operon host-access folder grant) uses the same
`["openDirectory"]` shape
- `tools/test-harness/src/lib/claudeai.ts:122``installOpenDialogMock`
intercepts both `(opts)` and `(window, opts)` arities, matching the
call sites at index.js:509196 and :450534
**Inventory anchor:** `root.main.region.button-by-name.select-folder`
(role `button`, label `Select folder…`) — the persistent button the
T17 runner clicks before the dialog mock fires.
## T18 — Drag-and-drop files into prompt
**Severity:** Critical
**Surface:** Code tab → Prompt area
**Applies to:** All rows
**Issues:**
**Steps:**
1. Open a Code-tab session.
2. From the system file manager, drag one or more files into the prompt area.
3. Repeat with multiple files at once.
**Expected:** Files attach to the prompt. The renderer resolves dropped
`File` objects to absolute paths via the preload-bridged
`claudeAppSettings.filePickers.getPathForFile` (Electron's
`webUtils.getPathForFile`). Multi-file drops attach each file. Works on
both Wayland and X11.
**Diagnostics on failure:** Screen recording, `wl-paste --list-types` (Wayland) or `xclip -selection clipboard -t TARGETS -o` (X11) during drag, DevTools console, launcher log.
**References:** [Add files and context](https://code.claude.com/docs/en/desktop#add-files-and-context-to-prompts)
**Code anchors:**
- `build-reference/app-extracted/.vite/build/mainView.js:9267`
`filePickers.getPathForFile` wraps `webUtils.getPathForFile`
- `build-reference/app-extracted/.vite/build/mainView.js:9552`
exposed to the renderer as `window.claudeAppSettings`
## T19 — Integrated terminal
**Severity:** Critical
**Surface:** Code tab → Terminal pane
**Applies to:** All rows
**Issues:**
**Steps:**
1. In a Code-tab session, press `` Ctrl+` `` (or open via the Views menu).
2. Confirm the terminal opens in the session's working directory.
3. Run `git status`, `npm --version`, `gh auth status`.
**Expected:** Terminal pane opens in the session's working directory, inherits the same `PATH` Claude sees. Standard commands run cleanly. Terminal pane is local-session-only per docs.
**Diagnostics on failure:** Terminal pane content, `echo $PATH` from inside the pane, `pwd`, the shell binary in use, launcher log.
**References:** [Run commands in the terminal](https://code.claude.com/docs/en/desktop#run-commands-in-the-terminal)
**Code anchors:**
- `build-reference/app-extracted/.vite/build/index.js:69135` — IPC
channel `claude.web_LocalSessions_startShellPty` (also
`resizeShellPty`, `writeShellPty` at :69184, :69210)
- `build-reference/app-extracted/.vite/build/index.js:486438`
`startShellPty` body: spawns `node-pty` in
`n.worktreePath ?? n.cwd` with `TERM=xterm-256color`
- `build-reference/app-extracted/.vite/build/index.js:486463`
`node-pty` dynamic import (optional dep, `package.json` line 100)
- `build-reference/app-extracted/.vite/build/index.js:259306`
`shell-path-worker/shellPathWorker.js` resolves the user's interactive
PATH; `FX()` (line 259311) returns it for the spawned PTY env
## T20 — File pane opens and saves
**Severity:** Critical
**Surface:** Code tab → File pane
**Applies to:** All rows
**Issues:**
**Steps:**
1. In a Code-tab session, click a file path in chat or diff to open it in the file pane.
2. Make a small edit. Click **Save**.
3. Modify the file externally (e.g. `echo >> file`). Re-edit in the pane. Observe the on-disk-changed warning.
**Expected:** File opens in the editor pane. Edits write back to disk on Save. If the file changed on disk since opening, the pane shows the on-disk-changed warning and offers override or discard. (The conflict check is sha256-based, not mtime-based — `writeSessionFile` reads the current bytes, hashes them, and rejects with `Conflict` if the renderer-supplied `expectedHash` doesn't match.)
**Diagnostics on failure:** `sha256sum <file>` output (and stat mtime for cross-checking), launcher log, DevTools console, screen recording of the warning state.
**References:** [Open and edit files](https://code.claude.com/docs/en/desktop#open-and-edit-files)
**Code anchors:**
- `build-reference/app-extracted/.vite/build/index.js:68922` — IPC
channel `claude.web_LocalSessions_readSessionFile`
- `build-reference/app-extracted/.vite/build/index.js:69003` — IPC
channel `claude.web_LocalSessions_writeSessionFile` with
`expectedHash` argument at position 3
- `build-reference/app-extracted/.vite/build/index.js:492874`
`readSessionFile` impl
- `build-reference/app-extracted/.vite/build/index.js:492954`
`writeSessionFile` impl: sha256-hashes current on-disk bytes,
returns `{ status: nW.Conflict, currentHash }` when `expectedHash`
mismatches