From ec3c25f54ac1e8c003b76c7eb95de6be5661826e Mon Sep 17 00:00:00 2001
From: Victor Navarro
Date: Fri, 13 Jun 2025 05:31:43 -0600
Subject: [PATCH] test: deprecate react-unit-test-utils part-3 (#430)
---
src/components/ConfirmModal.test.jsx | 20 ++-
.../BaseRenderers/PDFRenderer.test.jsx | 55 +++++--
.../BaseRenderers/TXTRenderer.test.jsx | 34 +++-
.../__snapshots__/PDFRenderer.test.jsx.snap | 137 ----------------
.../__snapshots__/TXTRenderer.test.jsx.snap | 9 --
.../Head/__snapshots__/index.test.jsx.snap | 14 --
src/components/Head/index.test.jsx | 47 ++++--
.../__snapshots__/index.test.jsx.snap | 30 ----
src/components/InfoPopover/index.test.jsx | 35 ++--
.../CriterionFeedback.test.jsx | 139 +++++-----------
.../RadioCriterion.test.jsx | 99 +++++-------
.../ReviewCriterion.test.jsx | 49 +++---
.../CriterionFeedback.test.jsx.snap | 80 ---------
.../RadioCriterion.test.jsx.snap | 121 --------------
.../ReviewCriterion.test.jsx.snap | 66 --------
.../__snapshots__/index.test.jsx.snap | 153 ------------------
.../CriterionContainer/index.test.jsx | 124 +++++++++-----
17 files changed, 326 insertions(+), 886 deletions(-)
delete mode 100644 src/components/FilePreview/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap
delete mode 100644 src/components/FilePreview/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap
delete mode 100644 src/components/Head/__snapshots__/index.test.jsx.snap
delete mode 100644 src/components/InfoPopover/__snapshots__/index.test.jsx.snap
delete mode 100644 src/containers/CriterionContainer/__snapshots__/CriterionFeedback.test.jsx.snap
delete mode 100644 src/containers/CriterionContainer/__snapshots__/RadioCriterion.test.jsx.snap
delete mode 100644 src/containers/CriterionContainer/__snapshots__/ReviewCriterion.test.jsx.snap
delete mode 100644 src/containers/CriterionContainer/__snapshots__/index.test.jsx.snap
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),
);