const NodeClass = require('../src/nodeClass'); jest.mock('../src/specificClass', () => { return jest.fn().mockImplementation(() => ({ logger: { warn: jest.fn(), }, generateDashB: jest.fn().mockResolvedValue({ dashboard: { title: 'ok' } }), })); }); 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', payload: { dashboard: { title: 'ok' } }, }), ); 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' } }, }), ); 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' } }, }), ); expect(done).toHaveBeenCalledWith(); }); });