mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-24 16:02:20 +08:00
+40
-36
@@ -1,24 +1,20 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { observer } from '@formily/react';
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import { SchemaComponentProvider } from '@nocobase/client';
|
||||
import { sleep } from '@nocobase/test';
|
||||
import { sleep } from '@nocobase/test/web';
|
||||
import { act, render, screen, userEvent } from '@nocobase/test/client';
|
||||
import { App } from 'antd';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import React from 'react';
|
||||
import { BackupSettings } from '../components/BackupSettings';
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key), // Mock translation function, return the key directly
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
const storagesData = {
|
||||
data: [
|
||||
@@ -85,20 +81,30 @@ const settingsData = {
|
||||
};
|
||||
|
||||
describe('BackupSettings', () => {
|
||||
const mockedStorages = vi.fn().mockReturnValue(storagesData);
|
||||
const mockedGetSettings = vi.fn().mockReturnValue(settingsData);
|
||||
const mockedUpdateSettings = vi.fn().mockReturnValue({ data: { status: 'ok' } });
|
||||
const { Wrapper, mockRequest } = createMockAppWrapper();
|
||||
const mockedStorages = vi.fn();
|
||||
const mockedGetSettings = vi.fn();
|
||||
const mockedUpdateSettings = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.reset();
|
||||
mockedStorages.mockReset().mockReturnValue(storagesData);
|
||||
mockedGetSettings.mockReset().mockReturnValue(settingsData);
|
||||
mockedUpdateSettings.mockReset().mockReturnValue({ data: { status: 'ok' } });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const MockedBackupSettings = observer(() => {
|
||||
const api = nocobaseClient.useAPIClient();
|
||||
const axioMocked = new MockAdapter(api.axios);
|
||||
axioMocked.onGet(`backupSettings:get/1`).reply(() => {
|
||||
mockRequest.onGet(`backupSettings:get/1`).reply(() => {
|
||||
return [200, mockedGetSettings()];
|
||||
});
|
||||
axioMocked.onGet(`storages:list`).reply(() => {
|
||||
mockRequest.onGet(`storages:list`).reply(() => {
|
||||
return [200, mockedStorages()];
|
||||
});
|
||||
axioMocked.onPost(`backupSettings:update/1`).reply((config) => {
|
||||
mockRequest.onPost(`backupSettings:update/1`).reply((config) => {
|
||||
return [200, mockedUpdateSettings(config.data)];
|
||||
});
|
||||
|
||||
@@ -110,20 +116,19 @@ describe('BackupSettings', () => {
|
||||
});
|
||||
|
||||
test('should render backup settings', async () => {
|
||||
render(<MockedBackupSettings />);
|
||||
expect(screen.getByText('Restore password')).toBeInTheDocument();
|
||||
render(<MockedBackupSettings />, { wrapper: Wrapper });
|
||||
expect(await screen.findByText('Restore password')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should list "sync backup to cloud storage" options', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<MockedBackupSettings />);
|
||||
render(<MockedBackupSettings />, { wrapper: Wrapper });
|
||||
await sleep(3000);
|
||||
const inputEle: HTMLElement = document.querySelector('div.ant-formily-item-control') as HTMLInputElement;
|
||||
const storageCombobox = screen.getAllByRole('combobox').at(-1) as HTMLElement;
|
||||
await act(async () => {
|
||||
await user.click(inputEle);
|
||||
await user.click(storageCombobox);
|
||||
});
|
||||
const optionItems = document.querySelectorAll('span.ant-select-selection-item');
|
||||
expect(optionItems.length).toBe(2);
|
||||
expect(await screen.findByText('ali')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should trigger update backup settings api', async () => {
|
||||
@@ -134,14 +139,13 @@ describe('BackupSettings', () => {
|
||||
},
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<MockedBackupSettings />);
|
||||
render(<MockedBackupSettings />, { wrapper: Wrapper });
|
||||
await sleep(3000);
|
||||
const passwordInput = document.querySelector('input[type=password]') as HTMLInputElement;
|
||||
await act(async () => {
|
||||
await user.click(screen.getByText('Restore password').parentElement.firstElementChild);
|
||||
await user.click(document.querySelector('div.ant-formily-item-control'));
|
||||
await user.click(screen.getByText('ali'));
|
||||
await user.type(document.querySelector('input[type=password]'), '123456');
|
||||
await user.click(screen.getByText('Submit'));
|
||||
await user.clear(passwordInput);
|
||||
await user.type(passwordInput, '123456');
|
||||
await user.click(screen.getByRole('button', { name: /Submit/ }));
|
||||
});
|
||||
expect(mockedUpdateSettings).toHaveBeenCalled();
|
||||
expect(mockedUpdateSettings.mock.calls[0][0]).toEqual(
|
||||
|
||||
+32
-14
@@ -1,32 +1,50 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import { render, waitFor } from '@nocobase/test/client';
|
||||
import React from 'react';
|
||||
import { vi } from 'vitest';
|
||||
import { BackupsManagement } from '../components/BackupsManagement';
|
||||
import { NAMESPACE } from '../constants';
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key), // Mock translation function, return the key directly
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../BackupsTable', async () => {
|
||||
vi.mock('../components/BackupsTable', async () => {
|
||||
return {
|
||||
BackupsTable: vi.fn().mockImplementation(() => <div>Backup Table</div>),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../components/NewBackup', async () => {
|
||||
return {
|
||||
NewBackup: vi.fn().mockImplementation(() => <div>New Backup</div>),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../components/RefreshBackups', async () => {
|
||||
return {
|
||||
RefreshBackups: vi.fn().mockImplementation(() => <div>Refresh Backups</div>),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../components/RestoreFromLocal', async () => {
|
||||
return {
|
||||
RestoreFromLocal: vi.fn().mockImplementation(() => <div>Restore From Local</div>),
|
||||
};
|
||||
});
|
||||
|
||||
describe('BackupsManagement', () => {
|
||||
const { Wrapper } = createMockAppWrapper();
|
||||
|
||||
test('should query the backups list data on render', async () => {
|
||||
const spy = vi.spyOn(nocobaseClient, 'useRequest').mockReturnValue({} as any);
|
||||
render(<BackupsManagement />);
|
||||
render(<BackupsManagement />, { wrapper: Wrapper });
|
||||
await waitFor(() => {
|
||||
expect(spy).toHaveBeenCalledWith({
|
||||
url: `${NAMESPACE}:list`,
|
||||
|
||||
+26
-25
@@ -10,13 +10,13 @@
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import { render, screen, userEvent, waitFor } from '@nocobase/test/client';
|
||||
import { App } from 'antd';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { saveAs } from 'file-saver';
|
||||
import React from 'react';
|
||||
import { describe, vi } from 'vitest';
|
||||
import { BackupFile, BackupsTable } from '../components/BackupsTable';
|
||||
import { NAMESPACE } from '../constants';
|
||||
import { BackupsContext } from '../contexts';
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
const mockedData = {
|
||||
data: [
|
||||
@@ -33,19 +33,6 @@ const mockedData = {
|
||||
],
|
||||
};
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key),
|
||||
},
|
||||
}),
|
||||
useCurrentAppInfo: () => undefined,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('file-saver', async () => {
|
||||
return {
|
||||
saveAs: vi.fn(),
|
||||
@@ -53,14 +40,28 @@ vi.mock('file-saver', async () => {
|
||||
});
|
||||
|
||||
describe('BackupsTable', () => {
|
||||
let mocked: MockAdapter;
|
||||
const { Wrapper, mockRequest } = createMockAppWrapper();
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.reset();
|
||||
vi.spyOn(nocobaseClient, 'useCurrentAppInfo').mockReturnValue(undefined as any);
|
||||
mockRequest.onGet(`${NAMESPACE}:appInfo`).reply(200, {
|
||||
data: {
|
||||
database: {
|
||||
dialect: 'sqlite',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const MockedTable = () => {
|
||||
const api = nocobaseClient.useAPIClient();
|
||||
mocked = new MockAdapter(api.axios);
|
||||
mocked.onGet('backups:list').reply(200, mockedData);
|
||||
mocked.onGet('backups:download').reply(200, 'mocked-backup-content');
|
||||
mocked.onPost('backups:destroy').reply(200, {});
|
||||
mockRequest.onGet('backups:list').reply(200, mockedData);
|
||||
mockRequest.onGet('backups:download').reply(200, 'mocked-backup-content');
|
||||
mockRequest.onPost('backups:destroy').reply(200, {});
|
||||
|
||||
const useRequestValues = nocobaseClient.useRequest<{ data: BackupFile[] }>({
|
||||
url: `${NAMESPACE}:list`,
|
||||
@@ -75,7 +76,7 @@ describe('BackupsTable', () => {
|
||||
};
|
||||
|
||||
test('should render table with correct columns and data', async () => {
|
||||
render(<MockedTable />);
|
||||
render(<MockedTable />, { wrapper: Wrapper });
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Backup list')).toBeInTheDocument();
|
||||
expect(screen.getByText('backup_20240818_182301_9056.nbdata')).toBeInTheDocument();
|
||||
@@ -84,7 +85,7 @@ describe('BackupsTable', () => {
|
||||
|
||||
test('should handle download action', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<MockedTable />);
|
||||
render(<MockedTable />, { wrapper: Wrapper });
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByText('Download')[0]).toBeInTheDocument();
|
||||
});
|
||||
@@ -92,7 +93,7 @@ describe('BackupsTable', () => {
|
||||
await user.click(screen.getAllByText('Download')[0]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocked.history.get).toEqual(
|
||||
expect(mockRequest.history.get).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: 'backups:download',
|
||||
@@ -116,7 +117,7 @@ describe('BackupsTable', () => {
|
||||
},
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<MockedTable />);
|
||||
render(<MockedTable />, { wrapper: Wrapper });
|
||||
await waitFor(async () => {
|
||||
await user.click(screen.getAllByText('Delete')[0]);
|
||||
expect(modalConfirmSpy).toHaveBeenCalled();
|
||||
@@ -124,7 +125,7 @@ describe('BackupsTable', () => {
|
||||
});
|
||||
|
||||
test('should handle backing up data', async () => {
|
||||
render(<MockedTable />);
|
||||
render(<MockedTable />, { wrapper: Wrapper });
|
||||
await waitFor(async () => {
|
||||
expect(screen.getByText('Backup list')).toBeInTheDocument();
|
||||
expect(screen.getByRole('cell', { name: /Backing up/i })).toBeInTheDocument();
|
||||
|
||||
@@ -1,46 +1,52 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import { useAPIClient, useRequest } from '@nocobase/client';
|
||||
import { useRequest } from '@nocobase/client';
|
||||
import { act, render, screen, userEvent, waitFor } from '@nocobase/test/client';
|
||||
import { App } from 'antd';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import React from 'react';
|
||||
import { BackupFile } from '../components/BackupsTable';
|
||||
import { NewBackup } from '../components/NewBackup';
|
||||
import { NAMESPACE } from '../constants';
|
||||
import { BackupsContext } from '../contexts';
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key), // Mock translation function, return the key directly
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
describe('NewBackup', () => {
|
||||
let axioMocked: MockAdapter;
|
||||
const mockedRefresh = vi.fn().mockReturnValue({ data: [] });
|
||||
const mockedCreate = vi
|
||||
.fn()
|
||||
.mockReturnValue({ data: { name: 'backup_20240818_182302_2122.nbdata', inProgress: true } });
|
||||
const mockedStatus = vi
|
||||
.fn()
|
||||
.mockReturnValue({ data: { 'backup_20240818_182302_2122.nbdata': { inProgress: false } } });
|
||||
const { Wrapper, mockRequest } = createMockAppWrapper();
|
||||
const mockedRefresh = vi.fn();
|
||||
const mockedCreate = vi.fn();
|
||||
const mockedStatus = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.reset();
|
||||
mockedRefresh.mockReset().mockReturnValue({ data: [] });
|
||||
mockedCreate.mockReset().mockReturnValue({
|
||||
data: { name: 'backup_20240818_182302_2122.nbdata', inProgress: true },
|
||||
});
|
||||
mockedStatus.mockReset().mockReturnValue({
|
||||
data: { 'backup_20240818_182302_2122.nbdata': { inProgress: false } },
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const MockedNewBackupBtn = () => {
|
||||
const api = useAPIClient();
|
||||
axioMocked = new MockAdapter(api.axios);
|
||||
axioMocked.onGet(`${NAMESPACE}:list`).reply(() => {
|
||||
mockRequest.onGet(`${NAMESPACE}:list`).reply(() => {
|
||||
return [200, mockedRefresh()];
|
||||
});
|
||||
axioMocked.onPost(`${NAMESPACE}:create`).reply(() => {
|
||||
mockRequest.onPost(`${NAMESPACE}:create`).reply(() => {
|
||||
return [200, mockedCreate()];
|
||||
});
|
||||
axioMocked.onGet(`${NAMESPACE}:taskStatus`).reply(() => {
|
||||
mockRequest.onGet(`${NAMESPACE}:taskStatus`).reply(() => {
|
||||
return [200, mockedStatus()];
|
||||
});
|
||||
|
||||
@@ -57,7 +63,7 @@ describe('NewBackup', () => {
|
||||
};
|
||||
|
||||
test('should render new backup button', async () => {
|
||||
render(<MockedNewBackupBtn />);
|
||||
render(<MockedNewBackupBtn />, { wrapper: Wrapper });
|
||||
expect(screen.getByText('New backup')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -73,18 +79,20 @@ describe('NewBackup', () => {
|
||||
},
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<MockedNewBackupBtn />);
|
||||
render(<MockedNewBackupBtn />, { wrapper: Wrapper });
|
||||
const newBackupBtn = screen.getByText('New backup');
|
||||
await act(async () => {
|
||||
await user.click(newBackupBtn);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockedCreate).toHaveBeenCalled();
|
||||
expect(mockedRefresh).toHaveBeenCalled();
|
||||
});
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(mockedCreate).toHaveBeenCalled();
|
||||
expect(mockedStatus).toHaveBeenCalled();
|
||||
expect(mockedRefresh).toHaveBeenCalled();
|
||||
},
|
||||
{ interval: 1000 },
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+24
-22
@@ -1,32 +1,31 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { NAMESPACE } from '../constants';
|
||||
import { BackupFile } from '../components/BackupsTable';
|
||||
import { BackupsContext } from '../contexts';
|
||||
import React from 'react';
|
||||
import { act, render, screen, userEvent, waitFor } from '@nocobase/test/client';
|
||||
import { RefreshBackups } from '../components/RefreshBackups';
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key), // Mock translation function, return the key directly
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
describe('RefreshBackups', () => {
|
||||
let axioMocked: MockAdapter;
|
||||
const { Wrapper, mockRequest } = createMockAppWrapper();
|
||||
const mockedRefresh = vi.fn().mockReturnValue({ data: [] });
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.reset();
|
||||
});
|
||||
|
||||
const MockedRefreshBackupsBtn = () => {
|
||||
const api = nocobaseClient.useAPIClient();
|
||||
axioMocked = new MockAdapter(api.axios);
|
||||
axioMocked.onGet(`${NAMESPACE}:list`).reply(() => {
|
||||
mockRequest.onGet(`${NAMESPACE}:list`).reply(() => {
|
||||
return [200, mockedRefresh()];
|
||||
});
|
||||
|
||||
@@ -43,20 +42,23 @@ describe('RefreshBackups', () => {
|
||||
};
|
||||
|
||||
test('should render refresh backups button', async () => {
|
||||
render(<MockedRefreshBackupsBtn />);
|
||||
render(<MockedRefreshBackupsBtn />, { wrapper: Wrapper });
|
||||
expect(screen.getByText('Refresh')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should trigger refresh backup api', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<MockedRefreshBackupsBtn />);
|
||||
render(<MockedRefreshBackupsBtn />, { wrapper: Wrapper });
|
||||
expect(mockedRefresh).toHaveBeenCalledTimes(1);
|
||||
const newBackupBtn = screen.getByText('Refresh');
|
||||
const newBackupBtn = await screen.findByRole('button', { name: /Refresh/ });
|
||||
await waitFor(() => {
|
||||
expect(newBackupBtn).toBeEnabled();
|
||||
});
|
||||
await act(async () => {
|
||||
user.click(newBackupBtn);
|
||||
await user.click(newBackupBtn);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockedRefresh).toHaveBeenCalledTimes(2);
|
||||
expect(mockedRefresh.mock.calls.length).toBeGreaterThan(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+27
-21
@@ -1,31 +1,37 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { NAMESPACE } from '../constants';
|
||||
import { BackupFile } from '../components/BackupsTable';
|
||||
import React from 'react';
|
||||
import { act, render, screen, userEvent, waitFor } from '@nocobase/test/client';
|
||||
import { RestoreFromBackup } from '../components/RestoreFromBackup';
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key), // Mock translation function, return the key directly
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
describe('RestoreFromBackup', () => {
|
||||
let axioMocked: MockAdapter;
|
||||
const { Wrapper, mockRequest } = createMockAppWrapper();
|
||||
const mockedRestore = vi.fn().mockReturnValue({ data: { status: 'ok' } });
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.reset();
|
||||
mockRequest.onGet(`${NAMESPACE}:appInfo`).reply(200, {
|
||||
data: {
|
||||
database: {
|
||||
dialect: 'sqlite',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const MockedRestoreFromBackup = () => {
|
||||
const api = nocobaseClient.useAPIClient();
|
||||
axioMocked = new MockAdapter(api.axios);
|
||||
axioMocked.onPost(`${NAMESPACE}:restore`).reply(() => {
|
||||
mockRequest.onPost(`${NAMESPACE}:restore`).reply(() => {
|
||||
return [200, mockedRestore()];
|
||||
});
|
||||
|
||||
@@ -40,21 +46,21 @@ describe('RestoreFromBackup', () => {
|
||||
};
|
||||
|
||||
test('should render restore from backup button', async () => {
|
||||
render(<MockedRestoreFromBackup />);
|
||||
render(<MockedRestoreFromBackup />, { wrapper: Wrapper });
|
||||
expect(screen.getByText('Restore')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should trigger restore api', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<MockedRestoreFromBackup />);
|
||||
render(<MockedRestoreFromBackup />, { wrapper: Wrapper });
|
||||
await act(async () => {
|
||||
user.click(screen.getByText('Restore'));
|
||||
await user.click(screen.getByText('Restore'));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||
});
|
||||
await act(async () => {
|
||||
user.click(screen.getByText('Submit'));
|
||||
await user.click(screen.getByText('Submit'));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockedRestore).toHaveBeenCalled();
|
||||
|
||||
+28
-24
@@ -1,55 +1,59 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import * as nocobaseClient from '@nocobase/client';
|
||||
import { act, fireEvent, render, screen, userEvent, waitFor } from '@nocobase/test/client';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import React from 'react';
|
||||
import { RestoreFromLocal } from '../components/RestoreFromLocal';
|
||||
import { NAMESPACE } from '../constants';
|
||||
|
||||
vi.mock('@nocobase/client', async () => {
|
||||
const actual = await vi.importActual<typeof nocobaseClient>('@nocobase/client');
|
||||
return {
|
||||
...actual,
|
||||
useApp: () => ({
|
||||
i18n: {
|
||||
t: vi.fn().mockImplementation((key) => key), // Mock translation function, return the key directly
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
import { createMockAppWrapper } from './testUtils';
|
||||
|
||||
describe('RestoreFromLocal', () => {
|
||||
let axioMocked: MockAdapter;
|
||||
const { Wrapper, mockRequest } = createMockAppWrapper();
|
||||
const mockedRestore = vi.fn().mockReturnValue({ data: { status: 'ok' } });
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.reset();
|
||||
mockRequest.onGet(`${NAMESPACE}:appInfo`).reply(200, {
|
||||
data: {
|
||||
database: {
|
||||
dialect: 'sqlite',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const MockedRestoreFromLocal = () => {
|
||||
const api = nocobaseClient.useAPIClient();
|
||||
axioMocked = new MockAdapter(api.axios);
|
||||
axioMocked.onPost(`${NAMESPACE}:upload`).reply(() => {
|
||||
mockRequest.onPost(`${NAMESPACE}:upload`).reply(() => {
|
||||
return [200, mockedRestore()];
|
||||
});
|
||||
return <RestoreFromLocal />;
|
||||
};
|
||||
|
||||
test('should render restore from backup button', async () => {
|
||||
render(<MockedRestoreFromLocal />);
|
||||
render(<MockedRestoreFromLocal />, { wrapper: Wrapper });
|
||||
expect(screen.getByText('Restore backup from local')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should trigger restore api', async () => {
|
||||
const file = new File([''], 'backup_20240818_182302_2122.nbdata', { type: 'application/octet-stream' });
|
||||
const user = userEvent.setup();
|
||||
render(<MockedRestoreFromLocal />);
|
||||
render(<MockedRestoreFromLocal />, { wrapper: Wrapper });
|
||||
await act(async () => {
|
||||
user.click(screen.getByText('Restore backup from local'));
|
||||
await user.click(screen.getByText('Restore backup from local'));
|
||||
});
|
||||
await waitFor(() => {
|
||||
const fileBtn = screen.getByText((content, element) => {
|
||||
return element.tagName.toLowerCase() === 'input' && element['type'] === 'file';
|
||||
});
|
||||
const fileBtn = document.querySelector('input[type="file"]') as HTMLInputElement;
|
||||
fireEvent.change(fileBtn, { target: { files: [file] } });
|
||||
});
|
||||
await act(async () => {
|
||||
user.click(screen.getByText('Submit'));
|
||||
await user.click(screen.getByText('Submit'));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockedRestore).toHaveBeenCalled();
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { APIClientProvider, Application, ApplicationContext, mockAPIClient } from '@nocobase/client';
|
||||
import { App as AntdApp } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (query: string) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: () => {},
|
||||
removeListener: () => {},
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {},
|
||||
dispatchEvent: () => false,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export const createMockAppWrapper = () => {
|
||||
const { apiClient, mockRequest } = mockAPIClient();
|
||||
const app = new Application({
|
||||
apiClient,
|
||||
router: {
|
||||
type: 'memory',
|
||||
},
|
||||
});
|
||||
|
||||
app.i18n.t = ((key: string) => key) as any;
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => {
|
||||
return (
|
||||
<ApplicationContext.Provider value={app}>
|
||||
<APIClientProvider apiClient={app.apiClient}>
|
||||
<AntdApp>{children}</AntdApp>
|
||||
</APIClientProvider>
|
||||
</ApplicationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
app,
|
||||
apiClient: app.apiClient,
|
||||
mockRequest,
|
||||
Wrapper,
|
||||
};
|
||||
};
|
||||
+21
@@ -17,6 +17,8 @@ import fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import PluginFileManagerServer from '@nocobase/plugin-file-manager';
|
||||
import yauzl from 'yauzl';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Readable, Writable } from 'stream';
|
||||
|
||||
vi.mock('child_process', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof cp>();
|
||||
@@ -31,6 +33,24 @@ vi.mock('child_process', async (importOriginal) => {
|
||||
}
|
||||
callback(null, 'done');
|
||||
}),
|
||||
spawn: vi.fn().mockImplementation(() => {
|
||||
const stdout = Readable.from(['mocked database backup']);
|
||||
const stderr = Readable.from([]);
|
||||
const stdin = new Writable({
|
||||
write(_chunk, _encoding, callback) {
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const childProcess = new EventEmitter() as any;
|
||||
childProcess.stdout = stdout;
|
||||
childProcess.stderr = stderr;
|
||||
childProcess.stdin = stdin;
|
||||
setImmediate(() => {
|
||||
childProcess.emit('exit', 0);
|
||||
childProcess.emit('close', 0);
|
||||
});
|
||||
return childProcess;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -69,6 +89,7 @@ describe('BackupManager', async () => {
|
||||
beforeEach(async () => {
|
||||
app = await getApp();
|
||||
await fs.promises.mkdir(backupFilesFolder, { recursive: true });
|
||||
await fs.promises.unlink(finalBackupFilePath).catch(() => {});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
+43
@@ -19,11 +19,32 @@ import { BackupManager, BackupSettings } from '../../managers/backup';
|
||||
import { RestoreManager } from '../../managers/restore';
|
||||
import backupCliResource from '../../resourcers/backup-cli';
|
||||
import backupsResource from '../../resourcers/backups';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Readable, Writable } from 'stream';
|
||||
|
||||
let mockExecImplementation = (command, _options, callback) => {
|
||||
callback(null, 'done');
|
||||
};
|
||||
|
||||
let mockSpawnImplementation = () => {
|
||||
const stdout = Readable.from(['mocked database backup']);
|
||||
const stderr = Readable.from([]);
|
||||
const stdin = new Writable({
|
||||
write(_chunk, _encoding, callback) {
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const childProcess = new EventEmitter() as any;
|
||||
childProcess.stdout = stdout;
|
||||
childProcess.stderr = stderr;
|
||||
childProcess.stdin = stdin;
|
||||
setImmediate(() => {
|
||||
childProcess.emit('exit', 0);
|
||||
childProcess.emit('close', 0);
|
||||
});
|
||||
return childProcess;
|
||||
};
|
||||
|
||||
vi.mock('child_process', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof cp>();
|
||||
return {
|
||||
@@ -32,6 +53,7 @@ vi.mock('child_process', async (importOriginal) => {
|
||||
exec: vi
|
||||
.fn()
|
||||
.mockImplementation((command, options, callback) => mockExecImplementation(command, options, callback)),
|
||||
spawn: vi.fn().mockImplementation((command, args, options) => mockSpawnImplementation(command, args, options)),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -71,6 +93,8 @@ describe('RestoreManager', () => {
|
||||
beforeEach(async () => {
|
||||
app = await getApp();
|
||||
await fs.promises.mkdir(backupFilesFolder, { recursive: true });
|
||||
await fs.promises.unlink(finalBackupFilePath).catch(() => {});
|
||||
await fs.promises.unlink(schemaMismatchBackupFilePath).catch(() => {});
|
||||
|
||||
mockExecImplementation = (command, _options, callback) => {
|
||||
if (command.includes('-f')) {
|
||||
@@ -94,6 +118,25 @@ describe('RestoreManager', () => {
|
||||
}
|
||||
callback(null, 'done');
|
||||
};
|
||||
|
||||
mockSpawnImplementation = () => {
|
||||
const stdout = Readable.from(['mocked database backup']);
|
||||
const stderr = Readable.from([]);
|
||||
const stdin = new Writable({
|
||||
write(_chunk, _encoding, callback) {
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const childProcess = new EventEmitter() as any;
|
||||
childProcess.stdout = stdout;
|
||||
childProcess.stderr = stderr;
|
||||
childProcess.stdin = stdin;
|
||||
setImmediate(() => {
|
||||
childProcess.emit('exit', 0);
|
||||
childProcess.emit('close', 0);
|
||||
});
|
||||
return childProcess;
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
Reference in New Issue
Block a user