Skip to content

Commit

Permalink
DEVPROD-7655: Create General table on Build Information page (#317)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiabarness authored Aug 21, 2024
1 parent b018c51 commit b527924
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 0 deletions.
19 changes: 19 additions & 0 deletions apps/spruce/cypress/integration/image/build_information.ts
Original file line number Diff line number Diff line change
@@ -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");
Expand Down
20 changes: 20 additions & 0 deletions apps/spruce/src/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions apps/spruce/src/gql/queries/image-general.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
query ImageGeneral($imageId: String!) {
image(imageId: $imageId) {
ami
id
lastDeployed
latestTask {
execution
finishTime
id
}
}
}
2 changes: 2 additions & 0 deletions apps/spruce/src/gql/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -114,6 +115,7 @@ export {
HOSTS,
IMAGE_DISTROS,
IMAGE_EVENTS,
IMAGE_GENERAL,
IMAGE_PACKAGES,
IMAGE_TOOLCHAINS,
IMAGES,
Expand Down
88 changes: 88 additions & 0 deletions apps/spruce/src/pages/image/GeneralTable/GeneralTable.test.tsx
Original file line number Diff line number Diff line change
@@ -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 }) => (
<MockedProvider mocks={[imageGeneralMock]}>{children}</MockedProvider>
);

describe("general table", () => {
it("displays correct information", async () => {
const { Component } = RenderFakeToastContext(
<GeneralTable imageId="ubuntu2204" />,
);
render(<Component />, { 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,
},
},
},
},
};
103 changes: 103 additions & 0 deletions apps/spruce/src/pages/image/GeneralTable/index.tsx
Original file line number Diff line number Diff line change
@@ -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<GeneralTableProps> = ({ 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 ? (
<StyledRouterLink to={getTaskRoute(image.latestTask.id)}>
<WordBreak>{image.latestTask.id}</WordBreak>
</StyledRouterLink>
) : (
"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<HTMLDivElement>(null);
const table = useLeafyGreenTable<GeneralInfo>({
columns,
data,
containerRef: tableContainerRef,
defaultColumn: {
enableColumnFilter: false,
},
});

return (
<BaseTable
data-cy-row="general-table-row"
loading={loading}
loadingRows={data.length}
shouldAlternateRowColor
table={table}
/>
);
};

const columns: LGColumnDef<GeneralInfo>[] = [
{
header: "Property",
accessorKey: "property",
},
{
header: "Value",
accessorKey: "value",
cell: ({ getValue }) => getValue(),
},
];
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -11,6 +12,9 @@ export const BuildInformationTab: React.FC<BuildInformationTabProps> = ({
imageId,
}) => (
<>
<SpruceFormContainer title="General">
<GeneralTable imageId={imageId} />
</SpruceFormContainer>
<SpruceFormContainer title="Distros" data-cy="distros-card">
<DistrosTable imageId={imageId} />
</SpruceFormContainer>
Expand Down

0 comments on commit b527924

Please sign in to comment.