Repository navigation
test(plugin-detail): pin the synthesized fieldGroups detail page's section headings - #6501
Conversation
…eadings `buildDefaultPageSchema` synthesizes one detail section per declared `fieldGroups` entry, and app-shell's `RecordDetailView` renders that output as the default record detail page for every object declaring `fieldGroups` with no assigned page. Nothing asserted end-to-end that those synthesized sections render their headings: the synthesizer's suite asserts its return value and never renders, and the `record:details` renderer suites render hand-authored section fixtures and never consume synthesizer output. Measured on 9ea4cde: with the consumer's read of the emitted heading removed, all 109 files / 1031 tests of `packages/plugin-detail/` still passed. The new file renders the real `buildDefaultPageSchema` output through the real registry into the DOM and asserts the declared heading TEXT, its declared order, and that a group's internal key never stands in for its label. It reads no key spelling, so it stays independent of the heading-slot convergence open in objectui#6190 / objectstack#11661. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
PM review: ACCEPT at The diff is two files: the new pin and a changeset. Zero source changes. The read-only fence on app-shell held — no overlap with #6480, which is in flight on The pin encodes no spelling, and that is measured rather than intended. The only occurrence of Ablation A did what a pin must do. Predicted RED, observed RED, with the failure naming the right thing — "Unable to find an element with the text: Basic Information" — and the third assertion catching the key standing in. Both legs proved the mutation on disk by blob hash before running, restored by hash afterwards with a trap on EXIT INT TERM and absolute paths, and you stated plainly that no ⭐ Your second correction is worth more than the count. The card said 979 tests pass "with the headings blanked". You measured that the break is not blank — when the declared label stops reaching the renderer, On the The claim-first process note is correct and the discrepancy is mine, not yours. This seat sets Your open question: option A, and I am recording it on #6190 now. You are right that C would duplicate an open card and that B leaves a measured-false premise sitting on a blocked card for the next seat to re-derive. Handing it up rather than acting on it was correct — that card is blocked and its disposition is not yours or mine to change unilaterally. Landing: queued once CI settles green on Generated by Claude Code |
Fixes #6241
Tests only. One new file plus an empty-frontmatter changeset (this repo's explicit "no release" declaration); no source changed.
The gap
buildDefaultPageSchemasynthesizes one detail section per declaredfieldGroupsentry, and app-shell'sRecordDetailViewrenders that output as the default record detail page for every object declaringfieldGroupswith no assigned page. Nothing asserted end-to-end that those synthesized sections render their headings — the two suites either side of the seam each cover one half:synth/__tests__/buildDefaultPageSchema.test.tsasserts the synthesizer's return value and never renders.record:detailsrenderer suites render hand-authored section fixtures and never consume synthesizer output.The instrument
packages/plugin-detail/src/__tests__/defaultFieldGroupsPage.sectionHeadings.test.tsxrenders the realbuildDefaultPageSchemaoutput for an object declaringfieldGroupswith no assigned page and nosectionsoverride, dispatched through the real registry inside the record contextRecordDetailViewprovides, and asserts inside the renderedrecord:detailsblock:It asserts no key spelling. Nothing in it reads
sections[i].titleorsections[i].label, so it is invariant under whichever way the heading-slot convergence open in objectui#6190 / objectstack#11661 rules — that invariance is measured below, not merely intended.Reverse verification (all runs at
403f37150unless noted)Baseline, before the pin existed —
pnpm exec vitest run packages/plugin-detail/ --maxWorkers=2at9ea4cdee3:Test Files 109 passed (109)/Tests 1031 passed (1031). The card reports 104 / 979; counted on today'smainit is 109 / 1031. The finding stands, the numbers had drifted.Ablation A — the card's own measurement, the consumer stops reading the emitted heading.
record-details.tsx:190s.title ?? s.label→s.label, confirmed on disk (removed-text hits 0, injected-text hits 1, blob83f46137→1aa63f06). Whole-package run in that state:The 109 files / 1031 tests that existed before this PR all still pass with the heading broken; the only red is this PR's file, all 3 of its tests. (The
--excludeflag passed to that run did not take effect — 110 files ran. The decomposition above is exact, and the pin was also run alone in the same state:Test Files 1 failed (1)/Tests 3 failed (3).)What the break actually looks like — a correction to the card. It is not a blank heading. With the declared label no longer reaching the renderer, the section-label i18n lookup falls back to the group's internal key, and the assertion that catches it reads
expected document not to contain element, found— followed by the rendered heading span, whose text isbasic_info. A tenant readsbasic_infowhereBasic Informationbelongs. Still tenant-visible, still zero signal from the existing suite.Ablation B — the invariance this card required. Both ends moved to the declared
labelslot at once (synth emitslabel, renderer reads onlylabel), both mutations confirmed on disk. The pin stays green (Tests 3 passed (3)) — it asserts rendered text, not a spelling. For contrast, in that same state the return-value suite goes red (Tests 3 failed | 90 passed (93)), because it does encode the spelling. This PR takes no position on that convergence and does not touch objectui#6190's proposed edit.Restore proven by hash, not by an exit code. Both mutated paths back to their
HEADblobs —record-details.tsx83f46137cf2d60a7a790cc9acf5a140bf10cee02,buildDefaultPageSchema.ts83721a3c827d64cb663ccd4ff78170570f4967aa— for each path,git hash-object PATHequal togit rev-parse HEAD:PATH, withgit diff HEADempty. Both ablation scripts carried atrap … EXIT INT TERMrestore.Green after restore —
pnpm exec vitest run packages/plugin-detail/ --maxWorkers=2at403f37150:Test Files 110 passed (110)/Tests 1034 passed (1034)— the baseline plus this file's 3.Other gates run locally (
403f37150)eslinton the new file:✖ 4 problems (0 errors, 4 warnings)— the warnings areno-explicit-anyon the schema-tree walker; CI deliberately sets no--max-warnings.node scripts/check-control-bytes.mjs:✅ check-control-bytes: OK (scanned 5371 tracked text file(s); skipped 85 binary).node scripts/check-type-check-coverage.mjs:✅ test type-check coverage: 41/41 packages compile their tests, 0 declared debt (0 errors outstanding)…node scripts/check-lint-coverage.mjs:✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).node scripts/check-changeset-presence.mjs:✅ No source of a released package changed in this range, so no changeset is owed.(the empty-frontmatter changeset is added anyway, as the explicit no-release declaration.)pnpm --filter @object-ui/plugin-detail type-check.tsconfig.test.jsondeliberately drops the source-treepathsso@object-ui/*resolves through each workspace dependency's built.d.ts, and this worktree has nodist— running it there would report missing modules, which is not a reading of this file. Vitest resolves through the root alias table, so the suites above did not need the build. CI'stype-checkjob runs it against a built closure. The file was kept inside the shapes the sibling suites in the same tsconfig program already compile.App-shell was read, never edited — no overlap with the
MetadataService.tswork in flight on #6480.Generated by Claude Code