Files
apes-sidebar/tests/tab-bar-wheel.spec.tsx
T
Menghuan1918 223b2545d9 refactor(shell): retire the plugin's own right panel and free windows
DSH 0.1.5 owns the right column (its native Sidebar hosts every tab type
this plugin registers), so the plugin's own right panel, its width drag /
new-session seed prefs, and the free-window feature are gone. The bottom
workbench is the only plugin-owned surface left, and its expand/collapse
control is registered into DSH's session-header utilities list slot (the
header's corner seat belongs to the native sidebar's own control).

- state: one `bottomSplits` tree; `makeDefaultState()` takes no arguments;
  `toggleBottomPanel` / `openTabInBottomPane` are the only landing
  reducers; `setBottomHeight` and `sanitizeState` share the
  `CONVERSATION_MIN` cap (a persisted tall height no longer snaps on the
  first drag).
- service: every landing in the workbench (creation or focus) expands it;
  `SIDEBAR_FEATURES` drops `'floatWindows'`; the open/close/activate tab
  pools cover the single tree.
- shell: `Sidebar.tsx` renders only the bottom workbench; the toggle lives
  in `sidebar/bottom-toggle.tsx`; layout-push pushes height only (no width,
  no `--dsh-sidebar-width`).
- prefs: `openByDefault` / `defaultWidthPercent` / `changesDiffFloat`
  removed from the schema, defaults, client parser, settings page, and all
  20 dictionaries; the retired keys in stored documents are ignored.
- tests: free-window specs deleted, panel/float assertions dropped,
  remaining suites adapted (124 files / 1287 tests); the mount lane asserts
  the header toggle and sweeps the native sidebar's guide page.
- docs: README / README_EN / guide §0,§11 / AGENTS test map.
2026-09-09 15:18:06 +08:00

154 lines
4.8 KiB
TypeScript

/**
* Tab-strip wheel-scroll tests: a plain mouse wheel over the tab row emits
* deltaY, which a horizontal scrollport (`overflow-x: auto`) never consumes —
* the handler in TabBar translates it into horizontal scrolling. It must
* consume the event ONLY when the strip actually overflows (else the page
* scrolls normally), must leave modifier-key gestures to the browser (shift
* = native horizontal scroll, ctrl/cmd = zoom), and must scale deltaMode
* lines/pages like the browser would.
*/
// @vitest-environment jsdom
import { afterEach, describe, expect, it } from 'vitest'
import { createElement } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { act } from 'react-dom/test-utils'
// The act() environment flag (React 18.2 reads it before flushing effects).
import { setupReactAct } from './test-utils.ts'
setupReactAct()
import { TabBar } from '../src/client/TabBar.tsx'
import type { SidebarTab } from '../src/client/state.ts'
function mountBar(overflow: boolean): { list: HTMLElement; unmount: () => void } {
const container = document.createElement('div')
document.body.append(container)
const tabs: SidebarTab[] = [
{ id: 't1', type: 'explorer', title: 'Explorer' },
{ id: 't2', type: 'git', title: 'Git' },
]
const root: Root = createRoot(container)
act(() => {
root.render(createElement(TabBar, {
paneId: 'pane:1',
tabs,
active: 't1',
onActivate: () => {},
onClose: () => {},
onNewTab: () => {},
newTabOptions: [],
onDropTab: () => {},
}))
})
const list = container.querySelector('[class*="tabList"]') as HTMLElement
expect(list).not.toBeNull()
// jsdom has no layout: stub the scrollport geometry to simulate overflow.
Object.defineProperty(list, 'scrollWidth', { value: overflow ? 600 : 200, configurable: true })
Object.defineProperty(list, 'clientWidth', { value: 200, configurable: true })
return {
list,
unmount: () => {
act(() => { root.unmount() })
container.remove()
},
}
}
/** Dispatch a wheel event on the strip and return whether it was consumed. */
function wheel(list: HTMLElement, init: WheelEventInit): WheelEvent {
const event = new WheelEvent('wheel', { cancelable: true, bubbles: true, ...init })
list.dispatchEvent(event)
return event
}
afterEach(() => {
document.body.innerHTML = ''
})
describe('TabBar wheel → horizontal scroll', () => {
it('translates a vertical wheel delta into scrollLeft when the strip overflows', () => {
const { list, unmount } = mountBar(true)
try {
list.scrollLeft = 0
const event = wheel(list, { deltaY: 120 })
expect(event.defaultPrevented).toBe(true)
expect(list.scrollLeft).toBe(120)
} finally {
unmount()
}
})
it('scans left for a negative delta', () => {
const { list, unmount } = mountBar(true)
try {
list.scrollLeft = 500
const event = wheel(list, { deltaY: -80 })
expect(event.defaultPrevented).toBe(true)
expect(list.scrollLeft).toBe(420)
} finally {
unmount()
}
})
it('does not consume the event when the strip does not overflow', () => {
const { list, unmount } = mountBar(false)
try {
list.scrollLeft = 0
const event = wheel(list, { deltaY: 120 })
expect(event.defaultPrevented).toBe(false)
expect(list.scrollLeft).toBe(0)
} finally {
unmount()
}
})
it('leaves modifier-key gestures to the browser', () => {
const { list, unmount } = mountBar(true)
try {
list.scrollLeft = 0
const ctrl = wheel(list, { deltaY: 120, ctrlKey: true })
const shift = wheel(list, { deltaY: 120, shiftKey: true })
const meta = wheel(list, { deltaY: 120, metaKey: true })
expect(ctrl.defaultPrevented).toBe(false)
expect(shift.defaultPrevented).toBe(false)
expect(meta.defaultPrevented).toBe(false)
expect(list.scrollLeft).toBe(0)
} finally {
unmount()
}
})
it('scales line-mode deltas by 16px per line', () => {
const { list, unmount } = mountBar(true)
try {
list.scrollLeft = 0
const event = wheel(list, { deltaY: 3, deltaMode: 1 })
expect(event.defaultPrevented).toBe(true)
expect(list.scrollLeft).toBe(48)
} finally {
unmount()
}
})
it('adds a horizontal trackpad delta to the scroll', () => {
const { list, unmount } = mountBar(true)
try {
list.scrollLeft = 0
const event = wheel(list, { deltaX: 40 })
expect(event.defaultPrevented).toBe(true)
expect(list.scrollLeft).toBe(40)
} finally {
unmount()
}
})
it('stops scrolling after unmount', () => {
const { list, unmount } = mountBar(true)
list.scrollLeft = 0
unmount()
const event = wheel(list, { deltaY: 120 })
expect(event.defaultPrevented).toBe(false)
expect(list.scrollLeft).toBe(0)
})
})