diff --git a/src/components/shared/EditorVersionToggle.test.tsx b/src/components/shared/EditorVersionToggle.test.tsx new file mode 100644 index 000000000..a385af74b --- /dev/null +++ b/src/components/shared/EditorVersionToggle.test.tsx @@ -0,0 +1,81 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { EditorVersionToggle } from "./EditorVersionToggle"; + +const mockNavigate = vi.fn(); +let mockPathname = "/"; +let mockFlagEnabled = true; + +vi.mock("@tanstack/react-router", async (importOriginal) => ({ + ...(await importOriginal()), + useNavigate: () => mockNavigate, + useLocation: () => ({ pathname: mockPathname }), +})); + +vi.mock("./Settings/useFlags", () => ({ + useFlagValue: () => mockFlagEnabled, +})); + +describe("EditorVersionToggle", () => { + beforeEach(() => { + mockNavigate.mockClear(); + mockPathname = "/"; + mockFlagEnabled = true; + }); + + it("renders nothing when the v2_editor flag is disabled", () => { + mockFlagEnabled = false; + mockPathname = "/editor/my-pipeline"; + + render(); + + expect(screen.queryByRole("button")).toBeNull(); + }); + + it("renders nothing on routes without a v1/v2 counterpart", () => { + mockPathname = "/pipelines"; + + render(); + + expect(screen.queryByRole("button")).toBeNull(); + }); + + it.each([ + ["/editor/my-pipeline", "/editor-v2/my-pipeline", "Switch to new editor"], + ["/runs/run-123", "/runs-v2/run-123", "Switch to new view"], + ["/runs/run-123/sub-456", "/runs-v2/run-123/sub-456", "Switch to new view"], + ])("switches %s to the new version (%s)", (from, to, label) => { + mockPathname = from; + + render(); + + const button = screen.getByRole("button", { name: label }); + fireEvent.click(button); + + expect(mockNavigate).toHaveBeenCalledWith({ to }); + }); + + it.each([ + [ + "/editor-v2/my-pipeline", + "/editor/my-pipeline", + "Switch to legacy editor", + ], + ["/runs-v2/run-123", "/runs/run-123", "Switch to legacy view"], + [ + "/runs-v2/run-123/sub-456", + "/runs/run-123/sub-456", + "Switch to legacy view", + ], + ])("switches %s to the legacy version (%s)", (from, to, label) => { + mockPathname = from; + + render(); + + const button = screen.getByRole("button", { name: label }); + fireEvent.click(button); + + expect(mockNavigate).toHaveBeenCalledWith({ to }); + }); +}); diff --git a/src/components/shared/EditorVersionToggle.tsx b/src/components/shared/EditorVersionToggle.tsx index 35ced979e..47c61cc80 100644 --- a/src/components/shared/EditorVersionToggle.tsx +++ b/src/components/shared/EditorVersionToggle.tsx @@ -2,15 +2,44 @@ import { useLocation, useNavigate } from "@tanstack/react-router"; import TooltipButton from "@/components/shared/Buttons/TooltipButton"; import { Icon } from "@/components/ui/icon"; -import { APP_ROUTES, EDITOR_PATH } from "@/routes/router"; +import { APP_ROUTES, EDITOR_PATH } from "@/routes/appRoutes"; import { useFlagValue } from "./Settings/useFlags"; type EditorVersion = "v1" | "v2"; -const detectEditorVersion = (pathname: string): EditorVersion | null => { - if (pathname.startsWith(`${APP_ROUTES.EDITOR_V2}/`)) return "v2"; - if (pathname.startsWith(`${EDITOR_PATH}/`)) return "v1"; +const ROUTE_BASE_PAIRS: { v1: string; v2: string; noun: string }[] = [ + { v1: EDITOR_PATH, v2: APP_ROUTES.EDITOR_V2, noun: "editor" }, + { v1: APP_ROUTES.RUNS, v2: APP_ROUTES.RUNS_V2, noun: "view" }, +]; + +type ToggleTarget = { + to: string; + tooltip: string; + targetVersion: EditorVersion; +}; + +const getToggleTarget = (pathname: string): ToggleTarget | null => { + for (const { v1, v2, noun } of ROUTE_BASE_PAIRS) { + if (pathname.startsWith(`${v2}/`)) { + const rest = pathname.slice(v2.length + 1); + if (!rest) return null; + return { + to: `${v1}/${rest}`, + tooltip: `Switch to legacy ${noun}`, + targetVersion: "v1", + }; + } + if (pathname.startsWith(`${v1}/`)) { + const rest = pathname.slice(v1.length + 1); + if (!rest) return null; + return { + to: `${v2}/${rest}`, + tooltip: `Switch to new ${noun}`, + targetVersion: "v2", + }; + } + } return null; }; @@ -21,28 +50,16 @@ export const EditorVersionToggle = () => { if (!isEnabled) return null; - const version = detectEditorVersion(location.pathname); - if (!version) return null; - - const lastSegment = location.pathname.split("/").pop() ?? ""; - const pipelineName = decodeURIComponent(lastSegment); - if (!pipelineName) return null; - - const targetVersion = version === "v1" ? "v2" : "v1"; - const targetPath = - targetVersion === "v2" - ? `${APP_ROUTES.EDITOR_V2}/${encodeURIComponent(pipelineName)}` - : `${EDITOR_PATH}/${encodeURIComponent(pipelineName)}`; - const tooltip = - targetVersion === "v2" ? "Switch to new editor" : "Switch to legacy editor"; + const target = getToggleTarget(location.pathname); + if (!target) return null; return ( navigate({ to: targetPath })} - aria-label={tooltip} + tooltip={target.tooltip} + onClick={() => navigate({ to: target.to })} + aria-label={target.tooltip} > - + ); };