diff --git a/src/pages/VisProgPage/VisProg.tsx b/src/pages/VisProgPage/VisProg.tsx
index 3db9a00..5311feb 100644
--- a/src/pages/VisProgPage/VisProg.tsx
+++ b/src/pages/VisProgPage/VisProg.tsx
@@ -140,7 +140,7 @@ function VisualProgrammingUI() {
}
// currently outputs the prepared program to the console
-function runProgramm() {
+function runProgram() {
const phases = graphReducer();
const program = {phases}
console.log(JSON.stringify(program, null, 2));
@@ -183,11 +183,11 @@ function VisProgPage() {
const [showSimpleProgram, setShowSimpleProgram] = useState(false);
const setProgramState = useProgramStore((state) => state.setProgramState);
- const runProgram = () => {
+ const onClick = () => {
const phases = graphReducer(); // reduce graph
setProgramState({ phases }); // <-- save to store
setShowSimpleProgram(true); // show SimpleProgram
- runProgramm(); // send to backend if needed
+ runProgram(); // send to backend if needed
};
if (showSimpleProgram) {
@@ -204,7 +204,7 @@ function VisProgPage() {
return (
<>
-
+
>
)
}
diff --git a/test/pages/simpleProgram/SimpleProgram.test.tsx b/test/pages/simpleProgram/SimpleProgram.test.tsx
new file mode 100644
index 0000000..dcb56ba
--- /dev/null
+++ b/test/pages/simpleProgram/SimpleProgram.test.tsx
@@ -0,0 +1,176 @@
+import { render, screen, fireEvent } from "@testing-library/react";
+import SimpleProgram from "../../../src/pages/SimpleProgram/SimpleProgram";
+import useProgramStore from "../../../src/utils/programStore";
+
+/**
+ * Helper to preload the program store before rendering.
+ */
+function loadProgram(phases: Record[]) {
+ useProgramStore.getState().setProgramState({ phases });
+}
+
+describe("SimpleProgram", () => {
+ beforeEach(() => {
+ loadProgram([]);
+ });
+
+ test("shows empty state when no program is loaded", () => {
+ render();
+ expect(screen.getByText("No program loaded.")).toBeInTheDocument();
+ });
+
+ test("renders first phase content", () => {
+ loadProgram([
+ {
+ id: "phase-1",
+ norms: [{ id: "n1", norm: "Be polite" }],
+ goals: [{ id: "g1", description: "Finish task", achieved: true }],
+ triggers: [{ id: "t1", label: "Keyword trigger" }],
+ },
+ ]);
+
+ render();
+
+ expect(screen.getByText("Phase 1 / 1")).toBeInTheDocument();
+ expect(screen.getByText("Be polite")).toBeInTheDocument();
+ expect(screen.getByText("Finish task")).toBeInTheDocument();
+ expect(screen.getByText("Keyword trigger")).toBeInTheDocument();
+ });
+
+ test("renders empty messages when phase has no data", () => {
+ loadProgram([
+ {
+ id: "phase-1",
+ norms: [],
+ goals: [],
+ triggers: [],
+ },
+ ]);
+
+ render();
+
+ expect(screen.getAllByText("No norms defined.").length).toBeGreaterThanOrEqual(1);
+ expect(screen.getAllByText("No goals defined.").length).toBeGreaterThanOrEqual(1);
+ expect(screen.getAllByText("No triggers defined.").length).toBeGreaterThanOrEqual(1);
+ expect(
+ screen.getByText("No conditional norms defined.")
+ ).toBeInTheDocument();
+ });
+
+ test("allows navigating between phases", () => {
+ loadProgram([
+ {
+ id: "phase-1",
+ norms: [],
+ goals: [],
+ triggers: [],
+ },
+ {
+ id: "phase-2",
+ norms: [{ id: "n2", norm: "Be careful" }],
+ goals: [],
+ triggers: [],
+ },
+ ]);
+
+ render();
+
+ expect(screen.getByText("Phase 1 / 2")).toBeInTheDocument();
+
+ fireEvent.click(screen.getByText("Next ▶"));
+
+ expect(screen.getByText("Phase 2 / 2")).toBeInTheDocument();
+ expect(screen.getByText("Be careful")).toBeInTheDocument();
+ });
+
+ test("prev button is disabled on first phase", () => {
+ loadProgram([{ id: "phase-1", norms: [], goals: [], triggers: [] }]);
+
+ render();
+ expect(screen.getByText("◀ Prev")).toBeDisabled();
+ });
+
+ test("next button is disabled on last phase", () => {
+ loadProgram([{ id: "phase-1", norms: [], goals: [], triggers: [] }]);
+
+ render();
+ expect(screen.getByText("Next ▶")).toBeDisabled();
+ });
+
+ test("prev and next buttons enable/disable correctly when navigating", () => {
+ loadProgram([
+ { id: "p1", norms: [], goals: [], triggers: [] },
+ { id: "p2", norms: [], goals: [], triggers: [] },
+ ]);
+
+ render();
+
+ const prev = screen.getByText("◀ Prev");
+ const next = screen.getByText("Next ▶");
+
+ expect(prev).toBeDisabled();
+ expect(next).not.toBeDisabled();
+
+ fireEvent.click(next);
+
+ expect(prev).not.toBeDisabled();
+ expect(next).toBeDisabled();
+ });
+
+ test("renders achieved and unachieved goals with correct icons", () => {
+ loadProgram([
+ {
+ id: "phase-1",
+ norms: [],
+ goals: [
+ { id: "g1", description: "Done goal", achieved: true },
+ { id: "g2", description: "Failed goal", achieved: false },
+ ],
+ triggers: [],
+ },
+ ]);
+
+ render();
+
+ expect(screen.getByText("✔")).toBeInTheDocument();
+ expect(screen.getByText("✖")).toBeInTheDocument();
+ expect(screen.getByText("Done goal")).toBeInTheDocument();
+ expect(screen.getByText("Failed goal")).toBeInTheDocument();
+ });
+
+ test("renders fallback labels when optional fields are missing", () => {
+ loadProgram([
+ {
+ id: "phase-1",
+ norms: [{}],
+ goals: [{}],
+ triggers: [{}],
+ },
+ ]);
+
+ render();
+
+ expect(screen.getByText("Unnamed norm")).toBeInTheDocument();
+ expect(screen.getByText("Unnamed goal")).toBeInTheDocument();
+ expect(screen.getByText("Unnamed trigger")).toBeInTheDocument();
+ });
+
+ test("does not crash when navigating beyond boundaries", () => {
+ loadProgram([
+ { id: "p1", norms: [], goals: [], triggers: [] },
+ { id: "p2", norms: [], goals: [], triggers: [] },
+ ]);
+
+ render();
+
+ fireEvent.click(screen.getByText("Next ▶"));
+ fireEvent.click(screen.getByText("Next ▶"));
+
+ expect(screen.getByText("Phase 2 / 2")).toBeInTheDocument();
+
+ fireEvent.click(screen.getByText("◀ Prev"));
+ fireEvent.click(screen.getByText("◀ Prev"));
+
+ expect(screen.getByText("Phase 1 / 2")).toBeInTheDocument();
+ });
+});
diff --git a/test/pages/simpleProgram/SimpleProgram.tsx b/test/pages/simpleProgram/SimpleProgram.tsx
deleted file mode 100644
index 22fcbbf..0000000
--- a/test/pages/simpleProgram/SimpleProgram.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { render, screen, fireEvent } from "@testing-library/react";
-import SimpleProgram from "../../../src/pages/SimpleProgram/SimpleProgram";
-import useProgramStore from "../../../src/utils/programStore";
-
-/**
- * Helper to preload the program store before rendering.
- */
-function loadProgram(phases: Record[]) {
- useProgramStore.getState().setProgramState({ phases });
-}
-
-describe("SimpleProgram", () => {
- beforeEach(() => {
- loadProgram([]);
- });
-
- test("shows empty state when no program is loaded", () => {
- render();
- expect(screen.getByText("No program loaded.")).toBeInTheDocument();
- });
-
- test("renders first phase content", () => {
- loadProgram([
- {
- id: "phase-1",
- norms: [{ id: "n1", norm: "Be polite" }],
- goals: [{ id: "g1", description: "Finish task", achieved: true }],
- triggers: [{ id: "t1", label: "Keyword trigger" }],
- },
- ]);
-
- render();
-
- expect(screen.getByText("Phase 1 / 1")).toBeInTheDocument();
- expect(screen.getByText("Be polite")).toBeInTheDocument();
- expect(screen.getByText("Finish task")).toBeInTheDocument();
- expect(screen.getByText("Keyword trigger")).toBeInTheDocument();
- });
-
- test("allows navigating between phases", () => {
- loadProgram([
- {
- id: "phase-1",
- norms: [],
- goals: [],
- triggers: [],
- },
- {
- id: "phase-2",
- norms: [{ id: "n2", norm: "Be careful" }],
- goals: [],
- triggers: [],
- },
- ]);
-
- render();
-
- expect(screen.getByText("Phase 1 / 2")).toBeInTheDocument();
-
- fireEvent.click(screen.getByText("Next ▶"));
-
- expect(screen.getByText("Phase 2 / 2")).toBeInTheDocument();
- expect(screen.getByText("Be careful")).toBeInTheDocument();
- });
-
- test("prev button is disabled on first phase", () => {
- loadProgram([
- { id: "phase-1", norms: [], goals: [], triggers: [] },
- ]);
-
- render();
- expect(screen.getByText("◀ Prev")).toBeDisabled();
- });
-
- test("next button is disabled on last phase", () => {
- loadProgram([
- { id: "phase-1", norms: [], goals: [], triggers: [] },
- ]);
-
- render();
- expect(screen.getByText("Next ▶")).toBeDisabled();
- });
-});