126 lines
3.9 KiB
TypeScript
126 lines
3.9 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { act, fireEvent, render, screen } from '@testing-library/react';
|
|
import type { FormEvent } from 'react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { EditorAgentDraftTextarea } from './EditorAgentDraftTextarea.tsx';
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('EditorAgentDraftTextarea', () => {
|
|
it('uses native field sizing when the browser supports it', () => {
|
|
const supports = vi.fn().mockReturnValue(true);
|
|
const resizeObserver = vi.fn();
|
|
const onChange = vi.fn();
|
|
vi.stubGlobal('CSS', { supports });
|
|
vi.stubGlobal('ResizeObserver', resizeObserver);
|
|
|
|
render(<EditorAgentDraftTextarea value="" onChange={onChange} />);
|
|
|
|
const input = screen.getByLabelText(
|
|
'发送给画布 Agent',
|
|
) as HTMLTextAreaElement;
|
|
expect(supports).toHaveBeenCalledWith('field-sizing', 'content');
|
|
expect(resizeObserver).not.toHaveBeenCalled();
|
|
expect(input.className).toContain('[field-sizing:content]');
|
|
expect(input.className).toContain('min-h-10');
|
|
expect(input.className).toContain('max-h-32');
|
|
expect(input.className).toContain('overflow-y-auto');
|
|
expect(input.style.height).toBe('');
|
|
|
|
fireEvent.change(input, { target: { value: '新草稿' } });
|
|
expect(onChange).toHaveBeenCalledWith('新草稿');
|
|
});
|
|
|
|
it('falls back to measured sizing and remeasures when width changes', () => {
|
|
let resizeObserverCallback!: ResizeObserverCallback;
|
|
const observe = vi.fn();
|
|
const disconnect = vi.fn();
|
|
vi.stubGlobal('CSS', { supports: vi.fn().mockReturnValue(false) });
|
|
vi.stubGlobal(
|
|
'ResizeObserver',
|
|
vi.fn().mockImplementation((callback: ResizeObserverCallback) => {
|
|
resizeObserverCallback = callback;
|
|
return {
|
|
observe,
|
|
unobserve: vi.fn(),
|
|
disconnect,
|
|
};
|
|
}),
|
|
);
|
|
|
|
const { rerender, unmount } = render(
|
|
<EditorAgentDraftTextarea value="" onChange={vi.fn()} />,
|
|
);
|
|
const input = screen.getByLabelText(
|
|
'发送给画布 Agent',
|
|
) as HTMLTextAreaElement;
|
|
expect(observe).toHaveBeenCalledWith(input);
|
|
let contentHeight = 92;
|
|
Object.defineProperty(input, 'offsetHeight', {
|
|
configurable: true,
|
|
get: () => {
|
|
const styleHeight = Number.parseFloat(input.style.height);
|
|
return Number.isNaN(styleHeight) ? 42 : styleHeight;
|
|
},
|
|
});
|
|
Object.defineProperty(input, 'clientHeight', {
|
|
configurable: true,
|
|
get: () => input.offsetHeight - 2,
|
|
});
|
|
Object.defineProperty(input, 'scrollHeight', {
|
|
configurable: true,
|
|
get: () => contentHeight,
|
|
});
|
|
|
|
rerender(
|
|
<EditorAgentDraftTextarea
|
|
value="第一行\n第二行\n第三行\n第四行\n第五行"
|
|
onChange={vi.fn()}
|
|
/>,
|
|
);
|
|
expect(input.style.height).toBe('94px');
|
|
expect(input.style.overflowY).toBe('hidden');
|
|
expect(input.scrollHeight).toBeLessThanOrEqual(input.clientHeight);
|
|
|
|
contentHeight = 180;
|
|
act(() => {
|
|
resizeObserverCallback(
|
|
[
|
|
{
|
|
target: input,
|
|
contentRect: { width: 180 },
|
|
} as unknown as ResizeObserverEntry,
|
|
],
|
|
{} as ResizeObserver,
|
|
);
|
|
});
|
|
expect(input.style.height).toBe('128px');
|
|
expect(input.style.overflowY).toBe('auto');
|
|
|
|
unmount();
|
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('submits on Enter and keeps Shift+Enter for line breaks', () => {
|
|
const onSubmit = vi.fn((event: FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault();
|
|
});
|
|
render(
|
|
<form onSubmit={onSubmit}>
|
|
<EditorAgentDraftTextarea value="草稿" onChange={vi.fn()} />
|
|
</form>,
|
|
);
|
|
|
|
const input = screen.getByLabelText('发送给画布 Agent');
|
|
fireEvent.keyDown(input, { key: 'Enter', shiftKey: true });
|
|
expect(onSubmit).not.toHaveBeenCalled();
|
|
|
|
fireEvent.keyDown(input, { key: 'Enter' });
|
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|