Files
homelable/frontend/src/utils/__tests__/propertyIcons.test.ts
T
Pouzor 3d89ba3b6f feat: replace static hardware fields with dynamic node properties
Replaces the 4 fixed hardware columns (cpu_count, cpu_model, ram_gb,
disk_gb) with a flexible properties system. Each property has a key,
value, icon (from a curated Lucide picker), and a visibility toggle
that controls whether it appears on the canvas node card.

- Backend: add `properties` JSON column to Node model; data migration
  converts existing hardware rows to properties with correct icons
  (idempotent, old columns kept for safety)
- Backend: add `properties` to NodeBase, NodeUpdate, NodeSave schemas
  and canvasSerializer so values survive canvas save/load
- Frontend: add NodeProperty type; new propertyIcons.ts registry (20
  icons); BaseNode renders visible properties with legacy hardware
  fallback for unmigrated nodes
- Frontend: DetailPanel gains interactive properties section (add /
  edit / remove / toggle visibility / icon picker) replacing the
  read-only hardware block; hardware section removed from NodeModal
- Tests: 6 migration tests, 7 API tests, 8 DetailPanel property tests,
  6 BaseNode render/fallback tests, 9 propertyIcons util tests
2026-04-09 13:47:36 +02:00

54 lines
1.8 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { Cpu, HardDrive, MemoryStick } from 'lucide-react'
import { PROPERTY_ICONS, PROPERTY_ICON_NAMES, resolvePropertyIcon } from '../propertyIcons'
describe('PROPERTY_ICONS', () => {
it('contains the hardware migration icons', () => {
expect(PROPERTY_ICONS['Cpu']).toBe(Cpu)
expect(PROPERTY_ICONS['HardDrive']).toBe(HardDrive)
expect(PROPERTY_ICONS['MemoryStick']).toBe(MemoryStick)
})
it('has at least 10 icons', () => {
expect(Object.keys(PROPERTY_ICONS).length).toBeGreaterThanOrEqual(10)
})
it('every value is a renderable component (function or object)', () => {
for (const [, icon] of Object.entries(PROPERTY_ICONS)) {
// Lucide icons can be functions or forwardRef objects depending on environment
expect(icon).toBeTruthy()
expect(['function', 'object']).toContain(typeof icon)
}
})
})
describe('PROPERTY_ICON_NAMES', () => {
it('matches the keys of PROPERTY_ICONS', () => {
expect(PROPERTY_ICON_NAMES).toEqual(expect.arrayContaining(Object.keys(PROPERTY_ICONS)))
expect(PROPERTY_ICON_NAMES.length).toBe(Object.keys(PROPERTY_ICONS).length)
})
})
describe('resolvePropertyIcon', () => {
it('returns the icon for a known name', () => {
expect(resolvePropertyIcon('Cpu')).toBe(Cpu)
expect(resolvePropertyIcon('HardDrive')).toBe(HardDrive)
})
it('returns null for null input', () => {
expect(resolvePropertyIcon(null)).toBeNull()
})
it('returns null for undefined input', () => {
expect(resolvePropertyIcon(undefined)).toBeNull()
})
it('returns null for unknown icon name', () => {
expect(resolvePropertyIcon('NotARealIcon')).toBeNull()
})
it('returns null for empty string', () => {
expect(resolvePropertyIcon('')).toBeNull()
})
})