542fb09365
调整网页端充值渠道,微信内浏览器在开放平台接入前走 Native 支付。 拆分 Native 支付二维码弹窗,展示金额和充值内容并取消无效 code_url 跳转。 修复微信 Native 下单 time_expire 秒级 RFC3339 格式,避免小数秒参数错误。 新增充值订单 5 分钟过期调度,过期前查询微信真实订单状态。 补充微信授权绑定、路由加载恢复和后台表时间显示等配套逻辑。 更新 SpacetimeDB schema、绑定和相关文档测试。
164 lines
4.7 KiB
TypeScript
164 lines
4.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { act, render, screen } from '@testing-library/react';
|
|
import { createElement } from 'react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { RouteImageReadyGate } from './RouteImageReadyGate';
|
|
import {
|
|
collectRouteImageUrls,
|
|
extractCssImageUrls,
|
|
normalizePreloadImageUrl,
|
|
} from './routeImageReadyGateUtils';
|
|
import { RouteLoadingScreen } from './RouteLoadingScreen';
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
window.localStorage.clear();
|
|
window.sessionStorage.clear();
|
|
});
|
|
|
|
describe('RouteImageReadyGate image url helpers', () => {
|
|
it('extracts urls from layered CSS image values', () => {
|
|
expect(
|
|
extractCssImageUrls(
|
|
'linear-gradient(#000,#111), url("/hero.png"), url("icons/card.webp")',
|
|
),
|
|
).toEqual(['/hero.png', 'icons/card.webp']);
|
|
});
|
|
|
|
it('normalizes preloadable urls against the current document', () => {
|
|
expect(normalizePreloadImageUrl('/cover.png')).toBe(
|
|
new URL('/cover.png', document.baseURI).href,
|
|
);
|
|
expect(normalizePreloadImageUrl('data:image/png;base64,abc')).toBe(
|
|
'data:image/png;base64,abc',
|
|
);
|
|
expect(normalizePreloadImageUrl('')).toBeNull();
|
|
});
|
|
|
|
it('collects img and CSS background urls from a route root', () => {
|
|
const root = document.createElement('section');
|
|
root.innerHTML = `
|
|
<img src="/images/card.png" />
|
|
<div style='background-image: url("/images/bg.webp")'></div>
|
|
<div style='border-image-source: url("/ui/frame.png")'></div>
|
|
`;
|
|
|
|
expect(collectRouteImageUrls(root)).toEqual([
|
|
new URL('/images/card.png', document.baseURI).href,
|
|
new URL('/images/bg.webp', document.baseURI).href,
|
|
new URL('/ui/frame.png', document.baseURI).href,
|
|
]);
|
|
});
|
|
|
|
it('reveals route content after a short cap when images stay pending', () => {
|
|
vi.useFakeTimers();
|
|
|
|
render(
|
|
createElement(
|
|
RouteImageReadyGate,
|
|
{
|
|
eyebrow: '正在载入游戏',
|
|
text: '正在加载内容',
|
|
},
|
|
createElement(
|
|
'section',
|
|
{
|
|
'data-testid': 'route-content',
|
|
},
|
|
createElement('img', {
|
|
src: '/generated-characters/slow-cover.png',
|
|
alt: 'slow cover',
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
|
|
const content = screen.getByTestId('route-content');
|
|
const visibilityGate = content.parentElement;
|
|
expect(visibilityGate?.getAttribute('aria-hidden')).toBe('true');
|
|
expect(visibilityGate?.style.visibility).toBe('hidden');
|
|
|
|
act(() => {
|
|
vi.advanceTimersByTime(1600);
|
|
});
|
|
|
|
expect(visibilityGate?.getAttribute('aria-hidden')).toBe('false');
|
|
expect(visibilityGate?.style.visibility).toBe('visible');
|
|
});
|
|
|
|
it('does not show the full screen route loader again after the same route was revealed', () => {
|
|
vi.useFakeTimers();
|
|
|
|
const firstRender = render(
|
|
createElement(
|
|
RouteImageReadyGate,
|
|
{
|
|
eyebrow: 'Loading',
|
|
text: 'Please wait',
|
|
},
|
|
createElement(
|
|
'section',
|
|
{
|
|
'data-testid': 'route-content',
|
|
},
|
|
createElement('img', {
|
|
src: '/generated-characters/slow-cover.png',
|
|
alt: 'slow cover',
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(screen.getByText('Please wait')).toBeTruthy();
|
|
|
|
act(() => {
|
|
vi.advanceTimersByTime(1600);
|
|
});
|
|
|
|
expect(screen.queryByText('Please wait')).toBeNull();
|
|
firstRender.unmount();
|
|
|
|
render(
|
|
createElement(
|
|
RouteImageReadyGate,
|
|
{
|
|
eyebrow: 'Loading',
|
|
text: 'Please wait',
|
|
},
|
|
createElement('section', { 'data-testid': 'route-content' }, 'Ready'),
|
|
),
|
|
);
|
|
|
|
const content = screen.getByTestId('route-content');
|
|
const visibilityGate = content.parentElement;
|
|
expect(visibilityGate?.getAttribute('aria-hidden')).toBe('false');
|
|
expect(visibilityGate?.style.visibility).toBe('visible');
|
|
expect(screen.queryByText('Please wait')).toBeNull();
|
|
});
|
|
|
|
it('uses the saved platform theme tokens for the route loading screen', () => {
|
|
window.localStorage.setItem(
|
|
'tavernrealms.settings.v1',
|
|
JSON.stringify({
|
|
version: 1,
|
|
musicVolume: 0.42,
|
|
platformTheme: 'dark',
|
|
}),
|
|
);
|
|
|
|
const { container } = render(
|
|
createElement(RouteLoadingScreen, {
|
|
eyebrow: '正在载入游戏',
|
|
text: '正在加载内容',
|
|
}),
|
|
);
|
|
const shell = container.firstElementChild;
|
|
|
|
expect(shell?.classList.contains('platform-theme')).toBe(true);
|
|
expect(shell?.classList.contains('platform-theme--dark')).toBe(true);
|
|
expect(shell?.className).toContain('bg-[image:var(--platform-body-fill)]');
|
|
});
|
|
});
|