test: improve coverage for section, subsection & unit configuration

This commit is contained in:
Navin Karkera
2024-01-19 12:56:31 +05:30
committed by Kristin Aoki
parent 13cb1d3539
commit 5a2dbad343
3 changed files with 149 additions and 93 deletions

View File

@@ -15,10 +15,10 @@
"stylelint": "stylelint \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json",
"lint": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx . --fix",
"snapshot": "fedx-scripts jest --updateSnapshot",
"snapshot": "TZ=UTC fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"start:with-theme": "paragon install-theme && npm start && npm install",
"test": "fedx-scripts jest --coverage --passWithNoTests",
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests",
"types": "tsc --noEmit"
},
"husky": {

View File

@@ -20,9 +20,6 @@ import {
} from './data/api';
import { RequestStatus } from '../data/constants';
import {
configureCourseSectionQuery,
configureCourseSubsectionQuery,
configureCourseUnitQuery,
fetchCourseBestPracticesQuery,
fetchCourseLaunchQuery,
fetchCourseOutlineIndexQuery,
@@ -615,58 +612,69 @@ describe('<CourseOutline />', () => {
// section doesn't display badges
});
it('check configure section when configure query is successful', async () => {
const { findAllByTestId, findByPlaceholderText } = render(<RootWrapper />);
it('check configure modal for section', async () => {
const { findByTestId, findAllByTestId } = render(<RootWrapper />);
const section = courseOutlineIndexMock.courseStructure.childInfo.children[0];
const newReleaseDate = '2025-08-10T10:00:00Z';
const newReleaseDateIso = '2025-09-10T22:00:00Z';
const newReleaseDate = '09/10/2025';
axiosMock
.onPost(getCourseItemApiUrl(section.id), {
publish: 'republish',
metadata: {
visible_to_staff_only: true,
start: newReleaseDate,
start: newReleaseDateIso,
},
})
.reply(200, { dummy: 'value' });
axiosMock
.onGet(getXBlockApiUrl(section.id))
.reply(200, section);
.reply(200, {
...section,
start: newReleaseDateIso,
});
const [firstSection] = await findAllByTestId('section-card');
const sectionDropdownButton = await within(firstSection).findByTestId('section-card-header__menu-button');
fireEvent.click(sectionDropdownButton);
axiosMock
.onGet(getXBlockApiUrl(section.id))
.reply(200, {
...section,
start: newReleaseDate,
});
await executeThunk(configureCourseSectionQuery(section.id, true, newReleaseDate), store.dispatch);
fireEvent.click(sectionDropdownButton);
await act(async () => fireEvent.click(sectionDropdownButton));
const configureBtn = await within(firstSection).findByTestId('section-card-header__menu-configure-button');
fireEvent.click(configureBtn);
await act(async () => fireEvent.click(configureBtn));
let releaseDateStack = await findByTestId('release-date-stack');
let releaseDatePicker = await within(releaseDateStack).findByPlaceholderText('MM/DD/YYYY');
expect(releaseDatePicker).toHaveValue('08/10/2023');
const datePicker = await findByPlaceholderText('MM/DD/YYYY');
expect(datePicker).toHaveValue('08/10/2025');
await act(async () => fireEvent.change(releaseDatePicker, { target: { value: newReleaseDate } }));
expect(releaseDatePicker).toHaveValue(newReleaseDate);
const saveButton = await findByTestId('configure-save-button');
await act(async () => fireEvent.click(saveButton));
expect(axiosMock.history.post.length).toBe(1);
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
publish: 'republish',
metadata: {
visible_to_staff_only: true,
start: newReleaseDateIso,
},
}));
await act(async () => fireEvent.click(sectionDropdownButton));
await act(async () => fireEvent.click(configureBtn));
releaseDateStack = await findByTestId('release-date-stack');
releaseDatePicker = await within(releaseDateStack).findByPlaceholderText('MM/DD/YYYY');
expect(releaseDatePicker).toHaveValue(newReleaseDate);
});
it('check configure subsection when configure subsection query is successful', async () => {
it('check configure modal for subsection', async () => {
const {
findAllByTestId,
findByText,
findAllByRole,
findByRole,
findByTestId,
} = render(<RootWrapper />);
const section = courseOutlineIndexMock.courseStructure.childInfo.children[0];
const subsection = section.childInfo.children[0];
const newReleaseDate = '2025-08-10T10:00:00Z';
const newReleaseDate = '2025-08-10T05:00:00Z';
const newGraderType = 'Homework';
const newDue = '2025-09-10T10:00:00Z';
const newDue = '2025-09-10T00:00:00Z';
const isTimeLimited = true;
const defaultTimeLimitMinutes = 210;
@@ -675,10 +683,10 @@ describe('<CourseOutline />', () => {
publish: 'republish',
graderType: newGraderType,
metadata: {
visible_to_staff_only: true,
visible_to_staff_only: null,
due: newDue,
hide_after_due: false,
show_correctness: false,
show_correctness: 'always',
is_practice_exam: false,
is_time_limited: isTimeLimited,
exam_review_rules: '',
@@ -690,14 +698,9 @@ describe('<CourseOutline />', () => {
})
.reply(200, { dummy: 'value' });
axiosMock
.onGet(getXBlockApiUrl(section.id))
.reply(200, section);
const [currentSection] = await findAllByTestId('section-card');
const [firstSubsection] = await within(currentSection).findAllByTestId('subsection-card');
const subsectionDropdownButton = firstSubsection.querySelector('#subsection-card-header__menu');
expect(subsectionDropdownButton).toBeInTheDocument();
const subsectionDropdownButton = await within(firstSubsection).findByTestId('subsection-card-header__menu-button');
subsection.start = newReleaseDate;
subsection.due = newDue;
@@ -709,42 +712,78 @@ describe('<CourseOutline />', () => {
.onGet(getXBlockApiUrl(section.id))
.reply(200, section);
await executeThunk(configureCourseSubsectionQuery(
subsection.id,
section.id,
true,
newReleaseDate,
newGraderType,
newDue,
true,
defaultTimeLimitMinutes,
false,
false,
), store.dispatch);
fireEvent.click(subsectionDropdownButton);
const configureBtn = await within(firstSubsection).findByTestId('subsection-card-header__menu-configure-button');
fireEvent.click(configureBtn);
expect(await findByText(newGraderType)).toBeInTheDocument();
const releaseDateStack = await findByTestId('release-date-stack');
const releaseDatePicker = await within(releaseDateStack).findByPlaceholderText('MM/DD/YYYY');
expect(releaseDatePicker).toHaveValue('08/10/2025');
const dueDateStack = await findByTestId('due-date-stack');
const dueDatePicker = await within(dueDateStack).findByPlaceholderText('MM/DD/YYYY');
expect(dueDatePicker).toHaveValue('09/10/2025');
// update fields
let configureModal = await findByTestId('configure-modal');
expect(await within(configureModal).findByText(newGraderType)).toBeInTheDocument();
let releaseDateStack = await within(configureModal).findByTestId('release-date-stack');
let releaseDatePicker = await within(releaseDateStack).findByPlaceholderText('MM/DD/YYYY');
fireEvent.change(releaseDatePicker, { target: { value: '08/10/2025' } });
let dueDateStack = await within(configureModal).findByTestId('due-date-stack');
let dueDatePicker = await within(dueDateStack).findByPlaceholderText('MM/DD/YYYY');
fireEvent.change(dueDatePicker, { target: { value: '09/10/2025' } });
let graderTypeDropdown = await within(configureModal).findByTestId('grader-type-select');
fireEvent.change(graderTypeDropdown, { target: { value: newGraderType } });
const advancedTab = await findByRole('tab', { name: configureModalMessages.advancedTabTitle.defaultMessage });
let advancedTab = await within(configureModal).findByRole('tab', { name: configureModalMessages.advancedTabTitle.defaultMessage });
fireEvent.click(advancedTab);
const radioButtons = await findAllByRole('radio');
let radioButtons = await within(configureModal).findAllByRole('radio');
fireEvent.click(radioButtons[1]);
let hoursWrapper = await within(configureModal).findByTestId('advanced-tab-hours-picker-wrapper');
let hours = await within(hoursWrapper).findByRole('textbox');
fireEvent.change(hours, { target: { value: '03:30' } });
const saveButton = await within(configureModal).findByTestId('configure-save-button');
await act(async () => fireEvent.click(saveButton));
// verify request
expect(axiosMock.history.post.length).toBe(1);
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
publish: 'republish',
graderType: newGraderType,
metadata: {
visible_to_staff_only: null,
due: newDue,
hide_after_due: false,
show_correctness: 'always',
is_practice_exam: false,
is_time_limited: isTimeLimited,
exam_review_rules: '',
is_proctored_enabled: false,
default_time_limit_minutes: defaultTimeLimitMinutes,
is_onboarding_exam: false,
start: newReleaseDate,
},
}));
// reopen modal and check values
await act(async () => fireEvent.click(subsectionDropdownButton));
await act(async () => fireEvent.click(configureBtn));
configureModal = await findByTestId('configure-modal');
releaseDateStack = await within(configureModal).findByTestId('release-date-stack');
releaseDatePicker = await within(releaseDateStack).findByPlaceholderText('MM/DD/YYYY');
expect(releaseDatePicker).toHaveValue('08/10/2025');
dueDateStack = await await within(configureModal).findByTestId('due-date-stack');
dueDatePicker = await within(dueDateStack).findByPlaceholderText('MM/DD/YYYY');
expect(dueDatePicker).toHaveValue('09/10/2025');
graderTypeDropdown = await within(configureModal).findByTestId('grader-type-select');
expect(graderTypeDropdown).toHaveValue(newGraderType);
advancedTab = await within(configureModal).findByRole('tab', { name: configureModalMessages.advancedTabTitle.defaultMessage });
fireEvent.click(advancedTab);
radioButtons = await within(configureModal).findAllByRole('radio');
expect(radioButtons[0]).toHaveProperty('checked', false);
expect(radioButtons[1]).toHaveProperty('checked', true);
const hoursWrapper = await findByTestId('advanced-tab-hours-picker-wrapper');
const hours = await within(hoursWrapper).findByRole('textbox');
hoursWrapper = await within(configureModal).findByTestId('advanced-tab-hours-picker-wrapper');
hours = await within(hoursWrapper).findByRole('textbox');
expect(hours).toHaveValue('03:30');
});
it('check configure unit when configure query is successful', async () => {
const { findAllByTestId, findByText, findByTestId } = render(<RootWrapper />);
it('check configure modal for unit', async () => {
const { findAllByTestId, findByTestId } = render(<RootWrapper />);
const section = courseOutlineIndexMock.courseStructure.childInfo.children[0];
const [subsection] = section.childInfo.children;
const [unit] = subsection.childInfo.children;
@@ -771,8 +810,7 @@ describe('<CourseOutline />', () => {
const subsectionExpandButton = await within(firstSubsection).getByTestId('subsection-card-header__expanded-btn');
fireEvent.click(subsectionExpandButton);
const [firstUnit] = await within(firstSubsection).findAllByTestId('unit-card');
const unitDropdownButton = firstUnit.querySelector('#unit-card-header__menu');
expect(unitDropdownButton).toBeInTheDocument();
const unitDropdownButton = await within(firstUnit).findByTestId('unit-card-header__menu-button');
// after configuraiton response
unit.visibilityState = 'staff_only';
@@ -800,33 +838,49 @@ describe('<CourseOutline />', () => {
],
selectedPartitionIndex: 0,
};
subsection.childInfo.children[0] = unit;
section.childInfo.children[0] = subsection;
axiosMock
.onGet(getXBlockApiUrl(section.id))
.reply(200, section);
await executeThunk(
configureCourseUnitQuery(unit.id, section.id, isVisibleToStaffOnly, newGroupAccess),
store.dispatch,
);
fireEvent.click(unitDropdownButton);
const configureBtn = await within(firstUnit).getByTestId('unit-card-header__menu-configure-button');
// console.log('configureBtn', configureBtn);
fireEvent.click(configureBtn);
expect(await findByText(configureModalMessages.unitVisibility.defaultMessage)).toBeInTheDocument();
const visibilityCheckbox = await findByTestId('unit-visibility-checkbox');
let configureModal = await findByTestId('configure-modal');
expect(await within(configureModal).findByText(
configureModalMessages.unitVisibility.defaultMessage,
)).toBeInTheDocument();
let visibilityCheckbox = await within(configureModal).findByTestId('unit-visibility-checkbox');
await act(async () => fireEvent.click(visibilityCheckbox));
let groupeType = await within(configureModal).findByTestId('group-type-select');
fireEvent.change(groupeType, { target: { value: '0' } });
let checkboxes = await within(await within(configureModal).findByTestId('group-checkboxes')).findAllByRole('checkbox');
fireEvent.click(checkboxes[1]);
const saveButton = await within(configureModal).findByTestId('configure-save-button');
await act(async () => fireEvent.click(saveButton));
// reopen modal and check values
await act(async () => fireEvent.click(unitDropdownButton));
await act(async () => fireEvent.click(configureBtn));
configureModal = await findByTestId('configure-modal');
visibilityCheckbox = await within(configureModal).findByTestId('unit-visibility-checkbox');
expect(visibilityCheckbox).toBeChecked();
const groupeType = await findByTestId('group-type-select');
groupeType = await within(configureModal).findByTestId('group-type-select');
expect(groupeType).toHaveValue('0');
const checkboxes = await within(await findByTestId('group-checkboxes')).findAllByRole('checkbox');
checkboxes = await within(await within(configureModal).findByTestId('group-checkboxes')).findAllByRole('checkbox');
expect(checkboxes[0]).not.toBeChecked();
expect(checkboxes[1]).toBeChecked();
});
it('check update highlights when update highlights query is successfully', async () => {
const { getByRole } = render(<RootWrapper />);

View File

@@ -275,24 +275,26 @@ const ConfigureModal = ({
isFullscreenOnMobile
isFullscreenScroll
>
<ModalDialog.Header className="configure-modal__header">
<ModalDialog.Title>
{intl.formatMessage(messages.title, { title: displayName })}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body className="configure-modal__body">
{renderModalBody(category)}
</ModalDialog.Body>
<ModalDialog.Footer className="pt-1">
<ActionRow>
<ModalDialog.CloseButton variant="tertiary">
{intl.formatMessage(messages.cancelButton)}
</ModalDialog.CloseButton>
<Button onClick={handleSave} disabled={saveButtonDisabled}>
{intl.formatMessage(messages.saveButton)}
</Button>
</ActionRow>
</ModalDialog.Footer>
<div data-testid="configure-modal">
<ModalDialog.Header className="configure-modal__header">
<ModalDialog.Title>
{intl.formatMessage(messages.title, { title: displayName })}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body className="configure-modal__body">
{renderModalBody(category)}
</ModalDialog.Body>
<ModalDialog.Footer className="pt-1">
<ActionRow>
<ModalDialog.CloseButton variant="tertiary">
{intl.formatMessage(messages.cancelButton)}
</ModalDialog.CloseButton>
<Button data-testid="configure-save-button" onClick={handleSave} disabled={saveButtonDisabled}>
{intl.formatMessage(messages.saveButton)}
</Button>
</ActionRow>
</ModalDialog.Footer>
</div>
</ModalDialog>
)
);