Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { describe, expect, it } from "vitest";

import { renderWithMantine, screen } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";

import { ActiveCodeAnswerView } from "./ActiveCodeAnswerView";
import { AnswerRendererProps } from "../../types";
import { ActiveCodeAnswerView } from "../ActiveCodeAnswerView";

const baseProps = (answer: string): AnswerRendererProps => ({
answer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import { GraderAnswerHistoryItem } from "@store/grader/grader.logic.api";
import { describe, expect, it, vi } from "vitest";

import { renderWithMantine, screen } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";
import { AnswerRendererProps } from "../../types";
import { AnswerRenderer } from "../AnswerRenderer";

import { AnswerRenderer } from "./AnswerRenderer";

vi.mock("../McqAnswerView", () => ({ McqAnswerView: () => <div>VIEW:MCQ</div> }));
vi.mock("../AssociationAnswerView", () => ({
vi.mock("../../McqAnswerView", () => ({ McqAnswerView: () => <div>VIEW:MCQ</div> }));
vi.mock("../../AssociationAnswerView", () => ({
AssociationAnswerView: ({ kind }: { kind: string }) => <div>VIEW:{kind.toUpperCase()}</div>
}));
vi.mock("../FitbAnswerView", () => ({ FitbAnswerView: () => <div>VIEW:FITB</div> }));
vi.mock("../ShortAnswerView", () => ({ ShortAnswerView: () => <div>VIEW:SHORT</div> }));
vi.mock("../ParsonsAnswerView", () => ({ ParsonsAnswerView: () => <div>VIEW:PARSONS</div> }));
vi.mock("../ActiveCodeAnswerView", () => ({
vi.mock("../../FitbAnswerView", () => ({ FitbAnswerView: () => <div>VIEW:FITB</div> }));
vi.mock("../../ShortAnswerView", () => ({ ShortAnswerView: () => <div>VIEW:SHORT</div> }));
vi.mock("../../ParsonsAnswerView", () => ({ ParsonsAnswerView: () => <div>VIEW:PARSONS</div> }));
vi.mock("../../ActiveCodeAnswerView", () => ({
ActiveCodeAnswerView: () => <div>VIEW:ACTIVECODE</div>
}));
vi.mock("../DefaultAnswerView", () => ({ DefaultAnswerView: () => <div>VIEW:DEFAULT</div> }));
vi.mock("../IframeAnswerView", () => ({ IframeAnswerView: () => <div>VIEW:IFRAME</div> }));
vi.mock("../RunestoneGraderPreview", () => ({
vi.mock("../../DefaultAnswerView", () => ({ DefaultAnswerView: () => <div>VIEW:DEFAULT</div> }));
vi.mock("../../IframeAnswerView", () => ({ IframeAnswerView: () => <div>VIEW:IFRAME</div> }));
vi.mock("../../RunestoneGraderPreview", () => ({
RunestoneGraderPreview: ({ attemptId }: { attemptId?: string | number }) => (
<div>PREVIEW:{String(attemptId)}</div>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { describe, expect, it } from "vitest";

import { renderWithMantine, screen } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";

import { AssociationAnswerView } from "./AssociationAnswerView";
import { formatAssociationAnswer, parseAssociations } from "./utils";
import { AnswerRendererProps } from "../../types";
import { AssociationAnswerView } from "../AssociationAnswerView";
import { formatAssociationAnswer, parseAssociations } from "../utils";

const baseProps = (overrides: Partial<AnswerRendererProps> = {}): AnswerRendererProps => ({
answer: "",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { formatAssociationAnswer } from "../formatAssociationAnswer";

describe("formatAssociationAnswer", () => {
it("falls back to ids when the question has no matching label", () => {
expect(
formatAssociationAnswer(
"matching",
'{"connections":[{"from":"known","to":"unknown"}]}',
'<span id="known" data-subcomponent="draggable">Known label</span>'
)
).toBe("Known label → unknown");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { parseAssociationLabels } from "../parseAssociationLabels";

describe("parseAssociationLabels", () => {
it("extracts plain labels from JSON and legacy draggable markup", () => {
const htmlsrc = `<div>
<script type="application/json">{
"left":[{"id":"left-1","label":"<strong>Loop</strong>"}],
"right":[{"id":"right-1","label":"Repeats statements"}]
}</script>
<span id="legacy-item" data-subcomponent="draggable"> Legacy item </span>
</div>`;

expect(parseAssociationLabels(htmlsrc)).toEqual({
"left-1": "Loop",
"right-1": "Repeats statements",
"legacy-item": "Legacy item"
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { parseAssociations } from "../parseAssociations";

describe("parseAssociations", () => {
it("keeps only valid matching connections", () => {
const answer = JSON.stringify({
connections: [{ from: "left-1", to: "right-1" }, { from: "left-2" }, null, "invalid"]
});

expect(parseAssociations("matching", answer)).toEqual([{ from: "left-1", to: "right-1" }]);
});

it("flattens drag-and-drop zones and ignores invalid values", () => {
expect(
parseAssociations("dragndrop", '{"zone-1":["item-1",5,"item-2"],"zone-2":null}')
).toEqual([
{ from: "item-1", to: "zone-1" },
{ from: "item-2", to: "zone-1" }
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { parseJsonObject } from "../parseJsonObject";

describe("parseJsonObject", () => {
it("accepts JSON objects", () => {
expect(parseJsonObject('{"value":1}')).toEqual({ value: 1 });
});

it.each(["[]", "null", "not-json"])("rejects invalid or non-object JSON: %s", (value) => {
expect(parseJsonObject(value)).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { textOnly } from "../textOnly";

describe("textOnly", () => {
it("removes markup and trims text", () => {
expect(textOnly(" <strong>Loop</strong> ")).toBe("Loop");
});

it("returns an empty string for non-string values", () => {
expect(textOnly(42)).toBe("");
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { describe, expect, it } from "vitest";

import { renderWithMantine, screen } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";

import { DefaultAnswerView } from "./DefaultAnswerView";
import { AnswerRendererProps } from "../../types";
import { DefaultAnswerView } from "../DefaultAnswerView";

const baseProps = (answer: string): AnswerRendererProps => ({
answer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { describe, expect, it } from "vitest";

import { renderWithMantine, screen } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";

import { FitbAnswerView } from "./FitbAnswerView";
import { AnswerRendererProps } from "../../types";
import { FitbAnswerView } from "../FitbAnswerView";

const baseProps = (answer: string): AnswerRendererProps => ({
answer,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { parseBlankValues } from "../parseBlankValues";

describe("parseBlankValues", () => {
it.each([
['["first","second"]', ["first", "second"]],
["5", ["5"]],
["true", ["true"]],
['{"value":1}', ["[object Object]"]],
["first,second", ["first", "second"]],
["", []]
])("parses %s", (answer, expected) => {
expect(parseBlankValues(answer)).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { GraderAnswerHistoryItem } from "@store/grader/grader.logic.api";
import { describe, expect, it } from "vitest";

import { renderWithMantine, screen, waitFor } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";

import { IframeAnswerView } from "./IframeAnswerView";
import { extractFrameSpec } from "./utils";
import { AnswerRendererProps } from "../../types";
import { IframeAnswerView } from "../IframeAnswerView";
import { extractFrameSpec } from "../utils";

const DOENET_HTMLSRC = `
<div class="ptx-runestone-container">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { extractFrameSpec } from "../extractFrameSpec";

describe("extractFrameSpec", () => {
it("extracts iframe attributes", () => {
expect(extractFrameSpec('<iframe src="/activity" style="height: 20px"></iframe>')).toEqual({
src: "/activity",
style: "height: 20px"
});
});

it("rejects incomplete markup", () => {
expect(extractFrameSpec("<iframe></iframe>")).toBeNull();
expect(extractFrameSpec()).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { formatState } from "../formatState";

describe("formatState", () => {
it("formats empty, string, and object saved state", () => {
expect(formatState(null)).toBe("(no state recorded)");
expect(formatState("saved")).toBe("saved");
expect(formatState({ value: 1 })).toBe('{\n "value": 1\n}');
});

it("falls back to string conversion for circular state", () => {
const circular: Record<string, unknown> = {};

circular.self = circular;
expect(formatState(circular)).toBe("[object Object]");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getSpliceWrapper } from "../getSpliceWrapper";

describe("getSpliceWrapper", () => {
afterEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (window as any).spliceWrapper;
});

it("returns an already loaded SPLICE wrapper", () => {
const wrapper = {
registerGraderFrame: vi.fn(),
unregisterGraderFrame: vi.fn()
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).spliceWrapper = wrapper;

expect(getSpliceWrapper()).toBe(wrapper);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { WRAPPER_POLL_MS } from "../../constants";
import { waitForSpliceWrapper } from "../waitForSpliceWrapper";

describe("waitForSpliceWrapper", () => {
afterEach(() => {
vi.useRealTimers();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (window as any).spliceWrapper;
});

it("returns an already loaded SPLICE wrapper immediately", async () => {
const wrapper = {
registerGraderFrame: vi.fn(),
unregisterGraderFrame: vi.fn()
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).spliceWrapper = wrapper;

await expect(waitForSpliceWrapper()).resolves.toBe(wrapper);
});

it("waits until the SPLICE wrapper becomes available", async () => {
vi.useFakeTimers();
const wrapper = {
registerGraderFrame: vi.fn(),
unregisterGraderFrame: vi.fn()
};
const waiting = waitForSpliceWrapper();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).spliceWrapper = wrapper;
await vi.advanceTimersByTimeAsync(WRAPPER_POLL_MS);

await expect(waiting).resolves.toBe(wrapper);
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { describe, expect, it } from "vitest";

import { renderWithMantine, screen } from "@/test/renderWithMantine";

import { AnswerRendererProps } from "../types";

import { McqAnswerView } from "./McqAnswerView";
import { AnswerRendererProps } from "../../types";
import { McqAnswerView } from "../McqAnswerView";
const baseProps = (overrides: Partial<AnswerRendererProps> = {}): AnswerRendererProps => ({
answer: "",
history: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { optionIndexToLabel } from "../optionIndexToLabel";

describe("optionIndexToLabel", () => {
it.each([
["0", "A"],
["25", "Z"],
["26", "AA"],
["27", "AB"],
["51", "AZ"],
["52", "BA"]
])("converts option index %s to %s", (index, label) => {
expect(optionIndexToLabel(index)).toBe(label);
});

it("preserves legacy and unsafe option values", () => {
expect(optionIndexToLabel("legacy-a")).toBe("legacy-a");
expect(optionIndexToLabel("9007199254740992")).toBe("9007199254740992");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { parseSelectedOptions } from "../parseSelectedOptions";

describe("parseSelectedOptions", () => {
it("splits selected options and removes empty segments", () => {
expect(parseSelectedOptions("0,,2")).toEqual(["0", "2"]);
expect(parseSelectedOptions("")).toEqual([]);
});
});
Loading
Loading