Repository navigation
Commit 30da04f
test(showcase-e2e): aim the fieldGroup header-icon locator at the disclosure button the pinned console renders (#24303)
Fixes #24288
Clause-②: no
## What this changes
One locator in `examples/app-showcase/e2e/detail-shapes.spec.ts`, in the
test "grouped: fieldGroup sections render and Money starts collapsed".
Nothing else changes. Every other assertion stays as it was, including
`getByText('banknote', { exact: true })` at count 0, the "not literal
icon-name text" half.
- Before: `page.locator('div:has(> span:text-is("Money")) svg').first()`
- After: `page.getByRole('button', { name: 'Money' }).locator('*:has(>
span:text-is("Money")) svg').first()`
## Which of the card's two readings holds: reading 2 (a stale locator),
measured in a browser
I built the showcase closure and the vendored console at the current pin
`5e15928b4631` the way the workflow does: `pnpm turbo run build
--filter=@objectstack/example-showcase^...`, then `bash
scripts/build-console.sh`, then `pnpm check:console-sha` ("Console dist
matches the objectui pin (objectui@5e15928b4631)"). Then I ran the spec
in Chromium against my own `serve --dev` backend.
- **Red reproduced** with the unmodified spec at `d537a318b`: 1 failed,
2 passed. The failure is at `:117`, "Money header icon svg … element(s)
not found", the same assertion as CI runs `39013050476` and
`39107125964`.
- **The icon still renders.** The failure screenshot shows the banknote
glyph beside "Money". Here is the header's `outerHTML` from the running
console, with the svg path data trimmed:
```text
div.CardHeader(relative … cursor-pointer)
div(text-base font-semibold tracking-tight) ← CardTitle
button[type=button][aria-expanded=false][data-state=closed]
span.flex.items-center.gap-2
svg.lucide.lucide-banknote.h-4.w-4[aria-hidden=true] ← the fieldGroups[].icon
span "Money"
span.flex.items-center.gap-2
svg.lucide.lucide-chevron-right[aria-hidden=true] ← the disclosure chevron
```
- **Why the old locator misses.** The element that directly holds the
title span is now a `span`, not a `div`. objectui `4be76e0306`
(objectui#12625, PR objectui#12661) made the collapsible header a native
disclosure button. Before that change it was `CardTitle >
div.flex.items-center.gap-2 > SectionIcon + span`, with the chevron in a
sibling `div`.
- **Counts on the live page:**
| Locator | Count |
|:---|:---|
| old locator | 0 |
| `getByRole('button', { name: 'Money' })` | 1 |
| svgs anywhere in that button | 2 |
| svgs under the element that holds the title span | 1 |
| literal `banknote` text | 0 |
Ancestry, measured on a full-history (not shallow) objectui clone with
control legs:
| Check | Exit |
|:---|:---|
| `4be76e0306` is an ancestor of the pin `5e15928b4631` | 0 |
| `4be76e0306` is an ancestor of `daeb77fc84dd`, the pin of the last
green run `38866628889` | 1 |
| Control: `daeb77fc84dd~50` in `daeb77fc84dd` | 0 |
| Control: `daeb77fc84dd~200` in `daeb77fc84dd` | 0 |
The pins were read from `.objectui-sha` at each run's `head_sha`:
| Run | Pin | Result |
|:---|:---|:---|
| `38866628889` | `daeb77fc84dd` | green |
| `39013050476` | `5e15928b4631` | red |
| `39107125964` | `5e15928b4631` | red |
So the green-to-red window is `daeb77fc84dd` → `5e15928b4631`, and the
header rewrite is inside it.
## Why the locator is scoped this way
The disclosure button holds TWO svgs: the section icon and the chevron.
A plain `getByRole('button', { name: 'Money' }).locator('svg')` would
match the chevron, and the test would stay green with no icon at all.
The ablation below measures exactly that. So the new locator does two
things:
- It anchors on the button named by the section title. That is the
header's shape since objectui#12625.
- It then looks for the svg under whatever element **directly holds the
title span**. That keeps the old locator's precision: the icon sits
beside the title, and the chevron sits in a sibling. It is also
tag-agnostic, so a later `span`↔`div` swap of that wrapper does not make
it stale again.
## Proof the new locator can fail (throwaway, nothing kept)
The fix was committed first (`eff17f783`). Then `node
scripts/ablation-replace.mjs --delete` removed the Money fieldGroup's
`icon: 'banknote',` line from
`examples/app-showcase/src/data/objects/semantic-zoo.object.ts`. The
tool reported "anchor 1 -> 0, blob 938574b -> d5103f0d9319". With
the line removed, a fresh backend ran the spec:
- **Mutated leg: red.** 1 failed, 2 passed. The failure is the new
locator at `:125`, "element(s) not found". The live header still had its
button and chevron, but no icon svg:
| Count | Mutated leg |
|:---|:---|
| svgs anywhere in the button | 1 (the chevron, so a bare button-svg
locator would have stayed GREEN) |
| svgs under the element that holds the title span | 0 |
- **Restore.** The tool restored the file and proved it: "blob == HEAD
(938574b) and `git diff HEAD` is empty".
- **Restored leg: green** on a fresh backend at `eff17f783`, 3 passed.
## Verification (final head `eff17f783`, console pin `5e15928b4631`)
| Check | Result |
|:---|:---|
| `playwright test e2e/detail-shapes.spec.ts` | 3 passed |
| Full `playwright test` (all of `test:smoke`), one run | **53 passed
(2.5m)**. On CI this was 52 of 53. |
| `tsc --noEmit --listFiles` (the package's `typecheck`) | exit 0; the
spec is in the program (1 hit) |
| `vitest run --maxWorkers=2` (the package's `test`; it excludes
`e2e/**`) | 36 files, 431 tests passed |
Local runs used Chromium at `/opt/pw-browsers/chromium` (via
`OS_TEST_CHROMIUM_EXECUTABLE_PATH`) and a backend on a random port (via
`SMOKE_API_URL`). Two declared differences from the workflow:
- `CI` was unset, so `retries` was 0 rather than 1.
- objectui's dependency build ran with turbo `--concurrency=2`, and the
objectui source came from a scratch clone (`OBJECTUI_ROOT`) at the same
pin.
**This PR shows no `Showcase Smoke` check-run.**
`.github/workflows/showcase-smoke.yml` triggers only on
`workflow_dispatch` and the 07:00Z schedule, and no PR workflow runs
`examples/app-showcase/e2e`. The fleet relay's `workflow_dispatch`
allowlist is `shard-timings-refresh.yml` and `release.yml`. The local
runs above are the evidence. The card's last "Done when" bullet,
`Showcase Smoke` green on `main`, can only be read from the first
nightly run after this lands.
## Changeset
None. `@objectstack/example-showcase` is `private: true` and publishes
nothing, so this PR carries `skip-changeset`.
## Acceptance notes
- `#24283` (the pin move) is not touched here. The header shape measured
above is on objectui `main` as triage read it, so the re-aimed locator
should hold at that pin too. Whichever PR lands second merges `main` and
re-runs this spec against the pin it then carries (the claim's serial
note).
- The assertion stays svg-generic, as before. It proves a real icon
renders, not which glyph. `SectionIcon` falls back to Lucide's
`Database` svg for a name Lucide does not know, and that would also
satisfy it. Narrowing it to the declared glyph is outside this card.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VwoC2RxFfgzY2eXuC7Xo6g)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0c3db0f commit 30da04f
1 file changed
Lines changed: 9 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
114 | 119 | | |
115 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
116 | 124 | | |
117 | 125 | | |
118 | 126 | | |
| |||
0 commit comments