Skip to content

Commit

Permalink
feat: [AXIMST-11] add functionality and tests for unit page header
Browse files Browse the repository at this point in the history
  • Loading branch information
ihor-romaniuk committed Jan 3, 2024
1 parent 972ed99 commit 77e4b4c
Show file tree
Hide file tree
Showing 24 changed files with 1,654 additions and 326 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ EXAMS_BASE_URL=''
FAVICON_URL=''
LANGUAGE_PREFERENCE_COOKIE_NAME=''
LMS_BASE_URL=''
PREVIEW_BASE_URL=''
LEARNING_BASE_URL=''
LOGIN_URL=''
LOGO_TRADEMARK_URL=''
Expand Down
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ HOTJAR_VERSION=6
HOTJAR_DEBUG=true
INVITE_STUDENTS_EMAIL_TO="someone@domain.com"
AI_TRANSLATIONS_BASE_URL='http://localhost:18760'
PREVIEW_BASE_URL='http://preview.localhost:18000'
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ EXAMS_BASE_URL=
FAVICON_URL='https://edx-cdn.org/v3/default/favicon.ico'
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
LMS_BASE_URL='http://localhost:18000'
PREVIEW_BASE_URL='http://preview.localhost:18000'
LEARNING_BASE_URL='http://localhost:2000'
LOGIN_URL='http://localhost:18000/login'
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
Expand Down
39 changes: 27 additions & 12 deletions src/course-unit/CourseUnit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import { Container, Layout } from '@edx/paragon';
import { ErrorAlert } from '@edx/frontend-lib-content-components';

import { RequestStatus } from '../data/constants';
import PageSubHeader from './page-sub-header/PageSubHeader';
import getPageHeadTitle from '../generic/utils';
import ProcessingNotification from '../generic/processing-notification';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import { getProcessingNotification } from '../generic/processing-notification/data/selectors';

import HeaderTitle from './header-title/HeaderTitle';
import Breadcrumbs from './breadcrumbs/Breadcrumbs';
import HeaderNavigations from './header-navigations/HeaderNavigations';
import SubHeader from '../generic/sub-header/SubHeader';

import { useCourseUnit } from './hooks';
import messages from './messages';
import './CourseUnit.scss';
Expand All @@ -23,10 +27,9 @@ const CourseUnit = ({ courseId }) => {
const intl = useIntl();
const {
isLoading,
breadcrumbsData,
unitTitle,
savingStatus,
isTitleFormOpen,
isTitleEditFormOpen,
isInternetConnectionAlertFailed,
handleTitleEditSubmit,
headerNavigationsActions,
Expand All @@ -53,14 +56,25 @@ const CourseUnit = ({ courseId }) => {
<ErrorAlert hideHeading isError={savingStatus === RequestStatus.FAILED}>
{intl.formatMessage(messages.alertFailedGeneric, { actionName: 'save', type: 'changes' })}
</ErrorAlert>
<PageSubHeader
courseId={courseId}
breadcrumbsData={breadcrumbsData}
unitTitle={unitTitle}
isTitleFormOpen={isTitleFormOpen}
handleTitleEdit={handleTitleEdit}
handleTitleEditSubmit={handleTitleEditSubmit}
headerNavigationsActions={headerNavigationsActions}
<SubHeader
title={(
<HeaderTitle
unitTitle={unitTitle}
isTitleEditFormOpen={isTitleEditFormOpen}
handleTitleEdit={handleTitleEdit}
handleTitleEditSubmit={handleTitleEditSubmit}
/>
)}
subtitle={(
<Breadcrumbs
courseId={courseId}
/>
)}
headerActions={(
<HeaderNavigations
headerNavigationsActions={headerNavigationsActions}
/>
)}
/>
<Layout
lg={[{ span: 9 }, { span: 3 }]}
Expand All @@ -70,7 +84,8 @@ const CourseUnit = ({ courseId }) => {
xl={[{ span: 9 }, { span: 3 }]}
>
<Layout.Element>

Check failure on line 86 in src/course-unit/CourseUnit.jsx

View workflow job for this annotation

GitHub Actions / tests

Empty components are self-closing

</Layout.Element>
<Layout.Element>

Check failure on line 88 in src/course-unit/CourseUnit.jsx

View workflow job for this annotation

GitHub Actions / tests

Empty components are self-closing
</Layout.Element>
</Layout>
</section>
Expand Down
12 changes: 1 addition & 11 deletions src/course-unit/CourseUnit.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1 @@
.course-unit {
.sub-header-title .sub-header-title-subtitle {
.dropdown-toggle::after {
display: none;
}

[aria-expanded="true"] .pgn__icon {
transform: scale(1, -1);
}
}
}
@import "./breadcrumbs/Breadcrumbs";
144 changes: 144 additions & 0 deletions src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import MockAdapter from 'axios-mock-adapter';
import React from 'react';
import {
act, render, waitFor, fireEvent,

Check failure on line 4 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { getConfig, initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

import {
getCourseUnitApiUrl,

Check failure on line 13 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
setCourseUnitApiUrl,

Check failure on line 14 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
} from './data/api';
import {
fetchCourseUnitQuery,

Check failure on line 17 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
} from './data/thunk';
import initializeStore from '../store';
import {
courseUnitIndexMock,

Check failure on line 21 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
} from './__mocks__';
import { executeThunk } from '../utils';
import CourseUnit from './CourseUnit';
import headerNavigationsMessages from './header-navigations/messages';
import headerTitleMessages from './header-title/messages';

let axiosMock;
let store;
const courseId = '123';
const sectionId = '19a30717eff543078a5d94ae9d6c18a5';
const blockId = '567890';
const unitDisplayName = courseUnitIndexMock.metadata.display_name;

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),

Check failure on line 36 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
useParams: () => ({ blockId }),

Check failure on line 37 in src/course-unit/CourseUnit.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Expected indentation of 2 spaces but found 4
}));

const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en">
<CourseUnit courseId={courseId}/>
</IntlProvider>
</AppProvider>
);

describe('<CourseUnit />', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});

store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock
.onGet(getCourseUnitApiUrl(courseId))
.reply(200, courseUnitIndexMock);
await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
});

it('render CourseUnit component correctly', async () => {
const { getByText, getByRole } = render(<RootWrapper/>);
const currentSectionName = courseUnitIndexMock.ancestor_info.ancestors[1].display_name;
const currentSubSectionName = courseUnitIndexMock.ancestor_info.ancestors[1].display_name;

await waitFor(() => {
expect(getByText(unitDisplayName)).toBeInTheDocument();
expect(getByRole('button', { name: headerTitleMessages.altButtonEdit.defaultMessage })).toBeInTheDocument();
expect(getByRole('button', { name: headerTitleMessages.altButtonSettings.defaultMessage })).toBeInTheDocument();
expect(getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage })).toBeInTheDocument();
expect(getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage })).toBeInTheDocument();
expect(getByRole('button', { name: currentSectionName })).toBeInTheDocument();
expect(getByRole('button', { name: currentSubSectionName })).toBeInTheDocument();
});
});

it('handles CourseUnit header action buttons', async () => {
const { open } = window;
window.open = jest.fn();
const { getByRole } = render(<RootWrapper/>);

await waitFor(() => {
const viewLiveButton = getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage });
userEvent.click(viewLiveButton);
expect(window.open).toHaveBeenCalled();
expect(window.open).toHaveBeenCalledWith(`${getConfig().LMS_BASE_URL}/courses/${courseId}/jump_to/${blockId}`, `_blank`);

const previewButton = getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage });
userEvent.click(previewButton);
expect(window.open).toHaveBeenCalled();
expect(window.open).toHaveBeenCalledWith(`${getConfig().PREVIEW_BASE_URL}/courses/${courseId}/courseware/interactive_demonstrations/${sectionId}/1?activate_block_id=${blockId}`, `_blank`);
});

window.open = open;
});

it('checks courseUnit title changing when edit query is successfully', async () => {
const { findByText, queryByRole, getByRole, getByText } = render(<RootWrapper />);
let editTitleButton = null;
let titleEditField = null;
const newDisplayName = `${unitDisplayName} new`;

axiosMock
.onPost(setCourseUnitApiUrl(blockId, {
metadata: {
display_name: newDisplayName,
},
}))
.reply(200, { dummy: 'value' });
axiosMock
.onGet(getCourseUnitApiUrl(blockId))
.reply(200, {
...courseUnitIndexMock,
metadata: {
...courseUnitIndexMock.metadata,
display_name: newDisplayName,
},
});

await waitFor(() => {
editTitleButton = getByRole('button', { name: headerTitleMessages.altButtonEdit.defaultMessage });
titleEditField = queryByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
});
expect(titleEditField).not.toBeInTheDocument();
fireEvent.click(editTitleButton);
titleEditField = getByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
fireEvent.change(titleEditField, { target: { value: newDisplayName } });
await act(async () => {
fireEvent.blur(titleEditField);
});
expect(titleEditField).toHaveValue(newDisplayName);

titleEditField = queryByRole('textbox', { name: headerTitleMessages.ariaLabelButtonEdit.defaultMessage });
expect(titleEditField).not.toBeInTheDocument();
expect(await findByText(newDisplayName)).toBeInTheDocument();
//
});
});
Loading

0 comments on commit 77e4b4c

Please sign in to comment.