Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lk/ben doc #181

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ temp/babel-plugin-react-intl
### Vi ###
**/*.swp
**/*.swo
**/*.swn
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ const config = createConfig('jest', {
coveragePathIgnorePatterns: [
'src/setupTest.js',
'src/i18n',
'src/hooks/testHooks', // don't check coverage for jest mocking tools
// 'src/data/services/lms/fakeData', // don't check coverage for mock data
'src/test', // don't check coverage for test integration test utils
],
testTimeout: 120000,
});

config.moduleDirectories = ['node_modules', 'src'];
Expand Down
1,658 changes: 1,391 additions & 267 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.9.0-alpha.6",
"@edx/reactifex": "^2.1.1",
"@testing-library/dom": "^9.3.3",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "12.1.5",
"@testing-library/user-event": "^14.5.1",
"glob": "7.2.3",
"husky": "7.0.4",
"jest": "^26.6.3",
Expand Down
124 changes: 45 additions & 79 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,69 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import {
AuthenticatedPageRoute,
ErrorPage,
} from '@edx/frontend-platform/react';
import { ErrorPage } from '@edx/frontend-platform/react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { SkeletonTheme } from '@edx/paragon';

import AssessmentView from 'views/AssessmentView';
import SubmissionView from 'views/SubmissionView';
import XBlockView from 'views/XBlockView';
import XBlockStudioView from 'views/XBlockStudioView';
import GradeView from 'views/GradeView';

import AppContainer from 'components/AppContainer';
import ModalContainer from 'components/ModalContainer';
import PageRoute from 'components/PageRoute';

import { useRefreshPageData } from 'hooks/app';
import { useUpdateTestProgressKey } from 'hooks/testHooks';
import { useHandleModalCloseEvent } from 'hooks/modal';

import messages from './messages';
import routes from './routes';

const App = () => {
const refreshPageData = useRefreshPageData();
React.useEffect(() => {
window.addEventListener('message', (event) => {
if (event.data.type === 'plugin.modal-close') {
refreshPageData();
}
});
}, [refreshPageData]);

const { formatMessage } = useIntl();
const handleModalClose = useHandleModalCloseEvent();

// test
useUpdateTestProgressKey();

const pageWrapper = (children) => (
<AuthenticatedPageRoute>
<AppContainer>
<SkeletonTheme baseColor="#888" highlightColor="#444">
{children}
</SkeletonTheme>
</AppContainer>
</AuthenticatedPageRoute>
);
const appRoute = (route, Component) => (
<Route
path={route}
key={route}
element={pageWrapper(<Component />)}
/>
);
const modalRoute = (route, Component) => (
<Route
key={route}
path={route}
element={pageWrapper(
<ModalContainer>
<Component />
</ModalContainer>,
)}
/>
);

/*
const embeddedRoutes = [
<Route key="embedXblock" path={routes.xblockEmbed} element={<XBlockView />} />,
<Route key="embedXblockStudio" path={routes.xblockStudioEmbed} element={<XBlockStudioView />} />,
<Route key="embedXblockPreview" path={routes.xblockPreviewEmbed} element={<XBlockView />} />,
modalRoute(routes.peerAssessmentEmbed, PeerAssessmentView, 'ORA Peer Assessment'),
modalRoute(routes.selfAssessmentEmbed, SelfAssessmentView, 'ORA Self Assessment'),
modalRoute(routes.studentTrainingEmbed, StudentTrainingView, 'ORA Student Training'),
modalRoute(routes.submissionEmbed, SubmissionView, 'ORA Submission'),
modalRoute(routes.gradedEmbed, GradeView, 'My Grade'),
<Route
key="embedError"
path={routes.rootEmbed}
element={<ErrorPage message={formatMessage(messages.error404Message)} />}
/>,
];
*/
const baseRoutes = [
appRoute(routes.xblock, XBlockView),
appRoute(routes.xblockStudio, XBlockStudioView),
appRoute(routes.xblockPreview, XBlockView),
modalRoute(routes.peerAssessment, AssessmentView),
modalRoute(routes.selfAssessment, AssessmentView),
modalRoute(routes.studentTraining, AssessmentView),
modalRoute(routes.submission, SubmissionView),
modalRoute(routes.graded, GradeView),
<Route key="error" path={routes.root} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
];
React.useEffect(() => {
window.addEventListener('message', handleModalClose);
return () => window.removeEventListener('message', handleModalClose);
}, [handleModalClose]);

return (
<Routes>
{/* embeddedRoutes */}
{baseRoutes}
<Route
path={routes.xblock}
element={(<PageRoute><XBlockView /></PageRoute>)}
/>
<Route
path={routes.xblockStudio}
element={(<PageRoute><XBlockStudioView /></PageRoute>)}
/>
<Route
path={routes.xblockPreview}
element={(<PageRoute><XBlockView /></PageRoute>)}
/>
<Route
path={routes.peerAssessment}
element={(<PageRoute isModal><AssessmentView /></PageRoute>)}
/>
<Route
path={routes.selfAssessment}
element={(<PageRoute isModal><AssessmentView /></PageRoute>)}
/>
<Route
path={routes.studentTraining}
element={(<PageRoute isModal><AssessmentView /></PageRoute>)}
/>
<Route
path={routes.submission}
element={(<PageRoute isModal><SubmissionView /></PageRoute>)}
/>
<Route
path={routes.graded}
element={(<PageRoute isModal><GradeView /></PageRoute>)}
/>
<Route
key="error"
path={routes.root}
element={<ErrorPage message={formatMessage(messages.error404Message)} />}
/>
</Routes>
);
};
Expand Down
117 changes: 117 additions & 0 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React from 'react';
import { when } from 'jest-when';
import { Route, Routes } from 'react-router-dom';

import { ErrorPage } from '@edx/frontend-platform/react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { formatMessage, shallow } from '@edx/react-unit-test-utils';

import AssessmentView from 'views/AssessmentView';
import SubmissionView from 'views/SubmissionView';
import XBlockView from 'views/XBlockView';
import XBlockStudioView from 'views/XBlockStudioView';
import GradeView from 'views/GradeView';

import PageRoute from 'components/PageRoute';

import { useHandleModalCloseEvent } from 'hooks/modal';

import messages from './messages';
import routes from './routes';

import App from './App';

jest.mock('react-router-dom', () => ({
Routes: 'Routes',
Route: 'Route',
}));

jest.mock('@edx/frontend-platform/react', () => ({
AuthenticatedPageRoute: 'AuthenticatedPageRoute',
ErrorPage: 'ErrorPage',
}));
jest.mock('views/AssessmentView', () => 'AssessmentView');
jest.mock('views/SubmissionView', () => 'SubmissionView');
jest.mock('views/XBlockView', () => 'XBlockView');
jest.mock('views/XBlockStudioView', () => 'XBlockStudioView');
jest.mock('views/GradeView', () => 'GradeView');
jest.mock('components/PageRoute', () => 'PageRoute');

jest.mock('hooks/modal', () => ({
useHandleModalCloseEvent: jest.fn(),
}));

const handleModalClose = jest.fn();
when(useHandleModalCloseEvent).calledWith().mockReturnValue(handleModalClose);
const addEventListener = jest.fn();
const removeEventListener = jest.fn();

let el;
describe('App component', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(window, 'addEventListener').mockImplementation(addEventListener);
jest.spyOn(window, 'removeEventListener').mockImplementation(removeEventListener);
el = shallow(<App />);
});
describe('behavior', () => {
it('initializes i18n and refresh event from hooks', () => {
expect(useIntl).toHaveBeenCalled();
expect(useHandleModalCloseEvent).toHaveBeenCalled();
});
it('adds handler for modal close event that refreshes page data', () => {
expect(React.useEffect.mock.calls.length).toEqual(1);
const [[effect, prereqs]] = React.useEffect.mock.calls;
expect(prereqs).toEqual([handleModalClose]);
const out = effect();
expect(addEventListener).toHaveBeenCalledWith('message', handleModalClose);
out();
expect(removeEventListener).toHaveBeenCalledWith('message', handleModalClose);
});
});
describe('render', () => {
test('snapshot', () => {
expect(el.snapshot).toMatchSnapshot();
});
const testComponent = (toTest, { route, Component, isModal }) => {
expect(toTest.type).toEqual(Route);
expect(toTest.props.path).toEqual(route);
const { element } = toTest.props;
expect(toTest.props.element.type).toEqual(PageRoute);
if (isModal) {
expect(toTest.props.element.props.isModal).toEqual(true);
}
const expectedElement = shallow(<PageRoute><Component /></PageRoute>);
expect(shallow(element)).toMatchObject(expectedElement);
};
const testAssessmentRoute = (toTest, { route }) => {
testComponent(toTest, { route, Component: AssessmentView, isModal: true });
};
test('route order', () => {
const renderedRoutes = el.instance.findByType(Routes)[0].children;
testComponent(renderedRoutes[0], { route: routes.xblock, Component: XBlockView });
testComponent(renderedRoutes[1], { route: routes.xblockStudio, Component: XBlockStudioView });
testComponent(renderedRoutes[2], { route: routes.xblockPreview, Component: XBlockView });
testAssessmentRoute(renderedRoutes[3], { route: routes.peerAssessment });
testAssessmentRoute(renderedRoutes[4], { route: routes.selfAssessment });
testAssessmentRoute(renderedRoutes[5], { route: routes.studentTraining });
testComponent(renderedRoutes[6], {
route: routes.submission,
Component: SubmissionView,
isModal: true,
});
testComponent(renderedRoutes[7], {
route: routes.graded,
Component: GradeView,
isModal: true,
});
expect(renderedRoutes[8].matches(shallow(
<Route
key="error"
path={routes.root}
element={<ErrorPage message={formatMessage(messages.error404Message)} />}
/>,
)));
});
});
});
89 changes: 89 additions & 0 deletions src/__snapshots__/App.test.jsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`App component render snapshot 1`] = `
<Routes>
<Route
element={
<PageRoute>
<XBlockView />
</PageRoute>
}
path="xblock/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute>
<XBlockStudioView />
</PageRoute>
}
path="xblock_studio/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute>
<XBlockView />
</PageRoute>
}
path="xblock_preview/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute
isModal={true}
>
<AssessmentView />
</PageRoute>
}
path="peer_assessment/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute
isModal={true}
>
<AssessmentView />
</PageRoute>
}
path="self_assessment/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute
isModal={true}
>
<AssessmentView />
</PageRoute>
}
path="student_training/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute
isModal={true}
>
<SubmissionView />
</PageRoute>
}
path="submission/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<PageRoute
isModal={true}
>
<GradeView />
</PageRoute>
}
path="graded/:courseId/:xblockId/:progressKey?"
/>
<Route
element={
<ErrorPage
message="Page not found"
/>
}
key="error"
path="/*"
/>
</Routes>
`;
Loading
Loading