2026-07-23 19:01:18 +02:00
|
|
|
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: [],
|
2026-07-23 23:35:35 +02:00
|
|
|
custom_fields: [],
|
2026-07-23 19:01:18 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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: [],
|
2026-07-23 23:35:35 +02:00
|
|
|
custom_fields: [],
|
2026-07-23 19:01:18 +02:00
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-04 19:25:44 +02:00
|
|
|
it('renders loading spinner when manifests have not loaded yet', () => {
|
2026-07-23 19:01:18 +02:00
|
|
|
usePluginStore.setState({
|
|
|
|
|
manifests: [],
|
|
|
|
|
loaded: false,
|
|
|
|
|
});
|
|
|
|
|
const { container } = render(
|
|
|
|
|
<MemoryRouter initialEntries={['/mail']}>
|
|
|
|
|
<PluginRouteRenderer />
|
|
|
|
|
</MemoryRouter>
|
|
|
|
|
);
|
2026-08-04 19:25:44 +02:00
|
|
|
// Component now shows a loading spinner instead of empty HTML
|
|
|
|
|
expect(container.querySelector('[role="status"]')).toBeInTheDocument();
|
2026-07-23 19:01:18 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
});
|
|
|
|
|
});
|