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/learning language selection #1299

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
28 changes: 26 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { createConfig } = require('@edx/frontend-build');

module.exports = createConfig('jest', {
const config = createConfig('jest', {
setupFilesAfterEnv: [
'<rootDir>/src/setupTest.js',
],
Expand All @@ -16,6 +16,30 @@ module.exports = createConfig('jest', {
'react-markdown': '<rootDir>/node_modules/react-markdown/react-markdown.min.js',
},
testTimeout: 30000,
globalSetup: "./global-setup.js",
verbose: true,
testEnvironment: 'jsdom',
globalSetup: "./global-setup.js"
// testEnvironmentOptions: {
// url: 'http://localhost/',
// },
});

// delete config.testURL;

config.reporters = [...(config.reporters || []), ["jest-console-group-reporter", {
// change this setting if need to see less details for each test
// reportType: "summary" | "details",
// enable: true | false,
afterEachTest: {
enable: true,
filePaths: false,
reportType: "details",
},
afterAllTests: {
reportType: "summary",
enable: true,
filePaths: true,
},
}]];

module.exports = config;
20,012 changes: 9,012 additions & 11,000 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-component-footer": "12.2.1",
"@edx/frontend-component-header": "4.6.0",
"@edx/frontend-lib-special-exams": "2.29.0",
"@edx/frontend-lib-learning-assistant": "^1.24.0",
"@edx/frontend-lib-special-exams": "2.29.0",
"@edx/frontend-platform": "5.5.2",
"@edx/openedx-atlas": "^0.6.0",
"@edx/paragon": "20.46.0",
Expand All @@ -48,6 +48,7 @@
"classnames": "2.3.2",
"core-js": "3.22.2",
"history": "5.3.0",
"jest": "^26.6.3",
"joi": "^17.11.0",
"js-cookie": "3.0.5",
"lodash.camelcase": "4.3.0",
Expand Down Expand Up @@ -79,7 +80,7 @@
"copy-webpack-plugin": "^11.0.0",
"es-check": "6.2.1",
"husky": "7.0.4",
"jest": "29.5.0",
"jest-console-group-reporter": "^1.0.1",
"rosie": "2.1.1"
}
}
2 changes: 1 addition & 1 deletion src/course-home/courseware-search/CoursewareSearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
</div>
<div className="courseware-search__outer-content">
<div className="courseware-search__content">
<h1 class="h2">{intl.formatMessage(messages.searchModuleTitle)}</h1>
<h1 className="h2">{intl.formatMessage(messages.searchModuleTitle)}</h1>
<CoursewareSearchForm
searchTerm={searchKeyword}
onSubmit={handleSubmit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ const CertificateStatus = ({ intl }) => {
certAvailabilityDate = <FormattedDate value={certificateAvailableDate} day="numeric" month="long" year="numeric" />;
body = (
<FormattedMessage
id="courseCelebration.certificateBody.notAvailable.endDate"
id="progress.certificateStatus.notAvailable.endDate"
defaultMessage="This course ends on {endDate}. Final grades and any earned certificates are
scheduled to be available after {certAvailabilityDate}."
description="This shown for leaner when they are eligible for certifcate but it't not available yet, it could because leaners just finished the course quickly!"
Expand Down
5 changes: 0 additions & 5 deletions src/course-home/progress-tab/certificate-status/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ const messages = defineMessages({
defaultMessage: 'Your certificate is available!',
description: 'Header text when the certifcate is available',
},
downloadableBody: {
id: 'progress.certificateStatus.downloadableBody',
defaultMessage: 'Showcase your accomplishment on LinkedIn or your resumé today. You can download your certificate now and access it any time from your Dashboard and Profile.',
description: 'Recommending an action for learner when course certificate is available',
},
viewableButton: {
id: 'progress.certificateStatus.viewableButton',
defaultMessage: 'View my certificate',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const CompleteDonutSegment = ({ completePercentage, intl, lockedPercentage }) =>
show={showCompletePopover}
placement="top"
overlay={(
<Popover aria-hidden="true">
<Popover id="complete-content-tooltip-popover" aria-hidden="true">
<Popover.Content>
{intl.formatMessage(messages.completeContentTooltip)}
</Popover.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const IncompleteDonutSegment = ({ incompletePercentage, intl }) => {
show={showIncompletePopover}
placement="top"
overlay={(
<Popover aria-hidden="true">
<Popover id="incomplete-tooltip-popover" aria-hidden="true">
<Popover.Content>
{intl.formatMessage(messages.incompleteContentTooltip)}
</Popover.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const LockedDonutSegment = ({ intl, lockedPercentage }) => {
show={showLockedPopover}
placement="top"
overlay={(
<Popover aria-hidden="true">
<Popover id="locked-tooltip-popover" aria-hidden="true">
<Popover.Content>
{intl.formatMessage(messages.lockedContentTooltip)}
</Popover.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ const ProblemScoreDrawer = ({ intl, problemScores, subsection }) => {
<span id="problem-score-label" className="col-auto p-0">{intl.formatMessage(messages.problemScoreLabel)}</span>
<div className={classNames('col', 'p-0', { 'greyed-out': !subsection.learnerHasAccess })}>
<ul className="list-unstyled row w-100 m-0" aria-labelledby="problem-score-label">
{problemScores.map(problemScore => (
<li className="ml-3">{problemScore.earned}{isLocaleRtl ? '\\' : '/'}{problemScore.possible}</li>
{problemScores.map((problemScore, i) => (
// eslint-disable-next-line react/no-array-index-key
<li key={i} className="ml-3">{problemScore.earned}{isLocaleRtl ? '\\' : '/'}{problemScore.possible}</li>
))}
</ul>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/courseware/CoursewareContainer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe('CoursewareContainer', () => {
<Routes>
{DECODE_ROUTES.COURSEWARE.map((route) => (
<Route
key={route}
path={route}
element={<CoursewareContainer />}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/courseware/course/Course.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ describe('Course', () => {
expect(await screen.findByText('Loading learning sequence...')).toBeInTheDocument();

expect(screen.queryByRole('alert')).not.toBeInTheDocument();
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
// one from translation product tour
expect(screen.getAllByRole('dialog')).toHaveLength(1);
expect(screen.queryByRole('button', { name: 'Learn About Verified Certificates' })).not.toBeInTheDocument();

loadUnit();
Expand Down
5 changes: 4 additions & 1 deletion src/courseware/course/CourseBreadcrumbs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const CourseBreadcrumb = ({
<Menu>
{content.map((item) => (
<JumpNavMenuItem
key={item.label}
isDefault={item.default}
sequences={item.sequences}
courseId={courseId}
Expand Down Expand Up @@ -169,8 +170,10 @@ const CourseBreadcrumbs = ({
/>
</Link>
</li>
{links.map((content) => (
{links.map((content, i) => (
<CourseBreadcrumb
// eslint-disable-next-line react/no-array-index-key
key={i}
courseId={courseId}
sequenceId={sequenceId}
content={content}
Expand Down
3 changes: 2 additions & 1 deletion src/courseware/course/sequence/Sequence.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ describe('Sequence', () => {
render(<Sequence {...mockData} />, { wrapWithRouter: true });
expect(await screen.findByText('Loading learning sequence...')).toBeInTheDocument();
// `Previous`, `Bookmark` and `Close Tray` buttons
expect(screen.getAllByRole('button')).toHaveLength(3);
// `Change Language`, `Dismiss` and `Try it` buttons from translation selection.
expect(screen.getAllByRole('button')).toHaveLength(6);
// Renders `Next` button plus one button for each unit.
expect(screen.getAllByRole('link')).toHaveLength(1 + unitBlocks.length);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ exports[`Unit component output snapshot: not bookmarked, do not show content 1`]
<div
className="unit"
>
<h1
className="mb-0 h3"
<div
className="mb-0"
>
unit-title
</h1>
<h3
className="h3"
>
unit-title
</h3>
<TranslationSelection
courseId="test-course-id"
/>
</div>
<h2
className="sr-only"
>
Expand Down
14 changes: 9 additions & 5 deletions src/courseware/course/sequence/Unit/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import UnitSuspense from './UnitSuspense';
import { modelKeys, views } from './constants';
import { useExamAccess, useShouldDisplayHonorCode } from './hooks';
import { getIFrameUrl } from './urls';
import TranslationSelection from './translation-selection';
import { getTranslateLanguage } from './translation-selection/useTranslationSelection';

const Unit = ({
courseId,
format,
onLoaded,
id,
courseId, format, onLoaded, id,
}) => {
const { formatMessage } = useIntl();
const { authenticatedUser } = React.useContext(AppContext);
Expand All @@ -27,17 +26,22 @@ const Unit = ({
const unit = useModel(modelKeys.units, id);
const isProcessing = unit.bookmarkedUpdateState === 'loading';
const view = authenticatedUser ? views.student : views.public;
const translateLanguage = getTranslateLanguage(courseId);

const iframeUrl = getIFrameUrl({
id,
view,
format,
examAccess,
translateLanguage,
});

return (
<div className="unit">
<h1 className="mb-0 h3">{unit.title}</h1>
<div className="mb-0">
<h3 className="h3">{unit.title}</h3>
<TranslationSelection courseId={courseId} />
</div>
<h2 className="sr-only">{formatMessage(messages.headerPlaceholder)}</h2>
<BookmarkButton
unitId={unit.id}
Expand Down
4 changes: 4 additions & 0 deletions src/courseware/course/sequence/Unit/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jest.mock('./ContentIFrame', () => 'ContentIFrame');
jest.mock('./UnitSuspense', () => 'UnitSuspense');
jest.mock('../honor-code', () => 'HonorCode');
jest.mock('../lock-paywall', () => 'LockPaywall');
jest.mock('./translation-selection', () => 'TranslationSelection');
jest.mock('./translation-selection/useTranslationSelection', () => ({
getTranslateLanguage: jest.fn().mockReturnValue('test-translate-language'),
}));

jest.mock('../../../../generic/model-store', () => ({
useModel: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';

import { useIntl } from '@edx/frontend-platform/i18n';
import {
StandardModal,
ActionRow,
Button,
Icon,
ListBox,
ListBoxOption,
} from '@edx/paragon';
import { Check } from '@edx/paragon/icons';

import useTranslationSelection, { languages } from './useTranslationSelection';
import messages, { languageMessages } from './messages';

import './TranslationModal.scss';

const TranslationModal = ({ courseId, isOpen, close }) => {
const { formatMessage } = useIntl();
const { selectedIndex, setSelectedIndex, onSubmit } = useTranslationSelection({ courseId, close });

return (
<StandardModal
title={formatMessage(messages.languageSelectionModalTitle)}
isOpen={isOpen}
onClose={close}
footerNode={(
<ActionRow>
<ActionRow.Spacer />
<Button variant="tertiary" onClick={close}>
{formatMessage(messages.cancelButtonText)}
</Button>
<Button onClick={onSubmit}>{formatMessage(messages.submitButtonText)}</Button>
</ActionRow>
)}
>
<ListBox className="listbox-container">
{languages.map(([key, value], index) => (
<ListBoxOption
className="d-flex justify-content-between"
key={key}
selectedOptionIndex={selectedIndex}
onSelect={() => setSelectedIndex(index)}

Check warning on line 45 in src/courseware/course/sequence/Unit/translation-selection/TranslationModal.jsx

View check run for this annotation

Codecov / codecov/patch

src/courseware/course/sequence/Unit/translation-selection/TranslationModal.jsx#L45

Added line #L45 was not covered by tests
>
{formatMessage(languageMessages[value])}
{selectedIndex === index && <Icon src={Check} />}
</ListBoxOption>
))}
</ListBox>
</StandardModal>
);
};

TranslationModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
close: PropTypes.func.isRequired,
courseId: PropTypes.string.isRequired,
};

export default TranslationModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.listbox-container {
max-height: 400px;

:last-child {
margin-bottom: 5px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { shallow } from '@edx/react-unit-test-utils';

import TranslationModal from './TranslationModal';

jest.mock('./useTranslationSelection', () => ({
__esModule: true,
default: () => ({
selectedIndex: 0,
setSelectedIndex: jest.fn(),
onSubmit: jest.fn().mockName('onSubmit'),
}),
languages: [['en', 'English'], ['es', 'Spanish']],
}));
jest.mock('@edx/paragon', () => jest.requireActual('@edx/react-unit-test-utils').mockComponents({
StandardModal: 'StandardModal',
ActionRow: {
Spacer: 'Spacer',
},
Button: 'Button',
Icon: 'Icon',
ListBox: 'ListBox',
ListBoxOption: 'ListBoxOption',
}));
jest.mock('@edx/paragon/icons', () => ({
Check: jest.fn().mockName('icons.Check'),
}));
jest.mock('@edx/frontend-platform/i18n', () => {
const i18n = jest.requireActual('@edx/frontend-platform/i18n');
const { formatMessage } = jest.requireActual('@edx/react-unit-test-utils');
// this provide consistent for the test on different platform/timezone
const formatDate = jest.fn(date => new Date(date).toISOString()).mockName('useIntl.formatDate');
return {
...i18n,
useIntl: jest.fn(() => ({
formatMessage,
formatDate,
})),
defineMessages: m => m,
FormattedMessage: () => 'FormattedMessage',
};
});

describe('TranslationModal', () => {
const props = {
courseId: 'course-v1:edX+DemoX+Demo_Course',
isOpen: true,
close: jest.fn().mockName('close'),
};
it('renders correctly', () => {
const wrapper = shallow(<TranslationModal {...props} />);
expect(wrapper.snapshot).toMatchSnapshot();
});
});
Loading
Loading