Merge pull request #12 from scub-france/history-document-management
History document management
This commit is contained in:
commit
6ee54fafe9
7 changed files with 454 additions and 9 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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
v-for="analysis in store.analyses"
|
||||
:key="analysis.id"
|
||||
class="history-item"
|
||||
@click="openAnalysis(analysis)"
|
||||
>
|
||||
<div class="item-main">
|
||||
<div class="item-header">
|
||||
|
|
@ -21,7 +22,7 @@
|
|||
<span v-if="analysis.completedAt"> — {{ duration(analysis) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="item-delete" @click="store.remove(analysis.id)" title="Delete">
|
||||
<button class="item-delete" @click.stop="store.remove(analysis.id)" title="Delete">
|
||||
<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -30,12 +31,18 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useHistoryStore } from '../store.js'
|
||||
import { useI18n } from '../../../shared/i18n.js'
|
||||
|
||||
const store = useHistoryStore()
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
||||
function openAnalysis(analysis) {
|
||||
router.push({ name: 'studio', query: { analysisId: analysis.id } })
|
||||
}
|
||||
|
||||
function statusClass(status) {
|
||||
return {
|
||||
'status-pending': status === 'PENDING',
|
||||
|
|
@ -83,6 +90,7 @@ function duration(analysis) {
|
|||
padding: 14px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background var(--transition);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.history-item:hover { background: var(--bg-hover); }
|
||||
|
|
|
|||
|
|
@ -2,21 +2,80 @@
|
|||
<div class="history-page">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">{{ t('history.title') }}</h1>
|
||||
<div class="tab-bar">
|
||||
<button
|
||||
class="tab-btn"
|
||||
:class="{ active: tab === 'analyses' }"
|
||||
@click="tab = 'analyses'"
|
||||
>{{ t('history.tabAnalyses') }}</button>
|
||||
<button
|
||||
class="tab-btn"
|
||||
:class="{ active: tab === 'documents' }"
|
||||
@click="tab = 'documents'"
|
||||
>{{ t('history.tabDocuments') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<HistoryList />
|
||||
<HistoryList v-if="tab === 'analyses'" />
|
||||
<div v-else class="doc-tab">
|
||||
<div v-if="docStore.documents.length === 0" class="tab-empty">
|
||||
{{ t('history.emptyDocs') }}
|
||||
</div>
|
||||
<div v-else class="doc-items">
|
||||
<div
|
||||
v-for="doc in docStore.documents"
|
||||
:key="doc.id"
|
||||
class="doc-row"
|
||||
>
|
||||
<div class="doc-row-info">
|
||||
<svg class="doc-row-icon" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<div class="doc-row-meta">
|
||||
<span class="doc-row-name">{{ doc.filename }}</span>
|
||||
<span class="doc-row-detail">
|
||||
{{ formatSize(doc.fileSize) }}
|
||||
<template v-if="doc.pageCount"> — {{ doc.pageCount }} pages</template>
|
||||
<template v-if="doc.createdAt"> — {{ formatDate(doc.createdAt) }}</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="doc-row-delete" @click="docStore.remove(doc.id)">
|
||||
<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { HistoryList, useHistoryStore } from '../features/history/index.js'
|
||||
import { useDocumentStore } from '../features/document/store.js'
|
||||
import { useI18n } from '../shared/i18n.js'
|
||||
|
||||
const store = useHistoryStore()
|
||||
const historyStore = useHistoryStore()
|
||||
const docStore = useDocumentStore()
|
||||
const { t } = useI18n()
|
||||
onMounted(() => store.load())
|
||||
const tab = ref('analyses')
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (!bytes) return ''
|
||||
const mb = bytes / (1024 * 1024)
|
||||
return mb >= 1 ? `${mb.toFixed(1)} MB` : `${(bytes / 1024).toFixed(0)} KB`
|
||||
}
|
||||
|
||||
function formatDate(iso) {
|
||||
if (!iso) return ''
|
||||
return new Date(iso).toLocaleString()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
historyStore.load()
|
||||
docStore.load()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
@ -31,6 +90,9 @@ onMounted(() => store.load())
|
|||
padding: 16px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
|
|
@ -39,8 +101,115 @@ onMounted(() => store.load())
|
|||
color: var(--text);
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tab-empty {
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
padding: 60px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.doc-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.doc-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background var(--transition);
|
||||
}
|
||||
|
||||
.doc-row:hover { background: var(--bg-hover); }
|
||||
|
||||
.doc-row-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.doc-row-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--accent);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.doc-row-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.doc-row-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.doc-row-detail {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-top: 2px;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
}
|
||||
|
||||
.doc-row-delete {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
opacity: 0;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.doc-row:hover .doc-row-delete { opacity: 1; }
|
||||
.doc-row-delete:hover { color: var(--error); background: rgba(239, 68, 68, 0.1); }
|
||||
.doc-row-delete svg { width: 16px; height: 16px; }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -269,6 +269,7 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, computed, watch, nextTick, onMounted, reactive } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useDocumentStore } from '../features/document/store.js'
|
||||
import { useAnalysisStore } from '../features/analysis/store.js'
|
||||
import { DocumentUpload, DocumentList } from '../features/document/index.js'
|
||||
|
|
@ -277,6 +278,8 @@ import BboxOverlay from '../features/analysis/ui/BboxOverlay.vue'
|
|||
import { getPreviewUrl } from '../features/document/api.js'
|
||||
import { useI18n } from '../shared/i18n.js'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const documentStore = useDocumentStore()
|
||||
const analysisStore = useAnalysisStore()
|
||||
const { t } = useI18n()
|
||||
|
|
@ -346,9 +349,24 @@ watch(() => analysisStore.currentAnalysis?.status, (status) => {
|
|||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
documentStore.load()
|
||||
onMounted(async () => {
|
||||
await documentStore.load()
|
||||
analysisStore.load()
|
||||
|
||||
// Restore analysis from history via query param
|
||||
const analysisId = route.query.analysisId
|
||||
if (analysisId) {
|
||||
await analysisStore.select(analysisId)
|
||||
const analysis = analysisStore.currentAnalysis
|
||||
if (analysis) {
|
||||
documentStore.select(analysis.documentId)
|
||||
if (analysis.status === 'COMPLETED') {
|
||||
mode.value = 'verifier'
|
||||
}
|
||||
}
|
||||
// Clean query param from URL
|
||||
router.replace({ query: {} })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -67,8 +67,12 @@ const messages = {
|
|||
'upload.maxSize': 'Max 50Mo',
|
||||
|
||||
// History
|
||||
'history.title': 'Historique des analyses',
|
||||
'history.title': 'Historique',
|
||||
'history.tabAnalyses': 'Analyses',
|
||||
'history.tabDocuments': 'Documents',
|
||||
'history.empty': 'Aucune analyse. Allez dans Studio pour analyser votre premier document.',
|
||||
'history.emptyDocs': 'Aucun document. Importez un document depuis le Studio.',
|
||||
'history.open': 'Ouvrir',
|
||||
|
||||
// Settings
|
||||
'settings.title': 'Paramètres',
|
||||
|
|
@ -138,8 +142,12 @@ const messages = {
|
|||
'upload.uploading': 'Uploading...',
|
||||
'upload.maxSize': 'Max 50MB',
|
||||
|
||||
'history.title': 'Analysis History',
|
||||
'history.title': 'History',
|
||||
'history.tabAnalyses': 'Analyses',
|
||||
'history.tabDocuments': 'Documents',
|
||||
'history.empty': 'No analyses yet. Go to Studio to analyze your first document.',
|
||||
'history.emptyDocs': 'No documents yet. Upload a document from the Studio.',
|
||||
'history.open': 'Open',
|
||||
|
||||
'settings.title': 'Settings',
|
||||
'settings.apiUrl': 'API URL',
|
||||
|
|
|
|||
|
|
@ -59,4 +59,51 @@ describe('useI18n', () => {
|
|||
const { t } = useI18n()
|
||||
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