feat(M4): System-Rueckbau — Dashboard-Inhalte als MiniApps, Core = reiner Host (#362)
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
- system_miniapps.py: audit_activity (audit:read, settings max_items) + system_metrics (settings:read) als Core-Apps in Registry - base.py-Fix: native Manifest-MiniApps reichen component durch (M1-Luecke) - contacts-Manifest: contacts_stats (ContactsStatsWidget, contacts:read, show_companies/show_persons) - Seed-Fix: nur renderbare Apps (component) landen im Dashboard-Layout - Frontend: ContactsStatsWidget, AuditActivityWidget, SystemMetricsWidget; MiniAppHost-Registry +3 - Dashboard.tsx = reiner Host (26 Z.); Page-Tests auf Pure-Host umgeschrieben - Tests: M4 7/7 (TDD rot->gruen), Backend-Regression 46/46, Vitest 22/22, tsc clean, build OK
This commit is contained in:
@@ -5,48 +5,21 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { DashboardPage } from '@/pages/Dashboard';
|
||||
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
useUnifiedContacts: (page: number, pageSize: number, search: any, type: string) => ({
|
||||
data: { items: [], total: type === 'company' ? 24 : 156, page: 1, page_size: 25 },
|
||||
isLoading: false,
|
||||
}),
|
||||
useAuditLog: () => ({
|
||||
data: {
|
||||
items: [
|
||||
{ timestamp: '2025-06-29T10:00:00Z', user: 'anna.schmidt', action: 'create', entity: 'company', entity_id: '1', details: 'Firma erstellt' },
|
||||
{ timestamp: '2025-06-28T14:00:00Z', user: 'max.mustermann', action: 'update', entity: 'contact', entity_id: '2', details: 'Kontakt aktualisiert' },
|
||||
{ timestamp: '2025-06-27T09:00:00Z', user: 'admin', action: 'delete', entity: 'company', entity_id: '3', details: 'Firma gelöscht' },
|
||||
],
|
||||
total: 3,
|
||||
},
|
||||
isError: false,
|
||||
}),
|
||||
/**
|
||||
* DashboardPage tests (Phase M4): the page is a pure host — all content
|
||||
* (stat cards, activity feed, system metrics) lives in MiniApp instances
|
||||
* managed by the DashboardBuilder. The former legacy assertions
|
||||
* (stat-companies etc.) moved to the widget level in M4.
|
||||
*/
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({ t: (key: string) => key }),
|
||||
}));
|
||||
|
||||
vi.mock('@/api/dashboard', () => ({
|
||||
useDashboardWidgets: () => ({
|
||||
data: { items: [], total: 0 },
|
||||
isError: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
// M3: DashboardPage renders the DashboardBuilder — mock its data layer
|
||||
vi.mock('@/api/dashboards', () => ({
|
||||
useDashboards: () => ({ data: [], isLoading: false }),
|
||||
useCreateDashboard: () => ({ mutate: vi.fn(), isPending: false }),
|
||||
useUpdateDashboard: () => ({ mutate: vi.fn(), isPending: false }),
|
||||
useDeleteDashboard: () => ({ mutate: vi.fn() }),
|
||||
useSetDefaultDashboard: () => ({ mutate: vi.fn() }),
|
||||
}));
|
||||
|
||||
vi.mock('@/api/miniapps', () => ({
|
||||
useMiniapps: () => ({ data: { items: [], total: 0 } }),
|
||||
renderableDashboardApps: (apps: unknown[]) => (apps as never[]),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/dashboard/MiniAppHost', () => ({
|
||||
MiniAppHost: ({ appId }: { appId: string }) => (
|
||||
<div data-testid={`miniapp-${appId}`}>app:{appId}</div>
|
||||
// DashboardBuilder is covered by its own suite — render a stub here
|
||||
vi.mock('@/components/dashboard/DashboardBuilder', () => ({
|
||||
DashboardBuilder: () => (
|
||||
<div data-testid="dashboard-builder-stub">builder</div>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -64,64 +37,27 @@ function renderPage() {
|
||||
);
|
||||
}
|
||||
|
||||
describe('DashboardPage', () => {
|
||||
it('renders dashboard page', () => {
|
||||
describe('DashboardPage (pure host, M4)', () => {
|
||||
it('renders the dashboard page shell', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('dashboard-page')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders page title', () => {
|
||||
it('renders the page title', () => {
|
||||
renderPage();
|
||||
expect(screen.getByText('Dashboard')).toBeInTheDocument();
|
||||
expect(screen.getByText('dashboard.title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders stat card for companies', () => {
|
||||
it('renders the DashboardBuilder as its only content area', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('stat-companies')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('dashboard-builder-stub')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders stat card for contacts', () => {
|
||||
it('does not render legacy hard-coded blocks anymore', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('stat-contacts')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders stat card for active this week', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('stat-active-week')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders stat card for new this month', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('stat-new-month')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correct company count in stat card', () => {
|
||||
renderPage();
|
||||
const statCompanies = screen.getByTestId('stat-companies');
|
||||
expect(statCompanies).toHaveTextContent('24');
|
||||
});
|
||||
|
||||
it('renders correct contact count in stat card', () => {
|
||||
renderPage();
|
||||
const statContacts = screen.getByTestId('stat-contacts');
|
||||
expect(statContacts).toHaveTextContent('156');
|
||||
});
|
||||
|
||||
it('renders activity feed', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('activity-feed')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders activity feed with user names from audit log', () => {
|
||||
renderPage();
|
||||
expect(screen.getByText('anna.schmidt')).toBeInTheDocument();
|
||||
expect(screen.getByText('max.mustermann')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders activity feed with action descriptions', () => {
|
||||
renderPage();
|
||||
expect(screen.getByText(/create/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/update/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/delete/)).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('stat-companies')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('stat-contacts')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('stat-active-week')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('activity-feed')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user