diff --git a/src/views/SubmissionView/TextResponseEditor/LaTexPreview.test.jsx b/src/views/SubmissionView/TextResponseEditor/LaTexPreview.test.jsx index e00bad5f..6164a392 100644 --- a/src/views/SubmissionView/TextResponseEditor/LaTexPreview.test.jsx +++ b/src/views/SubmissionView/TextResponseEditor/LaTexPreview.test.jsx @@ -1,19 +1,66 @@ import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; import { renderMathJax } from 'utils/index'; import LatexPreview from './LaTexPreview'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('utils/index', () => ({ renderMathJax: jest.fn(), })); describe('', () => { - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); + const mockRenderMathJax = renderMathJax; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders with latex value', () => { + const latexValue = 'some latex value'; + const { container } = render(); + + const previewDiv = container.querySelector('.mt-2'); + expect(previewDiv).toBeInTheDocument(); + + const contentDiv = previewDiv.querySelector('div'); + expect(contentDiv).toBeInTheDocument(); + }); + + it('calls renderMathJax on mount', () => { + render(); + + expect(mockRenderMathJax).toHaveBeenCalledTimes(1); + }); + + it('calls renderMathJax when latexValue changes', () => { + const { rerender } = render(); + + expect(mockRenderMathJax).toHaveBeenCalledTimes(1); + + rerender(); + + expect(mockRenderMathJax).toHaveBeenCalledTimes(2); + }); + + it('renders with correct HTML content', () => { + const latexValue = 'x^2'; + const { container } = render(); + + const previewDiv = container.querySelector('.mt-2'); + const contentDiv = previewDiv.querySelector('div'); + expect(contentDiv.innerHTML).toBe(latexValue); + }); + + it('has correct CSS structure', () => { + const { container } = render(); - React.useEffect.mock.calls[0][0](); - expect(renderMathJax).toHaveBeenCalled(); + const outerDiv = container.querySelector('.mt-2'); + expect(outerDiv).toBeInTheDocument(); + expect(outerDiv.children).toHaveLength(1); }); }); diff --git a/src/views/SubmissionView/TextResponseEditor/TextEditor.test.jsx b/src/views/SubmissionView/TextResponseEditor/TextEditor.test.jsx index 6d8fc91e..9600d5b5 100644 --- a/src/views/SubmissionView/TextResponseEditor/TextEditor.test.jsx +++ b/src/views/SubmissionView/TextResponseEditor/TextEditor.test.jsx @@ -1,21 +1,33 @@ -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 TextEditor from './TextEditor'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + describe('', () => { - const props = { + const renderWithIntl = (component) => render({component}); + + const defaultProps = { optional: true, disabled: false, value: 'value', - onChange: jest.fn().mockName('onChange'), + onChange: jest.fn(), }; - it('render optional', () => { - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); - }); + it('has correct CSS classes and accessibility attributes', () => { + renderWithIntl(); - it('render required', () => { - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); + const textarea = screen.getByRole('textbox'); + expect(textarea).toBeInTheDocument(); + expect(textarea).toHaveAttribute('name', 'text-response'); + expect(textarea).toHaveAttribute( + 'placeholder', + 'Enter your response to the prompt above', + ); + expect(textarea).toHaveValue('value'); }); }); diff --git a/src/views/SubmissionView/TextResponseEditor/__snapshots__/LaTexPreview.test.jsx.snap b/src/views/SubmissionView/TextResponseEditor/__snapshots__/LaTexPreview.test.jsx.snap deleted file mode 100644 index 4abbea20..00000000 --- a/src/views/SubmissionView/TextResponseEditor/__snapshots__/LaTexPreview.test.jsx.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` renders 1`] = ` -
-
-
-`; diff --git a/src/views/SubmissionView/TextResponseEditor/__snapshots__/TextEditor.test.jsx.snap b/src/views/SubmissionView/TextResponseEditor/__snapshots__/TextEditor.test.jsx.snap deleted file mode 100644 index 52dbad22..00000000 --- a/src/views/SubmissionView/TextResponseEditor/__snapshots__/TextEditor.test.jsx.snap +++ /dev/null @@ -1,54 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` render optional 1`] = ` - - - Your response - ( - Optional - ) - - } - name="text-response" - onChange={[MockFunction onChange]} - placeholder="Enter your response to the prompt above" - value="value" - /> - -`; - -exports[` render required 1`] = ` - - - Your response - ( - required - ) - - } - name="text-response" - onChange={[MockFunction onChange]} - placeholder="Enter your response to the prompt above" - value="value" - /> - - This field is required - - -`; diff --git a/src/views/XBlockStudioView/components/StudioViewRubric.test.jsx b/src/views/XBlockStudioView/components/StudioViewRubric.test.jsx index e816da96..8eda42cf 100644 --- a/src/views/XBlockStudioView/components/StudioViewRubric.test.jsx +++ b/src/views/XBlockStudioView/components/StudioViewRubric.test.jsx @@ -1,68 +1,126 @@ -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 { useRubricConfig } from 'hooks/app'; import StudioViewRubric from './StudioViewRubric'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('hooks/app', () => ({ useRubricConfig: jest.fn(), })); jest.mock('./XBlockStudioViewProvider', () => ({ useXBlockStudioViewContext: () => ({ rubricIsOpen: true, - toggleRubric: jest.fn().mockName('toggleRubric'), + toggleRubric: jest.fn(), }), })); describe('', () => { - it('render with criteria and options', () => { - useRubricConfig.mockReturnValue({ - criteria: [ + const renderWithIntl = (component) => render({component}); + + const sampleCriteria = [ + { + name: 'criterion1', + description: 'description1', + options: [ { - name: 'criterion1', + name: 'option1', + label: 'label1', + points: 1, description: 'description1', - options: [ - { - name: 'option1', - label: 'label1', - points: 1, - description: 'description1', - }, - { - name: 'option2', - label: 'label2', - points: 2, - description: 'description2', - }, - ], }, { - name: 'criterion2', + name: 'option2', + label: 'label2', + points: 2, description: 'description2', - options: [ - { - name: 'option2', - label: 'label2', - points: 2, - description: 'description2', - }, - ], }, ], + }, + { + name: 'criterion2', + description: 'description2', + options: [ + { + name: 'option3', + label: 'label3', + points: 3, + description: 'description3', + }, + ], + }, + ]; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders rubric with criteria and options', () => { + useRubricConfig.mockReturnValue({ + criteria: sampleCriteria, + }); + + renderWithIntl(); + + expect(screen.getByText('Rubric')).toBeInTheDocument(); + expect(screen.getAllByTestId('criteria-test-id')).toHaveLength(2); + + expect(screen.getByText('criterion1')).toBeInTheDocument(); + expect(screen.getAllByText('description1')).toHaveLength(2); + expect(screen.getByText('criterion2')).toBeInTheDocument(); + expect(screen.getAllByText('description2')).toHaveLength(2); + }); + + it('renders criterion options with points', () => { + useRubricConfig.mockReturnValue({ + criteria: sampleCriteria, }); - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); + renderWithIntl(); - expect(wrapper.instance.findByTestId('criteria-test-id').length).toBe(2); + expect(screen.getByText(/label1: 1/)).toBeInTheDocument(); + expect(screen.getByText(/label2: 2/)).toBeInTheDocument(); + expect(screen.getByText(/label3: 3/)).toBeInTheDocument(); + expect(screen.getAllByText(/point/)).toHaveLength(3); }); - it('render without criteria', () => { + it('renders without criteria when empty', () => { useRubricConfig.mockReturnValue({ criteria: [] }); - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); + renderWithIntl(); + + expect(screen.getByText('Rubric')).toBeInTheDocument(); + expect(screen.queryAllByTestId('criteria-test-id')).toHaveLength(0); + }); + + it('renders criterion labels correctly', () => { + useRubricConfig.mockReturnValue({ + criteria: sampleCriteria, + }); + + renderWithIntl(); + + expect(screen.getAllByText('Criteria name:')).toHaveLength(2); + expect(screen.getAllByText('Criteria description:')).toHaveLength(2); + expect(screen.getAllByText('Criteria options:')).toHaveLength(2); + }); + + it('renders multiple criteria with different option counts', () => { + useRubricConfig.mockReturnValue({ + criteria: sampleCriteria, + }); + + renderWithIntl(); + + const criteria = screen.getAllByTestId('criteria-test-id'); + expect(criteria).toHaveLength(2); - expect(wrapper.instance.findByTestId('criteria-test-id').length).toBe(0); + const lists = screen.getAllByRole('list'); + expect(lists).toHaveLength(2); }); }); diff --git a/src/views/XBlockStudioView/components/__snapshots__/StudioViewRubric.test.jsx.snap b/src/views/XBlockStudioView/components/__snapshots__/StudioViewRubric.test.jsx.snap deleted file mode 100644 index 9d21574c..00000000 --- a/src/views/XBlockStudioView/components/__snapshots__/StudioViewRubric.test.jsx.snap +++ /dev/null @@ -1,122 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` render with criteria and options 1`] = ` - - Rubric - - } -> -
-

- - Criteria name: - - criterion1 -

-

- - Criteria description: - - description1 -

-

- - Criteria options: - -

-
    -
  • - - label1 - : - 1 - - points - -

    - description1 -

    -
  • -
  • - - label2 - : - 2 - - points - -

    - description2 -

    -
  • -
-
-
-

- - Criteria name: - - criterion2 -

-

- - Criteria description: - - description2 -

-

- - Criteria options: - -

-
    -
  • - - label2 - : - 2 - - points - -

    - description2 -

    -
  • -
-
-
-`; - -exports[` render without criteria 1`] = ` - - Rubric - - } -/> -`; diff --git a/src/views/XBlockView/StatusRow/DueDateMessage/__snapshots__/index.test.jsx.snap b/src/views/XBlockView/StatusRow/DueDateMessage/__snapshots__/index.test.jsx.snap deleted file mode 100644 index 6bda50b0..00000000 --- a/src/views/XBlockView/StatusRow/DueDateMessage/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should render the due date message 1`] = ` -
- Due in 1 day -
-`; diff --git a/src/views/XBlockView/StatusRow/DueDateMessage/index.test.jsx b/src/views/XBlockView/StatusRow/DueDateMessage/index.test.jsx index 9c30370c..6488fb11 100644 --- a/src/views/XBlockView/StatusRow/DueDateMessage/index.test.jsx +++ b/src/views/XBlockView/StatusRow/DueDateMessage/index.test.jsx @@ -1,15 +1,21 @@ -import { shallow } from '@edx/react-unit-test-utils'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; import useDueDateMessage from './useDueDateMessage'; - import DueDateMessage from './index'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('./useDueDateMessage'); describe('', () => { - it('should render the due date message', () => { - useDueDateMessage.mockReturnValue('Due in 1 day'); - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); + it('renders the due date message from hook', () => { + useDueDateMessage.mockReturnValue('Due tomorrow'); + render(); + + const messageElement = screen.getByText('Due tomorrow'); + expect(messageElement).toBeInTheDocument(); }); }); diff --git a/src/views/XBlockView/StatusRow/StatusBadge/__snapshots__/index.test.jsx.snap b/src/views/XBlockView/StatusRow/StatusBadge/__snapshots__/index.test.jsx.snap deleted file mode 100644 index ae3955e0..00000000 --- a/src/views/XBlockView/StatusRow/StatusBadge/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`StatusBadge renders 1`] = ` - - defaultMessage - -`; diff --git a/src/views/XBlockView/StatusRow/StatusBadge/index.test.jsx b/src/views/XBlockView/StatusRow/StatusBadge/index.test.jsx index bcd9f0f0..ab99ac8a 100644 --- a/src/views/XBlockView/StatusRow/StatusBadge/index.test.jsx +++ b/src/views/XBlockView/StatusRow/StatusBadge/index.test.jsx @@ -1,7 +1,13 @@ -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 StatusBadge from './index'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('./useBadgeConfig', () => () => ({ variant: 'variant', message: { @@ -10,9 +16,27 @@ jest.mock('./useBadgeConfig', () => () => ({ }, })); -describe('StatusBadge', () => { - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); +describe('', () => { + const renderWithIntl = (component) => render({component}); + + it('renders badge with message from hook', () => { + renderWithIntl(); + + expect(screen.getByText('defaultMessage')).toBeInTheDocument(); + }); + + it('renders badge component with correct role', () => { + renderWithIntl(); + + const badge = screen.getByText('defaultMessage'); + expect(badge).toBeInTheDocument(); + expect(badge.closest('.badge')).toBeInTheDocument(); + }); + + it('applies variant class from hook', () => { + renderWithIntl(); + + const badge = screen.getByText('defaultMessage'); + expect(badge).toHaveClass('badge'); }); });