const NodeClass = require('../src/nodeClass'); jest.mock('../src/specificClass', () => { return jest.fn().mockImplementation(() => ({ logger: { warn: jest.fn(), }, generateDashboardsForGraph: jest.fn(() => [{ dashboard: { title: 'ok' }, nodeId: 'child-node-id', softwareType: 'measurement', uid: 'child-uid', title: 'ok', }]), buildUpsertRequest: jest.fn(({ dashboard, folderId, overwrite }) => ({ dashboard, folderId, overwrite, })), grafanaUpsertUrl: jest.fn(() => 'http://grafana:3000/api/dashboards/db'), })); }); const SpecificClass = require('../src/specificClass'); function createNodeHarness(flowNode = null) { const handlers = {}; const node = { id: 'dashboard-node-id', on: jest.fn((event, handler) => { handlers[event] = handler; }), status: jest.fn(), error: jest.fn(), _flow: { getNode: jest.fn(() => flowNode), }, }; return { node, handlers }; } describe('dashboardAPI nodeClass', () => { beforeEach(() => { jest.clearAllMocks(); delete process.env.GRAFANA_TOKEN; }); it('uses RED.nodes.getNode when it returns a runtime child', async () => { const childNode = { source: { config: { general: { name: 'child' }, functionality: { softwareType: 'measurement' }, }, }, }; const { node, handlers } = createNodeHarness(); const RED = { nodes: { getNode: jest.fn(() => childNode), }, }; new NodeClass({ name: 'E2E-DashboardAPI', host: 'grafana', port: 3000 }, RED, node, 'dashboardapi'); expect(SpecificClass).toHaveBeenCalledWith( expect.objectContaining({ general: expect.objectContaining({ name: 'E2E-DashboardAPI', id: 'dashboard-node-id', }), functionality: expect.objectContaining({ softwareType: 'dashboardapi', role: 'auto ui generator', }), grafanaConnector: expect.objectContaining({ host: 'grafana', port: 3000, }), }), ); const send = jest.fn(); const done = jest.fn(); await handlers.input({ topic: 'registerChild', payload: 'measurement-e2e-node' }, send, done); expect(RED.nodes.getNode).toHaveBeenCalledWith('measurement-e2e-node'); expect(send).toHaveBeenCalledWith( expect.objectContaining({ topic: 'create', url: 'http://grafana:3000/api/dashboards/db', method: 'POST', payload: { dashboard: { title: 'ok' }, folderId: 0, overwrite: true, }, }), ); expect(done).toHaveBeenCalledWith(); }); it('falls back to the active flow when RED.nodes.getNode lacks source state', async () => { const flowChildNode = { source: { config: { general: { name: 'child' }, functionality: { softwareType: 'measurement' }, }, }, }; const { node, handlers } = createNodeHarness(flowChildNode); const RED = { nodes: { getNode: jest.fn(() => ({ id: 'measurement-e2e-node' })), }, }; new NodeClass({ name: 'E2E-DashboardAPI', host: 'grafana', port: 3000 }, RED, node, 'dashboardapi'); const send = jest.fn(); const done = jest.fn(); await handlers.input({ topic: 'registerChild', payload: 'measurement-e2e-node' }, send, done); expect(node._flow.getNode).toHaveBeenCalledWith('measurement-e2e-node'); expect(send).toHaveBeenCalledWith( expect.objectContaining({ topic: 'create', payload: { dashboard: { title: 'ok' }, folderId: 0, overwrite: true, }, }), ); expect(done).toHaveBeenCalledWith(); }); it('accepts a child config payload directly', async () => { const { node, handlers } = createNodeHarness(); const RED = { nodes: { getNode: jest.fn(), }, }; new NodeClass({ name: 'E2E-DashboardAPI', host: 'grafana', port: 3000 }, RED, node, 'dashboardapi'); const send = jest.fn(); const done = jest.fn(); await handlers.input( { topic: 'registerChild', payload: { config: { general: { name: 'E2E-Level-Sensor' }, functionality: { softwareType: 'measurement' }, }, }, }, send, done, ); expect(RED.nodes.getNode).not.toHaveBeenCalled(); expect(send).toHaveBeenCalledWith( expect.objectContaining({ topic: 'create', payload: { dashboard: { title: 'ok' }, folderId: 0, overwrite: true, }, }), ); expect(done).toHaveBeenCalledWith(); }); });