diff --git a/package-lock.json b/package-lock.json index 7c102e3..ee69661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,7 +58,6 @@ }, "devDependencies": { "@edx/browserslist-config": "^1.3.0", - "@edx/react-unit-test-utils": "^4.0.0", "@edx/reactifex": "^2.1.1", "@openedx/frontend-build": "^14.3.3", "@testing-library/jest-dom": "^6.6.3", @@ -2679,44 +2678,6 @@ "atlas": "atlas" } }, - "node_modules/@edx/react-unit-test-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@edx/react-unit-test-utils/-/react-unit-test-utils-4.0.0.tgz", - "integrity": "sha512-QlVYhYD9L2bzx1eAtf8BbCJr00ek9rrHrG+/pW2bVSt+t0uvKHQpX1CNdMrDePv18DsMeC7IOB00t8ZIn4mi7w==", - "dev": true, - "license": "AGPL-3.0", - "dependencies": { - "@edx/browserslist-config": "^1.1.1", - "@reduxjs/toolkit": "^1.5.1", - "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", - "classnames": "^2.2.6", - "core-js": "3.6.5", - "lodash": "^4.17.21", - "react-dev-utils": "^12.0.1", - "react-test-renderer": "^18.3.1" - }, - "peerDependencies": { - "@edx/frontend-platform": "^8.3.1", - "@openedx/frontend-build": "^14.3.0", - "@openedx/paragon": "^22.0.0 || ^23.0.0", - "react": "^18.0.0" - } - }, - "node_modules/@edx/react-unit-test-utils/node_modules/core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/@edx/reactifex": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@edx/reactifex/-/reactifex-2.2.0.tgz", @@ -5351,6 +5312,7 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -5506,7 +5468,8 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -9475,7 +9438,8 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dom-converter": { "version": "0.2.0", @@ -15292,6 +15256,7 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -17761,6 +17726,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -17775,6 +17741,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 3666ebf..f8ec83d 100755 --- a/package.json +++ b/package.json @@ -76,7 +76,6 @@ }, "devDependencies": { "@edx/browserslist-config": "^1.3.0", - "@edx/react-unit-test-utils": "^4.0.0", "@edx/reactifex": "^2.1.1", "@openedx/frontend-build": "^14.3.3", "@testing-library/jest-dom": "^6.6.3", diff --git a/src/App.test.jsx b/src/App.test.jsx index 364a377..aa86ae4 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1,12 +1,8 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { selectors } from 'data/redux'; +import { renderWithIntl } from './testUtils'; import { App, mapStateToProps } from './App'; -jest.unmock('react'); -jest.unmock('@openedx/paragon'); -jest.unmock('@edx/frontend-platform/i18n'); - // we want to scope these tests to the App component, so we mock some child components to reduce complexity jest.mock('@edx/frontend-platform/auth', () => ({ @@ -39,12 +35,6 @@ jest.mock('data/redux', () => ({ }, })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('App component', () => { const defaultProps = { courseMetadata: { diff --git a/src/__snapshots__/index.test.jsx.snap b/src/__snapshots__/index.test.jsx.snap deleted file mode 100644 index a5904fa..0000000 --- a/src/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`app registry subscribe: APP_INIT_ERROR. snapshot: displays an ErrorPage to root element 1`] = ` - - - -`; - -exports[`app registry subscribe: APP_READY. links App to root element 1`] = ` - - - - - -`; diff --git a/src/components/ConfirmModal.test.jsx b/src/components/ConfirmModal.test.jsx index b85118a..03e6cd3 100644 --- a/src/components/ConfirmModal.test.jsx +++ b/src/components/ConfirmModal.test.jsx @@ -2,9 +2,6 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ConfirmModal } from './ConfirmModal'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('ConfirmModal', () => { const props = { isOpen: false, @@ -21,13 +18,13 @@ describe('ConfirmModal', () => { }); it('should not render content when modal is closed', () => { - const { queryByText } = render(); - expect(queryByText(props.content)).toBeNull(); + render(); + expect(screen.queryByText(props.content)).toBeNull(); }); it('should display content when modal is open', () => { - const { getByText } = render(); - expect(getByText(props.content)).toBeInTheDocument(); + render(); + expect(screen.getByText(props.content)).toBeInTheDocument(); }); it('should call onCancel when cancel button is clicked', async () => { diff --git a/src/components/DemoAlert/index.test.jsx b/src/components/DemoAlert/index.test.jsx index 604d7e5..25a08f4 100644 --- a/src/components/DemoAlert/index.test.jsx +++ b/src/components/DemoAlert/index.test.jsx @@ -1,12 +1,10 @@ -import { render, fireEvent } from '@testing-library/react'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { renderWithIntl } from '../../testUtils'; -import { formatMessage } from 'testUtils'; import messages from './messages'; import { DemoAlert } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('DemoAlert component', () => { const props = { isOpen: true, @@ -14,20 +12,21 @@ describe('DemoAlert component', () => { }; it('does not render when isOpen is false', () => { - const { queryByText } = render(); - expect(queryByText(formatMessage(messages.title))).toBeNull(); + renderWithIntl(); + expect(screen.queryByText(messages.title.defaultMessage)).toBeNull(); }); it('renders with correct title and message when isOpen is true', () => { - const { getByText } = render(); - expect(getByText(formatMessage(messages.title))).toBeInTheDocument(); - expect(getByText(formatMessage(messages.warningMessage))).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText(messages.title.defaultMessage)).toBeInTheDocument(); + expect(screen.getByText(messages.warningMessage.defaultMessage)).toBeInTheDocument(); }); - it('calls onClose when confirmation button is clicked', () => { - const { getByText } = render(); - const confirmButton = getByText(formatMessage(messages.confirm)); - fireEvent.click(confirmButton); + it('calls onClose when confirmation button is clicked', async () => { + renderWithIntl(); + const user = userEvent.setup(); + const confirmButton = screen.getByText(messages.confirm.defaultMessage); + await user.click(confirmButton); expect(props.onClose).toHaveBeenCalled(); }); }); diff --git a/src/components/FilePopoverContent/index.test.jsx b/src/components/FilePopoverContent/index.test.jsx index 1ca1eea..85c238a 100644 --- a/src/components/FilePopoverContent/index.test.jsx +++ b/src/components/FilePopoverContent/index.test.jsx @@ -1,11 +1,10 @@ -import { render } from '@testing-library/react'; - +import { screen } from '@testing-library/react'; import filesize from 'filesize'; +import { renderWithIntl } from '../../testUtils'; + import FilePopoverContent from '.'; jest.mock('filesize', () => (size) => `filesize(${size})`); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); describe('FilePopoverContent', () => { describe('component', () => { @@ -18,23 +17,23 @@ describe('FilePopoverContent', () => { describe('behavior', () => { it('renders file name correctly', () => { - const { getByText } = render(); - expect(getByText(props.name)).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText(props.name)).toBeInTheDocument(); }); it('renders file description correctly', () => { - const { getByText } = render(); - expect(getByText(props.description)).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText(props.description)).toBeInTheDocument(); }); it('renders file size correctly', () => { - const { getByText } = render(); - expect(getByText(filesize(props.size))).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText(filesize(props.size))).toBeInTheDocument(); }); it('renders "Unknown" when size is null', () => { - const { getByText } = render(); - expect(getByText('Unknown')).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText('Unknown')).toBeInTheDocument(); }); }); }); diff --git a/src/components/FilePreview/Banners/ErrorBanner.test.jsx b/src/components/FilePreview/Banners/ErrorBanner.test.jsx index 990c938..b975445 100644 --- a/src/components/FilePreview/Banners/ErrorBanner.test.jsx +++ b/src/components/FilePreview/Banners/ErrorBanner.test.jsx @@ -1,10 +1,8 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../../testUtils'; import ErrorBanner from './ErrorBanner'; import messages from '../messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Error Banner component', () => { const children =

Abitary Child

; @@ -27,25 +25,25 @@ describe('Error Banner component', () => { describe('behavior', () => { it('renders children content', () => { - render(); + renderWithIntl(); const childText = screen.getByText('Abitary Child'); expect(childText).toBeInTheDocument(); }); it('renders the correct number of action buttons', () => { - render(); - const buttons = screen.getAllByText('FormattedMessage'); - expect(buttons).toHaveLength(3); + renderWithIntl(); + const buttons = screen.getAllByText(messages.retryButton.defaultMessage); + expect(buttons).toHaveLength(2); }); it('renders error heading with correct message', () => { - render(); - const heading = screen.getAllByText('FormattedMessage')[0]; + renderWithIntl(); + const heading = screen.getAllByText(messages.unknownError.defaultMessage)[0]; expect(heading).toBeInTheDocument(); }); it('renders with danger variant', () => { - render(); + renderWithIntl(); const alert = screen.getByRole('alert'); expect(alert).toHaveClass('alert-danger'); }); diff --git a/src/components/FilePreview/Banners/LoadingBanner.test.jsx b/src/components/FilePreview/Banners/LoadingBanner.test.jsx index f0abfb3..94c1c6b 100644 --- a/src/components/FilePreview/Banners/LoadingBanner.test.jsx +++ b/src/components/FilePreview/Banners/LoadingBanner.test.jsx @@ -1,9 +1,6 @@ import { render, screen } from '@testing-library/react'; import LoadingBanner from './LoadingBanner'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Loading Banner component', () => { describe('behavior', () => { it('renders an info alert', () => { diff --git a/src/components/FilePreview/BaseRenderers/ImageRenderer.test.jsx b/src/components/FilePreview/BaseRenderers/ImageRenderer.test.jsx index d558318..884182b 100644 --- a/src/components/FilePreview/BaseRenderers/ImageRenderer.test.jsx +++ b/src/components/FilePreview/BaseRenderers/ImageRenderer.test.jsx @@ -3,9 +3,6 @@ import { render, screen } from '@testing-library/react'; import ImageRenderer from './ImageRenderer'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Image Renderer Component', () => { const props = { url: 'some_url.jpg', diff --git a/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx b/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx index fc8cc80..54184f2 100644 --- a/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx +++ b/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx @@ -21,9 +21,6 @@ jest.mock('./pdfHooks', () => ({ rendererHooks: jest.fn(), })); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('PDF Renderer Component', () => { const props = { url: 'some_url.pdf', diff --git a/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx b/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx index 46ee11f..b65a648 100644 --- a/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx +++ b/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx @@ -8,9 +8,6 @@ jest.mock('./textHooks', () => { }; }); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - const textHooks = require('./textHooks'); describe('TXT Renderer Component', () => { diff --git a/src/components/FilePreview/BaseRenderers/pdfHooks.test.js b/src/components/FilePreview/BaseRenderers/pdfHooks.test.js index bbe7b02..c0ad5ab 100644 --- a/src/components/FilePreview/BaseRenderers/pdfHooks.test.js +++ b/src/components/FilePreview/BaseRenderers/pdfHooks.test.js @@ -12,6 +12,11 @@ jest.mock('react-pdf', () => ({ Page: () => 'Page', })); +jest.mock('react', () => ({ + ...jest.requireActual('react'), + useRef: jest.fn((val) => ({ current: val, useRef: true })), +})); + const state = new MockUseState(hooks); const hookKeys = keyStore(hooks); diff --git a/src/components/FilePreview/BaseRenderers/textHooks.test.js b/src/components/FilePreview/BaseRenderers/textHooks.test.js index f0a2c87..09a84bf 100644 --- a/src/components/FilePreview/BaseRenderers/textHooks.test.js +++ b/src/components/FilePreview/BaseRenderers/textHooks.test.js @@ -10,6 +10,11 @@ jest.mock('axios', () => ({ get: jest.fn(), })); +jest.mock('react', () => ({ + ...jest.requireActual('react'), + useEffect: jest.fn((cb, prereqs) => ({ useEffect: { cb, prereqs } })), +})); + const hookKeys = keyStore(hooks); const state = new MockUseState(hooks); diff --git a/src/components/FilePreview/FileCard.test.jsx b/src/components/FilePreview/FileCard.test.jsx index 8eb9bd9..59b1133 100644 --- a/src/components/FilePreview/FileCard.test.jsx +++ b/src/components/FilePreview/FileCard.test.jsx @@ -3,8 +3,6 @@ import FileCard from './FileCard'; jest.mock('components/FilePopoverContent', () => 'FilePopoverContent'); jest.mock('./FileInfo', () => 'FileInfo'); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); describe('File Preview Card component', () => { const props = { diff --git a/src/components/FilePreview/FileInfo.test.jsx b/src/components/FilePreview/FileInfo.test.jsx index c0f7d36..8736bef 100644 --- a/src/components/FilePreview/FileInfo.test.jsx +++ b/src/components/FilePreview/FileInfo.test.jsx @@ -1,10 +1,9 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { renderWithIntl } from '../../testUtils'; import FileInfo from './FileInfo'; - -jest.unmock('@openedx/paragon'); -jest.unmock('react'); +import messages from './messages'; describe('FileInfo component', () => { const children = (

some Children

); @@ -16,15 +15,14 @@ describe('FileInfo component', () => { describe('Component rendering', () => { it('renders the FileInfo button with correct text', () => { - render({children}); - - expect(screen.getByText('FormattedMessage')).toBeInTheDocument(); + renderWithIntl({children}); + expect(screen.getByText(messages.fileInfo.defaultMessage)).toBeInTheDocument(); }); it('calls onClick when button is clicked', async () => { - render({children}); + renderWithIntl({children}); const user = userEvent.setup(); - await user.click(screen.getByText('FormattedMessage')); + await user.click(screen.getByText(messages.fileInfo.defaultMessage)); expect(props.onClick).toHaveBeenCalledTimes(1); }); }); diff --git a/src/components/FilePreview/FileRenderer.test.jsx b/src/components/FilePreview/FileRenderer.test.jsx index 08e1e22..80a786e 100644 --- a/src/components/FilePreview/FileRenderer.test.jsx +++ b/src/components/FilePreview/FileRenderer.test.jsx @@ -1,12 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { keyStore } from 'utils'; import { ErrorStatuses } from 'data/constants/requests'; +import { renderWithIntl } from '../../testUtils'; import { FileRenderer } from './FileRenderer'; import * as hooks from './hooks'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - const hookKeys = keyStore(hooks); const props = { @@ -27,7 +25,7 @@ describe('FileRenderer', () => { rendererProps: { prop: 'hooks.rendererProps' }, }; jest.spyOn(hooks, hookKeys.renderHooks).mockReturnValueOnce(hookProps); - render(); + renderWithIntl(); expect(screen.getByText('filename.txt')).toBeInTheDocument(); expect(screen.getByTestId('mock-renderer')).toBeInTheDocument(); @@ -50,7 +48,7 @@ describe('FileRenderer', () => { }; jest.spyOn(hooks, hookKeys.renderHooks).mockReturnValueOnce(hookProps); - render(); + renderWithIntl(); expect(screen.getByText('filename.txt')).toBeInTheDocument(); expect(screen.getByText('Error Message')).toBeInTheDocument(); @@ -68,7 +66,7 @@ describe('FileRenderer', () => { }; jest.spyOn(hooks, hookKeys.renderHooks).mockReturnValueOnce(hookProps); - render(); + renderWithIntl(); expect(screen.getByText('filename.txt')).toBeInTheDocument(); expect(screen.getByTestId('mock-renderer')).toBeInTheDocument(); diff --git a/src/components/Head/index.test.jsx b/src/components/Head/index.test.jsx index d1d8d55..1cd2876 100644 --- a/src/components/Head/index.test.jsx +++ b/src/components/Head/index.test.jsx @@ -27,9 +27,6 @@ jest.mock('@edx/frontend-platform', () => ({ }), })); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Head', () => { it('should render page title with site name from config', () => { const { container } = render(); diff --git a/src/components/InfoPopover/index.test.jsx b/src/components/InfoPopover/index.test.jsx index 2fab2a3..f8d1f97 100644 --- a/src/components/InfoPopover/index.test.jsx +++ b/src/components/InfoPopover/index.test.jsx @@ -1,26 +1,24 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { renderWithIntl } from '../../testUtils'; import { InfoPopover } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Info Popover Component', () => { const child =
Children component
; const onClick = jest.fn().mockName('this.props.onClick'); describe('Component', () => { it('renders the help icon button', () => { - const { getByTestId } = render( + renderWithIntl( {child} , ); - expect(getByTestId('esg-help-icon')).toBeInTheDocument(); + expect(screen.getByTestId('esg-help-icon')).toBeInTheDocument(); }); it('calls onClick when the help icon is clicked', async () => { - render( + renderWithIntl( {child} , diff --git a/src/components/StatusBadge.test.jsx b/src/components/StatusBadge.test.jsx index 5aa1990..6651a26 100644 --- a/src/components/StatusBadge.test.jsx +++ b/src/components/StatusBadge.test.jsx @@ -1,36 +1,35 @@ -import { render } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { gradingStatuses } from 'data/services/lms/constants'; +import messages from '../data/services/lms/messages'; +import { renderWithIntl } from '../testUtils'; import { StatusBadge } from './StatusBadge'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - const className = 'test-className'; describe('StatusBadge component', () => { describe('behavior', () => { it('does not render if status does not have configured variant', () => { - const { container } = render(); + const { container } = renderWithIntl(); expect(container.firstChild).toBeNull(); }); describe('status rendering: loads badge with configured variant and message', () => { it('`ungraded` shows primary button variant and message', () => { - const { getByText } = render(); - const badge = getByText('FormattedMessage'); + renderWithIntl(); + const badge = screen.getByText(messages.ungraded.defaultMessage); expect(badge).toHaveClass('badge-primary'); }); it('`locked` shows light button variant and message', () => { - const { getByText } = render(); - const badge = getByText('FormattedMessage'); + renderWithIntl(); + const badge = screen.getByText(messages.locked.defaultMessage); expect(badge).toHaveClass('badge-light'); }); it('`graded` shows success button variant and message', () => { - const { getByText } = render(); - const badge = getByText('FormattedMessage'); + renderWithIntl(); + const badge = screen.getByText(messages.graded.defaultMessage); expect(badge).toHaveClass('badge-success'); }); it('`inProgress` shows warning button variant and message', () => { - const { getByText } = render(); - const badge = getByText('FormattedMessage'); + renderWithIntl(); + const badge = screen.getByText(messages['in-progress'].defaultMessage); expect(badge).toHaveClass('badge-warning'); }); }); diff --git a/src/containers/CriterionContainer/CriterionFeedback.test.jsx b/src/containers/CriterionContainer/CriterionFeedback.test.jsx index 7deae08..253a830 100644 --- a/src/containers/CriterionContainer/CriterionFeedback.test.jsx +++ b/src/containers/CriterionContainer/CriterionFeedback.test.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { actions, selectors } from 'data/redux'; @@ -12,6 +12,7 @@ import { mapStateToProps, mapDispatchToProps, } from './CriterionFeedback'; +import { renderWithIntl } from '../../testUtils'; jest.mock('data/redux/app/selectors', () => ({ rubric: { @@ -33,9 +34,6 @@ jest.mock('data/redux/grading/selectors', () => ({ }, })); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Criterion Feedback', () => { const props = { orderNum: 1, @@ -50,7 +48,7 @@ describe('Criterion Feedback', () => { describe('component', () => { describe('render', () => { it('shows a non-disabled input when grading', () => { - render(); + renderWithIntl(); const input = screen.getByTestId('criterion-feedback-input'); expect(input).toBeInTheDocument(); expect(input).not.toBeDisabled(); @@ -58,7 +56,7 @@ describe('Criterion Feedback', () => { }); it('shows a disabled input when not grading', () => { - render( + renderWithIntl( , ); const input = screen.getByTestId('criterion-feedback-input'); @@ -68,12 +66,12 @@ describe('Criterion Feedback', () => { }); it('displays an error message when feedback is invalid', () => { - render(); + renderWithIntl(); expect(screen.getByTestId('criterion-feedback-error-msg')).toBeInTheDocument(); }); it('does not render anything when config is set to disabled', () => { - const { container } = render( + const { container } = renderWithIntl( , ); expect(container.firstChild).toBeNull(); @@ -82,7 +80,7 @@ describe('Criterion Feedback', () => { describe('behavior', () => { it('calls setValue when input value changes', async () => { - render(); + renderWithIntl(); const user = userEvent.setup(); const input = screen.getByTestId('criterion-feedback-input'); await user.clear(input); diff --git a/src/containers/CriterionContainer/RadioCriterion.test.jsx b/src/containers/CriterionContainer/RadioCriterion.test.jsx index 7b55063..cd32a63 100644 --- a/src/containers/CriterionContainer/RadioCriterion.test.jsx +++ b/src/containers/CriterionContainer/RadioCriterion.test.jsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { actions, selectors } from 'data/redux'; import { @@ -6,6 +6,7 @@ import { mapDispatchToProps, mapStateToProps, } from './RadioCriterion'; +import { renderWithIntl } from '../../testUtils'; jest.mock('data/redux/app/selectors', () => ({ rubric: { @@ -27,9 +28,6 @@ jest.mock('data/redux/grading/selectors', () => ({ }, })); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Radio Criterion Container', () => { const props = { orderNum: 1, @@ -62,7 +60,7 @@ describe('Radio Criterion Container', () => { describe('component rendering', () => { it('should render radio buttons that are enabled when in grading mode', () => { - const { container } = render(); + const { container } = renderWithIntl(); const radioButtons = container.querySelectorAll('input[type="radio"]'); expect(radioButtons.length).toEqual(props.config.options.length); @@ -73,9 +71,9 @@ describe('Radio Criterion Container', () => { }); it('should render radio buttons that are disabled when not in grading mode', () => { - const { container } = render(); + renderWithIntl(); - const radioButtons = container.querySelectorAll('input[type="radio"]'); + const radioButtons = screen.queryAllByRole('radio'); expect(radioButtons.length).toEqual(props.config.options.length); radioButtons.forEach(button => { @@ -84,14 +82,14 @@ describe('Radio Criterion Container', () => { }); it('should render an error message when the criterion is invalid', () => { - const { container } = render(); + const { container } = renderWithIntl(); const errorMessage = container.querySelector('.feedback-error-msg'); expect(errorMessage).toBeInTheDocument(); }); it('should not render an error message when the criterion is valid', () => { - const { container } = render(); + const { container } = renderWithIntl(); const errorMessage = container.querySelector('.feedback-error-msg'); expect(errorMessage).not.toBeInTheDocument(); diff --git a/src/containers/CriterionContainer/ReviewCriterion.test.jsx b/src/containers/CriterionContainer/ReviewCriterion.test.jsx index 9dc6d8d..e5a447a 100644 --- a/src/containers/CriterionContainer/ReviewCriterion.test.jsx +++ b/src/containers/CriterionContainer/ReviewCriterion.test.jsx @@ -1,7 +1,8 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { selectors } from 'data/redux'; +import { renderWithIntl } from '../../testUtils'; import { ReviewCriterion, mapStateToProps } from './ReviewCriterion'; jest.mock('data/redux/app/selectors', () => ({ @@ -19,9 +20,6 @@ jest.mock('data/redux/grading/selectors', () => ({ }, })); -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('Review Criterion Container', () => { const props = { orderNum: 1, @@ -54,9 +52,9 @@ describe('Review Criterion Container', () => { describe('component', () => { it('renders all criteria options with correct labels and points', () => { - const { getAllByTestId } = render(); + renderWithIntl(); - const optionsElements = getAllByTestId('criteria-option'); + const optionsElements = screen.getAllByTestId('criteria-option'); expect(optionsElements.length).toEqual(props.config.options.length); props.config.options.forEach((option, index) => { @@ -65,7 +63,7 @@ describe('Review Criterion Container', () => { const pointsElement = optionElement.querySelector('[data-testid="option-points"]'); expect(labelElement.textContent).toEqual(option.label); - expect(pointsElement.textContent).toEqual('FormattedMessage'); + expect(pointsElement.textContent).toEqual(`${props.config.options[index].points} points`); }); }); }); diff --git a/src/containers/CriterionContainer/index.test.jsx b/src/containers/CriterionContainer/index.test.jsx index 0df1172..adfb564 100644 --- a/src/containers/CriterionContainer/index.test.jsx +++ b/src/containers/CriterionContainer/index.test.jsx @@ -6,9 +6,6 @@ import { gradeStatuses } from 'data/services/lms/constants'; import { CriterionContainer, mapStateToProps } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - const MockRadioCriterion = ({ orderNum, isGrading }) => (
RadioCriterion Component (orderNum={orderNum}, isGrading={String(isGrading)}) diff --git a/src/containers/DemoWarning/DemoWarning.test.jsx b/src/containers/DemoWarning/DemoWarning.test.jsx index 2542555..34b00e5 100644 --- a/src/containers/DemoWarning/DemoWarning.test.jsx +++ b/src/containers/DemoWarning/DemoWarning.test.jsx @@ -4,10 +4,6 @@ import { selectors } from 'data/redux'; import { DemoWarning, mapStateToProps } from '.'; import messages from './messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { app: { isEnabled: (args) => ({ isEnabled: args }) }, diff --git a/src/containers/ListView/EmptySubmission.test.jsx b/src/containers/ListView/EmptySubmission.test.jsx index 2bff790..7506169 100644 --- a/src/containers/ListView/EmptySubmission.test.jsx +++ b/src/containers/ListView/EmptySubmission.test.jsx @@ -1,12 +1,8 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import urls from 'data/services/lms/urls'; +import { renderWithIntl } from '../../testUtils'; import EmptySubmission from './EmptySubmission'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/services/lms/urls', () => ({ openResponse: (courseId) => `openResponseUrl(${courseId})`, })); @@ -16,25 +12,19 @@ jest.mock('./assets/empty-state.svg', () => './assets/empty-state.svg'); describe('EmptySubmission component', () => { const props = { courseId: 'test-course-id' }; - const renderWithIntl = (component) => render( - - {component} - , - ); - it('renders the empty state image with correct alt text', () => { - const { getByAltText } = renderWithIntl(); - expect(getByAltText('empty state')).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByAltText('empty state')).toBeInTheDocument(); }); it('renders the no results found title message', () => { - const { getByText } = renderWithIntl(); - expect(getByText('Nothing here yet')).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText('Nothing here yet')).toBeInTheDocument(); }); it('renders hyperlink with correct destination URL', () => { - const { container } = renderWithIntl(); - const hyperlink = container.querySelector('a'); + renderWithIntl(); + const hyperlink = screen.getByRole('link'); expect(hyperlink).toHaveAttribute( 'href', urls.openResponse(props.courseId), @@ -42,7 +32,7 @@ describe('EmptySubmission component', () => { }); it('renders the back to responses button', () => { - const { getByText } = renderWithIntl(); - expect(getByText('Back to all open responses')).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText('Back to all open responses')).toBeInTheDocument(); }); }); diff --git a/src/containers/ListView/FilterStatusComponent.test.jsx b/src/containers/ListView/FilterStatusComponent.test.jsx index aae61ef..daca105 100644 --- a/src/containers/ListView/FilterStatusComponent.test.jsx +++ b/src/containers/ListView/FilterStatusComponent.test.jsx @@ -5,9 +5,6 @@ import { DataTableContext } from '@openedx/paragon'; import * as module from './FilterStatusComponent'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - const fieldIds = ['field-id-0', 'field-id-1', 'field-id-2', 'field-id-3']; const filterOrder = [1, 0, 3, 2]; const filters = filterOrder.map((v) => ({ id: fieldIds[v] })); diff --git a/src/containers/ListView/ListError.test.jsx b/src/containers/ListView/ListError.test.jsx index 55dc574..2f2039e 100644 --- a/src/containers/ListView/ListError.test.jsx +++ b/src/containers/ListView/ListError.test.jsx @@ -1,14 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { selectors, thunkActions } from 'data/redux'; +import { renderWithIntl } from '../../testUtils'; import { ListError, mapDispatchToProps, mapStateToProps } from './ListError'; import messages from './messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { app: { @@ -38,28 +34,28 @@ describe('ListError component', () => { describe('behavior', () => { it('renders error alert with proper styling', () => { - render(); + renderWithIntl(); const alert = screen.getByRole('alert'); expect(alert).toBeInTheDocument(); expect(alert).toHaveClass('alert-danger'); }); it('displays error heading and message', () => { - render(); + renderWithIntl(); const heading = screen.getByRole('alert').querySelector('.alert-heading'); expect(heading).toBeInTheDocument(); expect(heading).toHaveTextContent(messages.loadErrorHeading.defaultMessage); }); it('displays try again button', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); expect(button).toHaveClass('btn-primary'); }); it('calls initializeApp when try again button is clicked', async () => { - render(); + renderWithIntl(); const user = userEvent.setup(); const button = screen.getByRole('button'); await user.click(button); diff --git a/src/containers/ListView/ListViewBreadcrumb.test.jsx b/src/containers/ListView/ListViewBreadcrumb.test.jsx index bb6c496..0b37432 100644 --- a/src/containers/ListView/ListViewBreadcrumb.test.jsx +++ b/src/containers/ListView/ListViewBreadcrumb.test.jsx @@ -1,15 +1,11 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { selectors } from 'data/redux'; +import { renderWithIntl } from '../../testUtils'; import { ListViewBreadcrumb, mapStateToProps, } from './ListViewBreadcrumb'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { app: { @@ -36,41 +32,37 @@ describe('ListViewBreadcrumb component', () => { oraName: 'fake-ora-name', }; - const renderWithIntl = (component) => render( - - {component} - , - ); - beforeEach(() => { jest.clearAllMocks(); }); describe('behavior', () => { it('renders back to responses link with correct destination', () => { - const { container } = renderWithIntl(); - const backLink = container.querySelector('a[href*="openResponseUrl"]'); + renderWithIntl(); + const backLink = screen.getAllByRole('link').find( + link => link.getAttribute('href') === `openResponseUrl(${props.courseId})`, + ); expect(backLink).toBeInTheDocument(); - expect(backLink).toHaveAttribute('href', `openResponseUrl(${props.courseId})`); }); it('displays ORA name in heading', () => { - const { getByText } = renderWithIntl(); - const heading = getByText(props.oraName); + renderWithIntl(); + const heading = screen.getByText(props.oraName); expect(heading).toBeInTheDocument(); expect(heading).toHaveClass('h3'); }); it('renders ORA link with correct destination', () => { - const { container } = renderWithIntl(); - const oraLink = container.querySelector('a[href*="oraUrl"]'); + renderWithIntl(); + const oraLink = screen.getAllByRole('link').find( + link => link.getAttribute('href') === `oraUrl(${props.courseId}, test-location-id)`, + ); expect(oraLink).toBeInTheDocument(); - expect(oraLink).toHaveAttribute('href', `oraUrl(${props.courseId}, test-location-id)`); }); it('displays back to responses text', () => { - const { getByText } = renderWithIntl(); - expect(getByText('Back to all open responses')).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText('Back to all open responses')).toBeInTheDocument(); }); }); diff --git a/src/containers/ListView/SelectedBulkAction.test.jsx b/src/containers/ListView/SelectedBulkAction.test.jsx index 7e50d6d..4b62c49 100644 --- a/src/containers/ListView/SelectedBulkAction.test.jsx +++ b/src/containers/ListView/SelectedBulkAction.test.jsx @@ -1,11 +1,7 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../testUtils'; import { SelectedBulkAction } from './SelectedBulkAction'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - describe('SelectedBulkAction component', () => { const props = { selectedFlatRows: [{ id: 1 }, { id: 2 }], @@ -17,21 +13,21 @@ describe('SelectedBulkAction component', () => { }); it('renders button with correct text and selected count', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); expect(button).toHaveTextContent(`View selected responses (${props.selectedFlatRows.length})`); }); it('applies correct CSS class to button', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toHaveClass('view-selected-responses-btn'); expect(button).toHaveClass('btn-primary'); }); it('calls handleClick with selectedFlatRows on render', () => { - render(); + renderWithIntl(); expect(props.handleClick).toHaveBeenCalledWith(props.selectedFlatRows); }); }); diff --git a/src/containers/ListView/SubmissionsTable.test.jsx b/src/containers/ListView/SubmissionsTable.test.jsx index 7a3c6f6..45da23d 100644 --- a/src/containers/ListView/SubmissionsTable.test.jsx +++ b/src/containers/ListView/SubmissionsTable.test.jsx @@ -1,17 +1,13 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { selectors, thunkActions } from 'data/redux'; import { gradingStatuses as statuses } from 'data/services/lms/constants'; +import { renderWithIntl } from '../../testUtils'; import { SubmissionsTable, mapStateToProps, mapDispatchToProps, } from './SubmissionsTable'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { app: { @@ -91,20 +87,14 @@ describe('SubmissionsTable component', () => { loadSelectionForReview: jest.fn(), }; - const renderWithIntl = (component) => render( - - {component} - , - ); - beforeEach(() => { jest.clearAllMocks(); }); describe('behavior', () => { it('renders DataTable component', () => { - const { container } = renderWithIntl(); - const submissionsTable = container.querySelector('.submissions-table'); + renderWithIntl(); + const submissionsTable = screen.getByRole('table'); expect(submissionsTable).toBeInTheDocument(); }); diff --git a/src/containers/ListView/TableAction.test.jsx b/src/containers/ListView/TableAction.test.jsx index e7e9d1c..9ef94d3 100644 --- a/src/containers/ListView/TableAction.test.jsx +++ b/src/containers/ListView/TableAction.test.jsx @@ -1,12 +1,8 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../testUtils'; import { TableAction } from './TableAction'; import messages from './messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - describe('TableAction component', () => { const props = { tableInstance: { rows: [{ id: 1 }, { id: 2 }] }, @@ -18,21 +14,21 @@ describe('TableAction component', () => { }); it('renders button with correct text', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); expect(button).toHaveTextContent(messages.viewAllResponses.defaultMessage); }); it('applies correct CSS class to button', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toHaveClass('view-all-responses-btn'); expect(button).toHaveClass('btn-primary'); }); it('enables button when rows are present', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).not.toBeDisabled(); }); @@ -42,13 +38,13 @@ describe('TableAction component', () => { tableInstance: { rows: [] }, handleClick: jest.fn(() => () => {}), }; - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toBeDisabled(); }); it('calls handleClick with table rows on render', () => { - render(); + renderWithIntl(); expect(props.handleClick).toHaveBeenCalledWith(props.tableInstance.rows); }); }); diff --git a/src/containers/ListView/index.test.jsx b/src/containers/ListView/index.test.jsx index c8bc5ac..be7fad2 100644 --- a/src/containers/ListView/index.test.jsx +++ b/src/containers/ListView/index.test.jsx @@ -1,18 +1,14 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { selectors, thunkActions } from 'data/redux'; import { RequestKeys } from 'data/constants/requests'; import { formatMessage } from 'testUtils'; +import { renderWithIntl } from '../../testUtils'; import { ListView, mapStateToProps, mapDispatchToProps } from '.'; import messages from './messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('containers/ReviewModal', () => { const ReviewModal = () =>
ReviewModal
; return ReviewModal; @@ -108,7 +104,7 @@ describe('ListView component', () => { }); it('displays loading spinner and message when not loaded and no error', () => { - render(); + renderWithIntl(); // Check for loading message expect(screen.getByText(messages.loadingResponses.defaultMessage)).toBeInTheDocument(); @@ -119,7 +115,7 @@ describe('ListView component', () => { }); it('displays ListViewBreadcrumb and SubmissionsTable when loaded with data', () => { - render(); + renderWithIntl(); expect( screen.getByText('Back to all open responses'), @@ -129,7 +125,7 @@ describe('ListView component', () => { }); it('displays EmptySubmission component when loaded but has no submission data', () => { - render(); + renderWithIntl(); expect( screen.getByRole('heading', { name: 'Nothing here yet' }), @@ -146,7 +142,7 @@ describe('ListView component', () => { }); it('displays ListError component when there is an error', () => { - render(); + renderWithIntl(); expect( screen.getByRole('button', { name: 'Reload submissions' }), @@ -155,18 +151,18 @@ describe('ListView component', () => { }); it('always displays ReviewModal component regardless of state', () => { - const { rerender } = render(); + const { rerender } = renderWithIntl(); expect(screen.getByText('ReviewModal')).toBeInTheDocument(); - rerender(); + rerender(); expect(screen.getByText('ReviewModal')).toBeInTheDocument(); - rerender(); + rerender(); expect(screen.getByText('ReviewModal')).toBeInTheDocument(); }); it('calls initializeApp on component mount', () => { - render(); + renderWithIntl(); expect(props.initializeApp).toHaveBeenCalledTimes(1); }); }); diff --git a/src/containers/ResponseDisplay/FileDownload.test.jsx b/src/containers/ResponseDisplay/FileDownload.test.jsx index 5fd0965..7eccd63 100644 --- a/src/containers/ResponseDisplay/FileDownload.test.jsx +++ b/src/containers/ResponseDisplay/FileDownload.test.jsx @@ -1,6 +1,5 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { RequestKeys, RequestStates } from 'data/constants/requests'; import { selectors, thunkActions } from 'data/redux'; import { @@ -10,10 +9,7 @@ import { statusMapping, } from './FileDownload'; import messages from './messages'; - -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); +import { renderWithIntl } from '../../testUtils'; jest.mock('data/redux', () => ({ selectors: { @@ -36,7 +32,7 @@ describe('FileDownload', () => { describe('behavior', () => { it('renders StatefulButton with default state when inactive', () => { - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); expect(button).toHaveTextContent(messages.downloadFiles.defaultMessage); @@ -44,9 +40,8 @@ describe('FileDownload', () => { it('renders with pending state when download is pending', () => { const props = { ...defaultProps, requestStatus: { status: RequestStates.pending } }; - render(); + renderWithIntl(); const button = screen.getByRole('button'); - screen.debug(); expect(button).toHaveClass('pgn__stateful-btn-state-pending'); expect(button).toHaveAttribute('aria-disabled', 'true'); expect(button).toHaveTextContent(messages.downloading.defaultMessage); @@ -54,14 +49,14 @@ describe('FileDownload', () => { it('renders with completed state when download is completed', () => { const props = { ...defaultProps, requestStatus: { status: RequestStates.completed } }; - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toHaveClass('pgn__stateful-btn-state-completed'); }); it('renders with failed state when download fails', () => { const props = { ...defaultProps, requestStatus: { status: RequestStates.failed } }; - render(); + renderWithIntl(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); expect(button).toHaveClass('pgn__stateful-btn-state-failed'); @@ -69,7 +64,7 @@ describe('FileDownload', () => { }); it('calls downloadFiles when button is clicked', async () => { - render(); + renderWithIntl(); const user = userEvent.setup(); const button = screen.getByRole('button'); await user.click(button); diff --git a/src/containers/ResponseDisplay/PreviewDisplay.test.jsx b/src/containers/ResponseDisplay/PreviewDisplay.test.jsx index ca0da40..f60c05c 100644 --- a/src/containers/ResponseDisplay/PreviewDisplay.test.jsx +++ b/src/containers/ResponseDisplay/PreviewDisplay.test.jsx @@ -1,12 +1,8 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { FileTypes } from 'data/constants/files'; +import { renderWithIntl } from '../../testUtils'; import { PreviewDisplay } from './PreviewDisplay'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - describe('PreviewDisplay', () => { const supportedTypes = Object.values(FileTypes); const props = { @@ -24,32 +20,26 @@ describe('PreviewDisplay', () => { ], }; - const renderWithIntl = (component) => render( - - {component} - , - ); - beforeEach(() => { jest.clearAllMocks(); }); it('renders preview display container', () => { - const { container } = renderWithIntl(); - const previewDisplay = container.querySelector('.preview-display'); + renderWithIntl(); + const previewDisplay = screen.getByRole('button', { name: 'fake_file_0.pdf' }); expect(previewDisplay).toBeInTheDocument(); }); it('renders empty container when no files provided', () => { - const { container } = renderWithIntl(); - const previewDisplay = container.querySelector('.preview-display'); + renderWithIntl(); + const previewDisplay = document.querySelector('.preview-display'); expect(previewDisplay).toBeInTheDocument(); expect(previewDisplay.children.length).toBe(0); }); it('only renders supported file types', () => { - const { container } = renderWithIntl(); - const previewDisplay = container.querySelector('.preview-display'); + renderWithIntl(); + const previewDisplay = document.querySelector('.preview-display'); expect(previewDisplay.children.length).toBe(supportedTypes.length); }); @@ -59,8 +49,8 @@ describe('PreviewDisplay', () => { description: 'unsupported file', downloadUrl: '/unsupported.xyz', }; - const { container } = renderWithIntl(); - const previewDisplay = container.querySelector('.preview-display'); + renderWithIntl(); + const previewDisplay = document.querySelector('.preview-display'); expect(previewDisplay.children.length).toBe(0); }); }); diff --git a/src/containers/ResponseDisplay/SubmissionFiles.test.jsx b/src/containers/ResponseDisplay/SubmissionFiles.test.jsx index 999f121..5af854a 100644 --- a/src/containers/ResponseDisplay/SubmissionFiles.test.jsx +++ b/src/containers/ResponseDisplay/SubmissionFiles.test.jsx @@ -1,12 +1,8 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { downloadAllLimit, downloadSingleLimit } from 'data/constants/files'; +import { renderWithIntl } from '../../testUtils'; import { SubmissionFiles } from './SubmissionFiles'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('./components/FileNameCell', () => jest.fn(({ value }) =>
Name: {value}
)); jest.mock('./components/FileExtensionCell', () => jest.fn(({ value }) =>
Extension: {value}
)); jest.mock('./components/FilePopoverCell', () => jest.fn(() =>
Popover
)); @@ -30,12 +26,6 @@ describe('SubmissionFiles', () => { ], }; - const renderWithIntl = (component) => render( - - {component} - , - ); - beforeEach(() => { jest.clearAllMocks(); }); @@ -88,8 +78,8 @@ describe('SubmissionFiles', () => { }); it('displays title only when no files are provided', () => { - const { container } = renderWithIntl(); - const title = container.querySelector('.submission-files-title h3'); + renderWithIntl(); + const title = screen.getByRole('heading', { level: 3 }); expect(title).toBeInTheDocument(); expect(title).toHaveTextContent('Submission Files (0)'); expect(screen.queryByTestId('file-download')).not.toBeInTheDocument(); diff --git a/src/containers/ResponseDisplay/components/FileExtensionCell.test.jsx b/src/containers/ResponseDisplay/components/FileExtensionCell.test.jsx index 57f64d2..3cccd54 100644 --- a/src/containers/ResponseDisplay/components/FileExtensionCell.test.jsx +++ b/src/containers/ResponseDisplay/components/FileExtensionCell.test.jsx @@ -1,9 +1,6 @@ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import FileExtensionCell from './FileExtensionCell'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - describe('FileExtensionCell', () => { const props = { value: 'file_name.with_extension.pdf', @@ -14,8 +11,8 @@ describe('FileExtensionCell', () => { }); it('renders file extension in uppercase', () => { - const { getByText } = render(); - expect(getByText('PDF')).toBeInTheDocument(); + render(); + expect(screen.getByText('PDF')).toBeInTheDocument(); }); it('applies correct CSS class', () => { @@ -25,13 +22,13 @@ describe('FileExtensionCell', () => { }); it('extracts extension from file with multiple dots', () => { - const { getByText } = render(); - expect(getByText('DOCX')).toBeInTheDocument(); + render(); + expect(screen.getByText('DOCX')).toBeInTheDocument(); }); it('handles file without extension', () => { - const { getByText } = render(); - expect(getByText('FILENAME')).toBeInTheDocument(); + render(); + expect(screen.getByText('FILENAME')).toBeInTheDocument(); }); it('handles empty file extension', () => { diff --git a/src/containers/ResponseDisplay/components/FilePopoverCell.test.jsx b/src/containers/ResponseDisplay/components/FilePopoverCell.test.jsx index 5c8f2a4..b0f9b58 100644 --- a/src/containers/ResponseDisplay/components/FilePopoverCell.test.jsx +++ b/src/containers/ResponseDisplay/components/FilePopoverCell.test.jsx @@ -1,18 +1,8 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../../testUtils'; import FilePopoverCell from './FilePopoverCell'; -const mockMessages = { - en: {}, -}; - -const renderWithIntl = (component) => render( - - {component} - , -); - describe('FilePopoverCell', () => { const props = { row: { @@ -25,20 +15,10 @@ describe('FilePopoverCell', () => { }, }; - it('renders the component without errors', () => { - const { container } = renderWithIntl(); - expect(container.firstChild).toBeInTheDocument(); - }); - - it('renders the info icon button', () => { - const { getByTestId } = renderWithIntl(); - expect(getByTestId('esg-help-icon')).toBeInTheDocument(); - }); - - it('info button has correct alt text', () => { - const { getByTestId } = renderWithIntl(); - const button = getByTestId('esg-help-icon'); - expect(button).toHaveAttribute('alt', 'Display more info'); + it('renders info button has correct alt text', () => { + renderWithIntl(); + const button = screen.getByRole('button', { name: /display more info/i }); + expect(button).toBeInTheDocument(); }); it('handles empty row.original object', () => { diff --git a/src/containers/ResponseDisplay/index.test.jsx b/src/containers/ResponseDisplay/index.test.jsx index be56044..8925400 100644 --- a/src/containers/ResponseDisplay/index.test.jsx +++ b/src/containers/ResponseDisplay/index.test.jsx @@ -3,9 +3,6 @@ import { fileUploadResponseOptions } from 'data/services/lms/constants'; import { selectors } from 'data/redux'; import { ResponseDisplay, mapStateToProps } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - jest.mock('data/redux', () => ({ selectors: { grading: { diff --git a/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx b/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx index 6bc037a..a297c58 100644 --- a/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx +++ b/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx @@ -1,17 +1,8 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { renderWithIntl } from '../../../testUtils'; import OverrideGradeConfirmModal from './OverrideGradeConfirmModal'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - -const renderWithIntl = (component) => render( - - {component} - , -); - describe('OverrideGradeConfirmModal', () => { const props = { isOpen: false, @@ -24,14 +15,14 @@ describe('OverrideGradeConfirmModal', () => { }); it('should not render content when modal is closed', () => { - const { queryByText } = renderWithIntl(); - expect(queryByText('This cannot be undone')).toBeNull(); + renderWithIntl(); + expect(screen.queryByText('This cannot be undone')).toBeNull(); }); it('should display content when modal is open', () => { - const { getByText } = renderWithIntl(); - expect(getByText('Are you sure you want to override this grade?')).toBeInTheDocument(); - expect(getByText(/This cannot be undone.*The learner may have already received their grade/)).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText('Are you sure you want to override this grade?')).toBeInTheDocument(); + expect(screen.getByText(/This cannot be undone.*The learner may have already received their grade/)).toBeInTheDocument(); }); it('should call onCancel when cancel button is clicked', async () => { diff --git a/src/containers/ReviewActions/components/StartGradingButton/index.test.jsx b/src/containers/ReviewActions/components/StartGradingButton/index.test.jsx index 2c806e6..b369e19 100644 --- a/src/containers/ReviewActions/components/StartGradingButton/index.test.jsx +++ b/src/containers/ReviewActions/components/StartGradingButton/index.test.jsx @@ -1,14 +1,10 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { useDispatch } from 'react-redux'; +import { renderWithIntl } from '../../../../testUtils'; import * as hooks from './hooks'; import { StartGradingButton } from '.'; import messages from '../messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('react-redux', () => ({ useDispatch: jest.fn(), })); @@ -20,12 +16,6 @@ jest.mock('./hooks', () => ({ describe('StartGradingButton', () => { const mockDispatch = jest.fn(); - const renderWithIntl = (component) => render( - - {component} - , - ); - beforeEach(() => { jest.clearAllMocks(); useDispatch.mockReturnValue(mockDispatch); diff --git a/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx b/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx index 433bd9b..e5083ee 100644 --- a/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx +++ b/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx @@ -1,17 +1,8 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { renderWithIntl } from '../../../testUtils'; import StopGradingConfirmModal from './StopGradingConfirmModal'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - -const renderWithIntl = (component) => render( - - {component} - , -); - describe('StopGradingConfirmModal', () => { const props = { isOpen: false, diff --git a/src/containers/ReviewActions/components/SubmissionNavigation.test.jsx b/src/containers/ReviewActions/components/SubmissionNavigation.test.jsx index f590597..a055a21 100644 --- a/src/containers/ReviewActions/components/SubmissionNavigation.test.jsx +++ b/src/containers/ReviewActions/components/SubmissionNavigation.test.jsx @@ -1,30 +1,15 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { thunkActions } from 'data/redux'; +import { renderWithIntl } from '../../../testUtils'; import { SubmissionNavigation, mapDispatchToProps, } from './SubmissionNavigation'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); - -const mockMessages = { - 'ora-grading.ReviewActions.loadPrevious': 'Load previous submission', - 'ora-grading.ReviewActions.loadNext': 'Load next submission', - 'ora-grading.ReviewActions.navigationLabel': '{current} of {total}', -}; - -const renderWithIntl = (component) => render( - - {component} - , -); - describe('SubmissionNavigation component', () => { describe('component', () => { const defaultProps = { @@ -44,7 +29,7 @@ describe('SubmissionNavigation component', () => { it('renders navigation with current position and total submissions', () => { renderWithIntl(); - expect(screen.getByText('FormattedMessage')).toBeInTheDocument(); + expect(screen.getByText(`${defaultProps.activeIndex + 1} of ${defaultProps.selectionLength}`)).toBeInTheDocument(); }); it('disables previous button when no previous submission exists', () => { @@ -90,10 +75,10 @@ describe('SubmissionNavigation component', () => { }); it('shows correct position when at first submission', () => { - render( + renderWithIntl( , ); - expect(screen.getByText('FormattedMessage')).toBeInTheDocument(); + expect(screen.getByText(`${1} of ${defaultProps.selectionLength}`)).toBeInTheDocument(); }); }); diff --git a/src/containers/ReviewActions/index.test.jsx b/src/containers/ReviewActions/index.test.jsx index a33cc38..051b618 100644 --- a/src/containers/ReviewActions/index.test.jsx +++ b/src/containers/ReviewActions/index.test.jsx @@ -1,14 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { actions, selectors } from 'data/redux'; import { RequestKeys } from 'data/constants/requests'; +import { renderWithIntl } from '../../testUtils'; import { ReviewActions, mapStateToProps, mapDispatchToProps } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ actions: { app: { @@ -65,12 +61,6 @@ jest.mock('./components/SubmissionNavigation', () => { }); describe('ReviewActions component', () => { - const renderWithIntl = (component) => render( - - {component} - , - ); - describe('component', () => { const props = { gradingStatus: 'ungraded', diff --git a/src/containers/ReviewModal/ReviewContent.test.jsx b/src/containers/ReviewModal/ReviewContent.test.jsx index 5edda55..a5c0ea7 100644 --- a/src/containers/ReviewModal/ReviewContent.test.jsx +++ b/src/containers/ReviewModal/ReviewContent.test.jsx @@ -1,13 +1,9 @@ -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../testUtils'; import { ReviewContent, } from './ReviewContent'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - // Since we are only testing the ReviewContent component, // we can mock the child components to avoid unnecessary complexity on mocking the redux store. jest.mock('containers/ReviewModal/ReviewErrors/FetchErrors', () => { @@ -50,12 +46,6 @@ jest.mock('data/redux', () => ({ })); describe('ReviewContent component', () => { - const renderWithIntl = (component) => render( - - {component} - , - ); - beforeEach(() => { jest.clearAllMocks(); }); @@ -80,10 +70,10 @@ describe('ReviewContent component', () => { }); it('renders with rubric when showRubric is true and loaded', () => { - const { container, getByText } = renderWithIntl(); + const { container } = renderWithIntl(); expect(container.querySelector('.content-block')).toBeInTheDocument(); expect(container.querySelector('.flex-nowrap')).toBeInTheDocument(); - expect(getByText('Rubric')).toBeInTheDocument(); + expect(screen.getByText('Rubric')).toBeInTheDocument(); }); }); }); diff --git a/src/containers/ReviewModal/ReviewErrors/DownloadErrors.test.jsx b/src/containers/ReviewModal/ReviewErrors/DownloadErrors.test.jsx index e5d7260..093e8a9 100644 --- a/src/containers/ReviewModal/ReviewErrors/DownloadErrors.test.jsx +++ b/src/containers/ReviewModal/ReviewErrors/DownloadErrors.test.jsx @@ -1,14 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { selectors, actions, thunkActions } from 'data/redux'; import { RequestKeys } from 'data/constants/requests'; +import { renderWithIntl } from '../../../testUtils'; import { DownloadErrors, mapStateToProps, mapDispatchToProps } from './DownloadErrors'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { requests: { @@ -24,12 +20,6 @@ jest.mock('data/redux', () => ({ }, })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('DownloadErrors component', () => { const defaultProps = { isFailed: false, diff --git a/src/containers/ReviewModal/ReviewErrors/FetchErrors.test.jsx b/src/containers/ReviewModal/ReviewErrors/FetchErrors.test.jsx index 39d0aad..0fcebf5 100644 --- a/src/containers/ReviewModal/ReviewErrors/FetchErrors.test.jsx +++ b/src/containers/ReviewModal/ReviewErrors/FetchErrors.test.jsx @@ -1,20 +1,15 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import { selectors, thunkActions } from 'data/redux'; import { RequestKeys } from 'data/constants/requests'; - +import { renderWithIntl } from '../../../testUtils'; import { FetchErrors, mapStateToProps, mapDispatchToProps, } from './FetchErrors'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { requests: { @@ -30,12 +25,6 @@ jest.mock('data/redux', () => ({ const requestKey = RequestKeys.fetchSubmission; -const renderWithIntl = (component) => render( - - {component} - , -); - describe('FetchErrors component', () => { const props = { isFailed: false, diff --git a/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx b/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx index d5f82b8..7ce085f 100644 --- a/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx +++ b/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx @@ -1,20 +1,15 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import '@testing-library/jest-dom'; - -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { selectors } from 'data/redux'; import { ErrorStatuses, RequestKeys } from 'data/constants/requests'; +import { renderWithIntl } from '../../../testUtils'; import { LockErrors, mapStateToProps, } from './LockErrors'; -jest.unmock('react'); -jest.unmock('@openedx/paragon'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ selectors: { requests: { @@ -24,12 +19,6 @@ jest.mock('data/redux', () => ({ }, })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('LockErrors component', () => { describe('when not failed', () => { it('renders nothing when isFailed is false', () => { diff --git a/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx b/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx index 6a315ed..44d33dc 100644 --- a/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx +++ b/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx @@ -1,18 +1,8 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { renderWithIntl } from '../../../testUtils'; import ReviewError from './ReviewError'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - -const renderWithIntl = (component) => render( - - {component} - , -); - describe('ReviewError component', () => { const messages = { heading: { diff --git a/src/containers/ReviewModal/ReviewErrors/SubmitErrors/index.test.jsx b/src/containers/ReviewModal/ReviewErrors/SubmitErrors/index.test.jsx index dc212d4..0eee1a5 100644 --- a/src/containers/ReviewModal/ReviewErrors/SubmitErrors/index.test.jsx +++ b/src/containers/ReviewModal/ReviewErrors/SubmitErrors/index.test.jsx @@ -1,14 +1,10 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../../../testUtils'; import * as hooks from './hooks'; import { SubmitErrors } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('react-redux', () => ({ useDispatch: jest.fn(() => jest.fn()), })); @@ -17,12 +13,6 @@ jest.mock('./hooks', () => ({ rendererHooks: jest.fn(() => ({ show: false })), })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('SubmitErrors component', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/src/containers/ReviewModal/ReviewErrors/index.test.jsx b/src/containers/ReviewModal/ReviewErrors/index.test.jsx index 2964280..6609b68 100644 --- a/src/containers/ReviewModal/ReviewErrors/index.test.jsx +++ b/src/containers/ReviewModal/ReviewErrors/index.test.jsx @@ -1,11 +1,6 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { renderWithIntl } from '../../../testUtils'; import { ReviewErrors } from '.'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('react-redux', () => ({ useDispatch: jest.fn(() => jest.fn()), useSelector: jest.fn((selector) => selector({ @@ -63,12 +58,6 @@ jest.mock('data/redux', () => ({ }, })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('ReviewErrors component', () => { it('renders without errors', () => { const { container } = renderWithIntl(); diff --git a/src/containers/ReviewModal/components/CloseReviewConfirmModal.test.jsx b/src/containers/ReviewModal/components/CloseReviewConfirmModal.test.jsx index 2667060..fc9dcc2 100644 --- a/src/containers/ReviewModal/components/CloseReviewConfirmModal.test.jsx +++ b/src/containers/ReviewModal/components/CloseReviewConfirmModal.test.jsx @@ -1,18 +1,8 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { renderWithIntl } from '../../../testUtils'; import CloseReviewConfirmModal from './CloseReviewConfirmModal'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - -const renderWithIntl = (component) => render( - - {component} - , -); - describe('CloseReviewConfirmModal', () => { const props = { isOpen: false, @@ -25,14 +15,14 @@ describe('CloseReviewConfirmModal', () => { }); it('should not render content when modal is closed', () => { - const { queryByText } = renderWithIntl(); - expect(queryByText('This cannot be undone')).toBeNull(); + renderWithIntl(); + expect(screen.queryByText('This cannot be undone')).toBeNull(); }); it('should display content when modal is open', () => { - const { getByText } = renderWithIntl(); - expect(getByText('Are you sure you want to close this modal?')).toBeInTheDocument(); - expect(getByText(/This cannot be undone.*This will discard unsaved work/)).toBeInTheDocument(); + renderWithIntl(); + expect(screen.getByText('Are you sure you want to close this modal?')).toBeInTheDocument(); + expect(screen.getByText(/This cannot be undone.*This will discard unsaved work/)).toBeInTheDocument(); }); it('should call onCancel when cancel button is clicked', async () => { diff --git a/src/containers/ReviewModal/index.test.jsx b/src/containers/ReviewModal/index.test.jsx index e3f73b7..a141ecd 100644 --- a/src/containers/ReviewModal/index.test.jsx +++ b/src/containers/ReviewModal/index.test.jsx @@ -1,14 +1,10 @@ import { useDispatch } from 'react-redux'; -import { render, screen } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; import * as hooks from './hooks'; import { ReviewModal } from '.'; import messages from './messages'; - -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); +import { renderWithIntl } from '../../testUtils'; jest.mock('react-redux', () => ({ useDispatch: jest.fn(), @@ -59,7 +55,7 @@ describe('ReviewModal', () => { }, }); - render(); + renderWithIntl(); expect(hooks.rendererHooks).toHaveBeenCalledWith({ dispatch: mockDispatch, @@ -80,7 +76,7 @@ describe('ReviewModal', () => { }, }); - render(); + renderWithIntl(); expect(useDispatch).toHaveBeenCalled(); }); @@ -97,7 +93,7 @@ describe('ReviewModal', () => { }, }); - render(); + renderWithIntl(); const reviewActions = screen.getByText('ReviewActions'); expect(reviewActions).toBeInTheDocument(); @@ -124,7 +120,7 @@ describe('ReviewModal', () => { }, }); - render(); + renderWithIntl(); const loadingMessage = screen.getByText(messages.loadingResponse.defaultMessage); expect(loadingMessage).toBeInTheDocument(); }); diff --git a/src/containers/Rubric/RubricFeedback.test.jsx b/src/containers/Rubric/RubricFeedback.test.jsx index 038e377..8d2e327 100644 --- a/src/containers/Rubric/RubricFeedback.test.jsx +++ b/src/containers/Rubric/RubricFeedback.test.jsx @@ -1,14 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; import { actions, selectors } from 'data/redux'; import { feedbackRequirement, gradeStatuses } from 'data/services/lms/constants'; +import { renderWithIntl } from '../../testUtils'; import { RubricFeedback, mapDispatchToProps, mapStateToProps } from './RubricFeedback'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('data/redux', () => ({ actions: { grading: { setRubricFeedback: jest.fn() }, @@ -32,12 +28,6 @@ jest.mock('data/redux', () => ({ }, })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('Rubric Feedback component', () => { const defaultProps = { config: 'config string', diff --git a/src/containers/Rubric/index.test.jsx b/src/containers/Rubric/index.test.jsx index 6a4598a..c9ace28 100644 --- a/src/containers/Rubric/index.test.jsx +++ b/src/containers/Rubric/index.test.jsx @@ -1,12 +1,8 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { screen } from '@testing-library/react'; +import { renderWithIntl } from '../../testUtils'; import { Rubric } from '.'; import * as hooks from './hooks'; -jest.unmock('@openedx/paragon'); -jest.unmock('react'); -jest.unmock('@edx/frontend-platform/i18n'); - jest.mock('react-redux', () => ({ useDispatch: jest.fn(() => jest.fn()), connect: jest.fn((mapStateToProps, mapDispatchToProps) => (Component) => { @@ -93,12 +89,6 @@ jest.mock('./hooks', () => ({ ButtonStates: jest.requireActual('./hooks').ButtonStates, })); -const renderWithIntl = (component) => render( - - {component} - , -); - describe('Rubric Container', () => { const hookProps = { criteria: [ @@ -115,20 +105,20 @@ describe('Rubric Container', () => { hooks.rendererHooks.mockReturnValue(hookProps); }); - it('renders rubric with footer when showFooter is true', () => { + it('renders rubric with submit button in footer when showFooter is true', () => { hooks.rendererHooks.mockReturnValueOnce({ ...hookProps, showFooter: true }); - const { container } = renderWithIntl(); - const rubricCard = container.querySelector('.grading-rubric-card'); - const footer = container.querySelector('.grading-rubric-footer'); - expect(rubricCard).toBeInTheDocument(); - expect(footer).toBeInTheDocument(); + renderWithIntl(); + const rubricCardTitle = screen.getByRole('heading', { name: /rubric/i }); + const submitButton = screen.queryByRole('button', { name: /submit grade/i }); + expect(rubricCardTitle).toBeInTheDocument(); + expect(submitButton).toBeInTheDocument(); }); - it('renders rubric without footer when showFooter is false', () => { - const { container } = renderWithIntl(); - const rubricCard = container.querySelector('.grading-rubric-card'); - const footer = container.querySelector('.grading-rubric-footer'); - expect(rubricCard).toBeInTheDocument(); - expect(footer).not.toBeInTheDocument(); + it('renders rubric without submit button on footer when showFooter is false', () => { + renderWithIntl(); + const rubricCardTitle = screen.getByRole('heading', { name: /rubric/i }); + const submitButton = screen.queryByRole('button', { name: /submit grade/i }); + expect(rubricCardTitle).toBeInTheDocument(); + expect(submitButton).not.toBeInTheDocument(); }); }); diff --git a/src/index.test.jsx b/src/index.test.jsx index 9436eee..c85df13 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -65,16 +65,12 @@ describe('app registry', () => { const callArgs = subscribe.mock.calls[0]; expect(callArgs[0]).toEqual(APP_READY); callArgs[1](); - const [rendered] = mockRender.mock.calls[0]; - expect(rendered).toMatchSnapshot(); }); test('subscribe: APP_INIT_ERROR. snapshot: displays an ErrorPage to root element', () => { const callArgs = subscribe.mock.calls[1]; expect(callArgs[0]).toEqual(APP_INIT_ERROR); const error = { message: 'test-error-message' }; callArgs[1](error); - const [rendered] = mockRender.mock.calls[0]; - expect(rendered).toMatchSnapshot(); }); test('initialize is called with footerMessages and requireAuthenticatedUser', () => { expect(initialize).toHaveBeenCalledTimes(1); diff --git a/src/setupTest.js b/src/setupTest.js index 47cdbc1..e2c8fcd 100755 --- a/src/setupTest.js +++ b/src/setupTest.js @@ -1,111 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ import '@testing-library/jest-dom'; -// breaking change here: https://github.com/testing-library/jest-dom/releases/tag/v6.0.0 - -jest.mock('react', () => ({ - ...jest.requireActual('react'), - useRef: jest.fn((val) => ({ current: val, useRef: true })), - useCallback: jest.fn((cb, prereqs) => ({ useCallback: { cb, prereqs } })), - useEffect: jest.fn((cb, prereqs) => ({ useEffect: { cb, prereqs } })), - useContext: jest.fn(context => context), -})); - -jest.mock('@edx/frontend-platform/i18n', () => { - const i18n = jest.requireActual('@edx/frontend-platform/i18n'); - const PropTypes = jest.requireActual('prop-types'); - const { formatMessage } = jest.requireActual('./testUtils'); - const formatDate = jest.fn(date => new Date(date).toLocaleDateString()).mockName('useIntl.formatDate'); - return { - ...i18n, - intlShape: PropTypes.shape({ - formatMessage: PropTypes.func, - }), - useIntl: () => ({ - formatMessage, - formatDate, - }), - defineMessages: m => m, - FormattedMessage: () => 'FormattedMessage', - }; -}); - -jest.mock('@openedx/paragon', () => jest.requireActual('testUtils').mockNestedComponents({ - Alert: { - Heading: 'Alert.Heading', - }, - AlertModal: 'AlertModal', - ActionRow: 'ActionRow', - Badge: 'Badge', - Button: 'Button', - Card: { - Body: 'Card.Body', - Section: 'Card.Section', - Footer: 'Card.Footer', - }, - Col: 'Col', - Collapsible: { - Advanced: 'Collapsible.Advanced', - Body: 'Collapsible.Body', - Trigger: 'Collapsible.Trigger', - Visible: 'Collapsible.Visible', - }, - Container: 'Container', - DataTable: { - EmptyTable: 'DataTable.EmptyTable', - Table: 'DataTable.Table', - TableControlBar: 'DataTable.TableControlBar', - TableController: 'DataTable.TableController', - TableFooter: 'DataTable.TableFooter', - }, - Dropdown: { - Item: 'Dropdown.Item', - Menu: 'Dropdown.Menu', - Toggle: 'Dropdown.Toggle', - }, - Form: { - Control: { - Feedback: 'Form.Control.Feedback', - }, - Group: 'Form.Group', - Label: 'Form.Label', - Radio: 'Form.Radio', - RadioSet: 'Form.RadioSet', - }, - FormControlFeedback: 'FormControlFeedback', - FullscreenModal: 'FullscreenModal', - Hyperlink: 'Hyperlink', - Icon: 'Icon', - IconButton: 'IconButton', - MultiSelectDropdownFilter: 'MultiSelectDropdownFilter', - OverlayTrigger: 'OverlayTrigger', - PageBanner: 'PageBanner', - Popover: { - Content: 'Popover.Content', - }, - Row: 'Row', - StatefulButton: 'StatefulButton', - TextFilter: 'TextFilter', - Spinner: 'Spinner', -})); - -jest.mock('@fortawesome/react-fontawesome', () => ({ - FontAwesomeIcon: 'FontAwesomeIcon', -})); -jest.mock('@fortawesome/free-solid-svg-icons', () => ({ - faUserCircle: jest.fn().mockName('fa-user-circle-icon'), -})); - -jest.mock('@openedx/paragon/icons', () => ({ - ArrowBack: jest.fn().mockName('icons.ArrowBack'), - ArrowDropDown: jest.fn().mockName('icons.ArrowDropDown'), - ArrowDropUp: jest.fn().mockName('icons.ArrowDropUp'), - Cancel: jest.fn().mockName('icons.Cancel'), - ChevronLeft: jest.fn().mockName('icons.ChevronLeft'), - ChevronRight: jest.fn().mockName('icons.ChevronRight'), - Highlight: jest.fn().mockName('icons.Highlight'), - InfoOutline: jest.fn().mockName('icons.InfoOutline'), - Launch: jest.fn().mockName('icons.Launch'), -})); jest.mock('data/constants/app', () => ({ locationId: () => 'fake-location-id', diff --git a/src/test/app.test.jsx b/src/test/app.test.jsx index 5f7d250..d009301 100644 --- a/src/test/app.test.jsx +++ b/src/test/app.test.jsx @@ -27,10 +27,6 @@ import App from 'App'; import Inspector from './inspector'; import appMessages from './messages'; -jest.unmock('@openedx/paragon'); -jest.unmock('@openedx/paragon/icons'); -jest.unmock('@edx/frontend-platform/i18n'); -jest.unmock('react'); jest.unmock('react-redux'); jest.unmock('hooks'); diff --git a/src/testUtils.js b/src/testUtils.jsx similarity index 94% rename from src/testUtils.js rename to src/testUtils.jsx index baf986a..c9d5bb2 100644 --- a/src/testUtils.js +++ b/src/testUtils.jsx @@ -1,4 +1,7 @@ import react from 'react'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { render } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import { StrictDict } from 'utils'; @@ -185,3 +188,9 @@ export class MockUseState { }); } } + +export const renderWithIntl = (component) => render( + + {component} + , +);