import { describe, it, expect } from 'vitest' import { toApiPath, subst, rows, mapTitle, mapBadge, mapMeta, manageLink, keyvalueFields, tableColumns, badges, panelVisible, tabSections, rollupStatus, rowAction, } from './pluginAssetPanels' const warrantyPanel = { id: 'warranty', title: 'Warranty', render: 'list', map: { title: 'vendor', badge: { label: 'status', color: 'statuscolor', labelmap: { active: 'Active', expired: 'Expired' }, }, meta: [ { key: 'servicelevel' }, { key: 'enddate', label: 'Ends', format: 'date' }, { key: 'servicetag', label: 'Tag', mono: true }, ], }, empty: 'No warranty on record.', manage: { to: '/warranties?addfor={assetid}', label: 'Add / manage' }, _data: [ { vendor: 'Dell', status: 'active', statuscolor: '#4CAF50', servicelevel: 'ProSupport', enddate: '2027-01-01', servicetag: 'ABC123', }, ], } describe('endpoint + subst', () => { it('substitutes {assetid} and strips the leading /api', () => { expect(toApiPath('/api/warranty/asset/{assetid}', 5)).toBe('/warranty/asset/5') expect(subst('/warranties?addfor={assetid}', 9)).toBe('/warranties?addfor=9') }) it('only strips a leading /api segment, not /apixyz', () => { expect(toApiPath('/apixyz/thing', 1)).toBe('/apixyz/thing') }) }) describe('list render mapping (warranty)', () => { it('maps title, badge (labelmap + color), and formatted meta', () => { const item = rows(warrantyPanel)[0] expect(mapTitle(warrantyPanel, item)).toBe('Dell') const badge = mapBadge(warrantyPanel, item) expect(badge.label).toBe('Active') expect(badge.color).toBe('#4CAF50') const meta = mapMeta(warrantyPanel, item) expect(meta[0].text).toBe('ProSupport') expect(meta[1].text).toMatch(/^Ends /) // date formatted + labelled expect(meta[2]).toEqual({ text: 'Tag ABC123', mono: true }) }) it('drops empty meta values', () => { const item = { vendor: 'HP', status: 'expired', statuscolor: '#F44336' } const meta = mapMeta(warrantyPanel, { ...item }) expect(meta).toEqual([]) // no servicelevel/enddate/servicetag expect(mapBadge(warrantyPanel, item).label).toBe('Expired') }) it('builds the manage link with the assetid', () => { expect(manageLink(warrantyPanel, 7)).toBe('/warranties?addfor=7') }) it('is visible when it has rows or an empty message', () => { expect(panelVisible(warrantyPanel)).toBe(true) expect(panelVisible({ ...warrantyPanel, _data: [] })).toBe(true) // has empty expect(panelVisible({ render: 'list', _data: [] })).toBe(false) }) }) describe('keyvalue + table + badge', () => { it('keyvalue reads a fields array or a plain object', () => { const fromFields = keyvalueFields({ render: 'keyvalue', _data: { fields: [{ label: 'Cal due', value: '2026-01-01', format: 'date' }] }, }) expect(fromFields[0].label).toBe('Cal due') const fromObject = keyvalueFields({ render: 'keyvalue', _data: { hostname: 'PC1', online: true } }) expect(fromObject).toEqual([ { label: 'Hostname', value: 'PC1' }, { label: 'Online', value: 'Yes' }, ]) }) it('table uses declared columns, else infers from row keys', () => { const declared = tableColumns({ columns: [{ key: 'a', label: 'Alpha' }], _data: [{ a: 1 }] }) expect(declared).toEqual([{ key: 'a', label: 'Alpha' }]) const inferred = tableColumns({ _data: [{ vendor: 'x', model: 'y' }] }) expect(inferred.map((c) => c.label)).toEqual(['Vendor', 'Model']) }) it('badge reads an array or a badges field', () => { expect(badges({ render: 'badge', _data: [{ label: 'A', color: '#111' }] })).toEqual([{ label: 'A', color: '#111' }]) expect(badges({ render: 'badge', _data: { badges: [{ label: 'B', color: '#222' }] } })).toEqual([{ label: 'B', color: '#222' }]) }) }) describe('tabSections', () => { const panel = { render: 'tabs', _data: { sections: [ { label: 'General', fields: [{ label: 'Cnc', value: 'OKUMA' }] }, { label: 'eFocas', fields: [{ label: 'IpAddr', value: '192.168.1.1', mono: true }] }, // A section whose fields all dropped out must not leave an empty tab. { label: 'NTSHR', fields: [] }, ], }, } it('returns one entry per non-empty section', () => { expect(tabSections(panel).map((s) => s.label)).toEqual(['General', 'eFocas']) }) it('formats field values and carries mono through', () => { const efocas = tabSections(panel)[1] expect(efocas.fields[0]).toEqual({ label: 'IpAddr', value: '192.168.1.1', mono: true }) }) it('is empty for a payload with no sections', () => { expect(tabSections({ render: 'tabs', _data: null })).toEqual([]) }) it('drives panel visibility', () => { expect(panelVisible(panel)).toBe(true) expect(panelVisible({ render: 'tabs', _data: { sections: [] } })).toBe(false) }) }) describe('rollupStatus', () => { const status = { key: 'statecolor', levels: { green: 'ok', yellow: 'warning', red: 'critical' }, } it('returns null when the panel declares no status', () => { expect(rollupStatus({ _data: [{ statecolor: 'green' }] })).toBeNull() }) it('returns null when there are no rows', () => { expect(rollupStatus({ status, _data: [] })).toBeNull() }) it('is ok (green) when every row is ok', () => { const result = rollupStatus({ status, _data: [{ statecolor: 'green' }, { statecolor: 'green' }] }) expect(result).toEqual({ level: 'ok', color: '#2dce89' }) }) it('is warning (yellow) when a warning is present but no critical', () => { const result = rollupStatus({ status, _data: [{ statecolor: 'green' }, { statecolor: 'yellow' }] }) expect(result).toEqual({ level: 'warning', color: '#ffc107' }) }) it('is critical (red) when any row is stopped', () => { const result = rollupStatus({ status, _data: [{ statecolor: 'yellow' }, { statecolor: 'red' }] }) expect(result).toEqual({ level: 'critical', color: '#f5365c' }) }) it('ignores unmapped values without raising the level', () => { const result = rollupStatus({ status, _data: [{ statecolor: 'gray' }, { statecolor: 'green' }] }) expect(result).toEqual({ level: 'ok', color: '#2dce89' }) }) it('returns null when no row value maps to a known level', () => { expect(rollupStatus({ status, _data: [{ statecolor: 'gray' }] })).toBeNull() }) }) describe('rowAction (inline MTConnect toggle)', () => { const panel = { id: 'endpoints-services', render: 'list', commandendpoint: '/api/endpoints/5/commands' } it('returns null for rows without an action', () => { expect(rowAction(panel, { name: 'Some Service' })).toBeNull() expect(rowAction(panel, { name: 'x', action: { status: 'mtconnect_status' } })).toBeNull() }) it('offers Stop when the row is running', () => { const info = rowAction(panel, { name: 'MTConnect', action: { running: true, start: 'mtconnect_start', stop: 'mtconnect_stop', status: 'mtconnect_status' }, }) expect(info.running).toBe(true) expect(info.toggle).toBe('mtconnect_stop') expect(info.toggleLabel).toBe('Stop') expect(info.endpoint).toBe('/api/endpoints/5/commands') }) it('offers Start when the row is not running', () => { const info = rowAction(panel, { name: 'MTConnect', action: { running: false, start: 'mtconnect_start', stop: 'mtconnect_stop', status: 'mtconnect_status' }, }) expect(info.running).toBe(false) expect(info.toggle).toBe('mtconnect_start') expect(info.toggleLabel).toBe('Start') }) })