diff --git a/src/containers/ListView/ListViewBreadcrumb.test.jsx b/src/containers/ListView/ListViewBreadcrumb.test.jsx index 0f20590..bb6c496 100644 --- a/src/containers/ListView/ListViewBreadcrumb.test.jsx +++ b/src/containers/ListView/ListViewBreadcrumb.test.jsx @@ -1,23 +1,21 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - -import { Hyperlink } from '@openedx/paragon'; - -import * as constants from 'data/constants/app'; -import urls from 'data/services/lms/urls'; +import { render } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import { selectors } from 'data/redux'; - import { ListViewBreadcrumb, mapStateToProps, } from './ListViewBreadcrumb'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + jest.mock('data/redux', () => ({ selectors: { app: { - courseId: (...args) => ({ courseId: args }), + courseId: jest.fn((state) => state.courseId || 'test-course-id'), ora: { - name: (...args) => ({ oraName: args }), + name: jest.fn((state) => state.oraName || 'test-ora-name'), }, }, }, @@ -28,41 +26,64 @@ jest.mock('data/services/lms/urls', () => ({ ora: (courseId, locationId) => `oraUrl(${courseId}, ${locationId})`, })); -let el; +jest.mock('data/constants/app', () => ({ + locationId: () => 'test-location-id', +})); describe('ListViewBreadcrumb component', () => { - describe('component', () => { - const props = { - courseId: 'test-course-id', - oraName: 'fake-ora-name', - }; - beforeEach(() => { - el = shallow(); + const props = { + courseId: 'test-course-id', + oraName: 'fake-ora-name', + }; + + const renderWithIntl = (component) => render( + + {component} + , + ); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('behavior', () => { + it('renders back to responses link with correct destination', () => { + const { container } = renderWithIntl(); + const backLink = container.querySelector('a[href*="openResponseUrl"]'); + expect(backLink).toBeInTheDocument(); + expect(backLink).toHaveAttribute('href', `openResponseUrl(${props.courseId})`); }); - test('snapshot: empty (no list data)', () => { - expect(el.snapshot).toMatchSnapshot(); + + it('displays ORA name in heading', () => { + const { getByText } = renderWithIntl(); + const heading = getByText(props.oraName); + expect(heading).toBeInTheDocument(); + expect(heading).toHaveClass('h3'); }); - test('openResponse destination', () => { - expect( - el.instance.findByType(Hyperlink)[0].props.destination, - ).toEqual(urls.openResponse(props.courseId)); + + it('renders ORA link with correct destination', () => { + const { container } = renderWithIntl(); + const oraLink = container.querySelector('a[href*="oraUrl"]'); + expect(oraLink).toBeInTheDocument(); + expect(oraLink).toHaveAttribute('href', `oraUrl(${props.courseId}, test-location-id)`); }); - test('ora destination', () => { - expect( - el.instance.findByType(Hyperlink)[1].props.destination, - ).toEqual(urls.ora(props.courseId, constants.locationId())); + + it('displays back to responses text', () => { + const { getByText } = renderWithIntl(); + expect(getByText('Back to all open responses')).toBeInTheDocument(); }); }); + describe('mapStateToProps', () => { - let mapped; const testState = { some: 'test-state' }; - beforeEach(() => { - mapped = mapStateToProps(testState); - }); - test('courseId loads from app.courseId', () => { + + it('maps courseId from app.courseId selector', () => { + const mapped = mapStateToProps(testState); expect(mapped.courseId).toEqual(selectors.app.courseId(testState)); }); - test('oraName loads from app.ora.name', () => { + + it('maps oraName from app.ora.name selector', () => { + const mapped = mapStateToProps(testState); expect(mapped.oraName).toEqual(selectors.app.ora.name(testState)); }); }); diff --git a/src/containers/ListView/SelectedBulkAction.test.jsx b/src/containers/ListView/SelectedBulkAction.test.jsx index e7b1b46..7e50d6d 100644 --- a/src/containers/ListView/SelectedBulkAction.test.jsx +++ b/src/containers/ListView/SelectedBulkAction.test.jsx @@ -1,20 +1,37 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - +import { render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import { SelectedBulkAction } from './SelectedBulkAction'; +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); + describe('SelectedBulkAction component', () => { const props = { selectedFlatRows: [{ id: 1 }, { id: 2 }], - handleClick: jest.fn(), + handleClick: jest.fn(() => () => {}), }; - test('snapshots', () => { - const el = shallow( jest.fn()} />); - expect(el.snapshot).toMatchSnapshot(); + + beforeEach(() => { + jest.clearAllMocks(); }); - test('handleClick', () => { - shallow(); + it('renders button with correct text and selected count', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toBeInTheDocument(); + expect(button).toHaveTextContent(`View selected responses (${props.selectedFlatRows.length})`); + }); + + it('applies correct CSS class to button', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toHaveClass('view-selected-responses-btn'); + expect(button).toHaveClass('btn-primary'); + }); + + it('calls handleClick with selectedFlatRows on render', () => { + render(); expect(props.handleClick).toHaveBeenCalledWith(props.selectedFlatRows); }); }); diff --git a/src/containers/ListView/SubmissionsTable.test.jsx b/src/containers/ListView/SubmissionsTable.test.jsx index 764857b..7a3c6f6 100644 --- a/src/containers/ListView/SubmissionsTable.test.jsx +++ b/src/containers/ListView/SubmissionsTable.test.jsx @@ -1,47 +1,35 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - -import { - MultiSelectDropdownFilter, - TextFilter, -} from '@openedx/paragon'; - +import { render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import { selectors, thunkActions } from 'data/redux'; -import { gradingStatuses as statuses, submissionFields } from 'data/services/lms/constants'; - -import StatusBadge from 'components/StatusBadge'; -import messages from './messages'; +import { gradingStatuses as statuses } from 'data/services/lms/constants'; import { SubmissionsTable, mapStateToProps, mapDispatchToProps, } from './SubmissionsTable'; -jest.mock('./FilterStatusComponent', () => jest.fn().mockName('FilterStatusComponent')); -jest.mock('./TableAction', () => jest.fn().mockName('TableAction')); -jest.mock('./SelectedBulkAction', () => jest.fn().mockName('SelectedBulkAction')); +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); jest.mock('data/redux', () => ({ selectors: { app: { ora: { - isIndividual: (...args) => ({ isIndividual: args }), + isIndividual: jest.fn((state) => state.isIndividual || true), }, }, submissions: { - listData: (...args) => ({ listData: args }), + listData: jest.fn((state) => state.listData || []), }, }, thunkActions: { grading: { - loadSelectionForReview: (...args) => ({ loadSelectionForReview: args }), + loadSelectionForReview: jest.fn(), }, }, })); -let el; -jest.useFakeTimers('modern'); - const dates = [ '2021-12-08 09:06:15.319213+00:00', '2021-12-10 18:06:15.319213+00:00', @@ -63,18 +51,15 @@ const individualData = [ dateSubmitted: dates[1], gradingStatus: statuses.graded, score: { - pointsEarned: 2, + pointsEarned: 9, pointsPossible: 10, }, }, { - username: 'username-3', - dateSubmitted: dates[2], - gradingStatus: statuses.inProgress, - score: { - pointsEarned: 3, - pointsPossible: 10, - }, + username: 'username-2', + dateSubmitted: dates[1], + gradingStatus: statuses.ungraded, + score: null, }, ]; @@ -97,218 +82,94 @@ const teamData = [ pointsPossible: 10, }, }, - { - teamName: 'teamName-3', - dateSubmitted: dates[2], - gradingStatus: statuses.inProgress, - score: { - pointsEarned: 3, - pointsPossible: 10, - }, - }, ]; describe('SubmissionsTable component', () => { - describe('component', () => { - const props = { - isIndividual: true, - listData: [...individualData], - }; - beforeEach(() => { - props.loadSelectionForReview = jest.fn(); + const defaultProps = { + isIndividual: true, + listData: [...individualData], + loadSelectionForReview: jest.fn(), + }; + + const renderWithIntl = (component) => render( + + {component} + , + ); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('behavior', () => { + it('renders DataTable component', () => { + const { container } = renderWithIntl(); + const submissionsTable = container.querySelector('.submissions-table'); + expect(submissionsTable).toBeInTheDocument(); }); - describe('render tests', () => { - const mockMethod = (methodName) => { - el.instance[methodName] = jest.fn().mockName(`this.${methodName}`); - }; - beforeEach(() => { - el = shallow(); - }); - describe('snapshots', () => { - beforeEach(() => { - mockMethod('handleViewAllResponsesClick'); - mockMethod('formatDate'); - mockMethod('formatGrade'); - mockMethod('formatStatus'); - }); - test('snapshot: empty (no list data)', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - expect(el.isEmptyRender()).toEqual(true); - }); - test('snapshot: happy path', () => { - expect(el.snapshot).toMatchSnapshot(); - }); - test('snapshot: team happy path', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - }); - }); - describe('DataTable', () => { - let tableProps; - beforeEach(() => { - tableProps = el.instance.findByTestId('data-table')[0].props; - }); - test.each([ - 'isFilterable', - 'isSelectable', - 'isSortable', - 'isPaginated', - ])('%s', key => expect(tableProps[key]).toEqual(true)); - test.each([ - ['numBreakoutFilters', 2], - ['defaultColumnValues', { Filter: TextFilter }], - ['itemCount', 3], - ['initialState', { pageSize: 10, pageIndex: 0 }], - ])('%s = %p', (key, value) => expect(tableProps[key]).toEqual(value)); - describe('individual columns', () => { - let columns; - beforeEach(() => { - columns = tableProps.columns; - }); - test('username column', () => { - expect(columns[0]).toEqual({ - Header: messages.username.defaultMessage, - accessor: submissionFields.username, - }); - }); - test('submission date column', () => { - expect(columns[1]).toEqual({ - Header: messages.learnerSubmissionDate.defaultMessage, - accessor: submissionFields.dateSubmitted, - Cell: el.instance.children[0].props.columns[1].Cell, - disableFilters: true, - }); - }); - test('grade column', () => { - expect(columns[2]).toEqual({ - Header: messages.grade.defaultMessage, - accessor: submissionFields.score, - Cell: el.instance.children[0].props.columns[2].Cell, - disableFilters: true, - }); - }); - test('grading status column', () => { - expect(columns[3]).toEqual({ - Header: messages.gradingStatus.defaultMessage, - accessor: submissionFields.gradingStatus, - Cell: el.instance.children[0].props.columns[3].Cell, - Filter: MultiSelectDropdownFilter, - filter: 'includesValue', - filterChoices: el.instance.children[0].props.columns[3].filterChoices, - }); - }); - }); - describe('team columns', () => { - let columns; - beforeEach(() => { - el = shallow(); - columns = el.instance.findByTestId('data-table')[0].props.columns; - }); - test('teamName column', () => { - expect(columns[0]).toEqual({ - Header: messages.teamName.defaultMessage, - accessor: submissionFields.teamName, - }); - }); - test('submission date column', () => { - expect(columns[1]).toEqual({ - Header: messages.teamSubmissionDate.defaultMessage, - accessor: submissionFields.dateSubmitted, - Cell: el.instance.children[0].props.columns[1].Cell, - disableFilters: true, - }); - }); - test('grade column', () => { - expect(columns[2]).toEqual({ - Header: messages.grade.defaultMessage, - accessor: submissionFields.score, - Cell: el.instance.children[0].props.columns[2].Cell, - disableFilters: true, - }); - }); - test('grading status column', () => { - expect(columns[3]).toEqual({ - Header: messages.gradingStatus.defaultMessage, - accessor: submissionFields.gradingStatus, - Cell: el.instance.children[0].props.columns[3].Cell, - Filter: MultiSelectDropdownFilter, - filter: 'includesValue', - filterChoices: el.instance.children[0].props.columns[3].filterChoices, - }); - }); - }); - }); + + it('returns empty render when no list data provided', () => { + const { container } = renderWithIntl(); + expect(container.firstChild).toBeNull(); }); - describe('behavior', () => { - describe('formatDate method', () => { - it('returns the date in locale time string', () => { - const fakeDate = 16131215154955; - const fakeDateString = 'test-date-string'; - const mock = jest.spyOn(Date.prototype, 'toLocaleString').mockReturnValue(fakeDateString); - expect(el.instance.children[0].props.columns[1].Cell({ value: fakeDate })).toEqual(fakeDateString); - mock.mockRestore(); - }); - }); - describe('formatGrade method', () => { - it('returns "-" if grade is null', () => { - expect(el.instance.children[0].props.columns[2].Cell({ value: null })).toEqual('-'); - }); - it('returns / if grade exists', () => { - expect( - el.instance.children[0].props.columns[2].Cell({ value: { pointsEarned: 1, pointsPossible: 10 } }), - ).toEqual('1/10'); - }); - }); - describe('formatStatus method', () => { - it('returns a StatusBadge with the given status', () => { - const status = 'graded'; - expect(el.instance.children[0].props.columns[3].Cell({ value: 'graded' })).toEqual( - , - ); - }); - }); - describe('handleViewAllResponsesClick', () => { - it('calls loadSelectionForReview with submissionUUID from all rows if there are no selectedRows', () => { - // Test the integration by simulating the function call directly - // Since handleViewAllResponsesClick is internal to the functional component, - // we test through the component behavior - const data = [ - { original: { submissionUUID: '123' } }, - { original: { submissionUUID: '456' } }, - { original: { submissionUUID: '789' } }, - ]; - // Create a test instance that we can call the function on - const testEl = shallow(); - const tableProps = testEl.instance.findByTestId('data-table')[0].props; + it('renders individual columns for individual submissions', () => { + renderWithIntl(); + expect(screen.getByText('Username')).toBeInTheDocument(); + expect(screen.getByText('Learner submission date')).toBeInTheDocument(); + }); - // Get the handleClick function from the TableAction props - const handleClickFunction = tableProps.tableActions[0].props.handleClick; + it('renders team columns for team submissions', () => { + renderWithIntl(); + expect(screen.getByText('Team name')).toBeInTheDocument(); + expect(screen.getByText('Team submission date')).toBeInTheDocument(); + }); - // Call the function as TableAction would call it - handleClickFunction(data)(); + it('formats date correctly', () => { + renderWithIntl(); + const formattedDate = screen.getAllByText('12/10/2021, 6:06:15 PM'); + expect(formattedDate.length).toBeGreaterThan(0); + }); - expect(props.loadSelectionForReview).toHaveBeenCalledWith(['123', '456', '789']); - }); - }); + it('formats grade as dash when null', () => { + renderWithIntl(); + const ungradedBadge = screen.getAllByText('Ungraded')[1].parentElement; + const score = ungradedBadge.previousSibling; + expect(score).toHaveTextContent('-'); + }); + + it('formats grade as points earned over points possible', () => { + renderWithIntl(); + const scored = screen.getByText('9/10'); + expect(scored).toBeInTheDocument(); + }); + + it('formats status as StatusBadge component', () => { + renderWithIntl(); + const gradedBadge = screen.getByText('Grading Completed'); + expect(gradedBadge).toHaveClass('badge-success'); + const ungradedBadge = screen.getAllByText('Ungraded')[0]; + expect(ungradedBadge).toHaveClass('badge-primary'); }); }); + describe('mapStateToProps', () => { - let mapped; const testState = { some: 'test-state' }; - beforeEach(() => { - mapped = mapStateToProps(testState); - }); - test('listData loads from submissions.listData', () => { + + it('maps listData from submissions.listData selector', () => { + const mapped = mapStateToProps(testState); expect(mapped.listData).toEqual(selectors.submissions.listData(testState)); }); + + it('maps isIndividual from app.ora.isIndividual selector', () => { + const mapped = mapStateToProps(testState); + expect(mapped.isIndividual).toEqual(selectors.app.ora.isIndividual(testState)); + }); }); + describe('mapDispatchToProps', () => { - it('loads loadSelectionForReview from thunkActions.grading.loadSelectionForReview', () => { - expect( - mapDispatchToProps.loadSelectionForReview, - ).toEqual(thunkActions.grading.loadSelectionForReview); + it('maps loadSelectionForReview from thunkActions', () => { + expect(mapDispatchToProps.loadSelectionForReview).toEqual(thunkActions.grading.loadSelectionForReview); }); }); }); diff --git a/src/containers/ListView/TableAction.test.jsx b/src/containers/ListView/TableAction.test.jsx index 5adef4c..e7e9d1c 100644 --- a/src/containers/ListView/TableAction.test.jsx +++ b/src/containers/ListView/TableAction.test.jsx @@ -1,29 +1,54 @@ -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - +import { render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import { TableAction } from './TableAction'; +import messages from './messages'; + +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); describe('TableAction component', () => { const props = { tableInstance: { rows: [{ id: 1 }, { id: 2 }] }, - handleClick: jest.fn(), + handleClick: jest.fn(() => () => {}), }; - test('snapshots', () => { - const el = shallow( jest.fn()} />); - expect(el.snapshot).toMatchSnapshot(); + + beforeEach(() => { + jest.clearAllMocks(); }); - test('Inactive Button "View All Responses"', () => { + it('renders button with correct text', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toBeInTheDocument(); + expect(button).toHaveTextContent(messages.viewAllResponses.defaultMessage); + }); + + it('applies correct CSS class to button', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toHaveClass('view-all-responses-btn'); + expect(button).toHaveClass('btn-primary'); + }); + + it('enables button when rows are present', () => { + render(); + const button = screen.getByRole('button'); + expect(button).not.toBeDisabled(); + }); + + it('disables button when no rows are present', () => { const emptyProps = { tableInstance: { rows: [] }, - handleClick: jest.fn(), + handleClick: jest.fn(() => () => {}), }; - const el = shallow(); - expect(el.snapshot).toMatchSnapshot(); + render(); + const button = screen.getByRole('button'); + expect(button).toBeDisabled(); }); - test('handleClick', () => { - shallow(); + it('calls handleClick with table rows on render', () => { + render(); expect(props.handleClick).toHaveBeenCalledWith(props.tableInstance.rows); }); }); diff --git a/src/containers/ListView/__snapshots__/ListViewBreadcrumb.test.jsx.snap b/src/containers/ListView/__snapshots__/ListViewBreadcrumb.test.jsx.snap deleted file mode 100644 index 1d30142..0000000 --- a/src/containers/ListView/__snapshots__/ListViewBreadcrumb.test.jsx.snap +++ /dev/null @@ -1,38 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ListViewBreadcrumb component component snapshot: empty (no list data) 1`] = ` - - - - - -

- - fake-ora-name - - - - -

-
-`; diff --git a/src/containers/ListView/__snapshots__/SelectedBulkAction.test.jsx.snap b/src/containers/ListView/__snapshots__/SelectedBulkAction.test.jsx.snap deleted file mode 100644 index 6c67533..0000000 --- a/src/containers/ListView/__snapshots__/SelectedBulkAction.test.jsx.snap +++ /dev/null @@ -1,20 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SelectedBulkAction component snapshots 1`] = ` - -`; diff --git a/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap b/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap deleted file mode 100644 index 1a8bdad..0000000 --- a/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap +++ /dev/null @@ -1,247 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SubmissionsTable component component render tests snapshots snapshot: empty (no list data) 1`] = `null`; - -exports[`SubmissionsTable component component render tests snapshots snapshot: happy path 1`] = ` -
- , - ] - } - columns={ - [ - { - "Header": "Username", - "accessor": "username", - }, - { - "Cell": [Function], - "Header": "Learner submission date", - "accessor": "dateSubmitted", - "disableFilters": true, - }, - { - "Cell": [Function], - "Header": "Grade", - "accessor": "score", - "disableFilters": true, - }, - { - "Cell": [Function], - "Filter": "MultiSelectDropdownFilter", - "Header": "Grading status", - "accessor": "gradingStatus", - "filter": "includesValue", - "filterChoices": [ - { - "name": "Ungraded", - "value": "ungraded", - }, - { - "name": "Grading Completed", - "value": "graded", - }, - { - "name": "Currently being graded by someone else", - "value": "locked", - }, - { - "name": "You are currently grading this response", - "value": "in-progress", - }, - ], - }, - ] - } - data={ - [ - { - "dateSubmitted": "2021-12-08 09:06:15.319213+00:00", - "gradingStatus": "ungraded", - "score": { - "pointsEarned": 1, - "pointsPossible": 10, - }, - "username": "username-1", - }, - { - "dateSubmitted": "2021-12-10 18:06:15.319213+00:00", - "gradingStatus": "graded", - "score": { - "pointsEarned": 2, - "pointsPossible": 10, - }, - "username": "username-2", - }, - { - "dateSubmitted": "2021-12-11 07:06:15.319213+00:00", - "gradingStatus": "in-progress", - "score": { - "pointsEarned": 3, - "pointsPossible": 10, - }, - "username": "username-3", - }, - ] - } - data-testid="data-table" - defaultColumnValues={ - { - "Filter": "TextFilter", - } - } - initialState={ - { - "pageIndex": 0, - "pageSize": 10, - } - } - isFilterable={true} - isPaginated={true} - isSelectable={true} - isSortable={true} - itemCount={3} - numBreakoutFilters={2} - tableActions={ - [ - , - ] - } - > - - - - -
-`; - -exports[`SubmissionsTable component component render tests snapshots snapshot: team happy path 1`] = ` -
- , - ] - } - columns={ - [ - { - "Header": "Team name", - "accessor": "teamName", - }, - { - "Cell": [Function], - "Header": "Team submission date", - "accessor": "dateSubmitted", - "disableFilters": true, - }, - { - "Cell": [Function], - "Header": "Grade", - "accessor": "score", - "disableFilters": true, - }, - { - "Cell": [Function], - "Filter": "MultiSelectDropdownFilter", - "Header": "Grading status", - "accessor": "gradingStatus", - "filter": "includesValue", - "filterChoices": [ - { - "name": "Ungraded", - "value": "ungraded", - }, - { - "name": "Grading Completed", - "value": "graded", - }, - { - "name": "Currently being graded by someone else", - "value": "locked", - }, - { - "name": "You are currently grading this response", - "value": "in-progress", - }, - ], - }, - ] - } - data={ - [ - { - "dateSubmitted": "2021-12-08 09:06:15.319213+00:00", - "gradingStatus": "ungraded", - "score": { - "pointsEarned": 1, - "pointsPossible": 10, - }, - "teamName": "teamName-1", - }, - { - "dateSubmitted": "2021-12-10 18:06:15.319213+00:00", - "gradingStatus": "graded", - "score": { - "pointsEarned": 2, - "pointsPossible": 10, - }, - "teamName": "teamName-2", - }, - { - "dateSubmitted": "2021-12-11 07:06:15.319213+00:00", - "gradingStatus": "in-progress", - "score": { - "pointsEarned": 3, - "pointsPossible": 10, - }, - "teamName": "teamName-3", - }, - ] - } - data-testid="data-table" - defaultColumnValues={ - { - "Filter": "TextFilter", - } - } - initialState={ - { - "pageIndex": 0, - "pageSize": 10, - } - } - isFilterable={true} - isPaginated={true} - isSelectable={true} - isSortable={true} - itemCount={3} - numBreakoutFilters={2} - tableActions={ - [ - , - ] - } - > - - - - -
-`; diff --git a/src/containers/ListView/__snapshots__/TableAction.test.jsx.snap b/src/containers/ListView/__snapshots__/TableAction.test.jsx.snap deleted file mode 100644 index 8867be0..0000000 --- a/src/containers/ListView/__snapshots__/TableAction.test.jsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TableAction component Inactive Button "View All Responses" 1`] = ` - -`; - -exports[`TableAction component snapshots 1`] = ` - -`; diff --git a/src/containers/ResponseDisplay/FileDownload.test.jsx b/src/containers/ResponseDisplay/FileDownload.test.jsx index 30504b1..5fd0965 100644 --- a/src/containers/ResponseDisplay/FileDownload.test.jsx +++ b/src/containers/ResponseDisplay/FileDownload.test.jsx @@ -1,6 +1,6 @@ -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 { RequestKeys, RequestStates } from 'data/constants/requests'; import { selectors, thunkActions } from 'data/redux'; import { @@ -9,10 +9,15 @@ import { FileDownload, statusMapping, } from './FileDownload'; +import messages from './messages'; + +jest.unmock('@openedx/paragon'); +jest.unmock('react'); +jest.unmock('@edx/frontend-platform/i18n'); jest.mock('data/redux', () => ({ selectors: { - requests: { requestStatus: (...args) => ({ requestStatus: args }) }, + requests: { requestStatus: jest.fn((state, { requestKey }) => ({ status: 'inactive', requestKey })) }, }, thunkActions: { download: { downloadFiles: jest.fn() }, @@ -20,50 +25,77 @@ jest.mock('data/redux', () => ({ })); describe('FileDownload', () => { - describe('component', () => { - const props = { - requestStatus: { status: RequestStates.inactive }, - }; - let el; - beforeEach(() => { - props.downloadFiles = jest.fn().mockName('this.props.downloadFiles'); - el = shallow(); + const defaultProps = { + requestStatus: { status: RequestStates.inactive }, + downloadFiles: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('behavior', () => { + it('renders StatefulButton with default state when inactive', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toBeInTheDocument(); + expect(button).toHaveTextContent(messages.downloadFiles.defaultMessage); }); - describe('snapshot', () => { - test('download is inactive', () => { - expect(el.snapshot).toMatchSnapshot(); - expect(el.instance.props.state).toEqual(statusMapping[RequestStates.inactive]); - }); - test('download is pending', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - expect(el.instance.props.state).toEqual(statusMapping[RequestStates.pending]); - }); - test('download is completed', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - expect(el.instance.props.state).toEqual(statusMapping[RequestStates.completed]); - }); - test('download is failed', () => { - el = shallow(); - expect(el.snapshot).toMatchSnapshot(); - expect(el.instance.props.state).toEqual(statusMapping[RequestStates.failed]); - }); + + it('renders with pending state when download is pending', () => { + const props = { ...defaultProps, requestStatus: { status: RequestStates.pending } }; + render(); + const button = screen.getByRole('button'); + screen.debug(); + expect(button).toHaveClass('pgn__stateful-btn-state-pending'); + expect(button).toHaveAttribute('aria-disabled', 'true'); + expect(button).toHaveTextContent(messages.downloading.defaultMessage); + }); + + it('renders with completed state when download is completed', () => { + const props = { ...defaultProps, requestStatus: { status: RequestStates.completed } }; + render(); + const button = screen.getByRole('button'); + expect(button).toHaveClass('pgn__stateful-btn-state-completed'); + }); + + it('renders with failed state when download fails', () => { + const props = { ...defaultProps, requestStatus: { status: RequestStates.failed } }; + render(); + const button = screen.getByRole('button'); + expect(button).toBeInTheDocument(); + expect(button).toHaveClass('pgn__stateful-btn-state-failed'); + expect(button).toHaveTextContent(messages.retryDownload.defaultMessage); + }); + + it('calls downloadFiles when button is clicked', async () => { + render(); + const user = userEvent.setup(); + const button = screen.getByRole('button'); + await user.click(button); + expect(defaultProps.downloadFiles).toHaveBeenCalledTimes(1); + }); + + it('maps request states to button states correctly', () => { + expect(statusMapping[RequestStates.inactive]).toBe('default'); + expect(statusMapping[RequestStates.pending]).toBe('pending'); + expect(statusMapping[RequestStates.completed]).toBe('completed'); + expect(statusMapping[RequestStates.failed]).toBe('failed'); }); }); + describe('mapStateToProps', () => { - let mapped; - const requestKey = RequestKeys.downloadFiles; const testState = { some: 'test-state' }; - beforeEach(() => { - mapped = mapStateToProps(testState); - }); - test('requestStatus loads from requests.requestStatus(downloadFiles)', () => { - expect(mapped.requestStatus).toEqual(selectors.requests.requestStatus(testState, { requestKey })); + + it('maps requestStatus from requests.requestStatus selector', () => { + const mapped = mapStateToProps(testState); + const expectedResult = selectors.requests.requestStatus(testState, { requestKey: RequestKeys.downloadFiles }); + expect(mapped.requestStatus).toEqual(expectedResult); }); }); + describe('mapDispatchToProps', () => { - it('loads downloadFiles from thunkActions.download.downloadFiles', () => { + it('maps downloadFiles from thunkActions', () => { expect(mapDispatchToProps.downloadFiles).toEqual(thunkActions.download.downloadFiles); }); }); diff --git a/src/containers/ResponseDisplay/__snapshots__/FileDownload.test.jsx.snap b/src/containers/ResponseDisplay/__snapshots__/FileDownload.test.jsx.snap deleted file mode 100644 index cad5a51..0000000 --- a/src/containers/ResponseDisplay/__snapshots__/FileDownload.test.jsx.snap +++ /dev/null @@ -1,213 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FileDownload component snapshot download is completed 1`] = ` -, - "default": , - "failed": , - "pending": , - } - } - labels={ - { - "complete": , - "default": , - "failed": , - "pending": , - } - } - onClick={[MockFunction this.props.downloadFiles]} - state="completed" -/> -`; - -exports[`FileDownload component snapshot download is failed 1`] = ` -, - "default": , - "failed": , - "pending": , - } - } - labels={ - { - "complete": , - "default": , - "failed": , - "pending": , - } - } - onClick={[MockFunction this.props.downloadFiles]} - state="failed" -/> -`; - -exports[`FileDownload component snapshot download is inactive 1`] = ` -, - "default": , - "failed": , - "pending": , - } - } - labels={ - { - "complete": , - "default": , - "failed": , - "pending": , - } - } - onClick={[MockFunction this.props.downloadFiles]} - state="default" -/> -`; - -exports[`FileDownload component snapshot download is pending 1`] = ` -, - "default": , - "failed": , - "pending": , - } - } - labels={ - { - "complete": , - "default": , - "failed": , - "pending": , - } - } - onClick={[MockFunction this.props.downloadFiles]} - state="pending" -/> -`; diff --git a/src/containers/ReviewModal/index.test.jsx b/src/containers/ReviewModal/index.test.jsx index bb34d06..e3f73b7 100644 --- a/src/containers/ReviewModal/index.test.jsx +++ b/src/containers/ReviewModal/index.test.jsx @@ -81,7 +81,6 @@ describe('ReviewModal', () => { }); render(); - screen.debug(); expect(useDispatch).toHaveBeenCalled(); }); @@ -126,7 +125,6 @@ describe('ReviewModal', () => { }); render(); - screen.debug(); const loadingMessage = screen.getByText(messages.loadingResponse.defaultMessage); expect(loadingMessage).toBeInTheDocument(); });