epaper-display/display/middleware/tests/display-builder.test.ts
Christian Mueller 2990f9aa4d feat(display): add display builder for ESP32 JSON + tests (Task 7)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 12:56:01 +02:00

117 lines
3.8 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { initDb, closeDb } from '../src/db/index';
import { buildDisplayJson } from '../src/modules/display/display-builder';
beforeAll(() => {
const db = initDb(':memory:');
// Create a layout
db.run(
`INSERT INTO layouts (id, name, type, width, height, grid_cols, grid_rows)
VALUES (1, 'Test Layout', 'grid', 800, 480, 2, 3)`
);
// Create a display with the layout
db.run(
`INSERT INTO displays (id, name, layout_id, refresh_sec)
VALUES (1, 'Test Display', 1, 60)`
);
// Create a display without layout
db.run(
`INSERT INTO displays (id, name, layout_id, refresh_sec)
VALUES (2, 'Empty Display', NULL, 300)`
);
// Create a broker and topic for datapoint
db.run(
`INSERT INTO mqtt_brokers (id, name, host) VALUES (1, 'Test Broker', 'localhost')`
);
db.run(
`INSERT INTO mqtt_topics (id, broker_id, topic) VALUES (1, 1, 'sensors/temp')`
);
// Create a datapoint
db.run(
`INSERT INTO datapoints (id, name, label, unit, value, source_type, source_id, topic_id, enabled)
VALUES (1, 'temperature', 'Temperature', '°C', '22.5', 'mqtt', 1, 1, 1)`
);
// Create layout elements
// Label element
db.run(
`INSERT INTO layout_elements (id, layout_id, type, x, y, static_text, sort_order)
VALUES (1, 1, 'label', 0, 0, 'Hello World', 0)`
);
// Value element linked to datapoint
db.run(
`INSERT INTO layout_elements (id, layout_id, datapoint_id, type, x, y, font_size, sort_order)
VALUES (2, 1, 1, 'value', 100, 0, 32, 1)`
);
});
afterAll(() => {
closeDb();
});
describe('buildDisplayJson', () => {
it('returns null for non-existent display', () => {
const result = buildDisplayJson(999);
expect(result).toBeNull();
});
it('returns complete JSON for a display with layout', () => {
const result = buildDisplayJson(1);
expect(result).not.toBeNull();
expect(result!.display.id).toBe(1);
expect(result!.display.name).toBe('Test Display');
expect(result!.display.width).toBe(800);
expect(result!.display.height).toBe(480);
expect(result!.display.refresh_sec).toBe(60);
});
it('includes grid layout info when grid_cols and grid_rows are set', () => {
const result = buildDisplayJson(1);
expect(result!.layout.type).toBe('grid');
expect(result!.layout.grid_cols).toBe(2);
expect(result!.layout.grid_rows).toBe(3);
});
it('includes label element with text', () => {
const result = buildDisplayJson(1);
const labelEl = result!.elements.find((e) => e.type === 'label');
expect(labelEl).toBeDefined();
expect(labelEl!.text).toBe('Hello World');
});
it('includes value element with datapoint data', () => {
const result = buildDisplayJson(1);
const valueEl = result!.elements.find((e) => e.type === 'value');
expect(valueEl).toBeDefined();
expect(valueEl!.label).toBe('Temperature');
expect(valueEl!.value).toBe('22.5');
expect(valueEl!.unit).toBe('°C');
});
it('includes non-default font_size in value element', () => {
const result = buildDisplayJson(1);
const valueEl = result!.elements.find((e) => e.type === 'value');
expect(valueEl!.font_size).toBe(32);
});
it('does not include default font_size in label element', () => {
const result = buildDisplayJson(1);
const labelEl = result!.elements.find((e) => e.type === 'label');
// font_size 24 is default, should not be included
expect(labelEl!.font_size).toBeUndefined();
});
it('returns empty elements and default dimensions for display without layout', () => {
const result = buildDisplayJson(2);
expect(result).not.toBeNull();
expect(result!.elements).toHaveLength(0);
expect(result!.display.width).toBe(800);
expect(result!.display.height).toBe(480);
});
});