Files
frontend-app-gradebook/src/data/selectors/grades.test.js
Ben Warzeski 7acefe0468 refactor: gradebook table tests (#192)
* clean up, test, and docstring Gradebook table

* v1.4.36
2021-06-09 14:34:15 -04:00

357 lines
12 KiB
JavaScript

import { EMAIL_HEADING, TOTAL_COURSE_GRADE_HEADING, USERNAME_HEADING } from '../constants/grades';
import { formatDateForDisplay } from '../actions/utils';
import * as selectors from './grades';
import exportedSelectors from './grades';
const { minGrade, maxGrade } = selectors;
const genericResultsRows = [
{
attempted: true,
category: 'Homework',
label: 'HW 01',
module_id: 'block-v1:edX+Term+type@sequential+block@1',
percent: 1,
score_earned: 1,
score_possible: 1,
subsection_name: 'Week 1',
},
{
attempted: true,
category: 'Homework',
label: 'HW 02',
module_id: 'block-v1:edX+Term+type@sequential+block@2',
percent: 1,
score_earned: 1,
score_possible: 1,
subsection_name: 'Week 2',
},
{
attempted: false,
category: 'Lab',
label: 'Lab 01',
module_id: 'block-v1:edX+Term+type@sequential+block@3',
percent: 0,
score_earned: 0,
score_possible: 0,
subsection_name: 'Week 3',
},
];
describe('grades selectors', () => {
// Transformers
describe('getRowsProcessed', () => {
const data = {
processed_rows: 20,
saved_rows: 10,
total_rows: 50,
};
expect(selectors.getRowsProcessed(data)).toEqual({
total: data.total_rows,
successfullyProcessed: data.saved_rows,
failed: data.processed_rows - data.saved_rows,
skipped: data.total_rows - data.processed_rows,
});
});
describe('grade formatters', () => {
const selectedAssignment = { assignmentId: 'block-v1:edX+type@sequential+block@abcde' };
describe('formatMinAssignmentGrade', () => {
const modifiedGrade = '1';
const selector = selectors.formatMinAssignmentGrade;
it('passes grade through when not min (0) and assignment is supplied', () => {
expect(selector(modifiedGrade, selectedAssignment)).toEqual(modifiedGrade);
});
it('returns null for min grade', () => {
expect(selector(minGrade, selectedAssignment)).toEqual(null);
});
it('returns null when assignment is not supplied', () => {
expect(selector(modifiedGrade, {})).toEqual(null);
});
});
describe('formatMaxAssignmentGrade', () => {
const modifiedGrade = '99';
const selector = selectors.formatMaxAssignmentGrade;
it('passes grade through when not max (100) and assignment is supplied', () => {
expect(selector(modifiedGrade, selectedAssignment)).toEqual(modifiedGrade);
});
it('returns null for max grade', () => {
expect(selector(maxGrade, selectedAssignment)).toEqual(null);
});
it('returns null when assignment is not supplied', () => {
expect(selector(modifiedGrade, {})).toEqual(null);
});
});
describe('formatMinCourseGrade', () => {
const modifiedGrade = '37';
const selector = selectors.formatMinCourseGrade;
it('passes grades through when not min (0) and assignment is supplied', () => {
expect(selector(modifiedGrade, selectedAssignment)).toEqual(modifiedGrade);
});
it('returns null for min grade', () => {
expect(selector(minGrade, selectedAssignment)).toEqual(null);
});
});
describe('formatMaxCourseGrade', () => {
const modifiedGrade = '42';
const selector = selectors.formatMaxCourseGrade;
it('passes grades through when not max and assignment is supplied', () => {
expect(selector(modifiedGrade, selectedAssignment)).toEqual(modifiedGrade);
});
it('returns null for max grade', () => {
expect(selector(maxGrade, selectedAssignment)).toEqual(null);
});
});
});
describe('headingMapper', () => {
const expectedHeaders = (subsectionLabels) => ([
USERNAME_HEADING,
EMAIL_HEADING,
...subsectionLabels,
TOTAL_COURSE_GRADE_HEADING,
]);
const rows = genericResultsRows;
const selector = selectors.headingMapper;
it('creates headers for all assignments when no filtering is applied', () => {
expect(selector('All')(genericResultsRows)).toEqual(
expectedHeaders([rows[0].label, rows[1].label, rows[2].label]),
);
});
it('creates headers for only matching assignment types when type filter is applied', () => {
expect(
selector('Homework')(genericResultsRows),
).toEqual(
expectedHeaders([rows[0].label, rows[1].label]),
);
});
it('creates headers for only matching assignment when label filter is applied', () => {
expect(selector('Homework', rows[1].label)(rows)).toEqual(
expectedHeaders([rows[1].label]),
);
});
it('returns an empty array when no entries are passed', () => {
expect(selector('all')(undefined)).toEqual([]);
});
});
describe('transformHistoryEntry', () => {
let getRowsProcessed;
let output;
const rowsProcessed = ['some', 'fake', 'rows'];
const rawEntry = {
modified: 'Jan 10 2021',
original_filename: 'fileName',
data: { some: 'data' },
also: 'some',
other: 'fields',
};
beforeEach(() => {
getRowsProcessed = selectors.getRowsProcessed;
selectors.getRowsProcessed = jest.fn(data => ({ data, rowsProcessed }));
output = selectors.transformHistoryEntry(rawEntry);
});
afterEach(() => {
selectors.getRowsProcessed = getRowsProcessed;
});
it('transforms modified into timeUploaded', () => {
expect(output.timeUploaded).toEqual(formatDateForDisplay(new Date(rawEntry.modified)));
});
it('forwards filename', () => {
expect(output.originalFilename).toEqual(rawEntry.original_filename);
});
it('summarizes processed rows', () => {
expect(output.summaryOfRowsProcessed).toEqual(selectors.getRowsProcessed(rawEntry.data));
});
});
describe('roundGrade', () => {
it('rounds values to 2 places', () => {
expect(selectors.roundGrade(23.124)).toEqual(23.12);
});
it('defaults to 0 if no value is passed', () => {
expect(selectors.roundGrade()).toEqual(0);
});
});
describe('subsectionGrade', () => {
const { roundGrade } = selectors;
beforeEach(() => {
selectors.roundGrade = jest.fn(grade => ({ roundGrade: grade }));
});
afterEach(() => {
selectors.roundGrade = roundGrade;
});
describe('absolute', () => {
const subsection = { score_earned: 2, score_possible: 5 };
describe('attempted', () => {
it('returns rounded {earned}/{possible}', () => {
const earned = selectors.roundGrade(subsection.score_earned);
const possible = selectors.roundGrade(subsection.score_possible);
expect(
selectors.subsectionGrade.absolute({ ...subsection, attempted: true }),
).toEqual(`${earned}/${possible}`);
});
});
describe('not attempted', () => {
it('returns rounded {earned}', () => {
const earned = selectors.roundGrade(subsection.score_earned);
expect(selectors.subsectionGrade.absolute(subsection)).toEqual(`${earned}`);
});
});
});
describe('percent', () => {
it('returns rounded grade.percent * 100', () => {
const percent = 42;
const expected = selectors.roundGrade(percent * 100);
expect(selectors.subsectionGrade.percent({ percent })).toEqual(expected);
});
});
});
// Selectors
describe('allGrades', () => {
it('returns the grades results from redux state', () => {
const results = ['some', 'fake', 'results'];
expect(selectors.allGrades({ grades: { results } })).toEqual(results);
});
});
describe('bulkImportError', () => {
it('returns an empty string when bulkManagement not run', () => {
expect(
selectors.bulkImportError({ grades: { bulkManagement: null } }),
).toEqual('');
});
it('returns an empty string when bulkManagement runs without error', () => {
expect(
selectors.bulkImportError({ grades: { bulkManagement: { uploadSuccess: true } } }),
).toEqual('');
});
it('returns error string when bulkManagement encounters an error', () => {
const errorMessages = ['error1', 'also error2'];
expect(
selectors.bulkImportError({ grades: { bulkManagement: { errorMessages } } }),
).toEqual(
`Errors while processing: ${errorMessages[0]}, ${errorMessages[1]}`,
);
});
});
describe('bulkManagementHistory', () => {
const selector = selectors.bulkManagementHistory;
it('returns history entries from grades.bulkManagement in redux store', () => {
const history = ['a', 'few', 'history', 'entries'];
expect(
selector({ grades: { bulkManagement: { history } } }),
).toEqual(history);
});
it('returns an empty list if not set', () => {
expect(
selector({ grades: { bulkManagement: {} } }),
).toEqual([]);
});
});
describe('bulkManagementHistoryEntries', () => {
let bulkManagementHistory;
let transformHistoryEntry;
const listFn = (state) => state.entries;
const mapFn = (entry) => ([entry]);
const entries = ['some', 'entries', 'for', 'testing'];
beforeEach(() => {
bulkManagementHistory = selectors.bulkManagementHistory;
transformHistoryEntry = selectors.transformHistoryEntry;
selectors.bulkManagementHistory = jest.fn(listFn);
selectors.transformHistoryEntry = jest.fn(mapFn);
});
afterEach(() => {
selectors.bulkManagementHistory = bulkManagementHistory;
selectors.transformHistoryEntry = transformHistoryEntry;
});
it('returns history entries mapped to transformer', () => {
expect(
selectors.bulkManagementHistoryEntries({ entries }),
).toEqual(entries.map(mapFn));
});
});
describe('getExampleSectionBreakdown', () => {
const selector = selectors.getExampleSectionBreakdown;
it('returns an empty array when results are unavailable', () => {
expect(selector({ grades: { results: [] } })).toEqual([]);
});
it('returns an empty array when breakdowns are unavailable', () => {
expect(selector({ grades: { results: [{ foo: 'bar' }] } })).toEqual([]);
});
it('gets section breakdown when available', () => {
const sectionBreakdown = { fake: 'section', breakdown: 'data' };
expect(
selector({ grades: { results: [{ section_breakdown: sectionBreakdown }] } }),
).toEqual(sectionBreakdown);
});
});
describe('gradeOverrides', () => {
it('returns grades.gradeOverrideHistoryResults from redux state', () => {
const testVal = 'Temp Test VALUE';
expect(
selectors.gradeOverrides({ grades: { gradeOverrideHistoryResults: testVal } }),
).toEqual(testVal);
});
});
describe('hasOverrideErrors', () => {
const selector = selectors.hasOverrideErrors;
it('returns true iff grades.gradeOverrideHistoryError is truthy', () => {
expect(
selector({ grades: { gradeOverrideHistoryError: ['some', 'results'] } }),
).toEqual(true);
expect(selector({ grades: { gradeOverrideHistoryError: null } })).toEqual(false);
expect(selector({ grades: {} })).toEqual(false);
});
});
describe('uploadSuccess', () => {
const selector = selectors.uploadSuccess;
it('shows upload success when bulkManagement data returned/completed successfully', () => {
expect(selector({ grades: { bulkManagement: { uploadSuccess: true } } })).toEqual(true);
});
it('returns false when bulk management data not returned', () => {
expect(selector({ grades: {} })).toEqual(false);
});
});
describe('simpleSelectors', () => {
const testVal = 'some TEST value';
const testSimpleSelector = (key) => {
test(key, () => {
expect(
exportedSelectors[key]({ grades: { [key]: testVal } }),
).toEqual(testVal);
});
};
testSimpleSelector('courseId');
testSimpleSelector('filteredUsersCount');
testSimpleSelector('totalUsersCount');
testSimpleSelector('gradeFormat');
testSimpleSelector('nextPage');
testSimpleSelector('prevPage');
testSimpleSelector('showSpinner');
testSimpleSelector('gradeOverrideCurrentEarnedGradedOverride');
testSimpleSelector('gradeOverrideHistoryError');
testSimpleSelector('gradeOriginalEarnedGraded');
testSimpleSelector('gradeOriginalPossibleGraded');
testSimpleSelector('nextPage');
testSimpleSelector('prevPage');
testSimpleSelector('showSuccess');
});
});