Skip to content

fix(site): fix and improve pending state on template editor UI #12766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 59 additions & 63 deletions site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
typeof editorValue === "string" ? isBinaryData(editorValue) : false;

// Auto scroll
const buildLogsRef = useRef<HTMLDivElement>(null);
const logsContentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (buildLogsRef.current) {
buildLogsRef.current.scrollTop = buildLogsRef.current.scrollHeight;
if (logsContentRef.current) {
logsContentRef.current.scrollTop = logsContentRef.current.scrollHeight;
}
}, [buildLogs]);

Expand Down Expand Up @@ -237,9 +237,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
paddingRight: 16,
}}
>
{buildLogs && (
<TemplateVersionStatusBadge version={templateVersion} />
)}
<TemplateVersionStatusBadge version={templateVersion} />

<ButtonGroup
variant="outlined"
Expand Down Expand Up @@ -575,62 +573,51 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
)}
</div>

<div
ref={buildLogsRef}
css={{
display: selectedTab !== "logs" ? "none" : "flex",
height: selectedTab ? 280 : 0,
flexDirection: "column",
overflowY: "auto",
}}
>
{templateVersion.job.error && (
<div>
<Alert
severity="error"
css={{
borderRadius: 0,
border: 0,
borderBottom: `1px solid ${theme.palette.divider}`,
borderLeft: `2px solid ${theme.palette.error.main}`,
}}
>
<AlertTitle>Error during the build</AlertTitle>
<AlertDetail>{templateVersion.job.error}</AlertDetail>
</Alert>
</div>
)}

{buildLogs && buildLogs.length === 0 && (
<Loader css={{ height: "100%" }} />
)}

{buildLogs && buildLogs.length > 0 && (
<WorkspaceBuildLogs
css={styles.buildLogs}
hideTimestamps
logs={buildLogs}
/>
)}
</div>
{selectedTab === "logs" && (
<div
css={[styles.logs, styles.tabContent]}
ref={logsContentRef}
>
{templateVersion.job.error && (
<div>
<Alert
severity="error"
css={{
borderRadius: 0,
border: 0,
borderBottom: `1px solid ${theme.palette.divider}`,
borderLeft: `2px solid ${theme.palette.error.main}`,
}}
>
<AlertTitle>Error during the build</AlertTitle>
<AlertDetail>{templateVersion.job.error}</AlertDetail>
</Alert>
</div>
)}

{buildLogs && buildLogs.length > 0 ? (
<WorkspaceBuildLogs
css={styles.buildLogs}
hideTimestamps
logs={buildLogs}
/>
) : (
<Loader css={{ height: "100%" }} />
)}
</div>
)}

<div
css={[
{
display: selectedTab !== "resources" ? "none" : undefined,
height: selectedTab ? 280 : 0,
},
styles.resources,
]}
>
{resources && (
<TemplateResourcesTable
resources={resources.filter(
(r) => r.workspace_transition === "start",
)}
/>
)}
</div>
{selectedTab === "resources" && (
<div css={[styles.resources, styles.tabContent]}>
{resources && (
<TemplateResourcesTable
resources={resources.filter(
(r) => r.workspace_transition === "start",
)}
/>
)}
</div>
)}
</div>
</div>
</div>
Expand Down Expand Up @@ -751,6 +738,17 @@ const styles = {
},
}),

tabContent: {
height: 280,
overflowY: "auto",
},

logs: {
display: "flex",
height: "100%",
flexDirection: "column",
},

buildLogs: {
borderRadius: 0,
border: 0,
Expand Down Expand Up @@ -780,8 +778,6 @@ const styles = {
},

resources: {
overflowY: "auto",

// Hack to access customize resource-card from here
"& .resource-card": {
borderLeft: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render, screen, waitFor, within } from "@testing-library/react";
import userEvent, { type UserEvent } from "@testing-library/user-event";
import WS from "jest-websocket-mock";
import { HttpResponse, http } from "msw";
import { QueryClient } from "react-query";
import { RouterProvider, createMemoryRouter } from "react-router-dom";
Expand All @@ -16,6 +17,7 @@ import {
MockWorkspaceBuildLogs,
} from "testHelpers/entities";
import {
createTestQueryClient,
renderWithAuth,
waitForLoaderToBeRemoved,
} from "testHelpers/renderHelpers";
Expand Down Expand Up @@ -291,30 +293,7 @@ describe.each([
);
}

render(
<AppProviders queryClient={queryClient}>
<RouterProvider
router={createMemoryRouter(
[
{
element: <RequireAuth />,
children: [
{
element: <TemplateVersionEditorPage />,
path: "/templates/:template/versions/:version/edit",
},
],
},
],
{
initialEntries: [
`/templates/${MockTemplate.name}/versions/${MockTemplateVersion.name}/edit`,
],
},
)}
/>
</AppProviders>,
);
renderEditorPage(queryClient);
await waitForLoaderToBeRemoved();

const dialogSelector = /template variables/i;
Expand All @@ -326,3 +305,80 @@ describe.each([
});
},
);

test("display pending badge and update it to running when status changes", async () => {
const MockPendingTemplateVersion = {
...MockTemplateVersion,
job: {
...MockTemplateVersion.job,
status: "pending",
},
};
const MockRunningTemplateVersion = {
...MockTemplateVersion,
job: {
...MockTemplateVersion.job,
status: "running",
},
};

let calls = 0;
server.use(
http.get(
"/api/v2/organizations/:org/templates/:template/versions/:version",
() => {
calls += 1;
return HttpResponse.json(
calls > 1 ? MockRunningTemplateVersion : MockPendingTemplateVersion,
);
},
),
);

// Mock the logs when the status is running. This prevents connection errors
// from being thrown in the console during the test.
new WS(
`ws://localhost/api/v2/templateversions/${MockTemplateVersion.name}/logs?follow=true`,
);

renderEditorPage(createTestQueryClient());

const status = await screen.findByRole("status");
expect(status).toHaveTextContent("Pending");

await waitFor(
() => {
expect(status).toHaveTextContent("Running");
},
// Increase the timeout due to the page fetching results every second, which
// may cause delays.
{ timeout: 5_000 },
);
});

function renderEditorPage(queryClient: QueryClient) {
return render(
<AppProviders queryClient={queryClient}>
<RouterProvider
router={createMemoryRouter(
[
{
element: <RequireAuth />,
children: [
{
element: <TemplateVersionEditorPage />,
path: "/templates/:template/versions/:version/edit",
},
],
},
],
{
initialEntries: [
`/templates/${MockTemplate.name}/versions/${MockTemplateVersion.name}/edit`,
],
},
)}
/>
</AppProviders>,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,31 @@ export const TemplateVersionEditorPage: FC = () => {
templateName,
versionName,
);
const templateVersionQuery = useQuery({
const activeTemplateVersionQuery = useQuery({
...templateVersionOptions,
keepPreviousData: true,
refetchInterval(data) {
return data?.job.status === "pending" ? 1_000 : false;
},
});
const { data: activeTemplateVersion } = activeTemplateVersionQuery;
const uploadFileMutation = useMutation(uploadFile());
const createTemplateVersionMutation = useMutation(
createTemplateVersion(organizationId),
);
const resourcesQuery = useQuery({
...resources(templateVersionQuery.data?.id ?? ""),
enabled: templateVersionQuery.data?.job.status === "succeeded",
...resources(activeTemplateVersion?.id ?? ""),
enabled: activeTemplateVersion?.job.status === "succeeded",
});
const logs = useWatchVersionLogs(templateVersionQuery.data, {
onDone: templateVersionQuery.refetch,
const logs = useWatchVersionLogs(activeTemplateVersion, {
onDone: activeTemplateVersionQuery.refetch,
});
const { fileTree, tarFile } = useFileTree(templateVersionQuery.data);
const { fileTree, tarFile } = useFileTree(activeTemplateVersion);
const {
missingVariables,
setIsMissingVariablesDialogOpen,
isMissingVariablesDialogOpen,
} = useMissingVariables(templateVersionQuery.data);
} = useMissingVariables(activeTemplateVersion);

// Handle template publishing
const [isPublishingDialogOpen, setIsPublishingDialogOpen] = useState(false);
Expand Down Expand Up @@ -109,25 +113,25 @@ export const TemplateVersionEditorPage: FC = () => {
Record<string, string>
>({});
useEffect(() => {
if (templateVersionQuery.data?.job.tags) {
setProvisionerTags(templateVersionQuery.data.job.tags);
if (activeTemplateVersion?.job.tags) {
setProvisionerTags(activeTemplateVersion.job.tags);
}
}, [templateVersionQuery.data?.job.tags]);
}, [activeTemplateVersion?.job.tags]);

return (
<>
<Helmet>
<title>{pageTitle(`${templateName} · Template Editor`)}</title>
</Helmet>

{!(templateQuery.data && templateVersionQuery.data && fileTree) ? (
{!(templateQuery.data && activeTemplateVersion && fileTree) ? (
<Loader fullscreen />
) : (
<TemplateVersionEditor
activePath={activePath}
onActivePathChange={onActivePathChange}
template={templateQuery.data}
templateVersion={templateVersionQuery.data}
templateVersion={activeTemplateVersion}
defaultFileTree={fileTree}
onPreview={async (newFileTree) => {
if (!tarFile) {
Expand Down Expand Up @@ -159,10 +163,10 @@ export const TemplateVersionEditorPage: FC = () => {
await publishVersionMutation.mutateAsync({
isActiveVersion,
data,
version: templateVersionQuery.data,
version: activeTemplateVersion,
});
const publishedVersion = {
...templateVersionQuery.data,
...activeTemplateVersion,
...data,
};
setIsPublishingDialogOpen(false);
Expand Down Expand Up @@ -190,13 +194,12 @@ export const TemplateVersionEditorPage: FC = () => {
isBuilding={
createTemplateVersionMutation.isLoading ||
uploadFileMutation.isLoading ||
templateVersionQuery.data.job.status === "running" ||
templateVersionQuery.data.job.status === "pending"
activeTemplateVersion.job.status === "running" ||
activeTemplateVersion.job.status === "pending"
}
canPublish={
templateVersionQuery.data.job.status === "succeeded" &&
templateQuery.data.active_version_id !==
templateVersionQuery.data.id
activeTemplateVersion.job.status === "succeeded" &&
templateQuery.data.active_version_id !== activeTemplateVersion.id
}
resources={resourcesQuery.data}
buildLogs={logs}
Expand Down
Loading
Loading