/**
* @vitest-environment jsdom
*
* The form-submit flow under test does not fire its callback under happy-dom
* (likely a DOM-event-dispatch divergence). Run this file under jsdom.
*/
import React from 'react';
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';
import { showSnackbar, useLayoutType, type Workspace2DefinitionProps } from '@openmrs/esm-framework';
import { useSWRConfig } from 'swr';
import { saveQueue } from './queue.resource';
import QueueForm from './queue-form.workspace';
vi.mock('swr', () => ({
useSWRConfig: vi.fn(),
}));
const defaultProps = {
closeWorkspace: vi.fn(),
promptBeforeClosing: vi.fn(),
setTitle: vi.fn(),
launchChildWorkspace: vi.fn(),
};
const mockSaveQueue = vi.mocked(saveQueue);
const mockShowSnackbar = vi.mocked(showSnackbar);
const mockUseLayoutType = vi.mocked(useLayoutType);
const mockMutate = vi.fn();
vi.mock('./queue.resource', () => ({
useServiceConcepts: () => ({
queueConcepts: [
{ uuid: '6f017eb0-b035-4acd-b284-da45f5067502', display: 'Concept 1' },
{ uuid: '5f017eb0-b035-4acd-b284-da45f5067502', display: 'Concept 2' },
],
}),
saveQueue: vi.fn().mockResolvedValue({ status: 201 }),
}));
vi.mock('../../create-queue-entry/hooks/useQueueLocations', () => ({
useQueueLocations: () => ({
queueLocations: [
{ id: '34567eb0-b035-4acd-b284-da45f5067502', name: 'Location 1' },
{ id: '12wi7eb0-b035-4acd-b284-da45f5067502', name: 'Location 2' },
],
isLoading: false,
error: undefined,
}),
}));
vi.mock('../../hooks/useQueues', () => ({
useQueues: vi.fn(() => ({ queues: [] })),
}));
describe('QueueForm', () => {
beforeEach(() => {
mockUseLayoutType.mockReturnValue('tablet');
(useSWRConfig as Mock).mockReturnValue({ mutate: mockMutate });
});
it('renders validation errors when form is submitted with missing fields', async () => {
const user = userEvent.setup();
render();
const queueNameInput = screen.getByRole('textbox', { name: /queue name/i });
const serviceTypeSelect = screen.getByRole('combobox', { name: /select a service type/i });
const locationSelect = screen.getByRole('combobox', { name: /select a location/i });
const cancelButton = screen.getByRole('button', { name: /cancel/i });
const saveButton = screen.getByRole('button', { name: /save/i });
expect(cancelButton).toBeInTheDocument();
expect(saveButton).toBeInTheDocument();
expect(queueNameInput).toBeInTheDocument();
expect(queueNameInput).not.toBeInvalid();
expect(serviceTypeSelect).toBeInTheDocument();
expect(serviceTypeSelect).not.toBeInvalid();
await user.click(saveButton);
expect(queueNameInput).toBeInvalid();
await user.type(queueNameInput, 'Test Queue');
expect(queueNameInput).not.toBeInvalid();
expect(serviceTypeSelect).toBeInvalid();
await user.selectOptions(serviceTypeSelect, '6f017eb0-b035-4acd-b284-da45f5067502');
await user.selectOptions(locationSelect, '34567eb0-b035-4acd-b284-da45f5067502');
await user.click(saveButton);
expect(serviceTypeSelect).not.toBeInvalid();
expect(queueNameInput).not.toBeInvalid();
expect(locationSelect).not.toBeInvalid();
});
it('submits the form when all required fields are filled', async () => {
const user = userEvent.setup();
render();
const queueNameInput = screen.getByRole('textbox', { name: /queue name/i });
const serviceTypeSelect = screen.getByRole('combobox', { name: /select a service type/i });
const locationSelect = screen.getByRole('combobox', { name: /select a location/i });
const saveButton = screen.getByRole('button', { name: /save/i });
await user.type(queueNameInput, 'Test Queue');
await user.selectOptions(serviceTypeSelect, '6f017eb0-b035-4acd-b284-da45f5067502');
await user.selectOptions(locationSelect, '34567eb0-b035-4acd-b284-da45f5067502');
await user.click(saveButton);
expect(mockSaveQueue).toHaveBeenCalledTimes(1);
expect(mockSaveQueue).toHaveBeenCalledWith(
'Test Queue',
'6f017eb0-b035-4acd-b284-da45f5067502',
'',
'34567eb0-b035-4acd-b284-da45f5067502',
);
expect(mockShowSnackbar).toHaveBeenCalledTimes(1);
expect(mockShowSnackbar).toHaveBeenCalledWith({
kind: 'success',
title: expect.stringMatching(/queue created/i),
subtitle: 'Test Queue',
});
});
it('renders an error message when the queue creation fails', async () => {
const user = userEvent.setup();
mockSaveQueue.mockRejectedValueOnce(new Error('Internal server error'));
render();
const queueNameInput = screen.getByRole('textbox', { name: /queue name/i });
const serviceTypeSelect = screen.getByRole('combobox', { name: /select a service type/i });
const locationSelect = screen.getByRole('combobox', { name: /select a location/i });
const saveButton = screen.getByRole('button', { name: /save/i });
await user.type(queueNameInput, 'Test Queue');
await user.selectOptions(serviceTypeSelect, '6f017eb0-b035-4acd-b284-da45f5067502');
await user.selectOptions(locationSelect, '34567eb0-b035-4acd-b284-da45f5067502');
await user.click(saveButton);
expect(mockShowSnackbar).toHaveBeenCalledTimes(1);
expect(mockShowSnackbar).toHaveBeenCalledWith({
isLowContrast: false,
kind: 'error',
title: expect.stringMatching(/error creating queue/i),
subtitle: expect.stringMatching(/internal server error/i),
});
});
it('invalidates the correct cache keys with query parameter pattern on successful creation', async () => {
const user = userEvent.setup();
render();
const queueNameInput = screen.getByRole('textbox', { name: /queue name/i });
const serviceTypeSelect = screen.getByRole('combobox', { name: /select a service type/i });
const locationSelect = screen.getByRole('combobox', { name: /select a location/i });
const saveButton = screen.getByRole('button', { name: /save/i });
await user.type(queueNameInput, 'Test Queue');
await user.selectOptions(serviceTypeSelect, '6f017eb0-b035-4acd-b284-da45f5067502');
await user.selectOptions(locationSelect, '34567eb0-b035-4acd-b284-da45f5067502');
await user.click(saveButton);
expect(mockMutate).toHaveBeenCalledWith(expect.any(Function));
const mutateCallback = mockMutate.mock.calls[0][0] as (key: unknown) => boolean;
// Should match queue endpoints with query parameters
expect(mutateCallback('/ws/rest/v1/queue?status=active')).toBe(true);
expect(mutateCallback('/ws/rest/v1/queue?v=full')).toBe(true);
// Should NOT match other endpoints that start with 'queue'
expect(mutateCallback('/ws/rest/v1/queue-room')).toBe(false);
expect(mutateCallback('/ws/rest/v1/queue-entry')).toBe(false);
expect(mutateCallback('/ws/rest/v1/queueroom')).toBe(false);
});
});