Skip to content

Commit

Permalink
Split out validation logic for iframe (#1705)
Browse files Browse the repository at this point in the history
## Summary:
For the server-side scoring project, we are moving validation logic out of each widget component. This moves the validation logic for the iframe widget and adds tests for the validator function.

Issue: LEMS-2480

## Test plan:
- Confirm all tests pass
- Confirm iframe stories still work in a ZND

Author: Myranae

Reviewers: Myranae, handeyeco

Required Reviewers:

Approved By: handeyeco

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

Pull Request URL: #1705
  • Loading branch information
Myranae authored Oct 1, 2024
1 parent d3767f7 commit e432666
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/nine-roses-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus": patch
---

Move validation logic out of the iframe widget and add tests
49 changes: 49 additions & 0 deletions packages/perseus/src/widgets/iframe/iframe-validator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {iframeValidator} from "./iframe-validator";

import type {PerseusIFrameUserInput} from "../../validation.types";

describe("iframeValidator", () => {
it("is correct when the state from the iframe shows the status is correct", () => {
// Arrange
const state: PerseusIFrameUserInput = {
status: "correct",
message: "Good job!",
};

// Act
const result = iframeValidator(state);

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

it("is incorrect when the state from the iframe shows the status is incorrect", () => {
// Arrange
const state: PerseusIFrameUserInput = {
status: "incorrect",
message: "Try again!",
};

// Act
const result = iframeValidator(state);

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

// Note: It looks like the iframe only says if the answer is correct or
// incorrect, but status is set to "incomplete" by default.
it("should return invalid score before user interactions", () => {
// Arrange
const state: PerseusIFrameUserInput = {
status: "incomplete",
message: null,
};

// Act
const result = iframeValidator(state);

// Assert
expect(result).toHaveInvalidInput("Keep going, you're not there yet!");
});
});
27 changes: 27 additions & 0 deletions packages/perseus/src/widgets/iframe/iframe-validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type {PerseusScore} from "../../types";
import type {PerseusIFrameUserInput} from "../../validation.types";

export function iframeValidator(state: PerseusIFrameUserInput): PerseusScore {
// The iframe can tell us whether it's correct or incorrect,
// and pass an optional message
if (state.status === "correct") {
return {
type: "points",
earned: 1,
total: 1,
message: state.message || null,
};
}
if (state.status === "incorrect") {
return {
type: "points",
earned: 0,
total: 1,
message: state.message || null,
};
}
return {
type: "invalid",
message: "Keep going, you're not there yet!",
};
}
45 changes: 10 additions & 35 deletions packages/perseus/src/widgets/iframe/iframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import {getDependencies} from "../../dependencies";
import * as Changeable from "../../mixins/changeable";
import Util from "../../util";

import {iframeValidator} from "./iframe-validator";

import type {PerseusIFrameWidgetOptions} from "../../perseus-types";
import type {WidgetExports, WidgetProps} from "../../types";
import type {PerseusScore, WidgetExports, WidgetProps} from "../../types";
import type {
PerseusIFrameRubric,
PerseusIFrameUserInput,
Expand Down Expand Up @@ -51,6 +53,10 @@ class Iframe extends React.Component<Props> {
allowTopNavigation: false,
};

static validate(state: PerseusIFrameUserInput): PerseusScore {
return iframeValidator(state);
}

componentDidMount() {
$(window).on("message", this.handleMessageEvent);
}
Expand Down Expand Up @@ -90,10 +96,9 @@ class Iframe extends React.Component<Props> {
return Changeable.change.apply(this, args);
};

simpleValidate: (arg1: any) => any = (rubric) => {
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Iframe'.
return Iframe.validate(this.getUserInput(), rubric);
};
simpleValidate(): PerseusScore {
return iframeValidator(this.getUserInput());
}

render(): React.ReactNode {
const style = {
Expand Down Expand Up @@ -163,36 +168,6 @@ class Iframe extends React.Component<Props> {
}
}

/**
* This is the widget's grading function
*/
_.extend(Iframe, {
validate: function (state, rubric) {
// The iframe can tell us whether it's correct or incorrect,
// and pass an optional message
if (state.status === "correct") {
return {
type: "points",
earned: 1,
total: 1,
message: state.message || null,
};
}
if (state.status === "incorrect") {
return {
type: "points",
earned: 0,
total: 1,
message: state.message || null,
};
}
return {
type: "invalid",
message: "Keep going, you're not there yet!",
};
},
});

export default {
name: "iframe",
displayName: "Iframe (deprecated)",
Expand Down

0 comments on commit e432666

Please sign in to comment.