Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/e2e-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ until `make e2e` is green and this file reflects it.
| ✅ Activity monitor | The sidebar footer button opens a SECOND window (its own `activity.html` entry, found by polling the WebDriver handles: a new webview is listed before its document loads); a live agent appears under its project and task; Termic's own processes get their own group and do NOT double-count the agents' subtrees (every PTY is our child, so the app row's stop-set is the invariant); a row reports a real CPU percentage even though the harness window is permanently `document.hidden`, which is the occluded-window back-off working; Pause halts sampling and resume restarts it; re-opening focuses the existing window instead of spawning a second; closing the window drops the sampling session and a fresh one is grantable; every column header sorts (default CPU desc, a new column starts biggest-first except Name which starts A-to-Z, the previous column lets go) and the PID column renders real pids. Grouping / sorting / formatting are unit-tested in `src/lib/activityGroups.test.ts`, the sampler math + FFI in `procmon.rs` tests. On Windows the same spec runs against `procmon_windows.rs`: the stop case ends a `cmd.exe` and its child together, and the Termic row must either hold WebView2's processes or say it cannot attribute them; suspend / resume and the stale-parent-pid rule are Rust tests there | `activity.e2e.ts` |

| ✅ Start from an issue (GH #21/#22) | The fixture's local bare remote resolves as NOT a forge, so `project_forge_issues` reports `unsupported-remote` rather than an empty list, and `buildIssuePrompt` composes context + deferred thread + library instructions; the route in is exercised end to end (palette row → shared project picker flagged `issue` → the SAME New Task dialog with its issue column open beside the form → the non-forge explanation → "blank task instead" drops the column and keeps the dialog). Picking a real issue needs a real forge, so the prompt auto-fill it triggers is unit-tested in `src/lib/issuePrompt.test.ts`. | `git.e2e.ts` |
| ✅ PR picker: waiting for your review | With a forge seeded for the fixture project (after the dialog's own `resolveProvider` has answered, which would otherwise overwrite it), "From a PR" opens the PR column with its intro, the fixture's non-forge remote is reported as a sentence with no `pr-row`, and no `pr-review-section` draws for a non-`ok` lookup; switching back to a new branch closes the column. The `gh` query (`review-requested:@me`, open, the shared JSON fields) and the GitHub-only empty answer for GitLab/Azure are pinned in `forge.rs` tests. A populated review list needs a real GitHub repo and is a manual check | `git.e2e.ts`, `forge.rs` |
| ✅ PR picker: For you, waiting for your review, All open | With a forge seeded for the fixture project (after the dialog's own `resolveProvider` has answered, which would otherwise overwrite it), "From a PR" opens the PR column with its intro, the fixture's non-forge remote is reported as a sentence with no `pr-row`, and neither `pr-review-section` nor the `pr-scope` chips draw for a non-`ok` lookup; switching back to a new branch closes the column. The `gh` query (`review-requested:@me`, open, the shared JSON fields), the GitLab REST paths (`scope=created_by_me` / `scope=reviews_for_me`), the GitLab MR parser (iid, fork from differing project ids, `draft` or the older `work_in_progress`) and Azure's empty answer are pinned in `forge.rs` tests. A populated list, and switching between For you and All open, need a real GitHub or GitLab repo and are a manual check | `git.e2e.ts`, `forge.rs` |
| ✅ Issue picker: scope chips | With GitHub seeded for the fixture project, the issue column offers All / Assigned to you / Mentions you, starting on All; each click presses exactly that chip and refetches, and on the non-forge remote each scope still reports the remote as a sentence rather than an empty "nothing assigned to you"; leaving and re-entering the issue source starts back on All. The `gh` query per scope, the parse default and the GitHub-only empty answer are pinned in `forge.rs` tests. Real assigned/mentioned results need a GitHub repo and are a manual check | `git.e2e.ts`, `forge.rs` |
| ✅ Profiles (GH #280) | The sidebar has no background image by default and takes a gradient only while `profileSidebarWash` is on (computed style). The dormant contract first: no strip renders until a profile exists, and the footer's profile button is the whole surface. Creating the first profile ADOPTS the install that already exists (both entries, one write) and the existing one owns the root data dir, so nothing moves; the strip then carries the name in clear and the popover lists every profile. Isolation is asserted through the app's own IPC rather than by driving two windows (the suite reuses one window per file): the seeded `fixture-repo` belongs to the root profile and the new one starts empty, seeded under `profiles/<slug>/tasks`. A rename never moves the slug. `profile_open` really does create a second WebDriver window. Delete is REFUSED while the profile's window is open, which is why backing out of the feature needs its own door: `profiles_disable` returns the app to its pre-profiles shape keeping every byte of data, and is itself refused while more than one profile exists. Two REAL windows are open at once and asserted disjoint (the second knows its own slug, renders its own name, and sees an empty project list while the first still holds `fixture-repo`). The delete dialog is driven for real, and asserts the PAINTED border colour moves with the selection, not just `aria-checked`: `transition-colors` froze it in WKWebView (docs/gotchas.md) while every attribute assertion stayed green. `profile_close` is the door the spec uses to hand the suite back its one window, and it refuses to close the window it is called from. Launch restore is compiled out under `feature = "e2e"`, since the suite asserts on window-handle counts | `profiles.e2e.ts` |
| ✅ Config sync (Settings > Sync) | A folder another machine adds (name, accent, one project) becomes a profile here on Sync now, on an install with no profiles: the report says so, the profile carries the folder's accent, the title-bar chip appears without a reload, and this window's own folder is untouched; deleting that profile here survives the next sync while the folder stays in the repo, Settings lists it under Profiles not on this machine, and Create here brings it back. Who is created, uploaded or left waiting (same name, ignored, opted out), slug-named folders, rename propagation and the opt-out are the two-machine Rust test `profiles_follow_the_repo_both_ways` and `plan_adoption`'s own. Against a local bare repo over `file://`, with the spec playing the other machine by cloning it, committing and pushing. The never-synced note shows before anything is connected. A URL git would run as a command (`ext::`) or read as an option (`--upload-pack=`) is refused before anything is cloned, and the page names the forms it takes. Connecting to an EMPTY repo pushes at once: the remote then holds README, prefs.json and the profile's settings, agent and project files, the commit reads `sync from <machine>`, and neither an agent's env value nor a local field (`root_path`, `repos_dir`, `sandbox_allowed_paths`) is anywhere in it. Sync now pulls and applies a field the other machine changed. A pulled YOLO default shows as a persistent notice and a toast, each naming the change ("changed from Default to On"), while the report only counts it, and Got it clears the notice. A project with no folder here, and a plain-folder one, wait with Locate and Skip; Skip survives another pull and Show again undoes it; the report gives them one count ("2 projects wait for a folder") and does not count them as applied. Disconnect, then connect to the repo that now holds a setup: the folder picker preselects it, Preview lists the YOLO change highlighted and the waiting projects, Cancel applies nothing, Apply does. Repo and Folder values are measured to share a left edge. Regaining window focus pulls when the last pull is more than five minutes old: the spec backdates `sync-state.json`, flips window focus, and the other machine's preview URL lands without Sync now; a second `sync_focus_pull` in that window is skipped. Never touches localStorage or the themes folder: the other machine edits project files only, because the e2e binary shares localStorage with an installed Termic | `sync.e2e.ts` |
Expand Down
58 changes: 43 additions & 15 deletions docs/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -1152,24 +1152,51 @@ the sandbox config get.
It was inline above the form first. That put a 220px scrolling list inside a
dialog you were already scrolling, and hid the effect of a pick below the fold.

### The PR picker lists two things, both scoped to you

"From a PR" shows your own open PRs and, under them, the open PRs waiting
on your review (`gh pr list --search review-requested:@me`, so a request to
a team you are on counts, as on GitHub's own Review requests tab). Still not
a repo-wide browser: both lists are about you, so they stay short on a busy
repo, and anything else is reached by pasting its number.

The review list is a second `project_forge_prs` call (`review: true`) fired
### The PR picker opens on you, and the repo's list is one click away

"From a PR" opens on **For you**: your own open PRs and, under them, the
open PRs waiting on your review (`gh pr list --search review-requested:@me`,
so a request to a team you are on counts, as on GitHub's own Review
requests tab). Both lists are about you, so they stay short on a busy repo.

**All open** is the repo's open PRs, newest first, one page of 50. It is a
chip, not the default, because on a repo with 1,300 open PRs the newest 50
are rarely the one you came for, while on a small team repo they are
exactly it. Three things keep it cheap for the first case:

- It is fetched on the click, never on entry. Nobody who stays on For you
pays for it.
- It is capped. A full page says so under the list ("Showing the newest
50") and points at the number box, which is the path for anything older.
- It is the forge's own default order (`gh pr list` with no filter, GitLab
`order_by=created_at`, Azure the CLI's own order, not verified).
`--search sort:updated-desc` was measured on a 1,300-PR repo and does not
come back in updated order, so "most active first" is not on offer.

The chips draw only for an `ok` lookup of your own list: a forge that
cannot be reached is already explained there, and All open would fail the
same way. The column starts back on For you every time it is entered, and
a slow All open answer is dropped by a sequence number if you clicked back.

On GitLab every list is one REST call through `glab api`
(`merge_requests?scope=created_by_me`, `scope=reviews_for_me` for the MRs
where you are a reviewer, and `scope=all` for All open), and a pasted
number is `merge_requests/<iid>`.
The REST API rather than `glab mr list` because the CLI's JSON has moved
between versions, the same reason the issue list uses it. A server that
does not accept a `scope` value answers 400; for `reviews_for_me` that
reads as an empty review list, so the section hides instead of erroring
under your own MRs.

The review list is a second `project_forge_prs` call (`scope: "review"`) fired
beside the first, never before it. It renders BELOW your own PRs, because it
lands on its own clock and a list that arrives late must not push down a row
you are about to click. It draws only for an `ok` status with rows: a forge
that cannot be reached is already explained by the first list, and saying it
twice is noise. GitHub only; GitLab has no picker list at all yet, and
Azure's reviewer filter wants an identity id rather than `@me`, so both
answer an empty list and the section stays hidden.
twice is noise. Azure's reviewer filter wants an identity id rather than
`@me`, so it answers an empty list and the section stays hidden there.

Picking a PR from either list is the same pick: fetch the head, then check
Picking a PR from any list is the same pick: fetch the head, then check
it out. Firing a review at it is the prompt library's Review entry, by hand.

The issue column has the matching half as chips over the list: **All**,
Expand All @@ -1179,8 +1206,9 @@ flag). Chips, not extra lists: the repo's list is long, so a list appended
under it would sit below the fold, and a chip only changes the list when
you click it. Each click refetches with a sequence number so a slow answer
for a scope you already left cannot overwrite the one you picked. The
column starts back on All every time it is entered. GitHub only, for the
same reason as the review list.
column starts back on All every time it is entered. GitHub only. GitLab's
issues API can say "assigned to me" but has no mention filter, so wiring it
means a per-forge chip set rather than the same three chips.

### The new-task dialog has TWO columns and TWO widths

Expand Down
3 changes: 3 additions & 0 deletions e2e/specs/git.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2799,6 +2799,9 @@ describe("the forge pickers: work that pulled you in", () => {
// does not matter: on this remote it can only answer a non-"ok" status,
// and the section draws for "ok" with rows and nothing else.
expect(await browser.execute(() => !!document.querySelector('[data-testid="pr-review-section"]'))).toBe(false);
// No For you / All open chips either: the sentence already says why the
// forge cannot be reached, and All open would only fail the same way.
expect(await browser.execute(() => !!document.querySelector('[data-testid="pr-scope"]'))).toBe(false);
await snap("pr-pane-no-forge.png");
});

Expand Down
Loading
Loading