Skip to content

Commit

Permalink
Split out InteractiveGraph validator (#1700)
Browse files Browse the repository at this point in the history
## Summary:
Split out the validation logic from the InteractiveGraph widget

Issue: LEMS-2356

## Test plan:
Nothing should change, mostly just moving code around

Author: handeyeco

Reviewers: benchristel, handeyeco

Required Reviewers:

Approved By: benchristel

Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald

Pull Request URL: #1700
  • Loading branch information
handeyeco authored Sep 30, 2024
1 parent 764b9ec commit 493715e
Show file tree
Hide file tree
Showing 5 changed files with 675 additions and 640 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-otters-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus": patch
---

Split out InteractiveGraph validator
301 changes: 1 addition & 300 deletions packages/perseus/src/widgets/interactive-graph.test.tsx
Original file line number Diff line number Diff line change
@@ -1,310 +1,11 @@
import invariant from "tiny-invariant";

import {clone} from "../../../../testing/object-utils";

import InteractiveGraph, {shouldUseMafs} from "./interactive-graph";
import {shouldUseMafs} from "./interactive-graph";

import type {
PerseusGraphTypeLinear,
PerseusGraphTypePoint,
PerseusGraphTypePolygon,
PerseusGraphType,
PerseusGraphTypeNone,
} from "../perseus-types";
import type {PerseusInteractiveGraphRubric} from "../validation.types";

function createRubric(graph: PerseusGraphType): PerseusInteractiveGraphRubric {
return {graph, correct: graph};
}

describe("InteractiveGraph.validate on a segment question", () => {
it("marks the answer invalid if guess.coords is missing", () => {
const guess: PerseusGraphType = {type: "segment"};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "segment",
coords: [
[
[0, 0],
[1, 1],
],
],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveInvalidInput();
});

it("does not award points if guess.coords is wrong", () => {
const guess: PerseusGraphType = {
type: "segment",
coords: [
[
[99, 0],
[1, 1],
],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "segment",
coords: [
[
[0, 0],
[1, 1],
],
],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveBeenAnsweredIncorrectly();
});

it("awards points if guess.coords is right", () => {
const guess: PerseusGraphType = {
type: "segment",
coords: [
[
[0, 0],
[1, 1],
],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "segment",
coords: [
[
[0, 0],
[1, 1],
],
],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveBeenAnsweredCorrectly();
});

it("allows points of a segment to be specified in reverse order", () => {
const guess: PerseusGraphType = {
type: "segment",
coords: [
[
[1, 1],
[0, 0],
],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "segment",
coords: [
[
[0, 0],
[1, 1],
],
],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveBeenAnsweredCorrectly();
});

it("does not modify the `guess` data", () => {
const guess: PerseusGraphType = {
type: "segment",
coords: [
[
[1, 1],
[0, 0],
],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "segment",
coords: [
[
[0, 0],
[1, 1],
],
],
});

InteractiveGraph.widget.validate(guess, rubric);

expect(guess.coords).toEqual([
[
[1, 1],
[0, 0],
],
]);
});

it("does not modify the `rubric` data", () => {
const guess: PerseusGraphType = {
type: "segment",
coords: [
[
[1, 1],
[0, 0],
],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "segment",
coords: [
[
[1, 1],
[0, 0],
],
],
});

InteractiveGraph.widget.validate(guess, rubric);

// Narrow the type of `rubric.correct` to segment graph; otherwise TS
// thinks it might not have a `coords` property.
invariant(rubric.correct.type === "segment");
expect(rubric.correct.coords).toEqual([
[
[1, 1],
[0, 0],
],
]);
});
});

describe("InteractiveGraph.validate on a point question", () => {
it("marks the answer invalid if guess.coords is missing", () => {
const guess: PerseusGraphType = {type: "point"};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
coords: [[0, 0]],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveInvalidInput();
});

it("throws an exception if correct.coords is missing", () => {
// Characterization test: this might not be desirable behavior, but
// it's the current behavior as of 2024-09-25.
const guess: PerseusGraphType = {
type: "point",
coords: [[0, 0]],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
});

expect(() =>
InteractiveGraph.widget.validate(guess, rubric),
).toThrowError();
});

it("does not award points if guess.coords is wrong", () => {
const guess: PerseusGraphType = {
type: "point",
coords: [[9, 9]],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
coords: [[0, 0]],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveBeenAnsweredIncorrectly();
});

it("awards points if guess.coords is right", () => {
const guess: PerseusGraphType = {
type: "point",
coords: [[7, 8]],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
coords: [[7, 8]],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toEqual({
type: "points",
earned: 1,
total: 1,
message: null,
});
});

it("allows points to be specified in any order", () => {
const guess: PerseusGraphType = {
type: "point",
coords: [
[7, 8],
[5, 6],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
coords: [
[5, 6],
[7, 8],
],
});

const result = InteractiveGraph.widget.validate(guess, rubric);

expect(result).toHaveBeenAnsweredCorrectly();
});

it("does not modify the `guess` data", () => {
const guess: PerseusGraphType = {
type: "point",
coords: [
[7, 8],
[5, 6],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
coords: [
[5, 6],
[7, 8],
],
});

const guessClone = clone(guess);

InteractiveGraph.widget.validate(guess, rubric);

expect(guess).toEqual(guessClone);
});

it("does not modify the `rubric` data", () => {
const guess: PerseusGraphType = {
type: "point",
coords: [
[7, 8],
[5, 6],
],
};
const rubric: PerseusInteractiveGraphRubric = createRubric({
type: "point",
coords: [
[5, 6],
[7, 8],
],
});

const rubricClone = clone(rubric);

InteractiveGraph.widget.validate(guess, rubric);

expect(rubric).toEqual(rubricClone);
});
});

describe("shouldUseMafs", () => {
it("is false given no mafs flags", () => {
Expand Down
Loading

0 comments on commit 493715e

Please sign in to comment.