Files
Genarrative/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx
T
k88936 8ed716c6e2 修复(充值弹层): 任一商品提交中即禁用会员结算
- 会员结算 canSubmit 由 !submitting 改为 !submittingProductId,与充点分支的 ctaDisabled 口径一致
- 避免充点在途时仍能提交会员结算、被 buyRechargeProduct 静默丢弃后直接关闭弹层
- 补充反向用例:泥点订单提交中时会员结算按钮禁用
2026-10-05 17:41:26 +08:00

809 lines
26 KiB
TypeScript

/* @vitest-environment jsdom */
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, test, vi } from 'vitest';
import type { ProfileRechargeProduct } from '@/packages/shared/src';
import { PlatformProfileRechargeModal } from './index';
function buildNormalMembership() {
return {
status: 'normal' as const,
plan: 'normal' as const,
startedAt: null,
expiresAt: null,
updatedAt: null,
cycleStartedAt: null,
cycleResetsAt: null,
cycleGrantedPoints: 0,
cycleRemainingPoints: 0,
cycleIndex: 1,
cycleCount: 1,
cycleKind: 'monthly' as const,
};
}
function buildActiveMembership(
plan: 'plus' | 'max',
cycleKind: 'monthly' | 'yearly',
) {
return {
status: 'active' as const,
plan,
startedAt: '2026-01-01T00:00:00+08:00',
expiresAt: '2027-01-01T00:00:00+08:00',
updatedAt: '2026-04-01T00:00:00+08:00',
cycleStartedAt: '2026-03-20T09:00:00+08:00',
cycleResetsAt: '2026-04-20T09:00:00+08:00',
cycleGrantedPoints: 1_150,
cycleRemainingPoints: 615,
cycleIndex: 4,
cycleCount: 12,
cycleKind,
};
}
/** 三池余额 fixture:每日免费 / 月度 / 永久。 */
function buildMudPointBalance(totalPoints: number) {
return {
dailyFreePoints: 0,
dailyFreeResetPoints: 20,
dailyFreeResetsAt: '2026-08-04T00:00:00+08:00',
monthlyPoints: 0,
monthlyResetsAt: null,
permanentPoints: totalPoints,
};
}
function buildPointProduct(
overrides: Partial<ProfileRechargeProduct> = {},
): ProfileRechargeProduct {
return {
productId: 'points_60',
title: '60泥点',
priceCents: 600,
kind: 'points' as const,
pointsAmount: 60,
bonusPoints: 0,
durationDays: 0,
badgeLabel: '',
description: '60泥点',
...overrides,
};
}
// 六档充点商品:没有首充赠送。
function buildPointProducts(): ProfileRechargeProduct[] {
return [60, 180, 300, 680, 1_280, 3_280].map((pointsAmount) =>
buildPointProduct({
productId: `points_${pointsAmount}`,
title: `${pointsAmount}泥点`,
priceCents: pointsAmount * 10,
pointsAmount,
description: `${pointsAmount}泥点`,
}),
);
}
const STARTER_PLAN = {
plan: 'starter' as const,
title: 'Starter',
rank: 1,
monthPriceCents: 3_900,
yearPriceCents: 39_000,
periodPoints: 400,
modelAccess: 'basic' as const,
concurrentJobLimit: 1,
recommended: false,
unlimitedConcurrency: false,
enabled: true,
};
const PLUS_PLAN = {
...STARTER_PLAN,
plan: 'plus' as const,
title: 'Plus',
rank: 2,
monthPriceCents: 9_900,
yearPriceCents: 99_000,
periodPoints: 1_150,
modelAccess: 'full' as const,
concurrentJobLimit: 3,
recommended: true,
};
const MAX_PLAN = {
...STARTER_PLAN,
plan: 'max' as const,
title: 'Max',
rank: 4,
monthPriceCents: 69_900,
yearPriceCents: 699_000,
periodPoints: 8_650,
modelAccess: 'full' as const,
concurrentJobLimit: 128,
unlimitedConcurrency: true,
};
vi.mock('qrcode', () => ({
default: {
toDataURL: vi.fn(async () => 'data:image/png;base64,wechat-native-qr'),
},
}));
describe('PlatformProfileRechargeModal', () => {
test('renders the membership plans and forwards a first-time purchase', async () => {
const user = userEvent.setup();
const onSubmitMembershipCheckout = vi.fn();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(29),
membership: buildNormalMembership(),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN, PLUS_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={onSubmitMembershipCheckout}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
expect(screen.getByRole('dialog', { name: '会员与泥点' })).toBeTruthy();
expect(screen.getByText('可用总额 29 泥点')).toBeTruthy();
const tablist = screen.getByRole('tablist', { name: '会员与泥点' });
expect(
within(tablist)
.getByRole('tab', { name: '会员订阅' })
.getAttribute('aria-selected'),
).toBe('true');
expect(screen.getAllByText('Starter').length).toBeGreaterThan(0);
expect(screen.getAllByText('Plus').length).toBeGreaterThan(0);
await user.click(
screen.getByRole('button', { name: 'Starter 月付 开通会员' }),
);
const checkout = screen.getByRole('dialog', { name: '确认订单' });
expect(within(checkout).getByText('¥39')).toBeTruthy();
expect(within(checkout).getByText('400(首期)')).toBeTruthy();
expect(within(checkout).getByText('微信支付')).toBeTruthy();
expect(within(checkout).queryByText(/报价已失效/)).toBeNull();
expect(within(checkout).queryByText(/倒计时/)).toBeNull();
await user.click(
within(checkout).getByRole('button', { name: '支付 ¥39' }),
);
expect(onSubmitMembershipCheckout).toHaveBeenCalledWith(
'starter',
'monthly',
);
});
test('周期切换按钮支持方向键与 roving tabindex', async () => {
const user = userEvent.setup();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(29),
membership: buildNormalMembership(),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN, PLUS_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
const group = screen.getByRole('radiogroup', { name: '付费周期' });
const monthly = within(group).getByRole('radio', { name: '月付' });
const yearly = within(group).getByRole('radio', { name: '年付' });
expect(monthly.getAttribute('tabindex')).toBe('0');
expect(yearly.getAttribute('tabindex')).toBe('-1');
monthly.focus();
await user.keyboard('{ArrowRight}');
expect(yearly.getAttribute('aria-checked')).toBe('true');
expect(monthly.getAttribute('aria-checked')).toBe('false');
expect(yearly.getAttribute('tabindex')).toBe('0');
expect(monthly.getAttribute('tabindex')).toBe('-1');
expect(document.activeElement).toBe(yearly);
});
test('requests the server quote and shows the upgrade amount', async () => {
const user = userEvent.setup();
const onRequestMembershipQuote = vi.fn();
const onSubmitMembershipCheckout = vi.fn();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(615),
membership: buildActiveMembership('plus', 'yearly'),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN, PLUS_PLAN, MAX_PLAN],
latestOrder: null,
hasPointsRecharged: true,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={{
currentPlan: 'plus',
targetPlan: 'max',
cycleKind: 'yearly',
cycleIndex: 4,
cycleCount: 12,
amountCents: 10_000,
grantedPointsDelta: 1_250,
monthlyBalanceAfter: 1_865,
expiresAt: '2027-01-01T09:00:00+08:00',
cycleResetsAt: '2026-04-20T09:00:00+08:00',
remainingFullMonths: 8,
}}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={onRequestMembershipQuote}
onSubmitMembershipCheckout={onSubmitMembershipCheckout}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
expect(
screen.getByRole('button', { name: 'Plus 年付 当前套餐' }),
).toHaveProperty('disabled', true);
expect(screen.getByText('当前 Plus · 年付')).toBeTruthy();
expect(screen.getAllByText(/可同周期补差升档/).length).toBeGreaterThan(0);
await user.click(screen.getByRole('button', { name: 'Max 年付 补差升级' }));
expect(onRequestMembershipQuote).toHaveBeenCalledWith('max');
const checkout = screen.getByRole('dialog', { name: '确认套餐变更' });
expect(within(checkout).getByText('¥100')).toBeTruthy();
expect(within(checkout).getByText('本期补发月度泥点')).toBeTruthy();
await user.click(
within(checkout).getByRole('button', { name: '补差升级 ¥100' }),
);
expect(onSubmitMembershipCheckout).toHaveBeenCalledWith('max', 'yearly');
});
test('已是最高的 max 档不再提示可补差升档', () => {
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(8_650),
membership: buildActiveMembership('max', 'yearly'),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN, PLUS_PLAN, MAX_PLAN],
latestOrder: null,
hasPointsRecharged: true,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
expect(screen.getByText('当前 Max · 年付')).toBeTruthy();
expect(screen.queryByText(/可同周期补差升档/)).toBeNull();
expect(screen.getByText('周期切换需等到期后')).toBeTruthy();
});
test('keeps an active member as member when their current plan is disabled', async () => {
const user = userEvent.setup();
const onRequestMembershipQuote = vi.fn();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(615),
membership: buildActiveMembership('plus', 'yearly'),
pointProducts: buildPointProducts(),
// plus 在后台被临时禁用:目录里仍有该档,但过滤后的可购买列表只剩 max。
membershipPlans: [{ ...PLUS_PLAN, enabled: false }, MAX_PLAN],
latestOrder: null,
hasPointsRecharged: true,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={onRequestMembershipQuote}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
// 状态卡仍认得当前档位,而不是退化成「会员」。
expect(screen.getByText('当前 Plus · 年付')).toBeTruthy();
// 有效会员看到的是补差升级,而不是可在有效会员上重复开通的「开通会员」。
await user.click(screen.getByRole('button', { name: 'Max 年付 补差升级' }));
expect(onRequestMembershipQuote).toHaveBeenCalledWith('max');
const checkout = screen.getByRole('dialog', { name: '确认套餐变更' });
// 即使原档位已禁用,升级摘要也要给出「原套餐标准年价」。
expect(within(checkout).getByText('原套餐标准年价')).toBeTruthy();
expect(within(checkout).getByText('¥990')).toBeTruthy();
});
test('shows a placeholder instead of the list price while the upgrade quote is loading', async () => {
const user = userEvent.setup();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(615),
membership: buildActiveMembership('plus', 'yearly'),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN, PLUS_PLAN, MAX_PLAN],
latestOrder: null,
hasPointsRecharged: true,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={true}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
await user.click(screen.getByRole('button', { name: 'Max 年付 补差升级' }));
const checkout = screen.getByRole('dialog', { name: '确认套餐变更' });
// 报价未到:金额占位,不能把 Max 标准年价(¥6990)当成「本次补付」。
expect(within(checkout).getByText('—')).toBeTruthy();
expect(
within(checkout).queryByRole('button', { name: /^补差升级 ¥/ }),
).toBeNull();
expect(
within(checkout).getByRole('button', { name: '补差升级' }),
).toHaveProperty('disabled', true);
});
test('recharge tab picks a point product and forwards the buy action', async () => {
const user = userEvent.setup();
const onBuy = vi.fn();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(29),
membership: buildNormalMembership(),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={onBuy}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
await user.click(screen.getByRole('tab', { name: '泥点充值' }));
const products = screen.getByRole('radiogroup', { name: '泥点充值档位' });
await user.click(
within(products).getByRole('radio', { name: '60泥点 ¥6' }),
);
await user.click(screen.getByRole('button', { name: '充值 ¥6' }));
const checkout = screen.getByRole('dialog', { name: '确认订单' });
expect(within(checkout).getByText('60 永久泥点')).toBeTruthy();
await user.click(within(checkout).getByRole('button', { name: '充值 ¥6' }));
expect(onBuy).toHaveBeenCalledTimes(1);
expect(onBuy.mock.calls[0][0]).toMatchObject({
productId: 'points_60',
priceCents: 600,
});
});
test('会员订单提交中时泥点档位仍可选,但下单入口禁用', async () => {
const user = userEvent.setup();
const renderModal = (submittingProductId: string | null) => (
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(29),
membership: buildNormalMembership(),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={submittingProductId}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>
);
const { rerender } = render(renderModal(null));
await user.click(screen.getByRole('tab', { name: '泥点充值' }));
const product = within(
screen.getByRole('radiogroup', { name: '泥点充值档位' }),
).getByRole('radio', { name: '60泥点 ¥6' });
// 既有行为:会员订单提交不波及泥点档位的可选状态。
expect(product).not.toHaveProperty('disabled', true);
await user.click(product);
await user.click(screen.getByRole('button', { name: '充值 ¥6' }));
// 会员订单提交中:泥点结算 CTA 禁用,避免被宿主守卫静默丢弃。
rerender(renderModal('membership-pro-monthly'));
expect(
within(screen.getByRole('dialog', { name: '确认订单' })).getByRole(
'button',
{ name: '充值 ¥6' },
),
).toHaveProperty('disabled', true);
});
test('泥点订单提交中时会员结算入口禁用', async () => {
const user = userEvent.setup();
const renderModal = (submittingProductId: string | null) => (
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(29),
membership: buildNormalMembership(),
pointProducts: buildPointProducts(),
membershipPlans: [STARTER_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={submittingProductId}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>
);
const { rerender } = render(renderModal(null));
await user.click(
screen.getByRole('button', { name: 'Starter 月付 开通会员' }),
);
// 泥点订单提交中:会员结算 CTA 同样禁用,避免宿主守卫静默丢弃提交后直接关弹层。
rerender(renderModal('points_60'));
expect(
within(screen.getByRole('dialog', { name: '确认订单' })).getByRole(
'button',
{ name: '支付 ¥39' },
),
).toHaveProperty('disabled', true);
});
test('shows the empty state when the recharge tab has no point product', async () => {
const user = userEvent.setup();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(29),
membership: buildNormalMembership(),
pointProducts: [],
membershipPlans: [STARTER_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
nativePayment={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
await user.click(screen.getByRole('tab', { name: '泥点充值' }));
expect(screen.getAllByText('Starter').length).toBeGreaterThan(0);
expect(screen.getByText('暂无可购买套餐')).toBeTruthy();
});
test('opens native payment QR code in a separate dialog', async () => {
const user = userEvent.setup();
const onConfirmNativePayment = vi.fn();
const onCloseNativePayment = vi.fn();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(12),
membership: buildNormalMembership(),
pointProducts: [],
membershipPlans: [],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
nativePayment={{
orderId: 'rcg_native_001',
productTitle: '100泥点',
amountCents: 1000,
codeUrl: 'weixin://wxpay/bizpayurl?pr=test',
expiresAt: '2099-01-01T00:05:00Z',
isConfirming: false,
confirmMessage: '暂未确认到账,请确认付款完成后再点一次。',
}}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={onConfirmNativePayment}
onCloseNativePayment={onCloseNativePayment}
/>,
);
const rechargeDialog = screen.getByRole('dialog', {
name: '会员与泥点',
});
const paymentDialog = screen.getByRole('dialog', {
name: '微信扫码支付',
});
expect(
within(rechargeDialog).queryByAltText('微信 Native 支付二维码'),
).toBeNull();
expect(
await within(paymentDialog).findByAltText('微信 Native 支付二维码'),
).toBeTruthy();
expect(within(paymentDialog).getByText('100泥点')).toBeTruthy();
expect(within(paymentDialog).getByText('¥10')).toBeTruthy();
expect(
within(paymentDialog).getByText(
'暂未确认到账,请确认付款完成后再点一次。',
),
).toBeTruthy();
await user.click(
within(paymentDialog).getByRole('button', { name: '我已支付' }),
);
expect(onConfirmNativePayment).toHaveBeenCalledTimes(1);
await user.click(
within(paymentDialog).getByRole('button', {
name: '关闭微信扫码支付',
}),
);
expect(onCloseNativePayment).toHaveBeenCalledTimes(1);
});
test('shows expired native payment state when qr countdown ends', async () => {
const onConfirmNativePayment = vi.fn();
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(12),
membership: buildNormalMembership(),
pointProducts: [],
membershipPlans: [],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
nativePayment={{
orderId: 'rcg_native_expired',
productTitle: '100泥点',
amountCents: 1000,
codeUrl: 'weixin://wxpay/bizpayurl?pr=expired',
expiresAt: '2000-01-01T00:00:00Z',
isConfirming: false,
}}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={onConfirmNativePayment}
onCloseNativePayment={vi.fn()}
/>,
);
const paymentDialog = screen.getByRole('dialog', {
name: '微信扫码支付',
});
const confirmButton = within(paymentDialog).getByRole('button', {
name: '已过期',
});
expect(
await within(paymentDialog).findByAltText('微信 Native 支付二维码'),
).toBeTruthy();
expect(within(paymentDialog).getAllByText('已过期')).toHaveLength(2);
expect(confirmButton).toHaveProperty('disabled', true);
expect(onConfirmNativePayment).not.toHaveBeenCalled();
});
test('hides the daily free pool when the daily grant is zero', () => {
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: {
...buildMudPointBalance(100),
dailyFreePoints: 0,
dailyFreeResetPoints: 0,
},
membership: buildNormalMembership(),
pointProducts: [],
membershipPlans: [STARTER_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
nativePayment={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
const dialog = screen.getByRole('dialog', { name: '会员与泥点' });
expect(within(dialog).queryByText('每日免费')).toBeNull();
expect(within(dialog).getByText('月度泥点')).toBeTruthy();
expect(within(dialog).getByText('永久泥点')).toBeTruthy();
});
test('shows the daily free pool while the daily grant is positive', () => {
render(
<PlatformProfileRechargeModal
center={{
mudPointBalance: buildMudPointBalance(100),
membership: buildNormalMembership(),
pointProducts: [],
membershipPlans: [STARTER_PLAN],
latestOrder: null,
hasPointsRecharged: false,
}}
isLoading={false}
error={null}
submittingProductId={null}
membershipQuote={null}
isLoadingMembershipQuote={false}
membershipQuoteError={null}
nativePayment={null}
onClose={vi.fn()}
onRetry={vi.fn()}
onBuy={vi.fn()}
onRequestMembershipQuote={vi.fn()}
onSubmitMembershipCheckout={vi.fn()}
onConfirmNativePayment={vi.fn()}
onCloseNativePayment={vi.fn()}
/>,
);
const dialog = screen.getByRole('dialog', { name: '会员与泥点' });
expect(within(dialog).getByText('每日免费')).toBeTruthy();
});
});