Repository navigation
test(plugin-list): fold eight more ListView pins that mock nothing onto the shared harness - #30843
Merged
Merged
Conversation
…to the shared harness Eight per-issue ListView pins that render the real ListView with no vi.mock become top-level describes in ListView.harness.test.tsx, moved verbatim, each opening with restoreAfterBlock(). The helper now also puts back window.print, which the printButtonSemantics block swaps for a spy. The two blocks that register an object-grid stand-in sit after the blocks that register nothing. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibwT4kjTEGArV4JTj3c
Contributor
Author
|
domain:ui#2 · os-tesla · session_01L9qibw7DekEe3wx5f3zphf 席位核对 · ACCEPT · 2026-11-06T06:34Z · head
|
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 Tesla · project thread
Part of #29469
Clause-②: no
Metadata-protocol: none
维护者速读
ListView再有 8 个单卡测试文件(都不 mock 任何模块)合进上一片建好的共享测试文件ListView.harness.test.tsx,每个原文件变成一个顶层describe,断言一字未改;restoreAfterBlock()多复原一样东西:window.print。localStorage、window.print、<html lang>、react-i18next 全局实例)在原顺序和三种乱序下排除。回滚 = revert 本 PR。ListView.harness.test.tsx里的restoreAfterBlock()(新增window.print的两行),再看ListView.printButtonSemantics与ListView.resetSortToDefaultNoProviderFallback两块(前者换window.print,后者原本因 i18n 全局实例单独成文件)。ui/下跑pnpm exec vitest run packages/plugin-list/src/__tests__/ListView.harness.test.tsx,应见 1 个文件、85 个测试全过。79076390的 diff 复核:9 个文件全在plugin-list/src/__tests__/,8 个单卡文件并入共享文件、断言与标题一字未改(it(44=44、expect(104=104),只多复原window.print;CI 全绿,纯测试改动无发布面,不欠契约复审。同意落地。What changes
Slice 2b, the second and last
ListView*PR of the shared-setup rollout (ruling 6583487846 on #29469; seat amendment 2, 6672694520: at most two PRs per family; the slice named in Release 6675298252, item 1). Eight more pins inui/packages/plugin-list/src/__tests__/that render the realListViewwith novi.mockjoinListView.harness.test.tsx, which slice 2a (#30769) created:describenamed after its file stem, with its docblock above it and everything else moved verbatim, re-indented by two spaces, opening withrestoreAfterBlock(). The 8 files are deleted. The harness goes from 13 to 21 top-leveldescribeblocks.contractEnvelope-6917,emptyStateAccess-25898,emptyStateFilteredCopy,printButtonSemantics,refusedSortNotSent-30024,switcherChrome-7547) sit, in name order, among the eight from slice 2a that register nothing. The two that register anobject-gridstand-in in their hooks (nameFieldSelect-13116,resetSortToDefaultNoProviderFallback) sit, in name order, among the five that do, after all the others. No existing block moved or changed.restoreAfterBlock()also puts backwindow.print.printButtonSemanticsinstalls awindow.printspy withObject.definePropertyin one of its tests and never removes it. A former file got a fresh window; a block does not. The helper now snapshots theprintproperty descriptor in itsbeforeAlland redefines it (or deletes an own property the block added) in itsafterAll. Measured with a probe that logs state at each block's first test: without the two new lines, the ten blocks afterprintButtonSemanticsstart with the spy installed; with them, none does.resetSortToDefaultNoProviderFallback's docblock explains why it was its own file: anI18nProvidermounted anywhere in a file used to install react-i18next's global instance for every later provider-less render in it. That no longer holds, and a note under the docblock says why:ui/vitest.setup.i18n-global.tsputs the pointer the file inherited back after every test (objectui#4514). The probe reads react-i18next'sgetI18n()at every block's first test and finds no global instance in any of the four orders, including after the blocks that mount a provider (contractEnvelope-6917,refreshIndicatorLocale-10580,recordDrawerCount-27545). The same holds forprintButtonSemantics, which reads the provider-less fallback too.contractEnvelope-6917'sbeforeAllpatchesElement.prototype.scrollIntoViewonly when it is missing. happy-dom defines it, so the patch never runs (probed:typeof Element.prototype.scrollIntoViewisfunctionbefore any block, and the probe compares its identity at every block).I18nProviderstays imported once from@object-ui/i18n(contractEnvelope-6917took it from@object-ui/react, which re-exports that binding), andimport * as Reactin two former files is the harness'simport React.No other file names any of the 8:
git grepof each stem overui,scripts,packages,content,appsand.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
9b24f3ca(themainthis was cut from, where they still exist) against thedescribeof the same name in the harness at this PR's head. Titles are compared as the sorted list of fulldescribe > itpaths.itbeforeitafterexpect(beforeexpect(afterListView.contractEnvelope-6917.test.tsxListView.emptyStateAccess-25898.test.tsxListView.emptyStateFilteredCopy.test.tsxListView.printButtonSemantics.test.tsxListView.refusedSortNotSent-30024.test.tsxListView.switcherChrome-7547.test.tsxListView.nameFieldSelect-13116.test.tsxListView.resetSortToDefaultNoProviderFallback.test.tsxThe whole harness: 41
it/ 95expect(at9b24f3ca, 85 / 199 at this head (41 + 44, 95 + 104). The diff touches no line of the thirteen slice-2a blocks: every hunk inside them is an insertion between blocks.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 packages/plugin-list/src/__tests__/ListView. --maxWorkers=2(everyListView.*file in that directory,ListView.test.tsxincluded), with a throwaway reporter summing each module'sdiagnostic(). Fixed = environment + prepare + setup + collect (import). The "before" leg writes the 8 files and the harness from9b24f3cainto the branch's tree, so both legs run the same 101 other files; run order before, after, after, before.The 8 files took 37.0 / 37.4 s of CPU on their own; the harness grew from 6.6 / 7.3 s to 9.6 / 8.8 s to hold them, so the fold saves about 35 s of CPU (−79 % for those nine files together). The family read 635.6 s before and 618.5 s after on average (−2.7 %), and wall 333.2 s to 324.2 s (−2.7 %). That family figure sits inside this box's run-to-run spread (the two before runs differ by 23.5 s), so it is a reading, not a measured saving; the per-file figures above are the measurement. This box ran about 40 % slower than slice 2a's (2a's family wall was 230 s on 129 files), so compare ratios, not seconds, across the two PRs.
Acceptance notes
ListView*pins that pass every census check in test(plugin-list): fold thirteen ListView pins that mock nothing onto one shared harness #30769's "Why these 13" (novi.mock; no statement at module scope or collection time other than imports, declarations and test calls; novi.spyOn,vi.stubGlobalorvi.useFakeTimers; not on thedom-heavylist; no side-effect import other than@testing-library/jest-dom; no other file naming it), the third row of the table in Release 6675298252. 1,339 lines moved make 2,637 changed lines; the next smallest (toolbarRows-27520, 210 lines) would take it past 3,000.f2ea524d, minus the 8 folded here, re-checked at9b24f3ca: the same files are present and every line count is unchanged, and no newListView.*pin has landed inplugin-list/src/__tests__/since. Whether to fold further is a new card the seat files after Tests grow 2.5 times faster than source: measure the per-fix test weight and the #25911 pilot, then roll the shared-setup merge out to the heaviest components or retire it #29469 closes (amendment 2).printButtonSemanticsandresetSortToDefaultNoProviderFallbackare the two pins whose own prose says a provider in the same file would make them assert nothing. They fold cleanly only because theui/setup resets react-i18next's global after every test; the probe reading above is the evidence, and a later change to that setup file is the thing that would undo it.ListView pins left unfolded at
9b24f3ca: 109 files, 24,343 lines, 15 setupsListView.test.tsxis the component's main suite, not a per-issue pin, and is not counted. A file that mocks nothing sits under the first check it fails, in the order of the rows below that start with "novi.mock". Names drop theListView.prefix and.test.tsx.vi.mock, but a statement at module scope or in adescribebody that is not an import, a declaration or a test call: it runs when the file is collectedfieldListRead-11984(765),permissions(607),readersRetired-6152(445),timeline-binding(378),strayGroupByRefused-8365(353),gantt-binding-7070(330),mapFlatten(319),chart-capability-7544(318),objectProviderBinding-7477(317),datasetChartAppliedFilter-10512(303),selfQueryingViewsToolbarState-10250(303),mapViewLevelConfig(302),sharedSelect-11865(299),timelineMetaFieldsRetired-10222(280),sortPersistLeak(261),searchWithheldTreeChart-10326(259),ganttNavigationForward-7334(258),sortabilitySignal(256),calendarAliasRefused-8355(238),descriptionInlineLocale-7199(230),overlayTitleI18n(227),filterDatePicker-12743(215),searchlessObject-12966(212),calendar-binding-7029(211),overlayTitleInlineLocale-9373(204),ganttToolbarFilter-10037(197),ariaLabelInlineLocale(195),treeEmptyState-28966(191),ganttPagingChrome-7210(163),datasetChartToolbarFilter-10327(156),onGroupingChange-11860(153),overlayTitleNoProviderFallback(143),toolbarLeading-12440(143),hostRoutedRecordClick-27057(135),density.offspec(126),kanbanDeclaredLanes-28211(116),nestedAriaSharedReader-11083(105),calendarInitialDate-28102(86),toolbarNames-12510(55)vi.mockand passes the checks above, but another file names it, so folding it also edits that fileloadErrorKind(283),userActionsCollision(281),viewSwitchRefetch-7394(250),sortRemovableOnly-11943(246),personalization-28950(241),phoneSortSearch-28911(240),relationalSort(220),harvestInputsFetchKey-10689(200),kanbanOwnsLanes-12619(196),unauthoredColumnProjection-6598(194),headerSort(188),toolbarIconButtons-12695(183),clearFiltersChips-29052(179),findParamsHandoff(176),groupingNeedsHeaderQuery-10881(176),serverPagination(175),groupedGridOwnsFetch-7189(169),displayResetBaseline-12949(166),kanbanBlockCanonical-8193(164),toolbarAccessibleNames-12500(153),rowGroups-28461(151),footerOneRow-12528(131),emptyStateI18nLabel-11227(123),storedRuleDialect-11533(108),refreshButton(92)vi.mockand passes every check: left out for the 3,000-line bar, the next fold's first candidatessortFieldFallback(338),pagerClampAfterWrite-10384(245),sortFieldRead-11943(240),groupingProjection-7179(227),displayPopover-12423(214),emptyStateClearFilters-28908(212),restoreViewDefaults-28948(211),toolbarRows-27520(210)vi.mock, butvi.spyOn,vi.stubGlobalorvi.useFakeTimerspageSizeNonPositive-9897(377),calendarDateWindow-12081(317),pageSizeAndFetchBatch-9853(257),queueSittingFilter-25769(246),bulkDeleteVisibleWhen(238),filterContextTokens-10607(215),phoneWidth-29074(174),exportMatchesView(173),nonGridBulkDeclared-30394(162),nonGridBulkGate-30535(160),calendarFooterCount-27355(143),emptyStateLink-12270(137),gridObjectFields-10657(119),invalidationRefetch-10572(107),exportRefusal-10789(82)@object-ui/permissionsmocked:usePermissionsreturns avi.hoistedholder each test fillsspeculativeFls-7216(413),mapLocationSelect-10370(267),allDeniedSelect-10275(251),referenceViaSelect-13036(193),dependsOnSelect-13214(191),deadlineSettledSelect-11815(182),rowColorSelect-12376(176)vi.mock, but on thedom-heavylist inui/vitest.config.mtscrossPageSelectAll(359),inlineFlsNoop-6723(185)@object-ui/componentsmocked:FilterBuilderreplaced by a stand-in that records itsfieldsand renders nothingcolumnsOnlyRelationalReads-7531(175),columnsOnlyOptions-10547(157),declaredDisplayField-10545(154)vi.mock, but a side-effect import other than@testing-library/jest-domelementDataSource(402)@object-ui/componentsmocked:FilterBuilderwrapped, recording itsfieldsand rendering the real onefilterFieldRead-11925(210),filterFieldList-11810(192)../ListViewstubbed and@object-ui/react'sElementDataSourceGatewrappedsharedGate(313)@object-ui/permissionsmocked:usePermissionsreturns a module-level stubexpandFls-7215(242)vi.mock, but.test.tsfiles in theunitproject, which is not isolated per file, so a fold saves nothingpropsResolution.test.ts(149),noAnySchemaCast.test.ts(77)reactmocked withuseMemopatcheddiscardedExpandFieldsMemo(220)@object-ui/permissionsmocked:usePermissionsreturns a fixed object built in the factorycolumnIdentity(174)@object-ui/permissionsmocked, inui/packages/app-shell/src/__tests__/(another package, so another harness)gridRowColorSelect-12393(318)Verification
At head
79076390, 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/: 171 files, 1,826 tests, all passed. That is 8 files fewer than after slice 2a (179) and the same 1,826 tests.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, as after slice 2a).eslinton the harness fromui/: exit 0, 0 errors, 56 warnings, the same 56 that the 8 former files and the slice-2a harness give together at9b24f3ca.--sequence.shuffle.testswith--sequence.seed11, 222 and 3333. A throwaway probe (a copy of the harness with abeforeAllafter each block'srestoreAfterBlock()) logged, at every block's start, each registry entry's renderer and meta against the post-import registry, thelocalStorageandwindow.printown-property values,Element.prototype.scrollIntoView, both storages' lengths,<html lang>/dir, and react-i18next'sgetI18n(): every one of the 21 blocks starts clean in all four orders. The same probe with the two newwindow.printlines removed finds the spy installed at the start of all ten blocks afterprintButtonSemantics.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, change set 9 paths against merge base9b24f3ca): 26 derived, 26 run, each exit 0 captured before any pipe;pnpm --dir ui exec vitest run scripts/ran underos-verify-lock(VERDICT command-exit 0) and 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 9 files, +1,291 / −1,346, 2,637 changed lines. No open pull request the dispatch named as touchingui/(perf(i18n, app-shell)!: the marketplace pages' strings leave the eager language packs for a lazymarketplacenamespace #30640, feat(console): an administrator's first-run checklist on Home replaces the currency and time zone dialog #30552, feat(i18n)!: the Console's language packs follow the platform locale table, keyed by its tags, and Traditional Chinese (zh-Hant) ships #30595, feat(console): a marketplace app's Install confirms what it adds before it installs #30545, feat(app-shell): the ban dialog says how many records the user holds and offers an optional hand-over #30652) changes any of the 9 paths (their file lists read at 2026-11-06 06:00Z).mainhas moved 15 commits past9b24f3casince this was cut; none touchesui/packages/plugin-list,ui/vitest.config.mtsorui/vitest.setup.i18n-global.ts, so the branch is not re-merged and the readings above stand for this head.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/.