open-design/apps/web/tests/components/FileWorkspace.test.tsx

247 lines
7.3 KiB
TypeScript
Raw Normal View History

import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import { FileWorkspace, scrollWorkspaceTabsWithWheel } from '../../src/components/FileWorkspace';
import { projectSplitClassName } from '../../src/components/ProjectView';
describe('FileWorkspace upload input', () => {
it('keeps the Design Files picker aligned with drag-and-drop file support', () => {
const markup = renderToStaticMarkup(
<FileWorkspace
projectId="project-1"
files={[]}
liveArtifacts={[]}
onRefreshFiles={vi.fn()}
isDeck={false}
tabsState={{ tabs: [], active: null }}
onTabsStateChange={vi.fn()}
/>,
);
expect(markup).toContain('data-testid="design-files-upload-input"');
expect(markup).not.toContain('accept=');
});
it('keeps focus mode controls in the workspace tab bar', () => {
const markup = renderToStaticMarkup(
<FileWorkspace
projectId="project-1"
files={[]}
liveArtifacts={[]}
onRefreshFiles={vi.fn()}
isDeck={false}
tabsState={{ tabs: [], active: null }}
onTabsStateChange={vi.fn()}
focusMode={false}
onFocusModeChange={vi.fn()}
/>,
);
expect(markup).toContain('data-testid="workspace-focus-toggle"');
expect(markup).toContain('Focus workspace');
});
it('keeps the focus mode action outside the horizontally scrollable tablist', () => {
const markup = renderToStaticMarkup(
<FileWorkspace
projectId="project-1"
files={[]}
liveArtifacts={[]}
onRefreshFiles={vi.fn()}
isDeck={false}
tabsState={{ tabs: [], active: null }}
onTabsStateChange={vi.fn()}
focusMode={false}
onFocusModeChange={vi.fn()}
/>,
);
expect(markup).toContain('class="ws-tabs-shell"');
expect(markup).toContain('class="ws-tabs-actions"');
expect(markup).toMatch(
/<div class="ws-tabs-bar" role="tablist"[^>]*>[\s\S]*?<\/div><div class="ws-tabs-actions">/,
);
});
it('labels the same workspace control as chat restore while focused', () => {
const markup = renderToStaticMarkup(
<FileWorkspace
projectId="project-1"
files={[]}
liveArtifacts={[]}
onRefreshFiles={vi.fn()}
isDeck={false}
tabsState={{ tabs: [], active: null }}
onTabsStateChange={vi.fn()}
focusMode
onFocusModeChange={vi.fn()}
/>,
);
expect(markup).toContain('Show chat');
});
});
describe('projectSplitClassName', () => {
it('marks the project split as focused so the chat pane can collapse globally', () => {
expect(projectSplitClassName(false)).toBe('split');
expect(projectSplitClassName(true)).toBe('split split-focus');
});
});
describe('scrollWorkspaceTabsWithWheel', () => {
function makeTabBar(scrollLeft: number, scrollWidth = 400, clientWidth = 200) {
return { scrollLeft, scrollWidth, clientWidth } as HTMLDivElement;
}
function makeClampedTabBar(scrollLeft: number, scrollWidth = 400, clientWidth = 200) {
let value = scrollLeft;
return {
scrollWidth,
clientWidth,
get scrollLeft() {
return value;
},
set scrollLeft(next: number) {
value = Math.min(Math.max(next, 0), scrollWidth - clientWidth);
},
} as HTMLDivElement;
}
it('maps vertical mouse wheel movement to horizontal tab scrolling', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(12);
const event = {
ctrlKey: false,
deltaMode: 0,
deltaX: 0,
deltaY: 40,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(52);
expect(preventDefault).toHaveBeenCalledTimes(1);
});
it('supports reverse vertical wheel movement', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(52);
const event = {
ctrlKey: false,
deltaMode: 0,
deltaX: 0,
deltaY: -40,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(12);
expect(preventDefault).toHaveBeenCalledTimes(1);
});
it('normalizes line-based wheel deltas to useful pixel movement', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(12);
const event = {
ctrlKey: false,
deltaMode: 1,
deltaX: 0,
deltaY: 3,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(60);
expect(preventDefault).toHaveBeenCalledTimes(1);
});
it('normalizes page-based wheel deltas to useful pixel movement', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(12, 600, 200);
const event = {
ctrlKey: false,
deltaMode: 2,
deltaX: 0,
deltaY: 1,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(172);
expect(preventDefault).toHaveBeenCalledTimes(1);
});
it('leaves native horizontal wheel gestures alone', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(12);
const event = {
ctrlKey: false,
deltaMode: 0,
deltaX: 50,
deltaY: 10,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(12);
expect(preventDefault).not.toHaveBeenCalled();
});
it('leaves ctrl-wheel zoom gestures alone', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(12);
const event = {
ctrlKey: true,
deltaMode: 0,
deltaX: 0,
deltaY: 40,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(12);
expect(preventDefault).not.toHaveBeenCalled();
});
it('does not intercept vertical wheel movement when tabs do not overflow', () => {
const preventDefault = vi.fn();
const currentTarget = makeTabBar(12, 200, 200);
const event = {
ctrlKey: false,
deltaMode: 0,
deltaX: 0,
deltaY: 40,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(12);
expect(preventDefault).not.toHaveBeenCalled();
});
it('lets page scrolling continue when the tab bar is already at the wheel boundary', () => {
const preventDefault = vi.fn();
const currentTarget = makeClampedTabBar(200, 400, 200);
const event = {
ctrlKey: false,
deltaMode: 0,
deltaX: 0,
deltaY: 40,
preventDefault,
} as unknown as WheelEvent;
scrollWorkspaceTabsWithWheel(currentTarget, event);
expect(currentTarget.scrollLeft).toBe(200);
expect(preventDefault).not.toHaveBeenCalled();
});
});