Skip to content

Commit 85a6ce1

Browse files
committed
session reset cleanup, footer
1 parent 7604af3 commit 85a6ce1

File tree

4 files changed

+99
-6
lines changed

4 files changed

+99
-6
lines changed

poliloom-gui/src/app/stats/page.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,10 @@ function EvaluationsChart({ data }: { data: EvaluationTimeseriesPoint[] }) {
181181
function CoverageBar({ item }: { item: CountryCoverage }) {
182182
const evaluatedPercent =
183183
item.total_count > 0 ? (item.evaluated_count / item.total_count) * 100 : 0
184-
const enrichedNotEvaluated = item.enriched_count - item.evaluated_count
185-
const enrichedNotEvaluatedPercent =
186-
item.total_count > 0 ? (enrichedNotEvaluated / item.total_count) * 100 : 0
187-
const barWidth = evaluatedPercent + enrichedNotEvaluatedPercent
184+
const enrichedPercent = item.total_count > 0 ? (item.enriched_count / item.total_count) * 100 : 0
185+
const barWidth = enrichedPercent
188186
const countLabel = `${item.enriched_count} / ${item.total_count}`
189-
const percentLabel = `${item.evaluated_count} evaluated (${Math.round(evaluatedPercent)}%), ${enrichedNotEvaluated} processed (${Math.round(enrichedNotEvaluatedPercent)}%)`
187+
const percentLabel = `${item.evaluated_count} evaluated (${Math.round(evaluatedPercent)}%), ${item.enriched_count} processed (${Math.round(enrichedPercent)}%)`
190188

191189
const labels = (
192190
<>

poliloom-gui/src/components/ui/Footer.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ export function Footer() {
1010
<Button href="https://www.opensanctions.org/impressum/" variant="secondary" size="small">
1111
Impressum
1212
</Button>
13+
<Button
14+
href="https://github.com/opensanctions/poliloom/issues"
15+
variant="secondary"
16+
size="small"
17+
>
18+
Issues
19+
</Button>
1320
<Button
1421
href={WIKIDATA_CONTRIBUTIONS_URL}
1522
variant="secondary"
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest'
2+
import { renderHook, act, waitFor } from '@testing-library/react'
3+
import { EvaluationSessionProvider, useEvaluationSession } from './EvaluationSessionContext'
4+
import { mockPolitician } from '@/test/mock-data'
5+
import { Politician } from '@/types'
6+
7+
// Mock useAuthSession
8+
vi.mock('@/hooks/useAuthSession', () => ({
9+
useAuthSession: () => ({
10+
session: { accessToken: 'mock-token' },
11+
status: 'authenticated',
12+
isAuthenticated: true,
13+
}),
14+
}))
15+
16+
// Mock UserPreferencesContext with STABLE references to avoid infinite loops
17+
// The context has a useEffect that clears politicians when filters change,
18+
// and useMemo depends on filters - so a new array each render causes loops
19+
const stableFilters: never[] = []
20+
vi.mock('./UserPreferencesContext', () => ({
21+
useUserPreferences: () => ({
22+
filters: stableFilters,
23+
initialized: true,
24+
}),
25+
}))
26+
27+
// Create a second mock politician for testing advancement
28+
const mockPolitician2: Politician = {
29+
...mockPolitician,
30+
id: 'pol-2',
31+
name: 'Second Politician',
32+
wikidata_id: 'Q123456',
33+
}
34+
35+
describe('EvaluationSessionContext', () => {
36+
beforeEach(() => {
37+
vi.clearAllMocks()
38+
39+
// Setup mock fetch - always returns same politicians (stable response)
40+
// has_enrichable_politicians: false prevents auto-polling
41+
global.fetch = vi.fn().mockImplementation((url: string) => {
42+
if (url.includes('/api/evaluations/politicians')) {
43+
return Promise.resolve({
44+
ok: true,
45+
json: async () => ({
46+
politicians: [mockPolitician, mockPolitician2],
47+
meta: { has_enrichable_politicians: false, total_matching_filters: 10 },
48+
}),
49+
})
50+
}
51+
if (url.includes('/api/evaluations')) {
52+
return Promise.resolve({
53+
ok: true,
54+
json: async () => ({ success: true, message: 'OK', errors: [] }),
55+
})
56+
}
57+
return Promise.resolve({ ok: false })
58+
}) as unknown as typeof fetch
59+
})
60+
61+
it('advances to next politician when session is reset', async () => {
62+
const wrapper = ({ children }: { children: React.ReactNode }) => (
63+
<EvaluationSessionProvider>{children}</EvaluationSessionProvider>
64+
)
65+
66+
const { result } = renderHook(() => useEvaluationSession(), { wrapper })
67+
68+
// Wait for initial load
69+
await waitFor(() => {
70+
expect(result.current.currentPolitician).not.toBeNull()
71+
})
72+
73+
// Verify we start with first politician and have next pre-fetched
74+
expect(result.current.currentPolitician?.id).toBe('pol-1')
75+
expect(result.current.nextPolitician?.id).toBe('pol-2')
76+
expect(result.current.completedCount).toBe(0)
77+
78+
// Reset the session (simulating "Start new round" on unmount)
79+
act(() => {
80+
result.current.resetSession()
81+
})
82+
83+
// After reset, next politician should be promoted to current
84+
expect(result.current.currentPolitician?.id).toBe('pol-2')
85+
expect(result.current.completedCount).toBe(0)
86+
})
87+
})

poliloom-gui/src/contexts/EvaluationSessionContext.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ export function EvaluationSessionProvider({ children }: { children: React.ReactN
242242

243243
const resetSession = useCallback(() => {
244244
setCompletedCount(0)
245-
}, [])
245+
advanceToNextPolitician()
246+
}, [advanceToNextPolitician])
246247

247248
const value: EvaluationSessionContextType = {
248249
currentPolitician,

0 commit comments

Comments
 (0)