diff --git a/package-lock.json b/package-lock.json
index 0b6739f..763f98f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -52,6 +52,7 @@
"@openedx/frontend-build": "^14.3.3",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
+ "@testing-library/user-event": "^14.6.1",
"es-check": "^2.3.0",
"fetch-mock": "^12.2.0",
"identity-obj-proxy": "^3.0.0",
@@ -5223,6 +5224,20 @@
}
}
},
+ "node_modules/@testing-library/user-event": {
+ "version": "14.6.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
+ "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": ">=7.21.4"
+ }
+ },
"node_modules/@tokens-studio/sd-transforms": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@tokens-studio/sd-transforms/-/sd-transforms-1.3.0.tgz",
diff --git a/package.json b/package.json
index 684f0b7..c1c5a4c 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"@openedx/frontend-build": "^14.3.3",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
+ "@testing-library/user-event": "^14.6.1",
"es-check": "^2.3.0",
"fetch-mock": "^12.2.0",
"identity-obj-proxy": "^3.0.0",
diff --git a/src/components/GradebookFilters/AssignmentGradeFilter/__snapshots__/index.test.jsx.snap b/src/components/GradebookFilters/AssignmentGradeFilter/__snapshots__/index.test.jsx.snap
deleted file mode 100644
index 109d1d7..0000000
--- a/src/components/GradebookFilters/AssignmentGradeFilter/__snapshots__/index.test.jsx.snap
+++ /dev/null
@@ -1,67 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AssignmentFilter component render with selected assignment snapshot 1`] = `
-
-`;
-
-exports[`AssignmentFilter component render without selected assignment snapshot 1`] = `
-
-`;
diff --git a/src/components/GradebookFilters/AssignmentGradeFilter/index.test.jsx b/src/components/GradebookFilters/AssignmentGradeFilter/index.test.jsx
index 18c5919..a929cc7 100644
--- a/src/components/GradebookFilters/AssignmentGradeFilter/index.test.jsx
+++ b/src/components/GradebookFilters/AssignmentGradeFilter/index.test.jsx
@@ -1,17 +1,18 @@
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-import { useIntl } from '@edx/frontend-platform/i18n';
-import { Button } from '@openedx/paragon';
+/* eslint-disable import/no-extraneous-dependencies */
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
-import PercentGroup from '../PercentGroup';
import useAssignmentGradeFilterData from './hooks';
import AssignmentFilter from '.';
-jest.mock('../PercentGroup', () => 'PercentGroup');
jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() }));
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+jest.unmock('@edx/frontend-platform/i18n');
const hookData = {
- handleChange: jest.fn(),
+ handleSubmit: jest.fn(),
handleSetMax: jest.fn(),
handleSetMin: jest.fn(),
selectedAssignment: 'test-assignment',
@@ -22,37 +23,39 @@ useAssignmentGradeFilterData.mockReturnValue(hookData);
const updateQueryParams = jest.fn();
-let el;
describe('AssignmentFilter component', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- el = shallow();
- });
describe('behavior', () => {
it('initializes hooks', () => {
+ render();
expect(useAssignmentGradeFilterData).toHaveBeenCalledWith({ updateQueryParams });
- expect(useIntl).toHaveBeenCalledWith();
});
});
describe('render', () => {
describe('with selected assignment', () => {
- test('snapshot', () => {
- expect(el.snapshot).toMatchSnapshot();
+ beforeEach(() => {
+ jest.clearAllMocks();
+ render();
});
- it('renders a PercentGroup for both Max and Min filters', () => {
- let { props } = el.instance.findByType(PercentGroup)[0];
- expect(props.value).toEqual(hookData.assignmentGradeMin);
- expect(props.disabled).toEqual(false);
- expect(props.onChange).toEqual(hookData.handleSetMin);
- props = el.instance.findByType(PercentGroup)[1].props;
- expect(props.value).toEqual(hookData.assignmentGradeMax);
- expect(props.disabled).toEqual(false);
- expect(props.onChange).toEqual(hookData.handleSetMax);
+ it('renders a PercentGroup for both Max and Min filters', async () => {
+ const user = userEvent.setup();
+ const minGradeInput = screen.getByRole('spinbutton', { name: /Min Grade/i });
+ const maxGradeInput = screen.getByRole('spinbutton', { name: /Max Grade/i });
+ expect(minGradeInput).toBeInTheDocument();
+ expect(maxGradeInput).toBeInTheDocument();
+ expect(minGradeInput).toBeEnabled();
+ expect(maxGradeInput).toBeEnabled();
+ await user.type(minGradeInput, '25');
+ expect(hookData.handleSetMin).toHaveBeenCalled();
+ await user.type(maxGradeInput, '50');
+ expect(hookData.handleSetMax).toHaveBeenCalled();
});
- it('renders a submit button', () => {
- const { props } = el.instance.findByType(Button)[0];
- expect(props.disabled).toEqual(false);
- expect(props.onClick).toEqual(hookData.handleSubmit);
+ it('renders a submit button', async () => {
+ const user = userEvent.setup();
+ const submitButton = screen.getByRole('button', { name: /Apply/ });
+ expect(submitButton).toBeInTheDocument();
+ expect(submitButton).not.toHaveAttribute('disabled');
+ await user.click(submitButton);
+ expect(hookData.handleSubmit).toHaveBeenCalled();
});
});
describe('without selected assignment', () => {
@@ -61,16 +64,13 @@ describe('AssignmentFilter component', () => {
...hookData,
selectedAssignment: null,
});
- el = shallow();
- });
- test('snapshot', () => {
- expect(el.snapshot).toMatchSnapshot();
+ render();
});
it('disables controls', () => {
- let { props } = el.instance.findByType(PercentGroup)[0];
- expect(props.disabled).toEqual(true);
- props = el.instance.findByType(PercentGroup)[1].props;
- expect(props.disabled).toEqual(true);
+ const minGrade = screen.getByRole('spinbutton', { name: /Min Grade/ });
+ const maxGrade = screen.getByRole('spinbutton', { name: /Max Grade/ });
+ expect(minGrade).toHaveAttribute('disabled');
+ expect(maxGrade).toHaveAttribute('disabled');
});
});
});
diff --git a/src/components/GradebookFilters/PercentGroup.test.jsx b/src/components/GradebookFilters/PercentGroup.test.jsx
index 1addab8..fa356d6 100644
--- a/src/components/GradebookFilters/PercentGroup.test.jsx
+++ b/src/components/GradebookFilters/PercentGroup.test.jsx
@@ -1,8 +1,6 @@
-import React from 'react';
-import { render, screen } from 'testUtilsExtra';
+import { render, screen, initializeMocks } from 'testUtilsExtra';
import PercentGroup from './PercentGroup';
-import { initializeMocks } from '../../testUtilsExtra';
jest.unmock('@openedx/paragon');
jest.unmock('react');
diff --git a/src/components/GradesView/EditModal/ModalHeaders.test.jsx b/src/components/GradesView/EditModal/ModalHeaders.test.jsx
index 10b3421..c6ff0a6 100644
--- a/src/components/GradesView/EditModal/ModalHeaders.test.jsx
+++ b/src/components/GradesView/EditModal/ModalHeaders.test.jsx
@@ -1,16 +1,12 @@
import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-
-import { useIntl } from '@edx/frontend-platform/i18n';
import { selectors } from 'data/redux/hooks';
-import { formatMessage } from 'testUtils';
+import { render, screen, initializeMocks } from 'testUtilsExtra';
+import ModalHeaders from './ModalHeaders';
-import HistoryHeader from './HistoryHeader';
-import ModalHeaders, { HistoryKeys } from './ModalHeaders';
-import messages from './messages';
-
-jest.mock('./HistoryHeader', () => 'HistoryHeader');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+jest.unmock('@edx/frontend-platform/i18n');
jest.mock('data/redux/hooks', () => ({
selectors: {
@@ -29,57 +25,25 @@ const gradeData = {
gradeOriginalEarnedGraded: 'test-original-grade',
};
selectors.grades.useGradeData.mockReturnValue(gradeData);
+initializeMocks();
-let el;
describe('ModalHeaders', () => {
beforeEach(() => {
jest.clearAllMocks();
- el = shallow();
- });
- describe('behavior', () => {
- it('initializes intl', () => {
- expect(useIntl).toHaveBeenCalled();
- });
- it('initializes redux hooks', () => {
- expect(selectors.app.useModalData).toHaveBeenCalled();
- expect(selectors.grades.useGradeData).toHaveBeenCalled();
- });
+ render();
});
describe('render', () => {
- test('snapshot', () => {
- expect(el.snapshot).toMatchSnapshot();
- });
test('assignment header', () => {
- const headerProps = el.instance.findByType(HistoryHeader)[0].props;
- expect(headerProps).toMatchObject({
- id: HistoryKeys.assignment,
- label: formatMessage(messages.assignmentHeader),
- value: modalData.assignmentName,
- });
+ expect(screen.getByText(modalData.assignmentName)).toBeInTheDocument();
});
test('student header', () => {
- const headerProps = el.instance.findByType(HistoryHeader)[1].props;
- expect(headerProps).toMatchObject({
- id: HistoryKeys.student,
- label: formatMessage(messages.studentHeader),
- value: modalData.updateUserName,
- });
+ expect(screen.getByText(modalData.updateUserName)).toBeInTheDocument();
});
test('originalGrade header', () => {
- const headerProps = el.instance.findByType(HistoryHeader)[2].props;
- expect(headerProps).toMatchObject({
- id: HistoryKeys.originalGrade,
- label: formatMessage(messages.originalGradeHeader),
- value: gradeData.gradeOriginalEarnedGraded,
- });
+ expect(screen.getByText(gradeData.gradeOriginalEarnedGraded)).toBeInTheDocument();
});
test('currentGrade header', () => {
- const headerProps = el.instance.findByType(HistoryHeader)[3].props;
- expect(headerProps).toMatchObject({
- id: HistoryKeys.currentGrade,
- label: formatMessage(messages.currentGradeHeader),
- value: gradeData.gradeOverrideCurrentEarnedGradedOverride,
- });
+ expect(screen.getByText(gradeData.gradeOverrideCurrentEarnedGradedOverride)).toBeInTheDocument();
});
});
});
diff --git a/src/components/GradesView/EditModal/__snapshots__/ModalHeaders.test.jsx.snap b/src/components/GradesView/EditModal/__snapshots__/ModalHeaders.test.jsx.snap
deleted file mode 100644
index 22efe80..0000000
--- a/src/components/GradesView/EditModal/__snapshots__/ModalHeaders.test.jsx.snap
+++ /dev/null
@@ -1,26 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ModalHeaders render snapshot 1`] = `
-
-
-
-
-
-
-`;
diff --git a/src/components/GradesView/ImportGradesButton/__snapshots__/index.test.jsx.snap b/src/components/GradesView/ImportGradesButton/__snapshots__/index.test.jsx.snap
deleted file mode 100644
index ce266ad..0000000
--- a/src/components/GradesView/ImportGradesButton/__snapshots__/index.test.jsx.snap
+++ /dev/null
@@ -1,53 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ImportGradesButton component render Form 1`] = `
-
-
-
-
-`;
-
-exports[`ImportGradesButton component render snapshot 1`] = `
-
-
-
-
-
-
-
-`;
diff --git a/src/components/GradesView/ImportGradesButton/index.test.jsx b/src/components/GradesView/ImportGradesButton/index.test.jsx
index ad4fd88..4766141 100644
--- a/src/components/GradesView/ImportGradesButton/index.test.jsx
+++ b/src/components/GradesView/ImportGradesButton/index.test.jsx
@@ -1,46 +1,30 @@
import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-import { useIntl } from '@edx/frontend-platform/i18n';
-import { Form } from '@openedx/paragon';
+import {
+ render, screen, initializeMocks,
+} from 'testUtilsExtra';
-import NetworkButton from 'components/NetworkButton';
-import useImportGradesButtonData from './hooks';
import ImportGradesButton from '.';
-jest.mock('components/NetworkButton', () => 'NetworkButton');
-jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() }));
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+jest.unmock('@edx/frontend-platform/i18n');
+
+initializeMocks();
-let el;
-let props;
describe('ImportGradesButton component', () => {
- beforeAll(() => {
- props = {
- fileInputRef: { current: null },
- gradeExportUrl: 'test-grade-export-url',
- handleClickImportGrades: jest.fn().mockName('props.handleClickImportGrades'),
- handleFileInputChange: jest.fn().mockName('props.handleFileInputChange'),
- };
- useImportGradesButtonData.mockReturnValue(props);
- el = shallow();
- });
- describe('behavior', () => {
- it('initializes hooks', () => {
- expect(useImportGradesButtonData).toHaveBeenCalledWith();
- expect(useIntl).toHaveBeenCalledWith();
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ render();
});
+
describe('render', () => {
- test('snapshot', () => {
- expect(el.snapshot).toMatchSnapshot();
- });
- test('Form', () => {
- expect(el.instance.findByType(Form)[0].snapshot).toMatchSnapshot();
- expect(el.instance.findByType(Form)[0].props.action).toEqual(props.gradeExportUrl);
- expect(el.instance.findByType(Form.Control)[0].props.onChange).toEqual(props.handleFileInputChange);
+ test('Form', async () => {
+ const uploader = screen.getByTestId('file-control');
+ expect(uploader).toBeInTheDocument();
});
test('import button', () => {
- expect(el.instance.findByType(NetworkButton)[0].props.onClick).toEqual(props.handleClickImportGrades);
+ expect(screen.getByRole('button', { name: 'Import Grades' })).toBeInTheDocument();
});
});
});
diff --git a/src/components/GradesView/__snapshots__/index.test.jsx.snap b/src/components/GradesView/__snapshots__/index.test.jsx.snap
deleted file mode 100644
index 9960686..0000000
--- a/src/components/GradesView/__snapshots__/index.test.jsx.snap
+++ /dev/null
@@ -1,41 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`GradesView component render snapshot 1`] = `
-
-
-
-
- filter-step-heading
-
-
-
-
-
-
-
-
- gradebook-step-heading
-
-
-
-
-
-
-
-
-
- *
- test-masters-hint
-
-
-
-
-`;
diff --git a/src/components/GradesView/index.test.jsx b/src/components/GradesView/index.test.jsx
index c86a946..fafd550 100644
--- a/src/components/GradesView/index.test.jsx
+++ b/src/components/GradesView/index.test.jsx
@@ -1,24 +1,14 @@
import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-
-import FilterBadges from './FilterBadges';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import userEvent from '@testing-library/user-event';
+import { render, initializeMocks } from 'testUtilsExtra';
import useGradesViewData from './hooks';
import GradesView from '.';
-jest.mock('./BulkManagementControls', () => 'BulkManagementControls');
-jest.mock('./EditModal', () => 'EditModal');
-jest.mock('./FilterBadges', () => 'FilterBadges');
-jest.mock('./FilteredUsersLabel', () => 'FilteredUsersLabel');
-jest.mock('./FilterMenuToggle', () => 'FilterMenuToggle');
-jest.mock('./GradebookTable', () => 'GradebookTable');
-jest.mock('./ImportSuccessToast', () => 'ImportSuccessToast');
-jest.mock('./InterventionsReport', () => 'InterventionsReport');
-jest.mock('./PageButtons', () => 'PageButtons');
-jest.mock('./ScoreViewInput', () => 'ScoreViewInput');
-jest.mock('./SearchControls', () => 'SearchControls');
-jest.mock('./SpinnerIcon', () => 'SpinnerIcon');
-jest.mock('./StatusAlerts', () => 'StatusAlerts');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+jest.unmock('@edx/frontend-platform/i18n');
jest.mock('./hooks', () => jest.fn());
const hookProps = {
@@ -35,23 +25,21 @@ const updateQueryParams = jest.fn().mockName('props.updateQueryParams');
let el;
describe('GradesView component', () => {
+ beforeAll(() => {
+ initializeMocks();
+ });
beforeEach(() => {
jest.clearAllMocks();
- el = shallow();
- });
- describe('behavior', () => {
- it('initializes component hooks', () => {
- expect(useGradesViewData).toHaveBeenCalled();
- });
+ el = render();
});
describe('render', () => {
- test('snapshot', () => {
- expect(el.snapshot).toMatchSnapshot();
+ test('component to be rendered', () => {
+ expect(el.container).toBeInTheDocument();
});
- test('filterBadges load close behavior from hook', () => {
- expect(el.instance.findByType(FilterBadges)[0].props.handleClose).toEqual(
- hookProps.handleFilterBadgeClose,
- );
+ test('filterBadges load close behavior from hook', async () => {
+ const user = userEvent.setup();
+ await user.click(el.getAllByRole('button', { name: 'close' })[0]); // All the buttons use the same handler
+ expect(hookProps.handleFilterBadgeClose).toHaveBeenCalled();
});
});
});