Skip to content

Commit

Permalink
feat: smoke test coverage
Browse files Browse the repository at this point in the history
feat: smoke test coverage
  • Loading branch information
muselesscreator authored Dec 19, 2023
2 parents eb67049 + db05fec commit 32d3024
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 126 deletions.
1 change: 1 addition & 0 deletions src/constants/mockData.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const progressKeys = StrictDict({
peerAssessmentLate: 'peer_assessment_late',
peerAssessmentPartial: 'peer_assessment_partial',
peerAssessmentWaiting: 'peer_assessment_waiting',
peerAssessmentWaitingForGrades: 'peer_assessment_waiting_for_grades',
peerAssessmentFinished: 'peer_assessment_finished',
staffAfterSubmission: 'staff_after_submission',
staffAfterSelf: 'staff_after_self',
Expand Down
2 changes: 2 additions & 0 deletions src/data/services/lms/fakeData/pageData/progress.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const peerStatuses = StrictDict({
notAvailable: createPeerStepInfo({ closedState: closedStates.notAvailable }),
waiting: createPeerStepInfo({ isWaiting: true }),
partial: createPeerStepInfo({ numCompleted: 1 }),
waitingForGrades: createPeerStepInfo({ numCompleted: assessmentSteps.settings.peer.min_number_to_be_graded_by }),
finished: createPeerStepInfo({
closedState: closedStates.open,
numCompleted: assessmentSteps.settings.peer.min_number_to_grade,
Expand Down Expand Up @@ -217,6 +218,7 @@ export const getProgressState = ({ viewStep, progressKey, stepConfig }) => {
[progressKeys.peerAssessment]: peerState(peerStatuses.unsubmitted),
[progressKeys.peerAssessmentEarly]: peerState(peerStatuses.notAvailable),
[progressKeys.peerAssessmentWaiting]: peerState(peerStatuses.waiting),
[progressKeys.peerAssessmentWaitingForGrades]: peerState(peerStatuses.waitingForGrades),
[progressKeys.peerAssessmentLate]: peerState(peerStatuses.closed),
[progressKeys.peerAssessmentPartial]: peerState(peerStatuses.partial),
[progressKeys.peerAssessmentFinished]: createFinishedState(stepNames.peer),
Expand Down
4 changes: 3 additions & 1 deletion src/data/services/lms/hooks/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const post = (...args) => getAuthenticatedHttpClient().post(...args);
export const fakeResponse = (data) => Promise.resolve(camelCaseObject(data));

export const logPageData = (data) => {
console.log({ pageData: data });
if (process.env.NODE_ENV === 'development') {
console.log({ pageData: data });

Check warning on line 12 in src/data/services/lms/hooks/utils.js

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
}
return data;
};
64 changes: 64 additions & 0 deletions src/test/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { stepNames } from 'constants/index';
import { progressKeys } from 'constants/mockData';

const lmsBaseUrl = 'test-base-url';
export const courseId = 'test-course-id';
export const xblockId = 'test-xblock-id';
export const baseUrl = `${lmsBaseUrl}/courses/${courseId}/xblock/${xblockId}/handler`;
export const config = {
LMS_BASE_URL: lmsBaseUrl,
};

const stepProgressKeys = {
[stepNames.submission]: [
progressKeys.submissionUnsaved,
progressKeys.submissionSaved,
],
[stepNames.studentTraining]: [
progressKeys.studentTraining,
progressKeys.studentTrainingPartial,
],
[stepNames.self]: [progressKeys.selfAssessment],
[stepNames.peer]: [
progressKeys.peerAssessment,
progressKeys.peerAssessmentPartial,
progressKeys.peerAssessmentWaiting,
progressKeys.peerAssessmentWaitingForGrades,
],
[stepNames.done]: [progressKeys.graded],
};
const viewProgressKeys = {
[stepNames.xblock]: Object.values(progressKeys),
[stepNames.submission]: [
...stepProgressKeys.submission,
...stepProgressKeys.studentTraining,
...stepProgressKeys.self,
...stepProgressKeys.peer,
...stepProgressKeys.done,
],
[stepNames.studentTraining]: stepProgressKeys.studentTraining,
[stepNames.self]: stepProgressKeys.self,
[stepNames.peer]: stepProgressKeys.peer,
[stepNames.done]: stepProgressKeys.done,
};

export const stepOrders = {
self: [stepNames.self],
peer: [stepNames.studentTraining, stepNames.peer],
staff: [stepNames.staff],
selfToPeer: [stepNames.studentTraining, stepNames.self, stepNames.peer],
selfToStaff: [stepNames.self, stepNames.staff],
peerToSelf: [stepNames.peer, stepNames.self],
peerToSelfWithTraining: [stepNames.studentTraining, stepNames.peer, stepNames.self],
peerToStaff: [stepNames.peer, stepNames.self, stepNames.staff],
peerToStaffWithTraining: [stepNames.studentTraining, stepNames.peer, stepNames.self, stepNames.staff],
};

const allSteps = (stepOrder) => [stepNames.submission, ...stepOrders[stepOrder]];
const loadKeys = (step) => stepProgressKeys[step];
const checkForProgressKeys = (steps) => (key) => steps.map(loadKeys).flat().includes(key);

export const getProgressKeys = (stepOrder, stepName) => viewProgressKeys[stepName]
.filter(checkForProgressKeys(
stepName === stepNames.xblock ? allSteps(stepOrder) : stepOrders[stepOrder],
));
184 changes: 59 additions & 125 deletions src/test/smoke.test.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import React from 'react';
import { render } from '@testing-library/react';
import { when } from 'jest-when';
import { MemoryRouter, useParams } from 'react-router-dom';
import { Provider } from 'react-redux';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useParams } from 'react-router-dom';

import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';

import App from 'App';

import fakeData from 'data/services/lms/fakeData';
import { loadState } from 'data/services/lms/fakeData/dataStates';
import { paths } from 'data/services/lms/urls';

import { stepNames, stepRoutes } from 'constants/index';
import { progressKeys } from 'constants/mockData';

import { createStore } from 'data/store';
import {
courseId,
xblockId,
baseUrl,
config,
getProgressKeys,
stepOrders,
} from './constants';
import {
mockQuerySelector,
post,
pageDataUrl,
loadApp,
mockPageData,
} from './utils';

jest.mock('@edx/frontend-platform/auth', () => ({
...jest.requireActual('@edx/frontend-platform/auth'),
Expand All @@ -42,137 +48,65 @@ jest.mock('axios', () => ({
...jest.requireActual('axios'),
get: jest.fn().mockResolvedValue({ data: 'fake file data' }),
}));
jest.mock('components/HotjarSurvey', () => 'HotjarSurvey');
jest.mock('components/HotjarSurvey', () => 'hot-jar-survey');

jest.unmock('react');
jest.unmock('@edx/paragon');

jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
if (selector === 'html') {
return {
scrollTo: jest.fn(),
};
}
return selector;
});
mockQuerySelector();

const post = jest.fn();
const lmsBaseUrl = 'test-base-url';
const courseId = 'test-course-id';
const xblockId = 'test-xblock-id';
const baseUrl = `${lmsBaseUrl}/courses/${courseId}/xblock/${xblockId}/handler`;
const config = {
LMS_BASE_URL: lmsBaseUrl,
};
when(getConfig).calledWith().mockReturnValue(config);

when(getAuthenticatedHttpClient).calledWith().mockReturnValue({ post });
when(useParams).calledWith().mockReturnValue({ courseId, xblockId });

const renderApp = (route) => {
const store = createStore(false);
const queryClient = new QueryClient({
queries: { retry: false },
});
const location = `/${route}/${courseId}/${xblockId}/`;
return (
<IntlProvider locale="en">
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[location]}>
<App />
</MemoryRouter>
</QueryClientProvider>
</Provider>
</IntlProvider>
);
};

const pageDataUrl = (view = undefined) => {
const url = `${baseUrl}/get_learner_data/`;
return view ? `${url}${view}` : url;
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const loadApp = async (progressKey, step, hasSubmitted = false) => {
const app = renderApp(stepRoutes[step]);
return render(app);
};

const mockPageData = (url, { body, response }) => {
when(post).calledWith(`${baseUrl}${paths.oraConfig}`, {})
.mockResolvedValue({ data: fakeData.oraConfig.assessmentText });
if (body) {
when(post).calledWith(url, expect.anything()).mockResolvedValue({ data: response });
} else {
when(post).calledWith(url).mockResolvedValue({ data: response });
}
};

let el;

describe('Integration smoke tests', () => {
beforeEach(() => {
jest.clearAllMocks();
});
const testModalView = ({ step, keys }) => {
it.each(keys)('renders %s progress state', async (progressKey) => {
const state = loadState({ view: stepRoutes[step], progressKey });
mockPageData(pageDataUrl(step), { body: {}, response: state });
el = await loadApp(progressKey, step);
await el.findAllByText('Create response');
});
};
describe('xblock view', () => {
const xblockProgressKeys = Object.values(progressKeys);
it.each(xblockProgressKeys)('renders %s progress state', async (progressKey) => {
const state = loadState({
view: stepNames.xblock,
progressKey,
describe.each(Object.keys(stepOrders))('For step order %s', (stepOrder) => {
const oraConfig = { ...fakeData.oraConfig.assessmentText };
oraConfig.assessment_steps.order = stepOrders[stepOrder];
when(post).calledWith(`${baseUrl}${paths.oraConfig}`, {})
.mockResolvedValue({ data: oraConfig });
const testModalView = ({ step }) => {
const keys = getProgressKeys(stepOrder, step);
if (keys.length === 0) { return; }
it.each(keys)('renders %s progress state', async (progressKey) => {
const state = loadState({ view: stepRoutes[step], progressKey });
mockPageData(pageDataUrl(step), { body: {}, response: state });
el = await loadApp(progressKey, step);
await el.findAllByText('Create response');
});
};
describe('xblock view', () => {
const keys = getProgressKeys(stepOrder, stepNames.xblock);
it.each(keys)('renders %s progress state', async (progressKey) => {
const state = loadState({
view: stepNames.xblock,
progressKey,
});
mockPageData(pageDataUrl(), { body: {}, response: state });
el = await loadApp(progressKey, stepNames.xblock);
const { title } = fakeData.oraConfig.assessmentText;
await el.findByText(title);
});
mockPageData(pageDataUrl(), { body: {}, response: state });
el = await loadApp(progressKey, stepNames.xblock);
await el.findByText('Open Response Assessment');
});
});
describe('submission view', () => {
const submissionProgressKeys = [
progressKeys.submissionUnsaved,
progressKeys.submissionSaved,
progressKeys.studentTraining,
progressKeys.studentTrainingPartial,
progressKeys.selfAssessment,
progressKeys.peerAssessment,
progressKeys.peerAssessmentPartial,
progressKeys.peerAssessmentWaiting,
progressKeys.staffAfterPeer,
progressKeys.graded,
];
testModalView({ step: stepNames.submission, keys: submissionProgressKeys });
});
describe('studentTraining view', () => {
const trainingProgressKeys = [
progressKeys.studentTraining,
progressKeys.studentTrainingPartial,
];
testModalView({ step: stepNames.studentTraining, keys: trainingProgressKeys });
});
describe('self assessment view', () => {
const selfProgressKeys = [progressKeys.selfAssessment];
testModalView({ step: stepNames.self, keys: selfProgressKeys });
});
describe('peer assessment view', () => {
const peerProgressKeys = [
progressKeys.peerAssessment,
progressKeys.peerAssessmentPartial,
progressKeys.peerAssessmentWaiting,
progressKeys.staffAfterPeer,
progressKeys.graded,
];
testModalView({ step: stepNames.peer, keys: peerProgressKeys });
});
describe('graded view', () => {
const gradedProgressKeys = [progressKeys.graded];
testModalView({ step: stepNames.done, keys: gradedProgressKeys });
describe('submission view', () => {
testModalView({ step: stepNames.submission });
});
describe('studentTraining view', () => {
testModalView({ step: stepNames.studentTraining });
});
describe('self assessment view', () => {
testModalView({ step: stepNames.self });
});
describe('peer assessment view', () => {
testModalView({ step: stepNames.peer });
});
describe('graded view', () => {
testModalView({ step: stepNames.done });
});
});
});
53 changes: 53 additions & 0 deletions src/test/utils.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import { render } from '@testing-library/react';
import { when } from 'jest-when';
import { MemoryRouter } from 'react-router-dom';
import { Provider } from 'react-redux';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { IntlProvider } from '@edx/frontend-platform/i18n';

import App from 'App';

import { stepRoutes } from 'constants/index';

import { createStore } from 'data/store';
import {
courseId,
xblockId,
baseUrl,
} from './constants';

export const mockQuerySelector = () => {
jest.spyOn(document, 'querySelector').mockImplementation((selector) => (
selector === 'html' ? { scrollTo: jest.fn() } : selector
));
};

export const post = jest.fn();
export const renderApp = (route) => {
const store = createStore(false);
const queryClient = new QueryClient({ queries: { retry: false } });
const location = `/${route}/${courseId}/${xblockId}/`;
return (
<IntlProvider locale="en">
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[location]}>
<App />
</MemoryRouter>
</QueryClientProvider>
</Provider>
</IntlProvider>
);
};

const basePageUrl = `${baseUrl}/get_learner_data/`;
export const pageDataUrl = (view = undefined) => (view ? `${basePageUrl}${view}` : basePageUrl);
export const loadApp = async (progressKey, step) => render(renderApp(stepRoutes[step]));

export const mockPageData = (url, { response }) => when(post)
.calledWith(url, expect.anything()).mockResolvedValue({ data: response })
.calledWith(url).mockResolvedValue({ data: response }); // eslint-disable-line newline-per-chained-call
1 change: 1 addition & 0 deletions src/views/SubmissionView/TextResponseEditor/TextEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import { TextArea } from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';
import './TextEditor.scss';

const TextEditor = ({
// id,
Expand Down
3 changes: 3 additions & 0 deletions src/views/SubmissionView/TextResponseEditor/TextEditor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.form-control.textarea-response {
height: 500px;
}

0 comments on commit 32d3024

Please sign in to comment.