xingyun/__tests__/screens/main/SharePosterScreen.test.tsx

221 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as React from 'react';
import { act, create, type ReactTestRenderer } from 'react-test-renderer';
import { beforeEach, describe, expect, it, vi } from 'vitest';
const mockHost = (
type: string,
props?: Record<string, unknown>,
...children: React.ReactNode[]
) => React.createElement(type as React.ElementType, props, ...children);
function flushPromises(ms = 0) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const {
nativeShareMock,
toastErrorMock,
userInfoRef,
} = vi.hoisted(() => ({
nativeShareMock: vi.fn(),
toastErrorMock: vi.fn(),
userInfoRef: {
current: {
inviteUrl: 'https://example.com/invite/abc',
},
},
}));
vi.mock('react-native', () => ({
Image: (props: Record<string, unknown> & { children?: React.ReactNode }) =>
mockHost('Image', props, props.children),
ImageBackground: (props: Record<string, unknown> & { children?: React.ReactNode }) =>
mockHost('ImageBackground', props, props.children),
Pressable: (props: Record<string, unknown> & { children?: React.ReactNode }) =>
mockHost('Pressable', props, props.children),
Share: {
share: nativeShareMock,
},
StyleSheet: {
absoluteFillObject: {},
create: <T extends Record<string, unknown>>(styles: T) => styles,
},
View: (props: Record<string, unknown> & { children?: React.ReactNode }) =>
mockHost('View', props, props.children),
}));
vi.mock('react-native-safe-area-context', () => ({
SafeAreaView: (props: Record<string, unknown> & { children?: React.ReactNode }) =>
mockHost('SafeAreaView', props, props.children),
}));
vi.mock('@telescope/shim-text', () => ({
__esModule: true,
default: (props: Record<string, unknown> & { children?: React.ReactNode }) =>
mockHost('Text', props, props.children),
}));
vi.mock('@telescope/toast', () => ({
useToast: () => ({
error: toastErrorMock,
}),
}));
vi.mock('@telescope/user-info', () => ({
useUserInfo: () => ({
userInfo: userInfoRef.current,
}),
}));
vi.mock('../../../src/assets', () => ({
appAssets: {
xingyunAuthBackground: 'xingyun-auth-bg',
xingyunShareTop: 'xingyun-share-top',
},
}));
vi.mock('../../../src/theme', () => ({
theme: {
colors: {
white: '#ffffff',
},
},
}));
vi.mock('../../../src/components/AppPrimitives', () => ({
AppStatusBar: (props: Record<string, unknown>) => mockHost('AppStatusBar', props),
}));
vi.mock('../../../src/components/AppIcons', () => ({
AppIcon: (props: Record<string, unknown>) => mockHost('AppIcon', props),
}));
vi.mock('../../../src/navigation/AppScreenHeader', () => ({
AppPageHeader: (props: Record<string, unknown>) => mockHost('AppPageHeader', props),
}));
describe('screens/main/SharePosterScreen', () => {
const navigation = {
goBack: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
userInfoRef.current = {
inviteUrl: 'https://example.com/invite/abc',
};
nativeShareMock.mockResolvedValue(undefined);
});
it('点击分享按钮会调用系统分享并带上邀请链接', async () => {
const { SharePosterScreen } = await import('../../../src/screens/main/SharePosterScreen');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<SharePosterScreen navigation={navigation} />);
});
const shareButton = renderer.root.findAllByType('Pressable' as React.ElementType).find(
node => node
.findAllByType('Text' as React.ElementType)
.some(text => text.props.children === '通过链接分享'),
);
await act(async () => {
shareButton?.props.onPress();
await flushPromises(10);
});
expect(nativeShareMock).toHaveBeenCalledWith({
message:
'我正在使用行云网络服务邀请你一起体验稳定连接https://example.com/invite/abc',
});
});
it('没有邀请链接时会分享 App Store 地址', async () => {
userInfoRef.current = {
inviteUrl: '',
};
const { SharePosterScreen } = await import('../../../src/screens/main/SharePosterScreen');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<SharePosterScreen navigation={navigation} />);
});
const shareButton = renderer.root.findAllByType('Pressable' as React.ElementType).find(
node => node
.findAllByType('Text' as React.ElementType)
.some(text => text.props.children === '通过链接分享'),
);
await act(async () => {
shareButton?.props.onPress();
await flushPromises(10);
});
expect(nativeShareMock).toHaveBeenCalledWith({
message:
'我正在使用行云网络服务邀请你一起体验稳定连接https://apps.apple.com/cn/app/%E8%A1%8C%E4%BA%91%E5%8A%A0%E9%80%9F%E5%99%A8-%E5%85%A8%E7%90%83%E7%BD%91%E7%BB%9C%E5%8A%A0%E9%80%9F/id6751933882',
});
});
it('顶部导航会复用统一的 AppPageHeader', async () => {
const { SharePosterScreen } = await import('../../../src/screens/main/SharePosterScreen');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<SharePosterScreen navigation={navigation} />);
});
const header = renderer.root.findByType('AppPageHeader' as React.ElementType);
expect(header.props.title).toBe('分享有奖');
expect(header.props.tone).toBe('dark');
expect(header.props.showBackButton).toBe(true);
});
it('渲染 XINY 风格的分享页内容', async () => {
const { SharePosterScreen } = await import('../../../src/screens/main/SharePosterScreen');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<SharePosterScreen navigation={navigation} />);
});
expect(renderer.root.findByType('ImageBackground' as React.ElementType).props.source)
.toBe('xingyun-auth-bg');
expect(renderer.root.findByType('Image' as React.ElementType).props.source)
.toBe('xingyun-share-top');
const texts = renderer.root.findAllByType('Text' as React.ElementType);
expect(texts.some(node => node.props.children === '推荐朋友,领永久会员')).toBe(true);
expect(texts.some(node => node.props.children === '通过链接分享')).toBe(true);
const shareButton = renderer.root.findAllByType('Pressable' as React.ElementType).find(
node => node
.findAllByType('Text' as React.ElementType)
.some(text => text.props.children === '通过链接分享'),
);
expect(shareButton?.props.style).toMatchObject({
width: 280,
height: 48,
borderRadius: 20.5,
backgroundColor: '#757EFF',
});
});
it('作为底部 tab 页面时隐藏返回按钮', async () => {
const { SharePosterScreen } = await import('../../../src/screens/main/SharePosterScreen');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(
<SharePosterScreen navigation={navigation} showBackButton={false} />,
);
});
const header = renderer.root.findByType('AppPageHeader' as React.ElementType);
expect(header.props.showBackButton).toBe(false);
});
});