import React from 'react';
import { beforeAll, describe, expect, it, vi } from 'vitest';
import '@testing-library/jest-dom/vitest';
import { act, screen, renderHook, render, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { registerWorkspace } from '@openmrs/esm-extensions';
import { ComponentContext, isDesktop, useLayoutType } from '@openmrs/esm-react-utils';
import { type DefaultWorkspaceProps, WorkspaceContainer, launchWorkspace, useWorkspaces } from '..';
vi.mock('./workspace-renderer.component.tsx', () => {
return {
WorkspaceRenderer: ({ workspace }) => (
),
};
});
vi.mock('react-i18next', async () => ({
...(await vi.importActual('react-i18next')),
useTranslation: vi.fn(() => ({ t: (arg: string) => arg })),
}));
const mockIsDesktop = vi.mocked(isDesktop);
const mockUseLayoutType = vi.mocked(useLayoutType);
window.history.pushState({}, 'Workspace Container', '/workspace-container');
vi.mock('single-spa-react/parcel', () => vi.fn().mockImplementation(() => Parcel
));
interface ClinicalFormWorkspaceProps extends DefaultWorkspaceProps {
patientUuid: string;
}
describe('WorkspaceContainer in window mode', () => {
beforeAll(() => {
registerWorkspace({
name: 'clinical-form',
title: 'clinicalForm',
load: vi.fn(),
moduleName: '@openmrs/foo',
canHide: true,
canMaximize: true,
});
registerWorkspace({
name: 'order-basket',
title: 'orderBasket',
load: vi.fn(),
moduleName: '@openmrs/bar',
canHide: true,
canMaximize: true,
});
});
it('should translate the workspace title inside the workspace container', () => {
mockIsDesktop.mockReturnValue(true);
renderWorkspaceWindow();
act(() => launchWorkspace('clinical-form'));
let header = screen.getByRole('banner');
expect(within(header).getByText('clinicalForm')).toBeInTheDocument();
act(() => launchWorkspace('order-basket'));
header = screen.getByRole('banner');
expect(within(header).getByText('orderBasket')).toBeInTheDocument();
});
it('should override title via additional props and via setTitle', async () => {
mockIsDesktop.mockReturnValue(true);
renderWorkspaceWindow();
// In this line we are also verifying that the type argument to `launchWorkspace`
// behaves as expected, constraining the type of the `additionalProps` argument.
act(() =>
launchWorkspace('clinical-form', {
workspaceTitle: 'COVID Admission',
patientUuid: '123',
}),
);
const header = screen.getByRole('banner');
expect(within(header).getByText('COVID Admission')).toBeInTheDocument();
const utils = renderHook(() => useWorkspaces());
act(() => utils.result.current.workspaces[0].setTitle('COVID Discharge'));
expect(within(header).getByText('COVID Discharge')).toBeInTheDocument();
act(() =>
utils.result.current.workspaces[0].setTitle('Space Ghost', Space Ghost
),
);
expect(within(header).getByTestId('patient-name')).toBeInTheDocument();
});
it('re-launching workspace should update title, but only if setTitle was not used', async () => {
mockIsDesktop.mockReturnValue(true);
renderWorkspaceWindow();
// In this line we are also testing that `launchWorkspace` allows arbitrary additional props
// when no type argument is provided.
act(() => launchWorkspace('clinical-form', { workspaceTitle: 'COVID Admission', foo: 'bar' }));
const header = screen.getByRole('banner');
expect(within(header).getByText('COVID Admission')).toBeInTheDocument();
act(() => launchWorkspace('clinical-form', { workspaceTitle: 'COVID Discharge' }));
expect(within(header).getByText('COVID Discharge')).toBeInTheDocument();
const utils = renderHook(() => useWorkspaces());
act(() => utils.result.current.workspaces[0].setTitle('Fancy Special Title'));
expect(within(header).getByText('Fancy Special Title')).toBeInTheDocument();
act(() => launchWorkspace('clinical-form', { workspaceTitle: 'COVID Admission Again' }));
expect(within(header).getByText('Fancy Special Title')).toBeInTheDocument();
});
it('should reopen hidden workspace window when user relaunches the same workspace window', async () => {
const user = userEvent.setup();
const utils = renderHook(() => useWorkspaces());
mockIsDesktop.mockReturnValue(true);
expect(utils.result.current.workspaces.length).toBe(0);
renderWorkspaceWindow();
act(() => launchWorkspace('clinical-form', { workspaceTitle: 'POC Triage' }));
expect(utils.result.current.workspaces.length).toBe(1);
const header = screen.getByRole('banner');
expect(within(header).getByText('POC Triage')).toBeInTheDocument();
expectToBeVisible(screen.getByRole('complementary'));
let input = screen.getByRole('textbox');
await user.type(input, "what's good");
const hideButton = screen.getByRole('button', { name: 'Hide' });
await user.click(hideButton);
expect(screen.queryByRole('complementary')?.getAttribute('class')).toContain('hiddenRelative');
act(() => launchWorkspace('clinical-form', { workspaceTitle: 'POC Triage' }));
expectToBeVisible(await screen.findByRole('complementary'));
expect(screen.queryByRole('complementary')?.getAttribute('class')).not.toContain('hiddenRelative');
expect(screen.queryByRole('complementary')?.getAttribute('class')).not.toContain('hiddenFixed');
expect(utils.result.current.workspaces.length).toBe(1);
input = screen.getByRole('textbox');
expect(input).toHaveValue("what's good");
});
it('should toggle between maximized and normal screen size', async () => {
const user = userEvent.setup();
mockIsDesktop.mockReturnValue(true);
renderWorkspaceWindow();
act(() => launchWorkspace('clinical-form'));
const header = screen.getByRole('banner');
expect(within(header).getByText('clinicalForm')).toBeInTheDocument();
// eslint-disable-next-line testing-library/no-node-access
expect(screen.getByRole('complementary').firstElementChild?.getAttribute('class')).not.toContain('maximizedWindow');
const maximizeButton = await screen.findByRole('button', { name: 'Maximize' });
await user.click(maximizeButton);
// eslint-disable-next-line testing-library/no-node-access
expect(screen.getByRole('complementary').firstElementChild?.getAttribute('class')).toContain('maximizedWindow');
const minimizeButton = await screen.findByRole('button', { name: 'Minimize' });
await user.click(minimizeButton);
// eslint-disable-next-line testing-library/no-node-access
expect(screen.getByRole('complementary').firstElementChild?.getAttribute('class')).not.toContain('maximizedWindow');
});
// This would be a nice test if it worked, but it seems there are important differences between
// the React DOM and Jest DOM that cause this to fail when it should be working.
// This logic should be tested in the E2E tests.
// Try this again periodically to see if it starts working.
it.skip("shouldn't lose data when transitioning between workspaces", async () => {
renderWorkspaceWindow();
const user = userEvent.setup();
act(() => launchWorkspace('clinical-form'));
let container = screen.getByRole('complementary');
expect(within(container).getByText('clinical-form')).toBeInTheDocument();
let input = screen.getByRole('textbox');
await user.type(input, 'howdy');
await user.click(screen.getByRole('button', { name: 'Hide' }));
act(() => launchWorkspace('order-basket'));
container = screen.getByRole('complementary');
expect(within(container).getByText('order-basket')).toBeInTheDocument();
act(() => launchWorkspace('clinical-form'));
expect(within(container).getByText('clinical-form')).toBeInTheDocument();
input = screen.getByRole('textbox');
expect(input).toHaveValue('howdy');
});
});
function renderWorkspaceWindow() {
render(
,
);
}
describe('WorkspaceContainer in overlay mode', () => {
beforeAll(() => {
registerWorkspace({
name: 'patient-search',
title: 'Patient Search',
load: vi.fn(),
moduleName: '@openmrs/foo',
});
});
it('opens with overridable title and closes', async () => {
mockUseLayoutType.mockReturnValue('small-desktop');
const user = userEvent.setup();
renderWorkspaceOverlay();
act(() => launchWorkspace('patient-search', { workspaceTitle: 'Make an appointment' }));
expect(screen.getByRole('complementary')).toBeInTheDocument();
expectToBeVisible(screen.getByRole('complementary'));
expect(screen.getByText('Make an appointment')).toBeInTheDocument();
const closeButton = screen.getByRole('button', { name: 'Close' });
await user.click(closeButton);
// toHaveAttribute() cannot do either partial or regex matches
// eslint-disable-next-line jest-dom/prefer-to-have-attribute
expect(screen.getByRole('complementary').getAttribute('class')).toContain('hiddenRelative');
});
});
function renderWorkspaceOverlay() {
render(
,
);
}
function expectToBeVisible(element: HTMLElement) {
expect(element).toBeVisible();
expect(element.getAttribute('class')).not.toContain('hiddenRelative');
expect(element.getAttribute('class')).not.toContain('hiddenFixed');
expect(element.getAttribute('class')).not.toContain('hidden');
}