test: deprecate react-unit-test-utils part-4 (#462)
* test: deprecate react-unit-test-utils part-4 * test: address feedback * test: address feedback * test: improve tests and address feedback --------- Co-authored-by: diana-villalvazo-wgu <diana.villalvazo@wgu.edu>
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
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 { selectors } from 'data/redux';
|
||||
import { DemoWarning, mapStateToProps } from '.';
|
||||
import messages from './messages';
|
||||
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
|
||||
jest.mock('data/redux', () => ({
|
||||
selectors: {
|
||||
@@ -10,24 +14,26 @@ jest.mock('data/redux', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
let el;
|
||||
|
||||
describe('DemoWarning component', () => {
|
||||
describe('snapshots', () => {
|
||||
test('does not render if disabled flag is missing', () => {
|
||||
el = shallow(<DemoWarning hide />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
expect(el.isEmptyRender()).toEqual(true);
|
||||
describe('behavior', () => {
|
||||
it('does not render when hide prop is true', () => {
|
||||
const { container } = render(<IntlProvider locale="en"><DemoWarning hide /></IntlProvider>);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
test('snapshot: disabled flag is present', () => {
|
||||
el = shallow(<DemoWarning hide={false} />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
expect(el.isEmptyRender()).toEqual(false);
|
||||
|
||||
it('renders alert with warning message when hide prop is false', () => {
|
||||
render(<IntlProvider locale="en"><DemoWarning hide={false} /></IntlProvider>);
|
||||
const alert = screen.getByRole('alert');
|
||||
expect(alert).toBeInTheDocument();
|
||||
expect(alert).toHaveClass('alert-warning');
|
||||
expect(alert).toHaveTextContent(messages.demoModeMessage.defaultMessage);
|
||||
expect(alert).toHaveTextContent(messages.demoModeHeading.defaultMessage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { some: 'test-state' };
|
||||
test('hide is forwarded from app.isEnabled', () => {
|
||||
it('maps hide prop from app.isEnabled selector', () => {
|
||||
const testState = { some: 'test-state' };
|
||||
expect(mapStateToProps(testState).hide).toEqual(
|
||||
selectors.app.isEnabled(testState),
|
||||
);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DemoWarning component snapshots does not render if disabled flag is missing 1`] = `null`;
|
||||
|
||||
exports[`DemoWarning component snapshots snapshot: disabled flag is present 1`] = `
|
||||
<Alert
|
||||
className="mb-0 rounded-0"
|
||||
variant="warning"
|
||||
>
|
||||
<Alert.Heading>
|
||||
<FormattedMessage
|
||||
defaultMessage="Demo Mode"
|
||||
description="Demo mode heading"
|
||||
id="ora-grading.ReviewModal.demoHeading"
|
||||
/>
|
||||
</Alert.Heading>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="You are demoing the new ORA staff grading experience. You will be unable to submit grades until you activate the feature. This will become the default grading experience on May 9th (05/09/2022). To opt-in early, or opt-out, please contact Partner Support."
|
||||
description="Demo mode message"
|
||||
id="ora-grading.ReviewModal.demoMessage"
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
`;
|
||||
@@ -1,33 +1,48 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
|
||||
import { Hyperlink } from '@openedx/paragon';
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import urls from 'data/services/lms/urls';
|
||||
|
||||
import EmptySubmission from './EmptySubmission';
|
||||
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
|
||||
jest.mock('data/services/lms/urls', () => ({
|
||||
openResponse: (courseId) => `openResponseUrl(${courseId})`,
|
||||
}));
|
||||
|
||||
jest.mock('./assets/emptyState.svg', () => './assets/emptyState.svg');
|
||||
|
||||
let el;
|
||||
jest.mock('./assets/empty-state.svg', () => './assets/empty-state.svg');
|
||||
|
||||
describe('EmptySubmission component', () => {
|
||||
describe('component', () => {
|
||||
const props = { courseId: 'test-course-id' };
|
||||
beforeEach(() => {
|
||||
el = shallow(<EmptySubmission {...props} />);
|
||||
});
|
||||
test('snapshot', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('openResponse destination', () => {
|
||||
expect(
|
||||
el.instance.findByType(Hyperlink)[0].props.destination,
|
||||
).toEqual(urls.openResponse(props.courseId));
|
||||
});
|
||||
const props = { courseId: 'test-course-id' };
|
||||
|
||||
const renderWithIntl = (component) => render(
|
||||
<IntlProvider locale="en" messages={{}}>
|
||||
{component}
|
||||
</IntlProvider>,
|
||||
);
|
||||
|
||||
it('renders the empty state image with correct alt text', () => {
|
||||
const { getByAltText } = renderWithIntl(<EmptySubmission {...props} />);
|
||||
expect(getByAltText('empty state')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the no results found title message', () => {
|
||||
const { getByText } = renderWithIntl(<EmptySubmission {...props} />);
|
||||
expect(getByText('Nothing here yet')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders hyperlink with correct destination URL', () => {
|
||||
const { container } = renderWithIntl(<EmptySubmission {...props} />);
|
||||
const hyperlink = container.querySelector('a');
|
||||
expect(hyperlink).toHaveAttribute(
|
||||
'href',
|
||||
urls.openResponse(props.courseId),
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the back to responses button', () => {
|
||||
const { getByText } = renderWithIntl(<EmptySubmission {...props} />);
|
||||
expect(getByText('Back to all open responses')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,54 +1,21 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import PropTypes from 'prop-types';
|
||||
import { render } from '@testing-library/react';
|
||||
import { DataTableContext } from '@openedx/paragon';
|
||||
|
||||
import * as module from './FilterStatusComponent';
|
||||
|
||||
const fieldIds = [
|
||||
'field-id-0',
|
||||
'field-id-1',
|
||||
'field-id-2',
|
||||
'field-id-3',
|
||||
];
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
|
||||
const fieldIds = ['field-id-0', 'field-id-1', 'field-id-2', 'field-id-3'];
|
||||
const filterOrder = [1, 0, 3, 2];
|
||||
const filters = filterOrder.map(v => ({ id: fieldIds[v] }));
|
||||
const headers = [0, 1, 2, 3].map(v => ({
|
||||
const filters = filterOrder.map((v) => ({ id: fieldIds[v] }));
|
||||
const headers = [0, 1, 2, 3].map((v) => ({
|
||||
id: fieldIds[v],
|
||||
Header: `HeaDer-${v}`,
|
||||
}));
|
||||
|
||||
describe('FilterStatusComponent hooks', () => {
|
||||
const context = { headers, state: { filters } };
|
||||
const mockTableContext = (newContext) => {
|
||||
React.useContext.mockReturnValueOnce(newContext);
|
||||
};
|
||||
beforeEach(() => {
|
||||
context.setAllFilters = jest.fn();
|
||||
});
|
||||
it('returns empty dict if setAllFilters or state.filters is falsey', () => {
|
||||
mockTableContext({ ...context, setAllFilters: null });
|
||||
expect(module.filterHooks()).toEqual({});
|
||||
mockTableContext({ ...context, state: { filters: null } });
|
||||
expect(module.filterHooks()).toEqual({});
|
||||
});
|
||||
describe('clearFilters', () => {
|
||||
it('uses React.useCallback to clear filters, only once', () => {
|
||||
mockTableContext(context);
|
||||
const { cb, prereqs } = module.filterHooks().clearFilters.useCallback;
|
||||
expect(prereqs).toEqual([context.setAllFilters]);
|
||||
expect(context.setAllFilters).not.toHaveBeenCalled();
|
||||
cb();
|
||||
expect(context.setAllFilters).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
describe('filterNames', () => {
|
||||
it('returns list of Header values by filter order', () => {
|
||||
mockTableContext(context);
|
||||
expect(module.filterHooks().filterNames).toEqual(
|
||||
filterOrder.map(v => headers[v].Header),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('FilterStatusComponent component', () => {
|
||||
const props = {
|
||||
className: 'css-class-name',
|
||||
@@ -58,34 +25,98 @@ describe('FilterStatusComponent component', () => {
|
||||
buttonClassName: 'css-class-name-for-button',
|
||||
showFilteredFields: true,
|
||||
};
|
||||
const hookProps = {
|
||||
clearFilters: jest.fn().mockName('hookProps.clearFilters'),
|
||||
filterNames: ['filter-name-0', 'filter-name-1'],
|
||||
};
|
||||
const { FilterStatusComponent } = module;
|
||||
const mockHooks = (value) => {
|
||||
jest.spyOn(module, 'filterHooks').mockReturnValueOnce(value);
|
||||
|
||||
const renderWithContext = (contextValue, componentProps = props) => {
|
||||
const TestWrapper = ({ children }) => (
|
||||
<DataTableContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</DataTableContext.Provider>
|
||||
);
|
||||
TestWrapper.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
return render(
|
||||
<TestWrapper>
|
||||
<FilterStatusComponent {...componentProps} />
|
||||
</TestWrapper>,
|
||||
);
|
||||
};
|
||||
describe('snapshot', () => {
|
||||
describe('with filters', () => {
|
||||
test('showFilteredFields', () => {
|
||||
mockHooks(hookProps);
|
||||
const el = shallow(<FilterStatusComponent {...props} />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('showFilteredFields=false - hide filterTexts', () => {
|
||||
mockHooks(hookProps);
|
||||
const el = shallow(
|
||||
<FilterStatusComponent {...props} showFilteredFields={false} />,
|
||||
);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('behavior', () => {
|
||||
it('does not render when there are no filters', () => {
|
||||
const contextValue = {
|
||||
headers,
|
||||
state: { filters: null },
|
||||
setAllFilters: jest.fn(),
|
||||
};
|
||||
const { container } = renderWithContext(contextValue);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render when setAllFilters is not available', () => {
|
||||
const contextValue = { headers, state: { filters }, setAllFilters: null };
|
||||
const { container } = renderWithContext(contextValue);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('renders clear filters button with correct text when filters exist', () => {
|
||||
const contextValue = {
|
||||
headers,
|
||||
state: { filters },
|
||||
setAllFilters: jest.fn(),
|
||||
};
|
||||
const { getByText } = renderWithContext(contextValue);
|
||||
expect(getByText(props.clearFiltersText)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays filtered field names when showFilteredFields is true', () => {
|
||||
const contextValue = {
|
||||
headers,
|
||||
state: { filters },
|
||||
setAllFilters: jest.fn(),
|
||||
};
|
||||
const { getByText } = renderWithContext(contextValue);
|
||||
const expectedFilterNames = filterOrder.map((v) => headers[v].Header);
|
||||
expectedFilterNames.forEach((name) => {
|
||||
expect(getByText(name, { exact: false })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
test('without filters', () => {
|
||||
mockHooks({});
|
||||
const el = shallow(<FilterStatusComponent {...props} />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
expect(el.isEmptyRender()).toEqual(true);
|
||||
|
||||
it('does not display filtered field names when showFilteredFields is false', () => {
|
||||
const contextValue = {
|
||||
headers,
|
||||
state: { filters },
|
||||
setAllFilters: jest.fn(),
|
||||
};
|
||||
const { queryByText } = renderWithContext(contextValue, {
|
||||
...props,
|
||||
showFilteredFields: false,
|
||||
});
|
||||
expect(queryByText(/Filtered by/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('applies correct CSS classes to the component', () => {
|
||||
const contextValue = {
|
||||
headers,
|
||||
state: { filters },
|
||||
setAllFilters: jest.fn(),
|
||||
};
|
||||
const { container } = renderWithContext(contextValue);
|
||||
expect(container.firstChild).toHaveClass(props.className);
|
||||
});
|
||||
|
||||
it('calls setAllFilters with empty array when clear button is clicked', () => {
|
||||
const setAllFilters = jest.fn();
|
||||
const contextValue = { headers, state: { filters }, setAllFilters };
|
||||
const { getByText } = renderWithContext(contextValue);
|
||||
const clearButton = getByText(props.clearFiltersText);
|
||||
clearButton.click();
|
||||
expect(setAllFilters).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
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 { selectors, thunkActions } from 'data/redux';
|
||||
import { ListError, mapDispatchToProps, mapStateToProps } from './ListError';
|
||||
import messages from './messages';
|
||||
|
||||
import { formatMessage } from 'testUtils';
|
||||
import {
|
||||
ListError,
|
||||
mapDispatchToProps,
|
||||
mapStateToProps,
|
||||
} from './ListError';
|
||||
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'),
|
||||
},
|
||||
},
|
||||
thunkActions: {
|
||||
@@ -27,41 +26,60 @@ jest.mock('data/services/lms/urls', () => ({
|
||||
openResponse: (courseId) => `api/openResponse/${courseId}`,
|
||||
}));
|
||||
|
||||
let el;
|
||||
jest.useFakeTimers('modern');
|
||||
|
||||
describe('ListError component', () => {
|
||||
describe('component', () => {
|
||||
const props = {
|
||||
courseId: 'test-course-id',
|
||||
};
|
||||
beforeEach(() => {
|
||||
props.loadSelectionForReview = jest.fn();
|
||||
props.intl = { formatMessage };
|
||||
props.initializeApp = jest.fn();
|
||||
const props = {
|
||||
courseId: 'test-course-id',
|
||||
initializeApp: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('behavior', () => {
|
||||
it('renders error alert with proper styling', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListError {...props} /></IntlProvider>);
|
||||
const alert = screen.getByRole('alert');
|
||||
expect(alert).toBeInTheDocument();
|
||||
expect(alert).toHaveClass('alert-danger');
|
||||
});
|
||||
describe('render tests', () => {
|
||||
beforeEach(() => {
|
||||
el = shallow(<ListError {...props} />);
|
||||
});
|
||||
test('snapshot', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('displays error heading and message', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListError {...props} /></IntlProvider>);
|
||||
const heading = screen.getByRole('alert').querySelector('.alert-heading');
|
||||
expect(heading).toBeInTheDocument();
|
||||
expect(heading).toHaveTextContent(messages.loadErrorHeading.defaultMessage);
|
||||
});
|
||||
|
||||
it('displays try again button', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListError {...props} /></IntlProvider>);
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveClass('btn-primary');
|
||||
});
|
||||
|
||||
it('calls initializeApp when try again button is clicked', async () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListError {...props} /></IntlProvider>);
|
||||
const user = userEvent.setup();
|
||||
const button = screen.getByRole('button');
|
||||
await user.click(button);
|
||||
expect(props.initializeApp).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapDispatchToProps', () => {
|
||||
it('loads initializeApp from thunkActions.app.initialize', () => {
|
||||
expect(mapDispatchToProps.initializeApp).toEqual(thunkActions.app.initialize);
|
||||
it('maps initializeApp from thunkActions.app.initialize', () => {
|
||||
expect(mapDispatchToProps.initializeApp).toEqual(
|
||||
thunkActions.app.initialize,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EmptySubmission component component snapshot 1`] = `
|
||||
<div
|
||||
className="empty-submission"
|
||||
>
|
||||
<img
|
||||
alt="empty state"
|
||||
src="./assets/emptyState.svg"
|
||||
/>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Nothing here yet"
|
||||
description="Empty table for the submission table title"
|
||||
id="ora-grading.ListView.noResultsFoundTitle"
|
||||
/>
|
||||
</h3>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="When learners submit responses, they will appear here"
|
||||
description="Empty table messages"
|
||||
id="ora-grading.ListView.noResultsFoundBody"
|
||||
/>
|
||||
</p>
|
||||
<Hyperlink
|
||||
className="py-4"
|
||||
destination="openResponseUrl(test-course-id)"
|
||||
>
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Back to all open responses"
|
||||
description="Breadcrumbs link text to return to ORA list in LMS"
|
||||
id="ora-grading.ListView.ListViewBreadcrumbs.backToResponses"
|
||||
/>
|
||||
</Button>
|
||||
</Hyperlink>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,37 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FilterStatusComponent component snapshot with filters showFilteredFields 1`] = `
|
||||
<div
|
||||
className="css-class-name"
|
||||
>
|
||||
<p>
|
||||
Filtered by
|
||||
filter-name-0, filter-name-1
|
||||
</p>
|
||||
<Button
|
||||
className="css-class-name-for-button"
|
||||
onClick={[MockFunction hookProps.clearFilters]}
|
||||
size="button-size"
|
||||
variant="button-variant"
|
||||
>
|
||||
clear-filter-text
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`FilterStatusComponent component snapshot with filters showFilteredFields=false - hide filterTexts 1`] = `
|
||||
<div
|
||||
className="css-class-name"
|
||||
>
|
||||
<Button
|
||||
className="css-class-name-for-button"
|
||||
onClick={[MockFunction hookProps.clearFilters]}
|
||||
size="button-size"
|
||||
variant="button-variant"
|
||||
>
|
||||
clear-filter-text
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`FilterStatusComponent component snapshot without filters 1`] = `null`;
|
||||
@@ -1,48 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ListError component component render tests snapshot 1`] = `
|
||||
<Alert
|
||||
actions={
|
||||
[
|
||||
<Button
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Reload submissions"
|
||||
description="Reload button text in case of network failure"
|
||||
id="ora-grading.ListView.reloadSubmissions"
|
||||
/>
|
||||
</Button>,
|
||||
]
|
||||
}
|
||||
variant="danger"
|
||||
>
|
||||
<Alert.Heading>
|
||||
<FormattedMessage
|
||||
defaultMessage="Error loading submissions"
|
||||
description="Initialization failure alert header"
|
||||
id="ora-grading.ListView.loadErrorHeading"
|
||||
/>
|
||||
</Alert.Heading>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="An error occurred while loading the submissions for this response. Try reloading the page or going {backToResponses}."
|
||||
description="Initialization failure alert message line 2"
|
||||
id="ora-grading.ListView.loadErrorMessage1"
|
||||
values={
|
||||
{
|
||||
"backToResponses": <Hyperlink
|
||||
destination="api/openResponse/test-course-id"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="back to all Open Responses"
|
||||
description="lowercase string for link to list of all open responses in lms"
|
||||
id="ora-grading.ListView.backToResponsesLowercase"
|
||||
/>
|
||||
</Hyperlink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
`;
|
||||
@@ -1,56 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ListView component component snapshots error 1`] = `
|
||||
<Container
|
||||
className="py-4"
|
||||
>
|
||||
<ListError />
|
||||
<ReviewModal />
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`ListView component component snapshots loaded has data 1`] = `
|
||||
<Container
|
||||
className="py-4"
|
||||
>
|
||||
<Fragment>
|
||||
<ListViewBreadcrumb />
|
||||
<SubmissionsTable />
|
||||
</Fragment>
|
||||
<ReviewModal />
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`ListView component component snapshots loaded with no data 1`] = `
|
||||
<Container
|
||||
className="py-4"
|
||||
>
|
||||
<EmptySubmission
|
||||
courseId="test-course-id"
|
||||
/>
|
||||
<ReviewModal />
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`ListView component component snapshots loading 1`] = `
|
||||
<Container
|
||||
className="py-4"
|
||||
>
|
||||
<div
|
||||
className="w-100 h-100 text-center"
|
||||
>
|
||||
<Spinner
|
||||
animation="border"
|
||||
variant="primary"
|
||||
/>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
defaultMessage="Loading responses"
|
||||
description="loading text for submission response list"
|
||||
id="ora-grading.ListView.loadingResponses"
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<ReviewModal />
|
||||
</Container>
|
||||
`;
|
||||
@@ -1,47 +1,97 @@
|
||||
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 { selectors, thunkActions } from 'data/redux';
|
||||
import { RequestKeys } from 'data/constants/requests';
|
||||
|
||||
import { formatMessage } from 'testUtils';
|
||||
import { ListView, mapStateToProps, mapDispatchToProps } from '.';
|
||||
import messages from './messages';
|
||||
|
||||
jest.mock('components/StatusBadge', () => 'StatusBadge');
|
||||
jest.mock('containers/ReviewModal', () => 'ReviewModal');
|
||||
jest.mock('./ListViewBreadcrumb', () => 'ListViewBreadcrumb');
|
||||
jest.mock('./ListError', () => 'ListError');
|
||||
jest.mock('./SubmissionsTable', () => 'SubmissionsTable');
|
||||
jest.mock('./EmptySubmission', () => 'EmptySubmission');
|
||||
jest.unmock('@openedx/paragon');
|
||||
jest.unmock('react');
|
||||
jest.unmock('@edx/frontend-platform/i18n');
|
||||
|
||||
jest.mock('containers/ReviewModal', () => {
|
||||
const ReviewModal = () => <div data-testid="review-modal">ReviewModal</div>;
|
||||
return ReviewModal;
|
||||
});
|
||||
|
||||
jest.mock('./ListViewBreadcrumb', () => {
|
||||
const ListViewBreadcrumb = () => (
|
||||
<div data-testid="breadcrumb">Back to all open responses</div>
|
||||
);
|
||||
return ListViewBreadcrumb;
|
||||
});
|
||||
|
||||
jest.mock('./ListError', () => {
|
||||
const ListError = () => (
|
||||
<div data-testid="list-error">
|
||||
<button type="button">Reload submissions</button>
|
||||
</div>
|
||||
);
|
||||
return ListError;
|
||||
});
|
||||
|
||||
jest.mock('./SubmissionsTable', () => {
|
||||
const SubmissionsTable = () => (
|
||||
<div data-testid="submissions-table">SubmissionsTable</div>
|
||||
);
|
||||
return SubmissionsTable;
|
||||
});
|
||||
|
||||
jest.mock('./EmptySubmission', () => {
|
||||
const EmptySubmission = () => (
|
||||
<div data-testid="empty-submission">
|
||||
<h3>Nothing here yet</h3>
|
||||
<p>When learners submit responses, they will appear here</p>
|
||||
</div>
|
||||
);
|
||||
return EmptySubmission;
|
||||
});
|
||||
|
||||
jest.mock('data/redux', () => ({
|
||||
selectors: {
|
||||
app: {
|
||||
courseId: (...args) => ({ courseId: args }),
|
||||
isEnabled: () => false,
|
||||
oraName: () => 'Test ORA Name',
|
||||
},
|
||||
requests: {
|
||||
isCompleted: (...args) => ({ isCompleted: args }),
|
||||
isFailed: (...args) => ({ isFailed: args }),
|
||||
allowNavigation: () => true,
|
||||
},
|
||||
submissions: {
|
||||
isEmptySubmissionData: (...args) => ({ isEmptySubmissionData: args }),
|
||||
},
|
||||
grading: {
|
||||
activeIndex: () => 0,
|
||||
selectionLength: () => 1,
|
||||
selected: {
|
||||
submissionUUID: () => null,
|
||||
overallFeedback: () => '',
|
||||
criteria: () => [],
|
||||
},
|
||||
next: {
|
||||
doesExist: () => false,
|
||||
},
|
||||
prev: {
|
||||
doesExist: () => false,
|
||||
},
|
||||
},
|
||||
},
|
||||
thunkActions: {
|
||||
app: {
|
||||
initialize: (...args) => ({ initialize: args }),
|
||||
},
|
||||
grading: {
|
||||
submitGrade: () => jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@openedx/paragon', () => ({
|
||||
Container: 'Container',
|
||||
Spinner: 'Spinner',
|
||||
}));
|
||||
|
||||
let el;
|
||||
jest.useFakeTimers('modern');
|
||||
|
||||
describe('ListView component', () => {
|
||||
describe('component', () => {
|
||||
const props = {
|
||||
@@ -49,37 +99,75 @@ describe('ListView component', () => {
|
||||
isLoaded: false,
|
||||
hasError: false,
|
||||
isEmptySubmissionData: false,
|
||||
initializeApp: jest.fn(),
|
||||
intl: { formatMessage },
|
||||
};
|
||||
beforeEach(() => {
|
||||
props.initializeApp = jest.fn();
|
||||
props.intl = { formatMessage };
|
||||
});
|
||||
describe('snapshots', () => {
|
||||
beforeEach(() => {
|
||||
el = shallow(<ListView {...props} />);
|
||||
});
|
||||
test('loading', () => {
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('loaded has data', () => {
|
||||
el = shallow(<ListView {...props} isLoaded />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('loaded with no data', () => {
|
||||
el = shallow(<ListView {...props} isLoaded isEmptySubmissionData />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('error', () => {
|
||||
el = shallow(<ListView {...props} hasError />);
|
||||
expect(el.snapshot).toMatchSnapshot();
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe('behavior', () => {
|
||||
it('calls initializeApp on load', () => {
|
||||
el = shallow(<ListView {...props} />);
|
||||
expect(props.initializeApp).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('displays loading spinner and message when not loaded and no error', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListView {...props} /></IntlProvider>);
|
||||
|
||||
// Check for loading message
|
||||
expect(screen.getByText(messages.loadingResponses.defaultMessage)).toBeInTheDocument();
|
||||
|
||||
// Check for spinner by finding element with spinner class
|
||||
const spinner = document.querySelector('.pgn__spinner');
|
||||
expect(spinner).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays ListViewBreadcrumb and SubmissionsTable when loaded with data', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListView {...props} isLoaded /></IntlProvider>);
|
||||
|
||||
expect(
|
||||
screen.getByText('Back to all open responses'),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByTestId('submissions-table')).toBeInTheDocument();
|
||||
expect(screen.queryByText('FormattedMessage')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays EmptySubmission component when loaded but has no submission data', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListView {...props} isLoaded isEmptySubmissionData /></IntlProvider>);
|
||||
|
||||
expect(
|
||||
screen.getByRole('heading', { name: 'Nothing here yet' }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'When learners submit responses, they will appear here',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('Back to all open responses'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('submissions-table')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays ListError component when there is an error', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListView {...props} hasError /></IntlProvider>);
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Reload submissions' }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('FormattedMessage')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('always displays ReviewModal component regardless of state', () => {
|
||||
const { rerender } = render(<IntlProvider locale="en" messages={{}}><ListView {...props} /></IntlProvider>);
|
||||
expect(screen.getByText('ReviewModal')).toBeInTheDocument();
|
||||
|
||||
rerender(<IntlProvider locale="en" messages={{}}><ListView {...props} isLoaded /></IntlProvider>);
|
||||
expect(screen.getByText('ReviewModal')).toBeInTheDocument();
|
||||
|
||||
rerender(<IntlProvider locale="en" messages={{}}><ListView {...props} hasError /></IntlProvider>);
|
||||
expect(screen.getByText('ReviewModal')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls initializeApp on component mount', () => {
|
||||
render(<IntlProvider locale="en" messages={{}}><ListView {...props} /></IntlProvider>);
|
||||
expect(props.initializeApp).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
@@ -89,27 +177,27 @@ describe('ListView component', () => {
|
||||
beforeEach(() => {
|
||||
mapped = mapStateToProps(testState);
|
||||
});
|
||||
test('courseId loads from app.courseId', () => {
|
||||
it('maps courseId from app.courseId selector', () => {
|
||||
expect(mapped.courseId).toEqual(selectors.app.courseId(testState));
|
||||
});
|
||||
test('isLoaded loads from requests.isCompleted', () => {
|
||||
it('maps isLoaded from requests.isCompleted selector', () => {
|
||||
expect(mapped.isLoaded).toEqual(
|
||||
selectors.requests.isCompleted(testState, { requestKey }),
|
||||
);
|
||||
});
|
||||
test('hasError loads from requests.isFailed', () => {
|
||||
it('maps hasError from requests.isFailed selector', () => {
|
||||
expect(mapped.hasError).toEqual(
|
||||
selectors.requests.isFailed(testState, { requestKey }),
|
||||
);
|
||||
});
|
||||
test('isEmptySubmissionData loads from submissions.isEmptySubmissionData', () => {
|
||||
it('maps isEmptySubmissionData from submissions.isEmptySubmissionData selector', () => {
|
||||
expect(mapped.isEmptySubmissionData).toEqual(
|
||||
selectors.submissions.isEmptySubmissionData(testState),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('mapDispatchToProps', () => {
|
||||
it('loads initializeApp from thunkActions.app.initialize', () => {
|
||||
it('maps initializeApp to thunkActions.app.initialize', () => {
|
||||
expect(mapDispatchToProps.initializeApp).toEqual(
|
||||
thunkActions.app.initialize,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user