Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a11fa1a

Browse files
committedJan 18, 2025
Re-work where validation is called in scoring
1 parent 3f7dade commit a11fa1a

File tree

2 files changed

+14
-32
lines changed

2 files changed

+14
-32
lines changed
 

‎packages/perseus/src/renderer-util.test.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -749,14 +749,16 @@ describe("renderer utils", () => {
749749
// Act
750750
const validatorSpy = jest
751751
.spyOn(DropdownWidgetExport, "validator")
752-
// Empty
752+
// 1st call - Empty
753753
.mockReturnValueOnce({
754754
type: "invalid",
755755
message: null,
756756
})
757-
// Not empty
757+
// 2nd call - Not empty
758758
.mockReturnValueOnce(null);
759-
const scoringSpy = jest.spyOn(DropdownWidgetExport, "scorer");
759+
const scoringSpy = jest
760+
.spyOn(DropdownWidgetExport, "scorer")
761+
.mockReturnValueOnce({type: "points", total: 1, earned: 1});
760762

761763
// Act
762764
const score = scorePerseusItem(
@@ -777,7 +779,8 @@ describe("renderer utils", () => {
777779

778780
// Assert
779781
expect(validatorSpy).toHaveBeenCalledTimes(2);
780-
expect(scoringSpy).not.toHaveBeenCalled();
782+
// Scoring is only called if validation passes
783+
expect(scoringSpy).toHaveBeenCalledTimes(1);
781784
expect(score).toEqual({type: "invalid", message: null});
782785
});
783786

‎packages/perseus/src/renderer-util.ts

+7-28
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ import type {
2020
PerseusWidgetsMap,
2121
} from "@khanacademy/perseus-core";
2222

23-
function mapWidgetIdsToEmptyScore(widgetIds: ReadonlyArray<string>): {
24-
[widgetId: string]: PerseusScore;
25-
} {
26-
const emptyResult: {[widgetId: string]: PerseusScore} = {};
27-
widgetIds.forEach((widgetId) => {
28-
emptyResult[widgetId] = {type: "invalid", message: null};
29-
});
30-
return emptyResult;
31-
}
32-
3323
export function getUpgradedWidgetOptions(
3424
oldWidgetOptions: PerseusWidgetsMap,
3525
): PerseusWidgetsMap {
@@ -122,18 +112,6 @@ export function scoreWidgetsFunctional(
122112
): {[widgetId: string]: PerseusScore} {
123113
const upgradedWidgets = getUpgradedWidgetOptions(widgets);
124114

125-
// Do empty check first
126-
const emptyWidgets = emptyWidgetsFunctional(
127-
widgets,
128-
widgetIds,
129-
userInputMap,
130-
strings,
131-
locale,
132-
);
133-
if (emptyWidgets.length > 0) {
134-
return mapWidgetIdsToEmptyScore(emptyWidgets);
135-
}
136-
137115
const gradedWidgetIds = widgetIds.filter((id) => {
138116
const props = upgradedWidgets[id];
139117
const widgetIsGraded: boolean = props?.graded == null || props.graded;
@@ -150,13 +128,14 @@ export function scoreWidgetsFunctional(
150128
}
151129

152130
const userInput = userInputMap[id];
131+
const validator = getWidgetValidator(widget.type);
153132
const scorer = getWidgetScorer(widget.type);
154-
const score = scorer?.(
155-
userInput as UserInput,
156-
widget.options,
157-
strings,
158-
locale,
159-
);
133+
134+
// We do validation (empty checks) first and then scoring. If
135+
// validation fails, it's result is itself a PerseusScore.
136+
const score =
137+
validator?.(userInput, widget.options, strings, locale) ??
138+
scorer?.(userInput, widget.options, strings, locale);
160139
if (score != null) {
161140
widgetScores[id] = score;
162141
}

0 commit comments

Comments
 (0)
Please sign in to comment.