e8c2e8d532
邀请码填写和兑换码弹窗在宿主声明 clipboard.readText 时显示粘贴入口 粘贴动作只读取宿主纯文本并填入现有受控输入框,不自动提交或伪造兑换成功 补充填码粘贴回归测试和 profile 剪贴板 helper 更新 Expo/Tauri 宿主壳方案、HostBridge 协议和共享决策记录
173 lines
5.2 KiB
TypeScript
173 lines
5.2 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { render, screen, within } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
import type { ProfileReferralInviteCenterResponse } from '../../../packages/shared/src/contracts/runtime';
|
|
import {
|
|
canPasteFromNativeHostClipboard,
|
|
readNativeHostClipboardText,
|
|
} from './platformProfileHostClipboard';
|
|
import { PlatformProfileReferralModal } from './PlatformProfileReferralModal';
|
|
|
|
vi.mock('./platformProfileHostClipboard', () => ({
|
|
canPasteFromNativeHostClipboard: vi.fn(() => false),
|
|
readNativeHostClipboardText: vi.fn(),
|
|
}));
|
|
|
|
const canPasteFromNativeHostClipboardMock = vi.mocked(
|
|
canPasteFromNativeHostClipboard,
|
|
);
|
|
const readNativeHostClipboardTextMock = vi.mocked(readNativeHostClipboardText);
|
|
|
|
function buildCenter(
|
|
overrides: Partial<ProfileReferralInviteCenterResponse> = {},
|
|
): ProfileReferralInviteCenterResponse {
|
|
return {
|
|
inviteCode: 'ABCD1234',
|
|
inviteLinkPath: '/invite/ABCD1234',
|
|
invitedCount: 1,
|
|
rewardedInviteCount: 1,
|
|
todayInviterRewardCount: 1,
|
|
todayInviterRewardRemaining: 9,
|
|
rewardPoints: 66,
|
|
invitedUsers: [
|
|
{
|
|
userId: 'user-2',
|
|
displayName: '海盐',
|
|
avatarUrl: null,
|
|
boundAt: '2026-06-10T08:00:00.000Z',
|
|
},
|
|
],
|
|
hasRedeemedCode: false,
|
|
boundInviterUserId: null,
|
|
boundAt: null,
|
|
updatedAt: '2026-06-10T08:00:00.000Z',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('PlatformProfileReferralModal', () => {
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
canPasteFromNativeHostClipboardMock.mockReturnValue(false);
|
|
});
|
|
|
|
test('renders invite panel with shared profile content', () => {
|
|
render(
|
|
<PlatformProfileReferralModal
|
|
panel="invite"
|
|
center={buildCenter()}
|
|
isLoading={false}
|
|
isSubmittingRedeem={false}
|
|
redeemCode=""
|
|
copyInviteState="idle"
|
|
error={null}
|
|
success={null}
|
|
onClose={vi.fn()}
|
|
onCopyInvite={vi.fn()}
|
|
onRedeemCodeChange={vi.fn()}
|
|
onSubmitRedeemCode={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
const dialog = screen.getByRole('dialog', { name: '邀请好友' });
|
|
|
|
expect(within(dialog).getByText('邀请码')).toBeTruthy();
|
|
expect(within(dialog).getByText('ABCD1234')).toBeTruthy();
|
|
expect(within(dialog).getByText('海盐')).toBeTruthy();
|
|
expect(within(dialog).getByText('成功邀请')).toBeTruthy();
|
|
expect(
|
|
within(dialog).getByRole('button', { name: /复制邀请/u }),
|
|
).toBeTruthy();
|
|
});
|
|
|
|
test('submits redeem panel with the shared form shell', async () => {
|
|
const user = userEvent.setup();
|
|
const onRedeemCodeChange = vi.fn();
|
|
const onSubmitRedeemCode = vi.fn();
|
|
|
|
render(
|
|
<PlatformProfileReferralModal
|
|
panel="redeem"
|
|
center={buildCenter()}
|
|
isLoading={false}
|
|
isSubmittingRedeem={false}
|
|
redeemCode="ab12"
|
|
copyInviteState="idle"
|
|
error={null}
|
|
success={null}
|
|
onClose={vi.fn()}
|
|
onCopyInvite={vi.fn()}
|
|
onRedeemCodeChange={onRedeemCodeChange}
|
|
onSubmitRedeemCode={onSubmitRedeemCode}
|
|
/>,
|
|
);
|
|
|
|
const dialog = screen.getByRole('dialog', { name: '填邀请码' });
|
|
const input = within(dialog).getByRole('textbox', { name: '邀请码' });
|
|
|
|
await user.type(input, ' c');
|
|
expect(onRedeemCodeChange).toHaveBeenCalled();
|
|
|
|
await user.click(within(dialog).getByRole('button', { name: '提交' }));
|
|
expect(onSubmitRedeemCode).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
test('redeem panel can paste invite code from native host clipboard', async () => {
|
|
const user = userEvent.setup();
|
|
const onRedeemCodeChange = vi.fn();
|
|
canPasteFromNativeHostClipboardMock.mockReturnValue(true);
|
|
readNativeHostClipboardTextMock.mockResolvedValue('spring-2026');
|
|
|
|
render(
|
|
<PlatformProfileReferralModal
|
|
panel="redeem"
|
|
center={buildCenter()}
|
|
isLoading={false}
|
|
isSubmittingRedeem={false}
|
|
redeemCode=""
|
|
copyInviteState="idle"
|
|
error={null}
|
|
success={null}
|
|
onClose={vi.fn()}
|
|
onCopyInvite={vi.fn()}
|
|
onRedeemCodeChange={onRedeemCodeChange}
|
|
onSubmitRedeemCode={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
await user.click(screen.getByRole('button', { name: '粘贴' }));
|
|
|
|
expect(readNativeHostClipboardTextMock).toHaveBeenCalledTimes(1);
|
|
expect(onRedeemCodeChange).toHaveBeenCalledWith('spring-2026');
|
|
});
|
|
|
|
test('renders community QR panels', () => {
|
|
render(
|
|
<PlatformProfileReferralModal
|
|
panel="community"
|
|
center={buildCenter()}
|
|
isLoading={false}
|
|
isSubmittingRedeem={false}
|
|
redeemCode=""
|
|
copyInviteState="idle"
|
|
error={null}
|
|
success={null}
|
|
onClose={vi.fn()}
|
|
onCopyInvite={vi.fn()}
|
|
onRedeemCodeChange={vi.fn()}
|
|
onSubmitRedeemCode={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
const dialog = screen.getByRole('dialog', { name: '玩家社区' });
|
|
|
|
expect(within(dialog).getByAltText('玩家社区微信群二维码')).toBeTruthy();
|
|
expect(within(dialog).getByAltText('玩家社区 QQ 群二维码')).toBeTruthy();
|
|
expect(within(dialog).getByText('微信群')).toBeTruthy();
|
|
expect(within(dialog).getByText('QQ群')).toBeTruthy();
|
|
});
|
|
});
|