feat: Add redirect to enterprise learner dashboard (#1251)

When a learner tries to load the B2C course page for a course that
starts in the future (error_code="course_not_started"), normally the
learning MFE automatically redirects to the B2C dashboard.  This change
supports an alternate error_code "course_not_started_enterprise_learner"
to trigger an alternative redirect to the B2B (enterprise) learner
dashboard.

This does two main things:

1. When the course metadata API response indicates
   course_access.error_code = "course_not_started_enterprise_learner"
   then redirect to "/redirect/enterprise-learner-dashboard".
2. When the top-level router matches path "/redirect/enterprise-learner-dashboard"
   then redirec to to the value of config `ENTERPRISE_LEARNER_PORTAL_URL`.

ENT-8078
This commit is contained in:
Troy Sankey
2023-12-15 04:45:36 -08:00
committed by GitHub
parent 2bf326fc67
commit 79575330c9
8 changed files with 46 additions and 5 deletions

4
global-setup.js Normal file
View File

@@ -0,0 +1,4 @@
// Force all tests to run in UTC to prevent tests from being sensitive to host timezone.
module.exports = async () => {
process.env.TZ = 'UTC';
};

View File

@@ -16,5 +16,6 @@ module.exports = createConfig('jest', {
'react-markdown': '<rootDir>/node_modules/react-markdown/react-markdown.min.js', 'react-markdown': '<rootDir>/node_modules/react-markdown/react-markdown.min.js',
}, },
testTimeout: 30000, testTimeout: 30000,
testEnvironment: 'jsdom' testEnvironment: 'jsdom',
globalSetup: "./global-setup.js"
}); });

View File

@@ -22,11 +22,13 @@ export const ROUTES = {
UNSUBSCRIBE: '/goal-unsubscribe/:token', UNSUBSCRIBE: '/goal-unsubscribe/:token',
REDIRECT: '/redirect/*', REDIRECT: '/redirect/*',
DASHBOARD: 'dashboard', DASHBOARD: 'dashboard',
ENTERPRISE_LEARNER_DASHBOARD: 'enterprise-learner-dashboard',
CONSENT: 'consent', CONSENT: 'consent',
}; };
export const REDIRECT_MODES = { export const REDIRECT_MODES = {
DASHBOARD_REDIRECT: 'dashboard-redirect', DASHBOARD_REDIRECT: 'dashboard-redirect',
ENTERPRISE_LEARNER_DASHBOARD_REDIRECT: 'enterprise-learner-dashboard-redirect',
CONSENT_REDIRECT: 'consent-redirect', CONSENT_REDIRECT: 'consent-redirect',
HOME_REDIRECT: 'home-redirect', HOME_REDIRECT: 'home-redirect',
SURVEY_REDIRECT: 'survey-redirect', SURVEY_REDIRECT: 'survey-redirect',

View File

@@ -515,5 +515,12 @@ describe('CoursewareContainer', () => {
const startDate = '2/5/2013'; // This date is based on our courseMetadata factory's sample data. const startDate = '2/5/2013'; // This date is based on our courseMetadata factory's sample data.
expect(global.location.href).toEqual(`http://localhost/redirect/dashboard?notlive=${startDate}`); expect(global.location.href).toEqual(`http://localhost/redirect/dashboard?notlive=${startDate}`);
}); });
it('should go to the enterprise learner dashboard for a course_not_started_enterprise_learner error code', async () => {
setUpWithDeniedStatus('course_not_started_enterprise_learner');
await loadContainer();
expect(global.location.href).toEqual('http://localhost/redirect/enterprise-learner-dashboard');
});
}); });
}); });

View File

@@ -29,6 +29,10 @@ const CoursewareRedirectLandingPage = () => (
path={ROUTES.DASHBOARD} path={ROUTES.DASHBOARD}
element={<PageWrap><RedirectPage pattern="/dashboard" mode={REDIRECT_MODES.DASHBOARD_REDIRECT} /></PageWrap>} element={<PageWrap><RedirectPage pattern="/dashboard" mode={REDIRECT_MODES.DASHBOARD_REDIRECT} /></PageWrap>}
/> />
<Route
path={ROUTES.ENTERPRISE_LEARNER_DASHBOARD}
element={<PageWrap><RedirectPage mode={REDIRECT_MODES.ENTERPRISE_LEARNER_DASHBOARD_REDIRECT} /></PageWrap>}
/>
<Route <Route
path={ROUTES.CONSENT} path={ROUTES.CONSENT}
element={<PageWrap><RedirectPage mode={REDIRECT_MODES.CONSENT_REDIRECT} /></PageWrap>} element={<PageWrap><RedirectPage mode={REDIRECT_MODES.CONSENT_REDIRECT} /></PageWrap>}

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { MemoryRouter as Router } from 'react-router-dom'; import { MemoryRouter as Router } from 'react-router-dom';
import { mergeConfig } from '@edx/frontend-platform';
import { render, initializeMockApp } from '../setupTest'; import { render, initializeMockApp } from '../setupTest';
import CoursewareRedirectLandingPage from './CoursewareRedirectLandingPage'; import CoursewareRedirectLandingPage from './CoursewareRedirectLandingPage';
@@ -11,6 +12,9 @@ jest.mock('../decode-page-route', () => jest.fn(({ children }) => <div>{children
describe('CoursewareRedirectLandingPage', () => { describe('CoursewareRedirectLandingPage', () => {
beforeEach(async () => { beforeEach(async () => {
await initializeMockApp(); await initializeMockApp();
mergeConfig({
ENTERPRISE_LEARNER_PORTAL_URL: 'http://localhost:8734',
}, 'Add configs for URLs');
delete global.location; delete global.location;
global.location = { assign: redirectUrl }; global.location = { assign: redirectUrl };
}); });
@@ -34,4 +38,14 @@ describe('CoursewareRedirectLandingPage', () => {
expect(redirectUrl).toHaveBeenCalledWith('/course/course-v1:edX+DemoX+Demo_Course/home'); expect(redirectUrl).toHaveBeenCalledWith('/course/course-v1:edX+DemoX+Demo_Course/home');
}); });
it('Redirects to correct enterprise dashboard URL', () => {
render(
<Router initialEntries={['/enterprise-learner-dashboard']}>
<CoursewareRedirectLandingPage />
</Router>,
);
expect(redirectUrl).toHaveBeenCalledWith('http://localhost:8734');
});
}); });

View File

@@ -14,20 +14,26 @@ const RedirectPage = ({
const location = useLocation(); const location = useLocation();
const { consentPath } = queryString.parse(location?.search); const { consentPath } = queryString.parse(location?.search);
const BASE_URL = getConfig().LMS_BASE_URL; const {
LMS_BASE_URL,
ENTERPRISE_LEARNER_PORTAL_URL,
} = getConfig();
switch (mode) { switch (mode) {
case REDIRECT_MODES.DASHBOARD_REDIRECT: case REDIRECT_MODES.DASHBOARD_REDIRECT:
global.location.assign(`${BASE_URL}${pattern}${location?.search}`); global.location.assign(`${LMS_BASE_URL}${pattern}${location?.search}`);
break;
case REDIRECT_MODES.ENTERPRISE_LEARNER_DASHBOARD_REDIRECT:
global.location.assign(ENTERPRISE_LEARNER_PORTAL_URL);
break; break;
case REDIRECT_MODES.CONSENT_REDIRECT: case REDIRECT_MODES.CONSENT_REDIRECT:
global.location.assign(`${BASE_URL}${consentPath}`); global.location.assign(`${LMS_BASE_URL}${consentPath}`);
break; break;
case REDIRECT_MODES.HOME_REDIRECT: case REDIRECT_MODES.HOME_REDIRECT:
global.location.assign(generatePath(pattern, { courseId })); global.location.assign(generatePath(pattern, { courseId }));
break; break;
default: default:
global.location.assign(`${BASE_URL}${generatePath(pattern, { courseId })}`); global.location.assign(`${LMS_BASE_URL}${generatePath(pattern, { courseId })}`);
} }
return null; return null;

View File

@@ -15,6 +15,9 @@ export function getAccessDeniedRedirectUrl(courseId, activeTabSlug, courseAccess
const startDate = (new Intl.DateTimeFormat(getLocale())).format(new Date(start)); const startDate = (new Intl.DateTimeFormat(getLocale())).format(new Date(start));
url = `/redirect/dashboard?notlive=${startDate}`; url = `/redirect/dashboard?notlive=${startDate}`;
break; break;
case 'course_not_started_enterprise_learner':
url = '/redirect/enterprise-learner-dashboard';
break;
case 'survey_required': case 'survey_required':
url = `/redirect/survey/${courseId}`; url = `/redirect/survey/${courseId}`;
break; break;