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:
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user