diff --git a/src/features/metadata/components/BuildDropdown.tsx b/src/features/metadata/components/BuildDropdown.tsx
index b8d8f485..2603d6aa 100644
--- a/src/features/metadata/components/BuildDropdown.tsx
+++ b/src/features/metadata/components/BuildDropdown.tsx
@@ -6,22 +6,25 @@ import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import IconButton from "@mui/material/IconButton";
import { useAppDispatch } from "../../../hooks";
import { currentBuildIdChanged } from "..";
+import { Build } from "../../../common/models";
+import { buildDatetimeStatus } from "../../../utils/helpers/buildMapper";
interface IBuildProps {
/**
* @param builds list of builds
* @param currentBuildId id of the current build
- * @param onChangeStatus update the build status
+ * @param selectedBuildId id of the build selected from the dropdown
*/
- builds: {
- id: number;
- name: string;
- status: string;
- }[];
+ builds: Build[];
+ currentBuildId: number;
selectedBuildId: number;
}
-export const Build = ({ builds, selectedBuildId }: IBuildProps) => {
+export const BuildDropdown = ({
+ builds,
+ currentBuildId,
+ selectedBuildId
+}: IBuildProps) => {
const dispatch = useAppDispatch();
const { palette } = useTheme();
const [open, setOpen] = useState(false);
@@ -92,7 +95,7 @@ export const Build = ({ builds, selectedBuildId }: IBuildProps) => {
{builds
? builds.map(build => (
))
: null}
diff --git a/src/features/metadata/components/EnvBuildStatus.tsx b/src/features/metadata/components/EnvBuildStatus.tsx
new file mode 100644
index 00000000..e05f2943
--- /dev/null
+++ b/src/features/metadata/components/EnvBuildStatus.tsx
@@ -0,0 +1,74 @@
+import React from "react";
+import { CircularProgress, Typography } from "@mui/material";
+import Link from "@mui/material/Link";
+import OpenInNewIcon from "@mui/icons-material/OpenInNew";
+import { Artifact, Build } from "../../../common/models";
+import { PrefContext } from "../../../preferences";
+import { StyledMetadataItem } from "../../../styles/StyledMetadataItem";
+import artifactList from "../../../utils/helpers/artifact";
+import { artifactBaseUrl } from "../../../utils/helpers/parseArtifactList";
+import { buildStatus } from "../../../utils/helpers/buildMapper";
+
+const LogLink = ({ logArtifact }: { logArtifact: Artifact }) => {
+ const pref = React.useContext(PrefContext);
+ const url = new URL(
+ logArtifact.route,
+ artifactBaseUrl(pref.apiUrl, window.location.origin)
+ );
+ return (
+
+
+ Log
+
+ );
+};
+
+interface IEnvBuildStatusProps {
+ build: Build;
+}
+
+export const EnvBuildStatus = ({ build }: IEnvBuildStatusProps) => {
+ const logArtifact: Artifact | void = artifactList(build.id, ["LOGS"])[0];
+
+ return (
+
+ Status: {""}
+
+ {buildStatus(build)}
+ {build.status_info && ` (${build.status_info})`}
+ {build.status === "BUILDING" || build.status === "QUEUED" ? (
+
+ ) : (
+ // If the selected build is a failed build, render the link to the build log.
+ build.status === "FAILED" &&
+ logArtifact && (
+ <>
+ .
+ >
+ )
+ )}
+
+
+ );
+};
diff --git a/src/features/metadata/components/EnvBuilds.tsx b/src/features/metadata/components/EnvBuilds.tsx
index ce06497e..780f08f7 100644
--- a/src/features/metadata/components/EnvBuilds.tsx
+++ b/src/features/metadata/components/EnvBuilds.tsx
@@ -1,15 +1,9 @@
import React from "react";
-import { CircularProgress, Typography } from "@mui/material";
+import { CircularProgress } from "@mui/material";
import { StyledMetadataItem } from "../../../styles/StyledMetadataItem";
import { Build as IBuild } from "../../../common/models";
-import { Build } from "../../../features/metadata/components";
-import { buildMapper } from "../../../utils/helpers/buildMapper";
-import Link from "@mui/material/Link";
-import OpenInNewIcon from "@mui/icons-material/OpenInNew";
-import { artifactBaseUrl } from "../../../utils/helpers";
-import { PrefContext } from "../../../preferences";
-import artifactList from "../../../utils/helpers/artifact";
-import { Artifact } from "../../../common/models";
+import { BuildDropdown } from "../../../features/metadata/components";
+import { EnvBuildStatus } from "./EnvBuildStatus";
export interface IData {
currentBuildId: number;
@@ -24,37 +18,7 @@ export const EnvBuilds = ({
builds,
mode
}: IData) => {
- const envBuilds = builds.length ? buildMapper(builds, currentBuildId) : [];
- const currentBuild = envBuilds.find(build => build.id === selectedBuildId);
-
- // If the selected build is a failed build, we will render the link to the build log.
- let logLink;
- const showLogLink = currentBuild?.status === "Failed";
- const logArtifact: Artifact | never = artifactList(currentBuild?.id, [
- "LOGS"
- ])[0];
- if (showLogLink && logArtifact) {
- const pref = React.useContext(PrefContext);
- const url = new URL(
- logArtifact.route,
- artifactBaseUrl(pref.apiUrl, window.location.origin)
- );
- logLink = (
-
-
- Log
-
- );
- }
-
+ const selectedBuild = builds.find(build => build.id === selectedBuildId);
return (
<>
{mode === "edit" ? "Change active environment version:" : "Builds:"}
- {currentBuild ? (
+ {selectedBuild ? (
<>
-
-
- Status: {""}
-
- {currentBuild.status}
- {currentBuild.status_info && ` (${currentBuild.status_info})`}
- {((currentBuild.status === "Building" ||
- currentBuild.status === "Queued") && (
-
- )) ||
- // If the selected build is a failed build, render the link to the build log.
- (showLogLink && <>. {logLink}>)}
-
-
+
+
>
) : (
{
- const BUILD_STATUS = ["BUILDING"];
- return BUILD_STATUS.includes(status);
-};
-
-const isQueued = (status: string) => {
- const BUILD_STATUS = ["QUEUED"];
- return BUILD_STATUS.includes(status);
-};
-
-const isCompleted = (status: string, duration: number) => {
- if (status === "COMPLETED") {
- if (duration > 0) {
- return `Completed in ${duration} min`;
- }
- return "Completed";
- }
- return STATUS_OPTIONS[status];
-};
-
const dateToTimezone = (date: string) => {
if (!date) {
return "";
@@ -40,51 +20,41 @@ const dateToTimezone = (date: string) => {
});
};
-export const buildMapper = (data: Build[], currentBuildId: number) => {
- return data.map(
- ({ id, status, status_info, ended_on, scheduled_on }: Build) => {
- let duration = 0;
+export const buildDatetimeStatus = (
+ { id, status, ended_on, scheduled_on }: Build,
+ currentBuildId: number
+): string => {
+ if (id === currentBuildId) {
+ return `${dateToTimezone(ended_on ?? scheduled_on)} - Active`;
+ } else if (status === "BUILDING") {
+ return `${dateToTimezone(scheduled_on)} - Building`;
+ } else if (status === "QUEUED") {
+ return `${dateToTimezone(scheduled_on)} - Queued`;
+ } else {
+ return `${dateToTimezone(ended_on ?? scheduled_on)} - ${
+ STATUS_OPTIONS[status]
+ }`;
+ }
+};
+
+export const buildStatus = ({
+ status,
+ ended_on,
+ scheduled_on
+}: Build): string => {
+ switch (status) {
+ case "COMPLETED":
if (ended_on && scheduled_on) {
const startTime = new Date(scheduled_on);
const endTime = new Date(ended_on);
- duration = (endTime.valueOf() - startTime.valueOf()) / 60000;
+ let duration = (endTime.valueOf() - startTime.valueOf()) / 60000;
duration = Math.round(duration);
+ if (duration > 0) {
+ return `Completed in ${duration} min`;
+ }
}
- if (id === currentBuildId) {
- return {
- id,
- name: `${dateToTimezone(ended_on ?? scheduled_on)} - Active`,
- status: isCompleted(status, duration),
- status_info
- };
- }
-
- if (isBuilding(status)) {
- return {
- id,
- name: `${dateToTimezone(scheduled_on)} - Building`,
- status: "Building",
- status_info
- };
- }
-
- if (isQueued(status)) {
- return {
- id,
- name: `${dateToTimezone(scheduled_on)} - Queued`,
- status: "Building",
- status_info
- };
- }
-
- return {
- id,
- name: `${dateToTimezone(ended_on ?? scheduled_on)} - ${
- STATUS_OPTIONS[status]
- }`,
- status: isCompleted(status, duration),
- status_info
- };
- }
- );
+ return "Completed";
+ default:
+ return STATUS_OPTIONS[status];
+ }
};
diff --git a/test/helpers/BuildMapper.test.tsx b/test/helpers/BuildMapper.test.tsx
index 336b0a98..83bddcda 100644
--- a/test/helpers/BuildMapper.test.tsx
+++ b/test/helpers/BuildMapper.test.tsx
@@ -1,54 +1,46 @@
-import { buildMapper } from "../../src/utils/helpers";
+import { buildDatetimeStatus } from "../../src/utils/helpers";
import { BUILD } from "../testutils";
-const generateBuild = (status: string) => [
- {
- ...BUILD,
- status
- }
-];
-describe("buildMapper", () => {
+const generateBuild = (status: string) => ({
+ ...BUILD,
+ status
+});
+
+describe("buildDatetimeStatus", () => {
it("should return an active build", () => {
- const builds = generateBuild("COMPLETED");
- const [mappedBuild] = buildMapper(builds, 1);
- expect(mappedBuild.name).toContain("Active");
+ const build = generateBuild("COMPLETED");
+ expect(buildDatetimeStatus(build, 1)).toMatch(/Active$/);
});
it("should return build", () => {
- const builds = generateBuild("BUILDING");
- const [mappedBuild] = buildMapper(builds, 2);
- expect(mappedBuild.name).toContain("Building");
+ const build = generateBuild("BUILDING");
+ expect(buildDatetimeStatus(build, 2)).toMatch(/Building$/);
});
it("should return queued build", () => {
- const builds = generateBuild("QUEUED");
- const [mappedBuild] = buildMapper(builds, 2);
- expect(mappedBuild.name).toContain("Queued");
+ const build = generateBuild("QUEUED");
+ expect(buildDatetimeStatus(build, 2)).toMatch(/Queued$/);
});
it("should return completed build", () => {
- const builds = generateBuild("COMPLETED");
- const [mappedBuild] = buildMapper(builds, 2);
- expect(mappedBuild.name).toContain("Available");
+ const build = generateBuild("COMPLETED");
+ expect(buildDatetimeStatus(build, 2)).toMatch(/Available$/);
});
it("should return failed build", () => {
- const builds = generateBuild("FAILED");
- const [mappedBuild] = buildMapper(builds, 2);
- expect(mappedBuild.name).toContain("Failed");
+ const build = generateBuild("FAILED");
+ expect(buildDatetimeStatus(build, 2)).toMatch(/Failed$/);
});
it("should use the scheduled_on date if the ended_on prop is null", () => {
- const [mappedBuild] = buildMapper(
- [
- {
- ...BUILD,
- status: "FAILED",
- ended_on: null
- }
- ],
+ const datetimeStatus = buildDatetimeStatus(
+ {
+ ...BUILD,
+ status: "FAILED",
+ ended_on: null
+ },
2
);
- expect(mappedBuild.name).toContain("November 8th, 2022");
+ expect(datetimeStatus).toContain("November 8th, 2022");
});
});
diff --git a/test/metadata/BuildDropdown.test.tsx b/test/metadata/BuildDropdown.test.tsx
index d8f548fa..709b192c 100644
--- a/test/metadata/BuildDropdown.test.tsx
+++ b/test/metadata/BuildDropdown.test.tsx
@@ -3,33 +3,23 @@ import { Provider } from "react-redux";
import { render, RenderResult, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { store } from "../../src/store";
-import { Build } from "../../src/features/metadata/components/BuildDropdown";
+import { BuildDropdown } from "../../src/features/metadata/components/BuildDropdown";
+import { mockBuilds } from "../../src/features/metadata/mocks/mockBuilds";
import { mockTheme } from "../testutils";
-
-const builds = [
- {
- id: 0,
- name: "August 5th, 2022 - 4:04 - Available",
- status: "Building"
- },
- {
- id: 1,
- name: "August 5th, 2022 - 3:57 - Available",
- status: "Building"
- }
-];
+import { buildDatetimeStatus } from "../../src/utils/helpers/buildMapper";
describe("", () => {
let component: RenderResult;
+ const currentBuildId = 1;
beforeEach(() => {
component = render(
mockTheme(
-
)
@@ -37,18 +27,25 @@ describe("", () => {
});
it("should render component", () => {
- expect(component.container).toHaveTextContent(builds[0].name);
+ expect(component.container).toHaveTextContent(
+ buildDatetimeStatus(mockBuilds[0], currentBuildId)
+ );
});
it("should display builds in the dropdown", async () => {
+ // This is what the dropdown should display for the build
+ const dropdownOptionName = buildDatetimeStatus(
+ mockBuilds[1],
+ currentBuildId
+ );
const [dropdownButton] = screen.getAllByRole("button");
userEvent.click(dropdownButton);
const dropdownItem = await screen.findByRole("option", {
- name: builds[1].name
+ name: dropdownOptionName
});
userEvent.click(dropdownItem);
- const buildName = await screen.findByText(builds[1].name);
+ const buildName = await screen.findByText(dropdownOptionName);
expect(buildName).toBeInTheDocument();
});
});
diff --git a/test/metadata/EnvBuildStatus.test.tsx b/test/metadata/EnvBuildStatus.test.tsx
new file mode 100644
index 00000000..573f80c5
--- /dev/null
+++ b/test/metadata/EnvBuildStatus.test.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { Provider } from "react-redux";
+import { render } from "@testing-library/react";
+import { EnvBuildStatus } from "../../src/features/metadata/components";
+import { BUILD } from "../testutils";
+import { store } from "../../src/store";
+
+describe("", () => {
+ it("should render link to log for failed build", () => {
+ const { getByTestId, getByRole } = render(
+
+
+
+ );
+ expect(getByRole("link", { name: "Log" })).toBeInTheDocument();
+ expect(getByTestId("build-status")).toHaveTextContent(
+ /^Status: Failed\. Log$/
+ );
+ });
+
+ it("should not render link to log for normal build", () => {
+ const { getByTestId, queryByRole } = render(
+
+
+
+ );
+ expect(queryByRole("link", { name: "Log" })).not.toBeInTheDocument();
+ expect(getByTestId("build-status")).toHaveTextContent(
+ /^Status: Completed$/
+ );
+ });
+});