Files
Genarrative/src/components/platform-entry/PlatformProfileRewardCodeRedeemModal.test.tsx
T
kdletters e8c2e8d532 接入填码原生剪贴板读取
邀请码填写和兑换码弹窗在宿主声明 clipboard.readText 时显示粘贴入口

粘贴动作只读取宿主纯文本并填入现有受控输入框,不自动提交或伪造兑换成功

补充填码粘贴回归测试和 profile 剪贴板 helper

更新 Expo/Tauri 宿主壳方案、HostBridge 协议和共享决策记录
2026-06-18 06:48:46 +08:00

120 lines
3.4 KiB
TypeScript

/* @vitest-environment jsdom */
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, describe, expect, test, vi } from 'vitest';
import {
canPasteFromNativeHostClipboard,
readNativeHostClipboardText,
} from './platformProfileHostClipboard';
import { PlatformProfileRewardCodeRedeemModal } from './PlatformProfileRewardCodeRedeemModal';
vi.mock('./platformProfileHostClipboard', () => ({
canPasteFromNativeHostClipboard: vi.fn(() => false),
readNativeHostClipboardText: vi.fn(),
}));
const canPasteFromNativeHostClipboardMock = vi.mocked(
canPasteFromNativeHostClipboard,
);
const readNativeHostClipboardTextMock = vi.mocked(readNativeHostClipboardText);
describe('PlatformProfileRewardCodeRedeemModal', () => {
afterEach(() => {
vi.clearAllMocks();
canPasteFromNativeHostClipboardMock.mockReturnValue(false);
});
test('submits on button click and enter key', async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
const onChange = vi.fn();
render(
<PlatformProfileRewardCodeRedeemModal
value="ab12"
isSubmitting={false}
error={null}
success={null}
onChange={onChange}
onSubmit={onSubmit}
onClose={vi.fn()}
/>,
);
const input = screen.getByRole('textbox', { name: '兑换码' });
await user.type(input, 'c');
await user.keyboard('{Enter}');
await user.click(screen.getByRole('button', { name: '兑换' }));
expect(onChange).toHaveBeenCalled();
expect(onSubmit).toHaveBeenCalledTimes(2);
});
test('disables submit when the code is blank', () => {
render(
<PlatformProfileRewardCodeRedeemModal
value=" "
isSubmitting={false}
error={null}
success={null}
onChange={vi.fn()}
onSubmit={vi.fn()}
onClose={vi.fn()}
/>,
);
expect(
screen.getByRole('button', { name: '兑换' }).hasAttribute('disabled'),
).toBe(true);
expect(screen.queryByRole('button', { name: '粘贴' })).toBeNull();
});
test('can paste reward code from native host clipboard', async () => {
const user = userEvent.setup();
const onChange = vi.fn();
canPasteFromNativeHostClipboardMock.mockReturnValue(true);
readNativeHostClipboardTextMock.mockResolvedValue('reward-2026');
render(
<PlatformProfileRewardCodeRedeemModal
value=""
isSubmitting={false}
error={null}
success={null}
onChange={onChange}
onSubmit={vi.fn()}
onClose={vi.fn()}
/>,
);
await user.click(screen.getByRole('button', { name: '粘贴' }));
expect(readNativeHostClipboardTextMock).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('reward-2026');
});
test('reuses the shared profile modal footer chrome for submit action', () => {
render(
<PlatformProfileRewardCodeRedeemModal
value="ab12"
isSubmitting={false}
error={null}
success={null}
onChange={vi.fn()}
onSubmit={vi.fn()}
onClose={vi.fn()}
/>,
);
const submitButton = screen.getByRole('button', { name: '兑换' });
const footer = submitButton.closest('div');
expect(footer?.className).toContain('border-t');
expect(footer?.className).toContain('pb-5');
expect(footer?.className).toContain('pt-0');
});
});