e3682fd06f
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m31s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m32s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m33s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m40s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m21s
Project CI / AI game creator shell Rust crates (push) Successful in 3m11s
Project CI / Native shell tests (push) Successful in 16m15s
Project CI / Frontend tests (push) Successful in 14m57s
Project CI / Repository checks (push) Successful in 14m48s
Project CI / Backend tests (push) Successful in 20m7s
Project CI / AI game creator shell web tests (push) Successful in 6m20s
移除服务器选择并按来源隔离登录凭据 新增官网客户端下载入口和匿名平台聚合接口 根据发布清单自动展示Windows与macOS首装包 补齐Mac首装元数据和上传顺序校验 同步定向测试与下载发布规范
224 lines
8.1 KiB
TypeScript
224 lines
8.1 KiB
TypeScript
/* @vitest-environment jsdom */
|
||
|
||
import {
|
||
act,
|
||
cleanup,
|
||
fireEvent,
|
||
render,
|
||
screen,
|
||
waitFor,
|
||
within,
|
||
} from '@testing-library/react';
|
||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||
|
||
import { ClientDownloadEntry } from './ClientDownloadEntry';
|
||
|
||
const fetchMock = vi.fn();
|
||
const installerUrl = (version: string) =>
|
||
`https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-win/${version}/setup.exe`;
|
||
const releaseResponse = (version: string) =>
|
||
new Response(
|
||
JSON.stringify({
|
||
downloads: [
|
||
{
|
||
platform: 'windows',
|
||
architecture: 'x86_64',
|
||
version,
|
||
downloadUrl: installerUrl(version),
|
||
},
|
||
],
|
||
unavailablePlatforms: [],
|
||
}),
|
||
);
|
||
|
||
const macDownload = (architecture: 'aarch64' | 'x86_64') => ({
|
||
platform: 'macos',
|
||
architecture,
|
||
version: '0.2.0',
|
||
downloadUrl: `https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-mac/0.2.0/app_${architecture}.dmg`,
|
||
});
|
||
|
||
describe('官网下载入口', () => {
|
||
beforeEach(() => {
|
||
fetchMock.mockReset();
|
||
vi.stubGlobal('fetch', fetchMock);
|
||
});
|
||
|
||
afterEach(() => {
|
||
cleanup();
|
||
vi.unstubAllGlobals();
|
||
vi.useRealTimers();
|
||
});
|
||
|
||
it('无需登录,每次打开重新获取最新版本和安装包链接', async () => {
|
||
fetchMock.mockImplementationOnce(() =>
|
||
Promise.resolve(releaseResponse('0.1.73')),
|
||
);
|
||
render(<ClientDownloadEntry />);
|
||
expect(fetchMock).not.toHaveBeenCalled();
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
expect(
|
||
(
|
||
await screen.findByRole('link', { name: '下载 Windows 版' })
|
||
).getAttribute('href'),
|
||
).toBe(installerUrl('0.1.73'));
|
||
expect(screen.getByText('最新版本 v0.1.73')).toBeTruthy();
|
||
expect(fetchMock).toHaveBeenCalledWith(
|
||
'/api/client-downloads',
|
||
expect.objectContaining({ cache: 'no-store', credentials: 'omit' }),
|
||
);
|
||
fireEvent.keyDown(document, { key: 'Escape' });
|
||
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
|
||
fetchMock.mockImplementationOnce(() =>
|
||
Promise.resolve(releaseResponse('0.1.74')),
|
||
);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
expect(
|
||
(
|
||
await screen.findByRole('link', { name: '下载 Windows 版' })
|
||
).getAttribute('href'),
|
||
).toBe(installerUrl('0.1.74'));
|
||
});
|
||
|
||
it('服务不可用时不显示下载链接,重试后使用成功响应', async () => {
|
||
fetchMock.mockResolvedValueOnce(new Response('', { status: 502 }));
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
expect(await screen.findByRole('alert')).toBeTruthy();
|
||
expect(screen.queryByRole('link')).toBeNull();
|
||
fetchMock.mockImplementationOnce(() =>
|
||
Promise.resolve(releaseResponse('0.1.75')),
|
||
);
|
||
fireEvent.click(screen.getByRole('button', { name: '重试' }));
|
||
expect(
|
||
(
|
||
await screen.findByRole('link', { name: '下载 Windows 版' })
|
||
).getAttribute('href'),
|
||
).toBe(installerUrl('0.1.75'));
|
||
});
|
||
|
||
it('Mac 发布后重新打开即显示各架构和独立版本,未发布时不显示', async () => {
|
||
fetchMock.mockImplementationOnce(() =>
|
||
Promise.resolve(releaseResponse('0.1.73')),
|
||
);
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
await screen.findByRole('link', { name: '下载 Windows 版' });
|
||
expect(screen.queryByText(/macOS/u)).toBeNull();
|
||
fireEvent.keyDown(document, { key: 'Escape' });
|
||
fetchMock.mockImplementationOnce(async () => {
|
||
const windows = await releaseResponse('0.1.74').json();
|
||
return new Response(
|
||
JSON.stringify({
|
||
...windows,
|
||
downloads: [
|
||
...windows.downloads,
|
||
macDownload('aarch64'),
|
||
macDownload('x86_64'),
|
||
],
|
||
}),
|
||
);
|
||
});
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
const arm = await screen.findByRole('region', {
|
||
name: 'macOS(Apple Silicon)',
|
||
});
|
||
const intel = screen.getByRole('region', { name: 'macOS(Intel)' });
|
||
expect(within(arm).getByText('最新版本 v0.2.0')).toBeTruthy();
|
||
expect(within(arm).getByRole('link').getAttribute('href')).toBe(
|
||
macDownload('aarch64').downloadUrl,
|
||
);
|
||
expect(within(intel).getByRole('link').getAttribute('href')).toBe(
|
||
macDownload('x86_64').downloadUrl,
|
||
);
|
||
expect(screen.getByText('最新版本 v0.1.74')).toBeTruthy();
|
||
expect(screen.getAllByRole('link')).toHaveLength(3);
|
||
});
|
||
|
||
it('部分平台失败仍可下载其他平台,重试成功后补齐列表', async () => {
|
||
fetchMock.mockImplementationOnce(async () => {
|
||
const windows = await releaseResponse('0.1.74').json();
|
||
return new Response(
|
||
JSON.stringify({ ...windows, unavailablePlatforms: ['macos'] }),
|
||
);
|
||
});
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
expect(
|
||
(
|
||
await screen.findByRole('link', { name: '下载 Windows 版' })
|
||
).getAttribute('href'),
|
||
).toBe(installerUrl('0.1.74'));
|
||
expect(screen.getByRole('alert').textContent).toContain('macOS');
|
||
fetchMock.mockResolvedValueOnce(
|
||
new Response(
|
||
JSON.stringify({
|
||
downloads: [macDownload('aarch64')],
|
||
unavailablePlatforms: [],
|
||
}),
|
||
),
|
||
);
|
||
fireEvent.click(screen.getByRole('button', { name: '重试' }));
|
||
await screen.findByRole('region', { name: 'macOS(Apple Silicon)' });
|
||
expect(screen.queryByRole('alert')).toBeNull();
|
||
expect(screen.queryByRole('link', { name: '下载 Windows 版' })).toBeNull();
|
||
});
|
||
|
||
it('所有平台尚未发布时显示空状态而不伪造链接', async () => {
|
||
fetchMock.mockResolvedValueOnce(
|
||
new Response(JSON.stringify({ downloads: [], unavailablePlatforms: [] })),
|
||
);
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
expect(await screen.findByText('暂无可下载版本')).toBeTruthy();
|
||
expect(screen.queryByRole('link')).toBeNull();
|
||
expect(screen.getByRole('button', { name: '重试' })).toBeTruthy();
|
||
});
|
||
|
||
it('响应缺少平台列表时显示可重试错误', async () => {
|
||
fetchMock.mockResolvedValueOnce(new Response('{}'));
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
expect(await screen.findByRole('alert')).toBeTruthy();
|
||
expect(screen.queryByRole('link')).toBeNull();
|
||
});
|
||
|
||
it('关闭取消请求,迟到响应不覆盖重开后版本', async () => {
|
||
let resolveFirst!: (response: Response) => void;
|
||
fetchMock.mockImplementationOnce(
|
||
() =>
|
||
new Promise<Response>((resolve) => {
|
||
resolveFirst = resolve;
|
||
}),
|
||
);
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
const signal = fetchMock.mock.calls[0][1].signal as AbortSignal;
|
||
fireEvent.keyDown(document, { key: 'Escape' });
|
||
expect(signal.aborted).toBe(true);
|
||
fetchMock.mockImplementationOnce(() =>
|
||
Promise.resolve(releaseResponse('0.1.76')),
|
||
);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
await screen.findByText('最新版本 v0.1.76');
|
||
await act(async () => {
|
||
resolveFirst(releaseResponse('0.1.73'));
|
||
});
|
||
expect(screen.getByText('最新版本 v0.1.76')).toBeTruthy();
|
||
});
|
||
|
||
it('请求超时后退出加载并允许重试', async () => {
|
||
vi.useFakeTimers();
|
||
fetchMock.mockImplementationOnce(() => new Promise(() => {}));
|
||
render(<ClientDownloadEntry />);
|
||
fireEvent.click(screen.getByRole('button', { name: '下载客户端' }));
|
||
const signal = fetchMock.mock.calls[0][1].signal as AbortSignal;
|
||
await act(async () => {
|
||
vi.advanceTimersByTime(15_000);
|
||
});
|
||
expect(signal.aborted).toBe(true);
|
||
expect(screen.getByRole('alert')).toBeTruthy();
|
||
expect(screen.getByRole('button', { name: '重试' })).toBeTruthy();
|
||
});
|
||
});
|