98 lines
3.7 KiB
TypeScript
98 lines
3.7 KiB
TypeScript
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { SidebarUpdateButton } from '@/components/layout/SidebarUpdateButton';
|
|
import { useUpdateStore } from '@/stores/update';
|
|
|
|
const toastError = vi.hoisted(() => vi.fn());
|
|
vi.mock('sonner', () => ({ toast: { error: toastError, success: vi.fn() } }));
|
|
|
|
describe('SidebarUpdateButton', () => {
|
|
const init = vi.fn().mockResolvedValue(undefined);
|
|
const downloadUpdate = vi.fn().mockResolvedValue(undefined);
|
|
const installUpdate = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
downloadUpdate.mockResolvedValue(undefined);
|
|
useUpdateStore.setState({
|
|
status: 'idle',
|
|
currentVersion: '0.9.1',
|
|
updateInfo: null,
|
|
progress: null,
|
|
error: null,
|
|
isInitialized: true,
|
|
isInitializing: false,
|
|
autoInstallCountdown: null,
|
|
init,
|
|
downloadUpdate,
|
|
installUpdate,
|
|
});
|
|
});
|
|
|
|
it.each(['idle', 'checking', 'not-available', 'error'] as const)(
|
|
'stays hidden while the update status is %s',
|
|
(status) => {
|
|
useUpdateStore.setState({ status });
|
|
render(<SidebarUpdateButton collapsed={false} />);
|
|
|
|
expect(screen.queryByTestId('sidebar-update-button')).not.toBeInTheDocument();
|
|
},
|
|
);
|
|
|
|
it('initializes update discovery even while the control is hidden', async () => {
|
|
render(<SidebarUpdateButton collapsed={false} />);
|
|
|
|
expect(screen.queryByTestId('sidebar-update-button')).not.toBeInTheDocument();
|
|
await waitFor(() => expect(init).toHaveBeenCalledTimes(1));
|
|
});
|
|
|
|
it('shows the download action when an update is available', () => {
|
|
useUpdateStore.setState({ status: 'available', updateInfo: { version: '0.9.2' } });
|
|
render(<SidebarUpdateButton collapsed={false} />);
|
|
|
|
expect(screen.getByTestId('sidebar-update-button')).toHaveAccessibleName('下载新版本 0.9.2');
|
|
});
|
|
|
|
it('downloads an available version on the first click', async () => {
|
|
useUpdateStore.setState({ status: 'available', updateInfo: { version: '0.9.2' } });
|
|
render(<SidebarUpdateButton collapsed={false} />);
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '下载新版本 0.9.2' }));
|
|
|
|
await waitFor(() => expect(downloadUpdate).toHaveBeenCalledTimes(1));
|
|
expect(installUpdate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('disables duplicate actions while downloading', () => {
|
|
useUpdateStore.setState({ status: 'downloading', updateInfo: { version: '0.9.2' } });
|
|
render(<SidebarUpdateButton collapsed={false} />);
|
|
|
|
expect(screen.getByTestId('sidebar-update-button')).toBeDisabled();
|
|
expect(screen.getByTestId('sidebar-update-button')).toHaveAccessibleName('正在下载新版本 0.9.2');
|
|
});
|
|
|
|
it('installs only after the update is downloaded', () => {
|
|
useUpdateStore.setState({ status: 'downloaded', updateInfo: { version: '0.9.2' } });
|
|
render(<SidebarUpdateButton collapsed />);
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '重启并安装新版本 0.9.2' }));
|
|
|
|
expect(installUpdate).toHaveBeenCalledTimes(1);
|
|
expect(downloadUpdate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('shows a bounded toast when download fails', async () => {
|
|
downloadUpdate.mockImplementationOnce(async () => {
|
|
useUpdateStore.setState({ status: 'error', error: 'Remote update download failed' });
|
|
});
|
|
useUpdateStore.setState({ status: 'available', updateInfo: { version: '0.9.2' } });
|
|
render(<SidebarUpdateButton collapsed={false} />);
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '下载新版本 0.9.2' }));
|
|
|
|
await waitFor(() => expect(toastError).toHaveBeenCalledWith('更新下载失败', {
|
|
description: 'Remote update download failed',
|
|
}));
|
|
});
|
|
});
|