fix: display app templates correctly in build preview (#10994)

* fix: appropriately display display_app apps in template build preview

* added display apps to build preview

* added test, consolidated names

* handling empty state
This commit is contained in:
Kira Pilot
2023-12-05 16:01:40 -05:00
committed by GitHub
parent 81a3b36884
commit 53453c06a1
8 changed files with 194 additions and 23 deletions
@@ -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",
},
])(
`<AgentRowPreview agent={$testName} /> displays appropriately`,
({ workspaceAgent }) => {
renderComponent(<AgentRowPreview agent={workspaceAgent} />);
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();
}
},
);
});
@@ -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<AgentRowPreviewProps> = ({
spacing={0.5}
wrap="wrap"
>
{/* We display all modules returned in agent.apps */}
{agent.apps.map((app) => (
<AppPreviewLink key={app.slug} app={app} />
<AppPreview key={app.slug}>
<>
<BaseIcon app={app} />
{app.display_name}
</>
</AppPreview>
))}
{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") && (
<AppPreview>{DisplayAppNameMap["web_terminal"]}</AppPreview>
)}
{agent.display_apps.includes("ssh_helper") && (
<AppPreview>{DisplayAppNameMap["ssh_helper"]}</AppPreview>
)}
{agent.display_apps.includes("port_forwarding_helper") && (
<AppPreview>
{DisplayAppNameMap["port_forwarding_helper"]}
</AppPreview>
)}
{/* VSCode display apps (vscode, vscode_insiders) get special presentation */}
{agent.display_apps.includes("vscode") ? (
<AppPreview>
<VSCodeIcon sx={{ width: 12, height: 12 }} />
{DisplayAppNameMap["vscode"]}
</AppPreview>
) : (
agent.display_apps.includes("vscode_insiders") && (
<AppPreview>
<VSCodeIcon sx={{ width: 12, height: 12 }} />
{DisplayAppNameMap["vscode_insiders"]}
</AppPreview>
)
)}
{agent.apps.length === 0 && agent.display_apps.length === 0 && (
<span css={styles.agentDataValue}>None</span>
)}
</Stack>
@@ -13,6 +13,14 @@ import { generateRandomString } from "utils/random";
import { BaseIcon } from "./BaseIcon";
import { ShareIcon } from "./ShareIcon";
export const DisplayAppNameMap: Record<TypesGen.DisplayApp, string> = {
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}`,
@@ -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<AppPreviewProps> = ({ app }) => {
export const AppPreview: FC<PropsWithChildren> = ({ children }) => {
return (
<Stack
css={(theme) => ({
@@ -29,9 +22,7 @@ export const AppPreviewLink: FC<AppPreviewProps> = ({ app }) => {
direction="row"
spacing={1}
>
<BaseIcon app={app} />
{app.display_name}
<ShareIcon app={app} />
{children}
</Stack>
);
};
@@ -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<PortForwardButtonProps> = (props) => {
<Popover>
<PopoverTrigger>
<AgentButton disabled={!portsQuery.data}>
Ports
{DisplayAppNameMap["port_forwarding_helper"]}
{portsQuery.data ? (
<div css={styles.portCount}>{portsQuery.data.ports.length}</div>
) : (
@@ -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<PropsWithChildren<SSHButtonProps>> = ({
return (
<Popover isDefaultOpen={isDefaultOpen}>
<PopoverTrigger>
<AgentButton>SSH</AgentButton>
<AgentButton>{DisplayAppNameMap["ssh_helper"]}</AgentButton>
</PopoverTrigger>
<PopoverContent horizontal="right" classes={{ paper }}>
@@ -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<React.PropsWithChildren<TerminalLinkProps>> = ({
}}
data-testid="terminal"
>
<AgentButton>{Language.linkText}</AgentButton>
<AgentButton>{DisplayAppNameMap["web_terminal"]}</AgentButton>
</Link>
);
};
@@ -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<
}}
>
<VSCodeIcon css={{ width: 12, height: 12 }} />
VS Code Desktop
{DisplayAppNameMap["vscode"]}
</MenuItem>
<MenuItem
css={{ fontSize: 14 }}
@@ -106,7 +107,7 @@ export const VSCodeDesktopButton: FC<
}}
>
<VSCodeInsidersIcon css={{ width: 12, height: 12 }} />
VS Code Insiders
{DisplayAppNameMap["vscode_insiders"]}
</MenuItem>
</Menu>
</div>
@@ -156,7 +157,7 @@ const VSCodeButton = ({
});
}}
>
VS Code Desktop
{DisplayAppNameMap["vscode"]}
</AgentButton>
);
};
@@ -200,7 +201,7 @@ const VSCodeInsidersButton = ({
});
}}
>
VS Code Insiders
{DisplayAppNameMap["vscode_insiders"]}
</AgentButton>
);
};