diff --git a/src/components/ConfirmModal.test.jsx b/src/components/ConfirmModal.test.jsx index 0311ed1..1ccce7b 100644 --- a/src/components/ConfirmModal.test.jsx +++ b/src/components/ConfirmModal.test.jsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import { ConfirmModal } from './ConfirmModal'; jest.unmock('@openedx/paragon'); @@ -14,12 +14,30 @@ describe('ConfirmModal', () => { onCancel: jest.fn().mockName('this.props.onCancel'), onConfirm: jest.fn().mockName('this.props.onConfirm'), }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should not render content when modal is closed', () => { const { queryByText } = render(); expect(queryByText(props.content)).toBeNull(); }); + it('should display content when modal is open', () => { const { getByText } = render(); expect(getByText(props.content)).toBeInTheDocument(); }); + + it('should call onCancel when cancel button is clicked', () => { + render(); + fireEvent.click(screen.getByText(props.cancelText)); + expect(props.onCancel).toHaveBeenCalledTimes(1); + }); + + it('should call onConfirm when confirm button is clicked', () => { + render(); + fireEvent.click(screen.getByText(props.confirmText)); + expect(props.onConfirm).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx b/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx index d4c996c..fc8cc80 100644 --- a/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx +++ b/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx @@ -1,20 +1,29 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - +import { Document, Page } from 'react-pdf'; +import { render } from '@testing-library/react'; +import PropTypes from 'prop-types'; import PDFRenderer from './PDFRenderer'; - import * as hooks from './pdfHooks'; jest.mock('react-pdf', () => ({ pdfjs: { GlobalWorkerOptions: {} }, - Document: () => 'Document', - Page: () => 'Page', + Document: jest.fn(), + Page: jest.fn(), })); +Document.mockImplementation((props) =>
{props.children}
); +Document.propTypes = { + children: PropTypes.node, +}; + +Page.mockImplementation(() =>
Page Content
); + jest.mock('./pdfHooks', () => ({ rendererHooks: jest.fn(), })); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); + describe('PDF Renderer Component', () => { const props = { url: 'some_url.pdf', @@ -33,25 +42,45 @@ describe('PDF Renderer Component', () => { onNextPageButtonClick: jest.fn().mockName('hooks.onNextPageButtonClick'), onPrevPageButtonClick: jest.fn().mockName('hooks.onPrevPageButtonClick'), hasNext: true, - hasPref: false, + hasPrev: false, }; beforeEach(() => { jest.clearAllMocks(); }); - describe('snapshots', () => { - test('first page, prev is disabled', () => { + + describe('rendering', () => { + it('should render the PDF document with navigation controls', () => { hooks.rendererHooks.mockReturnValue(hookProps); - expect(shallow().snapshot).toMatchSnapshot(); + const { getByTestId, getAllByText, container } = render(); + expect(getByTestId('pdf-document')).toBeInTheDocument(); + expect(getByTestId('pdf-page')).toBeInTheDocument(); + expect(container.querySelector('input[type="number"]')).toBeInTheDocument(); + expect(getAllByText(/Page/).length).toBeGreaterThan(0); + expect(getAllByText(`of ${hookProps.numPages}`).length).toBeGreaterThan(0); }); - test('on last page, next is disabled', () => { + + it('should have disabled previous button when on the first page', () => { + hooks.rendererHooks.mockReturnValue({ + ...hookProps, + hasPrev: false, + }); + + const { container } = render(); + const prevButton = container.querySelector('button[aria-label="previous pdf page"]'); + expect(prevButton).toBeDisabled(); + }); + + it('should have disabled next button when on the last page', () => { hooks.rendererHooks.mockReturnValue({ ...hookProps, - pageNumber: hookProps.numPages, hasNext: false, hasPrev: true, }); - expect(shallow().snapshot).toMatchSnapshot(); + + const { container } = render(); + const nextButton = container.querySelector('button[aria-label="next pdf page"]'); + expect(nextButton).toBeDisabled(); }); }); }); diff --git a/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx b/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx index 43f8224..46ee11f 100644 --- a/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx +++ b/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx @@ -1,23 +1,41 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - +import { render } from '@testing-library/react'; import TXTRenderer from './TXTRenderer'; jest.mock('./textHooks', () => { - const content = 'test-content'; + const mockRendererHooks = jest.fn().mockReturnValue({ content: 'test-content' }); return { - content, - rendererHooks: (args) => ({ content, rendererHooks: args }), + rendererHooks: mockRendererHooks, }; }); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); + +const textHooks = require('./textHooks'); + describe('TXT Renderer Component', () => { const props = { url: 'some_url.txt', onError: jest.fn().mockName('this.props.onError'), onSuccess: jest.fn().mockName('this.props.onSuccess'), }; - test('snapshot', () => { - expect(shallow().snapshot).toMatchSnapshot(); + + beforeEach(() => { + textHooks.rendererHooks.mockClear(); + }); + + it('renders the text content in a pre element', () => { + const { getByText, container } = render(); + expect(getByText('test-content')).toBeInTheDocument(); + expect(container.querySelector('pre')).toHaveClass('txt-renderer'); + }); + + it('passes the correct props to rendererHooks', () => { + render(); + expect(textHooks.rendererHooks).toHaveBeenCalledWith({ + url: props.url, + onError: props.onError, + onSuccess: props.onSuccess, + }); }); }); diff --git a/src/components/FilePreview/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap b/src/components/FilePreview/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap deleted file mode 100644 index 1cffd01..0000000 --- a/src/components/FilePreview/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap +++ /dev/null @@ -1,137 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PDF Renderer Component snapshots first page, prev is disabled 1`] = ` -
- -
- -
-
- - - - - Page - - - - of - 10 - - - - -
-`; - -exports[`PDF Renderer Component snapshots on last page, next is disabled 1`] = ` -
- -
- -
-
- - - - - Page - - - - of - 10 - - - - -
-`; diff --git a/src/components/FilePreview/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap b/src/components/FilePreview/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap deleted file mode 100644 index 7675f90..0000000 --- a/src/components/FilePreview/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TXT Renderer Component snapshot 1`] = ` -
-  test-content
-
-`; diff --git a/src/components/Head/__snapshots__/index.test.jsx.snap b/src/components/Head/__snapshots__/index.test.jsx.snap deleted file mode 100644 index ceb42a5..0000000 --- a/src/components/Head/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Head snapshot 1`] = ` - - - ORA staff grading | site-name - - - -`; diff --git a/src/components/Head/index.test.jsx b/src/components/Head/index.test.jsx index 977cc21..d1d8d55 100644 --- a/src/components/Head/index.test.jsx +++ b/src/components/Head/index.test.jsx @@ -1,25 +1,48 @@ -import React from 'react'; -import { getConfig } from '@edx/frontend-platform'; -import { shallow } from '@edx/react-unit-test-utils'; +import { render } from '@testing-library/react'; +import { Helmet } from 'react-helmet'; import Head from '.'; -jest.mock('react-helmet', () => ({ - Helmet: 'Helmet', +jest.mock('@edx/frontend-platform/i18n', () => ({ + useIntl: () => ({ + formatMessage: (message, values) => { + if (message.defaultMessage && values) { + return message.defaultMessage.replace('{siteName}', values.siteName); + } + return message.defaultMessage || message.id; + }, + }), + defineMessages: (messages) => messages, })); +jest.mock('react-helmet', () => ({ + Helmet: jest.fn(), +})); + +Helmet.mockImplementation(({ children }) =>
{children}
); + jest.mock('@edx/frontend-platform', () => ({ - getConfig: () => ({ + getConfig: jest.fn().mockReturnValue({ SITE_NAME: 'site-name', FAVICON_URL: 'favicon-url', }), })); -describe('Head', () => { - it('snapshot', () => { - const el = shallow(); - expect(el.snapshot).toMatchSnapshot(); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); - expect(el.instance.findByType('title')[0].el.children[0]).toContain(getConfig().SITE_NAME); - expect(el.instance.findByType('link')[0].props.href).toEqual(getConfig().FAVICON_URL); +describe('Head', () => { + it('should render page title with site name from config', () => { + const { container } = render(); + const titleElement = container.querySelector('title'); + expect(titleElement).toBeInTheDocument(); + expect(titleElement.textContent).toContain('ORA staff grading | site-name'); + }); + + it('should render favicon link with URL from config', () => { + const { container } = render(); + const faviconLink = container.querySelector('link[rel="shortcut icon"]'); + expect(faviconLink).toBeInTheDocument(); + expect(faviconLink.getAttribute('href')).toEqual('favicon-url'); + expect(faviconLink.getAttribute('type')).toEqual('image/x-icon'); }); }); diff --git a/src/components/InfoPopover/__snapshots__/index.test.jsx.snap b/src/components/InfoPopover/__snapshots__/index.test.jsx.snap deleted file mode 100644 index 89c14c1..0000000 --- a/src/components/InfoPopover/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Info Popover Component snapshot 1`] = ` - - -
- Children component -
-
- - } - placement="right-end" - trigger="focus" -> - -
-`; diff --git a/src/components/InfoPopover/index.test.jsx b/src/components/InfoPopover/index.test.jsx index f3987aa..d6682f0 100644 --- a/src/components/InfoPopover/index.test.jsx +++ b/src/components/InfoPopover/index.test.jsx @@ -1,23 +1,32 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - +import { render, fireEvent } from '@testing-library/react'; import { formatMessage } 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'); - let el; - beforeEach(() => { - el = shallow({child}); - }); - test('snapshot', () => { - expect(el.snapshot).toMatchSnapshot(); - }); + describe('Component', () => { - test('Test component render', () => { - expect(el.instance.children.length).toEqual(1); - expect(el.instance.findByTestId('esg-help-icon').length).toEqual(1); + it('renders the help icon button', () => { + const { getByTestId } = render( + + {child} + , + ); + expect(getByTestId('esg-help-icon')).toBeInTheDocument(); + }); + + it('calls onClick when the help icon is clicked', () => { + const { getByTestId } = render( + + {child} + , + ); + fireEvent.click(getByTestId('esg-help-icon')); + expect(onClick).toHaveBeenCalled(); }); }); }); diff --git a/src/containers/CriterionContainer/CriterionFeedback.test.jsx b/src/containers/CriterionContainer/CriterionFeedback.test.jsx index 741df7a..34f0130 100644 --- a/src/containers/CriterionContainer/CriterionFeedback.test.jsx +++ b/src/containers/CriterionContainer/CriterionFeedback.test.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; +import { render, fireEvent } from '@testing-library/react'; import { actions, selectors } from 'data/redux'; import { @@ -12,7 +12,6 @@ import { mapStateToProps, mapDispatchToProps, } from './CriterionFeedback'; -import messages from './messages'; jest.mock('data/redux/app/selectors', () => ({ rubric: { @@ -34,6 +33,9 @@ jest.mock('data/redux/grading/selectors', () => ({ }, })); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); + describe('Criterion Feedback', () => { const props = { intl: { formatMessage }, @@ -45,110 +47,49 @@ describe('Criterion Feedback', () => { setValue: jest.fn().mockName('this.props.setValue'), isInvalid: false, }; - let el; - beforeEach(() => { - el = shallow(); - el.instance.onChange = jest.fn().mockName('this.onChange'); - }); - describe('snapshot', () => { - test('is grading', () => { - expect(el.snapshot).toMatchSnapshot(); - }); - - test('is graded', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); - - test('feedback value is invalid', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); - - Object.values(feedbackRequirement).forEach((requirement) => { - test(`feedback is configured to ${requirement}`, () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); - }); - }); describe('component', () => { describe('render', () => { - test('is grading (the feedback input is not disabled)', () => { - expect(el.isEmptyRender()).toEqual(false); - const controlEl = el.instance.findByTestId('criterion-feedback-input')[0]; - expect(controlEl.props.disabled).toEqual(false); - expect(controlEl.props.value).toEqual(props.value); + it('shows a non-disabled input when grading', () => { + const { getByTestId } = render(); + const input = getByTestId('criterion-feedback-input'); + expect(input).toBeInTheDocument(); + expect(input).not.toBeDisabled(); + expect(input).toHaveValue(props.value); }); - test('is graded (the input is disabled)', () => { - el = shallow(); - const controlEl = el.instance.findByTestId('criterion-feedback-input')[0]; - expect(controlEl.props.disabled).toEqual(true); - expect(controlEl.props.value).toEqual(props.value); + + it('shows a disabled input when not grading', () => { + const { getByTestId } = render( + , + ); + const input = getByTestId('criterion-feedback-input'); + expect(input).toBeInTheDocument(); + expect(input).toBeDisabled(); + expect(input).toHaveValue(props.value); }); - test('is having invalid feedback (feedback get render)', () => { - el = shallow(); - const feedbackErrorEl = el.instance.findByTestId('criterion-feedback-error-msg'); - expect(feedbackErrorEl).toBeDefined(); + + it('displays an error message when feedback is invalid', () => { + const { getByTestId } = render(); + expect(getByTestId('criterion-feedback-error-msg')).toBeInTheDocument(); }); - test('is configure to disabled (the input does not get render)', () => { - el = shallow(); - expect(el.isEmptyRender()).toEqual(true); + + it('does not render anything when config is set to disabled', () => { + const { container } = render( + , + ); + expect(container.firstChild).toBeNull(); }); }); describe('behavior', () => { - test('onChange call set value', () => { - el = shallow(); - el.instance.findByTestId('criterion-feedback-input')[0].props.onChange({ - target: { - value: 'some value', - }, + it('calls setValue when input value changes', () => { + const { getByTestId } = render(); + const input = getByTestId('criterion-feedback-input'); + fireEvent.change(input, { target: { value: 'some value' } }); + expect(props.setValue).toHaveBeenCalledWith({ + value: 'some value', + orderNum: props.orderNum, }); - expect(props.setValue).toBeCalledTimes(1); - }); - }); - - describe('getter commentMessage', () => { - test('is grading', () => { - let commentMessage; - - el = shallow(); - commentMessage = el.instance.findByTestId('criterion-feedback-input')[0].props.floatingLabel; - expect(commentMessage).toContain( - messages.optional.defaultMessage, - ); - - el = shallow(); - commentMessage = el.instance.findByTestId('criterion-feedback-input')[0].props.floatingLabel; - expect(commentMessage).not.toContain( - messages.optional.defaultMessage, - ); - - expect(commentMessage).toContain( - messages.addComments.defaultMessage, - ); - }); - - test('is not grading', () => { - let commentMessage; - - el = shallow(); - commentMessage = el.instance.findByTestId('criterion-feedback-input')[0].props.floatingLabel; - expect(commentMessage).toContain( - messages.optional.defaultMessage, - ); - - el = shallow(); - commentMessage = el.instance.findByTestId('criterion-feedback-input')[0].props.floatingLabel; - expect(commentMessage).not.toContain( - messages.optional.defaultMessage, - ); - - expect(commentMessage).toContain( - messages.comments.defaultMessage, - ); }); }); }); @@ -160,17 +101,17 @@ describe('Criterion Feedback', () => { beforeEach(() => { mapped = mapStateToProps(testState, ownProps); }); - test('selectors.app.rubric.criterionFeedbackConfig', () => { + it('gets config from selectors.app.rubric.criterionFeedbackConfig', () => { expect(mapped.config).toEqual( selectors.app.rubric.criterionFeedbackConfig(testState, ownProps), ); }); - test('selector.grading.selected.criterionFeedback', () => { + it('gets value from selectors.grading.selected.criterionFeedback', () => { expect(mapped.value).toEqual( selectors.grading.selected.criterionFeedback(testState, ownProps), ); }); - test('selector.grading.validation.criterionFeedbackIsInvalid', () => { + it('gets isInvalid from selectors.grading.validation.criterionFeedbackIsInvalid', () => { expect(mapped.isInvalid).toEqual( selectors.grading.validation.criterionFeedbackIsInvalid( testState, @@ -181,7 +122,7 @@ describe('Criterion Feedback', () => { }); describe('mapDispatchToProps', () => { - test('maps actions.grading.setCriterionFeedback to setValue prop', () => { + it('maps actions.grading.setCriterionFeedback to setValue prop', () => { expect(mapDispatchToProps.setValue).toEqual( actions.grading.setCriterionFeedback, ); diff --git a/src/containers/CriterionContainer/RadioCriterion.test.jsx b/src/containers/CriterionContainer/RadioCriterion.test.jsx index 1037415..e0a07c4 100644 --- a/src/containers/CriterionContainer/RadioCriterion.test.jsx +++ b/src/containers/CriterionContainer/RadioCriterion.test.jsx @@ -1,5 +1,4 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; +import { render } from '@testing-library/react'; import { actions, selectors } from 'data/redux'; import { formatMessage } from 'testUtils'; @@ -29,6 +28,9 @@ jest.mock('data/redux/grading/selectors', () => ({ }, })); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); + describe('Radio Criterion Container', () => { const props = { intl: { formatMessage }, @@ -55,70 +57,47 @@ describe('Radio Criterion Container', () => { }, ], }, - data: 'selected radio option', + data: 'option name', setCriterionOption: jest.fn().mockName('this.props.setCriterionOption'), isInvalid: false, }; - let el; - beforeEach(() => { - el = shallow(); - el.instance.onChange = jest.fn().mockName('this.onChange'); - }); - describe('snapshot', () => { - test('is grading', () => { - expect(el.snapshot).toMatchSnapshot(); - }); + describe('component rendering', () => { + it('should render radio buttons that are enabled when in grading mode', () => { + const { container } = render(); - test('is not grading', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); + const radioButtons = container.querySelectorAll('input[type="radio"]'); + expect(radioButtons.length).toEqual(props.config.options.length); - test('radio contain invalid response', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); - }); - - describe('component', () => { - describe('rendering', () => { - test('is grading (all options are not disabled)', () => { - expect(el.isEmptyRender()).toEqual(false); - const optionsEl = el.instance.children; - expect(optionsEl.length).toEqual(props.config.options.length); - optionsEl.forEach((optionEl) => expect(optionEl.props.disabled).toEqual(false)); - }); - - test('is not grading (all options are disabled)', () => { - el = shallow(); - expect(el.isEmptyRender()).toEqual(false); - const optionsEl = el.instance.children; - expect(optionsEl.length).toEqual(props.config.options.length); - optionsEl.forEach((optionEl) => expect(optionEl.props.disabled).toEqual(true)); - }); - - test('radio contain invalid response (error response get render)', () => { - el = shallow(); - expect(el.isEmptyRender()).toEqual(false); - const radioErrorEl = el.instance.children[2]; - expect(radioErrorEl.props.type).toBe('invalid'); - expect(radioErrorEl.props.className).toBe('feedback-error-msg'); - expect(radioErrorEl).toBeTruthy(); + radioButtons.forEach(button => { + expect(button).not.toBeDisabled(); }); }); - describe('behavior', () => { - test('onChange call set crition option', () => { - el = shallow(); - el.instance.children[0].props.onChange({ - target: { - value: 'some value', - }, - }); - expect(props.setCriterionOption).toBeCalledTimes(1); + it('should render radio buttons that are disabled when not in grading mode', () => { + const { container } = render(); + + const radioButtons = container.querySelectorAll('input[type="radio"]'); + expect(radioButtons.length).toEqual(props.config.options.length); + + radioButtons.forEach(button => { + expect(button).toBeDisabled(); }); }); + + it('should render an error message when the criterion is invalid', () => { + const { container } = render(); + + 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 errorMessage = container.querySelector('.feedback-error-msg'); + expect(errorMessage).not.toBeInTheDocument(); + }); }); describe('mapStateToProps', () => { @@ -128,18 +107,20 @@ describe('Radio Criterion Container', () => { beforeEach(() => { mapped = mapStateToProps(testState, ownProps); }); - test('selectors.app.rubric.criterionConfig', () => { + + it('should properly map config from rubric criterion config selector', () => { expect(mapped.config).toEqual( selectors.app.rubric.criterionConfig(testState, ownProps), ); }); - test('selectors.grading.selected.criterionSelectedOption', () => { + it('should properly map data from selected criterion option selector', () => { expect(mapped.data).toEqual( selectors.grading.selected.criterionSelectedOption(testState, ownProps), ); }); - test('selectors.grading.validation.criterionSelectedOptionIsInvalid', () => { + + it('should properly map isInvalid from criterion validation selector', () => { expect(mapped.isInvalid).toEqual( selectors.grading.validation.criterionSelectedOptionIsInvalid(testState, ownProps), ); @@ -147,7 +128,7 @@ describe('Radio Criterion Container', () => { }); describe('mapDispatchToProps', () => { - test('maps actions.grading.setCriterionFeedback to setValue prop', () => { + it('should map setCriterionOption action to props', () => { expect(mapDispatchToProps.setCriterionOption).toEqual( actions.grading.setCriterionOption, ); diff --git a/src/containers/CriterionContainer/ReviewCriterion.test.jsx b/src/containers/CriterionContainer/ReviewCriterion.test.jsx index 2af5ea4..9dc6d8d 100644 --- a/src/containers/CriterionContainer/ReviewCriterion.test.jsx +++ b/src/containers/CriterionContainer/ReviewCriterion.test.jsx @@ -1,9 +1,8 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; +import { render } from '@testing-library/react'; import { selectors } from 'data/redux'; import { ReviewCriterion, mapStateToProps } from './ReviewCriterion'; -import messages from './messages'; jest.mock('data/redux/app/selectors', () => ({ rubric: { @@ -20,7 +19,10 @@ jest.mock('data/redux/grading/selectors', () => ({ }, })); -describe('Review Crition Container', () => { +jest.unmock('@openedx/paragon'); +jest.unmock('react'); + +describe('Review Criterion Container', () => { const props = { orderNum: 1, config: { @@ -50,29 +52,20 @@ describe('Review Crition Container', () => { }, }; - let el; - beforeEach(() => { - el = shallow(); - }); - test('snapshot', () => { - expect(el.snapshot).toMatchSnapshot(); - }); - describe('component', () => { - test('rendering (everything show up)', () => { - expect(el.isEmptyRender()).toEqual(false); - const optionsEl = el.instance.findByTestId('criteria-option'); - expect(optionsEl.length).toEqual(props.config.options.length); - optionsEl.forEach((optionEl, i) => { - const option = props.config.options[i]; - expect(optionEl.props.key).toEqual(option.name); - expect(optionEl.findByTestId('option-label')[0].children[0].el).toEqual( - option.label, - ); - expect(optionEl.findByTestId('option-points')[0].children[0].props).toEqual({ - ...messages.optionPoints, - values: { points: option.points }, - }); + it('renders all criteria options with correct labels and points', () => { + const { getAllByTestId } = render(); + + const optionsElements = getAllByTestId('criteria-option'); + expect(optionsElements.length).toEqual(props.config.options.length); + + props.config.options.forEach((option, index) => { + const optionElement = optionsElements[index]; + const labelElement = optionElement.querySelector('[data-testid="option-label"]'); + const pointsElement = optionElement.querySelector('[data-testid="option-points"]'); + + expect(labelElement.textContent).toEqual(option.label); + expect(pointsElement.textContent).toEqual('FormattedMessage'); }); }); }); @@ -81,16 +74,18 @@ describe('Review Crition Container', () => { const testState = { arbitrary: 'some data' }; const ownProps = { orderNum: props.orderNum }; let mapped; + beforeEach(() => { mapped = mapStateToProps(testState, ownProps); }); - test('selectors.app.rubric.criterionConfig', () => { + + it('should map criterion config from state', () => { expect(mapped.config).toEqual( selectors.app.rubric.criterionConfig(testState, ownProps), ); }); - test('selectors.grading.selected.criterionGradeData', () => { + it('should map criterion grade data from state', () => { expect(mapped.data).toEqual( selectors.grading.selected.criterionGradeData(testState, ownProps), ); diff --git a/src/containers/CriterionContainer/__snapshots__/CriterionFeedback.test.jsx.snap b/src/containers/CriterionContainer/__snapshots__/CriterionFeedback.test.jsx.snap deleted file mode 100644 index 1354387..0000000 --- a/src/containers/CriterionContainer/__snapshots__/CriterionFeedback.test.jsx.snap +++ /dev/null @@ -1,80 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Criterion Feedback snapshot feedback is configured to disabled 1`] = `null`; - -exports[`Criterion Feedback snapshot feedback is configured to optional 1`] = ` - - - -`; - -exports[`Criterion Feedback snapshot feedback is configured to required 1`] = ` - - - -`; - -exports[`Criterion Feedback snapshot feedback value is invalid 1`] = ` - - - - The feedback is required - - -`; - -exports[`Criterion Feedback snapshot is graded 1`] = ` - - - -`; - -exports[`Criterion Feedback snapshot is grading 1`] = ` - - - -`; diff --git a/src/containers/CriterionContainer/__snapshots__/RadioCriterion.test.jsx.snap b/src/containers/CriterionContainer/__snapshots__/RadioCriterion.test.jsx.snap deleted file mode 100644 index de94cdb..0000000 --- a/src/containers/CriterionContainer/__snapshots__/RadioCriterion.test.jsx.snap +++ /dev/null @@ -1,121 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Radio Criterion Container snapshot is grading 1`] = ` - - - this label - - - this label 2 - - -`; - -exports[`Radio Criterion Container snapshot is not grading 1`] = ` - - - this label - - - this label 2 - - -`; - -exports[`Radio Criterion Container snapshot radio contain invalid response 1`] = ` - - - this label - - - this label 2 - - - Rubric selection is required - - -`; diff --git a/src/containers/CriterionContainer/__snapshots__/ReviewCriterion.test.jsx.snap b/src/containers/CriterionContainer/__snapshots__/ReviewCriterion.test.jsx.snap deleted file mode 100644 index 60210db..0000000 --- a/src/containers/CriterionContainer/__snapshots__/ReviewCriterion.test.jsx.snap +++ /dev/null @@ -1,66 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Review Crition Container snapshot 1`] = ` -
-
-
- - this label - - - - -
-
-
-
- - this label 2 - - - - -
-
-
-`; diff --git a/src/containers/CriterionContainer/__snapshots__/index.test.jsx.snap b/src/containers/CriterionContainer/__snapshots__/index.test.jsx.snap deleted file mode 100644 index 806c1a9..0000000 --- a/src/containers/CriterionContainer/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,153 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Criterion Container snapshot is graded and is not grading 1`] = ` - - - - prompt - - -
- - this label - -
- explanation -
-
- - this label 2 - -
- explanation 2 -
-
-
-
- -
- -
-`; - -exports[`Criterion Container snapshot is ungraded and is grading 1`] = ` - - - - prompt - - -
- - this label - -
- explanation -
-
- - this label 2 - -
- explanation 2 -
-
-
-
- -
- -
-`; - -exports[`Criterion Container snapshot is ungraded and is not grading 1`] = ` - - - - prompt - - -
- - this label - -
- explanation -
-
- - this label 2 - -
- explanation 2 -
-
-
-
- -
- -
-`; diff --git a/src/containers/CriterionContainer/index.test.jsx b/src/containers/CriterionContainer/index.test.jsx index 6cc2ceb..0df1172 100644 --- a/src/containers/CriterionContainer/index.test.jsx +++ b/src/containers/CriterionContainer/index.test.jsx @@ -1,15 +1,53 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; +import { render, screen } from '@testing-library/react'; +import PropTypes from 'prop-types'; import { selectors } from 'data/redux'; import { gradeStatuses } from 'data/services/lms/constants'; import { CriterionContainer, mapStateToProps } from '.'; -jest.mock('components/InfoPopover', () => 'InfoPopover'); -jest.mock('./RadioCriterion', () => 'RadioCriterion'); -jest.mock('./CriterionFeedback', () => 'CriterionFeedback'); -jest.mock('./ReviewCriterion', () => 'ReviewCriterion'); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); + +const MockRadioCriterion = ({ orderNum, isGrading }) => ( +
+ RadioCriterion Component (orderNum={orderNum}, isGrading={String(isGrading)}) +
+); + +MockRadioCriterion.propTypes = { + orderNum: PropTypes.number.isRequired, + isGrading: PropTypes.bool.isRequired, +}; + +const MockReviewCriterion = ({ orderNum }) => ( +
+ ReviewCriterion Component (orderNum={orderNum}) +
+); + +MockReviewCriterion.propTypes = { + orderNum: PropTypes.number.isRequired, +}; + +const MockCriterionFeedback = ({ orderNum, isGrading }) => ( +
+ CriterionFeedback Component (orderNum={orderNum}, isGrading={String(isGrading)}) +
+); + +MockCriterionFeedback.propTypes = { + orderNum: PropTypes.number.isRequired, + isGrading: PropTypes.bool.isRequired, +}; + +const MockInfoPopover = ({ children }) => ( +
{children}
+); + +MockInfoPopover.propTypes = { + children: PropTypes.node.isRequired, +}; jest.mock('data/redux/app/selectors', () => ({ rubric: { @@ -18,12 +56,18 @@ jest.mock('data/redux/app/selectors', () => ({ })), }, })); + jest.mock('data/redux/grading/selectors', () => ({ selected: { gradeStatus: jest.fn((...args) => ({ selectedGradeStatus: args })), }, })); +jest.mock('./RadioCriterion', () => jest.fn((props) => MockRadioCriterion(props))); +jest.mock('./ReviewCriterion', () => jest.fn((props) => MockReviewCriterion(props))); +jest.mock('./CriterionFeedback', () => jest.fn((props) => MockCriterionFeedback(props))); +jest.mock('components/InfoPopover', () => jest.fn((props) => MockInfoPopover(props))); + describe('Criterion Container', () => { const props = { isGrading: true, @@ -51,53 +95,43 @@ describe('Criterion Container', () => { }, gradeStatus: gradeStatuses.ungraded, }; - let el; - beforeEach(() => { - el = shallow(); - }); - describe('snapshot', () => { - test('is ungraded and is grading', () => { - expect(el.snapshot).toMatchSnapshot(); + describe('component rendering', () => { + it('displays the criterion prompt', () => { + render(); + expect(screen.getByText('prompt')).toBeInTheDocument(); }); - test('is ungraded and is not grading', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); + it('displays all option explanations in the info popover', () => { + render(); + const infoPopover = screen.getByTestId('info-popover'); + expect(infoPopover).toHaveTextContent('explanation'); + expect(infoPopover).toHaveTextContent('explanation 2'); + expect(infoPopover).toHaveTextContent('this label'); + expect(infoPopover).toHaveTextContent('this label 2'); }); - test('is graded and is not grading', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); - }); - - describe('component', () => { - test('rendering and all of the option show up', () => { - expect(el.isEmptyRender()).toEqual(false); - const optionsEl = el.instance.findByTestId('help-popover-option'); - expect(optionsEl.length).toEqual(props.config.options.length); - optionsEl.forEach((optionEl, i) => { - expect(optionEl.props.key).toEqual(props.config.options[i].name); - expect(optionEl.children[2].el).toContain(props.config.options[i].explanation); - }); + it('renders RadioCriterion when is ungraded and is grading', () => { + render(); + expect(screen.getByTestId('radio-criterion-component')).toBeInTheDocument(); + expect(screen.queryByTestId('review-criterion-component')).not.toBeInTheDocument(); }); - test('is ungraded and is grading (Radio criterion get render)', () => { - const rubricCriteria = el.instance.findByTestId('rubric-criteria')[0]; - expect(rubricCriteria.children[0].el.type).toEqual('RadioCriterion'); + it('renders ReviewCriterion when is ungraded and is not grading', () => { + render(); + expect(screen.getByTestId('review-criterion-component')).toBeInTheDocument(); + expect(screen.queryByTestId('radio-criterion-component')).not.toBeInTheDocument(); }); - test('is ungraded and is not grading (Review criterion get render)', () => { - el = shallow(); - const rubricCriteria = el.instance.findByTestId('rubric-criteria')[0]; - expect(rubricCriteria.children[0].el.type).toEqual('ReviewCriterion'); + it('renders RadioCriterion when is graded and is not grading', () => { + render(); + expect(screen.getByTestId('radio-criterion-component')).toBeInTheDocument(); + expect(screen.queryByTestId('review-criterion-component')).not.toBeInTheDocument(); }); - test('is graded and is not grading (Radio criterion get render)', () => { - el = shallow(); - const rubricCriteria = el.instance.findByTestId('rubric-criteria')[0]; - expect(rubricCriteria.children[0].el.type).toEqual('RadioCriterion'); + it('renders CriterionFeedback component', () => { + render(); + expect(screen.getByTestId('criterion-feedback-component')).toBeInTheDocument(); }); }); @@ -105,16 +139,18 @@ describe('Criterion Container', () => { const testState = { arbitraryState: 'some data' }; const ownProps = { orderNum: props.orderNum }; let mapped; + beforeEach(() => { mapped = mapStateToProps(testState, ownProps); }); - test('selectors.app.rubric.criterionConfig', () => { + + it('maps rubric criterion config to props', () => { expect(mapped.config).toEqual( selectors.app.rubric.criterionConfig(testState, ownProps), ); }); - test('selectors.grading.selected.gradeStatus', () => { + it('maps grading status to props', () => { expect(mapped.gradeStatus).toEqual( selectors.grading.selected.gradeStatus(testState), );