Skip to content

Commit 24d34a1

Browse files
os-zhuangclaude
andauthored
perf(test): default DOM tests to a light setup; isolate heavy registry setup (#2644)
Follow-up to #2641, which sharded the Vitest suite but only parallelized its cost. This reduces the cost itself. `setup` was the largest line item in a CI run — 990s cumulative vs 205s of actual `tests` — because `vitest.setup.dom.tsx` side-effect imports @object-ui/components, fields, plugin-dashboard and plugin-grid and re-registers 9 widgets, and under `isolate: true` that module graph re-executes for every DOM test file. Measured at ~3.3s of pure setup per file. But an empirical run of the whole `dom` project under a trimmed setup showed only ~20 of ~310 files actually render through the ComponentRegistry; the other ~290 paid that 3.3s for nothing. Split the DOM tests into two projects by need: - `dom` (default): the new light `vitest.setup.dom-light.tsx` — base polyfills + jest-dom + RTL cleanup, none of the heavy package graphs. - `dom-heavy`: the ~20 `heavyDomTests` that drive the registry, keeping the full `vitest.setup.dom.tsx` (which apps/console also still uses). `heavyDomTests` was derived empirically, not guessed: every file in it failed under the light setup (e.g. "page:card not registered"). The list is self-correcting — a heavy test left off it lands in the light project and fails loudly in CI, so coverage can't be silently lost, only turned red with a pointer to the fix. Behavior-preserving: full suite is 566 files / 6892 tests (6868 passed, 24 skipped) both before and after, at the same commit. Local cumulative `setup` drops from ~990s to ~167s. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 6c53960 commit 24d34a1

3 files changed

Lines changed: 111 additions & 12 deletions

File tree

vitest.config.mts

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,66 @@ const domTsTests = [
5252
'packages/react/src/hooks/__tests__/useRecordSearch.test.ts',
5353
];
5454

55+
// Test files that render through the ComponentRegistry and therefore need the
56+
// heavy `vitest.setup.dom.tsx` (the four side-effect package imports + widget
57+
// re-registrations). They run in the `dom-heavy` project; every other DOM test
58+
// gets the trimmed `vitest.setup.dom-light.tsx` default.
59+
//
60+
// This list was derived empirically: run the whole `dom` project under the
61+
// light setup and every file here is one that failed (e.g. "page:card not
62+
// registered", or an element that never rendered). It is a small minority
63+
// (~20 of ~310 DOM files) — the other ~290 never touch the registry and used
64+
// to pay ~3.3s/file of setup for nothing.
65+
//
66+
// If a NEW test renders a schema / page:* / dashboard / grid widget and fails
67+
// with "<type> not registered", add it here. Misfiling is self-correcting, not
68+
// silent: a heavy test left out of this list lands in the light `dom` project
69+
// and fails loudly in CI (it can never be skipped), so coverage cannot be lost
70+
// — only a red build that points at the fix.
71+
const heavyDomTests = [
72+
'packages/app-shell/src/console/ai/LiveCanvas.test.tsx',
73+
'packages/app-shell/src/console/ai/__tests__/ConversationsSidebar.test.tsx',
74+
'packages/app-shell/src/console/organizations/__tests__/CreateWorkspaceDialog.test.tsx',
75+
'packages/app-shell/src/hooks/__tests__/useConsoleActionRuntime.test.tsx',
76+
'packages/app-shell/src/layout/__tests__/AiUsageIndicator.test.tsx',
77+
'packages/app-shell/src/layout/__tests__/ChatDock.test.tsx',
78+
'packages/app-shell/src/preview/__tests__/DraftChangesPanel.test.tsx',
79+
'packages/app-shell/src/preview/__tests__/DraftPreviewBar.test.tsx',
80+
'packages/app-shell/src/views/metadata-admin/AccessExplainPanel.test.tsx',
81+
'packages/app-shell/src/views/metadata-admin/AssignedUsersSection.test.tsx',
82+
'packages/app-shell/src/views/metadata-admin/previews/DatasetPreview.test.tsx',
83+
'packages/app-shell/src/views/metadata-admin/previews/PagePreview.test.tsx',
84+
'packages/app-shell/src/views/metadata-admin/previews/ReportPreview.dataset.test.tsx',
85+
'packages/components/src/__tests__/action-bar.test.tsx',
86+
'packages/components/src/__tests__/page-card-i18n-title.test.tsx',
87+
'packages/components/src/__tests__/page-header-actions.test.tsx',
88+
'packages/components/src/__tests__/page-header-title.test.tsx',
89+
'packages/plugin-calendar/src/registration.test.tsx',
90+
'packages/plugin-dashboard/src/__tests__/DashboardRenderer.designMode.test.tsx',
91+
'packages/plugin-kanban/src/registration.test.tsx',
92+
];
93+
5594
export default defineConfig({
5695
test: {
5796
globals: true,
5897
environment: 'happy-dom',
5998
testTimeout: 15000, // Increase default timeout for integration tests with MSW
6099
exclude: sharedExclude,
61-
// Two root-level projects split by environment cost. The heavy DOM setup
62-
// (vitest.setup.dom.tsx imports @object-ui/components / fields /
63-
// plugin-dashboard / plugin-grid from source and re-registers widgets)
64-
// used to run for EVERY file: with `isolate: true` that module graph
65-
// re-executes per file, and a CI run spent ~20 min cumulative in setup
66-
// for ~2 min of actual tests. Pure-logic *.test.ts files now run in the
67-
// `unit` project: node env + vitest.setup.base.ts only.
100+
// Three root-level projects split by environment cost.
101+
//
102+
// - `unit`: pure-logic *.test.ts in node env + vitest.setup.base.ts only.
103+
// - `dom`: React tests in happy-dom with the LIGHT setup — jsdom polyfills,
104+
// jest-dom and RTL cleanup, but none of the @object-ui/components / fields
105+
// / plugin-dashboard / plugin-grid graphs.
106+
// - `dom-heavy`: the ~20 `heavyDomTests` that render through the
107+
// ComponentRegistry, with the full `vitest.setup.dom.tsx`.
108+
//
109+
// Why the split: with `isolate: true` every setup import re-executes per
110+
// file. The old single DOM setup pulled the four heavy package graphs into
111+
// every one of ~300 DOM files (~3.3s/file of setup) even though ~290 never
112+
// touch the registry — a CI run spent ~20 min cumulative in setup for ~3
113+
// min of actual tests. Restricting that graph to the files that need it
114+
// leaves the common case paying only the light setup.
68115
//
69116
// (The former `environmentMatchGlobs` split silently stopped working on
70117
// Vitest 4 — the option was removed — so every file was paying happy-dom
@@ -89,12 +136,24 @@ export default defineConfig({
89136
test: {
90137
name: 'dom',
91138
environment: 'happy-dom',
92-
setupFiles: [path.resolve(__dirname, 'vitest.setup.dom.tsx')],
139+
setupFiles: [path.resolve(__dirname, 'vitest.setup.dom-light.tsx')],
93140
include: [
94141
'packages/**/*.test.tsx',
95142
'examples/**/*.test.tsx',
96143
...domTsTests,
97144
],
145+
// heavyDomTests render through the registry — they run in `dom-heavy`
146+
// with the full setup, so keep them out of the light project.
147+
exclude: [...sharedExclude, ...heavyDomTests],
148+
},
149+
},
150+
{
151+
extends: true,
152+
test: {
153+
name: 'dom-heavy',
154+
environment: 'happy-dom',
155+
setupFiles: [path.resolve(__dirname, 'vitest.setup.dom.tsx')],
156+
include: [...heavyDomTests],
98157
},
99158
},
100159
path.resolve(__dirname, './apps/console/vitest.config.ts'),

vitest.setup.dom-light.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* ObjectUI — light DOM test setup (default for the `dom` project)
3+
*
4+
* Just enough to render a React component with @testing-library: jsdom-ish
5+
* polyfills (via vitest.setup.base), jest-dom matchers, and RTL auto-cleanup.
6+
* It deliberately does NOT import @object-ui/components / fields /
7+
* plugin-dashboard / plugin-grid or re-register any widgets.
8+
*
9+
* Under `isolate: true` every setup import re-executes per test file, and the
10+
* old single DOM setup pulled those four package graphs into ~300 files that
11+
* mostly never render through the ComponentRegistry — ~3.3s of pure setup per
12+
* file. Tests that DO drive the registry (SchemaRenderer, or page / dashboard /
13+
* grid widgets) run in the `dom-heavy` project instead, which keeps the full
14+
* `vitest.setup.dom.tsx`. See `heavyDomTests` in vitest.config.mts.
15+
*
16+
* If a test renders a component that resolves other components by type and you
17+
* see "<type> not registered" or an element that never appears, it belongs in
18+
* `heavyDomTests`, not here.
19+
*/
20+
21+
import './vitest.setup.base';
22+
import '@testing-library/jest-dom';
23+
import { afterEach } from 'vitest';
24+
import { cleanup } from '@testing-library/react';
25+
26+
// RTL installs its auto-cleanup afterEach only on first import; with modules
27+
// cached across files in a worker, later files would accumulate DOM nodes.
28+
// Register cleanup here so every test file gets an unmount.
29+
afterEach(() => {
30+
cleanup();
31+
});
32+
33+
// jsdom/happy-dom do not implement Element.prototype.scrollIntoView; some
34+
// components call it inside effects. Polyfill as a no-op so component tests
35+
// don't throw inside React's commit phase.
36+
if (typeof Element !== 'undefined' && !Element.prototype.scrollIntoView) {
37+
Element.prototype.scrollIntoView = function () {};
38+
}

vitest.setup.dom.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
/**
2-
* ObjectUI — DOM test setup
2+
* ObjectUI — heavy DOM test setup (the `dom-heavy` project + apps/console)
33
*
4-
* Heavy setup for tests that render React components. Registers ObjectUI
4+
* For tests that render through the ComponentRegistry. Registers ObjectUI
55
* component widgets (text, email, password, textarea, image, html, avatar,
66
* select, slider, grid) and pulls in @object-ui/components, @object-ui/fields,
77
* @object-ui/plugin-dashboard, @object-ui/plugin-grid for their side-effect
88
* registrations.
99
*
10-
* Pure-logic unit tests should use `vitest.setup.base.ts` instead to avoid
11-
* paying this boot cost.
10+
* This graph is expensive (~3.3s/file under `isolate: true`), so it is NOT the
11+
* default. Most `dom` tests use the trimmed `vitest.setup.dom-light.tsx`; only
12+
* the files listed in `heavyDomTests` (vitest.config.mts) run here. Pure-logic
13+
* unit tests use `vitest.setup.base.ts`.
1214
*/
1315

1416
import './vitest.setup.base';

0 commit comments

Comments
 (0)