diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/extractFrameSpec.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/extractFrameSpec.spec.ts
new file mode 100644
index 000000000..eb9d76dea
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/extractFrameSpec.spec.ts
@@ -0,0 +1,15 @@
+import { extractFrameSpec } from "../extractFrameSpec";
+
+describe("extractFrameSpec", () => {
+ it("extracts iframe attributes", () => {
+ expect(extractFrameSpec('
')).toEqual({
+ src: "/activity",
+ style: "height: 20px"
+ });
+ });
+
+ it("rejects incomplete markup", () => {
+ expect(extractFrameSpec("
")).toBeNull();
+ expect(extractFrameSpec()).toBeNull();
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/formatState.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/formatState.spec.ts
new file mode 100644
index 000000000..75891ce46
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/formatState.spec.ts
@@ -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
= {};
+
+ circular.self = circular;
+ expect(formatState(circular)).toBe("[object Object]");
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/getSpliceWrapper.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/getSpliceWrapper.spec.ts
new file mode 100644
index 000000000..e7e15891e
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/getSpliceWrapper.spec.ts
@@ -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);
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/waitForSpliceWrapper.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/waitForSpliceWrapper.spec.ts
new file mode 100644
index 000000000..5b0a364cb
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/IframeAnswerView/utils/spec/waitForSpliceWrapper.spec.ts
@@ -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);
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/McqAnswerView.test.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/spec/McqAnswerView.spec.tsx
similarity index 93%
rename from bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/McqAnswerView.test.tsx
rename to bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/spec/McqAnswerView.spec.tsx
index 7a2e05208..e94be8f68 100644
--- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/McqAnswerView.test.tsx
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/spec/McqAnswerView.spec.tsx
@@ -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 => ({
answer: "",
history: [],
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/utils/spec/optionIndexToLabel.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/utils/spec/optionIndexToLabel.spec.ts
new file mode 100644
index 000000000..d75016c4d
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/utils/spec/optionIndexToLabel.spec.ts
@@ -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");
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/utils/spec/parseSelectedOptions.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/utils/spec/parseSelectedOptions.spec.ts
new file mode 100644
index 000000000..44091a8c6
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/McqAnswerView/utils/spec/parseSelectedOptions.spec.ts
@@ -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([]);
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/spec/ParsonsAnswerView.test.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/spec/ParsonsAnswerView.spec.tsx
similarity index 100%
rename from bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/spec/ParsonsAnswerView.test.tsx
rename to bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/spec/ParsonsAnswerView.spec.tsx
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/extractParsonsSource.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/extractParsonsSource.spec.ts
new file mode 100644
index 000000000..82fc3c3db
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/extractParsonsSource.spec.ts
@@ -0,0 +1,69 @@
+import { extractParsonsSource } from "../extractParsonsSource";
+
+describe("extractParsonsSource", () => {
+ it("extracts decoded lines, indentation levels, and removes Parsons metadata", () => {
+ const htmlsrc = `
+if left < right:
+---
+ print(left) #distractor: not needed
+---
+ return left
+
`;
+
+ expect(extractParsonsSource(htmlsrc)).toEqual({
+ indentUnit: " ",
+ lines: [
+ { indentLevel: 0, text: "if left < right:" },
+ { indentLevel: 1, text: "print(left)" },
+ { indentLevel: 2, text: "return left" }
+ ],
+ noIndent: false
+ });
+ });
+
+ it("recognizes questions where indentation cannot be changed", () => {
+ expect(
+ extractParsonsSource('a\n---\n b
')
+ ).toMatchObject({ indentUnit: " ", noIndent: true });
+ });
+
+ it.each([
+ ["line #paired:alternative", "line"],
+ ["line #tag:condition;depends:setup;", "line"]
+ ])("removes supported block metadata from %j", (block, expected) => {
+ expect(extractParsonsSource(`${block}`)).toMatchObject({
+ lines: [{ indentLevel: 0, text: expected }]
+ });
+ });
+
+ it("parses line-based sources without block separators", () => {
+ expect(extractParsonsSource('first\n second
')).toMatchObject({
+ indentUnit: " ",
+ lines: [
+ { indentLevel: 0, text: "first" },
+ { indentLevel: 1, text: "second" }
+ ]
+ });
+ });
+
+ it("uses the default indentation when the source has no indented lines", () => {
+ expect(extractParsonsSource('first\nsecond
')).toMatchObject({
+ indentUnit: " "
+ });
+ });
+
+ it("recognizes tabs as the indentation unit", () => {
+ expect(
+ extractParsonsSource('first\n---\n\tsecond
')
+ ).toMatchObject({
+ indentUnit: "\t"
+ });
+ });
+
+ it.each([undefined, "", "Question only
"])(
+ "returns null when the Parsons source is absent from %j",
+ (source) => {
+ expect(extractParsonsSource(source)).toBeNull();
+ }
+ );
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/extractParsonsSource.test.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/extractParsonsSource.test.ts
deleted file mode 100644
index ca4ad6f82..000000000
--- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/extractParsonsSource.test.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { extractParsonsSource } from "../extractParsonsSource";
-
-describe("extractParsonsSource", () => {
- it("extracts decoded lines, indentation levels, and removes Parsons metadata", () => {
- const htmlsrc = `
-if left < right:
----
- print(left) #distractor: not needed
----
- return left
-
`;
-
- expect(extractParsonsSource(htmlsrc)).toEqual({
- indentUnit: " ",
- lines: [
- { indentLevel: 0, text: "if left < right:" },
- { indentLevel: 1, text: "print(left)" },
- { indentLevel: 2, text: "return left" }
- ],
- noIndent: false
- });
- });
-
- it("recognizes questions where indentation cannot be changed", () => {
- expect(
- extractParsonsSource('a\n---\n b
')
- ).toMatchObject({ indentUnit: " ", noIndent: true });
- });
-
- it("returns null when the Parsons source is absent", () => {
- expect(extractParsonsSource("Question only
")).toBeNull();
- });
-});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/parseParsonsHash.test.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/parseParsonsHash.spec.ts
similarity index 76%
rename from bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/parseParsonsHash.test.ts
rename to bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/parseParsonsHash.spec.ts
index 24ae6712d..4efe45205 100644
--- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/parseParsonsHash.test.ts
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/parseParsonsHash.spec.ts
@@ -8,6 +8,13 @@ describe("parseParsonsHash", () => {
]);
});
+ it("ignores surrounding whitespace", () => {
+ expect(parseParsonsHash(" 0_1_0-2_2 ")).toEqual([
+ { lineIndexes: [0, 1], indent: 0 },
+ { lineIndexes: [2], indent: 2 }
+ ]);
+ });
+
it.each(["", "-", null, undefined])("treats %j as an empty answer", (answer) => {
expect(parseParsonsHash(answer)).toEqual([]);
});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/reconstructParsonsAnswer.test.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/reconstructParsonsAnswer.spec.ts
similarity index 60%
rename from bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/reconstructParsonsAnswer.test.ts
rename to bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/reconstructParsonsAnswer.spec.ts
index 6998d0ee4..66b5c97bd 100644
--- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/reconstructParsonsAnswer.test.ts
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ParsonsAnswerView/utils/spec/reconstructParsonsAnswer.spec.ts
@@ -29,16 +29,45 @@ describe("reconstructParsonsAnswer", () => {
);
});
+ it("preserves source indentation when indentation changes are disabled", () => {
+ const noIndentSource =
+ 'first\n---\n second
';
+
+ expect(reconstructParsonsAnswer("0_4-1_0", noIndentSource)).toBe("first\n second");
+ });
+
+ it("preserves blank lines inside a multi-line block", () => {
+ const sourceWithBlankLine = `
+if ready:
+---
+ start()
+
+ finish()
+
`;
+
+ expect(reconstructParsonsAnswer("0_1_2_3_0", sourceWithBlankLine)).toBe(
+ "if ready:\n start()\n\n finish()"
+ );
+ });
+
it("formats legacy plain-text answers without block separators", () => {
expect(reconstructParsonsAnswer("def greet(name):\n---\n if name:")).toBe(
"def greet(name):\n if name:"
);
});
+ it("normalizes legacy line endings and padded block separators", () => {
+ expect(reconstructParsonsAnswer("first\r\n \t--- \r\n second\r")).toBe("first\n second");
+ });
+
it("returns null for a hash when its question source is unavailable", () => {
expect(reconstructParsonsAnswer("0_0")).toBeNull();
});
+ it("returns null when a hash references a missing source line", () => {
+ expect(reconstructParsonsAnswer("99_0", htmlsrc)).toBeNull();
+ });
+
it.each([null, undefined])("treats a %s answer as empty", (answer) => {
expect(reconstructParsonsAnswer(answer, htmlsrc)).toBe("");
});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/utils/spec/answerForRestore.spec.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/utils/spec/answerForRestore.spec.ts
new file mode 100644
index 000000000..4e93f5ad2
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/utils/spec/answerForRestore.spec.ts
@@ -0,0 +1,15 @@
+import { answerForRestore } from "../answerForRestore";
+
+describe("answerForRestore", () => {
+ it("parses legacy string matching answers before calling restoreAnswers", () => {
+ expect(answerForRestore("matching", '{"connections":[{"from":"left","to":"right"}]}')).toEqual({
+ connections: [{ from: "left", to: "right" }]
+ });
+ });
+
+ it("keeps drag-and-drop JSON serialized because its component parses the string", () => {
+ const answer = '{"zone":["item"]}';
+
+ expect(answerForRestore("dragndrop", answer)).toBe(answer);
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/RunestoneGraderPreview.test.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/utils/spec/restoreDataForAttempt.spec.ts
similarity index 53%
rename from bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/RunestoneGraderPreview.test.ts
rename to bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/utils/spec/restoreDataForAttempt.spec.ts
index aae7814d9..49b90e8af 100644
--- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/RunestoneGraderPreview.test.ts
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestoneGraderPreview/utils/spec/restoreDataForAttempt.spec.ts
@@ -1,20 +1,8 @@
import { GraderAnswerHistoryItem } from "@store/grader/grader.logic.api";
-import { describe, expect, it } from "vitest";
-import { answerForRestore, restoreDataForAttempt } from "./utils";
-describe("RunestoneGraderPreview restore data", () => {
- it("parses legacy string matching answers before calling restoreAnswers", () => {
- expect(answerForRestore("matching", '{"connections":[{"from":"left","to":"right"}]}')).toEqual({
- connections: [{ from: "left", to: "right" }]
- });
- });
-
- it("keeps drag-and-drop JSON serialized because its component parses the string", () => {
- const answer = '{"zone":["item"]}';
-
- expect(answerForRestore("dragndrop", answer)).toBe(answer);
- });
+import { restoreDataForAttempt } from "../restoreDataForAttempt";
+describe("restoreDataForAttempt", () => {
it("normalizes a null stored answer to the empty value expected by Runestone components", () => {
expect(restoreDataForAttempt("mchoice", { id: 2, answer: null }, "student-1").answer).toBe("");
});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestonePreview/spec/RunestonePreview.spec.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestonePreview/spec/RunestonePreview.spec.tsx
new file mode 100644
index 000000000..4d62b42f8
--- /dev/null
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/RunestonePreview/spec/RunestonePreview.spec.tsx
@@ -0,0 +1,55 @@
+import { renderRunestoneComponent } from "@/componentFuncs";
+import { renderWithMantine, screen, waitFor } from "@/test/renderWithMantine";
+
+import { QuestionPreviewHeader, RunestonePreview } from "../RunestonePreview";
+
+vi.mock("@/componentFuncs", () => ({
+ renderRunestoneComponent: vi.fn(() => Promise.resolve())
+}));
+
+vi.mock("@components/routes/AssignmentBuilder/MathJaxWrapper", () => ({
+ MathJaxWrapper: ({ children }: { children: React.ReactNode }) => <>{children}>
+}));
+
+vi.mock("better-react-mathjax", () => ({
+ MathJax: ({ children }: { children: React.ReactNode }) => <>{children}>
+}));
+
+const renderRunestoneComponentMock = vi.mocked(renderRunestoneComponent);
+
+describe("RunestonePreview", () => {
+ beforeEach(() => {
+ renderRunestoneComponentMock.mockClear();
+ });
+
+ it("shows an explanatory message when rendered question HTML is unavailable", () => {
+ renderWithMantine();
+
+ expect(screen.getByText("No rendered question preview available.")).toBeInTheDocument();
+ expect(renderRunestoneComponentMock).not.toHaveBeenCalled();
+ });
+
+ it("injects the question markup and initializes the Runestone component", async () => {
+ const { container } = renderWithMantine(
+
+ );
+
+ expect(screen.getByTestId("rendered-question")).toHaveTextContent("Question body");
+ await waitFor(() => expect(renderRunestoneComponentMock).toHaveBeenCalledTimes(1));
+
+ const previewElement = container.querySelector(".ptx-runestone-container > div > div");
+ const [previewRef, options] = renderRunestoneComponentMock.mock.calls[0];
+
+ expect(previewRef.current).toBe(previewElement);
+ expect(options).toEqual({ isCalledFromBuilder: true, graderactive: false });
+ });
+
+ it("renders the question name in the shared preview header", () => {
+ renderWithMantine();
+
+ expect(screen.getByRole("heading", { name: "Question: parsons-greeting" })).toBeInTheDocument();
+ });
+});
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ShortAnswerView/ShortAnswerView.test.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ShortAnswerView/spec/ShortAnswerView.spec.tsx
similarity index 83%
rename from bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ShortAnswerView/ShortAnswerView.test.tsx
rename to bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ShortAnswerView/spec/ShortAnswerView.spec.tsx
index 70949c351..72c16a566 100644
--- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ShortAnswerView/ShortAnswerView.test.tsx
+++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/Grader/components/questionTypes/ShortAnswerView/spec/ShortAnswerView.spec.tsx
@@ -1,10 +1,7 @@
-import { describe, expect, it } from "vitest";
-
import { renderWithMantine, screen } from "@/test/renderWithMantine";
-import { AnswerRendererProps } from "../types";
-
-import { ShortAnswerView } from "./ShortAnswerView";
+import { AnswerRendererProps } from "../../types";
+import { ShortAnswerView } from "../ShortAnswerView";
const baseProps = (answer: string): AnswerRendererProps => ({
answer,
history: [],
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/tsconfig.json b/bases/rsptx/assignment_server_api/assignment_builder/tsconfig.json
index f9a4ca13b..0aed58e2d 100755
--- a/bases/rsptx/assignment_server_api/assignment_builder/tsconfig.json
+++ b/bases/rsptx/assignment_server_api/assignment_builder/tsconfig.json
@@ -31,5 +31,5 @@
"vite.config.ts",
"playwright.config.ts"
],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules", "coverage", "e2e/playwright-report", "e2e/test-results"]
}
diff --git a/bases/rsptx/assignment_server_api/assignment_builder/vitest.setup.ts b/bases/rsptx/assignment_server_api/assignment_builder/vitest.setup.ts
index 7c6522ca3..1962a60dd 100755
--- a/bases/rsptx/assignment_server_api/assignment_builder/vitest.setup.ts
+++ b/bases/rsptx/assignment_server_api/assignment_builder/vitest.setup.ts
@@ -26,6 +26,7 @@ const installStorage = () => {
const missing = (["localStorage", "sessionStorage"] as const).filter(
(name) => typeof (globalThis as Record)[name] === "undefined"
);
+
if (missing.length === 0) {
return;
}
@@ -62,7 +63,9 @@ if (typeof window !== "undefined") {
if (!("ResizeObserver" in window)) {
class ResizeObserverMock {
observe() {}
+
unobserve() {}
+
disconnect() {}
}
(window as unknown as { ResizeObserver: unknown }).ResizeObserver = ResizeObserverMock;