fix(web): remove circular 'Edit settings' link from DegradedProjectState

The 'Edit settings' link pointed to /projects/{id}/settings, which also
renders DegradedProjectState — making the button a self-link on the
settings page. Remove it entirely; 'Back to dashboard' (/) is the only
escape hatch needed.

Add DegradedProjectState component tests and assert both pages show no
'Edit settings' link and that 'Back to dashboard' points to /.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Aditi Chauhan 2026-05-20 18:34:13 +05:30
parent 20fb5a2237
commit 119b5dd7ba
4 changed files with 90 additions and 6 deletions

View File

@ -53,5 +53,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();
});
});

View File

@ -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();
});
});

View File

@ -66,12 +66,6 @@ export function DegradedProjectState({
>
Back to dashboard
</Link>
<Link
href={`/projects/${encodeURIComponent(projectId)}/settings`}
className="rounded-lg border border-[var(--color-border-default)] px-4 py-2 text-sm font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-bg-elevated-hover)]"
>
Edit settings
</Link>
</div>
</div>
</div>

View File

@ -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<React.AnchorHTMLAttributes<HTMLAnchorElement>>) => (
<a {...props}>{children}</a>
),
}));
vi.mock("@/components/RepairDegradedProjectButton", () => ({
RepairDegradedProjectButton: ({ projectId }: { projectId: string }) => (
<button>Repair {projectId}</button>
),
}));
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(<DegradedProjectState {...baseProps} />);
expect(screen.getByText("This project's config failed to load")).toBeInTheDocument();
});
it("renders a custom heading when provided", () => {
render(<DegradedProjectState {...baseProps} heading="Custom heading" />);
expect(screen.getByText("Custom heading")).toBeInTheDocument();
});
it("displays the resolve error", () => {
render(<DegradedProjectState {...baseProps} />);
expect(screen.getByText(baseProps.resolveError)).toBeInTheDocument();
});
it("extracts and displays the config path from the resolve error", () => {
render(<DegradedProjectState {...baseProps} />);
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(
<DegradedProjectState
{...baseProps}
resolveError="Something went wrong"
/>,
);
expect(
screen.getByText("/tmp/my-project/agent-orchestrator.yaml or .yml"),
).toBeInTheDocument();
});
it("renders 'Back to dashboard' link pointing to /", () => {
render(<DegradedProjectState {...baseProps} />);
const link = screen.getByRole("link", { name: "Back to dashboard" });
expect(link).toHaveAttribute("href", "/");
});
it("does not render an 'Edit settings' link", () => {
render(<DegradedProjectState {...baseProps} />);
expect(screen.queryByRole("link", { name: "Edit settings" })).not.toBeInTheDocument();
});
it("does not show the repair button for a generic validation error", () => {
render(<DegradedProjectState {...baseProps} />);
expect(screen.queryByRole("button", { name: /repair/i })).not.toBeInTheDocument();
});
it("shows the repair button when the error indicates a wrapped projects format", () => {
render(
<DegradedProjectState
{...baseProps}
resolveError="Local config at /tmp/x/agent-orchestrator.yaml still uses a wrapped projects: format"
/>,
);
expect(screen.getByRole("button", { name: /repair/i })).toBeInTheDocument();
});
});