Untitled

 avatar
unknown
plain_text
9 months ago
24 kB
13
Indexable
import React from 'react';
import userEvent from '@testing-library/user-event';
import ClosedWorkOrders from '../../components/ClosedWorkOrders';
import { render, screen, createTestStore, within } from '../test-utils';

// Mock Apollo useQuery
const mockUseQuery = jest.fn();
jest.mock('@apollo/client', () => ({
  ...jest.requireActual('@apollo/client'),
  useQuery: () => mockUseQuery(),
  gql: jest.requireActual('@apollo/client').gql,
}));

// Mock MUI X DataGrid
jest.mock('@mui/x-data-grid', () => ({
  ...jest.requireActual('@mui/x-data-grid'),
  DataGrid: ({ rows, columns, ..._props }: any) => (
    <div data-testid="data-grid" role="grid">
      <div data-testid="grid-rows-count">{rows?.length || 0} rows</div>
      <div data-testid="grid-columns-count">{columns?.length || 0} columns</div>
      {rows?.map((row: any, index: number) => (
        <div key={index} data-testid={`grid-row-${row.id || index}`}>
          {row.workOrderNo} - {row.assignedTo}
        </div>
      ))}
    </div>
  ),
  useGridApiRef: () => ({
    current: {
      setColumns: jest.fn(),
      getColumns: jest.fn(() => []),
      updateColumns: jest.fn(),
    },
  }),
}));

// Mock WorkOrderColumns if it exists
jest.mock('../../components/WorkOrderColumns', () => ({
  getWorkOrderColumns: jest.fn(() => [
    { field: 'workOrderNo', headerName: 'Work Order', width: 150 },
    { field: 'assignedTo', headerName: 'Assigned', width: 120 },
    { field: 'shift', headerName: 'Shift', width: 100 },
    { field: 'notes', headerName: 'Notes', width: 200 },
  ]),
  applyConfigurations: jest.fn().mockResolvedValue([
    { field: 'workOrderNo', headerName: 'Work Order', width: 150 },
    { field: 'assignedTo', headerName: 'Assigned', width: 120 },
    { field: 'shift', headerName: 'Shift', width: 100 },
    { field: 'notes', headerName: 'Notes', width: 200 },
  ]),
}));

// Mock the custom hooks
const mockUseDragAndDrop = jest.fn();
const mockUseDropZone = jest.fn();

jest.mock('../../hooks/useDragAndDrop', () => ({
  useDragAndDrop: () => mockUseDragAndDrop(),
}));

jest.mock('../../hooks/useDropZone', () => ({
  useDropZone: () => mockUseDropZone(),
}));

// Mock ConfirmDialog
jest.mock('../../components/ConfirmDialog', () => {
  return function MockConfirmDialog({ open, title, message, confirmText, cancelText, onConfirm, onCancel }: any) {
    if (!open) return null;
    return (
      <div data-testid="confirm-dialog">
        <h2>{title}</h2>
        <div>{message}</div>
        <button onClick={onConfirm}>{confirmText}</button>
        <button onClick={onCancel}>{cancelText}</button>
      </div>
    );
  };
});

// Mock DraggableTableRow
jest.mock('../../components/DraggableTableRow', () => {
  return function MockDraggableTableRow({ 
    order, 
    handleCommitChange, 
    editingId, 
    editValue,
    editingShiftId,
    shiftValue,
    editingNotesId,
    notesValue,
    setEditingId,
    setEditValue,
    setEditingShiftId,
    setShiftValue,
    setEditingNotesId,
    setNotesValue,
    handleSave,
    handleSaveShift,
    handleSaveNotes,
    handleDeleteRow
  }: any) {
    return (
      <tr data-testid={`work-order-row-${order.workOrderNo}`}>
        <td>
          <input
            type="checkbox"
            checked={order.committed}
            onChange={(e) => handleCommitChange(order, e.target.checked)}
            data-testid={`commit-checkbox-${order.workOrderNo}`}
          />
        </td>
        <td data-testid={`work-order-no-${order.workOrderNo}`}>{order.workOrderNo}</td>
        <td>{order.ncOpenCount}</td>
        <td>{order.defaultWoDescription}</td>
        <td>
          {editingId === order.workOrderNo ? (
            <input
              value={editValue}
              onChange={(e) => setEditValue(e.target.value)}
              onBlur={() => handleSave(order.workOrderNo, editValue)}
              data-testid={`assigned-input-${order.workOrderNo}`}
            />
          ) : (
            <span 
              onClick={() => {
                setEditingId(order.workOrderNo);
                setEditValue(order.assignedTo);
              }}
              data-testid={`assigned-text-${order.workOrderNo}`}
            >
              {order.assignedTo}
            </span>
          )}
        </td>
        <td>
          {editingShiftId === order.workOrderNo ? (
            <input
              value={shiftValue}
              onChange={(e) => setShiftValue(e.target.value)}
              onBlur={() => handleSaveShift(order.workOrderNo, shiftValue)}
              data-testid={`shift-input-${order.workOrderNo}`}
            />
          ) : (
            <span 
              onClick={() => {
                setEditingShiftId(order.workOrderNo);
                setShiftValue(order.shift);
              }}
              data-testid={`shift-text-${order.workOrderNo}`}
            >
              {order.shift}
            </span>
          )}
        </td>
        <td>
          {editingNotesId === order.workOrderNo ? (
            <input
              value={notesValue}
              onChange={(e) => setNotesValue(e.target.value)}
              onBlur={() => handleSaveNotes(order.workOrderNo, notesValue)}
              data-testid={`notes-input-${order.workOrderNo}`}
            />
          ) : (
            <span 
              onClick={() => {
                setEditingNotesId(order.workOrderNo);
                setNotesValue(order.notes);
              }}
              data-testid={`notes-text-${order.workOrderNo}`}
            >
              {order.notes}
            </span>
          )}
        </td>
        <td>
          <button 
            onClick={() => handleDeleteRow(order.workOrderNo)}
            data-testid={`delete-button-${order.workOrderNo}`}
          >
            Delete
          </button>
        </td>
      </tr>
    );
  };
});

describe('ClosedWorkOrders', () => {
  const mockWorkOrders = [
    {
      workOrderNo: 'WO001',
      tailNumber: 'TAIL001',
      committed: false,
      committedOn: null,
      assignedTo: 'John Doe',
      shift: 'Day',
      notes: 'Test notes',
      ncOpenCount: 2,
      defaultWoDescription: 'Test Description',
      workType: 'REP',
      category: 'electrical',
      planDate: '2024-01-15',
      budgetHours: 8,
      createdOn: '2024-01-15T08:00:00Z',
      estimatedCompletedOn: null,
    },
    {
      workOrderNo: 'WO002',
      tailNumber: 'TAIL001',
      committed: true,
      committedOn: '2024-01-15T10:00:00Z',
      assignedTo: 'Jane Smith',
      shift: 'Night',
      notes: 'Urgent repair',
      ncOpenCount: 1,
      defaultWoDescription: 'Critical Fix',
      workType: 'DIS',
      category: 'mechanical',
      planDate: '2024-01-15',
      budgetHours: 4,
      createdOn: '2024-01-15T09:00:00Z',
      estimatedCompletedOn: '2024-01-15T14:00:00Z',
    },
  ];

  const mockDropZoneHandlers = {
    onDragOver: jest.fn(),
    onDragEnter: jest.fn(),
    onDragLeave: jest.fn(),
    onDrop: jest.fn(),
  };

  const mockProps = {
    tailNumber: 'TEST-001',
    closedWorkOrderRow: jest.fn(),
    saveConfigurations: jest.fn(),
    applyConfigurations: jest.fn().mockResolvedValue([
      { field: 'workOrderNo', headerName: 'Work Order', width: 150 },
      { field: 'assignedTo', headerName: 'Assigned', width: 120 },
      { field: 'shift', headerName: 'Shift', width: 100 },
      { field: 'notes', headerName: 'Notes', width: 200 },
    ]),
  };

  const renderClosedWorkOrders = (props = {}, initialState: any = {}) => {
    const store = createTestStore({
      whiteBoard: {
        electrical: [],
        structures: [],
        mechanical: [],
        closedWorkOrders: mockWorkOrders,
        locked: false,
        planDate: '2024-01-15',
        ...initialState.whiteBoard,
      },
    });

    const ClosedWorkOrdersComponent = ClosedWorkOrders as any;
    return render(<ClosedWorkOrdersComponent {...mockProps} {...props} />, { store });
  };

  beforeEach(() => {
    jest.clearAllMocks();
    
    mockUseQuery.mockReturnValue({
      data: null,
      loading: false,
      error: null,
    });
    
    mockUseDropZone.mockReturnValue({
      isDraggingOver: false,
      dropZoneHandlers: mockDropZoneHandlers,
      dropZoneRef: { current: null },
    });

    mockUseDragAndDrop.mockReturnValue({
      dragHandlers: {
        onDragStart: jest.fn(),
        onDragEnd: jest.fn(),
      },
      isDragging: false,
    });
  });

  describe('Basic Rendering', () => {
    // Tests that the component renders with proper structure and title
    it('renders ClosedWorkOrders with correct title and structure', () => {
      renderClosedWorkOrders();
      
      expect(screen.getByText('Closed Work Orders')).toBeInTheDocument();
      expect(screen.getByRole('table')).toBeInTheDocument();
      
      // Check table headers
      expect(screen.getByText('Committed')).toBeInTheDocument();
      expect(screen.getByText('Work Order')).toBeInTheDocument();
      expect(screen.getByText('MC Open')).toBeInTheDocument();
      expect(screen.getByText('EDUC')).toBeInTheDocument();
      expect(screen.getByText('Assigned')).toBeInTheDocument();
      expect(screen.getByText('Shift')).toBeInTheDocument();
      expect(screen.getByText('Notes')).toBeInTheDocument();
      expect(screen.getByText('X')).toBeInTheDocument();
    });

    // Tests that work orders are displayed correctly
    it('displays work orders from Redux state', () => {
      renderClosedWorkOrders();
      
      expect(screen.getByTestId('work-order-row-WO001')).toBeInTheDocument();
      expect(screen.getByTestId('work-order-row-WO002')).toBeInTheDocument();
      expect(screen.getByTestId('work-order-no-WO001')).toHaveTextContent('WO001');
      expect(screen.getByTestId('work-order-no-WO002')).toHaveTextContent('WO002');
    });

    // Tests rendering with empty work orders list
    it('renders empty table when no work orders exist', () => {
      renderClosedWorkOrders({}, {
        whiteBoard: {
          closedWorkOrders: [],
          locked: false,
        },
      });
      
      expect(screen.getByText('Closed Work Orders')).toBeInTheDocument();
      expect(screen.getByRole('table')).toBeInTheDocument();
      expect(screen.queryByTestId('work-order-row-WO001')).not.toBeInTheDocument();
    });

    // Tests that clear table button is present
    it('renders clear table button with correct icon', () => {
      renderClosedWorkOrders();
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      expect(clearButton).toBeInTheDocument();
      expect(clearButton).not.toBeDisabled();
    });
  });

  describe('Redux State Management', () => {
    // Tests that committed checkbox updates Redux state
    it('updates work order committed status via Redux', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const checkbox = screen.getByTestId('commit-checkbox-WO001');
      expect(checkbox).not.toBeChecked();
      
      await user.click(checkbox);
      
      // The mock will handle the change, we just verify the interaction
      expect(checkbox).toBeInTheDocument();
    });

    // Tests that work order data is properly passed to child components
    it('passes correct work order data to DraggableTableRow components', () => {
      renderClosedWorkOrders();
      
      // Verify work order data is displayed
      expect(screen.getByTestId('assigned-text-WO001')).toHaveTextContent('John Doe');
      expect(screen.getByTestId('assigned-text-WO002')).toHaveTextContent('Jane Smith');
      expect(screen.getByTestId('shift-text-WO001')).toHaveTextContent('Day');
      expect(screen.getByTestId('shift-text-WO002')).toHaveTextContent('Night');
      expect(screen.getByTestId('notes-text-WO001')).toHaveTextContent('Test notes');
      expect(screen.getByTestId('notes-text-WO002')).toHaveTextContent('Urgent repair');
    });

    // Tests behavior when whiteboard is locked
    it('disables clear table button when whiteboard is locked', () => {
      renderClosedWorkOrders({}, {
        whiteBoard: {
          closedWorkOrders: mockWorkOrders,
          locked: true,
          planDate: '2024-01-10', // Past date to ensure locked state
        },
      });
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      expect(clearButton).toBeDisabled();
    });
  });

  describe('Inline Editing Functionality', () => {
    // Tests editing assigned field
    it('allows editing assigned field inline', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const assignedText = screen.getByTestId('assigned-text-WO001');
      await user.click(assignedText);
      
      const assignedInput = screen.getByTestId('assigned-input-WO001');
      expect(assignedInput).toBeInTheDocument();
      expect(assignedInput).toHaveValue('John Doe');
      
      await user.clear(assignedInput);
      await user.type(assignedInput, 'Updated Name');
      await user.tab(); // Trigger blur event
      
      // The mock handles the save, we verify the interaction occurred
      expect(assignedInput).toHaveValue('Updated Name');
    });

    // Tests editing shift field
    it('allows editing shift field inline', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const shiftText = screen.getByTestId('shift-text-WO001');
      await user.click(shiftText);
      
      const shiftInput = screen.getByTestId('shift-input-WO001');
      expect(shiftInput).toBeInTheDocument();
      expect(shiftInput).toHaveValue('Day');
      
      await user.clear(shiftInput);
      await user.type(shiftInput, 'Night');
      await user.tab(); // Trigger blur event
      
      expect(shiftInput).toHaveValue('Night');
    });

    // Tests editing notes field
    it('allows editing notes field inline', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const notesText = screen.getByTestId('notes-text-WO001');
      await user.click(notesText);
      
      const notesInput = screen.getByTestId('notes-input-WO001');
      expect(notesInput).toBeInTheDocument();
      expect(notesInput).toHaveValue('Test notes');
      
      await user.clear(notesInput);
      await user.type(notesInput, 'Updated notes');
      await user.tab(); // Trigger blur event
      
      expect(notesInput).toHaveValue('Updated notes');
    });

    // Tests that only one field can be edited at a time
    it('allows editing only one field at a time', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      // Start editing assigned field
      const assignedText = screen.getByTestId('assigned-text-WO001');
      await user.click(assignedText);
      
      expect(screen.getByTestId('assigned-input-WO001')).toBeInTheDocument();
      
      // Try to edit shift field - should not show input since assigned is being edited
      const shiftText = screen.getByTestId('shift-text-WO001');
      await user.click(shiftText);
      
      // Both inputs should not be present simultaneously in a real scenario
      // This test verifies the component structure
      expect(screen.getByTestId('assigned-input-WO001')).toBeInTheDocument();
    });
  });

  describe('Drag and Drop Functionality', () => {
    // Tests that drop zone is properly configured
    it('configures drop zone with correct handlers', () => {
      renderClosedWorkOrders();
      
      // Verify that useDropZone was called with correct parameters
      expect(mockUseDropZone).toHaveBeenCalledWith({
        onDrop: expect.any(Function),
        tableType: 'closedWorkOrders',
      });
    });

    // Tests drag over visual feedback
    it('shows visual feedback when dragging over drop zone', () => {
      mockUseDropZone.mockReturnValue({
        isDraggingOver: true,
        dropZoneHandlers: mockDropZoneHandlers,
        dropZoneRef: { current: null },
      });
      
      renderClosedWorkOrders();
      
      // The drop zone should have visual feedback styling
      const dropZone = screen.getByRole('table').closest('[data-droppable="true"]');
      expect(dropZone).toBeInTheDocument();
      expect(dropZone).toHaveAttribute('data-table-type', 'closedWorkOrders');
    });

    // Tests that drop zone handlers are attached
    it('attaches drop zone event handlers correctly', () => {
      renderClosedWorkOrders();
      
      const dropZone = screen.getByRole('table').closest('[data-droppable="true"]');
      expect(dropZone).toBeInTheDocument();
      
      // Verify the drop zone is configured for touch interactions
      expect(dropZone).toHaveStyle('touch-action: none');
    });
  });

  describe('Confirmation Dialogs', () => {
    // Tests clear table confirmation dialog
    it('shows confirmation dialog when clear table button is clicked', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      await user.click(clearButton);
      
      const dialog = screen.getByTestId('confirm-dialog');
      expect(dialog).toBeInTheDocument();
      expect(within(dialog).getByText('Clear Table')).toBeInTheDocument();
      expect(within(dialog).getByText('Are you sure you want to clear this table?')).toBeInTheDocument();
      expect(within(dialog).getByText('Clear')).toBeInTheDocument();
      expect(within(dialog).getByText('Cancel')).toBeInTheDocument();
    });

    // Tests delete row confirmation dialog
    it('shows confirmation dialog when delete button is clicked', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const deleteButton = screen.getByTestId('delete-button-WO001');
      await user.click(deleteButton);
      
      const dialog = screen.getByTestId('confirm-dialog');
      expect(dialog).toBeInTheDocument();
      expect(within(dialog).getByText('Clear the row')).toBeInTheDocument();
      expect(within(dialog).getByText('Are you sure you want to clear this row?')).toBeInTheDocument();
      expect(within(dialog).getByText('Work Order:')).toBeInTheDocument();
      expect(within(dialog).getByText('WO001')).toBeInTheDocument();
    });

    // Tests canceling clear table dialog
    it('closes clear table dialog when cancel is clicked', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      await user.click(clearButton);
      
      const dialog = screen.getByTestId('confirm-dialog');
      const cancelButton = within(dialog).getByText('Cancel');
      await user.click(cancelButton);
      
      expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument();
    });

    // Tests confirming clear table action
    it('handles clear table confirmation correctly', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      await user.click(clearButton);
      
      const dialog = screen.getByTestId('confirm-dialog');
      const confirmButton = within(dialog).getByText('Clear');
      await user.click(confirmButton);
      
      // Dialog should close after confirmation
      expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument();
    });
  });

  describe('Component Layout and Styling', () => {
    // Tests that component has correct width and layout
    it('renders with correct layout and width', () => {
      renderClosedWorkOrders();
      
      // The component should be wrapped in PaperFrame with 33% width
      const container = screen.getByText('Closed Work Orders').closest('[class*="MuiPaper-root"]');
      expect(container).toBeInTheDocument();
    });

    // Tests table container scrolling behavior
    it('configures table container for proper scrolling', () => {
      renderClosedWorkOrders();
      
      const tableContainer = screen.getByRole('table').closest('[class*="MuiTableContainer-root"]');
      expect(tableContainer).toBeInTheDocument();
      expect(tableContainer).toHaveStyle('touch-action: none');
    });

    // Tests header styling and layout
    it('renders header with correct styling and controls', () => {
      renderClosedWorkOrders();
      
      const header = screen.getByText('Closed Work Orders').parentElement;
      expect(header).toBeInTheDocument();
      
      // Should contain both title and clear button
      expect(within(header!).getByText('Closed Work Orders')).toBeInTheDocument();
      expect(within(header!).getByRole('button', { name: /clear table/i })).toBeInTheDocument();
    });
  });

  describe('Error Handling and Edge Cases', () => {
    // Tests behavior with malformed work order data
    it('handles malformed work order data gracefully', () => {
      const malformedWorkOrders = [
        {
          workOrderNo: 'WO003',
          // Missing some required fields
          committed: false,
          assignedTo: '',
          shift: '',
          notes: '',
        },
      ];
      
      renderClosedWorkOrders({}, {
        whiteBoard: {
          closedWorkOrders: malformedWorkOrders,
          locked: false,
        },
      });
      
      expect(screen.getByText('Closed Work Orders')).toBeInTheDocument();
      expect(screen.getByTestId('work-order-row-WO003')).toBeInTheDocument();
    });

    // Tests behavior when Redux state is undefined
    it('handles undefined Redux state gracefully', () => {
      renderClosedWorkOrders({}, {
        whiteBoard: {
          closedWorkOrders: undefined,
          locked: false,
        },
      });
      
      expect(screen.getByText('Closed Work Orders')).toBeInTheDocument();
      expect(screen.getByRole('table')).toBeInTheDocument();
    });

    // Tests behavior with very long work order numbers
    it('handles long work order numbers correctly', () => {
      const longWorkOrderNumber = 'WO' + '1'.repeat(50);
      const workOrderWithLongNumber = [{
        ...mockWorkOrders[0],
        workOrderNo: longWorkOrderNumber,
      }];
      
      renderClosedWorkOrders({}, {
        whiteBoard: {
          closedWorkOrders: workOrderWithLongNumber,
          locked: false,
        },
      });
      
      expect(screen.getByTestId(`work-order-row-${longWorkOrderNumber}`)).toBeInTheDocument();
      expect(screen.getByTestId(`work-order-no-${longWorkOrderNumber}`)).toHaveTextContent(longWorkOrderNumber);
    });
  });

  describe('Accessibility', () => {
    // Tests that table has proper accessibility structure
    it('provides proper table accessibility structure', () => {
      renderClosedWorkOrders();
      
      const table = screen.getByRole('table');
      expect(table).toBeInTheDocument();
      
      // Should have proper table headers
      const columnHeaders = screen.getAllByRole('columnheader');
      expect(columnHeaders).toHaveLength(8); // 8 columns as defined in the component
    });

    // Tests that interactive elements have proper accessibility
    it('provides proper accessibility for interactive elements', () => {
      renderClosedWorkOrders();
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      expect(clearButton).toBeInTheDocument();
      expect(clearButton).toHaveAttribute('title', 'Clear Table');
      
      // Checkboxes should be accessible
      const checkboxes = screen.getAllByRole('checkbox');
      expect(checkboxes.length).toBeGreaterThan(0);
    });

    // Tests keyboard navigation support
    it('supports keyboard navigation for interactive elements', async () => {
      const user = userEvent.setup();
      renderClosedWorkOrders();
      
      const clearButton = screen.getByRole('button', { name: /clear table/i });
      
      // Should be focusable
      await user.tab();
      expect(clearButton).toHaveFocus();
      
      // Should be activatable with Enter
      await user.keyboard('{Enter}');
      expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument();
    });
  });
});
Editor is loading...
Leave a Comment