1
This commit is contained in:
@@ -149,14 +149,14 @@ const {
|
||||
pointProducts: [
|
||||
{
|
||||
productId: 'points_60',
|
||||
title: '60光点',
|
||||
title: '60泥点',
|
||||
priceCents: 600,
|
||||
kind: 'points',
|
||||
pointsAmount: 60,
|
||||
bonusPoints: 60,
|
||||
durationDays: 0,
|
||||
badgeLabel: '首充双倍',
|
||||
description: '首充送60光点',
|
||||
description: '首充送60泥点',
|
||||
tier: 'normal',
|
||||
},
|
||||
],
|
||||
@@ -176,7 +176,7 @@ const {
|
||||
],
|
||||
benefits: [
|
||||
{
|
||||
benefitName: '免光点回合数',
|
||||
benefitName: '免泥点回合数',
|
||||
normalValue: '30',
|
||||
monthValue: '100',
|
||||
seasonValue: '100',
|
||||
@@ -191,7 +191,7 @@ const {
|
||||
order: {
|
||||
orderId: 'order-1',
|
||||
productId: 'points_60',
|
||||
productTitle: '60光点',
|
||||
productTitle: '60泥点',
|
||||
kind: 'points',
|
||||
amountCents: 600,
|
||||
status: 'paid',
|
||||
@@ -335,6 +335,38 @@ function dispatchPointerEvent(
|
||||
target.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function stubImage(width = 800, height = 600) {
|
||||
class MockImage {
|
||||
onload: null | (() => void) = null;
|
||||
onerror: null | (() => void) = null;
|
||||
naturalWidth = width;
|
||||
naturalHeight = height;
|
||||
width = width;
|
||||
height = height;
|
||||
|
||||
set src(_value: string) {
|
||||
this.onload?.();
|
||||
}
|
||||
}
|
||||
|
||||
vi.stubGlobal('Image', MockImage as unknown as typeof Image);
|
||||
}
|
||||
|
||||
function stubFileReader(dataUrl: string) {
|
||||
class MockFileReader {
|
||||
result: string | null = null;
|
||||
onload: null | (() => void) = null;
|
||||
onerror: null | (() => void) = null;
|
||||
|
||||
readAsDataURL() {
|
||||
this.result = dataUrl;
|
||||
this.onload?.();
|
||||
}
|
||||
}
|
||||
|
||||
vi.stubGlobal('FileReader', MockFileReader as unknown as typeof FileReader);
|
||||
}
|
||||
|
||||
const puzzlePublicEntry = {
|
||||
sourceType: 'puzzle',
|
||||
workId: 'puzzle-work-public-1',
|
||||
@@ -826,6 +858,7 @@ afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.clearAllMocks();
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
mockGetRpgProfileReferralInviteCenter.mockResolvedValue(
|
||||
mockBuildReferralCenter(),
|
||||
);
|
||||
@@ -901,9 +934,9 @@ test('opens wallet ledger modal from narrative coin card', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderProfileView();
|
||||
await user.click(screen.getByRole('button', { name: /光点\s*0/u }));
|
||||
await user.click(screen.getByRole('button', { name: /泥点\s*0/u }));
|
||||
|
||||
expect(await screen.findByText('光点账单')).toBeTruthy();
|
||||
expect(await screen.findByText('泥点账单')).toBeTruthy();
|
||||
expect(mockGetRpgProfileWalletLedger).toHaveBeenCalledTimes(1);
|
||||
expect(screen.getByText('资产操作消耗')).toBeTruthy();
|
||||
expect(screen.getByText('-1')).toBeTruthy();
|
||||
@@ -923,7 +956,7 @@ test('profile recharge modal buys points through mock channel outside mini progr
|
||||
|
||||
expect(await screen.findByText('账户充值')).toBeTruthy();
|
||||
expect(mockGetRpgProfileRechargeCenter).toHaveBeenCalledTimes(1);
|
||||
await user.click(screen.getByRole('button', { name: /60光点/u }));
|
||||
await user.click(screen.getByRole('button', { name: /60泥点/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCreateRpgProfileRechargeOrder).toHaveBeenCalledWith(
|
||||
@@ -953,7 +986,7 @@ test('profile recharge modal posts requestPayment params in mini program web-vie
|
||||
order: {
|
||||
orderId: 'order-wechat-1',
|
||||
productId: 'points_60',
|
||||
productTitle: '60光点',
|
||||
productTitle: '60泥点',
|
||||
kind: 'points',
|
||||
amountCents: 600,
|
||||
status: 'pending' as const,
|
||||
@@ -993,7 +1026,7 @@ test('profile recharge modal posts requestPayment params in mini program web-vie
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: /60光点/u }));
|
||||
await user.click(await screen.findByRole('button', { name: /60泥点/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCreateRpgProfileRechargeOrder).toHaveBeenCalledWith(
|
||||
@@ -1029,7 +1062,7 @@ test('profile daily task shortcut opens task center and claims reward', async ()
|
||||
expect(mockClaimRpgProfileTaskReward).toHaveBeenCalledWith('daily_login');
|
||||
});
|
||||
expect(onRechargeSuccess).toHaveBeenCalledTimes(1);
|
||||
expect(await screen.findByText('已领取 10 光点')).toBeTruthy();
|
||||
expect(await screen.findByText('已领取 10 泥点')).toBeTruthy();
|
||||
expect(
|
||||
(screen.getByRole('button', { name: '已领取' }) as HTMLButtonElement)
|
||||
.disabled,
|
||||
@@ -1073,17 +1106,42 @@ test('desktop account entry uses saved avatar image when available', () => {
|
||||
expect(within(accountEntry).queryByText('测')).toBeNull();
|
||||
});
|
||||
|
||||
test('profile avatar upload uses the shared square crop tool', async () => {
|
||||
stubFileReader('data:image/png;base64,avatar-source');
|
||||
stubImage(800, 600);
|
||||
|
||||
renderProfileView();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '上传头像' }));
|
||||
fireEvent.change(screen.getByLabelText('上传头像', { selector: 'input' }), {
|
||||
target: {
|
||||
files: [new File(['x'], 'avatar.png', { type: 'image/png' })],
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('dialog', { name: '裁剪头像' })).toBeTruthy();
|
||||
});
|
||||
expect(screen.getByLabelText('头像裁剪操作区')).toBeTruthy();
|
||||
expect(
|
||||
screen.getByRole('button', { name: '拖拽右下角裁剪边界' }),
|
||||
).toBeTruthy();
|
||||
expect(screen.queryByText('缩放')).toBeNull();
|
||||
expect(screen.queryByText('横向')).toBeNull();
|
||||
expect(screen.queryByText('纵向')).toBeNull();
|
||||
});
|
||||
|
||||
test('wallet ledger modal shows empty and error states', async () => {
|
||||
const user = userEvent.setup();
|
||||
mockGetRpgProfileWalletLedger.mockResolvedValueOnce({ entries: [] });
|
||||
|
||||
renderProfileView();
|
||||
await user.click(screen.getByRole('button', { name: /光点\s*0/u }));
|
||||
await user.click(screen.getByRole('button', { name: /泥点\s*0/u }));
|
||||
expect(await screen.findByText('暂无账单记录')).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByLabelText('关闭光点账单'));
|
||||
await user.click(screen.getByLabelText('关闭泥点账单'));
|
||||
mockGetRpgProfileWalletLedger.mockRejectedValueOnce(new Error('加载失败'));
|
||||
await user.click(screen.getByRole('button', { name: /光点\s*0/u }));
|
||||
await user.click(screen.getByRole('button', { name: /泥点\s*0/u }));
|
||||
|
||||
expect(await screen.findByText('加载失败')).toBeTruthy();
|
||||
expect(screen.getByText('重新加载')).toBeTruthy();
|
||||
@@ -1104,7 +1162,7 @@ test('profile invite shortcut shows reward subtitle and invited users', async ()
|
||||
|
||||
expect(mockGetRpgProfileReferralInviteCenter).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
await screen.findByText('邀请一个用户注册,双方都可以获得30光点。'),
|
||||
await screen.findByText('邀请一个用户注册,双方都可以获得30泥点。'),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByText('每日最多获得十次邀请奖励。')).toBeTruthy();
|
||||
expect(screen.getByText('成功邀请')).toBeTruthy();
|
||||
|
||||
Reference in New Issue
Block a user