Skip to content

Commit

Permalink
(test) Add testing-related plugins to ESLint config (openmrs#1227)
Browse files Browse the repository at this point in the history
  • Loading branch information
denniskigen authored Jul 6, 2024
1 parent 357b084 commit f590e9f
Show file tree
Hide file tree
Showing 38 changed files with 325 additions and 169 deletions.
14 changes: 12 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
"env": {
"node": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest-dom/recommended",
"plugin:testing-library/react"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "react-hooks"],
"plugins": ["@typescript-eslint", "jest-dom", "react-hooks", "testing-library"],
"rules": {
"react-hooks/rules-of-hooks": "error",
// Disabling these rules for now just to keep the diff small. I'll enable them one by one as we go.
Expand All @@ -14,6 +19,11 @@
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/triple-slash-reference": "off",
// The following rules need `noImplicitAny` to be set to `true` in our tsconfig. They are too restrictive for now, but should be reconsidered in future
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/unbound-method": "off",
// Use `import type` instead of `import` for type imports https://typescript-eslint.io/blog/consistent-type-imports-and-exports-why-and-how
"@typescript-eslint/consistent-type-imports": [
"error",
Expand Down
1 change: 1 addition & 0 deletions __mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export * from './queue-rooms.mock';
export * from './search.mock';
export * from './session.mock';
export * from './wards.mock';
export * from './ward-patient';
export * from './visits.mock';
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
"dayjs": "^1.8.36",
"dotenv": "^16.0.3",
"eslint": "^8.55.0",
"eslint-plugin-jest-dom": "^5.4.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-testing-library": "^6.2.2",
"husky": "^8.0.3",
"i18next": "^21.10.0",
"i18next-parser": "^6.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('ActiveVisitsTable', () => {
await user.type(searchInput, 'John');

expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.queryByText('Some One')).toBeNull();
expect(screen.queryByText('Some One')).not.toBeInTheDocument();
});

it('displays empty state when there are no active visits', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import { render, screen, act } from '@testing-library/react';
import VisitDetailComponent from './visit-detail.component';
import { useVisit } from './visit.resource';
import { formatDate } from '@openmrs/esm-framework';
import { useVisit } from './visit.resource';
import VisitDetailComponent from './visit-detail.component';

jest.mock('./visit.resource');

Expand Down Expand Up @@ -117,6 +117,6 @@ describe('VisitDetailComponent', () => {

render(<VisitDetailComponent visitUuid={visitUuid} patientUuid={patientUuid} />);

expect(screen.queryByRole('button', { name: 'All Encounters' })).toBeNull();
expect(screen.queryByRole('button', { name: 'All Encounters' })).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { render } from '@testing-library/react';
import React from 'react';
import AppointmentActions from './appointments-actions.component';
import { render, screen } from '@testing-library/react';
import { useConfig } from '@openmrs/esm-framework';
import { useTodaysVisits } from '../../hooks/useTodaysVisits';
import { type Appointment, AppointmentKind, AppointmentStatus } from '../../types';
import { useConfig } from '@openmrs/esm-framework';
import AppointmentActions from './appointments-actions.component';

const appointment: Appointment = {
uuid: '7cd38a6d-377e-491b-8284-b04cf8b8c6d8',
Expand Down Expand Up @@ -46,8 +46,13 @@ const appointment: Appointment = {
voided: false,
teleconsultationLink: null,
extensions: [],
endDateTime: null,
dateAppointmentScheduled: null,
};

const mockUseConfig = useConfig as jest.Mock;
const mockUseTodaysVisits = useTodaysVisits as jest.Mock;

jest.mock('../../hooks/useTodaysVisits', () => {
const originalModule = jest.requireActual('../../hooks/useTodaysVisits');

Expand Down Expand Up @@ -79,41 +84,41 @@ describe('AppointmentActions', () => {

it('renders the check in button when appointment is today and the patient has not checked in and check in button enabled', () => {
appointment.status = AppointmentStatus.SCHEDULED;
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: true },
checkOutButton: { enabled: true },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [],
}));
const props = { ...defaultProps };
const { getByText } = render(<AppointmentActions {...props} />);
const button = getByText(/check in/i);
expect(button).toBeInTheDocument();
render(<AppointmentActions {...props} />);

expect(screen.getByText(/check in/i)).toBeInTheDocument();
});

it('does not renders the check in button when appointment is today and the patient has not checked in but the check-in button is disabled', () => {
appointment.status = AppointmentStatus.SCHEDULED;
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: false },
checkOutButton: { enabled: true },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [],
}));
const props = { ...defaultProps };
const { queryByText } = render(<AppointmentActions {...props} />);
const button = queryByText('Check In');
expect(button).not.toBeInTheDocument();
render(<AppointmentActions {...props} />);

expect(screen.queryByText(/check in/i)).not.toBeInTheDocument();
});

it('renders the checked out button when the patient has checked out', () => {
appointment.status = AppointmentStatus.COMPLETED;
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: true },
checkOutButton: { enabled: true },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [
{
patient: { uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e' },
Expand All @@ -123,18 +128,18 @@ describe('AppointmentActions', () => {
],
}));
const props = { ...defaultProps };
const { getByText } = render(<AppointmentActions {...props} />);
const button = getByText('Checked out');
expect(button).toBeInTheDocument();
render(<AppointmentActions {...props} />);

expect(screen.getByText('Checked out')).toBeInTheDocument();
});

it('renders the check out button when the patient has an active visit and today is the appointment date and the check out button enabled', () => {
appointment.status = AppointmentStatus.CHECKEDIN;
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: true },
checkOutButton: { enabled: true },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [
{
patient: { uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e' },
Expand All @@ -144,18 +149,18 @@ describe('AppointmentActions', () => {
],
}));
const props = { ...defaultProps, scheduleType: 'Scheduled' };
const { getByText } = render(<AppointmentActions {...props} />);
const button = getByText('Check out');
expect(button).toBeInTheDocument();
render(<AppointmentActions {...props} />);

expect(screen.getByText(/check out/i)).toBeInTheDocument();
});

it('does not render check out button when the patient has an active visit and today is the appointment date but the check out button is disabled', () => {
appointment.status = AppointmentStatus.CHECKEDIN;
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: true },
checkOutButton: { enabled: false },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [
{
patient: { uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e' },
Expand All @@ -165,18 +170,18 @@ describe('AppointmentActions', () => {
],
}));
const props = { ...defaultProps, scheduleType: 'Scheduled' };
const { queryByText } = render(<AppointmentActions {...props} />);
const button = queryByText('Check out');
expect(button).not.toBeInTheDocument();
render(<AppointmentActions {...props} />);

expect(screen.queryByText(/check out/i)).not.toBeInTheDocument();
});

// commenting these tests out as this functionality is not implemented yet so not sure how they would have ever passed?
/*it('renders the correct button when today is the appointment date and the schedule type is pending', () => {
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: true },
checkOutButton: { enabled: true },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [],
}));
const props = { ...defaultProps, scheduleType: 'Pending' };
Expand All @@ -186,11 +191,11 @@ describe('AppointmentActions', () => {
});
it('renders the correct button when today is the appointment date and the schedule type is not pending', () => {
useConfig.mockImplementation(() => ({
mockUseConfig.mockImplementation(() => ({
checkInButton: { enabled: true },
checkOutButton: { enabled: true },
}));
useTodaysVisits.mockImplementation(() => ({
mockUseTodaysVisits.mockImplementation(() => ({
visits: [],
}));
const props = { ...defaultProps, scheduleType: 'Confirmed' };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('EndAppointmentModal', () => {
render(<EndAppointmentModal appointmentUuid={'abc'} patientUuid={'123'} closeModal={closeModal} />);

const submitButton = screen.getByRole('button', { name: /check out/i });
expect(submitButton).not.toBeDisabled();
expect(submitButton).toBeEnabled();
await user.click(submitButton);

expect(changeAppointmentStatus).toHaveBeenCalledWith('Completed', 'abc');
Expand All @@ -63,7 +63,7 @@ describe('EndAppointmentModal', () => {
render(<EndAppointmentModal appointmentUuid={'abc'} patientUuid={'123'} closeModal={closeModal} />);

const submitButton = screen.getByRole('button', { name: /check out/i });
expect(submitButton).not.toBeDisabled();
expect(submitButton).toBeEnabled();
await user.click(submitButton);

expect(changeAppointmentStatus).toHaveBeenCalledWith('Completed', 'abc');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import AppointmentDetails from './appointment-details.component';
import { render, screen } from '@testing-library/react';
import { type Appointment } from '../../types';
import AppointmentDetails from './appointment-details.component';

const appointment: Appointment = {
uuid: '7cd38a6d-377e-491b-8284-b04cf8b8c6d8',
Expand Down Expand Up @@ -78,26 +78,26 @@ jest.mock('@openmrs/esm-framework', () => {

test('renders appointment details correctly', async () => {
const { getByText } = render(<AppointmentDetails appointment={appointment} />);
expect(getByText(/Patient name/i)).toBeInTheDocument();
expect(getByText(/John Wilson/i)).toBeInTheDocument();
expect(getByText(/Age/i)).toBeInTheDocument();
expect(getByText(/34/i)).toBeInTheDocument();
expect(getByText(/Gender/i)).toBeInTheDocument();
expect(getByText(/Male/i)).toBeInTheDocument();
expect(getByText(/Date of birth/i)).toBeInTheDocument();
expect(getByText(/Date of birth/i)).toBeInTheDocument();
expect(getByText(/22-Mar-2020/i)).toBeInTheDocument();
expect(getByText(/Contact 1/i)).toBeInTheDocument();
expect(getByText(/0899129989932/i)).toBeInTheDocument();
expect(getByText(/Appointment Notes/i)).toBeInTheDocument();
expect(getByText(/Some comments/i)).toBeInTheDocument();
expect(getByText(/Appointment History/i)).toBeInTheDocument();
expect(getByText(/Completed/i)).toBeInTheDocument();
expect(getByText('1', { exact: true })).toBeInTheDocument();
expect(getByText(/Missed/i)).toBeInTheDocument();
expect(getByText('2', { exact: true })).toBeInTheDocument();
expect(getByText(/Cancelled/i)).toBeInTheDocument();
expect(getByText('3', { exact: true })).toBeInTheDocument();
expect(getByText(/Upcoming/i)).toBeInTheDocument();
expect(getByText('4', { exact: true })).toBeInTheDocument();
expect(screen.getByText(/Patient name/i)).toBeInTheDocument();
expect(screen.getByText(/John Wilson/i)).toBeInTheDocument();
expect(screen.getByText(/Age/i)).toBeInTheDocument();
expect(screen.getByText(/34/i)).toBeInTheDocument();
expect(screen.getByText(/Gender/i)).toBeInTheDocument();
expect(screen.getByText(/Male/i)).toBeInTheDocument();
expect(screen.getByText(/Date of birth/i)).toBeInTheDocument();
expect(screen.getByText(/Date of birth/i)).toBeInTheDocument();
expect(screen.getByText(/22-Mar-2020/i)).toBeInTheDocument();
expect(screen.getByText(/Contact 1/i)).toBeInTheDocument();
expect(screen.getByText(/0899129989932/i)).toBeInTheDocument();
expect(screen.getByText(/Appointment Notes/i)).toBeInTheDocument();
expect(screen.getByText(/Some comments/i)).toBeInTheDocument();
expect(screen.getByText(/Appointment History/i)).toBeInTheDocument();
expect(screen.getByText(/Completed/i)).toBeInTheDocument();
expect(screen.getByText('1', { exact: true })).toBeInTheDocument();
expect(screen.getByText(/Missed/i)).toBeInTheDocument();
expect(screen.getByText('2', { exact: true })).toBeInTheDocument();
expect(screen.getByText(/Cancelled/i)).toBeInTheDocument();
expect(screen.getByText('3', { exact: true })).toBeInTheDocument();
expect(screen.getByText(/Upcoming/i)).toBeInTheDocument();
expect(screen.getByText('4', { exact: true })).toBeInTheDocument();
});
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe('AppointmentForm', () => {
const serviceSelect = screen.getByRole('combobox', { name: /Select a service/i });
const appointmentTypeSelect = screen.getByRole('combobox', { name: /Select the type of appointment/i });

expect(saveButton).not.toBeDisabled();
expect(saveButton).toBeEnabled();

await user.clear(dateInput);
await user.type(dateInput, '4/4/2021');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ describe('ListDetails', () => {
expect(screen.getByText(/there are no patients in this list/i)).toBeInTheDocument();
});

it('opens overlay with a form when the "Edit name or description" button is clicked', () => {
it('opens overlay with a form when the "Edit name or description" button is clicked', async () => {
render(<ListDetails />);

userEvent.click(screen.getByText('Actions'));
await userEvent.click(screen.getByText('Actions'));
const editBtn = screen.getByText('Edit name or description');
userEvent.click(editBtn);
await userEvent.click(editBtn);
});

it('deletes patient list and navigates back to the list page', async () => {
Expand All @@ -105,7 +105,8 @@ describe('ListDetails', () => {
expect(screen.getByText('Delete')).toBeInTheDocument();
expect(screen.getByText('Cancel')).toBeInTheDocument();

expect(screen.getByText('Delete').closest('button')).not.toHaveAttribute('disabled');
// eslint-disable-next-line testing-library/no-node-access
expect(screen.getByText('Delete').closest('button')).toBeEnabled();

await userEvent.click(screen.getByText('Cancel'));
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import userEvent from '@testing-library/user-event';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { navigate } from '@openmrs/esm-framework';
import Root from './add-patient-link';

Expand All @@ -10,10 +10,9 @@ describe('Add patient link component', () => {
it('renders an "Add Patient" button and triggers navigation on click', async () => {
const user = userEvent.setup();

const { getByRole } = render(<Root />);
const addButton = getByRole('button', { name: /add patient/i });
render(<Root />);

await user.click(addButton);
await user.click(screen.getByRole('button', { name: /add patient/i }));

expect(mockedNavigate).toHaveBeenCalledWith({ to: '${openmrsSpaBase}/patient-registration' });
});
Expand Down
6 changes: 3 additions & 3 deletions packages/esm-patient-registration-app/src/nav-link.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import Root from './nav-link';

describe('Nav link component', () => {
it('renders a link to the patient registration page', () => {
const { getByText } = render(<Root />);
const linkElement = getByText('Patient Registration');
render(<Root />);
const linkElement = screen.getByText('Patient Registration');

expect(linkElement).toBeInTheDocument();
expect(linkElement).toHaveAttribute('href', '/openmrs/spa/patient-registration');
Expand Down
Loading

0 comments on commit f590e9f

Please sign in to comment.