Loading...
Loading...
Compare original and translation side by side
export const SearchAndSelect: Story = {
args: { options: mockData },
tags: ['test', 'interaction'],
play: async ({ canvasElement, args, step }) => {
const canvas = within(canvasElement);
await step('Search for option', async () => {
const input = canvas.getByTestId('search-input');
await userEvent.type(input, 'react');
});
await step('Select filtered option', async () => {
const option = await canvas.findByTestId('option-react');
await userEvent.click(option);
});
await step('Verify selection', async () => {
await expect(args.onChange).toHaveBeenCalledWith(
expect.arrayContaining([expect.objectContaining({ value: 'react' })]),
);
});
},
};export const SearchAndSelect: Story = {
args: { options: mockData },
tags: ['test', 'interaction'],
play: async ({ canvasElement, args, step }) => {
const canvas = within(canvasElement);
await step('Search for option', async () => {
const input = canvas.getByTestId('search-input');
await userEvent.type(input, 'react');
});
await step('Select filtered option', async () => {
const option = await canvas.findByTestId('option-react');
await userEvent.click(option);
});
await step('Verify selection', async () => {
await expect(args.onChange).toHaveBeenCalledWith(
expect.arrayContaining([expect.objectContaining({ value: 'react' })]),
);
});
},
};canvasElementargsstepwithin(canvasElement)step()await['test', 'interaction']canvasElementargsstepwithin(canvasElement)step()['test', 'interaction']| Priority | Query | Use When |
|---|---|---|
| 1st | | Element has an accessible role (button, textbox, etc.) |
| 2nd | | Form elements with associated labels |
| 3rd | | Inputs with placeholder text |
| Last | | No accessible query available |
// Preferred - accessible queries
const button = canvas.getByRole('button', { name: /submit/i });
const input = canvas.getByLabelText('Email address');
// Acceptable - when role/label not available
const dropdown = canvas.getByTestId('multiselect-dropdown');
// Never use - fragile selectors
const element = canvas.getByClassName('my-class'); // Breaks on style changes| 优先级 | 查询方法 | 适用场景 |
|---|---|---|
| 第一优先级 | | 元素具备可访问角色(如按钮、文本框等) |
| 第二优先级 | | 关联了标签的表单元素 |
| 第三优先级 | | 带有占位文本的输入框 |
| 最后选择 | | 没有可用的可访问查询方法时 |
// 推荐使用 - 可访问查询
const button = canvas.getByRole('button', { name: /submit/i });
const input = canvas.getByLabelText('Email address');
// 可接受 - 当角色/标签不可用时
const dropdown = canvas.getByTestId('multiselect-dropdown');
// 禁止使用 - 脆弱的选择器
const element = canvas.getByClassName('my-class'); // 样式变更时会失效// After interactions that render new elements, use findBy (auto-waits)
await userEvent.click(openButton);
const dropdown = await canvas.findByTestId('dropdown');
// For assertions on async state changes, use waitFor
await waitFor(() => {
expect(canvas.getByText('Loading...')).not.toBeInTheDocument();
});
// Always await userEvent calls
await userEvent.click(button); // Correct
await userEvent.type(input, 'x'); // Correct
userEvent.click(button); // WRONG - missing await// 在会渲染新元素的交互后,使用findBy(自动等待)
await userEvent.click(openButton);
const dropdown = await canvas.findByTestId('dropdown');
// 针对异步状态变更的断言,使用waitFor
await waitFor(() => {
expect(canvas.getByText('Loading...')).not.toBeInTheDocument();
});
// 始终await userEvent调用
await userEvent.click(button); // 正确
await userEvent.type(input, 'x'); // 正确
userEvent.click(button); // 错误 - 缺少awaitexport const SubmitLoginForm: Story = {
args: { onSubmit: fn() },
tags: ['test', 'interaction'],
play: async ({ canvasElement, args, step }) => {
const canvas = within(canvasElement);
await step('Fill in credentials', async () => {
await userEvent.type(canvas.getByLabelText('Email'), 'user@example.com');
await userEvent.type(canvas.getByLabelText('Password'), 'password123');
});
await step('Submit the form', async () => {
await userEvent.click(canvas.getByRole('button', { name: /sign in/i }));
});
await step('Verify submission', async () => {
await expect(args.onSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123',
});
});
},
};export const SubmitLoginForm: Story = {
args: { onSubmit: fn() },
tags: ['test', 'interaction'],
play: async ({ canvasElement, args, step }) => {
const canvas = within(canvasElement);
await step('填写凭证', async () => {
await userEvent.type(canvas.getByLabelText('Email'), 'user@example.com');
await userEvent.type(canvas.getByLabelText('Password'), 'password123');
});
await step('提交表单', async () => {
await userEvent.click(canvas.getByRole('button', { name: /sign in/i }));
});
await step('验证提交结果', async () => {
await expect(args.onSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123',
});
});
},
};export const KeyboardNavigation: Story = {
args: { options: mockOptions },
tags: ['test', 'interaction'],
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('Open dropdown with keyboard', async () => {
const combobox = canvas.getByRole('combobox');
combobox.focus();
await userEvent.keyboard('{ArrowDown}');
});
await step('Navigate and select option', async () => {
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{Enter}');
});
await step('Verify selection is displayed', async () => {
const selected = canvas.getByRole('combobox');
await expect(selected).toHaveTextContent('Option 2');
});
},
};export const KeyboardNavigation: Story = {
args: { options: mockOptions },
tags: ['test', 'interaction'],
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('用键盘打开下拉框', async () => {
const combobox = canvas.getByRole('combobox');
combobox.focus();
await userEvent.keyboard('{ArrowDown}');
});
await step('导航并选择选项', async () => {
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{Enter}');
});
await step('验证选中项已显示', async () => {
const selected = canvas.getByRole('combobox');
await expect(selected).toHaveTextContent('Option 2');
});
},
};export const InvalidEmail: Story = {
tags: ['test', 'interaction'],
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('Enter invalid email', async () => {
await userEvent.type(canvas.getByLabelText('Email'), 'not-an-email');
});
await step('Submit and verify error', async () => {
await userEvent.click(canvas.getByRole('button', { name: /submit/i }));
const errorMsg = await canvas.findByRole('alert');
await expect(errorMsg).toHaveTextContent(/invalid email/i);
});
},
};export const InvalidEmail: Story = {
tags: ['test', 'interaction'],
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('输入无效邮箱', async () => {
await userEvent.type(canvas.getByLabelText('Email'), 'not-an-email');
});
await step('提交并验证错误提示', async () => {
await userEvent.click(canvas.getByRole('button', { name: /submit/i }));
const errorMsg = await canvas.findByRole('alert');
await expect(errorMsg).toHaveTextContent(/invalid email/i);
});
},
};| Check | What to Look For |
|---|---|
| Structure | Uses |
| Queries | Prefers |
| Async | All |
| Async elements | Uses |
| Tags | Story has |
| Assertions | Uses |
| Canvas | Uses |
// Missing await
userEvent.click(button); // Fix: await userEvent.click(button);
// Missing step() grouping
play: async ({ canvasElement }) => { // Fix: wrap in step() calls
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole('button'));
await expect(...).toBe(...);
};
// Using getBy for async-rendered elements
await userEvent.click(openBtn);
const menu = canvas.getByTestId('menu'); // Fix: await canvas.findByTestId('menu');
// Missing tags
export const MyTest: Story = { // Fix: add tags: ['test', 'interaction']
play: async ({ canvasElement }) => { ... },
};| 检查项 | 检查内容 |
|---|---|
| 结构 | 使用 |
| 查询方法 | 优先使用 |
| 异步处理 | 所有 |
| 异步元素 | 对交互后才出现的元素使用 |
| 标签 | Story添加了 |
| 断言 | 使用 |
| Canvas | 使用 |
// 缺少await
userEvent.click(button); // 修复:await userEvent.click(button);
// 缺少step()分组
play: async ({ canvasElement }) => { // 修复:用step()调用包裹
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole('button'));
await expect(...).toBe(...);
};
// 对异步渲染的元素使用getBy
await userEvent.click(openBtn);
const menu = canvas.getByTestId('menu'); // 修复:await canvas.findByTestId('menu');
// 缺少标签
export const MyTest: Story = { // 修复:添加tags: ['test', 'interaction']
play: async ({ canvasElement }) => { ... },
};