172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 AionUi (aionui.com)
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import type { AutoUpdateStatus, UpdateDownloadProgressEvent, UpdateDownloadRequest } from '@/common/update/updateTypes';
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
manualProgressHandler: null as ((evt: UpdateDownloadProgressEvent) => void) | null,
|
|
autoStatusHandler: null as ((evt: AutoUpdateStatus) => void) | null,
|
|
autoUpdateCheckMock: vi.fn(),
|
|
autoUpdateRestoreDownloadedMock: vi.fn(),
|
|
updateCheckMock: vi.fn(),
|
|
updateDownloadMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({ t: (key: string) => key }),
|
|
}));
|
|
|
|
vi.mock('@/renderer/components/base/AionModal', () => ({
|
|
default: ({ children, visible }: { children: React.ReactNode; visible: boolean }) =>
|
|
visible ? <div>{children}</div> : null,
|
|
}));
|
|
|
|
vi.mock('@/renderer/components/Markdown', () => ({
|
|
default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
}));
|
|
|
|
vi.mock('@/common', () => ({
|
|
ipcBridge: {
|
|
autoUpdate: {
|
|
check: { invoke: mocks.autoUpdateCheckMock },
|
|
restoreDownloaded: { invoke: mocks.autoUpdateRestoreDownloadedMock },
|
|
download: { invoke: vi.fn() },
|
|
quitAndInstall: { invoke: vi.fn() },
|
|
status: {
|
|
on: vi.fn((handler: (evt: AutoUpdateStatus) => void) => {
|
|
mocks.autoStatusHandler = handler;
|
|
return vi.fn();
|
|
}),
|
|
},
|
|
},
|
|
update: {
|
|
check: { invoke: mocks.updateCheckMock },
|
|
download: { invoke: mocks.updateDownloadMock },
|
|
downloadProgress: {
|
|
on: vi.fn((handler: (evt: UpdateDownloadProgressEvent) => void) => {
|
|
mocks.manualProgressHandler = handler;
|
|
return vi.fn();
|
|
}),
|
|
},
|
|
consumeInstallerLastFailure: { invoke: vi.fn().mockResolvedValue({ success: true, data: null }) },
|
|
open: { on: vi.fn(() => vi.fn()) },
|
|
},
|
|
shell: {
|
|
openExternal: { invoke: vi.fn() },
|
|
openFile: { invoke: vi.fn() },
|
|
showItemInFolder: { invoke: vi.fn() },
|
|
},
|
|
},
|
|
}));
|
|
|
|
import UpdateModal from '@/renderer/components/settings/UpdateModal';
|
|
|
|
describe('UpdateModal manual install fallback', () => {
|
|
beforeEach(() => {
|
|
vi.stubGlobal('__APP_VERSION__', '2.1.15');
|
|
mocks.manualProgressHandler = null;
|
|
mocks.autoStatusHandler = null;
|
|
mocks.autoUpdateCheckMock.mockResolvedValue({ success: true });
|
|
mocks.autoUpdateRestoreDownloadedMock.mockResolvedValue({ success: true, data: { ready: false } });
|
|
mocks.updateCheckMock.mockResolvedValue({
|
|
success: true,
|
|
data: {
|
|
currentVersion: '2.1.13',
|
|
updateAvailable: true,
|
|
latest: {
|
|
tagName: 'v2.1.14',
|
|
version: '2.1.14',
|
|
name: 'v2.1.14',
|
|
body: 'notes',
|
|
htmlUrl: 'https://github.com/iOfficeAI/AionUi/releases/tag/v2.1.14',
|
|
prerelease: false,
|
|
draft: false,
|
|
assets: [],
|
|
recommendedAsset: {
|
|
name: 'AionUi-2.1.14-mac-arm64.dmg',
|
|
url: 'https://static.aionui.com/releases/2.1.14/AionUi-2.1.14-mac-arm64.dmg',
|
|
fallbackUrl: 'https://github.com/iOfficeAI/AionUi/releases/download/v2.1.14/AionUi-2.1.14-mac-arm64.dmg',
|
|
size: 123,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
mocks.updateDownloadMock.mockImplementation(async (request: UpdateDownloadRequest) => {
|
|
const downloadId = request.downloadId ?? 'missing-download-id';
|
|
mocks.manualProgressHandler?.({
|
|
downloadId,
|
|
status: 'completed',
|
|
receivedBytes: 123,
|
|
totalBytes: 123,
|
|
percent: 100,
|
|
file_path: '/tmp/AionUi-2.1.14-mac-arm64.dmg',
|
|
});
|
|
return {
|
|
success: true,
|
|
data: {
|
|
downloadId,
|
|
file_path: '/tmp/AionUi-2.1.14-mac-arm64.dmg',
|
|
},
|
|
};
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it('keeps fast manual download completion matched to the caller-provided download id', async () => {
|
|
const user = userEvent.setup();
|
|
render(<UpdateModal />);
|
|
|
|
act(() => {
|
|
window.dispatchEvent(new Event('aionui-open-update-modal'));
|
|
});
|
|
|
|
const downloadAndInstall = await screen.findByText('update.downloadButton');
|
|
await user.click(downloadAndInstall);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('update.restartNow')).toBeInTheDocument();
|
|
});
|
|
|
|
expect(mocks.updateDownloadMock).toHaveBeenCalledWith({
|
|
downloadId: expect.any(String),
|
|
url: 'https://static.aionui.com/releases/2.1.14/AionUi-2.1.14-mac-arm64.dmg',
|
|
fallbackUrl: 'https://github.com/iOfficeAI/AionUi/releases/download/v2.1.14/AionUi-2.1.14-mac-arm64.dmg',
|
|
file_name: 'AionUi-2.1.14-mac-arm64.dmg',
|
|
});
|
|
expect(screen.queryByText('update.manualInstall')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('loads release notes when auto-update status opens the modal', async () => {
|
|
render(<UpdateModal />);
|
|
|
|
act(() => {
|
|
mocks.autoStatusHandler?.({
|
|
status: 'available',
|
|
version: '2.1.14',
|
|
currentVersion: '2.1.13',
|
|
});
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(mocks.updateCheckMock).toHaveBeenCalled();
|
|
});
|
|
|
|
// Release notes now live in the Release Log modal, opened via the link.
|
|
fireEvent.click(await screen.findByText('update.releaseLog'));
|
|
expect(await screen.findByText('notes')).toBeInTheDocument();
|
|
expect(screen.getByText(/2\.1\.13/)).toBeInTheDocument();
|
|
});
|
|
});
|