Files
Inventory/frontend/tests/composables/useDocuments.test.ts
Matthieu daa0cb1e28
All checks were successful
Auto Tag Develop / tag (push) Successful in 9s
feat(fournisseurs) : categories (M2M) + telephones (1-N) + import customer.json
- Nouvelles entites ConstructeurCategorie (referentiel M2M) et ConstructeurTelephone (1-N)
- Constructeur : retrait colonne phone, ajout collections telephones/categories, groupes de serialisation constructeur:read/write
- Migration : cree les 3 tables, migre la colonne phone existante vers constructeur_telephone, drop phone
- Commande app:import-fournisseurs (dry-run par defaut, --force) : non destructive, find-or-create par nom, ne touche jamais un ID existant, ajout-seulement pour telephones/categories
- MAJ MCP tools / MachineStructureController / audit subscriber / tests
- Frontend : page constructeurs avec telephones multiples + categories (tableau, filtre, formulaire), composable useConstructeurCategories, composant ConstructeurCategorieSelect

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:02:44 +02:00

320 lines
10 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { wrapCollection } from '../fixtures/mockData'
// ---------------------------------------------------------------------------
// Import under test (AFTER all vi.mock calls)
// ---------------------------------------------------------------------------
import { useDocuments } from '~/composables/useDocuments'
// ---------------------------------------------------------------------------
// Mocks — API layer
// ---------------------------------------------------------------------------
const mockGet = vi.fn()
const mockPatch = vi.fn()
const mockPostFormData = vi.fn()
const mockDel = vi.fn()
vi.mock('~/composables/useApi', () => ({
useApi: () => ({
get: mockGet,
post: vi.fn(),
patch: mockPatch,
put: vi.fn(),
delete: mockDel,
postFormData: mockPostFormData,
}),
}))
// ---------------------------------------------------------------------------
// Mocks — Toast
// ---------------------------------------------------------------------------
const mockShowSuccess = vi.fn()
const mockShowError = vi.fn()
vi.mock('~/composables/useToast', () => ({
useToast: () => ({
showSuccess: mockShowSuccess,
showError: mockShowError,
showInfo: vi.fn(),
showToast: vi.fn(),
toasts: { value: [] },
clearAll: vi.fn(),
}),
}))
// ---------------------------------------------------------------------------
// Test data
// ---------------------------------------------------------------------------
const mockDocument = {
id: 'doc-001',
name: 'photo.jpg',
filename: 'photo.jpg',
mimeType: 'image/jpeg',
size: 12345,
fileUrl: '/files/photo.jpg',
downloadUrl: '/files/photo.jpg/download',
createdAt: '2025-01-10T08:00:00+00:00',
}
const mockDocument2 = {
id: 'doc-002',
name: 'schema.pdf',
filename: 'schema.pdf',
mimeType: 'application/pdf',
size: 54321,
fileUrl: '/files/schema.pdf',
downloadUrl: '/files/schema.pdf/download',
createdAt: '2025-01-11T09:00:00+00:00',
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function createMockFile(name: string, type = 'image/jpeg'): File {
return new File(['content'], name, { type })
}
// ---------------------------------------------------------------------------
// beforeEach
// ---------------------------------------------------------------------------
beforeEach(() => {
vi.clearAllMocks()
})
// ---------------------------------------------------------------------------
// uploadDocuments — FormData is built correctly
// ---------------------------------------------------------------------------
describe('uploadDocuments', () => {
it('builds FormData with file and context fields', async () => {
mockPostFormData.mockResolvedValue({ success: true, data: mockDocument })
const { uploadDocuments } = useDocuments()
const file = createMockFile('photo.jpg')
await uploadDocuments({
files: [file],
context: { pieceId: 'piece-001', composantId: 'comp-001' },
})
expect(mockPostFormData).toHaveBeenCalledTimes(1)
const [endpoint, formData] = mockPostFormData.mock.calls[0]!
expect(endpoint).toBe('/documents')
expect(formData).toBeInstanceOf(FormData)
expect(formData.get('file')).toBe(file)
expect(formData.get('name')).toBe('photo.jpg')
expect(formData.get('pieceId')).toBe('piece-001')
expect(formData.get('composantId')).toBe('comp-001')
})
it('uploads multiple files separately', async () => {
mockPostFormData
.mockResolvedValueOnce({ success: true, data: mockDocument })
.mockResolvedValueOnce({ success: true, data: mockDocument2 })
const { uploadDocuments } = useDocuments()
const file1 = createMockFile('photo.jpg')
const file2 = createMockFile('schema.pdf', 'application/pdf')
const result = await uploadDocuments({
files: [file1, file2],
context: { machineId: 'machine-001' },
})
expect(mockPostFormData).toHaveBeenCalledTimes(2)
// First call
const [, formData1] = mockPostFormData.mock.calls[0]!
expect(formData1.get('name')).toBe('photo.jpg')
expect(formData1.get('machineId')).toBe('machine-001')
// Second call
const [, formData2] = mockPostFormData.mock.calls[1]!
expect(formData2.get('name')).toBe('schema.pdf')
expect(formData2.get('machineId')).toBe('machine-001')
expect(result.success).toBe(true)
expect(Array.isArray(result.data) ? result.data : []).toHaveLength(2)
})
it('appends type to FormData when provided in context', async () => {
mockPostFormData.mockResolvedValue({ success: true, data: mockDocument })
const { uploadDocuments } = useDocuments()
const file = createMockFile('facture.pdf', 'application/pdf')
await uploadDocuments({
files: [file],
context: { siteId: 'site-001', type: 'facture' },
})
const [, formData] = mockPostFormData.mock.calls[0]!
expect(formData.get('type')).toBe('facture')
expect(formData.get('siteId')).toBe('site-001')
})
it('returns error when no files provided', async () => {
const { uploadDocuments } = useDocuments()
const result = await uploadDocuments({ files: [], context: {} })
expect(result.success).toBe(false)
expect(mockPostFormData).not.toHaveBeenCalled()
})
})
// ---------------------------------------------------------------------------
// loadDocumentsByComponent
// ---------------------------------------------------------------------------
describe('loadDocumentsByComponent', () => {
it('calls correct endpoint /documents/composant/{id}', async () => {
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
const { loadDocumentsByComponent } = useDocuments()
const result = await loadDocumentsByComponent('comp-001')
expect(mockGet).toHaveBeenCalledTimes(1)
expect(mockGet).toHaveBeenCalledWith('/documents/composant/comp-001')
expect(result.success).toBe(true)
})
it('returns error for empty componentId', async () => {
const { loadDocumentsByComponent } = useDocuments()
const result = await loadDocumentsByComponent('')
expect(mockGet).not.toHaveBeenCalled()
expect(result.success).toBe(false)
})
})
// ---------------------------------------------------------------------------
// loadDocumentsByPiece
// ---------------------------------------------------------------------------
describe('loadDocumentsByPiece', () => {
it('calls correct endpoint /documents/piece/{id}', async () => {
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
const { loadDocumentsByPiece } = useDocuments()
const result = await loadDocumentsByPiece('piece-001')
expect(mockGet).toHaveBeenCalledTimes(1)
expect(mockGet).toHaveBeenCalledWith('/documents/piece/piece-001')
expect(result.success).toBe(true)
})
it('returns error for empty pieceId', async () => {
const { loadDocumentsByPiece } = useDocuments()
const result = await loadDocumentsByPiece('')
expect(mockGet).not.toHaveBeenCalled()
expect(result.success).toBe(false)
})
})
// ---------------------------------------------------------------------------
// loadDocumentsByMachine
// ---------------------------------------------------------------------------
describe('loadDocumentsByMachine', () => {
it('calls correct endpoint /documents/machine/{id}', async () => {
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
const { loadDocumentsByMachine } = useDocuments()
const result = await loadDocumentsByMachine('machine-001')
expect(mockGet).toHaveBeenCalledTimes(1)
expect(mockGet).toHaveBeenCalledWith('/documents/machine/machine-001')
expect(result.success).toBe(true)
})
it('returns error for empty machineId', async () => {
const { loadDocumentsByMachine } = useDocuments()
const result = await loadDocumentsByMachine('')
expect(mockGet).not.toHaveBeenCalled()
expect(result.success).toBe(false)
})
})
// ---------------------------------------------------------------------------
// loadDocumentsByProduct
// ---------------------------------------------------------------------------
describe('loadDocumentsByProduct', () => {
it('calls correct endpoint /documents/product/{id}', async () => {
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
const { loadDocumentsByProduct } = useDocuments()
const result = await loadDocumentsByProduct('prod-001')
expect(mockGet).toHaveBeenCalledTimes(1)
expect(mockGet).toHaveBeenCalledWith('/documents/product/prod-001')
expect(result.success).toBe(true)
})
it('returns error for empty productId', async () => {
const { loadDocumentsByProduct } = useDocuments()
const result = await loadDocumentsByProduct('')
expect(mockGet).not.toHaveBeenCalled()
expect(result.success).toBe(false)
})
})
// ---------------------------------------------------------------------------
// deleteDocument
// ---------------------------------------------------------------------------
describe('deleteDocument', () => {
it('calls DELETE on correct endpoint', async () => {
mockDel.mockResolvedValue({ success: true })
const { deleteDocument } = useDocuments()
const result = await deleteDocument('doc-001')
expect(mockDel).toHaveBeenCalledTimes(1)
expect(mockDel).toHaveBeenCalledWith('/documents/doc-001')
expect(result.success).toBe(true)
})
it('removes from store when updateStore is true', async () => {
mockGet.mockResolvedValue({
success: true,
data: wrapCollection([mockDocument, mockDocument2]),
})
mockDel.mockResolvedValue({ success: true })
const { loadDocuments, deleteDocument, documents } = useDocuments()
// Load documents into store first
await loadDocuments({ force: true })
expect(documents.value).toHaveLength(2)
// Delete with updateStore: true
await deleteDocument('doc-001', { updateStore: true })
expect(documents.value).toHaveLength(1)
expect(documents.value[0]!.id).toBe('doc-002')
})
it('shows success toast on successful delete', async () => {
mockDel.mockResolvedValue({ success: true })
const { deleteDocument } = useDocuments()
await deleteDocument('doc-001')
expect(mockShowSuccess).toHaveBeenCalledWith('Document supprimé')
})
})