Merge pull request #7 from muselesscreator/tests_v1
Unit Tests, round 1 (Header Updates)
This commit is contained in:
@@ -10,9 +10,9 @@ import selectors from 'data/selectors';
|
||||
import ListView from 'containers/ListView';
|
||||
import './App.scss';
|
||||
|
||||
import { Header } from 'containers/CourseHeader';
|
||||
import Header from 'containers/CourseHeader';
|
||||
|
||||
const App = ({ courseMetadata }) => (
|
||||
export const App = ({ courseMetadata }) => (
|
||||
<Router>
|
||||
<div>
|
||||
<Header
|
||||
|
||||
@@ -1,75 +1,41 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import Footer from '@edx/frontend-component-footer';
|
||||
|
||||
import { routePath } from 'data/constants/app';
|
||||
import store from 'data/store';
|
||||
import ListView from 'containers/ListView';
|
||||
|
||||
import App from './App';
|
||||
import { App } from './App';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
BrowserRouter: () => 'BrowserRouter',
|
||||
Route: () => 'Route',
|
||||
Switch: () => 'Switch',
|
||||
}));
|
||||
jest.mock('react-redux', () => ({
|
||||
Provider: () => 'Provider',
|
||||
}));
|
||||
jest.mock('react-intl', () => ({
|
||||
IntlProvider: () => 'IntlProvider',
|
||||
}));
|
||||
jest.mock('data/constants/app', () => ({
|
||||
routePath: '/:courseId',
|
||||
}));
|
||||
jest.mock('@edx/frontend-component-footer', () => 'Footer');
|
||||
jest.mock('data/store', () => 'testStore');
|
||||
jest.mock('containers/ListView', () => 'ListView');
|
||||
jest.mock('containers/CourseHeader', () => 'CourseHeader');
|
||||
|
||||
const logo = 'fakeLogo.png';
|
||||
let el;
|
||||
let router;
|
||||
|
||||
describe('App router component', () => {
|
||||
const props = {
|
||||
courseMetadata: {
|
||||
org: 'course-org',
|
||||
number: 'course-number',
|
||||
title: 'course-title',
|
||||
},
|
||||
};
|
||||
test('snapshot', () => {
|
||||
expect(shallow(<App />)).toMatchSnapshot();
|
||||
expect(shallow(<App {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
describe('component', () => {
|
||||
beforeEach(() => {
|
||||
process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo;
|
||||
el = shallow(<App />);
|
||||
router = el.childAt(0).childAt(0);
|
||||
});
|
||||
describe('IntlProvider', () => {
|
||||
test('outer-wrapper component', () => {
|
||||
expect(el.type()).toBe(IntlProvider);
|
||||
});
|
||||
test('"en" locale', () => {
|
||||
expect(el.props().locale).toEqual('en');
|
||||
});
|
||||
});
|
||||
describe('Provider, inside IntlProvider', () => {
|
||||
test('first child, passed the redux store props', () => {
|
||||
expect(el.childAt(0).type()).toBe(Provider);
|
||||
expect(el.childAt(0).props().store).toEqual(store);
|
||||
});
|
||||
el = shallow(<App {...props} />);
|
||||
router = el.childAt(0);
|
||||
});
|
||||
describe('Router', () => {
|
||||
test('first child of Provider', () => {
|
||||
expect(router.type()).toBe(Router);
|
||||
});
|
||||
test('Routing - ListView is only route', () => {
|
||||
expect(router.find('main')).toEqual(shallow(
|
||||
<main>
|
||||
<Switch>
|
||||
<Route exact path={routePath} component={ListView} />
|
||||
</Switch>
|
||||
</main>,
|
||||
<main><ListView /></main>,
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`App router component snapshot 1`] = `
|
||||
<IntlProvider
|
||||
locale="en"
|
||||
>
|
||||
<Provider
|
||||
store="testStore"
|
||||
>
|
||||
<BrowserRouter>
|
||||
<div>
|
||||
<main>
|
||||
<Switch>
|
||||
<Route
|
||||
component="ListView"
|
||||
exact={true}
|
||||
path="/:courseId"
|
||||
/>
|
||||
</Switch>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
<BrowserRouter>
|
||||
<div>
|
||||
<CourseHeader
|
||||
courseNumber="course-number"
|
||||
courseOrg="course-org"
|
||||
courseTitle="course-title"
|
||||
/>
|
||||
<main>
|
||||
<ListView />
|
||||
</main>
|
||||
<Footer
|
||||
logo="https://edx-cdn.org/v3/stage/open-edx-tag.svg"
|
||||
/>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
`;
|
||||
|
||||
@@ -5,27 +5,31 @@ import { getLoginRedirectUrl } from '@edx/frontend-platform/auth';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button } from '@edx/paragon';
|
||||
|
||||
import message from './AnonymousUserMenu.messages';
|
||||
import message from './messages';
|
||||
|
||||
function AnonymousUserMenu({ intl }) {
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
className="mr-3"
|
||||
variant="outline-primary"
|
||||
href={`${getConfig().LMS_BASE_URL}/register?next=${encodeURIComponent(global.location.href)}`}
|
||||
>
|
||||
{intl.formatMessage(message.registerSentenceCase)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={`${getLoginRedirectUrl(global.location.href)}`}
|
||||
>
|
||||
{intl.formatMessage(message.signInSentenceCase)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export const getRegisterUrl = () => {
|
||||
const { LMS_BASE_URL } = getConfig();
|
||||
const locationHref = encodeURIComponent(global.location.href);
|
||||
return `${LMS_BASE_URL}/register?next=${locationHref}`;
|
||||
};
|
||||
|
||||
export const AnonymousUserMenu = ({ intl }) => (
|
||||
<div>
|
||||
<Button
|
||||
className="mr-3"
|
||||
variant="outline-primary"
|
||||
href={getRegisterUrl()}
|
||||
>
|
||||
{intl.formatMessage(message.registerSentenceCase)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={`${getLoginRedirectUrl(global.location.href)}`}
|
||||
>
|
||||
{intl.formatMessage(message.signInSentenceCase)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
AnonymousUserMenu.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
27
src/containers/CourseHeader/AnonymousUserMenu.test.jsx
Normal file
27
src/containers/CourseHeader/AnonymousUserMenu.test.jsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { AnonymousUserMenu } from './AnonymousUserMenu';
|
||||
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
getConfig: () => ({
|
||||
LMS_BASE_URL: '<LMS_BASE_URL>',
|
||||
}),
|
||||
}));
|
||||
jest.mock('@edx/frontend-platform/auth', () => ({
|
||||
getLoginRedirectUrl: (url) => `redirect:${url}`,
|
||||
}));
|
||||
jest.mock('@edx/paragon', () => ({
|
||||
Button: () => 'Button',
|
||||
}));
|
||||
|
||||
describe('Header AnonymousUserMenu component', () => {
|
||||
const props = {
|
||||
intl: { formatMessage: (msg) => msg.defaultMessage },
|
||||
};
|
||||
test('snapshot', () => {
|
||||
expect(
|
||||
shallow(<AnonymousUserMenu {...props} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Dropdown } from '@edx/paragon';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
function AuthenticatedUserDropdown({ intl, username }) {
|
||||
let dashboardMenuItem = (
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
|
||||
{intl.formatMessage(messages.dashboard)}
|
||||
</Dropdown.Item>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<a className="text-gray-700 mr-3" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
|
||||
<Dropdown className="user-dropdown">
|
||||
<Dropdown.Toggle variant="outline-primary">
|
||||
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
|
||||
<span data-hj-suppress className="d-none d-md-inline">
|
||||
{username}
|
||||
</span>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu className="dropdown-menu-right">
|
||||
{dashboardMenuItem}
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
|
||||
{intl.formatMessage(messages.profile)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
|
||||
{intl.formatMessage(messages.account)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={getConfig().LOGOUT_URL}>
|
||||
{intl.formatMessage(messages.signOut)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
AuthenticatedUserDropdown.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
AuthenticatedUserDropdown.defaultProps = {};
|
||||
|
||||
export default injectIntl(AuthenticatedUserDropdown);
|
||||
@@ -1,53 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Dropdown } from '@edx/paragon';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
function AuthenticatedUserDropdown({ intl, username }) {
|
||||
let dashboardMenuItem = (
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
|
||||
{intl.formatMessage(messages.dashboard)}
|
||||
</Dropdown.Item>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<a className="text-gray-700 mr-3" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
|
||||
<Dropdown className="user-dropdown">
|
||||
<Dropdown.Toggle variant="outline-primary">
|
||||
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
|
||||
<span data-hj-suppress className="d-none d-md-inline">
|
||||
{username}
|
||||
</span>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu className="dropdown-menu-right">
|
||||
{dashboardMenuItem}
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
|
||||
{intl.formatMessage(messages.profile)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
|
||||
{intl.formatMessage(messages.account)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={getConfig().LOGOUT_URL}>
|
||||
{intl.formatMessage(messages.signOut)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
AuthenticatedUserDropdown.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
AuthenticatedUserDropdown.defaultProps = {};
|
||||
|
||||
export default injectIntl(AuthenticatedUserDropdown);
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { Dropdown } from '@edx/paragon';
|
||||
|
||||
export const UserAvatar = ({ username }) => (
|
||||
<Dropdown.Toggle variant="outline-primary">
|
||||
<FontAwesomeIcon
|
||||
icon={faUserCircle}
|
||||
className="d-md-none"
|
||||
size="lg"
|
||||
/>
|
||||
<span data-hj-suppress className="d-none d-md-inline">
|
||||
{username}
|
||||
</span>
|
||||
</Dropdown.Toggle>
|
||||
);
|
||||
UserAvatar.propTypes = {
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
UserAvatar.defaultProps = {};
|
||||
|
||||
export default UserAvatar;
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import UserAvatar from './UserAvatar';
|
||||
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
getConfig: () => ({
|
||||
LMS_BASE_URL: '<LMS_BASE_URL>',
|
||||
LOGOUT_URL: '<LOGOUT_URL>',
|
||||
SUPPORT_URL: '<SUPPORT_URL>',
|
||||
}),
|
||||
}));
|
||||
jest.mock('@edx/paragon', () => ({
|
||||
Dropdown: {
|
||||
Toggle: () => 'Dropdown.Toggle',
|
||||
Item: () => 'Dropdown.Item',
|
||||
},
|
||||
}));
|
||||
jest.mock('@fortawesome/react-fontawesome', () => ({
|
||||
FontAwesomeIcon: () => 'FontAwesomeIcon',
|
||||
}));
|
||||
|
||||
describe('Header AuthenticatedUserDropdown UserAvatar component', () => {
|
||||
const props = {
|
||||
username: 'test-username',
|
||||
};
|
||||
test('snapshot', () => {
|
||||
expect(
|
||||
shallow(<UserAvatar {...props} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Dropdown } from '@edx/paragon';
|
||||
|
||||
import messages from '../messages';
|
||||
|
||||
export class UserMenu extends React.Component {
|
||||
menuItem(href, message) {
|
||||
return (
|
||||
<Dropdown.Item href={href}>
|
||||
{this.props.intl.formatMessage(message)}
|
||||
</Dropdown.Item>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { username } = this.props;
|
||||
const { LMS_BASE_URL, LOGOUT_URL } = getConfig();
|
||||
return (
|
||||
<Dropdown.Menu className="dropdown-menu-right">
|
||||
{this.menuItem(`${LMS_BASE_URL}/dashboard`, messages.dashboard)}
|
||||
{this.menuItem(`${LMS_BASE_URL}/u/${username}`, messages.profile)}
|
||||
{this.menuItem(`${LMS_BASE_URL}/account/settings`, messages.account)}
|
||||
{this.menuItem(LOGOUT_URL, messages.signOut)}
|
||||
</Dropdown.Menu>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UserMenu.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
UserMenu.defaultProps = {};
|
||||
|
||||
export default injectIntl(UserMenu);
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { UserMenu } from './UserMenu';
|
||||
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
getConfig: () => ({
|
||||
LMS_BASE_URL: '<LMS_BASE_URL>',
|
||||
LOGOUT_URL: '<LOGOUT_URL>',
|
||||
SUPPORT_URL: '<SUPPORT_URL>',
|
||||
}),
|
||||
}));
|
||||
jest.mock('@edx/paragon', () => {
|
||||
const Dropdown = () => 'Dropdown';
|
||||
Dropdown.Toggle = () => 'Dropdown.Toggle';
|
||||
Dropdown.Menu = () => 'Dropdown.Menu';
|
||||
Dropdown.Item = () => 'Dropdown.Item';
|
||||
return { Dropdown };
|
||||
});
|
||||
jest.mock('@fortawesome/react-fontawesome', () => ({
|
||||
FontAwesomeIcon: () => 'FontAwesomeIcon',
|
||||
}));
|
||||
jest.mock('@fortawesome/free-solid-svg-icons', () => ({
|
||||
faUserCircle: 'fa-user-circle-icon',
|
||||
}));
|
||||
|
||||
describe('Header AuthenticatedUserDropdown UserMenu component', () => {
|
||||
const props = {
|
||||
intl: { formatMessage: (msg) => msg.defaultMessage },
|
||||
username: 'test-username',
|
||||
};
|
||||
test('snapshot', () => {
|
||||
expect(
|
||||
shallow(<UserMenu {...props} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header AuthenticatedUserDropdown UserAvatar component snapshot 1`] = `
|
||||
<Toggle
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
className="d-md-none"
|
||||
icon={
|
||||
Object {
|
||||
"icon": Array [
|
||||
496,
|
||||
512,
|
||||
Array [],
|
||||
"f2bd",
|
||||
"M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 96c48.6 0 88 39.4 88 88s-39.4 88-88 88-88-39.4-88-88 39.4-88 88-88zm0 344c-58.7 0-111.3-26.6-146.5-68.2 18.8-35.4 55.6-59.8 98.5-59.8 2.4 0 4.8.4 7.1 1.1 13 4.2 26.6 6.9 40.9 6.9 14.3 0 28-2.7 40.9-6.9 2.3-.7 4.7-1.1 7.1-1.1 42.9 0 79.7 24.4 98.5 59.8C359.3 421.4 306.7 448 248 448z",
|
||||
],
|
||||
"iconName": "user-circle",
|
||||
"prefix": "fas",
|
||||
}
|
||||
}
|
||||
size="lg"
|
||||
/>
|
||||
<span
|
||||
className="d-none d-md-inline"
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
test-username
|
||||
</span>
|
||||
</Toggle>
|
||||
`;
|
||||
@@ -0,0 +1,28 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header AuthenticatedUserDropdown UserMenu component snapshot 1`] = `
|
||||
<Component
|
||||
className="dropdown-menu-right"
|
||||
>
|
||||
<Component
|
||||
href="<LMS_BASE_URL>/dashboard"
|
||||
>
|
||||
Dashboard
|
||||
</Component>
|
||||
<Component
|
||||
href="<LMS_BASE_URL>/u/test-username"
|
||||
>
|
||||
Profile
|
||||
</Component>
|
||||
<Component
|
||||
href="<LMS_BASE_URL>/account/settings"
|
||||
>
|
||||
Account
|
||||
</Component>
|
||||
<Component
|
||||
href="<LOGOUT_URL>"
|
||||
>
|
||||
Sign Out
|
||||
</Component>
|
||||
</Component>
|
||||
`;
|
||||
@@ -0,0 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header AuthenticatedUserDropdown component snapshot 1`] = `
|
||||
<Fragment>
|
||||
<a
|
||||
className="text-gray-700 mr-3"
|
||||
href="<SUPPORT_URL>"
|
||||
>
|
||||
Help
|
||||
</a>
|
||||
<Dropdown
|
||||
className="user-dropdown"
|
||||
>
|
||||
<UserAvatar
|
||||
username="test-username"
|
||||
/>
|
||||
<UserMenu />
|
||||
</Dropdown>
|
||||
</Fragment>
|
||||
`;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Dropdown } from '@edx/paragon';
|
||||
|
||||
import UserMenu from './UserMenu';
|
||||
import UserAvatar from './UserAvatar';
|
||||
|
||||
import messages from '../messages';
|
||||
|
||||
export const AuthenticatedUserDropdown = ({
|
||||
intl,
|
||||
username,
|
||||
}) => (
|
||||
<>
|
||||
<a className="text-gray-700 mr-3" href={`${getConfig().SUPPORT_URL}`}>
|
||||
{intl.formatMessage(messages.help)}
|
||||
</a>
|
||||
<Dropdown className="user-dropdown">
|
||||
<UserAvatar username={username} />
|
||||
<UserMenu />
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
|
||||
AuthenticatedUserDropdown.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
AuthenticatedUserDropdown.defaultProps = {};
|
||||
|
||||
export default injectIntl(AuthenticatedUserDropdown);
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { AuthenticatedUserDropdown } from '.';
|
||||
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
getConfig: () => ({
|
||||
SUPPORT_URL: '<SUPPORT_URL>',
|
||||
}),
|
||||
}));
|
||||
jest.mock('@edx/paragon', () => ({
|
||||
Dropdown: () => 'Dropdown',
|
||||
}));
|
||||
jest.mock('./UserAvatar', () => 'UserAvatar');
|
||||
jest.mock('./UserMenu', () => 'UserMenu');
|
||||
|
||||
describe('Header AuthenticatedUserDropdown component', () => {
|
||||
const props = {
|
||||
intl: { formatMessage: (msg) => msg.defaultMessage },
|
||||
username: 'test-username',
|
||||
};
|
||||
test('snapshot', () => {
|
||||
expect(
|
||||
shallow(<AuthenticatedUserDropdown {...props} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
32
src/containers/CourseHeader/CourseLabel.jsx
Normal file
32
src/containers/CourseHeader/CourseLabel.jsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const CourseLabel = ({
|
||||
courseOrg,
|
||||
courseNumber,
|
||||
courseTitle,
|
||||
}) => (
|
||||
<div
|
||||
className="flex-grow-1 course-title-lockup"
|
||||
style={{ lineHeight: 1 }}
|
||||
>
|
||||
<span className="d-block small m-0">
|
||||
{courseOrg} {courseNumber}
|
||||
</span>
|
||||
<span className="d-block m-0 font-weight-bold course-title">
|
||||
{courseTitle}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
CourseLabel.propTypes = {
|
||||
courseOrg: PropTypes.string,
|
||||
courseNumber: PropTypes.string,
|
||||
courseTitle: PropTypes.string,
|
||||
};
|
||||
CourseLabel.defaultProps = {
|
||||
courseOrg: null,
|
||||
courseNumber: null,
|
||||
courseTitle: null,
|
||||
};
|
||||
|
||||
export default CourseLabel;
|
||||
18
src/containers/CourseHeader/CourseLabel.test.jsx
Normal file
18
src/containers/CourseHeader/CourseLabel.test.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import CourseLabel from './CourseLabel';
|
||||
|
||||
const courseData = {
|
||||
courseOrg: 'course-org',
|
||||
courseNumber: 'course-number',
|
||||
courseTitle: 'course-title',
|
||||
};
|
||||
|
||||
describe('Header CourseLabel component', () => {
|
||||
test('snapshot', () => {
|
||||
expect(
|
||||
shallow(<CourseLabel {...courseData} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
|
||||
import AnonymousUserMenu from './AnonymousUserMenu';
|
||||
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
|
||||
import messages from './messages';
|
||||
|
||||
function LinkedLogo({
|
||||
href,
|
||||
src,
|
||||
alt,
|
||||
...attributes
|
||||
}) {
|
||||
return (
|
||||
<a href={href} {...attributes}>
|
||||
<img className="d-block" src={src} alt={alt} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
LinkedLogo.propTypes = {
|
||||
href: PropTypes.string.isRequired,
|
||||
src: PropTypes.string.isRequired,
|
||||
alt: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
function Header({
|
||||
courseOrg, courseNumber, courseTitle, intl, showUserDropdown,
|
||||
}) {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
|
||||
let headerLogo = (
|
||||
<LinkedLogo
|
||||
className="logo"
|
||||
href={`${getConfig().LMS_BASE_URL}/dashboard`}
|
||||
src={getConfig().LOGO_URL}
|
||||
alt={getConfig().SITE_NAME}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<header className="course-header">
|
||||
<a className="sr-only sr-only-focusable" href="#main-content">{intl.formatMessage(messages.skipNavLink)}</a>
|
||||
<div className="container-xl py-2 d-flex align-items-center">
|
||||
{headerLogo}
|
||||
<div className="flex-grow-1 course-title-lockup" style={{ lineHeight: 1 }}>
|
||||
<span className="d-block small m-0">{courseOrg} {courseNumber}</span>
|
||||
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
|
||||
</div>
|
||||
{showUserDropdown && authenticatedUser && (
|
||||
<AuthenticatedUserDropdown
|
||||
username={authenticatedUser.username}
|
||||
/>
|
||||
)}
|
||||
{showUserDropdown && !authenticatedUser && (
|
||||
<AnonymousUserMenu />
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
courseOrg: PropTypes.string,
|
||||
courseNumber: PropTypes.string,
|
||||
courseTitle: PropTypes.string,
|
||||
intl: intlShape.isRequired,
|
||||
showUserDropdown: PropTypes.bool,
|
||||
};
|
||||
|
||||
Header.defaultProps = {
|
||||
courseOrg: null,
|
||||
courseNumber: null,
|
||||
courseTitle: null,
|
||||
showUserDropdown: true,
|
||||
};
|
||||
|
||||
export default injectIntl(Header);
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
authenticatedUser, initializeMockApp, render, screen,
|
||||
} from '../setupTest';
|
||||
import { Header } from './index';
|
||||
|
||||
describe('Header', () => {
|
||||
beforeAll(async () => {
|
||||
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
|
||||
await initializeMockApp();
|
||||
});
|
||||
|
||||
it('displays user button', () => {
|
||||
render(<Header />);
|
||||
expect(screen.getByRole('button')).toHaveTextContent(authenticatedUser.username);
|
||||
});
|
||||
|
||||
it('displays course data', () => {
|
||||
const courseData = {
|
||||
courseOrg: 'course-org',
|
||||
courseNumber: 'course-number',
|
||||
courseTitle: 'course-title',
|
||||
};
|
||||
render(<Header {...courseData} />);
|
||||
|
||||
expect(screen.getByText(`${courseData.courseOrg} ${courseData.courseNumber}`)).toBeInTheDocument();
|
||||
expect(screen.getByText(courseData.courseTitle)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
17
src/containers/CourseHeader/LinkedLogo.jsx
Normal file
17
src/containers/CourseHeader/LinkedLogo.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
const LinkedLogo = () => (
|
||||
<a
|
||||
className="logo"
|
||||
href={`${getConfig().LMS_BASE_URL}/dashboard`}
|
||||
>
|
||||
<img
|
||||
className="d-block"
|
||||
src={getConfig().LOGO_URL}
|
||||
alt={getConfig().SITE_NAME}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
|
||||
export default LinkedLogo;
|
||||
20
src/containers/CourseHeader/LinkedLogo.test.jsx
Normal file
20
src/containers/CourseHeader/LinkedLogo.test.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import LinkedLogo from './LinkedLogo';
|
||||
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
getConfig: () => ({
|
||||
LMS_BASE_URL: '<getConfig().LMS_BASE_URL>',
|
||||
LOGO_URL: '<getConfig().LOGO_URL>',
|
||||
SITE_NAME: '<getConfig().SITE_NAME>',
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Header CourseLabel component', () => {
|
||||
test('snapshot', () => {
|
||||
expect(
|
||||
shallow(<LinkedLogo />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header AnonymousUserMenu component snapshot 1`] = `
|
||||
<div>
|
||||
<Button
|
||||
className="mr-3"
|
||||
href="<LMS_BASE_URL>/register?next=http%3A%2F%2Flocalhost%2F"
|
||||
variant="outline-primary"
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
<Button
|
||||
href="redirect:http://localhost/"
|
||||
variant="primary"
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,25 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header CourseLabel component snapshot 1`] = `
|
||||
<div
|
||||
className="flex-grow-1 course-title-lockup"
|
||||
style={
|
||||
Object {
|
||||
"lineHeight": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="d-block small m-0"
|
||||
>
|
||||
course-org
|
||||
|
||||
course-number
|
||||
</span>
|
||||
<span
|
||||
className="d-block m-0 font-weight-bold course-title"
|
||||
>
|
||||
course-title
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header CourseLabel component snapshot 1`] = `
|
||||
<a
|
||||
className="logo"
|
||||
href="<getConfig().LMS_BASE_URL>/dashboard"
|
||||
>
|
||||
<img
|
||||
alt="<getConfig().SITE_NAME>"
|
||||
className="d-block"
|
||||
src="<getConfig().LOGO_URL>"
|
||||
/>
|
||||
</a>
|
||||
`;
|
||||
51
src/containers/CourseHeader/__snapshots__/index.test.js.snap
Normal file
51
src/containers/CourseHeader/__snapshots__/index.test.js.snap
Normal file
@@ -0,0 +1,51 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header component snapshot 1`] = `
|
||||
<header
|
||||
className="course-header"
|
||||
>
|
||||
<a
|
||||
className="sr-only sr-only-focusable"
|
||||
href="#main-content"
|
||||
>
|
||||
Skip to main content.
|
||||
</a>
|
||||
<div
|
||||
className="container-xl py-2 d-flex align-items-center"
|
||||
>
|
||||
<LinkedLogo />
|
||||
<CourseLabel
|
||||
courseNumber="course-number"
|
||||
courseOrg="course-org"
|
||||
courseTitle="course-title"
|
||||
/>
|
||||
<AnonymousUserMenu />
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
|
||||
exports[`Header component snapshot with authenticatedUser 1`] = `
|
||||
<header
|
||||
className="course-header"
|
||||
>
|
||||
<a
|
||||
className="sr-only sr-only-focusable"
|
||||
href="#main-content"
|
||||
>
|
||||
Skip to main content.
|
||||
</a>
|
||||
<div
|
||||
className="container-xl py-2 d-flex align-items-center"
|
||||
>
|
||||
<LinkedLogo />
|
||||
<CourseLabel
|
||||
courseNumber="course-number"
|
||||
courseOrg="course-org"
|
||||
courseTitle="course-title"
|
||||
/>
|
||||
<AuthenticatedUserDropdown
|
||||
username="test"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
@@ -1 +0,0 @@
|
||||
export { default as Header } from './Header';
|
||||
47
src/containers/CourseHeader/index.jsx
Normal file
47
src/containers/CourseHeader/index.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
|
||||
import AnonymousUserMenu from './AnonymousUserMenu';
|
||||
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
|
||||
import LinkedLogo from './LinkedLogo';
|
||||
import CourseLabel from './CourseLabel';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
export const Header = ({
|
||||
courseOrg,
|
||||
courseNumber,
|
||||
courseTitle,
|
||||
intl,
|
||||
}) => {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
return (
|
||||
<header className="course-header">
|
||||
<a className="sr-only sr-only-focusable" href="#main-content">
|
||||
{intl.formatMessage(messages.skipNavLink)}
|
||||
</a>
|
||||
<div className="container-xl py-2 d-flex align-items-center">
|
||||
<LinkedLogo />
|
||||
<CourseLabel {...{ courseOrg, courseNumber, courseTitle }} />
|
||||
{authenticatedUser
|
||||
? (<AuthenticatedUserDropdown username={authenticatedUser.username} />)
|
||||
: (<AnonymousUserMenu />)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
Header.propTypes = {
|
||||
courseOrg: PropTypes.string,
|
||||
courseNumber: PropTypes.string,
|
||||
courseTitle: PropTypes.string,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
Header.defaultProps = {
|
||||
courseOrg: null,
|
||||
courseNumber: null,
|
||||
courseTitle: null,
|
||||
};
|
||||
|
||||
export default injectIntl(Header);
|
||||
38
src/containers/CourseHeader/index.test.jsx
Normal file
38
src/containers/CourseHeader/index.test.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
import { Header } from '.';
|
||||
|
||||
jest.mock('./AnonymousUserMenu', () => 'AnonymousUserMenu');
|
||||
jest.mock('./AuthenticatedUserDropdown', () => 'AuthenticatedUserDropdown');
|
||||
jest.mock('./LinkedLogo', () => 'LinkedLogo');
|
||||
jest.mock('./CourseLabel', () => 'CourseLabel');
|
||||
|
||||
jest.mock('@edx/frontend-platform/react', () => ({
|
||||
AppContext: { authenticatedUser: null },
|
||||
}));
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useContext: (context) => context,
|
||||
}));
|
||||
|
||||
const courseData = {
|
||||
courseOrg: 'course-org',
|
||||
courseNumber: 'course-number',
|
||||
courseTitle: 'course-title',
|
||||
};
|
||||
|
||||
describe('Header component', () => {
|
||||
const props = {
|
||||
...courseData,
|
||||
intl: { formatMessage: (msg) => msg.defaultMessage },
|
||||
};
|
||||
test('snapshot', () => {
|
||||
expect(shallow(<Header {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot with authenticatedUser', () => {
|
||||
AppContext.authenticatedUser = { username: 'test' };
|
||||
expect(shallow(<Header {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,11 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
courseMaterial: {
|
||||
id: 'learn.navigation.course.tabs.label',
|
||||
defaultMessage: 'Course Material',
|
||||
description: 'The accessible label for course tabs navigation',
|
||||
},
|
||||
dashboard: {
|
||||
id: 'header.menu.dashboard.label',
|
||||
defaultMessage: 'Dashboard',
|
||||
@@ -21,6 +26,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Account',
|
||||
description: 'The text for the user menu Account navigation link.',
|
||||
},
|
||||
orderHistory: {
|
||||
id: 'header.menu.orderHistory.label',
|
||||
defaultMessage: 'Order History',
|
||||
description: 'The text for the user menu Order History navigation link.',
|
||||
},
|
||||
skipNavLink: {
|
||||
id: 'header.navigation.skipNavLink',
|
||||
defaultMessage: 'Skip to main content.',
|
||||
@@ -31,6 +41,16 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Sign Out',
|
||||
description: 'The label for the user menu Sign Out action.',
|
||||
},
|
||||
registerSentenceCase: {
|
||||
id: 'header.register.sentenceCase',
|
||||
defaultMessage: 'Register',
|
||||
description: 'Text in a button, prompting the user to register.',
|
||||
},
|
||||
signInSentenceCase: {
|
||||
id: 'header.signIn.sentenceCase',
|
||||
defaultMessage: 'Sign in',
|
||||
description: 'Text in a button, prompting the user to log in.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -14,7 +14,7 @@ import selectors from 'data/selectors';
|
||||
*/
|
||||
export const ReviewCriterion = ({
|
||||
config,
|
||||
data,
|
||||
// data,
|
||||
}) => (
|
||||
<div className="review-criterion">
|
||||
{ config.options.map(option => (
|
||||
|
||||
@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ArrowBack } from '@edx/paragon/icons';
|
||||
import { Hyperlink } from '@edx/paragon'
|
||||
import { Hyperlink } from '@edx/paragon';
|
||||
|
||||
import { locationId } from '../../data/constants/app';
|
||||
import selectors from 'data/selectors';
|
||||
import { locationId } from '../../data/constants/app';
|
||||
|
||||
/**
|
||||
* <ListViewBreadcrumb />
|
||||
@@ -23,15 +23,18 @@ export const ListViewBreadcrumb = ({ courseId, oraName }) => {
|
||||
<p className="h3 py-4">{oraName}<Hyperlink
|
||||
destination={oraUrl}
|
||||
target="_blank"
|
||||
/></p>
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
ListViewBreadcrumb.defaultProps = {
|
||||
courseId: '',
|
||||
oraName: '',
|
||||
};
|
||||
ListViewBreadcrumb.propTypes = {
|
||||
courseId: PropTypes.string,
|
||||
oraName: PropTypes.string,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
@@ -42,4 +45,4 @@ export const mapStateToProps = (state) => ({
|
||||
export const mapDispatchToProps = {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ListViewBreadcrumb);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ListViewBreadcrumb);
|
||||
|
||||
@@ -23,7 +23,7 @@ import './ListView.scss';
|
||||
|
||||
const gradeStatusOptions = Object.keys(gradingStatusDisplay).map(key => ({
|
||||
name: gradingStatusDisplay[key],
|
||||
value: key
|
||||
value: key,
|
||||
}));
|
||||
|
||||
/**
|
||||
@@ -59,7 +59,7 @@ export class ListView extends React.Component {
|
||||
buttonText: `View selected responses (${selectedFlatRows.length})`,
|
||||
handleClick: this.handleViewAllResponsesClick,
|
||||
variant: 'primary',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -2,4 +2,4 @@ import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const routePath = `${getConfig().PUBLIC_PATH}:courseId`;
|
||||
export const locationId = window.location.pathname.slice(1)
|
||||
export const locationId = window.location.pathname.slice(1);
|
||||
|
||||
@@ -3,4 +3,6 @@ export const number = '101';
|
||||
export const title = 'Time Travel 101';
|
||||
export const courseId = 'course-v1:Foo+TT101+R0';
|
||||
|
||||
export default { org, number, title, courseId };
|
||||
export default {
|
||||
org, number, title, courseId,
|
||||
};
|
||||
|
||||
@@ -4,15 +4,6 @@ import oraMetadata from './ora';
|
||||
import courseMetadata from './course';
|
||||
import ids from './ids';
|
||||
|
||||
console.log({
|
||||
submissions,
|
||||
oraMetadata,
|
||||
courseMetadata,
|
||||
mockSubmission,
|
||||
mockSubmissionStatus,
|
||||
ids,
|
||||
});
|
||||
|
||||
export default {
|
||||
submissions,
|
||||
oraMetadata,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import ids from './ids';
|
||||
export const prompt = `
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin scelerisque finibus sem in aliquam. Cras volutpat ipsum sit amet porttitor bibendum. Nunc tempor ex neque, sed faucibus nisl accumsan vitae. Proin et sem nisl. Aenean placerat justo a ligula eleifend, in imperdiet sem sodales. Maecenas eget aliquet purus, ac ornare risus. Nullam eget interdum erat. Mauris semper porta sapien et egestas. Aliquam viverra convallis pulvinar. Aliquam suscipit ligula felis, eu viverra ligula dignissim ut. Vivamus sit amet commodo sem. Nullam a viverra nibh.
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import queryString from 'query-string';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { filters } from 'data/constants/filters';
|
||||
|
||||
/**
|
||||
* get(url)
|
||||
@@ -26,17 +25,3 @@ export const stringifyUrl = (url, query) => queryString.stringifyUrl(
|
||||
{ url, query },
|
||||
{ skipNull: true, skipEmptyString: true },
|
||||
);
|
||||
|
||||
/**
|
||||
* filterQuery(options)
|
||||
* Takes current filter object and returns it with only valid filters that are
|
||||
* set and have non-'All' values
|
||||
* @param {object} options - filter values
|
||||
* @return {object} - valid filters that are set and do not equal 'All'
|
||||
*/
|
||||
export const filterQuery = (options) => Object.values(filters)
|
||||
.filter(filter => options[filter] && options[filter] !== 'All')
|
||||
.reduce(
|
||||
(obj, filter) => ({ ...obj, [filter]: options[filter] }),
|
||||
{},
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import queryString from 'query-string';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { filters } from 'data/constants/filters';
|
||||
import * as utils from './utils';
|
||||
|
||||
jest.mock('query-string', () => ({
|
||||
@@ -37,61 +36,4 @@ describe('lms service utils', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('filterQuery', () => {
|
||||
it('returns all filters included in validated list that are not "All"', () => {
|
||||
const goodOptions = {
|
||||
[filters.assignmentType]: 'quiz',
|
||||
[filters.courseGradeMax]: 100,
|
||||
[filters.courseGradeMin]: 1,
|
||||
};
|
||||
const extraOptions = {
|
||||
fake: 'option',
|
||||
another: 'fake one',
|
||||
};
|
||||
expect(utils.filterQuery({
|
||||
...goodOptions,
|
||||
...extraOptions,
|
||||
[filters.includeCourseRoleMembers]: 'All',
|
||||
})).toEqual(goodOptions);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* get(url)
|
||||
* simple wrapper providing an authenticated Http client get action
|
||||
* @param {string} url - target url
|
||||
*/
|
||||
export const get = (...args) => getAuthenticatedHttpClient().get(...args);
|
||||
/**
|
||||
* post(url, data)
|
||||
* simple wrapper providing an authenticated Http client post action
|
||||
* @param {string} url - target url
|
||||
* @param {object|string} data - post payload
|
||||
*/
|
||||
export const post = (...args) => getAuthenticatedHttpClient().post(...args);
|
||||
|
||||
/**
|
||||
* stringifyUrl(url, query)
|
||||
* simple wrapper around queryString.stringifyUrl that sets skip behavior
|
||||
* @param {string} url - base url string
|
||||
* @param {object} query - query parameters
|
||||
*/
|
||||
export const stringifyUrl = (url, query) => queryString.stringifyUrl(
|
||||
{ url, query },
|
||||
{ skipNull: true, skipEmptyString: true },
|
||||
);
|
||||
|
||||
/**
|
||||
* filterQuery(options)
|
||||
* Takes current filter object and returns it with only valid filters that are
|
||||
* set and have non-'All' values
|
||||
* @param {object} options - filter values
|
||||
* @return {object} - valid filters that are set and do not equal 'All'
|
||||
*/
|
||||
export const filterQuery = (options) => Object.values(filters)
|
||||
.filter(filter => options[filter] && options[filter] !== 'All')
|
||||
.reduce(
|
||||
(obj, filter) => ({ ...obj, [filter]: options[filter] }),
|
||||
{},
|
||||
);
|
||||
|
||||
@@ -3,8 +3,6 @@ import thunkMiddleware from 'redux-thunk';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';
|
||||
import { createLogger } from 'redux-logger';
|
||||
|
||||
import fakeData from './services/lms/fakeData';
|
||||
|
||||
import actions from './actions';
|
||||
import selectors from './selectors';
|
||||
import reducers from './reducers';
|
||||
|
||||
@@ -5,100 +5,16 @@ import '@testing-library/jest-dom/extend-expect';
|
||||
import Enzyme from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
|
||||
import AppProvider from '@edx/frontend-platform/react/AppProvider';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { render as rtlRender } from '@testing-library/react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig, mergeConfig } from '@edx/frontend-platform';
|
||||
import { configure as configureAuth, MockAuthService } from '@edx/frontend-platform/auth';
|
||||
import { configure as configureI18n } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import appMessages from './i18n';
|
||||
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
|
||||
jest.mock('@edx/frontend-platform/i18n', () => {
|
||||
const i18n = jest.requireActual('@edx/frontend-platform/i18n');
|
||||
const PropTypes = jest.requireActual('prop-types');
|
||||
return {
|
||||
...i18n,
|
||||
intlShape: PropTypes.shape({
|
||||
intlShape: jest.requireActual('prop-types').shape({
|
||||
formatMessage: jest.fn(msg => msg.defaultMessage),
|
||||
}),
|
||||
defineMessages: m => m,
|
||||
FormattedMessage: () => 'FormattedMessage',
|
||||
};
|
||||
});
|
||||
|
||||
export const authenticatedUser = {
|
||||
userId: 'abc123',
|
||||
username: 'Mock User',
|
||||
roles: [],
|
||||
administrator: false,
|
||||
};
|
||||
|
||||
export function initializeMockApp() {
|
||||
mergeConfig({
|
||||
CONTACT_URL: process.env.CONTACT_URL || null,
|
||||
INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null,
|
||||
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null,
|
||||
TWITTER_URL: process.env.TWITTER_URL || null,
|
||||
authenticatedUser: {
|
||||
userId: 'abc123',
|
||||
username: 'Mock User',
|
||||
roles: [],
|
||||
administrator: false,
|
||||
},
|
||||
SUPPORT_URL_ID_VERIFICATION: 'http://example.com',
|
||||
});
|
||||
|
||||
const authService = configureAuth(MockAuthService, {
|
||||
config: getConfig()
|
||||
});
|
||||
|
||||
// i18n doesn't have a service class to return.
|
||||
configureI18n({
|
||||
config: getConfig(),
|
||||
messages: [appMessages, footerMessages],
|
||||
requireAuthenticatedUser: true,
|
||||
});
|
||||
|
||||
return { authService };
|
||||
}
|
||||
|
||||
|
||||
|
||||
function render(
|
||||
ui,
|
||||
{
|
||||
store = null,
|
||||
...renderOptions
|
||||
} = {},
|
||||
) {
|
||||
function Wrapper({ children }) {
|
||||
return (
|
||||
// eslint-disable-next-line react/jsx-filename-extension
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store || {}}>
|
||||
{children}
|
||||
</AppProvider>
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Wrapper.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
|
||||
}
|
||||
|
||||
// Re-export everything.
|
||||
export * from '@testing-library/react';
|
||||
|
||||
// Override `render` method.
|
||||
export {
|
||||
render,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user