test(04-01): add failing tests for marketplace page

- 4 tests: card grid rendering, activation status display,
  access-denied for non-admin, empty state
- Tests fail as expected (RED phase) - page and store not yet implemented
This commit is contained in:
2026-06-22 14:48:39 +02:00
parent 1e494b36ea
commit 0b7cf66abb
@@ -0,0 +1,181 @@
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
// Mock next-intl
vi.mock('next-intl', () => ({
useTranslations: () => (key: string) => {
const translations: Record<string, string> = {
title: 'Marktplatz',
subtitle: 'Entdecken und aktivieren Sie Module fuer Ihren Mandanten',
activate: 'Modul aktivieren',
deactivate: 'Deaktivieren',
statusActive: 'Aktiviert',
statusAvailable: 'Verfuegbar',
emptyTitle: 'Noch keine Module verfuegbar',
emptyBody: 'Module werden vom Systemadministrator bereitgestellt.',
accessDenied: 'Zugriff verweigert',
};
return translations[key] ?? key;
},
}));
// Mock auth store
const mockAuthStore = vi.fn();
vi.mock('@/lib/stores/auth-store', () => ({
useAuthStore: (selector: (state: unknown) => unknown) => mockAuthStore(selector),
}));
// Mock marketplace store
vi.mock('@/lib/stores/marketplace-store', () => ({
useMarketplaceStore: () => ({
sidebarRefreshKey: 0,
bumpSidebarRefresh: vi.fn(),
selectedTenantId: null,
setSelectedTenantId: vi.fn(),
}),
}));
const mockModules = [
{
id: 'mod-1',
slug: 'domaincheck',
name: 'Domaincheck',
version: '1.0.0',
category: 'Domain-Tools',
description: { de: 'Domain pruefen', en: 'Check domain' },
icon: 'Globe',
isSystem: false,
},
{
id: 'mod-2',
slug: 'converter',
name: 'Converter',
version: '1.0.0',
category: 'Utilities',
description: { de: 'Dateien konvertieren', en: 'Convert files' },
icon: null,
isSystem: false,
},
];
const mockActiveModules = [
{
id: 'mod-1',
slug: 'domaincheck',
name: 'Domaincheck',
version: '1.0.0',
category: 'Domain-Tools',
description: { de: 'Domain pruefen', en: 'Check domain' },
icon: 'Globe',
isSystem: false,
},
];
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
// Lazy import after mocks
async function importPage() {
const mod = await import('./page');
return mod.default;
}
describe('MarketplacePage', () => {
beforeEach(() => {
// Default: admin user
mockAuthStore.mockImplementation((selector: (state: { user: { id: string; username: string; displayName: string; role: string; tenantId: string } }) => unknown) =>
selector({
user: { id: 'u1', username: 'admin', displayName: 'Admin', role: 'ADMIN', tenantId: 't1' },
}),
);
});
it('renders one MarketplaceCard per module when fetch returns a list', async () => {
vi.stubGlobal(
'fetch',
vi.fn((url: string) => {
if (url.includes('/modules/active')) {
return Promise.resolve({ ok: true, json: () => Promise.resolve([]) });
}
if (url.includes('/modules')) {
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockModules) });
}
return Promise.resolve({ ok: false, text: () => Promise.resolve('') });
}),
);
const Page = await importPage();
render(<Page />);
await waitFor(() => {
expect(screen.getByText('Domaincheck')).toBeInTheDocument();
});
expect(screen.getByText('Converter')).toBeInTheDocument();
});
it('shows activated status for a module returned by /modules/active', async () => {
vi.stubGlobal(
'fetch',
vi.fn((url: string) => {
if (url.includes('/modules/active')) {
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockActiveModules) });
}
if (url.includes('/modules')) {
return Promise.resolve({ ok: true, json: () => Promise.resolve(mockModules) });
}
return Promise.resolve({ ok: false, text: () => Promise.resolve('') });
}),
);
const Page = await importPage();
render(<Page />);
await waitFor(() => {
expect(screen.getByText('Domaincheck')).toBeInTheDocument();
});
// One card should be activated, one available
const statusBadges = screen.getAllByRole('status');
const texts = statusBadges.map((b) => b.textContent);
expect(texts).toContain('Aktiviert');
expect(texts).toContain('Verfuegbar');
});
it('renders access-denied and no cards for non-admin users', async () => {
mockAuthStore.mockImplementation((selector: (state: { user: { id: string; username: string; displayName: string; role: string; tenantId: string } }) => unknown) =>
selector({
user: { id: 'u2', username: 'user', displayName: 'User', role: 'USER', tenantId: 't1' },
}),
);
const Page = await importPage();
render(<Page />);
expect(screen.getByText('Zugriff verweigert')).toBeInTheDocument();
expect(screen.queryByText('Domaincheck')).not.toBeInTheDocument();
});
it('renders empty-state heading when no modules are available', async () => {
vi.stubGlobal(
'fetch',
vi.fn((url: string) => {
if (url.includes('/modules/active')) {
return Promise.resolve({ ok: true, json: () => Promise.resolve([]) });
}
if (url.includes('/modules')) {
return Promise.resolve({ ok: true, json: () => Promise.resolve([]) });
}
return Promise.resolve({ ok: false, text: () => Promise.resolve('') });
}),
);
const Page = await importPage();
render(<Page />);
await waitFor(() => {
expect(screen.getByText('Noch keine Module verfuegbar')).toBeInTheDocument();
});
});
});