Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add unit test #12

Merged
merged 4 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dumi/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"include": ["**/*"]
}
8 changes: 8 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ jobs:
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Set up Cypress binary cache
uses: actions/cache@v3
with:
path: ~/.cache/Cypress
key: ${{ runner.os }}-cypress-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-cypress-

- name: Install deps
run: pnpm install

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`ActionIcon > should render correctly 1`] = `
<div>
<button
class="ant-btn ant-btn-default ant-btn-icon-only ant-action-icon"
style="max-height: 22px;"
type="button"
>
<span
class="ant-btn-icon"
>
<svg>
o
</svg>
</span>
</button>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`RecordShortcutInput > should render correctly 1`] = `
<div>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-readonly ant-record-hotkey-input"
>
<input
class="ant-input"
placeholder="Double click to edit"
readonly=""
type="text"
value=""
/>
<span
class="ant-input-suffix"
>
<button
class="ant-btn ant-btn-text ant-btn-sm ant-btn-icon-only ant-action-icon"
style="max-height: 22px;"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="edit"
class="anticon anticon-edit"
role="img"
>
<svg
aria-hidden="true"
data-icon="edit"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"
/>
</svg>
</span>
</span>
</button>
</span>
</span>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { fireEvent, render } from '@test/utils';
import { ConfigProvider } from 'antd';
import ActionIcon from '../ActionIcon';

const Icon = () => <svg>o</svg>;

describe('ActionIcon', () => {
it('should render correctly', () => {
const { container } = render(<ActionIcon icon={<Icon />} />);
expect(container).toMatchSnapshot();
});

it('should render with className', () => {
const { getByRole } = render(<ActionIcon icon={<Icon />} className="test" />);
expect(getByRole('button')).toHaveClass('test');
});

it('should render with onClick', () => {
const onClick = vi.fn();
const { getByRole } = render(<ActionIcon icon={<Icon />} onClick={onClick} />);
fireEvent.click(getByRole('button'));
expect(onClick).toBeCalled();
});

describe('disabled', () => {
it('should render with disabled', () => {
const onClick = vi.fn();
const { getByRole } = render(<ActionIcon icon={<Icon />} disabled onClick={onClick} />);
expect(getByRole('button')).toBeDisabled();
fireEvent.click(getByRole('button'));
expect(onClick).not.toBeCalled();
});

it('should render with disabled by antd ConfigProvider', () => {
const { getByRole } = render(
<ConfigProvider componentDisabled>
<ActionIcon icon={<Icon />} />,
</ConfigProvider>,
);
expect(getByRole('button')).toBeDisabled();
});
});

it('should render with style', () => {
const { getByRole } = render(<ActionIcon icon={<Icon />} style={{ color: 'red' }} />);
expect(getComputedStyle(getByRole('button')).color).toBe('rgb(255, 0, 0)');
});

describe('size', () => {
it('should render', () => {
const { getByRole } = render(<ActionIcon icon={<Icon />} size="large" />);
expect(getByRole('button')).toHaveClass('ant-btn-lg');
expect(getComputedStyle(getByRole('button')).maxHeight).toBeFalsy();
});

it.each([void 0, 'small', 'middle'])('should render with size %s', (size: any) => {
const { getByRole } = render(<ActionIcon icon={<Icon />} size={size} />);
expect(getComputedStyle(getByRole('button')).maxHeight).toBeTruthy();
});

it('overwrites max-height', () => {
const { getByRole } = render(
<ActionIcon icon={<Icon />} size="small" style={{ maxHeight: 100 }} />,
);
expect(getComputedStyle(getByRole('button')).maxHeight).toBe('100px');
});

it('should render with size by antd ConfigProvider', () => {
const { getByRole } = render(
<ConfigProvider componentSize="small">
<ActionIcon icon={<Icon />} />,
</ConfigProvider>,
);
expect(getByRole('button')).toHaveClass('ant-btn-sm');
expect(getComputedStyle(getByRole('button')).maxHeight).toBeTruthy();
});
});
});
137 changes: 137 additions & 0 deletions packages/antd-record-hotkey-input/src/__tests__/input.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { fireEvent, render, waitFakeTimer } from '@test/utils';
import { ConfigProvider } from 'antd';
import en_US from 'antd/locale/en_US';
import zh_CN from 'antd/locale/zh_CN';
import RecordShortcutInput from '../RecordShortcutInput';

// 抽离一个录制快捷键的测试用例
async function recordShortcut(input: HTMLElement, container: HTMLElement) {
fireEvent.dblClick(input); // 双击输入框进入编辑模式

await waitFakeTimer();

expect(container.querySelector('.ant-record-hotkey-input-recording')).toBeInTheDocument();

// 按下 shift + a
fireEvent.keyDown(input, { keyCode: 16, shiftKey: true, code: 'ShiftLeft' });
fireEvent.keyDown(input, { keyCode: 65, shiftKey: true, code: 'KeyA' });

// 按下回车结束录制
fireEvent.keyDown(input, { keyCode: 13, code: 'Enter' });

expect(input).toHaveValue('Shift + A');
}

describe('RecordShortcutInput', () => {
it('should render correctly', () => {
const { container } = render(<RecordShortcutInput />);
expect(container).toMatchSnapshot();
});

it('正常录制快捷键', async () => {
const { getByRole, container } = render(<RecordShortcutInput />);

const input = getByRole('textbox');

expect(input).toBeInTheDocument();

await recordShortcut(input, container);
});

it('should clear value when click clear icon', async () => {
const { getByRole, getAllByRole, container } = render(<RecordShortcutInput allowClear />);
const input = getByRole('textbox');

await recordShortcut(input, container);

expect(getAllByRole('button')).toHaveLength(2);

// 点击清除按钮
fireEvent.click(getAllByRole('button').at(-1)!);
expect(input).toHaveValue('');
});

it('should not record when disabled', async () => {
const { getByRole, container } = render(<RecordShortcutInput disabled />);
const input = getByRole('textbox');

fireEvent.dblClick(input);

await waitFakeTimer();

// 断言输入框未进入录制状态
expect(container.querySelector('.ant-record-hotkey-input-recording')).not.toBeInTheDocument();
});

it('should render placeholder correctly', () => {
const { getByPlaceholderText } = render(<RecordShortcutInput placeholder="请输入快捷键" />);
expect(getByPlaceholderText('请输入快捷键')).toBeInTheDocument();
});

it('should render placeholder function correctly', () => {
const placeholder = vi.fn((recording) => (recording ? 'foo' : 'bar'));
const { getByRole } = render(<RecordShortcutInput placeholder={placeholder} />);

const input = getByRole('textbox');

expect(input).toHaveAttribute('placeholder', 'bar');

fireEvent.dblClick(input);

expect(input).toHaveAttribute('placeholder', 'foo');
});

describe('模拟录制一半退出', () => {
it('should stop recording when blur', async () => {
const { getByRole, container } = render(<RecordShortcutInput />);
const input = getByRole('textbox');
fireEvent.dblClick(input);

await waitFakeTimer();

expect(container.querySelector('.ant-record-hotkey-input-recording')).toBeInTheDocument();
fireEvent.blur(input);
fireEvent.keyDown(input, { keyCode: 16, shiftKey: true, code: 'ShiftLeft' });
expect(container.querySelector('.ant-record-hotkey-input-recording')).not.toBeInTheDocument();
expect(input).toHaveValue('');
});

it('should stop recording when press esc', async () => {
const { getByRole, container } = render(<RecordShortcutInput />);
const input = getByRole('textbox');
fireEvent.dblClick(input);

await waitFakeTimer();

expect(container.querySelector('.ant-record-hotkey-input-recording')).toBeInTheDocument();
fireEvent.keyDown(input, { keyCode: 27, code: 'Escape' });
expect(container.querySelector('.ant-record-hotkey-input-recording')).not.toBeInTheDocument();
expect(input).toHaveValue('');
});
});

describe('i18n', () => {
it('should render correct i18n text', () => {
const { getByRole } = render(<RecordShortcutInput />);
expect(getByRole('textbox')).toHaveAttribute('placeholder', 'Double click to edit');
});

it('should render correct i18n text with ConfigProvider', () => {
const { getByRole, rerender } = render(
<ConfigProvider locale={zh_CN}>
<RecordShortcutInput />
</ConfigProvider>,
);

expect(getByRole('textbox')).toHaveAttribute('placeholder', '双击以编辑');

rerender(
<ConfigProvider locale={en_US}>
<RecordShortcutInput />
</ConfigProvider>,
);

expect(getByRole('textbox')).toHaveAttribute('placeholder', 'Double click to edit');
});
});
});
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"types": ["vitest/globals"],
"paths": {
"@@/*": [".dumi/tmp/*"],
"@test/*": ["tests"],
"@test/*": ["tests/*"],
"antd-record-hotkey-input": ["packages/antd-record-hotkey-input/src"],
"react-use-record-hotkey": ["packages/react-use-record-hotkey/src"]
}
Expand Down
1 change: 1 addition & 0 deletions vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineConfig({
alias: {
'antd-record-hotkey-input': resolve(__dirname, './packages/antd-record-hotkey-input/src'),
'react-use-record-hotkey': resolve(__dirname, './packages/react-use-record-hotkey/src'),
"@test": resolve(__dirname, './tests'),
},
coverage: {
reporter: ['text', 'text-summary', 'json', 'lcov'],
Expand Down
Loading