Default fuzzy reconcile threshold to 0 and refuse merges when digit runs differ so numbered labels like Bay 311 never bind to Bay 310. Keep edit forms from clearing saved model fields when category filtering hides untyped rows. Includes in-progress computers policy and endpoints command feature work.
203 lines
7.5 KiB
JavaScript
203 lines
7.5 KiB
JavaScript
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')
|
|
})
|
|
})
|