HMSErrorModal.test.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * Tests for the HMSErrorModal component.
  3. */
  4. import { describe, it, expect, vi, afterEach } from 'vitest';
  5. import { screen, fireEvent, cleanup, waitFor } from '@testing-library/react';
  6. import userEvent from '@testing-library/user-event';
  7. import { render } from '../utils';
  8. import { HMSErrorModal } from '../../components/HMSErrorModal';
  9. import { http, HttpResponse } from 'msw';
  10. import { server } from '../mocks/server';
  11. import type { HMSError } from '../../api/client';
  12. // Error code 0300_400C = "The task was canceled." (known code in the database)
  13. const knownError: HMSError = {
  14. attr: 0x0300,
  15. code: '0x400C',
  16. severity: 2,
  17. };
  18. // Error code FFFF_FFFF = unknown (not in the database)
  19. const unknownError: HMSError = {
  20. attr: 0xFFFF,
  21. code: '0xFFFF',
  22. severity: 1,
  23. };
  24. describe('HMSErrorModal', () => {
  25. const defaultProps = {
  26. printerName: 'Test Printer',
  27. errors: [knownError],
  28. onClose: vi.fn(),
  29. printerId: 1,
  30. hasPermission: vi.fn().mockReturnValue(true) as unknown as (permission: 'printers:control') => boolean,
  31. };
  32. afterEach(() => {
  33. cleanup();
  34. vi.clearAllMocks();
  35. });
  36. describe('rendering', () => {
  37. it('renders the modal title with printer name', () => {
  38. render(<HMSErrorModal {...defaultProps} />);
  39. expect(screen.getByText('Errors - Test Printer')).toBeInTheDocument();
  40. });
  41. it('shows error description for known error codes', () => {
  42. render(<HMSErrorModal {...defaultProps} />);
  43. expect(screen.getByText('The task was canceled.')).toBeInTheDocument();
  44. });
  45. it('shows no errors message when all errors are unknown', () => {
  46. render(<HMSErrorModal {...defaultProps} errors={[unknownError]} />);
  47. expect(screen.getByText('No errors')).toBeInTheDocument();
  48. });
  49. it('shows no errors message when errors array is empty', () => {
  50. render(<HMSErrorModal {...defaultProps} errors={[]} />);
  51. expect(screen.getByText('No errors')).toBeInTheDocument();
  52. });
  53. });
  54. describe('clear errors button', () => {
  55. it('shows clear button when there are known errors', () => {
  56. render(<HMSErrorModal {...defaultProps} />);
  57. expect(screen.getByText('Clear Errors')).toBeInTheDocument();
  58. });
  59. it('hides clear button when there are no known errors', () => {
  60. render(<HMSErrorModal {...defaultProps} errors={[]} />);
  61. expect(screen.queryByText('Clear Errors')).not.toBeInTheDocument();
  62. });
  63. it('hides clear button when all errors are unknown codes', () => {
  64. render(<HMSErrorModal {...defaultProps} errors={[unknownError]} />);
  65. expect(screen.queryByText('Clear Errors')).not.toBeInTheDocument();
  66. });
  67. it('disables clear button when user lacks permission', () => {
  68. const noPermission = vi.fn().mockReturnValue(false) as unknown as (permission: 'printers:control') => boolean;
  69. render(<HMSErrorModal {...defaultProps} hasPermission={noPermission} />);
  70. expect(screen.getByText('Clear Errors').closest('button')).toBeDisabled();
  71. });
  72. it('calls API and closes modal on successful clear', async () => {
  73. const user = userEvent.setup();
  74. const onClose = vi.fn();
  75. server.use(
  76. http.post('/api/v1/printers/1/hms/clear', () => {
  77. return HttpResponse.json({ success: true, message: 'HMS errors cleared' });
  78. })
  79. );
  80. render(<HMSErrorModal {...defaultProps} onClose={onClose} />);
  81. await user.click(screen.getByText('Clear Errors'));
  82. await waitFor(() => {
  83. expect(onClose).toHaveBeenCalledTimes(1);
  84. });
  85. });
  86. it('shows error toast on failed clear', async () => {
  87. const user = userEvent.setup();
  88. const onClose = vi.fn();
  89. server.use(
  90. http.post('/api/v1/printers/1/hms/clear', () => {
  91. return HttpResponse.json({ detail: 'Failed' }, { status: 500 });
  92. })
  93. );
  94. render(<HMSErrorModal {...defaultProps} onClose={onClose} />);
  95. await user.click(screen.getByText('Clear Errors'));
  96. await waitFor(() => {
  97. expect(onClose).not.toHaveBeenCalled();
  98. });
  99. });
  100. });
  101. describe('interactions', () => {
  102. it('calls onClose when X button is clicked', async () => {
  103. const user = userEvent.setup();
  104. const onClose = vi.fn();
  105. render(<HMSErrorModal {...defaultProps} onClose={onClose} />);
  106. // The X button is the button with the X icon in the header
  107. const closeButtons = screen.getAllByRole('button');
  108. // First button is the X close button in the header
  109. await user.click(closeButtons[0]);
  110. expect(onClose).toHaveBeenCalledTimes(1);
  111. });
  112. it('calls onClose when Escape key is pressed', () => {
  113. const onClose = vi.fn();
  114. render(<HMSErrorModal {...defaultProps} onClose={onClose} />);
  115. fireEvent.keyDown(window, { key: 'Escape' });
  116. expect(onClose).toHaveBeenCalledTimes(1);
  117. });
  118. });
  119. });