PrinterHASensorRow.test.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /**
  2. * The Home Assistant sensor row on the printer card (#1148, #448).
  3. *
  4. * The reporter's whole ask is a glance-and-know row: "is the enclosure shut?"
  5. * So the assertions are about what the pill actually says. "on" is not an
  6. * answer to that question — "Open" is, and only Home Assistant's device_class
  7. * tells us which word to use.
  8. */
  9. import { screen, waitFor } from '@testing-library/react';
  10. import { beforeEach, describe, expect, it, vi } from 'vitest';
  11. import { PrinterHASensorRow } from '../../components/PrinterHASensorRow';
  12. import { api } from '../../api/client';
  13. import { render } from '../utils';
  14. vi.mock('../../api/client', async () => {
  15. const actual = await vi.importActual<typeof import('../../api/client')>('../../api/client');
  16. return { ...actual, api: { ...actual.api, getHASensorReadings: vi.fn() } };
  17. });
  18. const getReadings = vi.mocked(api.getHASensorReadings);
  19. function reading(overrides = {}) {
  20. return {
  21. id: 1,
  22. name: 'Enclosure Door',
  23. entity_id: 'binary_sensor.enclosure_door',
  24. kind: 'binary' as const,
  25. device_class: 'door',
  26. unit: null,
  27. state: 'off',
  28. value: null,
  29. alerting: false,
  30. block_print: false,
  31. reachable: true,
  32. last_changed: null,
  33. ...overrides,
  34. };
  35. }
  36. describe('PrinterHASensorRow', () => {
  37. beforeEach(() => {
  38. getReadings.mockReset();
  39. });
  40. it('renders nothing when the printer has no sensors', async () => {
  41. getReadings.mockResolvedValue([]);
  42. render(<PrinterHASensorRow printerId={4} />);
  43. await waitFor(() => expect(getReadings).toHaveBeenCalledWith(4));
  44. // No label, no divider, no empty-state placeholder — the row must not
  45. // reserve space on every card that has no sensors configured.
  46. expect(screen.queryByText('Sensors')).not.toBeInTheDocument();
  47. });
  48. it('names a door state by its device class, not by on/off', async () => {
  49. getReadings.mockResolvedValue([reading({ state: 'off' })]);
  50. render(<PrinterHASensorRow printerId={4} />);
  51. expect(await screen.findByText('Closed')).toBeInTheDocument();
  52. expect(screen.getByText('Enclosure Door')).toBeInTheDocument();
  53. });
  54. it('says Open for the same sensor in the other state', async () => {
  55. getReadings.mockResolvedValue([reading({ state: 'on', alerting: true })]);
  56. render(<PrinterHASensorRow printerId={4} />);
  57. expect(await screen.findByText('Open')).toBeInTheDocument();
  58. });
  59. it('falls back to on/off for a class it has no wording for', async () => {
  60. getReadings.mockResolvedValue([reading({ device_class: null, state: 'on' })]);
  61. render(<PrinterHASensorRow printerId={4} />);
  62. expect(await screen.findByText('On')).toBeInTheDocument();
  63. });
  64. it('shows a numeric reading with its unit', async () => {
  65. getReadings.mockResolvedValue([
  66. reading({
  67. kind: 'numeric',
  68. device_class: 'temperature',
  69. entity_id: 'sensor.enclosure_temp',
  70. name: 'Enclosure Temp',
  71. unit: '°C',
  72. state: '41.2',
  73. value: 41.2,
  74. }),
  75. ]);
  76. render(<PrinterHASensorRow printerId={4} />);
  77. expect(await screen.findByText('41.2 °C')).toBeInTheDocument();
  78. });
  79. it('reports an unreachable sensor as unavailable rather than as a state', async () => {
  80. // The pill must not read "Closed" for a door contact that dropped off the
  81. // network — that is the one wrong answer with real consequences.
  82. getReadings.mockResolvedValue([reading({ state: null, reachable: false })]);
  83. render(<PrinterHASensorRow printerId={4} />);
  84. expect(await screen.findByText('Unavailable')).toBeInTheDocument();
  85. expect(screen.queryByText('Closed')).not.toBeInTheDocument();
  86. });
  87. it('marks an alerting sensor for the eye', async () => {
  88. getReadings.mockResolvedValue([reading({ state: 'on', alerting: true })]);
  89. render(<PrinterHASensorRow printerId={4} />);
  90. const pill = (await screen.findByText('Open')).closest('span[title]');
  91. expect(pill?.className).toContain('red');
  92. });
  93. it('does not colour a quiet sensor as an alert', async () => {
  94. getReadings.mockResolvedValue([reading({ state: 'on', alerting: false })]);
  95. render(<PrinterHASensorRow printerId={4} />);
  96. const pill = (await screen.findByText('Open')).closest('span[title]');
  97. expect(pill?.className).not.toContain('red');
  98. });
  99. it('says so in the tooltip when a sensor holds prints', async () => {
  100. getReadings.mockResolvedValue([reading({ block_print: true })]);
  101. render(<PrinterHASensorRow printerId={4} />);
  102. await screen.findByText('Closed');
  103. expect(
  104. screen.getByTitle('binary_sensor.enclosure_door — holds prints while alerting')
  105. ).toBeInTheDocument();
  106. });
  107. it('lists every sensor bound to the printer', async () => {
  108. getReadings.mockResolvedValue([
  109. reading(),
  110. reading({
  111. id: 2,
  112. kind: 'numeric',
  113. name: 'Enclosure Temp',
  114. entity_id: 'sensor.enclosure_temp',
  115. device_class: 'temperature',
  116. unit: '°C',
  117. state: '22',
  118. value: 22,
  119. }),
  120. ]);
  121. render(<PrinterHASensorRow printerId={4} />);
  122. expect(await screen.findByText('Enclosure Door')).toBeInTheDocument();
  123. expect(screen.getByText('Enclosure Temp')).toBeInTheDocument();
  124. });
  125. });