Repository navigation
test(app-shell): wait for the counted backfill checkbox in the unchecked publish case (objectui#13120) - #13168
Conversation
…ked publish case (objectui#13120) The unchecked case read the backfill checkbox by its counted name (N) synchronously right after openSheet(). openSheet() resolves once the option is drawn, but the drill-in asks the adapter for the record count only after that render, so on a slow runner the name had no number yet and getByRole missed it. The read now waits (findByRole), like the two cases above it; the name still has to carry N. Claude-Session: https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7 Co-authored-by: Claude <noreply@anthropic.com>
… (objectui#13120) check-changeset-presence counts any file under a released package's src/ as published source, the test file included, so the test-only change declares an empty-frontmatter changeset. Claude-Session: https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
…s one-choice case (objectui#13120)
DraftChangesPanel.backfill-12929's "one choice" case read the checkbox by
its counted name ('Backfill default (12 existing records)') synchronously
after the option was found. The drill-in asks the adapter for the count
only after the option renders, so the name could still carry no number;
the read now waits (findByRole) with the exact same name. The empty
changeset's text names both test files.
Claude-Session: https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7
Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
席位核对 · ACCEPT · objectui#13120 · PR #13168 ·
|
Fixes #13120
Clause-②: no
What changed
Two objectui#12929 test files read the backfill checkbox by its counted name with a synchronous
getByRole. Both reads now wait withfindByRole. Each keeps exactly the same name and the same assertion, so a checkbox without the count still fails.StudioDesignSurface.publishBackfill-12929.test.tsx, case "unchecked: the publish names no field, so nothing is written into existing rows". The read right afteropenSheet()is nowawait within(sheet).findByRole('checkbox', { name: new RegExp(...N...) }): the same\bN\bname (N = 12) and the samenot.toBeChecked(). The other two cases already read it this way.openSheet()gets a doc comment saying it resolves once the option is drawn, not once its count is.DraftChangesPanel.backfill-12929.test.tsx, case "one choice — the added literal-default field of the published object — unchecked, naming the record count". The read aftersettled()andfindAllByTestId('draft-backfill-option')is nowawait screen.findByRole('checkbox', { name: 'Backfill default (12 existing records)' }), with the exact same string. The claim was amended to add this file in patch round 1.The PR also adds
.changeset/13120-backfill-checkbox-wait.mdwith an empty frontmatter, so it releases nothing; its text names both files.node scripts/check-changeset-presence.mjsasked for it because it counts every file under a released package'ssrc/as published source, test files included. No product source changed.Why it flaked
In
DraftChangesPanel.tsx,EntryDetaildraws the backfill option in the same render that setsdetailandcandidates. The record count comes from an effect that runs after that render (source.find(entry.name, { $top: 0 })). UntilsetRecordCountlands, the label reads "Backfill default" with no number. Both tests waited for the option, not for its count, so on a slow runner they could read between those two renders. That read finds a checkbox named "Backfill default" and no checkbox whose name contains 12.Deterministic reproduction (one-shot, not left in the tree)
For each leg, a script rewrote the file's count-read mock (
ADAPTER.findin file 1, thefindset inbeforeEachin file 2) and checked the rewrite on disk: the anchor count went from 1 to 0 and the injected marker from 0 to 1. After each leg it restored the file fromHEAD(git checkout HEAD --with an absolute path, under an EXIT/INT/TERM trap). The restore was confirmed by comparing blob hashes and by an emptygit diff HEAD. "+200 ms" means the count resolves 200 ms after it is asked for (setTimeout). "Never" means it returns a promise that never settles.ed28057, unmutatedUnable to find an accessible element with the role "checkbox" and name /\b12\b/(the same message as merge-group run 39367006901); the role dump showsName "Backfill default"Unable to find role="checkbox" and name /\b12\b/once thefindByRolewindow runs out (1164 to 1396 ms)ed28057, unmutatedUnable to find an accessible element with the role "checkbox" and name "Backfill default (12 existing records)"; the role dump showsName "Backfill default"Unable to find role="checkbox" and name "Backfill default (12 existing records)"after 1306 msIn the "never" legs, every read of the counted name goes red, so none of them was weakened. In file 2, the other nine cases do not read the counted name and stay green.
Every read in both files
File 1 (publishBackfill)
findByRole.openSheet()reads only through waits.getByTestId('draft-changes-publish')is the footer button, drawn with the sheet and not count-gated.waitFor.File 2 (DraftChangesPanel)
options[0]and its= falsetext come from the option and the literal span, which are drawn with the option, not with the count. Thefindcall assertions sit behindwaitFor.findByRoleon the exact name 'Backfill default'. That name is also the pre-count name, so a slow runner can only push it toward green, never red. A one-shot probe showed the case still discriminates: given a numeric total (7), it goes red,Unable to find role="checkbox" and name "Backfill default", 1 failed and 9 passed.settled(). No option is drawn and no count is read, and an absence read cannot turn red because of a slow runner./^Backfill default/, a prefix that matches the label with or without the count. That includes the synchronousgetByRoleafter the click in "checked: Publish all passes…". With the count delayed 200 ms, all 10 cases pass on this branch, so these reads were measured as not count-gated.getByTestId('draft-changes-publish')is the footer button.backfillCandidatescases are pure function tests.Gates
Head
05dc463. Exit codes were captured before any pipe.File 2 unmutated, three runs: exit 0 each,
Tests 10 passed (10). File 1 unmutated at this head: exit 0,Tests 3 passed (3).pnpm --filter @object-ui/app-shell type-checkandlint: exit 0 each. They ran afterturbo run build --filter=@object-ui/app-shell^...(exit 0, 28 of 28 tasks from cache). Lint reports0 errors, plus the package's existing warnings, none on either file.eslint --max-warnings 0on the two files: exit 0.pnpm check:new-line-citations: exit 0 (0 new citation(s)).pnpm check:control-bytes: exit 0.node scripts/check-changeset-presence.mjs: exit 0 (2 source files, 1 changeset, empty frontmatter).These also exit 0:
check:changeset-claims,check:pending-changeset-literals,changeset:check,check-changeset-overwrite.mjs.check-governed-queue-guard.mjs --teston all three paths: NOT GOVERNED.The full CI farm runs in CI.
Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)
Implemented in session
https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7(round 0 and patch round 1).Generated by Claude Code