feat(simulations): resume session depuis localStorage (FTD-21)

This commit is contained in:
Hermann_Kitio 2026-04-21 04:02:15 +03:00
parent 549e5f698f
commit aaecc3f804
2 changed files with 125 additions and 4 deletions

View file

@ -18,7 +18,11 @@ import React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useSimulation } from '../useSimulation'
import { SimulationFlowProvider, useSimulationFlow } from '../../state/SimulationFlowProvider'
import { createSimulation } from '@/entities/production/api'
import {
createSimulation,
getSimulationState,
updateSujet,
} from '@/entities/production/api'
import { correctEe } from '@/entities/report/api'
import type { Production } from '@/entities/production/types'
import type { Report } from '@/entities/report/types'
@ -28,6 +32,8 @@ vi.mock('@/entities/report/api')
const mockCreateSimulation = vi.mocked(createSimulation)
const mockCorrectEe = vi.mocked(correctEe)
const mockGetSimulationState = vi.mocked(getSimulationState)
const mockUpdateSujet = vi.mocked(updateSujet)
const mockProduction: Production = {
id: 'sim-1',
@ -79,6 +85,9 @@ function createWrapper() {
beforeEach(() => {
vi.clearAllMocks()
localStorage.clear()
// FTD-21 — par défaut, pas de resume : la plupart des tests partent de idle.
mockUpdateSujet.mockResolvedValue(undefined)
})
describe('useSimulation — état initial', () => {
@ -204,6 +213,63 @@ describe('useSimulation — submitText', () => {
})
})
describe('useSimulation — FTD-21 resume depuis localStorage', () => {
it('restaure step=task-selected et production hydratée si rapport=null', async () => {
localStorage.setItem('expria_simulation_id', 'sim-42')
mockGetSimulationState.mockResolvedValue({
simulation_id: 'sim-42',
tache: 'EE_T1',
mode: 'entrainement',
created_at: '2026-04-21T00:00:00Z',
contenu: 'Mon brouillon.',
sujet: mockSujet,
rapport: null,
})
const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })
await waitFor(() => expect(result.current.step).toBe('task-selected'))
expect(mockGetSimulationState).toHaveBeenCalledWith('sim-42')
expect(result.current.production?.id).toBe('sim-42')
expect(result.current.production?.contenu).toBe('Mon brouillon.')
expect(result.current.sujet).toEqual(mockSujet)
expect(localStorage.getItem('expria_simulation_id')).toBe('sim-42')
})
it('nettoie localStorage si rapport présent (simulation déjà corrigée)', async () => {
localStorage.setItem('expria_simulation_id', 'sim-42')
mockGetSimulationState.mockResolvedValue({
simulation_id: 'sim-42',
tache: 'EE_T1',
mode: 'entrainement',
created_at: '2026-04-21T00:00:00Z',
contenu: 'texte',
sujet: null,
rapport: {
score: 14, nclc: 8, feedback_court: 'OK',
criteres: [], erreurs: [], modele: '', idees: [], exercices: [],
},
})
renderHook(() => useSimulation(), { wrapper: createWrapper() })
await waitFor(() => {
expect(localStorage.getItem('expria_simulation_id')).toBeNull()
})
})
it('nettoie localStorage si getSimulationState échoue', async () => {
localStorage.setItem('expria_simulation_id', 'sim-missing')
mockGetSimulationState.mockRejectedValue({ code: 'SIMULATION_NOT_FOUND' })
renderHook(() => useSimulation(), { wrapper: createWrapper() })
await waitFor(() => {
expect(localStorage.getItem('expria_simulation_id')).toBeNull()
})
})
})
describe('useSimulation — reset', () => {
it('reset depuis task-selected remet step à idle et production à null', async () => {
mockCreateSimulation.mockResolvedValue(mockProduction)