diff --git a/packages/web/src/app/projects/[projectId]/page.test.tsx b/packages/web/src/app/projects/[projectId]/page.test.tsx
index 8ba4b4d21..6cdb15ce8 100644
--- a/packages/web/src/app/projects/[projectId]/page.test.tsx
+++ b/packages/web/src/app/projects/[projectId]/page.test.tsx
@@ -80,5 +80,7 @@ describe("ProjectPage", () => {
expect(screen.getByText("This project's config failed to load")).toBeInTheDocument();
expect(screen.getByText("Local config failed validation")).toBeInTheDocument();
expect(screen.queryByTestId("dashboard")).not.toBeInTheDocument();
+ expect(screen.getByRole("link", { name: "Back to dashboard" })).toHaveAttribute("href", "/");
+ expect(screen.queryByRole("link", { name: "Edit settings" })).not.toBeInTheDocument();
});
});
diff --git a/packages/web/src/app/projects/[projectId]/settings/page.test.tsx b/packages/web/src/app/projects/[projectId]/settings/page.test.tsx
index 23686b24c..ae26393c6 100644
--- a/packages/web/src/app/projects/[projectId]/settings/page.test.tsx
+++ b/packages/web/src/app/projects/[projectId]/settings/page.test.tsx
@@ -77,5 +77,7 @@ describe("ProjectSettingsPage", () => {
).toBeInTheDocument();
expect(screen.getByText("Local config failed validation")).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Save changes" })).not.toBeInTheDocument();
+ expect(screen.getByRole("link", { name: "Back to dashboard" })).toHaveAttribute("href", "/");
+ expect(screen.queryByRole("link", { name: "Edit settings" })).not.toBeInTheDocument();
});
});
diff --git a/packages/web/src/components/DegradedProjectState.tsx b/packages/web/src/components/DegradedProjectState.tsx
index c1b147f63..c02c734a5 100644
--- a/packages/web/src/components/DegradedProjectState.tsx
+++ b/packages/web/src/components/DegradedProjectState.tsx
@@ -1,5 +1,5 @@
import Link from "next/link";
-import { projectDashboardPath } from "@/lib/routes";
+
import { RepairDegradedProjectButton } from "./RepairDegradedProjectButton";
interface DegradedProjectStateProps {
@@ -61,16 +61,10 @@ export function DegradedProjectState({
- Back to project
-
-
- Open dashboard view
+ Back to dashboard
diff --git a/packages/web/src/components/__tests__/DegradedProjectState.test.tsx b/packages/web/src/components/__tests__/DegradedProjectState.test.tsx
new file mode 100644
index 000000000..561af112f
--- /dev/null
+++ b/packages/web/src/components/__tests__/DegradedProjectState.test.tsx
@@ -0,0 +1,86 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import { DegradedProjectState } from "@/components/DegradedProjectState";
+
+vi.mock("next/link", () => ({
+ default: ({
+ children,
+ ...props
+ }: React.PropsWithChildren>) => (
+ {children}
+ ),
+}));
+
+vi.mock("@/components/RepairDegradedProjectButton", () => ({
+ RepairDegradedProjectButton: ({ projectId }: { projectId: string }) => (
+
+ ),
+}));
+
+const baseProps = {
+ projectId: "my-project",
+ resolveError: "Local config at /tmp/my-project/agent-orchestrator.yaml failed validation: bad field",
+ projectPath: "/tmp/my-project",
+};
+
+describe("DegradedProjectState", () => {
+ it("renders the default heading", () => {
+ render();
+ expect(screen.getByText("This project's config failed to load")).toBeInTheDocument();
+ });
+
+ it("renders a custom heading when provided", () => {
+ render();
+ expect(screen.getByText("Custom heading")).toBeInTheDocument();
+ });
+
+ it("displays the resolve error", () => {
+ render();
+ expect(screen.getByText(baseProps.resolveError)).toBeInTheDocument();
+ });
+
+ it("extracts and displays the config path from the resolve error", () => {
+ render();
+ expect(
+ screen.getByText("/tmp/my-project/agent-orchestrator.yaml"),
+ ).toBeInTheDocument();
+ });
+
+ it("falls back to the project path when the config path cannot be parsed from the error", () => {
+ render(
+ ,
+ );
+ expect(
+ screen.getByText("/tmp/my-project/agent-orchestrator.yaml or .yml"),
+ ).toBeInTheDocument();
+ });
+
+ it("renders 'Back to dashboard' link pointing to /", () => {
+ render();
+ const link = screen.getByRole("link", { name: "Back to dashboard" });
+ expect(link).toHaveAttribute("href", "/");
+ });
+
+ it("does not render an 'Edit settings' link", () => {
+ render();
+ expect(screen.queryByRole("link", { name: "Edit settings" })).not.toBeInTheDocument();
+ });
+
+ it("does not show the repair button for a generic validation error", () => {
+ render();
+ expect(screen.queryByRole("button", { name: /repair/i })).not.toBeInTheDocument();
+ });
+
+ it("shows the repair button when the error indicates a wrapped projects format", () => {
+ render(
+ ,
+ );
+ expect(screen.getByRole("button", { name: /repair/i })).toBeInTheDocument();
+ });
+});