test: keep Desktop pane-focus setup outside cold imports
This commit is contained in:
@@ -1,21 +1,24 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
async function setup() {
|
import { paneMirror } from '@/app/chat/pane-mirror'
|
||||||
const tree = await import('@/components/pane-shell/tree/store')
|
import * as model from '@/components/pane-shell/tree/model'
|
||||||
const model = await import('@/components/pane-shell/tree/model')
|
import * as tree from '@/components/pane-shell/tree/store'
|
||||||
const { registry } = await import('@/contrib/registry')
|
import { registry } from '@/contrib/registry'
|
||||||
const session = await import('@/store/session')
|
import { applyDesktopOverlay } from '@/store/profile-share'
|
||||||
const states = await import('@/store/session-states')
|
import * as session from '@/store/session'
|
||||||
const { paneMirror } = await import('@/app/chat/pane-mirror')
|
import * as states from '@/store/session-states'
|
||||||
|
|
||||||
registry.register({
|
// These app-lifetime watchers have no unsubscribe API. Install them once in
|
||||||
|
// Vitest's isolated file graph, not once per case (which accumulates listeners).
|
||||||
|
beforeAll(() => {
|
||||||
|
const disposeWorkspace = registry.register({
|
||||||
area: 'panes',
|
area: 'panes',
|
||||||
data: { placement: 'main', uncloseable: true },
|
data: { placement: 'main', uncloseable: true },
|
||||||
id: 'workspace',
|
id: 'workspace',
|
||||||
render: () => null,
|
render: () => null,
|
||||||
title: 'Chat'
|
title: 'Chat'
|
||||||
})
|
})
|
||||||
tree.declareDefaultTree(model.group(['workspace'], { active: 'workspace', id: 'main' }))
|
|
||||||
tree.watchContributedPanes()
|
tree.watchContributedPanes()
|
||||||
paneMirror({
|
paneMirror({
|
||||||
source: states.$sessionTiles,
|
source: states.$sessionTiles,
|
||||||
@@ -27,6 +30,23 @@ async function setup() {
|
|||||||
render: () => null,
|
render: () => null,
|
||||||
close: states.closeSessionTile
|
close: states.closeSessionTile
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
return disposeWorkspace
|
||||||
|
})
|
||||||
|
|
||||||
|
function resetState() {
|
||||||
|
// Empty the source while the mirror is live so it also disposes its pane.
|
||||||
|
states.discardSessionTile('canonical-chat')
|
||||||
|
tree.$layoutTree.set(null)
|
||||||
|
tree.$activeTreeGroup.set(null)
|
||||||
|
tree.$activePresetId.set('default')
|
||||||
|
session.$selectedStoredSessionId.set(null)
|
||||||
|
session.$lastReadAtBySessionId.set({})
|
||||||
|
window.localStorage.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
tree.declareDefaultTree(model.group(['workspace'], { active: 'workspace', id: 'main' }))
|
||||||
session.$selectedStoredSessionId.set('previous-chat')
|
session.$selectedStoredSessionId.set('previous-chat')
|
||||||
|
|
||||||
const scope = {
|
const scope = {
|
||||||
@@ -42,17 +62,19 @@ async function setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('focusing a saved Bot Chat requires a visible pane', () => {
|
describe('focusing a saved Bot Chat requires a visible pane', () => {
|
||||||
let ctx: Awaited<ReturnType<typeof setup>>
|
let ctx: ReturnType<typeof setup>
|
||||||
const paneId = 'session-tile:canonical-chat'
|
const paneId = 'session-tile:canonical-chat'
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(() => {
|
||||||
window.localStorage.clear()
|
resetState()
|
||||||
vi.resetModules()
|
ctx = setup()
|
||||||
ctx = await setup()
|
expect(model.findGroupOfPane(tree.$layoutTree.get()!, 'workspace')?.id).toBe('main')
|
||||||
|
expect(states.$sessionTiles.get().map(tile => tile.storedSessionId)).toEqual(['canonical-chat'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('re-adopts a saved tab after a profile overlay replaces the layout', async () => {
|
afterEach(resetState)
|
||||||
const { applyDesktopOverlay } = await import('@/store/profile-share')
|
|
||||||
|
it('re-adopts a saved tab after a profile overlay replaces the layout', () => {
|
||||||
const { model, scope, states, tree } = ctx
|
const { model, scope, states, tree } = ctx
|
||||||
const saved = states.$sessionTiles.get()
|
const saved = states.$sessionTiles.get()
|
||||||
applyDesktopOverlay('imported-profile', {
|
applyDesktopOverlay('imported-profile', {
|
||||||
|
|||||||
52
evals/desktop-pane-fixture/README.md
Normal file
52
evals/desktop-pane-fixture/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Saved-pane fixture import-delay control
|
||||||
|
|
||||||
|
From a root `npm ci` installation, run in `apps/desktop`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx vitest run --config ../../evals/desktop-pane-fixture/vitest.config.mts --reporter verbose
|
||||||
|
npx vitest run --project ui src/store/session-pane-focus.test.ts --sequence.shuffle.tests --sequence.seed=12345 --reporter verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
The Vite plugin appends an 11-second asynchronous wait to the real pane-tree
|
||||||
|
store's evaluation. It does not mock pane functions, inspect source text in an
|
||||||
|
assertion, lower a deadline, or alter the two test bodies. The UI test deadline
|
||||||
|
remains 15 seconds and the default hook deadline remains 10 seconds. This is a
|
||||||
|
scheduling-controlled reproduction of import latency, not a claim that a local
|
||||||
|
machine reproduced the original CI load unassisted. It is opt-in, not a slow CI
|
||||||
|
test.
|
||||||
|
|
||||||
|
For A/B, apply this eval directory to the parent revision and run the same
|
||||||
|
command, then run on the fixture fix. Base evaluates the graph inside each
|
||||||
|
`beforeEach`; fixed evaluates it once during test collection. The delay marker
|
||||||
|
appears twice on base and once on fixed. Expected: base fails with `Hook timed
|
||||||
|
out in 10000ms`; fixed runs both behavioral assertions successfully.
|
||||||
|
|
||||||
|
Measured on the initial base `fb3446a281e`:
|
||||||
|
|
||||||
|
| Control | Result |
|
||||||
|
| --- | --- |
|
||||||
|
| Base + evaluation delay | 2 hook timeouts, 20.04s test phase |
|
||||||
|
| Fixed + identical delay | 2 passed, 5ms test phase; 11.84s import phase |
|
||||||
|
| Fixed normal and reversed test order (seed 12345) | 2 passed in each order |
|
||||||
|
| Store directory, 8 workers | 123 files / 1491 tests passed |
|
||||||
|
| Complete UI project, 8 workers | 753 files / 7355 assertions passed, but exit 1 from an unrelated Radix focus-scope timer's wrong-realm Event in `statusbar-visibility.test.tsx` |
|
||||||
|
|
||||||
|
Mutation controls, applied separately to `session-states.ts` and then restored:
|
||||||
|
|
||||||
|
- Remove `revealTreePane(paneId)` in `focusOpenSession`: the overlay re-adoption
|
||||||
|
case fails (`null` instead of `canonical-chat`); the miss case passes.
|
||||||
|
- Make `focusWorkspaceOwnerSessionTile` return the stored id regardless of
|
||||||
|
`focusOpenSession`'s result: the miss case fails (`canonical-chat` instead of
|
||||||
|
`null`); the overlay case passes.
|
||||||
|
|
||||||
|
The fixture uses the real registry, tree watcher, pane mirror, overlay, tile
|
||||||
|
store, and focus helpers. Install app-lifetime watchers once per isolated Vitest
|
||||||
|
file graph. Discard the fixture tile through the real action to clear both the
|
||||||
|
reactive source and its persisted in-memory bucket; reset layout, focus, preset,
|
||||||
|
selection, read baseline, and localStorage between cases. The mirror observes the
|
||||||
|
discard and unregisters its contribution. No production API needs a test-only
|
||||||
|
reset or disposer.
|
||||||
|
|
||||||
|
If the host has exhausted inotify watches, prefix commands with
|
||||||
|
`CHOKIDAR_USEPOLLING=true`; this changes file watching, not test deadlines. The
|
||||||
|
recorded store/full-suite and final focused controls used that workaround.
|
||||||
28
evals/desktop-pane-fixture/vitest.config.mts
Normal file
28
evals/desktop-pane-fixture/vitest.config.mts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// From apps/desktop: npx vitest run --config ../../evals/desktop-pane-fixture/vitest.config.mts
|
||||||
|
// Replay a slow module evaluation without changing any pane behavior or deadlines.
|
||||||
|
import { defineConfig, mergeConfig } from 'vitest/config'
|
||||||
|
|
||||||
|
import desktopConfig from '../../apps/desktop/vite.config.ts'
|
||||||
|
|
||||||
|
export default defineConfig(env =>
|
||||||
|
mergeConfig(desktopConfig(env), {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: 'slow-pane-store-evaluation',
|
||||||
|
enforce: 'post',
|
||||||
|
transform(code: string, id: string) {
|
||||||
|
if (id.endsWith('/components/pane-shell/tree/store.ts')) {
|
||||||
|
return `${code}\nconsole.info('[pane-fixture] delaying real tree store evaluation by 11000ms');\nawait new Promise(resolve => setTimeout(resolve, 11000));\n`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
setupFiles: ['./vitest.setup.ts'],
|
||||||
|
include: ['src/store/session-pane-focus.test.ts'],
|
||||||
|
globals: true,
|
||||||
|
testTimeout: 15_000
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user