Add Vitest suites for relations, properties, catalogue, symbols, edge styles, flow solver, settings, JointJS shapes/serialization and the controller lifecycle (add/connect/edit/delete, undo-redo, copy-paste, flow, save-load). Introduce headless DiagramController (Paper-less) so the controller is testable outside a browser, and fix the flow direction sign. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
import { DiagramController } from '@diagram/controller'
|
|
|
|
// Headless controller (no DOM host / Paper) exercises the graph-level logic.
|
|
let ctrl: DiagramController
|
|
|
|
beforeEach(() => {
|
|
ctrl = new DiagramController(null)
|
|
})
|
|
|
|
afterEach(() => {
|
|
ctrl.dispose()
|
|
})
|
|
|
|
describe('node lifecycle', () => {
|
|
it('adds a node and selects it', () => {
|
|
const node = ctrl.addNode('pump', 100, 100)
|
|
expect(ctrl.graph.getElements().length).toBe(1)
|
|
expect(ctrl.getSelection().map((c) => c.id)).toContain(node.id)
|
|
})
|
|
|
|
it('edits properties through applyValues', () => {
|
|
const node = ctrl.addNode('valve', 40, 40)
|
|
ctrl.applyValues(node.id as string, { ...(node.get('values') as object), title: 'V-9', opening: 50 })
|
|
expect(node.attr('label/text')).toBe('V-9')
|
|
expect((node.get('values') as Record<string, unknown>).opening).toBe(50)
|
|
})
|
|
|
|
it('deletes the selection', () => {
|
|
const node = ctrl.addNode('tank', 40, 40)
|
|
ctrl.select(node.id as string, false)
|
|
ctrl.deleteSelected()
|
|
expect(ctrl.graph.getElements().length).toBe(0)
|
|
})
|
|
})
|
|
|
|
describe('undo / redo', () => {
|
|
it('reverts and reapplies an add', () => {
|
|
ctrl.addNode('pump', 0, 0)
|
|
expect(ctrl.graph.getElements().length).toBe(1)
|
|
ctrl.undo()
|
|
expect(ctrl.graph.getElements().length).toBe(0)
|
|
ctrl.redo()
|
|
expect(ctrl.graph.getElements().length).toBe(1)
|
|
})
|
|
})
|
|
|
|
describe('copy / paste', () => {
|
|
it('duplicates selected nodes', () => {
|
|
const node = ctrl.addNode('valve', 60, 60)
|
|
ctrl.select(node.id as string, false)
|
|
ctrl.duplicateSelected()
|
|
expect(ctrl.graph.getElements().length).toBe(2)
|
|
})
|
|
})
|
|
|
|
describe('flow', () => {
|
|
it('solves and animates a seeded network, then stops', () => {
|
|
ctrl.seedSample()
|
|
const sol = ctrl.runFlow()
|
|
expect(sol.edges.size).toBeGreaterThan(0)
|
|
expect(sol.maxMagnitude).toBeGreaterThan(0)
|
|
expect(ctrl.isFlowing()).toBe(true)
|
|
ctrl.stopFlow()
|
|
expect(ctrl.isFlowing()).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('serialization', () => {
|
|
it('round-trips the whole diagram including settings', () => {
|
|
ctrl.seedSample()
|
|
ctrl.setSettings({ jumpover: false, colorScheme: 'colorblind' })
|
|
const json = ctrl.toJSON()
|
|
const nElements = ctrl.graph.getElements().length
|
|
|
|
const ctrl2 = new DiagramController(null)
|
|
ctrl2.loadJSON(json)
|
|
expect(ctrl2.graph.getElements().length).toBe(nElements)
|
|
expect(ctrl2.settings.colorScheme).toBe('colorblind')
|
|
expect(ctrl2.settings.jumpover).toBe(false)
|
|
ctrl2.dispose()
|
|
})
|
|
})
|