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(); 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(); 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(); 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(); expect(screen.getByText('Generic update failure')).toBeInTheDocument(); expect(screen.queryByText(error)).not.toBeInTheDocument(); }); it('keeps concise actionable Chinese prose', () => { render(); expect(screen.getByText(actionableMessage)).toBeInTheDocument(); }); it('initializes on mount and retries through the existing store action', async () => { render(); await waitFor(() => expect(init).toHaveBeenCalledTimes(1)); fireEvent.click(screen.getByRole('button', { name: 'Retry' })); await waitFor(() => expect(checkForUpdates).toHaveBeenCalledTimes(1)); expect(clearError).toHaveBeenCalledTimes(1); }); });