feat: make maquerade stateful
feat: implement loading screen chore: update selector test chore: fiter/sort padding chore: update fail masquerade state chore: update unit test chore: Update src/containers/MasqueradeBar/index.jsx Co-authored-by: Ben Warzeski <bwarzeski@edx.org> chore: update test Co-authored-by: Ben Warzeski <bwarzeski@edx.org> chore: update snapshot
This commit is contained in:
committed by
leangseu-edx
parent
7f210e7483
commit
84446fe5cd
@@ -92,7 +92,7 @@ export const CourseFilterControls = ({
|
||||
<div className="filter-form-col">
|
||||
<FilterForm {...{ filters, handleFilterChange }} />
|
||||
</div>
|
||||
<hr className="h-100 bg-primary-200 m-1" />
|
||||
<hr className="h-100 bg-primary-200 mx-3 my-0" />
|
||||
<div className="filter-form-col text-left m-1">
|
||||
<SortForm {...{ sortBy, handleSortChange }} />
|
||||
</div>
|
||||
|
||||
@@ -98,7 +98,7 @@ exports[`CourseFilterControls snapshot is not mobile 1`] = `
|
||||
/>
|
||||
</div>
|
||||
<hr
|
||||
className="h-100 bg-primary-200 m-1"
|
||||
className="h-100 bg-primary-200 mx-3 my-0"
|
||||
/>
|
||||
<div
|
||||
className="filter-form-col text-left m-1"
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CourseList snapshots renders loading 1`] = `
|
||||
<div
|
||||
className="course-list-loading"
|
||||
>
|
||||
<Spinner
|
||||
animation="border"
|
||||
className="mie-3"
|
||||
screenReaderText="loading"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CourseList snapshots with filters 1`] = `
|
||||
<div
|
||||
className="course-list-container"
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useCheckboxSetValues } from '@edx/paragon';
|
||||
import { StrictDict } from 'utils';
|
||||
import { actions, hooks as appHooks } from 'data/redux';
|
||||
import { ListPageSize, SortKeys } from 'data/constants/app';
|
||||
import { RequestKeys } from 'data/constants/requests';
|
||||
|
||||
import * as module from './hooks';
|
||||
|
||||
@@ -27,6 +28,8 @@ export const useCourseListData = () => {
|
||||
});
|
||||
const handleRemoveFilter = (filter) => () => setFilters.remove(filter);
|
||||
const setPageNumber = (value) => dispatch(actions.app.setPageNumber(value));
|
||||
const initIsPending = appHooks.useIsPendingRequest(RequestKeys.initialize);
|
||||
|
||||
return {
|
||||
pageNumber,
|
||||
numPages,
|
||||
@@ -40,6 +43,7 @@ export const useCourseListData = () => {
|
||||
handleRemoveFilter,
|
||||
},
|
||||
showFilters: filters.length > 0,
|
||||
initIsPending,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ jest.mock('data/redux', () => ({
|
||||
hooks: {
|
||||
useCurrentCourseList: jest.fn(),
|
||||
usePageNumber: jest.fn(() => 23),
|
||||
useIsPendingRequest: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -34,6 +35,11 @@ const testCheckboxSetValues = [testFilters, testSetFilters];
|
||||
|
||||
describe('CourseList hooks', () => {
|
||||
let out;
|
||||
|
||||
appHooks.useCurrentCourseList.mockReturnValue(testListData);
|
||||
appHooks.useIsPendingRequest.mockReturnValue(false);
|
||||
paragon.useCheckboxSetValues.mockImplementation(() => testCheckboxSetValues);
|
||||
|
||||
describe('state values', () => {
|
||||
state.testGetter(state.keys.sortBy);
|
||||
jest.clearAllMocks();
|
||||
@@ -44,8 +50,6 @@ describe('CourseList hooks', () => {
|
||||
beforeEach(() => {
|
||||
state.mock();
|
||||
state.mockVal(state.keys.sortBy, testSortBy);
|
||||
paragon.useCheckboxSetValues.mockImplementationOnce(() => testCheckboxSetValues);
|
||||
appHooks.useCurrentCourseList.mockReturnValueOnce(testListData);
|
||||
out = hooks.useCourseListData();
|
||||
});
|
||||
describe('behavior', () => {
|
||||
@@ -72,12 +76,17 @@ describe('CourseList hooks', () => {
|
||||
test('showFilters is true iff filters is not empty', () => {
|
||||
expect(out.showFilters).toEqual(true);
|
||||
state.mockVal(state.keys.sortBy, testSortBy);
|
||||
appHooks.useCurrentCourseList.mockReturnValueOnce(testListData);
|
||||
paragon.useCheckboxSetValues.mockReturnValueOnce([[], testSetFilters]);
|
||||
out = hooks.useCourseListData();
|
||||
// checkbox values default to returning a list, and were only overridden once.
|
||||
// Thus this time, the values for the list should be empty.
|
||||
// don't show filter when list is empty.
|
||||
expect(out.showFilters).toEqual(false);
|
||||
});
|
||||
test('initIsPending loads from useIsPendingRequest', () => {
|
||||
expect(out.initIsPending).toEqual(false);
|
||||
appHooks.useIsPendingRequest.mockReturnValueOnce(true);
|
||||
out = hooks.useCourseListData();
|
||||
expect(out.initIsPending).toEqual(true);
|
||||
});
|
||||
describe('filterOptions', () => {
|
||||
test('sortBy and setSortBy are connected to the state value', () => {
|
||||
expect(out.filterOptions.sortBy).toEqual(testSortBy);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Pagination } from '@edx/paragon';
|
||||
import { Pagination, Spinner } from '@edx/paragon';
|
||||
|
||||
import { ActiveCourseFilters, CourseFilterControls } from 'containers/CourseFilterControls';
|
||||
import {
|
||||
ActiveCourseFilters,
|
||||
CourseFilterControls,
|
||||
} from 'containers/CourseFilterControls';
|
||||
import CourseCard from 'containers/CourseCard';
|
||||
|
||||
import { useCourseListData } from './hooks';
|
||||
@@ -20,21 +23,21 @@ export const CourseList = () => {
|
||||
numPages,
|
||||
showFilters,
|
||||
visibleList,
|
||||
initIsPending,
|
||||
} = useCourseListData();
|
||||
return (
|
||||
return initIsPending ? (
|
||||
<div className="course-list-loading">
|
||||
<Spinner animation="border" className="mie-3" screenReaderText="loading" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="course-list-container">
|
||||
<div id="course-list-heading-container">
|
||||
<h2 className="my-3">
|
||||
{formatMessage(messages.myCourses)}
|
||||
</h2>
|
||||
<div
|
||||
id="course-filter-controls-container"
|
||||
className="text-right"
|
||||
>
|
||||
<h2 className="my-3">{formatMessage(messages.myCourses)}</h2>
|
||||
<div id="course-filter-controls-container" className="text-right">
|
||||
<CourseFilterControls {...filterOptions} />
|
||||
</div>
|
||||
</div>
|
||||
{ showFilters && (
|
||||
{showFilters && (
|
||||
<div id="course-list-active-filters-container">
|
||||
<ActiveCourseFilters {...filterOptions} />
|
||||
</div>
|
||||
@@ -43,7 +46,7 @@ export const CourseList = () => {
|
||||
{visibleList.map(({ cardId }) => (
|
||||
<CourseCard key={cardId} cardId={cardId} />
|
||||
))}
|
||||
{(numPages > 1) && (
|
||||
{numPages > 1 && (
|
||||
<Pagination
|
||||
variant="secondary"
|
||||
paginationLabel="Course List"
|
||||
@@ -57,7 +60,6 @@ export const CourseList = () => {
|
||||
);
|
||||
};
|
||||
|
||||
CourseList.propTypes = {
|
||||
};
|
||||
CourseList.propTypes = {};
|
||||
|
||||
export default CourseList;
|
||||
|
||||
@@ -2,3 +2,10 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.course-list-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -20,6 +20,7 @@ describe('CourseList', () => {
|
||||
setPageNumber: jest.fn().mockName('setPageNumber'),
|
||||
showFilters: false,
|
||||
visibleList: [],
|
||||
initIsPending: false,
|
||||
};
|
||||
const createWrapper = (courseListData) => {
|
||||
useCourseListData.mockReturnValueOnce({
|
||||
@@ -30,6 +31,10 @@ describe('CourseList', () => {
|
||||
};
|
||||
|
||||
describe('snapshots', () => {
|
||||
it('renders loading', () => {
|
||||
const wrapper = createWrapper({ initIsPending: true });
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
test('with no filters', () => {
|
||||
const wrapper = createWrapper();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
@@ -10,7 +10,7 @@ exports[`Dashboard snapshots there are available dashboards 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Dashboard snapshots there are courses 1`] = `
|
||||
exports[`Dashboard snapshots there are courses, or they are still loading 1`] = `
|
||||
<div
|
||||
className="d-flex flex-column p-2"
|
||||
id="dashboard-container"
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
thunkActions,
|
||||
hooks as appHooks,
|
||||
} from 'data/redux';
|
||||
import { RequestKeys } from 'data/constants/requests';
|
||||
|
||||
import CourseList from 'containers/CourseList';
|
||||
import WidgetSidebar from 'containers/WidgetSidebar';
|
||||
@@ -25,10 +26,12 @@ export const Dashboard = () => {
|
||||
const hasCourses = appHooks.useHasCourses();
|
||||
const hasAvailableDashboards = appHooks.useHasAvailableDashboards();
|
||||
const showSelectSessionModal = appHooks.useShowSelectSessionModal();
|
||||
const initIsPending = appHooks.useIsPendingRequest(RequestKeys.initialize);
|
||||
|
||||
return (
|
||||
<div id="dashboard-container" className="d-flex flex-column p-2">
|
||||
{hasAvailableDashboards && <EnterpriseDashboardModal />}
|
||||
{hasCourses ? (
|
||||
{initIsPending || (!initIsPending && hasCourses) ? (
|
||||
<Container fluid size="xl">
|
||||
<Row>
|
||||
<Col
|
||||
|
||||
@@ -20,6 +20,7 @@ jest.mock('data/redux', () => ({
|
||||
useHasCourses: jest.fn(),
|
||||
useHasAvailableDashboards: jest.fn(),
|
||||
useShowSelectSessionModal: jest.fn(),
|
||||
useIsPendingRequest: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -34,21 +35,32 @@ describe('Dashboard', () => {
|
||||
hasCourses,
|
||||
hasAvailableDashboards,
|
||||
showSelectSessionModal,
|
||||
initIsPending,
|
||||
}) => {
|
||||
hooks.useHasCourses.mockReturnValueOnce(hasCourses);
|
||||
hooks.useHasAvailableDashboards.mockReturnValueOnce(hasAvailableDashboards);
|
||||
hooks.useShowSelectSessionModal.mockReturnValueOnce(showSelectSessionModal);
|
||||
hooks.useIsPendingRequest.mockReturnValueOnce(initIsPending);
|
||||
return shallow(<Dashboard />);
|
||||
};
|
||||
|
||||
describe('snapshots', () => {
|
||||
test('there are courses', () => {
|
||||
const wrapper = createWrapper({
|
||||
test('there are courses, or they are still loading', () => {
|
||||
const pendingNoCoursesWrapper = createWrapper({
|
||||
hasCourses: false,
|
||||
hasAvailableDashboards: false,
|
||||
showSelectSessionModal: false,
|
||||
initIsPending: true,
|
||||
});
|
||||
expect(pendingNoCoursesWrapper).toMatchSnapshot();
|
||||
|
||||
const doneLoadingWithCoursesWrapper = createWrapper({
|
||||
hasCourses: true,
|
||||
hasAvailableDashboards: false,
|
||||
showSelectSessionModal: false,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(doneLoadingWithCoursesWrapper).toEqual(pendingNoCoursesWrapper);
|
||||
});
|
||||
|
||||
test('there are no courses', () => {
|
||||
@@ -56,6 +68,7 @@ describe('Dashboard', () => {
|
||||
hasCourses: false,
|
||||
hasAvailableDashboards: false,
|
||||
showSelectSessionModal: false,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -65,6 +78,7 @@ describe('Dashboard', () => {
|
||||
hasCourses: false,
|
||||
hasAvailableDashboards: true,
|
||||
showSelectSessionModal: false,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -74,6 +88,7 @@ describe('Dashboard', () => {
|
||||
hasCourses: false,
|
||||
hasAvailableDashboards: false,
|
||||
showSelectSessionModal: true,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -85,6 +100,7 @@ describe('Dashboard', () => {
|
||||
hasCourses: false,
|
||||
hasAvailableDashboards: false,
|
||||
showSelectSessionModal: false,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper.find(EmptyCourse).length).toEqual(1);
|
||||
expect(wrapper.find(CourseList).length).toEqual(0);
|
||||
@@ -97,6 +113,7 @@ describe('Dashboard', () => {
|
||||
hasCourses: true,
|
||||
hasAvailableDashboards: true,
|
||||
showSelectSessionModal: true,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper.find(EmptyCourse).length).toEqual(0);
|
||||
expect(wrapper.find(CourseList).length).toEqual(1);
|
||||
@@ -109,6 +126,7 @@ describe('Dashboard', () => {
|
||||
hasCourses: true,
|
||||
hasAvailableDashboards: true,
|
||||
showSelectSessionModal: false,
|
||||
initIsPending: false,
|
||||
});
|
||||
expect(wrapper.find(EmptyCourse).length).toEqual(0);
|
||||
expect(wrapper.find(CourseList).length).toEqual(1);
|
||||
|
||||
@@ -20,12 +20,16 @@ exports[`MasqueradeBar snapshot can masquerade 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</FormGroup>
|
||||
<Button
|
||||
<StatefulButton
|
||||
disabled={true}
|
||||
variant="danger"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
labels={
|
||||
Object {
|
||||
"default": "Submit",
|
||||
}
|
||||
}
|
||||
state="default"
|
||||
variant="brand"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -49,12 +53,16 @@ exports[`MasqueradeBar snapshot can masquerade with input 1`] = `
|
||||
value="test"
|
||||
/>
|
||||
</FormGroup>
|
||||
<Button
|
||||
<StatefulButton
|
||||
disabled={false}
|
||||
variant="danger"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
labels={
|
||||
Object {
|
||||
"default": "Submit",
|
||||
}
|
||||
}
|
||||
state="default"
|
||||
variant="brand"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -80,17 +88,55 @@ exports[`MasqueradeBar snapshot is masquerading failed with error 1`] = `
|
||||
value=""
|
||||
/>
|
||||
<FormControlFeedback
|
||||
hasIcon={false}
|
||||
type="invalid"
|
||||
>
|
||||
test-error
|
||||
</FormControlFeedback>
|
||||
</FormGroup>
|
||||
<Button
|
||||
<StatefulButton
|
||||
disabled={true}
|
||||
variant="danger"
|
||||
labels={
|
||||
Object {
|
||||
"default": "Submit",
|
||||
}
|
||||
}
|
||||
state="default"
|
||||
variant="brand"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MasqueradeBar snapshot is masquerading pending 1`] = `
|
||||
<div
|
||||
className="masquerade-bar"
|
||||
>
|
||||
<FormLabel
|
||||
className="masquerade-form-label"
|
||||
inline={true}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
View as:
|
||||
</FormLabel>
|
||||
<FormGroup
|
||||
className="masquerade-form-input"
|
||||
isInvalid={false}
|
||||
>
|
||||
<FormControl
|
||||
floatingLabel="Student username or email"
|
||||
onChange={[MockFunction handleMasqueradeInputChange]}
|
||||
value=""
|
||||
/>
|
||||
</FormGroup>
|
||||
<StatefulButton
|
||||
disabled={true}
|
||||
labels={
|
||||
Object {
|
||||
"default": "Submit",
|
||||
}
|
||||
}
|
||||
state="pending"
|
||||
variant="brand"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { thunkActions, selectors } from 'data/redux';
|
||||
import { thunkActions, hooks as appHooks } from 'data/redux';
|
||||
import { StrictDict } from 'utils';
|
||||
import * as module from './hooks';
|
||||
|
||||
@@ -32,14 +32,16 @@ export const useMasqueradeBarData = ({
|
||||
const {
|
||||
isMasquerading,
|
||||
isMasqueradingFailed,
|
||||
isMasqueradingPending,
|
||||
masqueradeError,
|
||||
} = useSelector(selectors.requests.masquerade);
|
||||
} = appHooks.useMasqueradeData();
|
||||
const { masqueradeInput, handleMasqueradeInputChange } = module.useMasqueradeInput();
|
||||
|
||||
return {
|
||||
canMasquerade,
|
||||
isMasquerading,
|
||||
isMasqueradingFailed,
|
||||
isMasqueradingPending,
|
||||
masqueradeError,
|
||||
masqueradeInput,
|
||||
handleMasqueradeSubmit,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { MockUseState } from 'testUtils';
|
||||
import { thunkActions } from 'data/redux';
|
||||
import { thunkActions, hooks as appHooks } from 'data/redux';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import * as hooks from './hooks';
|
||||
|
||||
jest.mock('data/redux', () => ({
|
||||
@@ -11,58 +10,70 @@ jest.mock('data/redux', () => ({
|
||||
clearMasquerade: jest.fn(),
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
requests: {
|
||||
masquerade: jest.fn(),
|
||||
},
|
||||
hooks: {
|
||||
useMasqueradeData: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const state = new MockUseState(hooks);
|
||||
|
||||
describe('MasqueradeBar hooks', () => {
|
||||
let out;
|
||||
const authenticatedUser = {
|
||||
administrator: true,
|
||||
};
|
||||
const defaultMasqueradeData = {
|
||||
isMasquerading: false,
|
||||
isMasqueradingFailed: false,
|
||||
isMasqueradingPending: false,
|
||||
masqueradeError: null,
|
||||
};
|
||||
const createHook = (masqueradeData = {}, user) => {
|
||||
appHooks.useMasqueradeData.mockReturnValueOnce({
|
||||
...defaultMasqueradeData,
|
||||
...masqueradeData,
|
||||
});
|
||||
return hooks.useMasqueradeBarData({ authenticatedUser: user || authenticatedUser });
|
||||
};
|
||||
|
||||
describe('state values', () => {
|
||||
state.testGetter(state.keys.masqueradeInput);
|
||||
});
|
||||
describe('useMasqueradeBarData', () => {
|
||||
beforeEach(() => {
|
||||
state.mock();
|
||||
out = hooks.useMasqueradeBarData({ authenticatedUser });
|
||||
});
|
||||
beforeEach(() => state.mock());
|
||||
afterEach(state.restore);
|
||||
test('canMasquerade', () => {
|
||||
const out = createHook();
|
||||
expect(out.canMasquerade).toEqual(true);
|
||||
});
|
||||
test('cannotMasquerade', () => {
|
||||
out = hooks.useMasqueradeBarData({
|
||||
authenticatedUser: {
|
||||
administrator: false,
|
||||
},
|
||||
});
|
||||
const out = createHook({}, { administrator: false });
|
||||
expect(out.canMasquerade).toEqual(false);
|
||||
});
|
||||
test('masqueradeError', () => {
|
||||
expect(out.masqueradeError).toBeUndefined();
|
||||
useSelector.mockReturnValueOnce({
|
||||
masqueradeError: 'test error',
|
||||
});
|
||||
out = hooks.useMasqueradeBarData({ authenticatedUser });
|
||||
let out = createHook();
|
||||
expect(out.masqueradeError).toBeNull();
|
||||
out = createHook({ masqueradeError: 'test error' });
|
||||
expect(out.masqueradeError).toEqual('test error');
|
||||
});
|
||||
test('isMasqueradePending', () => {
|
||||
let out = createHook();
|
||||
expect(out.isMasqueradingPending).toEqual(false);
|
||||
out = createHook({ isMasqueradingPending: true });
|
||||
expect(out.isMasqueradingPending).toEqual(true);
|
||||
});
|
||||
test('handleMasqueradeInputChange', () => {
|
||||
const out = createHook();
|
||||
expect(state.stateVals.masqueradeInput).toEqual('');
|
||||
out.handleMasqueradeInputChange({ target: { value: 'test' } });
|
||||
expect(state.setState.masqueradeInput).toHaveBeenCalledWith('test');
|
||||
});
|
||||
test('handleMasqueradeSubmit', () => {
|
||||
const out = createHook();
|
||||
out.handleMasqueradeSubmit('test')();
|
||||
expect(thunkActions.app.masqueradeAs).toHaveBeenCalledWith('test');
|
||||
});
|
||||
test('handleClearMasquerade', () => {
|
||||
const out = createHook();
|
||||
out.handleClearMasquerade();
|
||||
expect(thunkActions.app.clearMasquerade).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -3,11 +3,11 @@ import { AppContext } from '@edx/frontend-platform/react';
|
||||
|
||||
import {
|
||||
Chip,
|
||||
Button,
|
||||
FormControl,
|
||||
FormControlFeedback,
|
||||
FormLabel,
|
||||
FormGroup,
|
||||
StatefulButton,
|
||||
} from '@edx/paragon';
|
||||
import { Close } from '@edx/paragon/icons';
|
||||
|
||||
@@ -22,6 +22,7 @@ export const MasqueradeBar = () => {
|
||||
canMasquerade,
|
||||
isMasquerading,
|
||||
isMasqueradingFailed,
|
||||
isMasqueradingPending,
|
||||
masqueradeInput,
|
||||
masqueradeError,
|
||||
handleMasqueradeInputChange,
|
||||
@@ -59,18 +60,20 @@ export const MasqueradeBar = () => {
|
||||
floatingLabel={formatMessage(messages.StudentNameInput)}
|
||||
/>
|
||||
{isMasqueradingFailed && (
|
||||
<FormControlFeedback type="invalid">
|
||||
<FormControlFeedback type="invalid" hasIcon={false}>
|
||||
{masqueradeError}
|
||||
</FormControlFeedback>
|
||||
)}
|
||||
</FormGroup>
|
||||
<Button
|
||||
<StatefulButton
|
||||
disabled={!masqueradeInput.length}
|
||||
variant="danger"
|
||||
variant="brand"
|
||||
onClick={handleMasqueradeSubmit(masqueradeInput)}
|
||||
>
|
||||
{formatMessage(messages.SubmitButton)}
|
||||
</Button>
|
||||
labels={{
|
||||
default: formatMessage(messages.SubmitButton),
|
||||
}}
|
||||
state={isMasqueradingPending ? 'pending' : 'default'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('MasqueradeBar', () => {
|
||||
canMasquerade: true,
|
||||
isMasquerading: false,
|
||||
isMasqueradingFailed: false,
|
||||
isMasqueradingPending: false,
|
||||
masqueradeInput: '',
|
||||
masqueradeError: '',
|
||||
handleMasqueradeInputChange: jest.fn().mockName('handleMasqueradeInputChange'),
|
||||
@@ -57,5 +58,12 @@ describe('MasqueradeBar', () => {
|
||||
});
|
||||
expect(shallow(<MasqueradeBar />)).toMatchSnapshot();
|
||||
});
|
||||
test('is masquerading pending', () => {
|
||||
hooks.useMasqueradeBarData.mockReturnValueOnce({
|
||||
...masqueradeMockData,
|
||||
isMasqueradingPending: true,
|
||||
});
|
||||
expect(shallow(<MasqueradeBar />)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,3 +40,5 @@ export const useUpdateSelectSessionModalCallback = (dispatch, cardId) => () => d
|
||||
);
|
||||
|
||||
export const useMasqueradeData = () => useSelector(requestSelectors.masquerade);
|
||||
|
||||
export const useIsPendingRequest = (requestName) => useSelector(requestSelectors.isPending(requestName));
|
||||
|
||||
@@ -4,7 +4,7 @@ import { RequestStates, RequestKeys } from 'data/constants/requests';
|
||||
|
||||
export const requestStatus = (state, { requestKey }) => state.requests[requestKey];
|
||||
|
||||
const statusSelector = (fn) => (state, { requestKey }) => fn(state.requests[requestKey]);
|
||||
const statusSelector = (fn) => (requestKey) => (state) => fn(state.requests[requestKey]);
|
||||
|
||||
export const isInactive = ({ status }) => status === RequestStates.inactive;
|
||||
export const isPending = ({ status }) => status === RequestStates.pending;
|
||||
@@ -21,6 +21,7 @@ export const masquerade = (state) => {
|
||||
return {
|
||||
isMasquerading: isCompleted(request),
|
||||
isMasqueradingFailed: isFailed(request),
|
||||
isMasqueradingPending: isPending(request),
|
||||
masqueradeError: error(request),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,22 +20,23 @@ const testState = {
|
||||
[requestKey]: requestData,
|
||||
},
|
||||
};
|
||||
const mockUseSelector = (selector, state) => selector(state);
|
||||
const genRequests = (request) => ({
|
||||
requests: { [requestKey]: request },
|
||||
});
|
||||
const select = (selector, request) => (
|
||||
selector(genRequests(request), { requestKey })
|
||||
mockUseSelector(
|
||||
selector(requestKey), genRequests(request),
|
||||
)
|
||||
);
|
||||
describe('requests selectors unit tests', () => {
|
||||
test('requestStatus returns data associated with given key', () => {
|
||||
expect(selectors.requestStatus(testState, { requestKey })).toEqual(requestData);
|
||||
});
|
||||
const testStatusSelector = (selector, matchingRequest) => {
|
||||
expect(selector(testState, { requestKey })).toEqual(false);
|
||||
expect(selector(
|
||||
{ requests: { [requestKey]: matchingRequest } },
|
||||
{ requestKey },
|
||||
)).toEqual(true);
|
||||
expect(mockUseSelector(selector(requestKey), testState)).toEqual(false);
|
||||
expect(mockUseSelector(selector(requestKey),
|
||||
{ requests: { [requestKey]: matchingRequest } })).toEqual(true);
|
||||
};
|
||||
test('isInactive returns true iff the given request is inactive', () => {
|
||||
testStatusSelector(selectors.isInactive, inactiveRequest);
|
||||
@@ -78,6 +79,13 @@ describe('requests selectors unit tests', () => {
|
||||
expect(selectors.masquerade(mockResponse(completedRequest))).toEqual({
|
||||
isMasquerading: true,
|
||||
isMasqueradingFailed: false,
|
||||
isMasqueradingPending: false,
|
||||
masqueradeError: undefined,
|
||||
});
|
||||
expect(selectors.masquerade(mockResponse(pendingRequest))).toEqual({
|
||||
isMasquerading: false,
|
||||
isMasqueradingFailed: false,
|
||||
isMasqueradingPending: true,
|
||||
masqueradeError: undefined,
|
||||
});
|
||||
expect(selectors.masquerade(mockResponse({
|
||||
@@ -86,6 +94,7 @@ describe('requests selectors unit tests', () => {
|
||||
}))).toEqual({
|
||||
isMasquerading: false,
|
||||
isMasqueradingFailed: true,
|
||||
isMasqueradingPending: false,
|
||||
masqueradeError: testValue,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user