diff --git a/src/components/InfoPopover/index.test.jsx b/src/components/InfoPopover/index.test.jsx
index ba408a4..2fab2a3 100644
--- a/src/components/InfoPopover/index.test.jsx
+++ b/src/components/InfoPopover/index.test.jsx
@@ -1,4 +1,5 @@
-import { render, fireEvent } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { InfoPopover } from '.';
jest.unmock('@openedx/paragon');
@@ -18,13 +19,14 @@ describe('Info Popover Component', () => {
expect(getByTestId('esg-help-icon')).toBeInTheDocument();
});
- it('calls onClick when the help icon is clicked', () => {
- const { getByTestId } = render(
+ it('calls onClick when the help icon is clicked', async () => {
+ render(
{child}
,
);
- fireEvent.click(getByTestId('esg-help-icon'));
+ const user = userEvent.setup();
+ await user.click(screen.getByTestId('esg-help-icon'));
expect(onClick).toHaveBeenCalled();
});
});
diff --git a/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx b/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx
index cf92f36..d5f82b8 100644
--- a/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx
+++ b/src/containers/ReviewModal/ReviewErrors/LockErrors.test.jsx
@@ -1,6 +1,8 @@
import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
+import { render, 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';
@@ -9,53 +11,94 @@ import {
mapStateToProps,
} from './LockErrors';
+jest.unmock('react');
+jest.unmock('@openedx/paragon');
+jest.unmock('@edx/frontend-platform/i18n');
+
jest.mock('data/redux', () => ({
selectors: {
requests: {
- errorStatus: (...args) => ({ errorStatus: args }),
- isFailed: (...args) => ({ isFailed: args }),
+ errorStatus: jest.fn(),
+ isFailed: jest.fn(),
},
},
}));
-let el;
-jest.mock('./ReviewError', () => 'ReviewError');
-
-const requestKey = RequestKeys.setLock;
+const renderWithIntl = (component) => render(
+
+ {component}
+ ,
+);
describe('LockErrors component', () => {
- const props = {
- isFailed: true,
- };
- describe('component', () => {
- beforeEach(() => {
- el = shallow();
- el.instance.dismissError = jest.fn().mockName('this.dismissError');
- });
- describe('snapshots', () => {
- test('no failure', () => {
- expect(el.snapshot).toMatchSnapshot();
- });
- test('snapshot: error with bad request', () => {
- el = shallow();
- expect(el.snapshot).toMatchSnapshot();
- });
- test('snapshot: error with conflicted lock', () => {
- el = shallow();
- expect(el.snapshot).toMatchSnapshot();
- });
+ describe('when not failed', () => {
+ it('renders nothing when isFailed is false', () => {
+ const { container } = renderWithIntl(
+ ,
+ );
+ expect(container.firstChild).toBeNull();
});
});
- describe('mapStateToProps', () => {
- let mapped;
- const testState = { some: 'test-state' };
- beforeEach(() => {
- mapped = mapStateToProps(testState);
- });
- test('errorStatus loads from requests.errorStatus(setLock)', () => {
- expect(mapped.errorStatus).toEqual(
- selectors.requests.errorStatus(testState, { requestKey }),
+
+ describe('when failed', () => {
+ it('renders bad request error when errorStatus is badRequest', () => {
+ renderWithIntl(
+ ,
);
+
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ const errorMessages = screen.getAllByText('Invalid request. Please check your input.');
+ expect(errorMessages).toHaveLength(2);
+ });
+
+ it('renders conflict error when errorStatus is conflict', () => {
+ renderWithIntl(
+ ,
+ );
+
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ const errorMessages = screen.getAllByText('The lock owned by another user');
+ expect(errorMessages).toHaveLength(2);
+ });
+
+ it('renders bad request error by default when no errorStatus provided', () => {
+ renderWithIntl(
+ ,
+ );
+
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ const errorMessages = screen.getAllByText('Invalid request. Please check your input.');
+ expect(errorMessages).toHaveLength(2);
+ });
+ });
+
+ describe('mapStateToProps', () => {
+ const testState = { some: 'test-state' };
+ const requestKey = RequestKeys.setLock;
+
+ beforeEach(() => {
+ selectors.requests.isFailed.mockReturnValue(true);
+ selectors.requests.errorStatus.mockReturnValue(ErrorStatuses.badRequest);
+ });
+
+ it('maps isFailed from requests selector', () => {
+ const mapped = mapStateToProps(testState);
+
+ expect(selectors.requests.isFailed).toHaveBeenCalledWith(testState, { requestKey });
+ expect(mapped.isFailed).toBe(true);
+ });
+
+ it('maps errorStatus from requests selector', () => {
+ const mapped = mapStateToProps(testState);
+
+ expect(selectors.requests.errorStatus).toHaveBeenCalledWith(testState, { requestKey });
+ expect(mapped.errorStatus).toBe(ErrorStatuses.badRequest);
});
});
});
diff --git a/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx b/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx
index a51d513..6a315ed 100644
--- a/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx
+++ b/src/containers/ReviewModal/ReviewErrors/ReviewError.test.jsx
@@ -1,82 +1,112 @@
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-
-import { Button } from '@openedx/paragon';
-import { FormattedMessage } from '@edx/frontend-platform/i18n';
-
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
import ReviewError from './ReviewError';
-let el;
-const messages = {
- heading: {
- id: 'test-header-message',
- defaultMessage: 'Test Header Message',
- },
- cancel: {
- id: 'test-cancel-message',
- defaultMessage: 'Test Cancel Message',
- },
- confirm: {
- id: 'test-confirm-message',
- defaultMessage: 'Test Confirm Message',
- },
-};
-const cancel = {
- onClick: jest.fn().mockName('this.props.cancel.onClick'),
- message: messages.cancel,
-};
-const confirm = {
- onClick: jest.fn().mockName('this.props.confirm.onClick'),
- message: messages.confirm,
-};
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+jest.unmock('@edx/frontend-platform/i18n');
-const confirmBtn = (
-
-);
-
-const cancelBtn = (
-
+const renderWithIntl = (component) => render(
+
+ {component}
+ ,
);
describe('ReviewError component', () => {
- describe('component', () => {
- const props = {
- headingMessage: messages.heading,
- };
- const children =
Test Children
;
- describe('snapshots', () => {
- test('no actions', () => {
- el = shallow({children});
- expect(el.snapshot).toMatchSnapshot();
- const { actions } = el.instance.props;
- expect(actions).toEqual([]);
- });
- test('cancel only', () => {
- el = shallow({children});
- expect(el.snapshot).toMatchSnapshot();
- const { actions } = el.instance.props;
- expect(actions.length).toEqual(1);
- expect(actions[0]).toEqual(cancelBtn);
- });
- test('confirm only', () => {
- el = shallow({children});
- expect(el.snapshot).toMatchSnapshot();
- const { actions } = el.instance.props;
- expect(actions.length).toEqual(1);
- expect(actions[0]).toEqual(confirmBtn);
- });
- test('cancel and confirm', () => {
- el = shallow({children});
- expect(el.snapshot).toMatchSnapshot();
- const { actions } = el.instance.props;
- expect(actions.length).toEqual(2);
- expect(actions[0]).toEqual(cancelBtn);
- expect(actions[1]).toEqual(confirmBtn);
- });
- });
+ const messages = {
+ heading: {
+ id: 'test-header-message',
+ defaultMessage: 'Test Header Message',
+ },
+ cancel: {
+ id: 'test-cancel-message',
+ defaultMessage: 'Test Cancel Message',
+ },
+ confirm: {
+ id: 'test-confirm-message',
+ defaultMessage: 'Test Confirm Message',
+ },
+ };
+
+ const cancel = {
+ onClick: jest.fn(),
+ message: messages.cancel,
+ };
+
+ const confirm = {
+ onClick: jest.fn(),
+ message: messages.confirm,
+ };
+
+ const defaultProps = {
+ headingMessage: messages.heading,
+ };
+
+ const children = 'Test error content';
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders alert with heading and children', () => {
+ renderWithIntl({children});
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ expect(screen.getByText('Test Header Message')).toBeInTheDocument();
+ expect(screen.getByText('Test error content')).toBeInTheDocument();
+ });
+
+ it('renders with default danger variant', () => {
+ renderWithIntl({children});
+ const alert = screen.getByRole('alert');
+ expect(alert).toHaveClass('alert-danger');
+ });
+
+ it('renders with custom variant', () => {
+ renderWithIntl({children});
+ const alert = screen.getByRole('alert');
+ expect(alert).toHaveClass('alert-warning');
+ });
+
+ it('renders cancel button when cancel action provided', () => {
+ renderWithIntl({children});
+ const cancelButton = screen.getByText('Test Cancel Message');
+ expect(cancelButton).toBeInTheDocument();
+ expect(cancelButton.closest('button')).toHaveClass('btn-outline-primary');
+ });
+
+ it('renders confirm button when confirm action provided', () => {
+ renderWithIntl({children});
+ const confirmButton = screen.getByText('Test Confirm Message');
+ expect(confirmButton).toBeInTheDocument();
+ expect(confirmButton.closest('button')).toHaveClass('btn-primary');
+ });
+
+ it('renders both cancel and confirm buttons when both actions provided', () => {
+ renderWithIntl({children});
+ expect(screen.getByText('Test Cancel Message')).toBeInTheDocument();
+ expect(screen.getByText('Test Confirm Message')).toBeInTheDocument();
+ });
+
+ it('calls cancel onClick when cancel button is clicked', async () => {
+ renderWithIntl({children});
+ const user = userEvent.setup();
+ const cancelButton = screen.getByText('Test Cancel Message');
+ await user.click(cancelButton);
+ expect(cancel.onClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls confirm onClick when confirm button is clicked', async () => {
+ renderWithIntl({children});
+ const user = userEvent.setup();
+ const confirmButton = screen.getByText('Test Confirm Message');
+ await user.click(confirmButton);
+ expect(confirm.onClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('applies custom className when provided', () => {
+ renderWithIntl({children});
+ const alert = screen.getByRole('alert');
+ expect(alert).toHaveClass('custom-class');
});
});
diff --git a/src/containers/ReviewModal/ReviewErrors/__snapshots__/LockErrors.test.jsx.snap b/src/containers/ReviewModal/ReviewErrors/__snapshots__/LockErrors.test.jsx.snap
deleted file mode 100644
index fd1121a..0000000
--- a/src/containers/ReviewModal/ReviewErrors/__snapshots__/LockErrors.test.jsx.snap
+++ /dev/null
@@ -1,58 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`LockErrors component component snapshots no failure 1`] = `
-
-
-
-`;
-
-exports[`LockErrors component component snapshots snapshot: error with bad request 1`] = `
-
-
-
-`;
-
-exports[`LockErrors component component snapshots snapshot: error with conflicted lock 1`] = `
-
-
-
-`;
diff --git a/src/containers/ReviewModal/ReviewErrors/__snapshots__/ReviewError.test.jsx.snap b/src/containers/ReviewModal/ReviewErrors/__snapshots__/ReviewError.test.jsx.snap
deleted file mode 100644
index 479aac2..0000000
--- a/src/containers/ReviewModal/ReviewErrors/__snapshots__/ReviewError.test.jsx.snap
+++ /dev/null
@@ -1,124 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ReviewError component component snapshots cancel and confirm 1`] = `
-
-
- ,
- ,
- ]
- }
- className=""
- variant="danger"
->
-
-
-
-
-
- Test Children
-
-
-
-`;
-
-exports[`ReviewError component component snapshots cancel only 1`] = `
-
-
- ,
- ]
- }
- className=""
- variant="danger"
->
-
-
-
-
-
- Test Children
-
-
-
-`;
-
-exports[`ReviewError component component snapshots confirm only 1`] = `
-
-
- ,
- ]
- }
- className=""
- variant="danger"
->
-
-
-
-
-
- Test Children
-
-
-
-`;
-
-exports[`ReviewError component component snapshots no actions 1`] = `
-
-
-
-
-
-
- Test Children
-
-
-
-`;
diff --git a/src/containers/Rubric/RubricFeedback.test.jsx b/src/containers/Rubric/RubricFeedback.test.jsx
index 0681577..038e377 100644
--- a/src/containers/Rubric/RubricFeedback.test.jsx
+++ b/src/containers/Rubric/RubricFeedback.test.jsx
@@ -1,162 +1,139 @@
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-
+import { render, 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 { feedbackRequirement, gradeStatuses } from 'data/services/lms/constants';
+import { RubricFeedback, mapDispatchToProps, mapStateToProps } from './RubricFeedback';
-import {
- RubricFeedback,
- mapDispatchToProps,
- mapStateToProps,
-} from './RubricFeedback';
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+jest.unmock('@edx/frontend-platform/i18n');
-jest.mock('components/InfoPopover', () => 'InfoPopover');
-
-jest.mock('data/redux/app/selectors', () => ({
- rubric: {
- feedbackConfig: jest.fn((...args) => ({
- rubricFeedbackConfig: args,
- })),
- feedbackPrompt: jest.fn((...args) => ({
- rubricFeedbackPrompt: args,
- })),
- },
-}));
-jest.mock('data/redux/grading/selectors', () => ({
- selected: {
- overallFeedback: jest.fn((...args) => ({
- selectedOverallFeedback: args,
- })),
- isGrading: jest.fn((...args) => ({ isGrading: args })),
- },
- validation: {
- overallFeedbackIsInvalid: jest.fn((...args) => ({
- selectedOverallFeedbackIsInvalid: args,
- })),
+jest.mock('data/redux', () => ({
+ actions: {
+ grading: { setRubricFeedback: jest.fn() },
+ },
+ selectors: {
+ app: {
+ rubric: {
+ feedbackConfig: jest.fn((state) => state.config || 'config string'),
+ feedbackPrompt: jest.fn((state) => state.feedbackPrompt || 'feedback prompt'),
+ },
+ },
+ grading: {
+ selected: {
+ overallFeedback: jest.fn((state) => state.value || 'some value'),
+ isGrading: jest.fn((state) => (state.isGrading !== undefined ? state.isGrading : true)),
+ },
+ validation: {
+ overallFeedbackIsInvalid: jest.fn((state) => state.isInvalid || false),
+ },
+ },
},
}));
+const renderWithIntl = (component) => render(
+
+ {component}
+ ,
+);
+
describe('Rubric Feedback component', () => {
- const props = {
+ const defaultProps = {
config: 'config string',
isGrading: true,
value: 'some value',
isInvalid: false,
- feedbackPrompt: 'feedback prompt',
gradeStatus: gradeStatuses.ungraded,
- setValue: jest.fn().mockName('this.props.setValue'),
+ setValue: jest.fn(),
+ intl: {
+ formatMessage: jest.fn((message) => message.defaultMessage),
+ },
};
- let el;
beforeEach(() => {
- el = shallow();
- });
- 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();
- });
-
- test('is configure to disabled', () => {
- el = shallow();
- expect(el.snapshot).toMatchSnapshot();
- });
+ jest.clearAllMocks();
});
- describe('component', () => {
- describe('render', () => {
- test('is grading (everything show up and the input is editable)', () => {
- expect(el.isEmptyRender()).toEqual(false);
- const input = el.instance.children[1];
- expect(input.props.disabled).toEqual(false);
- expect(input.props.value).toEqual(props.value);
- });
+ it('should render feedback form when config is not disabled', () => {
+ const { getByText } = renderWithIntl();
+ expect(getByText('Overall comments')).toBeInTheDocument();
+ });
- test('is graded (the input are disabled)', () => {
- el = shallow();
- expect(el.isEmptyRender()).toEqual(false);
- const input = el.instance.children[1];
- expect(input.props.disabled).toEqual(true);
- expect(input.props.value).toEqual(props.value);
- });
+ it('should not render when config is disabled', () => {
+ const props = { ...defaultProps, config: feedbackRequirement.disabled };
+ const { container } = renderWithIntl();
+ expect(container.firstChild).toBeNull();
+ });
- test('is having invalid feedback (feedback get render)', () => {
- el = shallow();
- const feedbackErrorEl = el.instance.children[2];
- expect(feedbackErrorEl.props.type).toBe('invalid');
- expect(feedbackErrorEl.props.className).toBe('feedback-error-msg');
- expect(feedbackErrorEl).toBeTruthy();
- });
+ it('should display feedback prompt in info popover', async () => {
+ const { getByText, getByTestId } = renderWithIntl();
+ const user = userEvent.setup();
+ const infoIcon = getByTestId('esg-help-icon');
+ await user.click(infoIcon);
+ expect(getByText(defaultProps.value)).toBeInTheDocument();
+ });
- test('is configure to disabled (this input does not get render)', () => {
- el = shallow();
- expect(el.isEmptyRender()).toEqual(true);
- });
- });
- describe('behavior', () => {
- test('onChange set value', () => {
- el = shallow();
- el.instance.children[1].props.onChange({
- target: {
- value: 'some value',
- },
- });
- expect(props.setValue).toBeCalledTimes(1);
- });
- });
+ it('should render textarea with correct value', () => {
+ renderWithIntl();
+ const textarea = screen.getByRole('textbox');
+ expect(textarea).toHaveValue(defaultProps.value);
+ });
+
+ it('should enable textarea when isGrading is true', () => {
+ renderWithIntl();
+ const textarea = screen.getByRole('textbox');
+ expect(textarea).not.toBeDisabled();
+ });
+
+ it('should disable textarea when isGrading is false', () => {
+ const props = { ...defaultProps, isGrading: false, gradeStatus: gradeStatuses.graded };
+ renderWithIntl();
+ const textarea = screen.getByRole('textbox');
+ expect(textarea).toBeDisabled();
+ });
+
+ it('should display error message when isInvalid is true', () => {
+ const props = { ...defaultProps, isInvalid: true };
+ const { getByText } = renderWithIntl();
+ expect(getByText('The overall feedback is required')).toBeInTheDocument();
+ });
+
+ it('should not display error message when isInvalid is false', () => {
+ const { queryByText } = renderWithIntl();
+ expect(queryByText('The overall feedback is required')).not.toBeInTheDocument();
+ });
+
+ it('should call setValue when textarea value changes', async () => {
+ renderWithIntl();
+ const user = userEvent.setup();
+ const textarea = screen.getByRole('textbox');
+ await user.clear(textarea);
+ expect(defaultProps.setValue).toHaveBeenCalledWith('');
});
describe('mapStateToProps', () => {
- const testState = { arbitraryState: 'some data' };
- let mapped;
- beforeEach(() => {
- mapped = mapStateToProps(testState);
- });
- test('selectors.grading.selected.isGrading', () => {
+ it('should map state properties correctly', () => {
+ const testState = { arbitraryState: 'some data' };
+ const mapped = mapStateToProps(testState);
+
+ expect(selectors.grading.selected.isGrading).toHaveBeenCalledWith(testState);
+ expect(selectors.app.rubric.feedbackConfig).toHaveBeenCalledWith(testState);
+ expect(selectors.grading.selected.overallFeedback).toHaveBeenCalledWith(testState);
+ expect(selectors.grading.validation.overallFeedbackIsInvalid).toHaveBeenCalledWith(testState);
+ expect(selectors.app.rubric.feedbackPrompt).toHaveBeenCalledWith(testState);
+
expect(mapped.isGrading).toEqual(selectors.grading.selected.isGrading(testState));
- });
-
- test('selectors.app.rubricFeedbackConfig', () => {
- expect(mapped.config).toEqual(
- selectors.app.rubric.feedbackConfig(testState),
- );
- });
-
- test('selectors.grading.selected.overallFeedback', () => {
- expect(mapped.value).toEqual(
- selectors.grading.selected.overallFeedback(testState),
- );
- });
-
- test('selectors.grading.validation.overallFeedbackIsInvalid', () => {
- expect(mapped.isInvalid).toEqual(
- selectors.grading.validation.overallFeedbackIsInvalid(testState),
- );
- });
-
- test('selectors.app.rubric.feedbackPrompt', () => {
- expect(mapped.feedbackPrompt).toEqual(
- selectors.app.rubric.feedbackPrompt(testState),
- );
+ expect(mapped.config).toEqual(selectors.app.rubric.feedbackConfig(testState));
+ expect(mapped.value).toEqual(selectors.grading.selected.overallFeedback(testState));
+ expect(mapped.isInvalid).toEqual(selectors.grading.validation.overallFeedbackIsInvalid(testState));
+ expect(mapped.feedbackPrompt).toEqual(selectors.app.rubric.feedbackPrompt(testState));
});
});
describe('mapDispatchToProps', () => {
- test('maps actions.grading.setRubricFeedback to setValue prop', () => {
- expect(mapDispatchToProps.setValue).toEqual(
- actions.grading.setRubricFeedback,
- );
+ it('should map setValue to setRubricFeedback action', () => {
+ expect(mapDispatchToProps.setValue).toEqual(actions.grading.setRubricFeedback);
});
});
});
diff --git a/src/containers/Rubric/__snapshots__/RubricFeedback.test.jsx.snap b/src/containers/Rubric/__snapshots__/RubricFeedback.test.jsx.snap
deleted file mode 100644
index 71f8203..0000000
--- a/src/containers/Rubric/__snapshots__/RubricFeedback.test.jsx.snap
+++ /dev/null
@@ -1,106 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Rubric Feedback component snapshot feedback value is invalid 1`] = `
-
-
-
-
-
-
-
- feedback prompt
-
-
-
-
-
-
-
-
-`;
-
-exports[`Rubric Feedback component snapshot is configure to disabled 1`] = `null`;
-
-exports[`Rubric Feedback component snapshot is graded 1`] = `
-
-
-
-
-
-
-
- feedback prompt
-
-
-
-
-
-`;
-
-exports[`Rubric Feedback component snapshot is grading 1`] = `
-
-
-
-
-
-
-
- feedback prompt
-
-
-
-
-
-`;