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:
parent
20fb5a2237
commit
119b5dd7ba
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue