Agrendalath/bb 2599 low priority tests (#214)
* [TNL-7269] WIP low priority tests * [TNL-7269] Add low priority tests * [TNL-7269] Fix failing EnrollmentAlert tests * [TNL-7269] Address review comments * Fixing test errors on rebase with master. Co-authored-by: Agrendalath <piotr@surowiec.it>
This commit is contained in:
157
src/courseware/course/Course.test.jsx
Normal file
157
src/courseware/course/Course.test.jsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import React from 'react';
|
||||
import { Factory } from 'rosie';
|
||||
import {
|
||||
loadUnit, render, screen, waitFor, getByRole, initializeTestStore, fireEvent,
|
||||
} from '../../setupTest';
|
||||
import Course from './Course';
|
||||
import { handleNextSectionCelebration } from './celebration';
|
||||
import * as celebrationUtils from './celebration/utils';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
const recordFirstSectionCelebration = jest.fn();
|
||||
celebrationUtils.recordFirstSectionCelebration = recordFirstSectionCelebration;
|
||||
|
||||
describe('Course', () => {
|
||||
let store;
|
||||
const mockData = {
|
||||
nextSequenceHandler: () => {},
|
||||
previousSequenceHandler: () => {},
|
||||
unitNavigationHandler: () => {},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
store = await initializeTestStore();
|
||||
const { courseware, models } = store.getState();
|
||||
const { courseId, sequenceId } = courseware;
|
||||
Object.assign(mockData, {
|
||||
courseId,
|
||||
sequenceId,
|
||||
unitId: Object.values(models.units)[0].id,
|
||||
});
|
||||
});
|
||||
|
||||
it('loads learning sequence', async () => {
|
||||
render(<Course {...mockData} />);
|
||||
expect(screen.getByRole('navigation', { name: 'breadcrumb' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Loading learning sequence...')).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Learn About Verified Certificates' })).not.toBeInTheDocument();
|
||||
|
||||
loadUnit();
|
||||
await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument());
|
||||
|
||||
const { models } = store.getState();
|
||||
const sequence = models.sequences[mockData.sequenceId];
|
||||
const section = models.sections[sequence.sectionId];
|
||||
const course = models.courses[mockData.courseId];
|
||||
expect(document.title).toMatch(
|
||||
`${sequence.title} | ${section.title} | ${course.title} | edX`,
|
||||
);
|
||||
});
|
||||
|
||||
it('displays celebration modal', async () => {
|
||||
// TODO: Remove these console mocks after merging https://github.com/edx/paragon/pull/526.
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
// Mock media queries, because `Celebration` modal uses `react-break` for responsive breakpoints.
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(query => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
const courseMetadata = Factory.build('courseMetadata', { celebrations: { firstSection: true } });
|
||||
const testStore = await initializeTestStore({ courseMetadata }, false);
|
||||
const { courseware, models } = testStore.getState();
|
||||
const { courseId, sequenceId } = courseware;
|
||||
const testData = {
|
||||
...mockData,
|
||||
courseId,
|
||||
sequenceId,
|
||||
unitId: Object.values(models.units)[0].id,
|
||||
};
|
||||
// Set up LocalStorage for testing.
|
||||
handleNextSectionCelebration(sequenceId, sequenceId, testData.unitId);
|
||||
render(<Course {...testData} />, { store: testStore });
|
||||
|
||||
const celebrationModal = screen.getByRole('dialog');
|
||||
expect(celebrationModal).toBeInTheDocument();
|
||||
expect(getByRole(celebrationModal, 'heading', { name: 'Congratulations!' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays upgrade sock', async () => {
|
||||
const courseMetadata = Factory.build('courseMetadata', { can_show_upgrade_sock: true });
|
||||
const testStore = await initializeTestStore({ courseMetadata, excludeFetchSequence: true }, false);
|
||||
|
||||
render(<Course {...mockData} courseId={courseMetadata.id} />, { store: testStore });
|
||||
expect(screen.getByRole('button', { name: 'Learn About Verified Certificates' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays offer and expiration alert', async () => {
|
||||
const offerText = 'test-offer';
|
||||
const offerId = `${offerText}-id`;
|
||||
const offerHtml = `<div data-testid="${offerId}">${offerText}</div>`;
|
||||
|
||||
const expirationText = 'test-expiration';
|
||||
const expirationId = `${expirationText}-id`;
|
||||
const expirationHtml = `<div data-testid="${expirationId}">${expirationText}</div>`;
|
||||
|
||||
const courseMetadata = Factory.build('courseMetadata', {
|
||||
offer_html: offerHtml,
|
||||
course_expired_message: expirationHtml,
|
||||
});
|
||||
const testStore = await initializeTestStore({ courseMetadata, excludeFetchSequence: true }, false);
|
||||
render(<Course {...mockData} courseId={courseMetadata.id} />, { store: testStore });
|
||||
|
||||
expect(await screen.findByTestId(offerId)).toHaveTextContent(offerText);
|
||||
expect(screen.getByTestId(expirationId)).toHaveTextContent(expirationText);
|
||||
});
|
||||
|
||||
it('passes handlers to the sequence', async () => {
|
||||
const nextSequenceHandler = jest.fn();
|
||||
const previousSequenceHandler = jest.fn();
|
||||
const unitNavigationHandler = jest.fn();
|
||||
|
||||
const courseMetadata = Factory.build('courseMetadata');
|
||||
const unitBlocks = Array.from({ length: 3 }).map(() => Factory.build(
|
||||
'block',
|
||||
{ type: 'vertical' },
|
||||
{ courseId: courseMetadata.id },
|
||||
));
|
||||
const testStore = await initializeTestStore({ courseMetadata, unitBlocks }, false);
|
||||
const { courseware, models } = testStore.getState();
|
||||
const { courseId, sequenceId } = courseware;
|
||||
const testData = {
|
||||
...mockData,
|
||||
courseId,
|
||||
sequenceId,
|
||||
unitId: Object.values(models.units)[1].id, // Corner cases are already covered in `Sequence` tests.
|
||||
nextSequenceHandler,
|
||||
previousSequenceHandler,
|
||||
unitNavigationHandler,
|
||||
};
|
||||
render(<Course {...testData} />, { store: testStore });
|
||||
|
||||
loadUnit();
|
||||
await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument());
|
||||
screen.getAllByRole('button', { name: /previous/i }).forEach(button => fireEvent.click(button));
|
||||
screen.getAllByRole('button', { name: /next/i }).forEach(button => fireEvent.click(button));
|
||||
|
||||
// We are in the middle of the sequence, so no
|
||||
expect(previousSequenceHandler).not.toHaveBeenCalled();
|
||||
expect(nextSequenceHandler).not.toHaveBeenCalled();
|
||||
expect(unitNavigationHandler).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
||||
94
src/courseware/course/bookmark/BookmarkButton.test.jsx
Normal file
94
src/courseware/course/bookmark/BookmarkButton.test.jsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { Factory } from 'rosie';
|
||||
import {
|
||||
render, screen, fireEvent, initializeTestStore, waitFor, authenticatedUser, logUnhandledRequests,
|
||||
} from '../../../setupTest';
|
||||
import { BookmarkButton } from './index';
|
||||
|
||||
describe('Bookmark Button', () => {
|
||||
let axiosMock;
|
||||
let store;
|
||||
const courseMetadata = Factory.build('courseMetadata');
|
||||
const mockData = {
|
||||
isProcessing: false,
|
||||
};
|
||||
const nonBookmarkedUnitBlock = Factory.build(
|
||||
'block',
|
||||
{ type: 'vertical' },
|
||||
{ courseId: courseMetadata.id },
|
||||
);
|
||||
const bookmarkedUnitBlock = Factory.build(
|
||||
'block',
|
||||
{ type: 'vertical', bookmarked: true },
|
||||
{ courseId: courseMetadata.id },
|
||||
);
|
||||
const unitBlocks = [nonBookmarkedUnitBlock, bookmarkedUnitBlock];
|
||||
|
||||
beforeEach(async () => {
|
||||
store = await initializeTestStore({ courseMetadata, unitBlocks });
|
||||
mockData.unitId = nonBookmarkedUnitBlock.id;
|
||||
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
const bookmarkUrl = `${getConfig().LMS_BASE_URL}/api/bookmarks/v1/bookmarks/`;
|
||||
axiosMock.onPost(bookmarkUrl).reply(200, { });
|
||||
|
||||
const bookmarkDeleteUrlRegExp = new RegExp(`${bookmarkUrl}*,*`);
|
||||
axiosMock.onDelete(bookmarkDeleteUrlRegExp).reply(200, { });
|
||||
logUnhandledRequests(axiosMock);
|
||||
});
|
||||
|
||||
it('handles adding bookmark', async () => {
|
||||
render(<BookmarkButton {...mockData} />);
|
||||
|
||||
const button = screen.getByRole('button', { name: 'Bookmark this page' });
|
||||
expect(button).not.toHaveClass('disabled');
|
||||
|
||||
fireEvent.click(button);
|
||||
await waitFor(() => expect(axiosMock.history.post).toHaveLength(1));
|
||||
expect(axiosMock.history.post[0].data).toEqual(JSON.stringify({ usage_id: nonBookmarkedUnitBlock.id }));
|
||||
expect(store.getState().models.units[nonBookmarkedUnitBlock.id].bookmarked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not handle adding bookmark when processing', async () => {
|
||||
render(<BookmarkButton {...mockData} isProcessing />);
|
||||
|
||||
const button = screen.getByRole('button', { name: 'Bookmark this page' });
|
||||
expect(button).toHaveClass('disabled');
|
||||
|
||||
fireEvent.click(button);
|
||||
// HACK: We don't have a function we could reliably await here, so this test relies on the timeout of `waitFor`.
|
||||
await expect(waitFor(
|
||||
() => expect(axiosMock.history.post).toHaveLength(1),
|
||||
{ timeout: 100 },
|
||||
)).rejects.toThrowError(/expect.*toHaveLength.*/);
|
||||
expect(store.getState().models.units[nonBookmarkedUnitBlock.id].bookmarked).toBeFalsy();
|
||||
});
|
||||
|
||||
it('handles removing bookmark', async () => {
|
||||
render(<BookmarkButton {...mockData} unitId={bookmarkedUnitBlock.id} isBookmarked />);
|
||||
const button = screen.getByRole('button', { name: 'Bookmarked' });
|
||||
|
||||
fireEvent.click(button);
|
||||
await waitFor(() => expect(axiosMock.history.delete).toHaveLength(1));
|
||||
expect(axiosMock.history.delete[0].url).toContain(`${authenticatedUser.username},${bookmarkedUnitBlock.id}`);
|
||||
expect(store.getState().models.units[bookmarkedUnitBlock.id].bookmarked).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not handle removing bookmark when processing', async () => {
|
||||
render(<BookmarkButton {...mockData} unitId={bookmarkedUnitBlock.id} isBookmarked isProcessing />);
|
||||
|
||||
const button = screen.getByRole('button', { name: 'Bookmarked' });
|
||||
expect(button).toHaveClass('disabled');
|
||||
|
||||
fireEvent.click(button);
|
||||
// HACK: We don't have a function we could reliably await here, so this test relies on the timeout of `waitFor`.
|
||||
await expect(waitFor(
|
||||
() => expect(axiosMock.history.delete).toHaveLength(1),
|
||||
{ timeout: 100 },
|
||||
)).rejects.toThrowError(/expect.*toHaveLength.*/);
|
||||
expect(store.getState().models.units[bookmarkedUnitBlock.id].bookmarked).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@ import * as thunks from './thunks';
|
||||
|
||||
import executeThunk from '../../../../utils';
|
||||
|
||||
import initializeMockApp from '../../../../setupTest';
|
||||
import { initializeMockApp } from '../../../../setupTest';
|
||||
import initializeStore from '../../../../store';
|
||||
|
||||
const { loggingService } = initializeMockApp();
|
||||
|
||||
42
src/courseware/course/content-tools/ContentTools.test.jsx
Normal file
42
src/courseware/course/content-tools/ContentTools.test.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { initializeTestStore, render, screen } from '../../../setupTest';
|
||||
import ContentTools from './ContentTools';
|
||||
|
||||
jest.mock('./calculator/Calculator', () => () => <div data-testid="Calculator" />);
|
||||
jest.mock('./notes-visibility/NotesVisibility', () => () => <div data-testid="NotesVisibility" />);
|
||||
|
||||
describe('Content Tools', () => {
|
||||
const mockData = {
|
||||
course: {
|
||||
notes: { enabled: false },
|
||||
showCalculator: false,
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeTestStore({ excludeFetchCourse: true, excludeFetchSequence: true });
|
||||
});
|
||||
|
||||
it('hides content tools', () => {
|
||||
const { container } = render(<ContentTools {...mockData} />);
|
||||
expect(container.getElementsByClassName('d-flex')[0]).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('displays Calculator', () => {
|
||||
const testData = JSON.parse(JSON.stringify(mockData));
|
||||
testData.course.showCalculator = true;
|
||||
render(<ContentTools {...testData} />);
|
||||
|
||||
expect(screen.getByTestId('Calculator')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('NotesVisibility')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays Notes Visibility', () => {
|
||||
const testData = JSON.parse(JSON.stringify(mockData));
|
||||
testData.course.notes.enabled = true;
|
||||
render(<ContentTools {...testData} />);
|
||||
|
||||
expect(screen.getByTestId('NotesVisibility')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('Calculator')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import Calculator from './Calculator';
|
||||
import {
|
||||
initializeTestStore, render, screen, fireEvent, waitFor, logUnhandledRequests,
|
||||
} from '../../../../setupTest';
|
||||
|
||||
describe('Calculator', () => {
|
||||
let axiosMock;
|
||||
let equationUrl;
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeTestStore({ excludeFetchCourse: true, excludeFetchSequence: true });
|
||||
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
equationUrl = new RegExp(`${getConfig().LMS_BASE_URL}/calculate*`);
|
||||
});
|
||||
|
||||
it('expands on click', () => {
|
||||
render(<Calculator />);
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Calculate' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Calculator Instructions' })).not.toBeInTheDocument();
|
||||
|
||||
const button = screen.getByRole('button', { name: 'Calculator' });
|
||||
expect(button.querySelector('svg')).toHaveClass('fa-calculator');
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(button.querySelector('svg')).toHaveClass('fa-times-circle');
|
||||
expect(screen.getByRole('button', { name: 'Calculate' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Calculator Instructions' })).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(button.querySelector('svg')).toHaveClass('fa-calculator');
|
||||
});
|
||||
|
||||
it('displays instructions on click', () => {
|
||||
render(<Calculator />);
|
||||
|
||||
const button = screen.getByRole('button', { name: 'Calculator' });
|
||||
fireEvent.click(button);
|
||||
|
||||
const instructionsButton = screen.getByRole('button', { name: 'Calculator Instructions' });
|
||||
expect(instructionsButton.querySelector('svg')).toHaveClass('fa-question-circle');
|
||||
expect(screen.queryByText(/For detailed information, see/)).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(instructionsButton);
|
||||
expect(instructionsButton.querySelector('svg')).toHaveClass('fa-times-circle');
|
||||
expect(screen.getByText(/For detailed information, see/)).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(instructionsButton);
|
||||
expect(instructionsButton.querySelector('svg')).toHaveClass('fa-question-circle');
|
||||
});
|
||||
|
||||
it('handles submitting equation', async () => {
|
||||
const equation = 'log2(2^10)';
|
||||
const result = '10';
|
||||
|
||||
axiosMock.reset();
|
||||
axiosMock.onGet(equationUrl).reply(200, { result });
|
||||
logUnhandledRequests(axiosMock);
|
||||
|
||||
render(<Calculator />);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Calculator' }));
|
||||
const input = screen.getByRole('textbox', { name: 'Calculator Input' });
|
||||
const output = screen.getByRole('textbox', { name: 'Calculator Result' });
|
||||
const submitButton = screen.getByRole('button', { name: 'Calculate' });
|
||||
|
||||
fireEvent.change(input, { target: { value: equation } });
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
await waitFor(() => expect(axiosMock.history.get).toHaveLength(1));
|
||||
expect(axiosMock.history.get[0].url).toContain(escape(equation));
|
||||
|
||||
expect(output).toHaveValue(result);
|
||||
});
|
||||
});
|
||||
@@ -57,11 +57,10 @@ class NotesVisibility extends Component {
|
||||
NotesVisibility.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
course: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
id: PropTypes.string.isRequired,
|
||||
notes: PropTypes.shape({
|
||||
enabled: PropTypes.bool,
|
||||
visible: PropTypes.bool,
|
||||
}),
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
import { waitFor } from '@testing-library/dom';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import {
|
||||
fireEvent, initializeTestStore, logUnhandledRequests, render, screen,
|
||||
} from '../../../../setupTest';
|
||||
import NotesVisibility from './NotesVisibility';
|
||||
|
||||
const originalConfig = jest.requireActual('@edx/frontend-platform').getConfig();
|
||||
jest.mock('@edx/frontend-platform', () => ({
|
||||
...jest.requireActual('@edx/frontend-platform'),
|
||||
getConfig: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Notes Visibility', () => {
|
||||
let axiosMock;
|
||||
let visibilityUrl;
|
||||
const mockData = {
|
||||
course: {
|
||||
id: 'test-course',
|
||||
notes: {
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeTestStore({ excludeFetchCourse: true, excludeFetchSequence: true });
|
||||
|
||||
// Mock `targetOrigin` of the `postMessage`.
|
||||
getConfig.mockImplementation(() => originalConfig);
|
||||
const config = { ...originalConfig };
|
||||
config.LMS_BASE_URL = `${window.location.protocol}//${window.location.host}`;
|
||||
getConfig.mockImplementation(() => config);
|
||||
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
visibilityUrl = `${config.LMS_BASE_URL}/courses/${mockData.course.id}/edxnotes/visibility/`;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
axiosMock.reset();
|
||||
axiosMock.onPut(visibilityUrl).reply(200);
|
||||
logUnhandledRequests(axiosMock);
|
||||
});
|
||||
|
||||
it('hides notes', () => {
|
||||
render(<NotesVisibility {...mockData} />);
|
||||
|
||||
const button = screen.getByRole('switch', { name: 'Show Notes' });
|
||||
expect(button)
|
||||
.not.toBeChecked()
|
||||
.toHaveClass('text-success');
|
||||
expect(button.querySelector('svg'))
|
||||
.toHaveClass('fa-pencil-alt')
|
||||
.toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('shows notes', () => {
|
||||
const testData = JSON.parse(JSON.stringify(mockData));
|
||||
testData.course.notes.visible = true;
|
||||
render(<NotesVisibility {...testData} />);
|
||||
|
||||
const button = screen.getByRole('switch', { name: 'Hide Notes' });
|
||||
expect(button)
|
||||
.toBeChecked()
|
||||
.toHaveClass('text-secondary');
|
||||
expect(button.querySelector('svg'))
|
||||
.toHaveClass('fa-pencil-alt')
|
||||
.toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('handles click', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const frame = document.createElement('iframe');
|
||||
frame.id = 'unit-iframe';
|
||||
const { container } = render(<NotesVisibility {...mockData} />);
|
||||
|
||||
container.appendChild(frame);
|
||||
frame.contentWindow.addEventListener('message', e => {
|
||||
mockFn(e.data);
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('switch', { name: 'Show Notes' }));
|
||||
await waitFor(() => expect(mockFn).toHaveBeenCalled());
|
||||
expect(mockFn)
|
||||
.toHaveBeenCalledTimes(1)
|
||||
.toHaveBeenCalledWith('tools.toggleNotes');
|
||||
|
||||
expect(axiosMock.history.put).toHaveLength(1);
|
||||
expect(axiosMock.history.put[0].url).toEqual(visibilityUrl);
|
||||
expect(axiosMock.history.put[0].data).toEqual(`{"visibility":${mockData.course.notes.visible}}`);
|
||||
|
||||
expect(screen.getByRole('switch', { name: 'Hide Notes' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -191,9 +191,5 @@ CourseSock.propTypes = {
|
||||
currencySymbol: PropTypes.string,
|
||||
sku: PropTypes.string,
|
||||
upgradeUrl: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
CourseSock.defaultProps = {
|
||||
verifiedMode: null,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
41
src/courseware/course/course-sock/CourseSock.test.jsx
Normal file
41
src/courseware/course/course-sock/CourseSock.test.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
render, screen, fireEvent, initializeMockApp,
|
||||
} from '../../../setupTest';
|
||||
import CourseSock from './CourseSock';
|
||||
|
||||
describe('Course Sock', () => {
|
||||
const mockData = {
|
||||
verifiedMode: {
|
||||
upgradeUrl: 'test-url',
|
||||
price: 1234,
|
||||
currency: 'dollars',
|
||||
currencySymbol: '$',
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
|
||||
await initializeMockApp();
|
||||
});
|
||||
|
||||
it('hides upsell information on load', () => {
|
||||
render(<CourseSock {...mockData} />);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Learn About Verified Certificates' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('edX Verified Certificate')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles click', () => {
|
||||
render(<CourseSock {...mockData} />);
|
||||
const upsellButton = screen.getByRole('button', { name: 'Learn About Verified Certificates' });
|
||||
fireEvent.click(upsellButton);
|
||||
|
||||
expect(screen.getByText('edX Verified Certificate')).toBeInTheDocument();
|
||||
const { currencySymbol, price, currency } = mockData.verifiedMode;
|
||||
expect(screen.getByText(`Upgrade (${currencySymbol}${price} ${currency})`)).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(upsellButton);
|
||||
expect(screen.queryByText('edX Verified Certificate')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { history } from '@edx/frontend-platform';
|
||||
import {
|
||||
render, screen, fireEvent, initializeMockApp,
|
||||
} from '../../../../setupTest';
|
||||
import ContentLock from './ContentLock';
|
||||
|
||||
describe('Content Lock', () => {
|
||||
const mockData = {
|
||||
courseId: 'test-course-id',
|
||||
prereqSectionName: 'test-prerequisite-section-name',
|
||||
prereqId: 'test-prerequisite-id',
|
||||
sequenceTitle: 'test-sequence-title',
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
|
||||
await initializeMockApp();
|
||||
});
|
||||
|
||||
it('displays sequence title along with lock icon', () => {
|
||||
const { container } = render(<ContentLock {...mockData} />);
|
||||
|
||||
const lockIcon = container.querySelector('svg');
|
||||
expect(lockIcon).toHaveClass('fa-lock');
|
||||
expect(lockIcon.parentElement).toHaveTextContent(mockData.sequenceTitle);
|
||||
});
|
||||
|
||||
it('displays prerequisite name', () => {
|
||||
const prereqText = `You must complete the prerequisite: '${mockData.prereqSectionName}' to access this content.`;
|
||||
render(<ContentLock {...mockData} />);
|
||||
|
||||
expect(screen.getByText(prereqText)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles click', () => {
|
||||
history.push = jest.fn();
|
||||
render(<ContentLock {...mockData} />);
|
||||
fireEvent.click(screen.getByRole('button'));
|
||||
|
||||
expect(history.push).toHaveBeenCalledWith(`/course/${mockData.courseId}/${mockData.prereqId}`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Factory } from 'rosie';
|
||||
import { initializeTestStore, render, screen } from '../../../../setupTest';
|
||||
import LockPaywall from './LockPaywall';
|
||||
|
||||
describe('Lock Paywall', () => {
|
||||
let store;
|
||||
const mockData = {};
|
||||
|
||||
beforeAll(async () => {
|
||||
store = await initializeTestStore();
|
||||
const { courseware } = store.getState();
|
||||
mockData.courseId = courseware.courseId;
|
||||
});
|
||||
|
||||
it('displays message along with lock icon', () => {
|
||||
const { container } = render(<LockPaywall {...mockData} />);
|
||||
|
||||
const lockIcon = container.querySelector('svg');
|
||||
expect(lockIcon).toHaveClass('fa-lock');
|
||||
expect(lockIcon.parentElement).toHaveTextContent('Verified Track Access');
|
||||
});
|
||||
|
||||
it('displays unlock link with price', () => {
|
||||
const { currencySymbol, price, upgradeUrl } = store.getState().models.courses[mockData.courseId].verifiedMode;
|
||||
render(<LockPaywall {...mockData} />);
|
||||
|
||||
const upgradeLink = screen.getByRole('link', { name: `Upgrade to unlock (${currencySymbol}${price})` });
|
||||
expect(upgradeLink).toHaveAttribute('href', `${upgradeUrl}`);
|
||||
});
|
||||
|
||||
it('does not display anything if course does not have verified mode', async () => {
|
||||
const courseMetadata = Factory.build('courseMetadata', { verified_mode: null });
|
||||
const testStore = await initializeTestStore({ courseMetadata, excludeFetchSequence: true }, false);
|
||||
const { container } = render(<LockPaywall {...mockData} courseId={courseMetadata.id} />, { store: testStore });
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user