Skip to content

Commit 32d3024

Browse files
author
Ben Warzeski
authored
feat: smoke test coverage
feat: smoke test coverage
2 parents eb67049 + db05fec commit 32d3024

File tree

8 files changed

+186
-126
lines changed

8 files changed

+186
-126
lines changed

src/constants/mockData.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const progressKeys = StrictDict({
3535
peerAssessmentLate: 'peer_assessment_late',
3636
peerAssessmentPartial: 'peer_assessment_partial',
3737
peerAssessmentWaiting: 'peer_assessment_waiting',
38+
peerAssessmentWaitingForGrades: 'peer_assessment_waiting_for_grades',
3839
peerAssessmentFinished: 'peer_assessment_finished',
3940
staffAfterSubmission: 'staff_after_submission',
4041
staffAfterSelf: 'staff_after_self',

src/data/services/lms/fakeData/pageData/progress.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const peerStatuses = StrictDict({
8080
notAvailable: createPeerStepInfo({ closedState: closedStates.notAvailable }),
8181
waiting: createPeerStepInfo({ isWaiting: true }),
8282
partial: createPeerStepInfo({ numCompleted: 1 }),
83+
waitingForGrades: createPeerStepInfo({ numCompleted: assessmentSteps.settings.peer.min_number_to_be_graded_by }),
8384
finished: createPeerStepInfo({
8485
closedState: closedStates.open,
8586
numCompleted: assessmentSteps.settings.peer.min_number_to_grade,
@@ -217,6 +218,7 @@ export const getProgressState = ({ viewStep, progressKey, stepConfig }) => {
217218
[progressKeys.peerAssessment]: peerState(peerStatuses.unsubmitted),
218219
[progressKeys.peerAssessmentEarly]: peerState(peerStatuses.notAvailable),
219220
[progressKeys.peerAssessmentWaiting]: peerState(peerStatuses.waiting),
221+
[progressKeys.peerAssessmentWaitingForGrades]: peerState(peerStatuses.waitingForGrades),
220222
[progressKeys.peerAssessmentLate]: peerState(peerStatuses.closed),
221223
[progressKeys.peerAssessmentPartial]: peerState(peerStatuses.partial),
222224
[progressKeys.peerAssessmentFinished]: createFinishedState(stepNames.peer),

src/data/services/lms/hooks/utils.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export const post = (...args) => getAuthenticatedHttpClient().post(...args);
88
export const fakeResponse = (data) => Promise.resolve(camelCaseObject(data));
99

1010
export const logPageData = (data) => {
11-
console.log({ pageData: data });
11+
if (process.env.NODE_ENV === 'development') {
12+
console.log({ pageData: data });
13+
}
1214
return data;
1315
};

src/test/constants.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { stepNames } from 'constants/index';
2+
import { progressKeys } from 'constants/mockData';
3+
4+
const lmsBaseUrl = 'test-base-url';
5+
export const courseId = 'test-course-id';
6+
export const xblockId = 'test-xblock-id';
7+
export const baseUrl = `${lmsBaseUrl}/courses/${courseId}/xblock/${xblockId}/handler`;
8+
export const config = {
9+
LMS_BASE_URL: lmsBaseUrl,
10+
};
11+
12+
const stepProgressKeys = {
13+
[stepNames.submission]: [
14+
progressKeys.submissionUnsaved,
15+
progressKeys.submissionSaved,
16+
],
17+
[stepNames.studentTraining]: [
18+
progressKeys.studentTraining,
19+
progressKeys.studentTrainingPartial,
20+
],
21+
[stepNames.self]: [progressKeys.selfAssessment],
22+
[stepNames.peer]: [
23+
progressKeys.peerAssessment,
24+
progressKeys.peerAssessmentPartial,
25+
progressKeys.peerAssessmentWaiting,
26+
progressKeys.peerAssessmentWaitingForGrades,
27+
],
28+
[stepNames.done]: [progressKeys.graded],
29+
};
30+
const viewProgressKeys = {
31+
[stepNames.xblock]: Object.values(progressKeys),
32+
[stepNames.submission]: [
33+
...stepProgressKeys.submission,
34+
...stepProgressKeys.studentTraining,
35+
...stepProgressKeys.self,
36+
...stepProgressKeys.peer,
37+
...stepProgressKeys.done,
38+
],
39+
[stepNames.studentTraining]: stepProgressKeys.studentTraining,
40+
[stepNames.self]: stepProgressKeys.self,
41+
[stepNames.peer]: stepProgressKeys.peer,
42+
[stepNames.done]: stepProgressKeys.done,
43+
};
44+
45+
export const stepOrders = {
46+
self: [stepNames.self],
47+
peer: [stepNames.studentTraining, stepNames.peer],
48+
staff: [stepNames.staff],
49+
selfToPeer: [stepNames.studentTraining, stepNames.self, stepNames.peer],
50+
selfToStaff: [stepNames.self, stepNames.staff],
51+
peerToSelf: [stepNames.peer, stepNames.self],
52+
peerToSelfWithTraining: [stepNames.studentTraining, stepNames.peer, stepNames.self],
53+
peerToStaff: [stepNames.peer, stepNames.self, stepNames.staff],
54+
peerToStaffWithTraining: [stepNames.studentTraining, stepNames.peer, stepNames.self, stepNames.staff],
55+
};
56+
57+
const allSteps = (stepOrder) => [stepNames.submission, ...stepOrders[stepOrder]];
58+
const loadKeys = (step) => stepProgressKeys[step];
59+
const checkForProgressKeys = (steps) => (key) => steps.map(loadKeys).flat().includes(key);
60+
61+
export const getProgressKeys = (stepOrder, stepName) => viewProgressKeys[stepName]
62+
.filter(checkForProgressKeys(
63+
stepName === stepNames.xblock ? allSteps(stepOrder) : stepOrders[stepOrder],
64+
));

src/test/smoke.test.jsx

Lines changed: 59 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
import React from 'react';
2-
import { render } from '@testing-library/react';
32
import { when } from 'jest-when';
4-
import { MemoryRouter, useParams } from 'react-router-dom';
5-
import { Provider } from 'react-redux';
6-
7-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3+
import { useParams } from 'react-router-dom';
84

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

13-
import App from 'App';
14-
158
import fakeData from 'data/services/lms/fakeData';
169
import { loadState } from 'data/services/lms/fakeData/dataStates';
1710
import { paths } from 'data/services/lms/urls';
1811

1912
import { stepNames, stepRoutes } from 'constants/index';
20-
import { progressKeys } from 'constants/mockData';
2113

22-
import { createStore } from 'data/store';
14+
import {
15+
courseId,
16+
xblockId,
17+
baseUrl,
18+
config,
19+
getProgressKeys,
20+
stepOrders,
21+
} from './constants';
22+
import {
23+
mockQuerySelector,
24+
post,
25+
pageDataUrl,
26+
loadApp,
27+
mockPageData,
28+
} from './utils';
2329

2430
jest.mock('@edx/frontend-platform/auth', () => ({
2531
...jest.requireActual('@edx/frontend-platform/auth'),
@@ -42,137 +48,65 @@ jest.mock('axios', () => ({
4248
...jest.requireActual('axios'),
4349
get: jest.fn().mockResolvedValue({ data: 'fake file data' }),
4450
}));
45-
jest.mock('components/HotjarSurvey', () => 'HotjarSurvey');
51+
jest.mock('components/HotjarSurvey', () => 'hot-jar-survey');
4652

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

50-
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
51-
if (selector === 'html') {
52-
return {
53-
scrollTo: jest.fn(),
54-
};
55-
}
56-
return selector;
57-
});
56+
mockQuerySelector();
5857

59-
const post = jest.fn();
60-
const lmsBaseUrl = 'test-base-url';
61-
const courseId = 'test-course-id';
62-
const xblockId = 'test-xblock-id';
63-
const baseUrl = `${lmsBaseUrl}/courses/${courseId}/xblock/${xblockId}/handler`;
64-
const config = {
65-
LMS_BASE_URL: lmsBaseUrl,
66-
};
6758
when(getConfig).calledWith().mockReturnValue(config);
68-
6959
when(getAuthenticatedHttpClient).calledWith().mockReturnValue({ post });
7060
when(useParams).calledWith().mockReturnValue({ courseId, xblockId });
7161

72-
const renderApp = (route) => {
73-
const store = createStore(false);
74-
const queryClient = new QueryClient({
75-
queries: { retry: false },
76-
});
77-
const location = `/${route}/${courseId}/${xblockId}/`;
78-
return (
79-
<IntlProvider locale="en">
80-
<Provider store={store}>
81-
<QueryClientProvider client={queryClient}>
82-
<MemoryRouter initialEntries={[location]}>
83-
<App />
84-
</MemoryRouter>
85-
</QueryClientProvider>
86-
</Provider>
87-
</IntlProvider>
88-
);
89-
};
90-
91-
const pageDataUrl = (view = undefined) => {
92-
const url = `${baseUrl}/get_learner_data/`;
93-
return view ? `${url}${view}` : url;
94-
};
95-
96-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
97-
const loadApp = async (progressKey, step, hasSubmitted = false) => {
98-
const app = renderApp(stepRoutes[step]);
99-
return render(app);
100-
};
101-
102-
const mockPageData = (url, { body, response }) => {
103-
when(post).calledWith(`${baseUrl}${paths.oraConfig}`, {})
104-
.mockResolvedValue({ data: fakeData.oraConfig.assessmentText });
105-
if (body) {
106-
when(post).calledWith(url, expect.anything()).mockResolvedValue({ data: response });
107-
} else {
108-
when(post).calledWith(url).mockResolvedValue({ data: response });
109-
}
110-
};
111-
11262
let el;
11363

11464
describe('Integration smoke tests', () => {
11565
beforeEach(() => {
11666
jest.clearAllMocks();
11767
});
118-
const testModalView = ({ step, keys }) => {
119-
it.each(keys)('renders %s progress state', async (progressKey) => {
120-
const state = loadState({ view: stepRoutes[step], progressKey });
121-
mockPageData(pageDataUrl(step), { body: {}, response: state });
122-
el = await loadApp(progressKey, step);
123-
await el.findAllByText('Create response');
124-
});
125-
};
126-
describe('xblock view', () => {
127-
const xblockProgressKeys = Object.values(progressKeys);
128-
it.each(xblockProgressKeys)('renders %s progress state', async (progressKey) => {
129-
const state = loadState({
130-
view: stepNames.xblock,
131-
progressKey,
68+
describe.each(Object.keys(stepOrders))('For step order %s', (stepOrder) => {
69+
const oraConfig = { ...fakeData.oraConfig.assessmentText };
70+
oraConfig.assessment_steps.order = stepOrders[stepOrder];
71+
when(post).calledWith(`${baseUrl}${paths.oraConfig}`, {})
72+
.mockResolvedValue({ data: oraConfig });
73+
const testModalView = ({ step }) => {
74+
const keys = getProgressKeys(stepOrder, step);
75+
if (keys.length === 0) { return; }
76+
it.each(keys)('renders %s progress state', async (progressKey) => {
77+
const state = loadState({ view: stepRoutes[step], progressKey });
78+
mockPageData(pageDataUrl(step), { body: {}, response: state });
79+
el = await loadApp(progressKey, step);
80+
await el.findAllByText('Create response');
81+
});
82+
};
83+
describe('xblock view', () => {
84+
const keys = getProgressKeys(stepOrder, stepNames.xblock);
85+
it.each(keys)('renders %s progress state', async (progressKey) => {
86+
const state = loadState({
87+
view: stepNames.xblock,
88+
progressKey,
89+
});
90+
mockPageData(pageDataUrl(), { body: {}, response: state });
91+
el = await loadApp(progressKey, stepNames.xblock);
92+
const { title } = fakeData.oraConfig.assessmentText;
93+
await el.findByText(title);
13294
});
133-
mockPageData(pageDataUrl(), { body: {}, response: state });
134-
el = await loadApp(progressKey, stepNames.xblock);
135-
await el.findByText('Open Response Assessment');
13695
});
137-
});
138-
describe('submission view', () => {
139-
const submissionProgressKeys = [
140-
progressKeys.submissionUnsaved,
141-
progressKeys.submissionSaved,
142-
progressKeys.studentTraining,
143-
progressKeys.studentTrainingPartial,
144-
progressKeys.selfAssessment,
145-
progressKeys.peerAssessment,
146-
progressKeys.peerAssessmentPartial,
147-
progressKeys.peerAssessmentWaiting,
148-
progressKeys.staffAfterPeer,
149-
progressKeys.graded,
150-
];
151-
testModalView({ step: stepNames.submission, keys: submissionProgressKeys });
152-
});
153-
describe('studentTraining view', () => {
154-
const trainingProgressKeys = [
155-
progressKeys.studentTraining,
156-
progressKeys.studentTrainingPartial,
157-
];
158-
testModalView({ step: stepNames.studentTraining, keys: trainingProgressKeys });
159-
});
160-
describe('self assessment view', () => {
161-
const selfProgressKeys = [progressKeys.selfAssessment];
162-
testModalView({ step: stepNames.self, keys: selfProgressKeys });
163-
});
164-
describe('peer assessment view', () => {
165-
const peerProgressKeys = [
166-
progressKeys.peerAssessment,
167-
progressKeys.peerAssessmentPartial,
168-
progressKeys.peerAssessmentWaiting,
169-
progressKeys.staffAfterPeer,
170-
progressKeys.graded,
171-
];
172-
testModalView({ step: stepNames.peer, keys: peerProgressKeys });
173-
});
174-
describe('graded view', () => {
175-
const gradedProgressKeys = [progressKeys.graded];
176-
testModalView({ step: stepNames.done, keys: gradedProgressKeys });
96+
describe('submission view', () => {
97+
testModalView({ step: stepNames.submission });
98+
});
99+
describe('studentTraining view', () => {
100+
testModalView({ step: stepNames.studentTraining });
101+
});
102+
describe('self assessment view', () => {
103+
testModalView({ step: stepNames.self });
104+
});
105+
describe('peer assessment view', () => {
106+
testModalView({ step: stepNames.peer });
107+
});
108+
describe('graded view', () => {
109+
testModalView({ step: stepNames.done });
110+
});
177111
});
178112
});

src/test/utils.jsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
// eslint-disable-next-line import/no-extraneous-dependencies
3+
import { render } from '@testing-library/react';
4+
import { when } from 'jest-when';
5+
import { MemoryRouter } from 'react-router-dom';
6+
import { Provider } from 'react-redux';
7+
8+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
9+
10+
import { IntlProvider } from '@edx/frontend-platform/i18n';
11+
12+
import App from 'App';
13+
14+
import { stepRoutes } from 'constants/index';
15+
16+
import { createStore } from 'data/store';
17+
import {
18+
courseId,
19+
xblockId,
20+
baseUrl,
21+
} from './constants';
22+
23+
export const mockQuerySelector = () => {
24+
jest.spyOn(document, 'querySelector').mockImplementation((selector) => (
25+
selector === 'html' ? { scrollTo: jest.fn() } : selector
26+
));
27+
};
28+
29+
export const post = jest.fn();
30+
export const renderApp = (route) => {
31+
const store = createStore(false);
32+
const queryClient = new QueryClient({ queries: { retry: false } });
33+
const location = `/${route}/${courseId}/${xblockId}/`;
34+
return (
35+
<IntlProvider locale="en">
36+
<Provider store={store}>
37+
<QueryClientProvider client={queryClient}>
38+
<MemoryRouter initialEntries={[location]}>
39+
<App />
40+
</MemoryRouter>
41+
</QueryClientProvider>
42+
</Provider>
43+
</IntlProvider>
44+
);
45+
};
46+
47+
const basePageUrl = `${baseUrl}/get_learner_data/`;
48+
export const pageDataUrl = (view = undefined) => (view ? `${basePageUrl}${view}` : basePageUrl);
49+
export const loadApp = async (progressKey, step) => render(renderApp(stepRoutes[step]));
50+
51+
export const mockPageData = (url, { response }) => when(post)
52+
.calledWith(url, expect.anything()).mockResolvedValue({ data: response })
53+
.calledWith(url).mockResolvedValue({ data: response }); // eslint-disable-line newline-per-chained-call

src/views/SubmissionView/TextResponseEditor/TextEditor.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
44
import { TextArea } from '@edx/paragon';
55
import { useIntl } from '@edx/frontend-platform/i18n';
66
import messages from './messages';
7+
import './TextEditor.scss';
78

89
const TextEditor = ({
910
// id,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.form-control.textarea-response {
2+
height: 500px;
3+
}

0 commit comments

Comments
 (0)