Repository navigation
Commit e603385
test(plugin-list): fold eight more ListView pins that mock nothing onto the shared harness (#30843)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoFc3WxLQeVDNyTRiwvaQips)_
Part of #29469
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:列表组件 `ListView` 再有 8 个单卡测试文件(都不 mock 任何模块)合进上一片建好的共享测试文件
`ListView.harness.test.tsx`,每个原文件变成一个顶层
`describe`,断言一字未改;`restoreAfterBlock()` 多复原一样东西:`window.print`。
- **为什么改**:#29469 第二族 ListView 的第二片,也是这一族的最后一个 PR(席位修订 2:每族最多两个
PR)。本地同机实测,这 8 个文件的 CPU 从约 37 秒降到并入后约 2 秒;ListView 族还没合并的 109 个文件按共享的
setup 列在正文末尾。
-
**风险与回滚**:只动测试文件,不改源码、门禁、CI,不发布任何东西;块之间串状态的风险用探针(逐块记录注册表、`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 个测试全过。
- **需要你做的**:无,席位落地。
- **席位意见**:ui 席 2 对照 head `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 in `ui/packages/plugin-list/src/__tests__/` that render the
real `ListView` with no `vi.mock` join `ListView.harness.test.tsx`,
which slice 2a (#30769) created:
- Each former file is a top-level `describe` named after its file stem,
with its docblock above it and everything else moved verbatim,
re-indented by two spaces, opening with `restoreAfterBlock()`. The 8
files are deleted. The harness goes from 13 to 21 top-level `describe`
blocks.
- The six that register nothing (`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 an `object-grid` stand-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 back `window.print`.**
`printButtonSemantics` installs a `window.print` spy with
`Object.defineProperty` in one of its tests and never removes it. A
former file got a fresh window; a block does not. The helper now
snapshots the `print` property descriptor in its `beforeAll` and
redefines it (or deletes an own property the block added) in its
`afterAll`. Measured with a probe that logs state at each block's first
test: without the two new lines, the ten blocks after
`printButtonSemantics` start with the spy installed; with them, none
does.
- `resetSortToDefaultNoProviderFallback`'s docblock explains why it was
its own file: an `I18nProvider` mounted 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.ts` puts the pointer the file
inherited back after every test (objectui#4514). The probe reads
react-i18next's `getI18n()` 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 for `printButtonSemantics`, which reads the provider-less fallback
too.
- `contractEnvelope-6917`'s `beforeAll` patches
`Element.prototype.scrollIntoView` only when it is missing. happy-dom
defines it, so the patch never runs (probed: `typeof
Element.prototype.scrollIntoView` is `function` before any block, and
the probe compares its identity at every block).
- The harness's import block takes the union of the former files'
imports. `I18nProvider` stays imported once from `@object-ui/i18n`
(`contractEnvelope-6917` took it from `@object-ui/react`, which
re-exports that binding), and `import * as React` in two former files is
the harness's `import React`.
No other file names any of the 8: `git grep` of each stem over `ui`,
`scripts`, `packages`, `content`, `apps` and `.github` gives no hit
outside the file itself, so no comment needed updating.
Test files only: no source file, no changeset (`skip-changeset`).
`@object-ui/plugin-list` builds from its entry with `vite 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` (the `main` this
was cut from, where they still exist) against the `describe` of the same
name in the harness at this PR's head. Titles are compared as the sorted
list of full `describe > it` paths.
| former file | `it` before | `it` after | `expect(` before | `expect(`
after | titles identical |
|---|--:|--:|--:|--:|:-:|
| `ListView.contractEnvelope-6917.test.tsx` | 4 | 4 | 5 | 5 | yes |
| `ListView.emptyStateAccess-25898.test.tsx` | 9 | 9 | 29 | 29 | yes |
| `ListView.emptyStateFilteredCopy.test.tsx` | 7 | 7 | 16 | 16 | yes |
| `ListView.printButtonSemantics.test.tsx` | 5 | 5 | 15 | 15 | yes |
| `ListView.refusedSortNotSent-30024.test.tsx` | 6 | 6 | 17 | 17 | yes |
| `ListView.switcherChrome-7547.test.tsx` | 8 | 8 | 8 | 8 | yes |
| `ListView.nameFieldSelect-13116.test.tsx` | 3 | 3 | 9 | 9 | yes |
| `ListView.resetSortToDefaultNoProviderFallback.test.tsx` | 2 | 2 | 5 |
5 | yes |
| **total** | 44 | 44 | 104 | 104 | yes |
The whole harness: 41 `it` / 95 `expect(` at `9b24f3ca`, 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-battery `main` run after this merges.
Local A/B on one 4-vCPU container, under `os-verify-lock` (shared-box
seconds), same command both legs, from `ui/`: `pnpm exec vitest run
packages/plugin-list/src/__tests__/ListView. --maxWorkers=2` (every
`ListView.*` file in that directory, `ListView.test.tsx` included), with
a throwaway reporter summing each module's `diagnostic()`. Fixed =
environment + prepare + setup + collect (import). The "before" leg
writes the 8 files and the harness from `9b24f3ca` into the branch's
tree, so both legs run the same 101 other files; run order before,
after, after, before.
| | files | tests passed | fixed CPU | test CPU | total CPU | wall |
|---|--:|--:|--:|--:|--:|--:|
| ListView family, before (run 1 / 2) | 118 | 1,140 | 527.5 / 544.9 s |
96.2 / 102.5 s | 623.8 / 647.3 s | 326.8 / 339.5 s |
| ListView family, after (run 1 / 2) | 110 | 1,140 | 527.4 / 504.9 s |
106.9 / 97.6 s | 634.4 / 602.5 s | 332.4 / 315.9 s |
| the 8 + the harness, before (run 1 / 2) | 9 | 85 | 37.9 / 39.1 s | 5.6
/ 5.6 s | 43.6 / 44.7 s | |
| the harness, after (run 1 / 2) | 1 | 85 | 4.7 / 4.4 s | 4.9 / 4.4 s |
9.6 / 8.8 s | |
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
- **Why these 8.** They are the 8 smallest of the 16 `ListView*` pins
that pass every census check in #30769's "Why these 13" (no `vi.mock`;
no statement at module scope or collection time other than imports,
declarations and test calls; no `vi.spyOn`, `vi.stubGlobal` or
`vi.useFakeTimers`; not on the `dom-heavy` list; 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.
- **This is ListView's last PR under amendment 2,** so the family's
unfolded pins are listed below by the setup they share. The table is
Release 6675298252's census at `f2ea524d`, minus the 8 folded here,
re-checked at `9b24f3ca`: the same files are present and every line
count is unchanged, and no new `ListView.*` pin has landed in
`plugin-list/src/__tests__/` since. Whether to fold further is a new
card the seat files after #29469 closes (amendment 2).
- `printButtonSemantics` and `resetSortToDefaultNoProviderFallback` are
the two pins whose own prose says a provider in the same file would make
them assert nothing. They fold cleanly only because the `ui/` 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.
<details><summary>ListView pins left unfolded at <code>9b24f3ca</code>:
109 files, 24,343 lines, 15 setups</summary>
`ListView.test.tsx` is 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 "no `vi.mock`".
Names drop the `ListView.` prefix and `.test.tsx`.
| setup the files share | files (lines) | lines |
|---|---|--:|
| no `vi.mock`, but a statement at module scope or in a `describe` body
that is not an import, a declaration or a test call: it runs when the
file is collected | `fieldListRead-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) | 9854 |
| no `vi.mock` and passes the checks above, but another file names it,
so folding it also edits that file | `loadErrorKind` (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) | 4685 |
| no `vi.mock` and passes every check: left out for the 3,000-line bar,
the next fold's first candidates | `sortFieldFallback` (338),
`pagerClampAfterWrite-10384` (245), `sortFieldRead-11943` (240),
`groupingProjection-7179` (227), `displayPopover-12423` (214),
`emptyStateClearFilters-28908` (212), `restoreViewDefaults-28948` (211),
`toolbarRows-27520` (210) | 1897 |
| no `vi.mock`, but `vi.spyOn`, `vi.stubGlobal` or `vi.useFakeTimers` |
`pageSizeNonPositive-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) | 2907 |
| `@object-ui/permissions` mocked: `usePermissions` returns a
`vi.hoisted` holder each test fills | `speculativeFls-7216` (413),
`mapLocationSelect-10370` (267), `allDeniedSelect-10275` (251),
`referenceViaSelect-13036` (193), `dependsOnSelect-13214` (191),
`deadlineSettledSelect-11815` (182), `rowColorSelect-12376` (176) | 1673
|
| no `vi.mock`, but on the `dom-heavy` list in `ui/vitest.config.mts` |
`crossPageSelectAll` (359), `inlineFlsNoop-6723` (185) | 544 |
| `@object-ui/components` mocked: `FilterBuilder` replaced by a stand-in
that records its `fields` and renders nothing |
`columnsOnlyRelationalReads-7531` (175), `columnsOnlyOptions-10547`
(157), `declaredDisplayField-10545` (154) | 486 |
| no `vi.mock`, but a side-effect import other than
`@testing-library/jest-dom` | `elementDataSource` (402) | 402 |
| `@object-ui/components` mocked: `FilterBuilder` wrapped, recording its
`fields` and rendering the real one | `filterFieldRead-11925` (210),
`filterFieldList-11810` (192) | 402 |
| `../ListView` stubbed and `@object-ui/react`'s `ElementDataSourceGate`
wrapped | `sharedGate` (313) | 313 |
| `@object-ui/permissions` mocked: `usePermissions` returns a
module-level stub | `expandFls-7215` (242) | 242 |
| no `vi.mock`, but `.test.ts` files in the `unit` project, which is not
isolated per file, so a fold saves nothing | `propsResolution.test.ts`
(149), `noAnySchemaCast.test.ts` (77) | 226 |
| `react` mocked with `useMemo` patched | `discardedExpandFieldsMemo`
(220) | 220 |
| `@object-ui/permissions` mocked: `usePermissions` returns a fixed
object built in the factory | `columnIdentity` (174) | 174 |
| `@object-ui/permissions` mocked, in
`ui/packages/app-shell/src/__tests__/` (another package, so another
harness) | `gridRowColorSelect-12393` (318) | 318 |
| **total** | 109 files | 24343 |
</details>
## Verification
At head `79076390`, after `pnpm install --frozen-lockfile` and building
the closure (`turbo run build` for `@objectstack/spec`, `formula`,
`lint`, `client`, `metadata-protocol`, then
`--filter='@object-ui/plugin-list^...'` from `ui/`), under
`os-verify-lock`, VERDICT command-exit 0:
- `pnpm exec vitest run packages/plugin-list/ --maxWorkers=2` from
`ui/`: 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-check` in `ui/packages/plugin-list` (`tsc --noEmit &&
tsc -p tsconfig.test.json`): exit 0, 0 `error TS`. The harness is in the
`tsconfig.test.json` program (`--listFilesOnly`).
- `pnpm run lint` in `ui/packages/plugin-list`: exit 0, 0 errors (780
warnings, as after slice 2a). `eslint` on the harness from `ui/`: exit
0, 0 errors, 56 warnings, the same 56 that the 8 former files and the
slice-2a harness give together at `9b24f3ca`.
- Order: the harness passes 85 / 85 in declaration order and under
`--sequence.shuffle.tests` with `--sequence.seed` 11, 222 and 3333. A
throwaway probe (a copy of the harness with a `beforeAll` after each
block's `restoreAfterBlock()`) logged, at every block's start, each
registry entry's renderer and meta against the post-import registry, the
`localStorage` and `window.print` own-property values,
`Element.prototype.scrollIntoView`, both storages' lengths, `<html
lang>` / `dir`, and react-i18next's `getI18n()`: every one of the 21
blocks starts clean in all four orders. The same probe with the two new
`window.print` lines removed finds the spy installed at the start of all
ten blocks after `printButtonSemantics`.
- Derived gates (`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, change set 9 paths against merge base
`9b24f3ca`): 26 derived, 26 run, each exit 0 captured before any pipe;
`pnpm --dir ui exec vitest run scripts/` ran under `os-verify-lock`
(VERDICT command-exit 0) and gave 170 files passed and 2 skipped, 4,970
tests passed and 2 skipped. `--ran` reconciliation: 26 derived, 26 run,
0 NOT-MEASURED, 0 UNRUN.
- Size: `git diff --shortstat origin/main HEAD` is 9 files, +1,291 /
−1,346, 2,637 changed lines. No open pull request the dispatch named as
touching `ui/` (#30640, #30552, #30595, #30545, #30652) changes any of
the 9 paths (their file lists read at 2026-11-06 06:00Z).
- `main` has moved 15 commits past `9b24f3ca` since this was cut; none
touches `ui/packages/plugin-list`, `ui/vitest.config.mts` or
`ui/vitest.setup.i18n-global.ts`, so the branch is not re-merged and the
readings above stand for this head.
- No ablation of a product behaviour: none changed, and the assertions
moved verbatim. The helper's new lines are shown by the probe with and
without them.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (`ui/` is linted by its
own config; the package lint is above.)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (Nothing
publishes: `skip-changeset`.)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the maintainer
quick-read is the allowed Chinese section).
<!-- signed: domain:ui#2 · os-tesla · session_01L9qibw7DekEe3wx5f3zphf
-->
Co-authored-by: Claude <noreply@anthropic.com>1 parent da3c742 commit e603385
9 files changed
Lines changed: 1291 additions & 1346 deletions
File tree
- ui/packages/plugin-list/src/__tests__
This file was deleted.
This file was deleted.
0 commit comments