17765e47b4
P1 Code Bugs: - SettingsPlugins.tsx: Array.isArray guard for plugins.map (9 tests) - HtmlBlock.tsx: javascript: URL sanitization in href attributes (1 test, security fix) P2 Test-Setup (QueryClientProvider): - Dashboard.test.tsx: Add QueryClientProvider + dashboard mock (11 tests) - CalendarPage.test.tsx: Add QueryClientProvider + savedFilters mock (8 tests) - SessionList.test.tsx: Add QueryClientProvider (6 tests) - MailPage.test.tsx: Add QueryClientProvider + savedFilters mock (8 tests fixed) - DmsPage.test.tsx: Add QueryClientProvider + DMS API mocks (2 tests fixed) - SettingsSystem.test.tsx: Add QueryClientProvider + sub-page mocks (1 test fixed) P2 Test-Setup (ChevronDown Mock): - Reports.test.tsx: Add ChevronDown to lucide-react mock (5 tests) P3 Text Fix: - UploadDropzone.test.tsx: Fix umlaut Auswaehlen -> Auswahlen (1 test) Pre-existing Test Fixes (18 tests): - MailPage.test.tsx: Remove 6 obsolete tests (compose-btn, shared-mailbox-selector, etc. — now plugin toolbar actions) - DmsPage.test.tsx: Adapt 3 tests to new testids, remove 3 obsolete tests (upload/folder/search now plugin toolbar actions) - SettingsSystem.test.tsx: Remove 4 obsolete tests (form fields moved to sub-pages) - PluginRouteRenderer.test.tsx: Adapt test to loading spinner behavior - ShareDialog.test.tsx: Fix button text i18n mismatch
124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import React from 'react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import { PluginRouteRenderer } from '../PluginRouteRenderer';
|
|
import { usePluginStore } from '@/store/pluginStore';
|
|
import type { PluginUiManifest } from '@/store/pluginStore';
|
|
|
|
// Mock PluginPage to avoid lazy loading issues in tests
|
|
vi.mock('../PluginLoader', () => ({
|
|
PluginPage: ({ component, pluginName }: { component: string; pluginName: string }) => (
|
|
<div data-testid="plugin-page" data-component={component} data-plugin={pluginName}>
|
|
Plugin: {pluginName}
|
|
</div>
|
|
),
|
|
}));
|
|
|
|
const mockManifests: PluginUiManifest[] = [
|
|
{
|
|
name: 'mail_plugin',
|
|
display_name: 'Mail',
|
|
version: '1.0.0',
|
|
is_core: true,
|
|
menu_items: [],
|
|
page_routes: [
|
|
{ path: '/mail', component: '@/pages/Mail', parent: '', protected: true, order: 100 },
|
|
{ path: '/mail/settings', component: '@/pages/MailSettings', parent: '/mail', protected: true, order: 200 },
|
|
],
|
|
detail_tabs: [],
|
|
settings_pages: [],
|
|
dashboard_widgets: [],
|
|
custom_fields: [],
|
|
},
|
|
{
|
|
name: 'calendar_plugin',
|
|
display_name: 'Calendar',
|
|
version: '1.0.0',
|
|
is_core: false,
|
|
menu_items: [],
|
|
page_routes: [
|
|
{ path: '/calendar', component: '@/pages/Calendar', parent: '', protected: true, order: 200 },
|
|
],
|
|
detail_tabs: [],
|
|
settings_pages: [],
|
|
dashboard_widgets: [],
|
|
custom_fields: [],
|
|
},
|
|
];
|
|
|
|
describe('PluginRouteRenderer', () => {
|
|
beforeEach(() => {
|
|
usePluginStore.getState().reset();
|
|
});
|
|
|
|
it('renders plugin page for a matching route', () => {
|
|
usePluginStore.setState({
|
|
manifests: mockManifests,
|
|
loaded: true,
|
|
});
|
|
render(
|
|
<MemoryRouter initialEntries={['/mail']}>
|
|
<PluginRouteRenderer />
|
|
</MemoryRouter>
|
|
);
|
|
expect(screen.getByTestId('plugin-page')).toBeInTheDocument();
|
|
expect(screen.getByText('Plugin: Mail')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders plugin page for a nested route', () => {
|
|
usePluginStore.setState({
|
|
manifests: mockManifests,
|
|
loaded: true,
|
|
});
|
|
render(
|
|
<MemoryRouter initialEntries={['/mail/settings']}>
|
|
<PluginRouteRenderer />
|
|
</MemoryRouter>
|
|
);
|
|
expect(screen.getByTestId('plugin-page')).toBeInTheDocument();
|
|
expect(screen.getByText('Plugin: Mail')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows Not Found for unmatched URLs', () => {
|
|
usePluginStore.setState({
|
|
manifests: mockManifests,
|
|
loaded: true,
|
|
});
|
|
render(
|
|
<MemoryRouter initialEntries={['/unknown']}>
|
|
<PluginRouteRenderer />
|
|
</MemoryRouter>
|
|
);
|
|
expect(screen.getByText('Page Not Found')).toBeInTheDocument();
|
|
expect(screen.getByText(/unknown/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders loading spinner when manifests have not loaded yet', () => {
|
|
usePluginStore.setState({
|
|
manifests: [],
|
|
loaded: false,
|
|
});
|
|
const { container } = render(
|
|
<MemoryRouter initialEntries={['/mail']}>
|
|
<PluginRouteRenderer />
|
|
</MemoryRouter>
|
|
);
|
|
// Component now shows a loading spinner instead of empty HTML
|
|
expect(container.querySelector('[role="status"]')).toBeInTheDocument();
|
|
});
|
|
|
|
it('matches the first route when multiple match', () => {
|
|
usePluginStore.setState({
|
|
manifests: mockManifests,
|
|
loaded: true,
|
|
});
|
|
render(
|
|
<MemoryRouter initialEntries={['/calendar']}>
|
|
<PluginRouteRenderer />
|
|
</MemoryRouter>
|
|
);
|
|
expect(screen.getByText('Plugin: Calendar')).toBeInTheDocument();
|
|
});
|
|
});
|