Skip to content

Commit 30da04f

Browse files
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

File tree

‎examples/app-showcase/e2e/detail-shapes.spec.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,16 @@ test('grouped: fieldGroup sections render and Money starts collapsed', async ({
111111

112112
// objectui#2577: fieldGroups[].icon renders as a real (Lucide svg) icon in
113113
// the section header — not as literal icon-name text.
114+
// A collapsible section's header is a disclosure button named by its title
115+
// (objectui#12625); the icon sits beside the title span inside it, and the
116+
// chevron in a SIBLING element. So the svg is looked up under whatever
117+
// element directly holds the title span: a bare `button svg` would also
118+
// match the chevron and stay green with no icon at all.
114119
await expect(
115-
page.locator('div:has(> span:text-is("Money")) svg').first(),
120+
page
121+
.getByRole('button', { name: 'Money' })
122+
.locator('*:has(> span:text-is("Money")) svg')
123+
.first(),
116124
'Money header icon svg',
117125
).toBeVisible();
118126
await expect(page.getByText('banknote', { exact: true })).toHaveCount(0);

0 commit comments

Comments
 (0)