From b527924c331c05c38e21ae15535aa4a0ae173a8b Mon Sep 17 00:00:00 2001
From: sophiabarness <80068149+sophiabarness@users.noreply.github.com>
Date: Wed, 21 Aug 2024 12:09:08 -0400
Subject: [PATCH] DEVPROD-7655: Create General table on Build Information page
(#317)
---
.../integration/image/build_information.ts | 19 ++++
apps/spruce/src/gql/generated/types.ts | 20 ++++
.../src/gql/queries/image-general.graphql | 12 ++
apps/spruce/src/gql/queries/index.ts | 2 +
.../image/GeneralTable/GeneralTable.test.tsx | 88 +++++++++++++++
.../src/pages/image/GeneralTable/index.tsx | 103 ++++++++++++++++++
.../image/tabs/BuildInformationTab/index.tsx | 4 +
7 files changed, 248 insertions(+)
create mode 100644 apps/spruce/src/gql/queries/image-general.graphql
create mode 100644 apps/spruce/src/pages/image/GeneralTable/GeneralTable.test.tsx
create mode 100644 apps/spruce/src/pages/image/GeneralTable/index.tsx
diff --git a/apps/spruce/cypress/integration/image/build_information.ts b/apps/spruce/cypress/integration/image/build_information.ts
index 2736933e2..ef71b01b6 100644
--- a/apps/spruce/cypress/integration/image/build_information.ts
+++ b/apps/spruce/cypress/integration/image/build_information.ts
@@ -1,4 +1,23 @@
describe("build information", () => {
+ describe("general", () => {
+ it("should show correct property values", () => {
+ cy.visit("/image/ubuntu2204");
+ cy.dataCy("general-table-row").should("have.length", 4);
+ cy.dataCy("general-table-row")
+ .eq(0)
+ .should("contain.text", "Last deployed");
+ cy.dataCy("general-table-row")
+ .eq(1)
+ .should("contain.text", "Amazon Machine Image (AMI)");
+ cy.dataCy("general-table-row")
+ .eq(2)
+ .should("contain.text", "Latest task");
+ cy.dataCy("general-table-row")
+ .eq(3)
+ .should("contain.text", "Latest task time");
+ });
+ });
+
describe("distros", () => {
it("should show the corresponding distros", () => {
cy.visit("/image/ubuntu1804");
diff --git a/apps/spruce/src/gql/generated/types.ts b/apps/spruce/src/gql/generated/types.ts
index dbbb42945..c63c5cf15 100644
--- a/apps/spruce/src/gql/generated/types.ts
+++ b/apps/spruce/src/gql/generated/types.ts
@@ -6286,6 +6286,26 @@ export type ImageEventsQuery = {
} | null;
};
+export type ImageGeneralQueryVariables = Exact<{
+ imageId: Scalars["String"]["input"];
+}>;
+
+export type ImageGeneralQuery = {
+ __typename?: "Query";
+ image?: {
+ __typename?: "Image";
+ ami: string;
+ id: string;
+ lastDeployed: Date;
+ latestTask?: {
+ __typename?: "Task";
+ execution: number;
+ finishTime?: Date | null;
+ id: string;
+ } | null;
+ } | null;
+};
+
export type ImagePackagesQueryVariables = Exact<{
imageId: Scalars["String"]["input"];
opts: PackageOpts;
diff --git a/apps/spruce/src/gql/queries/image-general.graphql b/apps/spruce/src/gql/queries/image-general.graphql
new file mode 100644
index 000000000..546af364b
--- /dev/null
+++ b/apps/spruce/src/gql/queries/image-general.graphql
@@ -0,0 +1,12 @@
+query ImageGeneral($imageId: String!) {
+ image(imageId: $imageId) {
+ ami
+ id
+ lastDeployed
+ latestTask {
+ execution
+ finishTime
+ id
+ }
+ }
+}
diff --git a/apps/spruce/src/gql/queries/index.ts b/apps/spruce/src/gql/queries/index.ts
index 42414d8c2..e2c4ce09b 100644
--- a/apps/spruce/src/gql/queries/index.ts
+++ b/apps/spruce/src/gql/queries/index.ts
@@ -25,6 +25,7 @@ import HOST from "./host.graphql";
import HOSTS from "./hosts.graphql";
import IMAGE_DISTROS from "./image-distros.graphql";
import IMAGE_EVENTS from "./image-events.graphql";
+import IMAGE_GENERAL from "./image-general.graphql";
import IMAGE_PACKAGES from "./image-packages.graphql";
import IMAGE_TOOLCHAINS from "./image-toolchains.graphql";
import IMAGES from "./images.graphql";
@@ -114,6 +115,7 @@ export {
HOSTS,
IMAGE_DISTROS,
IMAGE_EVENTS,
+ IMAGE_GENERAL,
IMAGE_PACKAGES,
IMAGE_TOOLCHAINS,
IMAGES,
diff --git a/apps/spruce/src/pages/image/GeneralTable/GeneralTable.test.tsx b/apps/spruce/src/pages/image/GeneralTable/GeneralTable.test.tsx
new file mode 100644
index 000000000..6b7dccbf4
--- /dev/null
+++ b/apps/spruce/src/pages/image/GeneralTable/GeneralTable.test.tsx
@@ -0,0 +1,88 @@
+import { MockedProvider } from "@apollo/client/testing";
+import { RenderFakeToastContext } from "context/toast/__mocks__";
+import {
+ ImageGeneralQuery,
+ ImageGeneralQueryVariables,
+} from "gql/generated/types";
+import { IMAGE_GENERAL } from "gql/queries";
+import {
+ renderWithRouterMatch as render,
+ screen,
+ waitFor,
+ within,
+} from "test_utils";
+import { ApolloMock } from "types/gql";
+import { GeneralTable } from ".";
+
+const wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
+);
+
+describe("general table", () => {
+ it("displays correct information", async () => {
+ const { Component } = RenderFakeToastContext(
+ ,
+ );
+ render(, { wrapper });
+ await waitFor(() => {
+ expect(screen.getAllByDataCy("general-table-row")).toHaveLength(4);
+ });
+
+ const lastDeployed = screen.getAllByDataCy("general-table-row")[0];
+ expect(within(lastDeployed).getAllByRole("cell")[0]).toHaveTextContent(
+ "Last deployed",
+ );
+ expect(within(lastDeployed).getAllByRole("cell")[1]).toHaveTextContent(
+ "Aug 7, 2021, 9:57:00 PM UTC",
+ );
+
+ const ami = screen.getAllByDataCy("general-table-row")[1];
+ expect(within(ami).getAllByRole("cell")[0]).toHaveTextContent(
+ "Amazon Machine Image (AMI)",
+ );
+ expect(within(ami).getAllByRole("cell")[1]).toHaveTextContent(
+ "ami-9809871715289206",
+ );
+
+ const latestTask = screen.getAllByDataCy("general-table-row")[2];
+ expect(within(latestTask).getAllByRole("cell")[0]).toHaveTextContent(
+ "Latest task",
+ );
+ expect(within(latestTask).getAllByRole("link")[0]).toHaveAttribute(
+ "href",
+ "/task/evergreen_ui_spruce_check_codegen_patch_b01dd570dbb2d060bf56d853e9eb1a71d60a6464_66be78cc4f92ba0007d94fcf_24_08_15_21_53_20",
+ );
+
+ const latestTaskTime = screen.getAllByDataCy("general-table-row")[3];
+ expect(within(latestTaskTime).getAllByRole("cell")[0]).toHaveTextContent(
+ "Latest task time",
+ );
+ expect(within(latestTaskTime).getAllByRole("cell")[1]).toHaveTextContent(
+ "Aug 8, 2021, 9:57:00 PM UTC",
+ );
+ });
+});
+
+const imageGeneralMock: ApolloMock<
+ ImageGeneralQuery,
+ ImageGeneralQueryVariables
+> = {
+ request: {
+ query: IMAGE_GENERAL,
+ variables: { imageId: "ubuntu2204" },
+ },
+ result: {
+ data: {
+ image: {
+ id: "ubuntu2204",
+ ami: "ami-9809871715289206",
+ lastDeployed: new Date("2021-08-07T17:57:00-04:00"),
+ latestTask: {
+ finishTime: new Date("2021-08-08T17:57:00-04:00"),
+ id: "evergreen_ui_spruce_check_codegen_patch_b01dd570dbb2d060bf56d853e9eb1a71d60a6464_66be78cc4f92ba0007d94fcf_24_08_15_21_53_20",
+ execution: 0,
+ },
+ },
+ },
+ },
+};
diff --git a/apps/spruce/src/pages/image/GeneralTable/index.tsx b/apps/spruce/src/pages/image/GeneralTable/index.tsx
new file mode 100644
index 000000000..ecda9ae68
--- /dev/null
+++ b/apps/spruce/src/pages/image/GeneralTable/index.tsx
@@ -0,0 +1,103 @@
+import { useMemo, useRef } from "react";
+import { useQuery } from "@apollo/client";
+import { useLeafyGreenTable, LGColumnDef } from "@leafygreen-ui/table";
+import { StyledRouterLink, WordBreak } from "components/styles";
+import { BaseTable } from "components/Table/BaseTable";
+import { getTaskRoute } from "constants/routes";
+import { useToastContext } from "context/toast";
+import {
+ ImageGeneralQuery,
+ ImageGeneralQueryVariables,
+} from "gql/generated/types";
+import { IMAGE_GENERAL } from "gql/queries";
+import { useDateFormat } from "hooks";
+
+type GeneralInfo = {
+ property: string;
+ value: React.ReactNode | string;
+};
+
+type GeneralTableProps = {
+ imageId: string;
+};
+
+export const GeneralTable: React.FC = ({ imageId }) => {
+ const dispatchToast = useToastContext();
+ const { data: imageData, loading } = useQuery<
+ ImageGeneralQuery,
+ ImageGeneralQueryVariables
+ >(IMAGE_GENERAL, {
+ variables: { imageId },
+ onError(err) {
+ dispatchToast.error(
+ `There was an error loading the image general table: ${err.message}`,
+ );
+ },
+ });
+
+ const getDateCopy = useDateFormat();
+
+ const data = useMemo(() => {
+ const image = imageData?.image;
+
+ return [
+ {
+ property: "Last deployed",
+ value: image?.lastDeployed ? getDateCopy(image.lastDeployed) : "N/A",
+ },
+ {
+ property: "Amazon Machine Image (AMI)",
+ value: image?.ami ?? "N/A",
+ },
+ {
+ property: "Latest task",
+ value: image?.latestTask?.id ? (
+
+ {image.latestTask.id}
+
+ ) : (
+ "N/A"
+ ),
+ },
+ {
+ property: "Latest task time",
+ value: image?.latestTask?.finishTime
+ ? getDateCopy(image.latestTask.finishTime)
+ : "N/A",
+ },
+ ];
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [imageData?.image]);
+
+ const tableContainerRef = useRef(null);
+ const table = useLeafyGreenTable({
+ columns,
+ data,
+ containerRef: tableContainerRef,
+ defaultColumn: {
+ enableColumnFilter: false,
+ },
+ });
+
+ return (
+
+ );
+};
+
+const columns: LGColumnDef[] = [
+ {
+ header: "Property",
+ accessorKey: "property",
+ },
+ {
+ header: "Value",
+ accessorKey: "value",
+ cell: ({ getValue }) => getValue(),
+ },
+];
diff --git a/apps/spruce/src/pages/image/tabs/BuildInformationTab/index.tsx b/apps/spruce/src/pages/image/tabs/BuildInformationTab/index.tsx
index 6bd13e84a..4c5a68814 100644
--- a/apps/spruce/src/pages/image/tabs/BuildInformationTab/index.tsx
+++ b/apps/spruce/src/pages/image/tabs/BuildInformationTab/index.tsx
@@ -1,5 +1,6 @@
import { SpruceFormContainer } from "components/SpruceForm";
import { DistrosTable } from "pages/image/DistrosTable";
+import { GeneralTable } from "pages/image/GeneralTable";
import { PackagesTable } from "pages/image/PackagesTable";
import { ToolchainsTable } from "pages/image/ToolchainsTable";
@@ -11,6 +12,9 @@ export const BuildInformationTab: React.FC = ({
imageId,
}) => (
<>
+
+
+