Add tests
This commit is contained in:
parent
7821cd397b
commit
ea71f7247c
3 changed files with 242 additions and 0 deletions
Binary file not shown.
195
frontend/src/features/history/navigation.test.js
Normal file
195
frontend/src/features/history/navigation.test.js
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||||
|
import { setActivePinia, createPinia } from 'pinia'
|
||||||
|
|
||||||
|
// Mock vue-router
|
||||||
|
const mockPush = vi.fn()
|
||||||
|
vi.mock('vue-router', () => ({
|
||||||
|
useRouter: () => ({ push: mockPush }),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('./api.js', () => ({
|
||||||
|
fetchAnalyses: vi.fn(),
|
||||||
|
deleteAnalysis: vi.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('../analysis/api.js', () => ({
|
||||||
|
fetchAnalyses: vi.fn(),
|
||||||
|
fetchAnalysis: vi.fn(),
|
||||||
|
createAnalysis: vi.fn(),
|
||||||
|
deleteAnalysis: vi.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('../document/api.js', () => ({
|
||||||
|
fetchDocuments: vi.fn(),
|
||||||
|
uploadDocument: vi.fn(),
|
||||||
|
deleteDocument: vi.fn(),
|
||||||
|
getPreviewUrl: vi.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
import { useHistoryStore } from './store.js'
|
||||||
|
import { useAnalysisStore } from '../analysis/store.js'
|
||||||
|
import { useDocumentStore } from '../document/store.js'
|
||||||
|
|
||||||
|
describe('History → Studio navigation', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setActivePinia(createPinia())
|
||||||
|
vi.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('History store provides data for navigation', () => {
|
||||||
|
it('analyses contain documentId for document selection', async () => {
|
||||||
|
const { fetchAnalyses } = await import('./api.js')
|
||||||
|
fetchAnalyses.mockResolvedValue([
|
||||||
|
{ id: 'a1', documentId: 'd1', documentFilename: 'test.pdf', status: 'COMPLETED' },
|
||||||
|
{ id: 'a2', documentId: 'd2', documentFilename: 'other.pdf', status: 'FAILED' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const store = useHistoryStore()
|
||||||
|
await store.load()
|
||||||
|
|
||||||
|
expect(store.analyses[0].documentId).toBe('d1')
|
||||||
|
expect(store.analyses[1].documentId).toBe('d2')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Analysis store select() restores analysis state', () => {
|
||||||
|
it('select() sets currentAnalysis from fetched data', async () => {
|
||||||
|
const { fetchAnalysis } = await import('../analysis/api.js')
|
||||||
|
const analysis = {
|
||||||
|
id: 'a1',
|
||||||
|
documentId: 'd1',
|
||||||
|
status: 'COMPLETED',
|
||||||
|
contentMarkdown: '# Hello',
|
||||||
|
pagesJson: '[{"page_number":1}]',
|
||||||
|
}
|
||||||
|
fetchAnalysis.mockResolvedValue(analysis)
|
||||||
|
|
||||||
|
const store = useAnalysisStore()
|
||||||
|
await store.select('a1')
|
||||||
|
|
||||||
|
expect(store.currentAnalysis).toEqual(analysis)
|
||||||
|
expect(store.currentAnalysis.documentId).toBe('d1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('select() allows document store to select the associated document', async () => {
|
||||||
|
const { fetchAnalysis } = await import('../analysis/api.js')
|
||||||
|
fetchAnalysis.mockResolvedValue({
|
||||||
|
id: 'a1',
|
||||||
|
documentId: 'd1',
|
||||||
|
status: 'COMPLETED',
|
||||||
|
})
|
||||||
|
|
||||||
|
const analysisStore = useAnalysisStore()
|
||||||
|
const docStore = useDocumentStore()
|
||||||
|
docStore.documents = [
|
||||||
|
{ id: 'd1', filename: 'test.pdf' },
|
||||||
|
{ id: 'd2', filename: 'other.pdf' },
|
||||||
|
]
|
||||||
|
|
||||||
|
await analysisStore.select('a1')
|
||||||
|
docStore.select(analysisStore.currentAnalysis.documentId)
|
||||||
|
|
||||||
|
expect(docStore.selectedId).toBe('d1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Navigation intent from history items', () => {
|
||||||
|
it('completed analysis navigates to studio with analysisId query', () => {
|
||||||
|
// Simulates what HistoryList.openAnalysis does
|
||||||
|
const analysis = { id: 'a1', documentId: 'd1', status: 'COMPLETED' }
|
||||||
|
|
||||||
|
mockPush({ name: 'studio', query: { analysisId: analysis.id } })
|
||||||
|
|
||||||
|
expect(mockPush).toHaveBeenCalledWith({
|
||||||
|
name: 'studio',
|
||||||
|
query: { analysisId: 'a1' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('pending analysis navigates with same pattern', () => {
|
||||||
|
const analysis = { id: 'a2', documentId: 'd2', status: 'PENDING' }
|
||||||
|
|
||||||
|
mockPush({ name: 'studio', query: { analysisId: analysis.id } })
|
||||||
|
|
||||||
|
expect(mockPush).toHaveBeenCalledWith({
|
||||||
|
name: 'studio',
|
||||||
|
query: { analysisId: 'a2' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('failed analysis navigates with same pattern', () => {
|
||||||
|
const analysis = { id: 'a3', documentId: 'd3', status: 'FAILED' }
|
||||||
|
|
||||||
|
mockPush({ name: 'studio', query: { analysisId: analysis.id } })
|
||||||
|
|
||||||
|
expect(mockPush).toHaveBeenCalledWith({
|
||||||
|
name: 'studio',
|
||||||
|
query: { analysisId: 'a3' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Full restore flow (store-level integration)', () => {
|
||||||
|
it('restores completed analysis: selects analysis + document + verifier mode', async () => {
|
||||||
|
const { fetchAnalysis } = await import('../analysis/api.js')
|
||||||
|
fetchAnalysis.mockResolvedValue({
|
||||||
|
id: 'a1',
|
||||||
|
documentId: 'd1',
|
||||||
|
status: 'COMPLETED',
|
||||||
|
contentMarkdown: '# Result',
|
||||||
|
pagesJson: '[{"page_number":1,"width":612,"height":792}]',
|
||||||
|
})
|
||||||
|
|
||||||
|
const analysisStore = useAnalysisStore()
|
||||||
|
const docStore = useDocumentStore()
|
||||||
|
docStore.documents = [{ id: 'd1', filename: 'test.pdf' }]
|
||||||
|
|
||||||
|
// Simulate what StudioPage.onMounted does with ?analysisId=a1
|
||||||
|
await analysisStore.select('a1')
|
||||||
|
const analysis = analysisStore.currentAnalysis
|
||||||
|
expect(analysis).not.toBeNull()
|
||||||
|
|
||||||
|
docStore.select(analysis.documentId)
|
||||||
|
expect(docStore.selectedId).toBe('d1')
|
||||||
|
|
||||||
|
// Mode would be set to 'verifier' since status is COMPLETED
|
||||||
|
const mode = analysis.status === 'COMPLETED' ? 'verifier' : 'configurer'
|
||||||
|
expect(mode).toBe('verifier')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('restores failed analysis: selects analysis + document, stays in configurer mode', async () => {
|
||||||
|
const { fetchAnalysis } = await import('../analysis/api.js')
|
||||||
|
fetchAnalysis.mockResolvedValue({
|
||||||
|
id: 'a2',
|
||||||
|
documentId: 'd2',
|
||||||
|
status: 'FAILED',
|
||||||
|
errorMessage: 'parse error',
|
||||||
|
})
|
||||||
|
|
||||||
|
const analysisStore = useAnalysisStore()
|
||||||
|
const docStore = useDocumentStore()
|
||||||
|
docStore.documents = [{ id: 'd2', filename: 'broken.pdf' }]
|
||||||
|
|
||||||
|
await analysisStore.select('a2')
|
||||||
|
const analysis = analysisStore.currentAnalysis
|
||||||
|
|
||||||
|
docStore.select(analysis.documentId)
|
||||||
|
expect(docStore.selectedId).toBe('d2')
|
||||||
|
|
||||||
|
const mode = analysis.status === 'COMPLETED' ? 'verifier' : 'configurer'
|
||||||
|
expect(mode).toBe('configurer')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles missing analysis gracefully', async () => {
|
||||||
|
const { fetchAnalysis } = await import('../analysis/api.js')
|
||||||
|
fetchAnalysis.mockRejectedValue(new Error('Not found'))
|
||||||
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
|
||||||
|
const analysisStore = useAnalysisStore()
|
||||||
|
await analysisStore.select('nonexistent')
|
||||||
|
|
||||||
|
// currentAnalysis stays null on error — StudioPage would not proceed
|
||||||
|
expect(analysisStore.currentAnalysis).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -59,4 +59,51 @@ describe('useI18n', () => {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
expect(t('results.pageOf', { current: 1, total: 5 })).toBe('Page 1 sur 5')
|
expect(t('results.pageOf', { current: 1, total: 5 })).toBe('Page 1 sur 5')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has history tab keys in French', () => {
|
||||||
|
useSettingsStore.mockReturnValue({ locale: 'fr' })
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
expect(t('history.tabAnalyses')).toBe('Analyses')
|
||||||
|
expect(t('history.tabDocuments')).toBe('Documents')
|
||||||
|
expect(t('history.emptyDocs')).toBe('Aucun document. Importez un document depuis le Studio.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has history tab keys in English', () => {
|
||||||
|
useSettingsStore.mockReturnValue({ locale: 'en' })
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
expect(t('history.tabAnalyses')).toBe('Analyses')
|
||||||
|
expect(t('history.tabDocuments')).toBe('Documents')
|
||||||
|
expect(t('history.emptyDocs')).toBe('No documents yet. Upload a document from the Studio.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has detailed pipeline option hints in French', () => {
|
||||||
|
useSettingsStore.mockReturnValue({ locale: 'fr' })
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
// Hints should be longer than simple labels
|
||||||
|
expect(t('config.ocrHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.tableStructureHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.codeEnrichmentHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.formulaEnrichmentHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.pictureClassificationHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.pictureDescriptionHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.generatePictureImagesHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.generatePageImagesHint').length).toBeGreaterThan(40)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has detailed pipeline option hints in English', () => {
|
||||||
|
useSettingsStore.mockReturnValue({ locale: 'en' })
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
expect(t('config.ocrHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.tableStructureHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.codeEnrichmentHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.formulaEnrichmentHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.pictureClassificationHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.pictureDescriptionHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.generatePictureImagesHint').length).toBeGreaterThan(40)
|
||||||
|
expect(t('config.generatePageImagesHint').length).toBeGreaterThan(40)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue