98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { UpdateSettings } from '@/components/settings/UpdateSettings';
|
|
import { useUpdateStore } from '@/stores/update';
|
|
|
|
const actionableMessage =
|
|
'\u5f53\u524d\u5e73\u53f0\u7684\u6b63\u5f0f\u66f4\u65b0\u5305\u5c1a\u672a\u53d1\u5e03\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002';
|
|
|
|
vi.mock('react-i18next', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('react-i18next')>();
|
|
return {
|
|
...actual,
|
|
useTranslation: () => ({
|
|
t: (key: string) =>
|
|
({
|
|
'updates.currentVersion': 'Current version',
|
|
'updates.status.failed': 'Generic update failure',
|
|
'updates.action.retry': 'Retry',
|
|
'updates.help': 'Keep Makelore updated',
|
|
})[key] ?? key,
|
|
}),
|
|
};
|
|
});
|
|
|
|
describe('UpdateSettings update errors', () => {
|
|
const init = vi.fn().mockResolvedValue(undefined);
|
|
const checkForUpdates = vi.fn().mockResolvedValue(undefined);
|
|
const clearError = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
useUpdateStore.setState({
|
|
status: 'error',
|
|
currentVersion: '1.0.0',
|
|
updateInfo: null,
|
|
progress: null,
|
|
error: actionableMessage,
|
|
isInitialized: true,
|
|
isInitializing: false,
|
|
autoInstallCountdown: null,
|
|
init,
|
|
checkForUpdates,
|
|
clearError,
|
|
});
|
|
});
|
|
|
|
it('shows a concise updater error only once', () => {
|
|
render(<UpdateSettings />);
|
|
|
|
expect(screen.getAllByText(actionableMessage)).toHaveLength(1);
|
|
});
|
|
|
|
it.each([
|
|
'Cannot find channel "latest.yml" update info: HttpError: 404\r\nPlease double check authentication\r\n at ElectronHttpExecutor.handleResponse (app.asar/node_modules/builder-util-runtime/out/httpExecutor.js:121:20)',
|
|
'Cannot find channel "latest.yml" update info: HttpError: 404\\r\\nPlease double check authentication\\n at ElectronHttpExecutor.handleResponse (app.asar/node_modules/builder-util-runtime/out/httpExecutor.js:121:20)',
|
|
])('does not expose updater stack details for a long technical error', (error) => {
|
|
useUpdateStore.setState({ error });
|
|
|
|
render(<UpdateSettings />);
|
|
|
|
expect(screen.getByText('Generic update failure')).toBeInTheDocument();
|
|
expect(screen.queryByText(/ElectronHttpExecutor|node_modules|Please double check/)).not.toBeInTheDocument();
|
|
expect(screen.queryByText('updates.errorDetails')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it.each([
|
|
'net::ERR_NAME_NOT_RESOLVED',
|
|
'Error: Update check failed',
|
|
'Update check temporarily unavailable',
|
|
"ENOENT: no such file or directory, open 'C:\\Users\\tester\\AppData\\Local\\Makelore\\latest.yml'",
|
|
'ERR_UPDATER_INVALID_RELEASE_FEED',
|
|
'{"code":"ERR_UPDATER_INVALID_RELEASE_FEED","message":"Update check failed"}',
|
|
])('falls back for a short technical updater diagnostic: %s', (error) => {
|
|
useUpdateStore.setState({ error });
|
|
|
|
render(<UpdateSettings />);
|
|
|
|
expect(screen.getByText('Generic update failure')).toBeInTheDocument();
|
|
expect(screen.queryByText(error)).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('keeps concise actionable Chinese prose', () => {
|
|
render(<UpdateSettings />);
|
|
|
|
expect(screen.getByText(actionableMessage)).toBeInTheDocument();
|
|
});
|
|
|
|
it('initializes on mount and retries through the existing store action', async () => {
|
|
render(<UpdateSettings />);
|
|
|
|
await waitFor(() => expect(init).toHaveBeenCalledTimes(1));
|
|
fireEvent.click(screen.getByRole('button', { name: 'Retry' }));
|
|
|
|
await waitFor(() => expect(checkForUpdates).toHaveBeenCalledTimes(1));
|
|
expect(clearError).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|