Skip to content

Name the controls the journeys and the screenshots share in one helper - #566

Merged
zaerl merged 2 commits into
trunkfrom
update/shared-journey-helpers
Sep 30, 2026
Merged

zaerl merged 2 commits into
trunkfrom
update/shared-journey-helpers

Conversation

@zaerl

@zaerl zaerl commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Why

The journeys and the documentation screenshots find the app's controls by the words and roles on screen, and each file wrote those out itself: the ticket field in ten files, "Link ticket" in ten, the site menu in five. The redesign in #542 renames controls and rebuilds the parts of the screen they sit in, and today each of those is an edit to every file that mentions it. This puts the names in one place before the screen starts to move.

What changes

tests/e2e/helpers/ui.cjs is new. It names what is on screen for the journeys, the other end-to-end specs and scripts/screenshots/ alike, and they all read it.

It holds:

  • Every control that more than one of those files reaches, such as the ticket field, "Link ticket", "Unlink", the site menu and its items, the pull request field, "Apply PR", "Apply and rebuild" and the two "Revert" buttons.
  • Four locators that find a part of the screen by how it is built rather than by what it says: the rendered app, a sidebar entry, a card and a ticket's row. The screenshots' card() is one of these, moved unchanged.
  • Two things the journeys had copied between themselves: the link-a-ticket steps, in five local functions and four inline sequences, and the document-order check, twice.

It does not hold a sentence one journey asserts, a button only one journey presses, or a selector only one file needs. About 120 locators stay in their journeys for that reason: they are what the journey is about. TESTING.md states the rule, and the review standard points to it.

Deliberately not in this PR: any change to what the app renders, and any new assertion. The screenshots' own crops (.patch-diff, the parent hops) stay in shots.cjs, since nothing else uses them.

How to test this

Platforms: any. There is no user-visible surface: this is test and screenshot code only.

From the repository root:

  1. npm run lint is clean.
  2. npm run test:e2e passes, the same 33 journeys as on trunk.
  3. npm run build:once && CSC_IDENTITY_AUTO_DISCOVERY=false npm run pack:dir, then npm run test:e2e:packaged, passes.
  4. npm run shots captures all twelve fixture shots. Discard them afterwards with git checkout -- docs/public/screenshots.

What must not have happened:

  • No journey has lost a step or an assertion. The diff of each spec is locator for locator; the review below went through every replacement.
  • No assertion that something is absent passes because its locator stopped matching. Every helper has at least one use that clicks, fills or waits for the thing it names, with one exception by design: anyPatchFileButton exists only to assert that no such button is there, and the button it would find is the one choosePatchFileButton clicks.

Risks and limitations

  • The screenshots match five names more strictly. "Create a site", "More", "Update to latest trunk", "Start dev server" and "Retry install & build" were matched by part of their name there and are matched exactly now, as in the journeys. A stricter match can make a shot fail loudly; it cannot photograph the wrong element.
  • linkTicket reports a timeout differently. The inline version ended in expect(…).toBeVisible({ timeout: 30_000 }); the shared one ends in waitFor({ timeout: 30_000 }), so the helper needs no expect and loads with playwright-core alone. Same condition, same limit, and it still fails the test, as a timeout rather than as an assertion.
  • The real-setup spec was not run. It needs the network and tens of minutes. Three of its lines changed, each to a helper the journeys or the screenshots exercise.
  • Windows is not verified locally. Nothing in the helper depends on the platform, and CI's Windows journeys are the evidence.
  • sidebarEntry finds only a site with nothing to report. A stale site, one with an incomplete update and one being deleted have longer names. That was true of the locator before it was shared; its comment now says so.
  • Review: 2 [fix here] and 1 [follow-up], all fixed.

Related

Closes #551

Part of #542. Follows #565.


Design decisions and alternatives considered

Locators, not strings. A file of label constants would leave the role and the match in every caller, and the redesign changes those too: a menu item can become a dialog button. A function that returns the locator takes all three with it.

Not every label. There are 152 distinct locators across these files and about 30 are used in more than one. Moving the rest would turn each journey into a list of names defined elsewhere, and TESTING.md's audit depends on reading a journey as a description of what it does. A one-off is already a one-file change.

No .first(), no waiting, no expect in the helper. Each locator is what the caller would have written, and the caller adds what it needs where a reader can see it. linkTicket is the one function that waits, because the wait is the flow.

The helper lives under tests/e2e/helpers/, and scripts/screenshots/shots.cjs requires it from there. The journeys are its main user, and TESTING.md already describes that directory.

card() still reads the markup's shape. Giving the cards a role and a name would make it a real landmark, and that is a change to the app that belongs with the redesigned cards.

Review outcome (required — see AGENTS.md)

2 [fix here] · 1 [follow-up] — all 3 fixed.

  • Tests, low, [fix here], fixed: five clicks on the patch-file button matched its whole name before the move and part of it after, and nothing else states that name. They match exactly again; the two assertions that the button is absent keep the looser match, as anyPatchFileButton.
  • Tests, low, [fix here], fixed: the rule for what belongs in the helper said more than the helper held, and was worded three ways. "Continue working on #N" and the ticket row moved in; the rule names the four structural locators and is stated once, in TESTING.md.
  • Tests, low, [follow-up], fixed here: sidebarEntry's comment did not say which sites it cannot find.

The review compared every replaced locator with what the helper returns and found the rest identical, with the same .first(), .last() and timeouts, and no step dropped or reordered.

  • Review: completed — fresh agent context, local, before the PR existed, read-only; head 03a7ea2 / base f9a7980; committed work only, working tree clean; outcome above. CodeRabbit: not run yet.
  • Since review: 03a7ea2 → 00261a2 checked. The difference is the three fixes. It was not reviewed again by a fresh context; it was verified by the runs below.

Checks on 00261a2, macOS, local:

Check Result
npm run lint clean
npm test 1670 pass, 2 skipped, 0 fail
npm run test:electron 1672 pass, 0 fail
npm run test:e2e 33 of 33 pass
npm run test:e2e:packaged 15 of 15 pass
npm run shots, fixture tier 12 of 12 captured, pixel-identical to the capture on 03a7ea2

Against trunk, eleven of the twelve shots are pixel-identical. The twelfth, stale-site-notice, reads "121 days old" where it read "120": the fixture's date is fixed, and a day boundary passed between the two captures (#326).

One mutation, to show the helper is what the journeys read: naming "Unlink" wrongly in ui.cjs fails the journeys that press it.

🤖 Generated with Claude Code

zaerl and others added 2 commits September 30, 2026 14:29
Fourth step of the redesign's groundwork (#542, #551). The journeys and
the documentation screenshots find the app's controls by the words and
roles on screen, and each file wrote those out itself: the ticket field
in ten files, "Link ticket" in ten, the site menu in five. A renamed
button, or a menu rebuilt as a dialog, was an edit to every one of them.

tests/e2e/helpers/ui.cjs holds them now: the controls more than one file
reaches, and the two locators that depend on how the screen is built
rather than on what it says, the sidebar entry and the card. It also
takes the three things the journeys had copied between themselves: the
link-a-ticket steps, five times over, and the document-order check,
twice. A sentence only one journey asserts stays in that journey.

Every locator is the one that was written inline. Two differ in how
strictly they match: the screenshots named four buttons by part of
their name and now name them exactly, and the patch-file button is
matched by part of its name everywhere, as the journeys asserting its
absence already did.

The screenshots' card() moves with the rest, unchanged. It still reads
the shape of the markup; the point is that it does so in one place.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…hat its rule says

Three findings from the review of the previous commit.

The five clicks on the patch-file button matched its whole name before
the move and part of it after, and nothing else states that name. They
match it exactly again. The two assertions that the button is absent
keep the looser match, under a name of its own.

The rule for what belongs in the helper said more than the helper held:
"Continue working on #N" was reached from two journeys and left inline,
and the ticket row reads the markup the same way the card does. Both
move in. The rule now names the four structural locators it holds and
says a selector only one file needs stays in that file, in one wording,
in TESTING.md, which the review standard points to.

sidebarEntry's comment says which sites it cannot find: one with a
staleness or an incomplete-update dot, and one being deleted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 23 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: WordPress/contributor-toolkit/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 89a95088-35b6-4a7d-86f3-3ee31a515c15

📥 Commits

Reviewing files that changed from the base of the PR and between f9a7980 and 00261a2.

📒 Files selected for processing (17)
  • .github/instructions/code-review.instructions.md
  • TESTING.md
  • scripts/screenshots/shots.cjs
  • tests/e2e/helpers/ui.cjs
  • tests/e2e/journeys/engine.spec.js
  • tests/e2e/journeys/gutenberg-site.spec.js
  • tests/e2e/journeys/legacy-site.spec.js
  • tests/e2e/journeys/merge-in-progress.spec.js
  • tests/e2e/journeys/open-pull-request.spec.js
  • tests/e2e/journeys/patch-apply.spec.js
  • tests/e2e/journeys/pr-checkout.spec.js
  • tests/e2e/journeys/store-persistence.spec.js
  • tests/e2e/journeys/ticket-branches.spec.js
  • tests/e2e/journeys/ticket-rebase.spec.js
  • tests/e2e/journeys/trunk-update.spec.js
  • tests/e2e/packaged/smoke.spec.js
  • tests/e2e/real-setup/create-site.spec.js

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zaerl

zaerl commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@zaerl zaerl self-assigned this Sep 30, 2026
@zaerl
zaerl merged commit 3b0ffa2 into trunk Sep 30, 2026
8 checks passed
@zaerl
zaerl deleted the update/shared-journey-helpers branch September 30, 2026 13:23
zaerl added a commit that referenced this pull request Sep 30, 2026
… their own (#567)

## Why

`src/renderer/index.jsx` is 6,420 lines, and the redesign in #542 has to
change almost all of it. Between its imports and the two components that
are most of the file sat seven components that take props and nothing
else, and five hooks. They can be changed one at a time once they are
files of their own, and what is left in `index.jsx` afterwards is the
part that still has to be taken apart.

## What changes

The twelve move, verbatim, and `index.jsx` imports them back:

- `src/renderer/components/`: `ReasonedButton`, `DiscardChangesLink`,
`DiffText`, `LogText`, `Destination` and `DestinationGroup` (one file),
`TerminalCommandLink`.
- `src/renderer/hooks/`: `useDetectedEditors`,
`useContributorProvenance`, `useNextActionCue`, `useSites`, and
`useConfirmation` with `ConfirmationContext`.

Nothing is rewritten. Each function gains `export`, each file gains the
imports it needs, and five comments that pointed "above" or "below" now
name the file they mean. `index.jsx` is 6,085 lines.

Two things follow from there being more than one renderer source:

- **`build.files` excludes every `.jsx` under `src/renderer/`**, where
it excluded `index.jsx` alone. In that directory `.jsx` now means
"bundled by esbuild", and without this the new files would ship
uncompiled beside the bundle that already contains them. The hooks are
named `.jsx` for that reason, although they hold no JSX. The packaged
smoke test reads the same rule.
- **The review standard and TESTING.md** said a decision made in
`index.jsx` is one the suite cannot reach. That is as true of a
component or a hook beside it, and they say so.

Deliberately not in this PR: any change to what a component renders or a
hook does, the five modals (#553), and `SiteRow`'s state (#554).

## How to test this

Platforms: any. Use the Buildkite build for the current head of this
branch.

The journeys carry this: `npm run test:e2e`, from the repository root,
drives every flow the moved components and hooks take part in. The
packaged smoke test, after `npm run build:once &&
CSC_IDENTITY_AUTO_DISCOVERY=false npm run pack:dir`, proves the app
boots from an archive without the sources: `npm run test:e2e:packaged`.
It is red if `build.files` excludes only `index.jsx`; that was checked
by putting the old pattern back.

Nothing on screen should differ, so a manual pass is a look for what the
journeys do not reach:

**Starting state:** the app open on a site that has finished setup, with
an edited file.

1. Hover a disabled button, for example "Unlink" while a command runs in
the terminal. The tooltip with the reason appears.
2. Click "Review & submit changes". The diff is coloured, and the
destinations are grouped in cards.
3. Start the dev server and open the "Logs" tabs. Log lines are coloured
by severity.
4. Under the terminal, click one of the command links in the hints. The
command is typed at the prompt and not run.
5. Click "Open directory in". The list of editors on this machine
appears.

**What must not have happened:**

- No confirmation toast is missing after an action that shows one
("Updated to the latest trunk" after an update, "Patch saved to …" after
saving a patch, "Local changes discarded." after a discard): they come
through the context that moved.
- The page still scrolls to the next step when it changes.

## Risks and limitations

- **One journey failed once and could not be reproduced.** In the first
full run after rebasing onto #541, "a failed review reports an error
instead of claiming there are no changes" failed. It passed in the next
two full runs, in 25 isolated repeats and in 4 repeats of its whole
file. The failure output was not kept, so its cause is unknown; nothing
the test exercises changed in this PR.
- **`npm test` was not rerun on the last commit.** It changes two
comments, the README and two smoke-test assertions. The unit suites ran
on the commit before it.
- ~~**Checked on macOS only.** Windows is covered by CI's journeys and
packaged smoke.~~ Checked on Windows also
- **The moved code keeps `index.jsx`'s two-space indentation**, where
`.editorconfig` asks for tabs in new code, so that the diff reads as a
move. `.editorconfig` steers editors and fails nothing.
- Review: no findings; three style notes, applied.

## Related

Closes #552

Part of #542. Follows #566.

---

<details>
<summary>Design decisions and alternatives considered</summary>

**`hooks/` beside `components/`**, where the issue's title names
`components/` for both. The step that splits `SiteRow`'s state (#554)
will add a hook per domain, and they are not components.

**Kebab-case file names**, like every other file under `src/`, rather
than the `PascalCase.tsx` of the prototype.

**One pattern, `!src/renderer/**/*.jsx`**, rather than a line per
directory. A directory list has to be remembered the next time one is
added; the extension is a rule a new file follows by being what it is.
The `.cjs` modules beside them are not covered, deliberately: the main
process requires some of them.

**No rewrite on the way.** `formatEmailDate`, the style constants and
the labels at the top of `index.jsx` stay where they are; they are not
components, and moving them is not what the issue asks.

</details>

<details>
<summary>Review outcome (required — see AGENTS.md)</summary>

**0 [fix here] · 0 [follow-up]** — no findings across the five
dimensions.

The review checked the move mechanically: it stripped each new file's
imports and `export`, found the rest as an exact substring of `trunk`'s
`index.jsx`, and diffed what remained against the branch's. All eleven
files are byte-identical to `trunk` apart from the comment edits. It
also confirmed no circular import, one shared `ConfirmationContext`, an
unchanged order of style registration, that the packaging pattern
matches all twelve `.jsx` and nothing that must ship, and that the
translation extraction script still sees the new files.

Style notes, applied in the second commit: two comments in `index.jsx`
still pointed at a note that had moved; the smoke test's directory
checks were written through `arrayContaining` where `toContain` says it;
the README named `index.jsx` alone as the bundle's source.

Style notes, left alone as unrelated to this change: `.editorconfig` and
`eslint.config.mjs` say `index.jsx` uses four spaces, and the review
standard still says "4000-line component".

- **Review:** completed — fresh agent context, local, before the PR
existed, read-only; head 1ab1520 / base
1f3808a; committed work only, working
tree clean; outcome above. CodeRabbit: not run yet.
- **Since review:** `1ab1520 → 4c98b4c` checked. The difference is the
three style notes. It was not reviewed again by a fresh context.

Checks, macOS, local:

| Check | On | Result |
|---|---|---|
| `eslint . --max-warnings=0` | 4c98b4c | clean (run with a local agent
worktree under `.claude/worktrees/` ignored; it is not part of the
repository) |
| `npm test` | 1ab1520 | 1691 pass, 2 skipped, 0 fail |
| `npm run test:electron` | 1ab1520 | 1693 pass, 0 fail |
| `npm run test:e2e` | 4c98b4c | 34 of 34 pass |
| `npm run test:e2e:packaged` | 4c98b4c | 15 of 15 pass |

</details>

<details>
<summary>Implementation notes</summary>

How the move was checked before the review: every line that left
`index.jsx` was looked up in the new files. Of 335, all but eight are
there unchanged; the eight are five import lines and three comments,
each rewritten.

`@wordpress/i18n`'s extraction (`scripts/generate-pot.cjs`, from #541)
walks `src/` and picks up `.jsx`, so a string wrapped in a component
later is found without a change there.

</details>

<details>
<summary>Screenshots or recording</summary>

Nothing on screen changed, and no images are attached. The twelve shots
`npm run shots` captures were taken on `trunk` and on this branch with a
two-second settle before each capture. Eleven are pixel-identical. The
twelfth, `gutenberg-site-view`, differed once in its terminal, which is
an intermittent rendering problem on `trunk` itself: eight captures of
that shot on unchanged `trunk` code showed it twice, and eight on this
branch not at all. It is being looked at separately.

With the harness's own 300ms settle, one shot differs from `trunk` by 14
corner pixels of a sidebar entry: a transition caught part-way, which is
why the comparison was repeated with a longer settle.

</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@zaerl zaerl added this to the v2.0.0 milestone Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move journey labels and the screenshot card() selector into shared helpers

1 participant