All checks were successful
Auto Tag Develop / tag (push) Successful in 9s
- 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>
701 lines
21 KiB
TypeScript
701 lines
21 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { ref } from 'vue'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Import under test (after mocks)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
import { useMachineDetailData } from '~/composables/useMachineDetailData'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Mock data — realistic /machines/{id}/structure response
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const MACHINE_ID = 'cl-machine-abc123'
|
|
const SITE_ID = 'cl-site-nord-001'
|
|
const COMPONENT_LINK_ID = 'cl-mcl-001'
|
|
const PIECE_LINK_ID = 'cl-mpl-001'
|
|
const PRODUCT_LINK_ID = 'cl-mprl-001'
|
|
const COMPOSANT_ID = 'cl-comp-moteur-001'
|
|
const PIECE_ID = 'cl-piece-roul-001'
|
|
const PRODUCT_ID = 'cl-prod-graisse-001'
|
|
const CONSTRUCTEUR_ID = 'cstr-skf-001'
|
|
|
|
const mockConstructeurSKF = {
|
|
id: CONSTRUCTEUR_ID,
|
|
name: 'SKF',
|
|
email: 'contact@skf.com',
|
|
phone: '+33 1 23 45 67 89',
|
|
}
|
|
|
|
const mockStructureResponse = {
|
|
success: true,
|
|
data: {
|
|
machine: {
|
|
id: MACHINE_ID,
|
|
name: 'Presse hydraulique PH-200',
|
|
reference: 'MACH-PH-200',
|
|
prix: 150000,
|
|
siteId: SITE_ID,
|
|
site: { id: SITE_ID, name: 'Usine Nord' },
|
|
documents: [{ id: 'doc-001', name: 'Manuel PH-200.pdf', type: 'manual' }],
|
|
customFieldValues: [
|
|
{
|
|
id: 'mcfv-001',
|
|
value: 'SN-2025-PH200',
|
|
customField: {
|
|
id: 'mcf-001',
|
|
name: 'Serial Number',
|
|
type: 'text',
|
|
required: true,
|
|
options: [],
|
|
defaultValue: null,
|
|
orderIndex: 0,
|
|
machineContextOnly: false,
|
|
},
|
|
},
|
|
],
|
|
customFields: [
|
|
{
|
|
id: 'mcf-001',
|
|
name: 'Serial Number',
|
|
type: 'text',
|
|
required: true,
|
|
options: [],
|
|
defaultValue: null,
|
|
orderIndex: 0,
|
|
machineContextOnly: false,
|
|
},
|
|
],
|
|
constructeurs: [
|
|
{
|
|
id: 'cl-mconst-001',
|
|
constructeur: mockConstructeurSKF,
|
|
supplierReference: 'SKF-PH200',
|
|
},
|
|
],
|
|
},
|
|
componentLinks: [
|
|
{
|
|
id: COMPONENT_LINK_ID,
|
|
composant: {
|
|
id: COMPOSANT_ID,
|
|
name: 'Moteur principal',
|
|
reference: 'COMP-MOT-001',
|
|
prix: 12500,
|
|
typeComposant: { id: 'tc-moteur', name: 'Moteur electrique' },
|
|
constructeurs: [mockConstructeurSKF],
|
|
constructeurIds: [CONSTRUCTEUR_ID],
|
|
documents: [],
|
|
customFields: [
|
|
{
|
|
definitionId: 'cf-comp-001',
|
|
name: 'Tension nominale',
|
|
type: 'number',
|
|
value: '380',
|
|
},
|
|
],
|
|
customFieldValues: [],
|
|
},
|
|
overrides: {
|
|
name: 'Moteur principal PH-200',
|
|
reference: 'COMP-MOT-PH200',
|
|
prix: 13000,
|
|
},
|
|
contextCustomFields: [
|
|
{
|
|
id: 'ctx-cf-001',
|
|
name: 'Position sur machine',
|
|
type: 'text',
|
|
machineContextOnly: true,
|
|
},
|
|
],
|
|
contextCustomFieldValues: [
|
|
{
|
|
id: 'ctx-cfv-001',
|
|
value: 'Bloc moteur gauche',
|
|
customField: {
|
|
id: 'ctx-cf-001',
|
|
name: 'Position sur machine',
|
|
type: 'text',
|
|
machineContextOnly: true,
|
|
},
|
|
},
|
|
],
|
|
pieceLinks: [
|
|
{
|
|
id: PIECE_LINK_ID,
|
|
piece: {
|
|
id: PIECE_ID,
|
|
name: 'Roulement 6205',
|
|
reference: 'ROUL-6205',
|
|
prix: 45.90,
|
|
typePiece: { id: 'tp-bearing', name: 'Roulement' },
|
|
constructeurs: [mockConstructeurSKF],
|
|
documents: [],
|
|
customFields: [],
|
|
},
|
|
overrides: {
|
|
name: 'Roulement 6205-RS',
|
|
},
|
|
quantity: 2,
|
|
parentComponentLinkId: COMPONENT_LINK_ID,
|
|
contextCustomFields: [],
|
|
contextCustomFieldValues: [
|
|
{
|
|
id: 'ctx-cfv-piece-001',
|
|
value: 'Cote entrainement',
|
|
customField: {
|
|
id: 'ctx-cf-piece-001',
|
|
name: 'Emplacement',
|
|
type: 'text',
|
|
machineContextOnly: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
childLinks: [],
|
|
},
|
|
],
|
|
pieceLinks: [],
|
|
productLinks: [
|
|
{
|
|
id: PRODUCT_LINK_ID,
|
|
product: {
|
|
id: PRODUCT_ID,
|
|
name: 'Graisse LGMT2',
|
|
reference: 'LUB-LGMT2',
|
|
prix: 45.90,
|
|
},
|
|
overrides: null,
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
// Response with NO overrides — for fallback testing
|
|
const mockStructureNoOverrides = {
|
|
success: true,
|
|
data: {
|
|
machine: {
|
|
...mockStructureResponse.data.machine,
|
|
},
|
|
componentLinks: [
|
|
{
|
|
id: COMPONENT_LINK_ID,
|
|
composant: {
|
|
id: COMPOSANT_ID,
|
|
name: 'Moteur principal',
|
|
reference: 'COMP-MOT-001',
|
|
prix: 12500,
|
|
typeComposant: { id: 'tc-moteur', name: 'Moteur electrique' },
|
|
constructeurs: [],
|
|
documents: [],
|
|
customFields: [],
|
|
customFieldValues: [],
|
|
},
|
|
overrides: null,
|
|
contextCustomFields: [],
|
|
contextCustomFieldValues: [],
|
|
pieceLinks: [
|
|
{
|
|
id: PIECE_LINK_ID,
|
|
piece: {
|
|
id: PIECE_ID,
|
|
name: 'Roulement 6205',
|
|
reference: 'ROUL-6205',
|
|
prix: 45.90,
|
|
typePiece: { id: 'tp-bearing', name: 'Roulement' },
|
|
constructeurs: [],
|
|
documents: [],
|
|
customFields: [],
|
|
},
|
|
overrides: null,
|
|
quantity: 1,
|
|
parentComponentLinkId: COMPONENT_LINK_ID,
|
|
contextCustomFields: [],
|
|
contextCustomFieldValues: [],
|
|
},
|
|
],
|
|
childLinks: [],
|
|
},
|
|
],
|
|
pieceLinks: [],
|
|
productLinks: [],
|
|
},
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Mocks — all composables used by useMachineDetailData
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const mockGet = vi.fn()
|
|
const mockPatch = vi.fn()
|
|
const mockPost = vi.fn()
|
|
const mockDel = vi.fn()
|
|
|
|
vi.mock('~/composables/useApi', () => ({
|
|
useApi: () => ({
|
|
get: mockGet,
|
|
patch: mockPatch,
|
|
post: mockPost,
|
|
delete: mockDel,
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useToast', () => ({
|
|
useToast: () => ({
|
|
showSuccess: vi.fn(),
|
|
showError: vi.fn(),
|
|
showInfo: vi.fn(),
|
|
showToast: vi.fn(),
|
|
toasts: { value: [] },
|
|
clearAll: vi.fn(),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useMachines', () => ({
|
|
useMachines: () => ({
|
|
updateMachine: vi.fn().mockResolvedValue({ success: true }),
|
|
updateStructure: vi.fn().mockResolvedValue({ success: true }),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useComposants', () => ({
|
|
useComposants: () => ({
|
|
updateComposant: vi.fn().mockResolvedValue({ success: true }),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/usePieces', () => ({
|
|
usePieces: () => ({
|
|
updatePiece: vi.fn().mockResolvedValue({ success: true }),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useComponentTypes', () => ({
|
|
useComponentTypes: () => ({
|
|
componentTypes: ref([
|
|
{ id: 'tc-moteur', name: 'Moteur electrique' },
|
|
]),
|
|
loadComponentTypes: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/usePieceTypes', () => ({
|
|
usePieceTypes: () => ({
|
|
pieceTypes: ref([
|
|
{ id: 'tp-bearing', name: 'Roulement' },
|
|
]),
|
|
loadPieceTypes: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useCustomFields', () => ({
|
|
useCustomFields: () => ({
|
|
upsertCustomFieldValue: vi.fn().mockResolvedValue({ success: true }),
|
|
updateCustomFieldValue: vi.fn().mockResolvedValue({ success: true }),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useConstructeurs', () => ({
|
|
useConstructeurs: () => ({
|
|
constructeurs: ref([mockConstructeurSKF]),
|
|
loadConstructeurs: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useSites', () => ({
|
|
useSites: () => ({
|
|
sites: ref([{ id: SITE_ID, name: 'Usine Nord' }]),
|
|
loadSites: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useProducts', () => ({
|
|
useProducts: () => ({
|
|
products: ref([
|
|
{ id: PRODUCT_ID, name: 'Graisse LGMT2', reference: 'LUB-LGMT2', prix: 45.90 },
|
|
]),
|
|
loadProducts: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useDocuments', () => ({
|
|
useDocuments: () => ({
|
|
uploadDocuments: vi.fn().mockResolvedValue({ success: true }),
|
|
deleteDocument: vi.fn().mockResolvedValue({ success: true }),
|
|
loadDocumentsByMachine: vi.fn().mockResolvedValue({ success: true, data: [] }),
|
|
loadDocumentsByProduct: vi.fn().mockResolvedValue({ success: true, data: [] }),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/composables/useConstructeurLinks', () => ({
|
|
useConstructeurLinks: () => ({
|
|
fetchLinks: vi.fn().mockResolvedValue([]),
|
|
syncLinks: vi.fn().mockResolvedValue({ success: true }),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('~/utils/printTemplates/machineReport', () => ({
|
|
buildMachinePrintContext: vi.fn(),
|
|
buildMachinePrintHtml: vi.fn().mockReturnValue('<html></html>'),
|
|
}))
|
|
|
|
vi.mock('~/utils/documentPreview', () => ({
|
|
canPreviewDocument: vi.fn().mockReturnValue(false),
|
|
}))
|
|
|
|
vi.mock('~/shared/utils/documentDisplayUtils', () => ({
|
|
downloadDocument: vi.fn(),
|
|
}))
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Setup
|
|
// ---------------------------------------------------------------------------
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockGet.mockResolvedValue(mockStructureResponse)
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
async function loadAndReturn(responseOverride?: unknown) {
|
|
if (responseOverride) {
|
|
mockGet.mockResolvedValue(responseOverride)
|
|
}
|
|
const result = useMachineDetailData(MACHINE_ID)
|
|
await result.loadMachineData()
|
|
return result
|
|
}
|
|
|
|
// ===========================================================================
|
|
// 1. Hierarchy loading
|
|
// ===========================================================================
|
|
|
|
describe('hierarchy loading', () => {
|
|
it('loads machine with all core fields', async () => {
|
|
const { machine, machineName, machineReference, machineSiteId } = await loadAndReturn()
|
|
|
|
expect(machine.value).not.toBeNull()
|
|
expect(machine.value!.id).toBe(MACHINE_ID)
|
|
expect(machine.value!.name).toBe('Presse hydraulique PH-200')
|
|
expect(machine.value!.reference).toBe('MACH-PH-200')
|
|
expect(machine.value!.prix).toBe(150000)
|
|
expect(machineName.value).toBe('Presse hydraulique PH-200')
|
|
expect(machineReference.value).toBe('MACH-PH-200')
|
|
expect(machineSiteId.value).toBe(SITE_ID)
|
|
})
|
|
|
|
it('calls GET /machines/{id}/structure', async () => {
|
|
await loadAndReturn()
|
|
|
|
expect(mockGet).toHaveBeenCalledWith(`/machines/${MACHINE_ID}/structure`)
|
|
})
|
|
|
|
it('loads componentLinks from structure response', async () => {
|
|
const { machineComponentLinks } = await loadAndReturn()
|
|
|
|
expect(machineComponentLinks.value).toHaveLength(1)
|
|
expect(machineComponentLinks.value[0]!.id).toBe(COMPONENT_LINK_ID)
|
|
})
|
|
|
|
it('builds component hierarchy with composant data', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
expect(components.value.length).toBeGreaterThanOrEqual(1)
|
|
const comp = components.value[0]!
|
|
expect(comp.composantId).toBe(COMPOSANT_ID)
|
|
})
|
|
|
|
it('loads piece links nested under their parent componentLink', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
const pieces = comp.pieces as Record<string, unknown>[]
|
|
expect(pieces).toBeDefined()
|
|
expect(pieces.length).toBeGreaterThanOrEqual(1)
|
|
|
|
const piece = pieces[0]!
|
|
expect(piece.pieceId).toBe(PIECE_ID)
|
|
expect(piece.parentComponentLinkId).toBe(COMPONENT_LINK_ID)
|
|
})
|
|
|
|
it('preserves piece quantity', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
const pieces = comp.pieces as Record<string, unknown>[]
|
|
const piece = pieces[0]!
|
|
expect(piece.quantity).toBe(2)
|
|
})
|
|
|
|
it('loads product links at machine level', async () => {
|
|
const { machineProductLinks } = await loadAndReturn()
|
|
|
|
expect(machineProductLinks.value).toHaveLength(1)
|
|
expect(machineProductLinks.value[0]!.id).toBe(PRODUCT_LINK_ID)
|
|
})
|
|
|
|
it('preserves machine documents', async () => {
|
|
const { machine } = await loadAndReturn()
|
|
|
|
const docs = machine.value!.documents as unknown[]
|
|
expect(docs).toHaveLength(1)
|
|
})
|
|
|
|
it('preserves machine customFieldValues', async () => {
|
|
const { machine } = await loadAndReturn()
|
|
|
|
const cfv = machine.value!.customFieldValues as Record<string, unknown>[]
|
|
expect(cfv).toHaveLength(1)
|
|
expect((cfv[0] as any).value).toBe('SN-2025-PH200')
|
|
})
|
|
|
|
it('sets loading to false after data load', async () => {
|
|
const { loading } = await loadAndReturn()
|
|
|
|
expect(loading.value).toBe(false)
|
|
})
|
|
|
|
it('handles failed API response gracefully', async () => {
|
|
const { machine, components, pieces } = await loadAndReturn({
|
|
success: false,
|
|
error: 'Not found',
|
|
})
|
|
|
|
expect(machine.value).toBeNull()
|
|
expect(components.value).toEqual([])
|
|
expect(pieces.value).toEqual([])
|
|
})
|
|
|
|
it('handles invalid machine payload gracefully', async () => {
|
|
const { machine } = await loadAndReturn({
|
|
success: true,
|
|
data: null,
|
|
})
|
|
|
|
expect(machine.value).toBeNull()
|
|
})
|
|
})
|
|
|
|
// ===========================================================================
|
|
// 2. Overrides
|
|
// ===========================================================================
|
|
|
|
describe('overrides on component links', () => {
|
|
it('uses nameOverride when present', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
expect(comp.name).toBe('Moteur principal PH-200')
|
|
})
|
|
|
|
it('falls back to composant.name when nameOverride is null', async () => {
|
|
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
|
|
|
const comp = components.value[0]!
|
|
expect(comp.name).toBe('Moteur principal')
|
|
})
|
|
|
|
it('uses referenceOverride when present', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
expect(comp.reference).toBe('COMP-MOT-PH200')
|
|
})
|
|
|
|
it('falls back to composant.reference when referenceOverride is null', async () => {
|
|
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
|
|
|
const comp = components.value[0]!
|
|
expect(comp.reference).toBe('COMP-MOT-001')
|
|
})
|
|
|
|
it('uses prixOverride when present', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
expect(comp.prix).toBe(13000)
|
|
})
|
|
|
|
it('falls back to composant.prix when prixOverride is null', async () => {
|
|
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
|
|
|
const comp = components.value[0]!
|
|
expect(comp.prix).toBe(12500)
|
|
})
|
|
})
|
|
|
|
describe('overrides on piece links', () => {
|
|
it('uses piece nameOverride when present', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
|
expect(piece.name).toBe('Roulement 6205-RS')
|
|
})
|
|
|
|
it('falls back to piece.name when nameOverride is null', async () => {
|
|
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
|
|
|
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
|
expect(piece.name).toBe('Roulement 6205')
|
|
})
|
|
|
|
it('preserves piece reference from underlying entity when no override', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
|
// The override only has name, so reference comes from the piece entity
|
|
expect(piece.reference).toBe('ROUL-6205')
|
|
})
|
|
|
|
it('preserves piece prix from underlying entity when no override', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
|
expect(piece.prix).toBe(45.90)
|
|
})
|
|
})
|
|
|
|
// ===========================================================================
|
|
// 3. Custom field values on links (context fields)
|
|
// ===========================================================================
|
|
|
|
describe('contextCustomFieldValues on component links', () => {
|
|
it('loads contextCustomFieldValues on component hierarchy nodes', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
const ctxValues = comp.contextCustomFieldValues as Record<string, unknown>[]
|
|
expect(ctxValues).toBeDefined()
|
|
expect(ctxValues).toHaveLength(1)
|
|
expect((ctxValues[0] as any).value).toBe('Bloc moteur gauche')
|
|
expect((ctxValues[0] as any).customField.name).toBe('Position sur machine')
|
|
})
|
|
|
|
it('loads contextCustomFields definitions on component hierarchy nodes', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const comp = components.value[0]!
|
|
const ctxFields = comp.contextCustomFields as Record<string, unknown>[]
|
|
expect(ctxFields).toBeDefined()
|
|
expect(ctxFields).toHaveLength(1)
|
|
expect((ctxFields[0] as any).name).toBe('Position sur machine')
|
|
expect((ctxFields[0] as any).machineContextOnly).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('contextCustomFieldValues on piece links', () => {
|
|
it('loads contextCustomFieldValues on piece hierarchy nodes', async () => {
|
|
const { components } = await loadAndReturn()
|
|
|
|
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
|
const ctxValues = piece.contextCustomFieldValues as Record<string, unknown>[]
|
|
expect(ctxValues).toBeDefined()
|
|
expect(ctxValues).toHaveLength(1)
|
|
expect((ctxValues[0] as any).value).toBe('Cote entrainement')
|
|
})
|
|
|
|
it('has empty contextCustomFieldValues when none provided', async () => {
|
|
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
|
|
|
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
|
const ctxValues = piece.contextCustomFieldValues as Record<string, unknown>[]
|
|
expect(ctxValues).toEqual([])
|
|
})
|
|
})
|
|
|
|
// ===========================================================================
|
|
// 4. Constructeur links on machine
|
|
// ===========================================================================
|
|
|
|
describe('constructeur links on machine', () => {
|
|
it('parses constructeur links from machine data', async () => {
|
|
const { constructeurLinks } = await loadAndReturn()
|
|
|
|
expect(constructeurLinks.value).toHaveLength(1)
|
|
expect(constructeurLinks.value[0]!.constructeurId).toBe(CONSTRUCTEUR_ID)
|
|
expect(constructeurLinks.value[0]!.supplierReference).toBe('SKF-PH200')
|
|
})
|
|
|
|
it('populates machineConstructeurIds from links', async () => {
|
|
const { machineConstructeurIds } = await loadAndReturn()
|
|
|
|
expect(machineConstructeurIds.value).toContain(CONSTRUCTEUR_ID)
|
|
})
|
|
|
|
it('stores original constructeur links for cancel rollback', async () => {
|
|
const { originalConstructeurLinks } = await loadAndReturn()
|
|
|
|
expect(originalConstructeurLinks.value).toHaveLength(1)
|
|
expect(originalConstructeurLinks.value[0]!.constructeurId).toBe(CONSTRUCTEUR_ID)
|
|
})
|
|
|
|
it('hasMachineConstructeur is true when constructeur present', async () => {
|
|
const { hasMachineConstructeur } = await loadAndReturn()
|
|
|
|
expect(hasMachineConstructeur.value).toBe(true)
|
|
})
|
|
|
|
it('resolves constructeur display objects', async () => {
|
|
const { machineConstructeursDisplay } = await loadAndReturn()
|
|
|
|
expect(machineConstructeursDisplay.value.length).toBeGreaterThanOrEqual(1)
|
|
const display = machineConstructeursDisplay.value[0] as any
|
|
expect(display.name).toBe('SKF')
|
|
})
|
|
})
|
|
|
|
// ===========================================================================
|
|
// 5. Site (required)
|
|
// ===========================================================================
|
|
|
|
describe('site loaded with machine data', () => {
|
|
it('machineSiteId is populated from machine payload', async () => {
|
|
const { machineSiteId } = await loadAndReturn()
|
|
|
|
expect(machineSiteId.value).toBe(SITE_ID)
|
|
})
|
|
|
|
it('sites ref is available for dropdowns', async () => {
|
|
const { sites } = await loadAndReturn()
|
|
|
|
expect(sites.value).toHaveLength(1)
|
|
expect((sites.value[0] as any).name).toBe('Usine Nord')
|
|
})
|
|
|
|
it('machine.site object is preserved in machine ref', async () => {
|
|
const { machine } = await loadAndReturn()
|
|
|
|
const site = machine.value!.site as Record<string, unknown>
|
|
expect(site.id).toBe(SITE_ID)
|
|
expect(site.name).toBe('Usine Nord')
|
|
})
|
|
})
|
|
|
|
// ===========================================================================
|
|
// 6. UI state defaults
|
|
// ===========================================================================
|
|
|
|
describe('UI state defaults', () => {
|
|
it('isEditMode starts as false', () => {
|
|
const { isEditMode } = useMachineDetailData(MACHINE_ID)
|
|
expect(isEditMode.value).toBe(false)
|
|
})
|
|
|
|
it('saving starts as false', () => {
|
|
const { saving } = useMachineDetailData(MACHINE_ID)
|
|
expect(saving.value).toBe(false)
|
|
})
|
|
|
|
it('loading starts as true', () => {
|
|
const { loading } = useMachineDetailData(MACHINE_ID)
|
|
expect(loading.value).toBe(true)
|
|
})
|
|
})
|