From 25f686a875c0f6e70d49b06d9f95dc8db5b20eef Mon Sep 17 00:00:00 2001 From: Javier Ontiveros Date: Thu, 7 Aug 2025 16:12:39 -0600 Subject: [PATCH] feat: deprecate shallow in tests part 1/9 (#481) * feat: deprecated shallow in tests * chore: test utils from dev to dependencies * chore: removed unused imports * chore: restore packages to devDep * chore: renamed header to pass lint --- package-lock.json | 26 ++------ package.json | 3 +- .../BulkManagementAlerts.test.jsx | 63 ++++--------------- .../ResultsSummary.jsx | 4 +- .../ResultsSummary.test.jsx | 39 ++++-------- .../BulkManagementAlerts.test.jsx.snap | 45 ------------- .../ResultsSummary.test.jsx.snap | 23 ------- src/components/EdxHeader/EdxHeader.test.jsx | 23 +++++++ .../EdxHeader/__snapshots__/test.jsx.snap | 23 ------- src/components/EdxHeader/test.jsx | 21 ------- .../__snapshots__/index.test.jsx.snap | 44 ------------- .../AssignmentTypeFilter/index.test.jsx | 30 +++------ .../__snapshots__/index.test.jsx.snap | 63 ------------------- .../CourseGradeFilter/index.test.jsx | 50 ++++++--------- src/setupTest.js | 2 + 15 files changed, 89 insertions(+), 370 deletions(-) delete mode 100644 src/components/BulkManagementHistoryView/__snapshots__/BulkManagementAlerts.test.jsx.snap delete mode 100644 src/components/BulkManagementHistoryView/__snapshots__/ResultsSummary.test.jsx.snap create mode 100644 src/components/EdxHeader/EdxHeader.test.jsx delete mode 100644 src/components/EdxHeader/__snapshots__/test.jsx.snap delete mode 100644 src/components/EdxHeader/test.jsx delete mode 100644 src/components/GradebookFilters/AssignmentTypeFilter/__snapshots__/index.test.jsx.snap delete mode 100644 src/components/GradebookFilters/CourseGradeFilter/__snapshots__/index.test.jsx.snap diff --git a/package-lock.json b/package-lock.json index 5c1075f..efa7f9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,8 @@ "devDependencies": { "@edx/browserslist-config": "^1.1.1", "@openedx/frontend-build": "^14.3.3", - "@testing-library/react": "^16.2.0", + "@testing-library/jest-dom": "^6.6.4", + "@testing-library/react": "^16.3.0", "es-check": "^2.3.0", "fetch-mock": "^12.2.0", "identity-obj-proxy": "^3.0.0", @@ -5172,17 +5173,16 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", - "license": "MIT", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.4.tgz", + "integrity": "sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -5191,19 +5191,6 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -5214,7 +5201,6 @@ "version": "16.3.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, diff --git a/package.json b/package.json index 3113842..684f0b7 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,8 @@ "devDependencies": { "@edx/browserslist-config": "^1.1.1", "@openedx/frontend-build": "^14.3.3", - "@testing-library/react": "^16.2.0", + "@testing-library/jest-dom": "^6.6.4", + "@testing-library/react": "^16.3.0", "es-check": "^2.3.0", "fetch-mock": "^12.2.0", "identity-obj-proxy": "^3.0.0", diff --git a/src/components/BulkManagementHistoryView/BulkManagementAlerts.test.jsx b/src/components/BulkManagementHistoryView/BulkManagementAlerts.test.jsx index e382da0..a229bf8 100644 --- a/src/components/BulkManagementHistoryView/BulkManagementAlerts.test.jsx +++ b/src/components/BulkManagementHistoryView/BulkManagementAlerts.test.jsx @@ -1,20 +1,11 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; -import { Alert } from '@openedx/paragon'; -import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { render, screen } from '@testing-library/react'; import selectors from 'data/selectors'; -import messages from './messages'; import { BulkManagementAlerts, mapStateToProps } from './BulkManagementAlerts'; -jest.mock('@edx/frontend-platform/i18n', () => ({ - defineMessages: m => m, - FormattedMessage: () => 'FormattedMessage', -})); -jest.mock('@openedx/paragon', () => ({ - Alert: () => 'Alert', -})); +jest.unmock('@openedx/paragon'); jest.mock('data/selectors', () => ({ __esModule: true, default: { @@ -29,47 +20,19 @@ const errorMessage = 'Oh noooooo'; describe('BulkManagementAlerts', () => { describe('component', () => { - let el; - describe('no errer, no upload success', () => { - beforeEach(() => { - el = shallow(); + describe('states of the warnings', () => { + test('no alert shown', () => { + const el = render(); + expect(el.container.querySelectorAll('.alert').length).toEqual(0); }); - test('snapshot - bulkImportError closed, success closed', () => { - expect(el.snapshot).toMatchSnapshot(); + test('Just success alert shown', () => { + const el = render(); + expect(el.container.querySelectorAll('.alert-success').length).toEqual(1); }); - test('closed danger alert', () => { - expect(el.instance.children[0].type).toBe('Alert'); - expect(el.instance.findByType(Alert)[0].props.show).toEqual(false); - expect(el.instance.findByType(Alert)[0].props.variant).toEqual('danger'); - }); - test('closed success alert', () => { - expect(el.instance.children[1].type).toBe('Alert'); - expect(el.instance.findByType(Alert)[1].props.show).toEqual(false); - expect(el.instance.findByType(Alert)[1].props.variant).toEqual('success'); - }); - }); - describe('no errer, no upload success', () => { - beforeEach(() => { - el = shallow(); - }); - const assertions = [ - 'danger alert open with bulkImportError', - 'success alert open with messages.successDialog', - ]; - test(`snapshot - ${assertions.join(', ')}`, () => { - expect(el.snapshot).toMatchSnapshot(); - }); - test('open danger alert with bulkImportError content', () => { - expect(el.instance.children[0].type).toBe('Alert'); - expect(el.instance.findByType(Alert)[0].children[0].el).toEqual(errorMessage); - expect(el.instance.findByType(Alert)[0].props.show).toEqual(true); - }); - test('open success alert with messages.successDialog content', () => { - expect(el.instance.children[1].type).toBe('Alert'); - expect(el.shallowWrapper.props.children[1].props.children).toEqual( - , - ); - expect(el.instance.children[1].props.show).toEqual(true); + test('Just error alert shown', () => { + const el = render(); + expect(el.container.querySelectorAll('.alert-danger').length).toEqual(1); + expect(screen.getByText(errorMessage)).toBeInTheDocument(); }); }); }); diff --git a/src/components/BulkManagementHistoryView/ResultsSummary.jsx b/src/components/BulkManagementHistoryView/ResultsSummary.jsx index 256dc24..2a222e9 100644 --- a/src/components/BulkManagementHistoryView/ResultsSummary.jsx +++ b/src/components/BulkManagementHistoryView/ResultsSummary.jsx @@ -19,10 +19,8 @@ const ResultsSummary = ({ text, }) => ( diff --git a/src/components/BulkManagementHistoryView/ResultsSummary.test.jsx b/src/components/BulkManagementHistoryView/ResultsSummary.test.jsx index 2f8bd34..34188a9 100644 --- a/src/components/BulkManagementHistoryView/ResultsSummary.test.jsx +++ b/src/components/BulkManagementHistoryView/ResultsSummary.test.jsx @@ -1,21 +1,13 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - -import { Download } from '@openedx/paragon/icons'; import lms from 'data/services/lms'; +import { render, screen } from '@testing-library/react'; import ResultsSummary from './ResultsSummary'; -jest.mock('@openedx/paragon', () => ({ - Hyperlink: () => 'Hyperlink', - Icon: () => 'Icon', -})); -jest.mock('@openedx/paragon/icons', () => ({ - Download: 'DownloadIcon', -})); +jest.unmock('@openedx/paragon'); jest.mock('data/services/lms', () => ({ urls: { - bulkGradesUrlByRow: jest.fn((rowId) => ({ url: { rowId } })), + bulkGradesUrlByRow: jest.fn((rowId) => (`www.edx.org/${rowId}`)), }, })); @@ -25,28 +17,21 @@ describe('ResultsSummary component', () => { text: 'texty', }; let el; - const assertions = [ - 'safe hyperlink with bulkGradesUrl with course and row id', - 'download icon', - 'results text', - ]; + let link; beforeEach(() => { - el = shallow(); - }); - test(`snapshot - ${assertions.join(', ')}`, () => { - expect(el.snapshot).toMatchSnapshot(); + el = render(); + link = screen.getByRole('link', { name: props.text }); }); test('Hyperlink has target="_blank" and rel="noopener noreferrer"', () => { - expect(el.instance.props.target).toEqual('_blank'); - expect(el.instance.props.rel).toEqual('noopener noreferrer'); + expect(link).toHaveAttribute('target', '_blank'); + expect(link).toHaveAttribute('rel', 'noopener noreferrer'); }); test('Hyperlink has href to bulkGradesUrl', () => { - expect(el.instance.props.href).toEqual(lms.urls.bulkGradesUrlByRow(props.rowId)); + expect(link).toHaveAttribute('href', lms.urls.bulkGradesUrlByRow(props.rowId)); }); test('displays Download Icon and text', () => { - const icon = el.instance.children[0]; - expect(icon.type).toEqual('Icon'); - expect(icon.props.src).toEqual(Download); - expect(el.instance.children[1].el).toEqual(props.text); + expect(link).toHaveTextContent(props.text); + const span = el.container.querySelector('span[aria-hidden="true"]'); + expect(span).toBeInTheDocument(); }); }); diff --git a/src/components/BulkManagementHistoryView/__snapshots__/BulkManagementAlerts.test.jsx.snap b/src/components/BulkManagementHistoryView/__snapshots__/BulkManagementAlerts.test.jsx.snap deleted file mode 100644 index 6ebce59..0000000 --- a/src/components/BulkManagementHistoryView/__snapshots__/BulkManagementAlerts.test.jsx.snap +++ /dev/null @@ -1,45 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BulkManagementAlerts component no errer, no upload success snapshot - bulkImportError closed, success closed 1`] = ` - - - - - - -`; - -exports[`BulkManagementAlerts component no errer, no upload success snapshot - danger alert open with bulkImportError, success alert open with messages.successDialog 1`] = ` - - - Oh noooooo - - - - - -`; diff --git a/src/components/BulkManagementHistoryView/__snapshots__/ResultsSummary.test.jsx.snap b/src/components/BulkManagementHistoryView/__snapshots__/ResultsSummary.test.jsx.snap deleted file mode 100644 index 4709dc0..0000000 --- a/src/components/BulkManagementHistoryView/__snapshots__/ResultsSummary.test.jsx.snap +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ResultsSummary component snapshot - safe hyperlink with bulkGradesUrl with course and row id, download icon, results text 1`] = ` - - - texty - -`; diff --git a/src/components/EdxHeader/EdxHeader.test.jsx b/src/components/EdxHeader/EdxHeader.test.jsx new file mode 100644 index 0000000..fc075bb --- /dev/null +++ b/src/components/EdxHeader/EdxHeader.test.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { getConfig } from '@edx/frontend-platform'; + +import Header from '.'; + +jest.unmock('@openedx/paragon'); + +jest.mock('@edx/frontend-platform', () => ({ + getConfig: jest.fn(), +})); + +describe('Header', () => { + test('has edx link with logo url', () => { + const url = 'www.ourLogo.url'; + const baseUrl = 'www.lms.url'; + getConfig.mockReturnValue({ LOGO_URL: url, LMS_BASE_URL: baseUrl }); + + render(
); + expect(screen.getByRole('link')).toHaveAttribute('href', `${baseUrl}/dashboard`); + expect(screen.getByAltText('edX logo')).toHaveAttribute('src', url); + }); +}); diff --git a/src/components/EdxHeader/__snapshots__/test.jsx.snap b/src/components/EdxHeader/__snapshots__/test.jsx.snap deleted file mode 100644 index 77cb97f..0000000 --- a/src/components/EdxHeader/__snapshots__/test.jsx.snap +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header snapshot - has edx link with logo url 1`] = ` -
-
- - edX logo - -
-
-
-`; diff --git a/src/components/EdxHeader/test.jsx b/src/components/EdxHeader/test.jsx deleted file mode 100644 index ec96dde..0000000 --- a/src/components/EdxHeader/test.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - -import { getConfig } from '@edx/frontend-platform'; - -import Header from '.'; - -jest.mock('@openedx/paragon', () => ({ - Hyperlink: () => 'Hyperlink', -})); -jest.mock('@edx/frontend-platform', () => ({ - getConfig: jest.fn(), -})); - -describe('Header', () => { - test('snapshot - has edx link with logo url', () => { - const url = 'www.ourLogo.url'; - getConfig.mockReturnValue({ LOGO_URL: url }); - expect(shallow(
).snapshot).toMatchSnapshot(); - }); -}); diff --git a/src/components/GradebookFilters/AssignmentTypeFilter/__snapshots__/index.test.jsx.snap b/src/components/GradebookFilters/AssignmentTypeFilter/__snapshots__/index.test.jsx.snap deleted file mode 100644 index c9782af..0000000 --- a/src/components/GradebookFilters/AssignmentTypeFilter/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,44 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AssignmentFilterType component render snapshot 1`] = ` -
- - All - , - , - , - , - , - ] - } - value="test-type" - /> -
-`; diff --git a/src/components/GradebookFilters/AssignmentTypeFilter/index.test.jsx b/src/components/GradebookFilters/AssignmentTypeFilter/index.test.jsx index 7486bdc..e94d9bf 100644 --- a/src/components/GradebookFilters/AssignmentTypeFilter/index.test.jsx +++ b/src/components/GradebookFilters/AssignmentTypeFilter/index.test.jsx @@ -1,12 +1,14 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; -import { useIntl } from '@edx/frontend-platform/i18n'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { render, screen } from '@testing-library/react'; -import SelectGroup from '../SelectGroup'; import useAssignmentFilterTypeData from './hooks'; import AssignmentFilterType from '.'; -jest.mock('../SelectGroup', () => 'SelectGroup'); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() })); const handleChange = jest.fn(); @@ -21,27 +23,15 @@ useAssignmentFilterTypeData.mockReturnValue({ const updateQueryParams = jest.fn(); -let el; describe('AssignmentFilterType component', () => { beforeAll(() => { - el = shallow(); - }); - describe('behavior', () => { - it('initializes hooks', () => { - expect(useAssignmentFilterTypeData).toHaveBeenCalledWith({ updateQueryParams }); - expect(useIntl).toHaveBeenCalledWith(); - }); + render(); }); describe('render', () => { - test('snapshot', () => { - expect(el.snapshot).toMatchSnapshot(); - }); test('filter options', () => { - const { options } = el.instance.findByType(SelectGroup)[0].props; - expect(options.length).toEqual(5); - const optionProps = options[1].props; - expect(optionProps.value).toEqual(assignmentTypes[0]); - expect(optionProps.children).toEqual(testType); + const options = screen.getAllByRole('option'); + expect(options.length).toEqual(5); // 4 types + "All Types" + expect(options[1]).toHaveTextContent(testType); }); }); }); diff --git a/src/components/GradebookFilters/CourseGradeFilter/__snapshots__/index.test.jsx.snap b/src/components/GradebookFilters/CourseGradeFilter/__snapshots__/index.test.jsx.snap deleted file mode 100644 index b60179e..0000000 --- a/src/components/GradebookFilters/CourseGradeFilter/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CourseFilter component render if disabled snapshot 1`] = ` - -
- - -
-
- -
-
-`; - -exports[`CourseFilter component render with selected assignment snapshot 1`] = ` - -
- - -
-
- -
-
-`; diff --git a/src/components/GradebookFilters/CourseGradeFilter/index.test.jsx b/src/components/GradebookFilters/CourseGradeFilter/index.test.jsx index 9bfe3bf..600fe0e 100644 --- a/src/components/GradebookFilters/CourseGradeFilter/index.test.jsx +++ b/src/components/GradebookFilters/CourseGradeFilter/index.test.jsx @@ -1,13 +1,14 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; -import { useIntl } from '@edx/frontend-platform/i18n'; -import { Button } from '@openedx/paragon'; +import { render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; -import PercentGroup from '../PercentGroup'; import useCourseGradeFilterData from './hooks'; import CourseFilter from '.'; -jest.mock('../PercentGroup', () => 'PercentGroup'); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() })); const hookData = { @@ -27,48 +28,37 @@ useCourseGradeFilterData.mockReturnValue(hookData); const updateQueryParams = jest.fn(); -let el; describe('CourseFilter component', () => { beforeEach(() => { jest.clearAllMocks(); - el = shallow(); - }); - describe('behavior', () => { - it('initializes hooks', () => { - expect(useCourseGradeFilterData).toHaveBeenCalledWith({ updateQueryParams }); - expect(useIntl).toHaveBeenCalledWith(); - }); }); describe('render', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); describe('with selected assignment', () => { - test('snapshot', () => { - expect(el.snapshot).toMatchSnapshot(); + beforeEach(() => { + render(); }); + it('renders a PercentGroup for both Max and Min filters', () => { - let { props } = el.instance.findByType(PercentGroup)[0]; - expect(props.value).toEqual(hookData.min.value); - expect(props.onChange).toEqual(hookData.min.onChange); - props = el.instance.findByType(PercentGroup)[1].props; - expect(props.value).toEqual(hookData.max.value); - expect(props.onChange).toEqual(hookData.max.onChange); + expect(screen.getByRole('spinbutton', { name: 'Min Grade' })).toHaveValue(hookData.min.value); + expect(screen.getByRole('spinbutton', { name: 'Max Grade' })).toHaveValue(hookData.max.value); }); it('renders a submit button', () => { - const { props } = el.instance.findByType(Button)[0]; - expect(props.disabled).toEqual(false); - expect(props.onClick).toEqual(hookData.handleApplyClick); + expect(screen.getByRole('button', { name: 'Apply' })).toBeInTheDocument(); + // Expect it to be enabled + expect(screen.getByRole('button', { name: 'Apply' })).not.toBeDisabled(); }); }); describe('if disabled', () => { beforeEach(() => { + jest.clearAllMocks(); useCourseGradeFilterData.mockReturnValueOnce({ ...hookData, isDisabled: true }); - el = shallow(); - }); - test('snapshot', () => { - expect(el.snapshot).toMatchSnapshot(); + render(); }); it('disables submit', () => { - const { props } = el.instance.findByType(Button)[0]; - expect(props.disabled).toEqual(true); + expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled(); }); }); }); diff --git a/src/setupTest.js b/src/setupTest.js index 5c6a712..4aa6180 100755 --- a/src/setupTest.js +++ b/src/setupTest.js @@ -1,3 +1,5 @@ +import '@testing-library/jest-dom'; + // These configuration values are usually set in webpack's EnvironmentPlugin however // Jest does not use webpack so we need to set these so for testing process.env.LMS_BASE_URL = 'http://localhost:18000';