feat: initialisation de ĞéoFlux — visualisation géographique Ğ1
- Carte Leaflet plein écran avec heatmap (OpenStreetMap, dark mode) - Sélecteur de période 24h / 7j / 30j - Panneau latéral : volume total, compteur de transactions, top 3 villes - mockData.ts : 2 400 transactions simulées sur 24 villes FR/EU - DataService.ts : abstraction prête pour branchement Subsquid/Ğ1v2 - Schémas Zod (g1.schema.ts) : validation runtime Duniter GVA + Cesium+ - Adaptateurs DuniterAdapter et CesiumAdapter (Ğ1v1, à migrer v2) - Suite de tests Vitest : 43 tests, conformité schéma Ğ1 vérifiée Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
57
src/test/StatsPanel.test.tsx
Normal file
57
src/test/StatsPanel.test.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { StatsPanel } from '../components/StatsPanel';
|
||||
import type { PeriodStats } from '../services/DataService';
|
||||
|
||||
const mockStats: PeriodStats = {
|
||||
totalVolume: 1234.56,
|
||||
transactionCount: 42,
|
||||
topCities: [
|
||||
{ name: 'Paris', volume: 700, count: 20 },
|
||||
{ name: 'Lyon', volume: 400, count: 15 },
|
||||
{ name: 'Bordeaux', volume: 134, count: 7 },
|
||||
],
|
||||
};
|
||||
|
||||
describe('StatsPanel', () => {
|
||||
it('shows loading skeletons when loading=true', () => {
|
||||
const { container } = render(
|
||||
<StatsPanel stats={null} loading={true} periodDays={7} />
|
||||
);
|
||||
const skeletons = container.querySelectorAll('.animate-pulse');
|
||||
expect(skeletons.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('displays the total volume when stats are available', () => {
|
||||
render(<StatsPanel stats={mockStats} loading={false} periodDays={7} />);
|
||||
expect(screen.getByText(/1\s*234/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays the transaction count', () => {
|
||||
render(<StatsPanel stats={mockStats} loading={false} periodDays={7} />);
|
||||
expect(screen.getByText('42')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders exactly 3 top cities in correct order', () => {
|
||||
render(<StatsPanel stats={mockStats} loading={false} periodDays={7} />);
|
||||
const cities = ['Paris', 'Lyon', 'Bordeaux'];
|
||||
cities.forEach((city) => expect(screen.getByText(city)).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('shows "24 dernières heures" for periodDays=1', () => {
|
||||
render(<StatsPanel stats={mockStats} loading={false} periodDays={1} />);
|
||||
expect(screen.getByText(/24 dernières heures/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "30 derniers jours" for periodDays=30', () => {
|
||||
render(<StatsPanel stats={mockStats} loading={false} periodDays={30} />);
|
||||
expect(screen.getByText(/30 derniers jours/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not crash with an empty topCities list', () => {
|
||||
const emptyStats = { ...mockStats, topCities: [] };
|
||||
expect(() =>
|
||||
render(<StatsPanel stats={emptyStats} loading={false} periodDays={7} />)
|
||||
).not.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user