Repository navigation
test(plugin-list): fold thirteen ListView pins that mock nothing onto one shared harness - #30769
Merged
Merged
Conversation
… one shared harness Thirteen per-issue ListView pins render the real ListView with no vi.mock. Each becomes a top-level describe in ListView.harness.test.tsx, named after its former file, with its fixtures, hooks and assertions moved verbatim. The five that register a stand-in renderer in ComponentRegistry sit after the eight that register nothing. I18nProvider is imported once from @object-ui/i18n, the binding @object-ui/react re-exports. Each block opens with restoreAfterBlock(), which puts back after the block's last test what a former file never handed on: ComponentRegistry entries the block added, replaced or removed, a swapped-in localStorage, the contents of both storages, and the lang / dir I18nProvider sets on <html>. Without it three blocks left stand-in renderers registered or the real object-grid unregistered, mutationRefresh left its localStorage stand-in installed, and displayMenuClose's saved view preference reached the four blocks after it. With it every block starts from the state its former file started from, and the file passes in shuffled orders. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXNVxCQk1CCy55gFjQ
Contributor
Author
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL 维护者速读
席位核对 — ACCEPT · head f2ea524
|
This was referenced Nov 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Sales · project thread
Part of #29469
Clause-②: no
Metadata-protocol: none
维护者速读
ListView的 13 个单卡测试文件(都不 mock 任何模块)合进一个新的共享测试文件ListView.harness.test.tsx,每个原文件变成一个顶层describe,断言一字未改;每块第一行调用restoreAfterBlock(),块结束后把注册表、localStorage和<html>的lang/dir复原,下一块从新文件的状态起步。localStorage、<html lang>)和三种乱序运行排除。回滚 = revert 本 PR。ListView.harness.test.tsx开头 90 行(块之间怎么隔离,以及restoreAfterBlock()),再看ListView.displayMenuClose-12621往后的 5 块(会往ComponentRegistry注册替身,所以排在最后)。ui/下跑pnpm exec vitest run packages/plugin-list/src/__tests__/ListView.harness.test.tsx,应见 1 个文件、41 个测试全过。What changes
Slice 2a, the first of the two
ListView*PRs of the shared-setup rollout (ruling 6583487846 on #29469; seat amendment 2, 6672694520: at most two PRs per family). Thirteen pins inui/packages/plugin-list/src/__tests__/render the realListViewwith novi.mockat all, and become one new file,ListView.harness.test.tsx:Each former file is a top-level
describenamed after its file stem, with its docblock above it and everything else moved verbatim, re-indented by two spaces. The 13 files are deleted.The harness's header says how the blocks stay apart: RTL
cleanup()after every test (thedomsetup registers it), every fixture and hook inside its own block, no module mocks.One addition the former files did not need:
restoreAfterBlock(), the first line of every block. A former file got a fresh window and a fresh registry; a block shares both with the blocks after it. Before adding it, a probe that logged the state at each block's first test found:displayMenuClose-12621left stand-ins forobject-kanban,object-gallery,object-calendarandobject-timelineregistered (its hooks put back only entries that existed);recordDrawerCount-27545left the realobject-gridunregistered, androwAdornment-13330then left its ownobject-gridstand-in registered;mutationRefreshleft itslocalStoragestand-in installed for the seven blocks after it;displayMenuClose-12621saved (listview-opportunity-view) was still in storage for the four blocks after it.All 41 tests passed anyway, but each later block was running against state its file never had. The helper snapshots the registry and the
localStorageproperty in abeforeAlldeclared before the block's own hooks, and after the block's last test (itsafterAllruns after the block's own) unregisters what the block added, re-registers each entry it replaced or removed from that entry's own config, putslocalStorageback, clears both storages, and removes thelang/dirthatI18nProvidersets on<html>. With it, the same probe finds every block starting from the post-import registry (every key, renderer and meta equal), the window's own emptylocalStorageand nolang, and the file passes in three shuffled orders (--sequence.shuffle.tests, seeds 11, 222, 3333), which put registering blocks before the others.The five blocks that register a stand-in renderer in
ComponentRegistry(displayMenuClose-12621,inPageScroll-12463,recordDrawerCount-27545,rowAdornment-13330,setupChrome-12606) still sit after the eight that register nothing.I18nProvideris imported once, from@object-ui/i18n. Some former files took it from@object-ui/react, which re-exports that same binding.No other file names any of the 13:
git grepof each stem overui,scripts,packages,contentand.githubgives no hit outside the file itself, so no comment needed updating.Test files only: no source file, no changeset (
skip-changeset).@object-ui/plugin-listbuilds from its entry withvite build, and no entry imports a test file.Tests
Former file → describe (no title or assertion dropped)
TypeScript AST walk of each former file at
e00bcc6a(themainthis was cut from, where they still exist) against thedescribeof the same name in the harness at this PR's head.itbeforeitafterexpect(beforeexpect(afterListView.concurrencyTokenProjection-12292.test.tsxListView.displayMenuClose-12621.test.tsxListView.emptyVsErrorRole-7132.test.tsxListView.errorStateComponent-7143.test.tsxListView.forbiddenNoRetry.test.tsxListView.galleryBlockCanonical-6152.test.tsxListView.inPageScroll-12463.test.tsxListView.mutationRefresh.test.tsxListView.onRowsChange-26679.test.tsxListView.recordDrawerCount-27545.test.tsxListView.refreshIndicatorLocale-10580.test.tsxListView.rowAdornment-13330.test.tsxListView.setupChrome-12606.test.tsxA line-multiset comparison (every non-blank line of the 13 files, trimmed, against the harness) finds no code or comment line dropped: what the files had and the harness lacks is the 13 license headers and the duplicate import lines.
CPU before and after (local A/B, one machine)
CI after: NOT MEASURED: the per-file timings reporter (#29517) uploads on
main's full-battery runs only. Its first after-reading is the first full-batterymainrun after this merges.Local A/B on one 4-vCPU container, under
os-verify-lock(shared-box seconds), same command both legs, fromui/:pnpm exec vitest run <every ListView.*.test.ts(x) in plugin-list/src/__tests__> --maxWorkers=2, with a throwaway reporter reading each module'sdiagnostic(). Fixed = environment + prepare + setup + import. The "before" leg restores the 13 files from the base into the branch's tree and removes the harness, so both legs run the same 116 other files. Runs 1 and 2 are on937fec9d, this fold beforerestoreAfterBlock()was added, over05a30e6e; run 3 is on this head, overe00bcc6a.The family went from 439.7 s to 404.7 s of CPU on average (−8.0 %), wall from 230.6 s to 212.2 s (−8.0 %). Run 3 puts
restoreAfterBlock()'s cost inside the run-to-run spread: the harness read 4.5 s against 4.8 / 4.6 s without it.Acceptance notes
ListView.*pins inplugin-list/src/__tests__/, 23,229 lines, far past one PR under the 3,000-line bar (a fold changes about twice the lines it moves).ListView.test.tsxis the component's main suite, not a per-issue pin, and is not counted. Of the 113, this PR takes only files with none of the following, whichrestoreAfterBlock()does not undo: no statement at module scope or collection time other than imports, declarations and test calls (39 files have one), novi.spyOn,vi.stubGlobalorvi.useFakeTimers(15), not on thedom-heavylist inui/vitest.config.mts(2), no side-effect import other than@testing-library/jest-dom(4), and no other file naming it (49; some files fail more than one test). That leaves 31, of which two are.test.tsfiles in theunitproject, which is not isolated per file and so has nothing to save. This PR folds the 13 smallest of the remaining 29, the most files per changed line: 1,375 lines moved make 2,729 changed lines, and the next smallest (140 lines) would take it past 3,000. Slice 2b folds the next ones that fit.ListView*pin inui/packages/app-shell/src/__tests__/is in another package, outside this harness.Verification
At head
f2ea524d, afterpnpm install --frozen-lockfileand building the closure (turbo run buildfor@objectstack/spec,formula,lint,client,metadata-protocol, then--filter='@object-ui/plugin-list^...'fromui/), underos-verify-lock, VERDICT command-exit 0:pnpm exec vitest run packages/plugin-list/ --maxWorkers=2fromui/: 179 files, 1,826 tests, all passed. That is 12 files fewer thanmain(13 removed, 1 added) and the same tests: the A/B's family legs ran 985 tests on both sides.pnpm run type-checkinui/packages/plugin-list(tsc --noEmit && tsc -p tsconfig.test.json): exit 0, 0error TS. The harness is in thetsconfig.test.jsonprogram (--listFilesOnly).pnpm run lintinui/packages/plugin-list: exit 0, 0 errors (780 warnings).eslinton the harness fromui/: exit 0, 0 errors, 26 warnings, the same 26 the 13 former files give atmain;restoreAfterBlock()adds none.--sequence.shuffle.testswith seeds 11, 222 and 3333, whose orders put registering blocks first. A probe that logged the registry (every key's renderer and meta), thelocalStorageobject and its length, and<html lang>at each block's first test found every block starting from the post-import state.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, change set 14 paths against merge basee00bcc6a): 26 derived, 26 run underos-verify-lock(VERDICT command-exit 0), each exit 0 captured before any pipe;pnpm --dir ui exec vitest run scripts/gave 170 files passed and 2 skipped, 4,970 tests passed and 2 skipped.--ranreconciliation: 26 derived, 26 run, 0 NOT-MEASURED, 0 UNRUN.git diff --shortstat origin/main HEADis 14 files, +1,354 / -1,375, 2,729 changed lines. No open pull request touches any of the 14 paths: the head trees of all 58 open PRs, compared withmainat those paths after test(plugin-gantt): fold four more ObjectGantt pins onto the shared harness #30745 merged, differ only for pm:mailbox — triage#1 · os-zhuang #28646, a mailbox PR whose file list is empty.Human Minimum
packages/specholds no runtime logic. (packages/specnot touched.)pnpm lintpasses. (ui/is linted by its own config; the package lint is above.)main, one pull request.skip-changeset.)packages/spectouched: its generated artifacts are regenerated and committed. (Not touched.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgXNVxCQk1CCy55gFjQ