Merge branch 'master' into hermes/hermes-4fd30995
This commit is contained in:
@@ -675,6 +675,10 @@ function renderProfileView(
|
||||
);
|
||||
}
|
||||
|
||||
async function openRechargeModal(user: ReturnType<typeof userEvent.setup>) {
|
||||
await user.click(screen.getByRole('button', { name: /充值\s*泥点\/会员/u }));
|
||||
}
|
||||
|
||||
function renderLoggedOutHomeView(
|
||||
openLoginModal = vi.fn(),
|
||||
overrides: Partial<
|
||||
@@ -924,7 +928,9 @@ afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
window.wx = undefined;
|
||||
document
|
||||
.querySelectorAll('script[src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"]')
|
||||
.querySelectorAll(
|
||||
'script[src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"]',
|
||||
)
|
||||
.forEach((script) => script.remove());
|
||||
mockGetRpgProfileReferralInviteCenter.mockResolvedValue(
|
||||
mockBuildReferralCenter(),
|
||||
@@ -1016,10 +1022,7 @@ test('profile recharge modal buys points through mock channel outside mini progr
|
||||
const onRechargeSuccess = vi.fn();
|
||||
|
||||
renderProfileView(onRechargeSuccess);
|
||||
const shortcutRegion = screen.getByRole('region', { name: '常用功能' });
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await openRechargeModal(user);
|
||||
|
||||
expect(await screen.findByText('账户充值')).toBeTruthy();
|
||||
expect(mockGetRpgProfileRechargeCenter).toHaveBeenCalledTimes(1);
|
||||
@@ -1031,13 +1034,52 @@ test('profile recharge modal buys points through mock channel outside mini progr
|
||||
'mock',
|
||||
);
|
||||
});
|
||||
expect(
|
||||
await screen.findByRole('dialog', { name: '支付成功' }),
|
||||
).toBeTruthy();
|
||||
expect(await screen.findByRole('dialog', { name: '支付成功' })).toBeTruthy();
|
||||
expect(screen.getByText('已到账,账户状态已刷新。')).toBeTruthy();
|
||||
expect(onRechargeSuccess).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('profile recharge modal hides first bonus display after points recharge', async () => {
|
||||
const user = userEvent.setup();
|
||||
mockGetRpgProfileRechargeCenter.mockResolvedValueOnce({
|
||||
walletBalance: 60,
|
||||
membership: {
|
||||
status: 'normal',
|
||||
tier: 'normal',
|
||||
startedAt: null,
|
||||
expiresAt: null,
|
||||
updatedAt: null,
|
||||
},
|
||||
pointProducts: [
|
||||
{
|
||||
productId: 'points_60',
|
||||
title: '60泥点',
|
||||
priceCents: 600,
|
||||
kind: 'points',
|
||||
pointsAmount: 60,
|
||||
bonusPoints: 60,
|
||||
durationDays: 0,
|
||||
badgeLabel: '首充双倍',
|
||||
description: '首充送60泥点',
|
||||
tier: 'normal',
|
||||
},
|
||||
],
|
||||
membershipProducts: [],
|
||||
benefits: [],
|
||||
latestOrder: null,
|
||||
hasPointsRecharged: true,
|
||||
});
|
||||
|
||||
renderProfileView();
|
||||
await openRechargeModal(user);
|
||||
|
||||
const rechargeDialog = await screen.findByText('账户充值');
|
||||
expect(rechargeDialog).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /60泥点/u })).toBeTruthy();
|
||||
expect(screen.queryByText('首充双倍')).toBeNull();
|
||||
expect(screen.queryByText('60+60泥点')).toBeNull();
|
||||
});
|
||||
|
||||
test('profile recharge modal posts requestPayment params in mini program web-view', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onRechargeSuccess = vi.fn();
|
||||
@@ -1090,10 +1132,7 @@ test('profile recharge modal posts requestPayment params in mini program web-vie
|
||||
});
|
||||
|
||||
renderProfileView(onRechargeSuccess);
|
||||
const shortcutRegion = screen.getByRole('region', { name: '常用功能' });
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await openRechargeModal(user);
|
||||
await user.click(await screen.findByRole('button', { name: /60泥点/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -1118,9 +1157,7 @@ test('profile recharge modal posts requestPayment params in mini program web-vie
|
||||
});
|
||||
expect(navigateUrl).toContain('order-wechat-1');
|
||||
expect(decodeURIComponent(navigateUrl)).toContain('prepay_id=wx-prepay');
|
||||
expect(
|
||||
await screen.findByRole('dialog', { name: '支付成功' }),
|
||||
).toBeTruthy();
|
||||
expect(await screen.findByRole('dialog', { name: '支付成功' })).toBeTruthy();
|
||||
expect(screen.getByText('已到账,账户状态已刷新。')).toBeTruthy();
|
||||
expect(mockConfirmWechatRpgProfileRechargeOrder).toHaveBeenCalledWith(
|
||||
'order-wechat-1',
|
||||
@@ -1243,10 +1280,7 @@ test('profile recharge modal waits for paid confirmation before refreshing dashb
|
||||
});
|
||||
|
||||
renderProfileView(onRechargeSuccess);
|
||||
const shortcutRegion = screen.getByRole('region', { name: '常用功能' });
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await openRechargeModal(user);
|
||||
await user.click(await screen.findByRole('button', { name: /60泥点/u }));
|
||||
|
||||
const navigateUrl = navigateTo.mock.calls[0]?.[0].url ?? '';
|
||||
@@ -1266,9 +1300,7 @@ test('profile recharge modal waits for paid confirmation before refreshing dashb
|
||||
expect(mockConfirmWechatRpgProfileRechargeOrder).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
expect(
|
||||
await screen.findByRole('dialog', { name: '支付成功' }),
|
||||
).toBeTruthy();
|
||||
expect(await screen.findByRole('dialog', { name: '支付成功' })).toBeTruthy();
|
||||
expect(onRechargeSuccess).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -1319,10 +1351,7 @@ test('profile recharge modal loads wechat js sdk before mini program payment bri
|
||||
});
|
||||
|
||||
renderProfileView();
|
||||
const shortcutRegion = screen.getByRole('region', { name: '常用功能' });
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await openRechargeModal(user);
|
||||
await user.click(await screen.findByRole('button', { name: /60泥点/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -1354,9 +1383,7 @@ test('profile recharge modal loads wechat js sdk before mini program payment bri
|
||||
window.location.hash = `wx_pay_result=${requestId}:success`;
|
||||
window.dispatchEvent(new HashChangeEvent('hashchange'));
|
||||
});
|
||||
expect(
|
||||
await screen.findByRole('dialog', { name: '支付成功' }),
|
||||
).toBeTruthy();
|
||||
expect(await screen.findByRole('dialog', { name: '支付成功' })).toBeTruthy();
|
||||
});
|
||||
|
||||
test('profile recharge modal releases submitting state after cancelled wechat pay result', async () => {
|
||||
@@ -1410,10 +1437,7 @@ test('profile recharge modal releases submitting state after cancelled wechat pa
|
||||
});
|
||||
|
||||
renderProfileView();
|
||||
const shortcutRegion = screen.getByRole('region', { name: '常用功能' });
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await openRechargeModal(user);
|
||||
const buyButton = await screen.findByRole('button', { name: /60泥点/u });
|
||||
await user.click(buyButton);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
ArrowRight,
|
||||
AlertCircle,
|
||||
ArrowRight,
|
||||
BookOpen,
|
||||
Camera,
|
||||
CheckCircle2,
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
Heart,
|
||||
LogIn,
|
||||
MessageCircle,
|
||||
Pencil,
|
||||
Palette,
|
||||
Pencil,
|
||||
Plus,
|
||||
Search,
|
||||
Settings,
|
||||
@@ -47,22 +47,22 @@ import communityQqQrImage from '../../../media/social-media-group/qq.png';
|
||||
import communityWechatQrImage from '../../../media/social-media-group/wechat.png';
|
||||
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
|
||||
import type {
|
||||
ConfirmWechatProfileRechargeOrderResponse,
|
||||
CustomWorldLibraryEntry,
|
||||
PlatformBrowseHistoryEntry,
|
||||
ConfirmWechatProfileRechargeOrderResponse,
|
||||
ProfileDashboardCardKey,
|
||||
ProfileDashboardSummary,
|
||||
ProfilePlayedWorkSummary,
|
||||
ProfilePlayStatsResponse,
|
||||
ProfileReferralInviteCenterResponse,
|
||||
ProfileRechargeCenterResponse,
|
||||
ProfileRechargeProduct,
|
||||
WechatMiniProgramPayParams,
|
||||
ProfileReferralInviteCenterResponse,
|
||||
ProfileSaveArchiveSummary,
|
||||
ProfileTaskCenterResponse,
|
||||
ProfileTaskItem,
|
||||
ProfileWalletLedgerResponse,
|
||||
RedeemProfileRewardCodeResponse,
|
||||
WechatMiniProgramPayParams,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
|
||||
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
|
||||
@@ -77,8 +77,8 @@ import {
|
||||
claimRpgProfileTaskReward,
|
||||
confirmWechatRpgProfileRechargeOrder,
|
||||
createRpgProfileRechargeOrder,
|
||||
getRpgProfileReferralInviteCenter,
|
||||
getRpgProfileRechargeCenter,
|
||||
getRpgProfileReferralInviteCenter,
|
||||
getRpgProfileTasks,
|
||||
getRpgProfileWalletLedger,
|
||||
redeemRpgProfileReferralInviteCode,
|
||||
@@ -2498,17 +2498,23 @@ async function confirmWechatRechargeOrderUntilSettled(
|
||||
|
||||
function RechargeProductCard({
|
||||
product,
|
||||
hasPointsRecharged,
|
||||
submittingProductId,
|
||||
onBuy,
|
||||
}: {
|
||||
product: ProfileRechargeProduct;
|
||||
hasPointsRecharged: boolean;
|
||||
submittingProductId: string | null;
|
||||
onBuy: (product: ProfileRechargeProduct) => void;
|
||||
}) {
|
||||
const submitting = submittingProductId === product.productId;
|
||||
const effectiveBonusPoints =
|
||||
product.kind === 'points' && hasPointsRecharged ? 0 : product.bonusPoints;
|
||||
const badgeLabel =
|
||||
product.kind === 'points' && hasPointsRecharged ? '' : product.badgeLabel;
|
||||
const value =
|
||||
product.kind === 'points'
|
||||
? `${product.pointsAmount}${product.bonusPoints > 0 ? `+${product.bonusPoints}` : ''}泥点`
|
||||
? `${product.pointsAmount}${effectiveBonusPoints > 0 ? `+${effectiveBonusPoints}` : ''}泥点`
|
||||
: `${product.durationDays}天`;
|
||||
|
||||
return (
|
||||
@@ -2518,9 +2524,9 @@ function RechargeProductCard({
|
||||
disabled={Boolean(submittingProductId)}
|
||||
className="platform-subpanel platform-interactive-card relative min-h-[7.25rem] rounded-[1.15rem] px-3.5 py-3.5 text-left disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{product.badgeLabel ? (
|
||||
{badgeLabel ? (
|
||||
<span className="platform-pill platform-pill--warm absolute right-3 top-3 max-w-[7rem] truncate px-2 py-0.5 text-[10px]">
|
||||
{product.badgeLabel}
|
||||
{badgeLabel}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="pr-20 text-sm font-black text-[var(--platform-text-strong)]">
|
||||
@@ -2640,6 +2646,7 @@ function ProfileRechargeModal({
|
||||
<RechargeProductCard
|
||||
key={product.productId}
|
||||
product={product}
|
||||
hasPointsRecharged={center?.hasPointsRecharged === true}
|
||||
submittingProductId={submittingProductId}
|
||||
onBuy={onBuy}
|
||||
/>
|
||||
@@ -3928,7 +3935,7 @@ export function RpgEntryHomeView({
|
||||
setIsWalletLedgerOpen(true);
|
||||
loadWalletLedger();
|
||||
};
|
||||
const loadRechargeCenter = () => {
|
||||
const loadRechargeCenter = useCallback(() => {
|
||||
setRechargeError(null);
|
||||
setIsLoadingRechargeCenter(true);
|
||||
void getRpgProfileRechargeCenter()
|
||||
@@ -3940,15 +3947,12 @@ export function RpgEntryHomeView({
|
||||
);
|
||||
})
|
||||
.finally(() => setIsLoadingRechargeCenter(false));
|
||||
};
|
||||
const refreshRechargeState = useCallback(
|
||||
() => {
|
||||
loadRechargeCenter();
|
||||
setSubmittingRechargeProductId(null);
|
||||
pendingWechatRechargeOrderIdRef.current = null;
|
||||
},
|
||||
[loadRechargeCenter],
|
||||
);
|
||||
}, []);
|
||||
const refreshRechargeState = useCallback(() => {
|
||||
loadRechargeCenter();
|
||||
setSubmittingRechargeProductId(null);
|
||||
pendingWechatRechargeOrderIdRef.current = null;
|
||||
}, [loadRechargeCenter]);
|
||||
const handleWechatPayResult = useCallback(() => {
|
||||
const payResult = readWechatPayResultFromHash();
|
||||
if (!payResult) {
|
||||
|
||||
Reference in New Issue
Block a user