import {t} from '@/scripts/i18n'; import * as fetch from '@/scripts/net'; import PluginsManagement from '@/views/admin/PluginsManagement'; import {fireEvent, render, waitFor} from '@testing-library/react'; import { expect, it, vi, } from 'vitest'; vi.mock('@/scripts/net'); it('show loading indicator', () => { fetch.get.mockResolvedValue([]); const {queryByTitle} = render(); expect(queryByTitle('Loading...')).not.toBeNull(); }); it('plugin info box', async () => { fetch.get.mockResolvedValue([ { name: 'a', title: 'My Plugin', version: '1.0.0', description: 'desc', config: true, readme: true, icon: {}, enabled: true, }, { name: 'b', title: 'Another Plugin', version: '0.1.0', description: '', config: false, readme: false, icon: {}, enabled: false, }, ]); const {queryByTitle, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); expect(queryByTitle(t('admin.configurePlugin'))).not.toBeNull(); expect(queryByTitle(t('admin.pluginReadme'))).not.toBeNull(); expect(queryByText('My Plugin')).not.toBeNull(); expect(queryByText('v1.0.0')).not.toBeNull(); expect(queryByText('desc')).not.toBeNull(); }); describe('enable plugin', () => { beforeEach(() => { fetch.get.mockResolvedValue([ { name: 'a', icon: {}, enabled: false, }, ]); }); it('succeeded', async () => { fetch.get.mockResolvedValue([ { name: 'a', icon: {}, enabled: false, }, ]); fetch.post.mockResolvedValue({code: 0, message: 'success'}); const {getByTitle, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.enablePlugin'))); await waitFor(() => { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'enable', name: 'a', }); }); expect(queryByText('success')).toBeInTheDocument(); expect(getByRole('status')).toHaveClass('alert-success'); expect(getByTitle(t('admin.disablePlugin'))).toBeChecked(); }); it('failed', async () => { fetch.post.mockResolvedValue({ code: 1, message: 'unresolved', data: {reason: ['abc']}, }); const {getByTitle, getByText, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.enablePlugin'))); await waitFor(() => { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'enable', name: 'a', }); }); expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'enable', name: 'a', }); expect(queryByText('unresolved')).toBeInTheDocument(); expect(queryByText('abc')).toBeInTheDocument(); fireEvent.click(getByText(t('general.confirm'))); }); }); describe('disable plugin', () => { beforeEach(() => { fetch.get.mockResolvedValue([ { name: 'a', icon: {}, enabled: true, }, ]); }); it('succeeded', async () => { fetch.post.mockResolvedValue({code: 0, message: 'success'}); const {getByTitle, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.disablePlugin'))); await waitFor(() => { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'disable', name: 'a', }); }); expect(queryByText('success')).toBeInTheDocument(); expect(getByRole('status')).toHaveClass('alert-success'); expect(getByTitle(t('admin.enablePlugin'))).not.toBeChecked(); }); it('failed', async () => { fetch.post.mockResolvedValue({code: 1, message: 'failed'}); const {getByTitle, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.disablePlugin'))); await waitFor(() => { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'disable', name: 'a', }); }); expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'disable', name: 'a', }); expect(queryByText('failed')).toBeInTheDocument(); expect(getByRole('alert')).toHaveClass('alert-danger'); }); }); describe('delete plugin', () => { beforeEach(() => { fetch.get.mockResolvedValue([ { name: 'a', title: 'My Plugin', icon: {}, enabled: false, }, ]); }); it('rejected by user', async () => { const {getByTitle, getByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.deletePlugin'))); fireEvent.click(getByText(t('general.cancel'))); await waitFor(() => { expect(fetch.post).not.toBeCalled(); }); }); it('succeeded', async () => { fetch.post.mockResolvedValue({code: 0, message: 'success'}); const {getByTitle, getByText, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.deletePlugin'))); fireEvent.click(getByText(t('general.confirm'))); await waitFor(() => { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'delete', name: 'a', }); }); expect(queryByText('success')).toBeInTheDocument(); expect(getByRole('status')).toHaveClass('alert-success'); expect(queryByText('My Plugin')).toBeNull(); }); it('failed', async () => { fetch.post.mockResolvedValue({code: 1, message: 'failed'}); const {getByTitle, getByText, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getByTitle(t('admin.deletePlugin'))); fireEvent.click(getByText(t('general.confirm'))); await waitFor(() => { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'delete', name: 'a', }); }); expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { action: 'delete', name: 'a', }); expect(queryByText('failed')).toBeInTheDocument(); expect(getByRole('alert')).toHaveClass('alert-danger'); expect(queryByText('My Plugin')).not.toBeNull(); }); }); describe('upload plugin archive', () => { it('no selected file', async () => { fetch.get.mockResolvedValue([]); const {getAllByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.click(getAllByText(t('general.submit'))[0]); expect(fetch.post).not.toBeCalled(); }); it('succeeded', async () => { fetch.get.mockResolvedValue([]); fetch.post.mockResolvedValue({code: 0, message: 'ok'}); const {getByTitle, getAllByText, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); const file = new File([], 'plugin.zip'); fireEvent.change(getByTitle(t('skinlib.upload.select-file')), { target: {files: [file]}, }); fireEvent.click(getAllByText(t('general.submit'))[0]); await waitFor(() => { expect(fetch.get).toBeCalledTimes(2); expect(fetch.post).toBeCalledWith( '/admin/plugins/upload', expect.any(FormData), ); }); const formData = fetch.post.mock.calls[0]![1] as FormData; expect(formData.get('file')).toStrictEqual(file); expect(queryByText('plugin.zip')).not.toBeInTheDocument(); expect(queryByText('ok')).toBeInTheDocument(); expect(getByRole('status')).toHaveClass('alert-success'); }); it('failed', async () => { fetch.get.mockResolvedValue([]); fetch.post.mockResolvedValue({code: 1, message: 'failed'}); const {getByTitle, getAllByText, getByRole, queryByText} = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); const file = new File([], 'plugin.zip'); fireEvent.change(getByTitle(t('skinlib.upload.select-file')), { target: {files: [file]}, }); fireEvent.click(getAllByText(t('general.submit'))[0]); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); expect(fetch.post).toBeCalledWith( '/admin/plugins/upload', expect.any(FormData), ); }); expect(queryByText('plugin.zip')).toBeInTheDocument(); expect(queryByText('failed')).toBeInTheDocument(); expect(getByRole('alert')).toHaveClass('alert-danger'); }); }); describe('submit remote URL', () => { it('succeeded', async () => { fetch.get.mockResolvedValue([]); fetch.post.mockResolvedValue({code: 0, message: 'ok'}); const { getByLabelText, getAllByText, getByRole, queryByText, queryByDisplayValue, } = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.input(getByLabelText('URL'), { target: {value: 'https://example.com/a.zip'}, }); fireEvent.click(getAllByText(t('general.submit'))[1]); await waitFor(() => { expect(fetch.get).toBeCalledTimes(2); expect(fetch.post).toBeCalledWith('/admin/plugins/wget', { url: 'https://example.com/a.zip', }); }); expect(queryByDisplayValue('https://example.com/a.zip')).not.toBeInTheDocument(); expect(queryByText('ok')).toBeInTheDocument(); expect(getByRole('status')).toHaveClass('alert-success'); }); it('failed', async () => { fetch.get.mockResolvedValue([]); fetch.post.mockResolvedValue({code: 1, message: 'failed'}); const { getByLabelText, getAllByText, getByRole, queryByText, queryByDisplayValue, } = render(); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); }); fireEvent.input(getByLabelText('URL'), { target: {value: 'https://example.com/a.zip'}, }); fireEvent.click(getAllByText(t('general.submit'))[1]); await waitFor(() => { expect(fetch.get).toBeCalledTimes(1); expect(fetch.post).toBeCalledWith('/admin/plugins/wget', { url: 'https://example.com/a.zip', }); }); expect(queryByDisplayValue('https://example.com/a.zip')).toBeInTheDocument(); expect(queryByText('failed')).toBeInTheDocument(); expect(getByRole('alert')).toHaveClass('alert-danger'); }); });