diff --git a/site/src/components/Resources/AgentRowPreview.test.tsx b/site/src/components/Resources/AgentRowPreview.test.tsx new file mode 100644 index 0000000000..0fad0ceda0 --- /dev/null +++ b/site/src/components/Resources/AgentRowPreview.test.tsx @@ -0,0 +1,133 @@ +import { MockWorkspaceAgent } from "testHelpers/entities"; +import { WorkspaceAgent, DisplayApps, DisplayApp } from "api/typesGenerated"; +import { renderComponent } from "testHelpers/renderHelpers"; +import { AgentRowPreview } from "./AgentRowPreview"; +import { screen } from "@testing-library/react"; +import { DisplayAppNameMap } from "./AppLink/AppLink"; + +const AllDisplayAppsAndModule = MockWorkspaceAgent; +const VSCodeNoInsiders = { + ...MockWorkspaceAgent, + display_apps: [ + "ssh_helper", + "port_forwarding_helper", + "vscode", + "web_terminal", + ] as DisplayApp[], +}; +const VSCodeWithInsiders = { + ...MockWorkspaceAgent, + display_apps: [ + "ssh_helper", + "port_forwarding_helper", + "vscode", + "vscode_insiders", + "web_terminal", + ] as DisplayApp[], +}; +const NoVSCode = { + ...MockWorkspaceAgent, + display_apps: [ + "ssh_helper", + "port_forwarding_helper", + "web_terminal", + ] as DisplayApp[], +}; + +const NoModulesJustApps = { + ...MockWorkspaceAgent, + apps: [], +}; + +const NoAppsJustModules = { + ...MockWorkspaceAgent, + display_apps: [] as DisplayApp[], +}; + +const EmptyAppPreview = { + ...MockWorkspaceAgent, + apps: [], + display_apps: [] as DisplayApp[], +}; + +describe("AgentRowPreviewApps", () => { + it.each<{ + workspaceAgent: WorkspaceAgent; + testName: string; + }>([ + { + workspaceAgent: AllDisplayAppsAndModule, + testName: "AllDisplayAppsAndModule", + }, + { + workspaceAgent: VSCodeNoInsiders, + testName: "VSCodeNoInsiders", + }, + { + workspaceAgent: VSCodeWithInsiders, + testName: "VSCodeWithInsiders", + }, + { + workspaceAgent: NoVSCode, + testName: "NoVSCode", + }, + { + workspaceAgent: NoModulesJustApps, + testName: "NoModulesJustApps", + }, + { + workspaceAgent: NoAppsJustModules, + testName: "NoAppsJustModules", + }, + { + workspaceAgent: EmptyAppPreview, + testName: "EmptyAppPreview", + }, + ])( + ` displays appropriately`, + ({ workspaceAgent }) => { + renderComponent(); + workspaceAgent.apps.forEach((module) => { + expect(screen.getByText(module.display_name)).toBeInTheDocument(); + }); + workspaceAgent.display_apps + .filter((app) => app !== "vscode" && app !== "vscode_insiders") // these get special treatment + .forEach((app) => { + expect(screen.getByText(DisplayAppNameMap[app])).toBeInTheDocument(); + }); + + // test VS Code display + if (workspaceAgent.display_apps.includes("vscode")) { + expect( + screen.getByText(DisplayAppNameMap["vscode"]), + ).toBeInTheDocument(); + } else if (workspaceAgent.display_apps.includes("vscode_insiders")) { + expect( + screen.getByText(DisplayAppNameMap["vscode_insiders"]), + ).toBeInTheDocument(); + } else { + expect(screen.queryByText("vscode")).not.toBeInTheDocument(); + expect(screen.queryByText("vscode_insiders")).not.toBeInTheDocument(); + } + + // difference between all possible display apps and those displayed + const excludedApps = DisplayApps.filter( + (a) => !workspaceAgent.display_apps.includes(a), + ); + + excludedApps.forEach((app) => { + expect( + screen.queryByText(DisplayAppNameMap[app]), + ).not.toBeInTheDocument(); + }); + + // test empty state + if ( + workspaceAgent.display_apps.length === 0 && + workspaceAgent.apps.length === 0 + ) { + expect(screen.getByText("None")).toBeInTheDocument(); + } + }, + ); +}); diff --git a/site/src/components/Resources/AgentRowPreview.tsx b/site/src/components/Resources/AgentRowPreview.tsx index 43e3594847..e4372a1315 100644 --- a/site/src/components/Resources/AgentRowPreview.tsx +++ b/site/src/components/Resources/AgentRowPreview.tsx @@ -2,7 +2,10 @@ import { type Interpolation, type Theme } from "@emotion/react"; import { type FC } from "react"; import type { WorkspaceAgent } from "api/typesGenerated"; import { Stack } from "../Stack/Stack"; -import { AppPreviewLink } from "./AppLink/AppPreviewLink"; +import { AppPreview } from "./AppLink/AppPreview"; +import { BaseIcon } from "./AppLink/BaseIcon"; +import { VSCodeIcon } from "components/Icons/VSCodeIcon"; +import { DisplayAppNameMap } from "./AppLink/AppLink"; interface AgentRowPreviewStyles { // Helpful when there are more than one row so the values are aligned @@ -86,10 +89,43 @@ export const AgentRowPreview: FC = ({ spacing={0.5} wrap="wrap" > + {/* We display all modules returned in agent.apps */} {agent.apps.map((app) => ( - + + <> + + {app.display_name} + + ))} - {agent.apps.length === 0 && ( + {/* Additionally, we display any apps that are visible, e.g. + apps that are included in agent.display_apps */} + {agent.display_apps.includes("web_terminal") && ( + {DisplayAppNameMap["web_terminal"]} + )} + {agent.display_apps.includes("ssh_helper") && ( + {DisplayAppNameMap["ssh_helper"]} + )} + {agent.display_apps.includes("port_forwarding_helper") && ( + + {DisplayAppNameMap["port_forwarding_helper"]} + + )} + {/* VSCode display apps (vscode, vscode_insiders) get special presentation */} + {agent.display_apps.includes("vscode") ? ( + + + {DisplayAppNameMap["vscode"]} + + ) : ( + agent.display_apps.includes("vscode_insiders") && ( + + + {DisplayAppNameMap["vscode_insiders"]} + + ) + )} + {agent.apps.length === 0 && agent.display_apps.length === 0 && ( None )} diff --git a/site/src/components/Resources/AppLink/AppLink.tsx b/site/src/components/Resources/AppLink/AppLink.tsx index 36d7ebd045..24afe3cef5 100644 --- a/site/src/components/Resources/AppLink/AppLink.tsx +++ b/site/src/components/Resources/AppLink/AppLink.tsx @@ -13,6 +13,14 @@ import { generateRandomString } from "utils/random"; import { BaseIcon } from "./BaseIcon"; import { ShareIcon } from "./ShareIcon"; +export const DisplayAppNameMap: Record = { + port_forwarding_helper: "Ports", + ssh_helper: "SSH", + vscode: "VS Code Desktop", + vscode_insiders: "VS Code Insiders", + web_terminal: "Terminal", +}; + const Language = { appTitle: (appName: string, identifier: string): string => `${appName} - ${identifier}`, diff --git a/site/src/components/Resources/AppLink/AppPreviewLink.tsx b/site/src/components/Resources/AppLink/AppPreview.tsx similarity index 58% rename from site/src/components/Resources/AppLink/AppPreviewLink.tsx rename to site/src/components/Resources/AppLink/AppPreview.tsx index 0a30788aab..3dc40a40cd 100644 --- a/site/src/components/Resources/AppLink/AppPreviewLink.tsx +++ b/site/src/components/Resources/AppLink/AppPreview.tsx @@ -1,14 +1,7 @@ import { Stack } from "components/Stack/Stack"; -import { type FC } from "react"; -import type * as TypesGen from "api/typesGenerated"; -import { BaseIcon } from "./BaseIcon"; -import { ShareIcon } from "./ShareIcon"; +import { type FC, type PropsWithChildren } from "react"; -interface AppPreviewProps { - app: TypesGen.WorkspaceApp; -} - -export const AppPreviewLink: FC = ({ app }) => { +export const AppPreview: FC = ({ children }) => { return ( ({ @@ -29,9 +22,7 @@ export const AppPreviewLink: FC = ({ app }) => { direction="row" spacing={1} > - - {app.display_name} - + {children} ); }; diff --git a/site/src/components/Resources/PortForwardButton.tsx b/site/src/components/Resources/PortForwardButton.tsx index c4c2cbf226..387d770932 100644 --- a/site/src/components/Resources/PortForwardButton.tsx +++ b/site/src/components/Resources/PortForwardButton.tsx @@ -26,6 +26,7 @@ import { PopoverContent, PopoverTrigger, } from "components/Popover/Popover"; +import { DisplayAppNameMap } from "./AppLink/AppLink"; export interface PortForwardButtonProps { host: string; @@ -50,7 +51,7 @@ export const PortForwardButton: FC = (props) => { - Ports + {DisplayAppNameMap["port_forwarding_helper"]} {portsQuery.data ? (
{portsQuery.data.ports.length}
) : ( diff --git a/site/src/components/Resources/SSHButton/SSHButton.tsx b/site/src/components/Resources/SSHButton/SSHButton.tsx index 50bb041115..9788618df6 100644 --- a/site/src/components/Resources/SSHButton/SSHButton.tsx +++ b/site/src/components/Resources/SSHButton/SSHButton.tsx @@ -15,6 +15,7 @@ import { } from "components/Popover/Popover"; import { Stack } from "components/Stack/Stack"; import { AgentButton } from "../AgentButton"; +import { DisplayAppNameMap } from "../AppLink/AppLink"; export interface SSHButtonProps { workspaceName: string; @@ -34,7 +35,7 @@ export const SSHButton: FC> = ({ return ( - SSH + {DisplayAppNameMap["ssh_helper"]} diff --git a/site/src/components/Resources/TerminalLink/TerminalLink.tsx b/site/src/components/Resources/TerminalLink/TerminalLink.tsx index d504be3902..d1a8e4e9b1 100644 --- a/site/src/components/Resources/TerminalLink/TerminalLink.tsx +++ b/site/src/components/Resources/TerminalLink/TerminalLink.tsx @@ -3,9 +3,9 @@ import { AgentButton } from "components/Resources/AgentButton"; import { FC } from "react"; import * as TypesGen from "api/typesGenerated"; import { generateRandomString } from "utils/random"; +import { DisplayAppNameMap } from "../AppLink/AppLink"; export const Language = { - linkText: "Terminal", terminalTitle: (identifier: string): string => `Terminal - ${identifier}`, }; @@ -46,7 +46,7 @@ export const TerminalLink: FC> = ({ }} data-testid="terminal" > - {Language.linkText} + {DisplayAppNameMap["web_terminal"]} ); }; diff --git a/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx b/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx index 352a497ff3..33f4b8a0c3 100644 --- a/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx +++ b/site/src/components/Resources/VSCodeDesktopButton/VSCodeDesktopButton.tsx @@ -9,6 +9,7 @@ import { useLocalStorage } from "hooks"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import { DisplayApp } from "api/typesGenerated"; +import { DisplayAppNameMap } from "../AppLink/AppLink"; export interface VSCodeDesktopButtonProps { userName: string; @@ -97,7 +98,7 @@ export const VSCodeDesktopButton: FC< }} > - VS Code Desktop + {DisplayAppNameMap["vscode"]} - VS Code Insiders + {DisplayAppNameMap["vscode_insiders"]} @@ -156,7 +157,7 @@ const VSCodeButton = ({ }); }} > - VS Code Desktop + {DisplayAppNameMap["vscode"]}
); }; @@ -200,7 +201,7 @@ const VSCodeInsidersButton = ({ }); }} > - VS Code Insiders + {DisplayAppNameMap["vscode_insiders"]} ); };