diff --git a/src/gql/generated/types.ts b/src/gql/generated/types.ts index 88b1794c22..2a74e58b89 100644 --- a/src/gql/generated/types.ts +++ b/src/gql/generated/types.ts @@ -936,6 +936,7 @@ export type MainlineCommitsOptions = { limit?: InputMaybe; projectIdentifier: Scalars["String"]["input"]; requesters?: InputMaybe>; + revision?: InputMaybe; shouldCollapse?: InputMaybe; skipOrderNumber?: InputMaybe; }; @@ -1503,6 +1504,7 @@ export type Patches = { */ export type PatchesInput = { includeCommitQueue?: InputMaybe; + includeHidden?: InputMaybe; limit?: Scalars["Int"]["input"]; onlyCommitQueue?: InputMaybe; page?: Scalars["Int"]["input"]; @@ -2105,6 +2107,7 @@ export type RepoRef = { manualPrTestingEnabled: Scalars["Boolean"]["output"]; notifyOnBuildFailure: Scalars["Boolean"]["output"]; owner: Scalars["String"]["output"]; + parsleyFilters?: Maybe>; patchTriggerAliases?: Maybe>; patchingDisabled: Scalars["Boolean"]["output"]; perfEnabled: Scalars["Boolean"]["output"]; @@ -2146,6 +2149,7 @@ export type RepoRefInput = { manualPrTestingEnabled?: InputMaybe; notifyOnBuildFailure?: InputMaybe; owner?: InputMaybe; + parsleyFilters?: InputMaybe>; patchTriggerAliases?: InputMaybe>; patchingDisabled?: InputMaybe; perfEnabled?: InputMaybe; diff --git a/src/pages/task/actionButtons/previousCommits/PreviousCommits.test.tsx b/src/pages/task/actionButtons/previousCommits/PreviousCommits.test.tsx index b4a0601d08..e8dbdfa1dd 100644 --- a/src/pages/task/actionButtons/previousCommits/PreviousCommits.test.tsx +++ b/src/pages/task/actionButtons/previousCommits/PreviousCommits.test.tsx @@ -11,15 +11,12 @@ import { renderWithRouterMatch, screen, userEvent, waitFor } from "test_utils"; import { ApolloMock } from "types/gql"; import { PreviousCommits } from "."; -const goButton = { name: "Go" }; -const select = { name: "Previous commits for this task" }; - describe("previous commits", () => { // Patch and mainline commit behavior only have a significant difference when it comes to determining // the base or previous task. Patch gets the base task directly from BASE_VERSION_AND_TASK, while // mainline commits needs to run another query LAST_MAINLINE_COMMIT to get previous task. describe("patch specific", () => { - it("the GO button is disabled when there is no base task", async () => { + it("the button is disabled when there is no base task", async () => { const { Component } = RenderFakeToastContext( @@ -27,25 +24,15 @@ describe("previous commits", () => { ); renderWithRouterMatch(); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "aria-disabled", - "true" - ); - }); - // Select won't be disabled because baseVersion exists - await waitFor(() => { - expect(screen.getByRole("button", select)).toHaveAttribute( - "aria-disabled", - "false" - ); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "true"); }); - // Should say "base" for patches - expect(screen.getByText("Go to base commit")).toBeInTheDocument(); }); }); describe("mainline commits specific", () => { - it("the GO button is disabled when getParentTask returns null", async () => { + it("the button is disabled when getParentTask returns null", async () => { const { Component } = RenderFakeToastContext( { renderWithRouterMatch(); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "aria-disabled", - "true" - ); - }); - // Select won't be disabled because baseVersion exists - await waitFor(() => { - expect(screen.getByRole("button", select)).toHaveAttribute( - "aria-disabled", - "false" - ); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "true"); }); - // Should say "previous" for versions - expect(screen.getByText("Go to previous commit")).toBeInTheDocument(); }); - it("the GO button is disabled when getParentTask returns an error", async () => { + it("the button is disabled when getParentTask returns an error", async () => { const { Component } = RenderFakeToastContext( { renderWithRouterMatch(); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "aria-disabled", - "true" - ); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "true"); }); - // Select won't be disabled because baseVersion exists - await waitFor(() => { - expect(screen.getByRole("button", select)).toHaveAttribute( - "aria-disabled", - "false" - ); - }); - // Should say "previous" for versions - expect(screen.getByText("Go to previous commit")).toBeInTheDocument(); }); }); - it("the select & GO button are disabled when no base version exists", async () => { + it("the button is disabled when no base version exists", async () => { const { Component } = RenderFakeToastContext( @@ -109,50 +76,45 @@ describe("previous commits", () => { renderWithRouterMatch(); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "aria-disabled", - "true" - ); - }); - await waitFor(() => { - expect(screen.getByRole("button", select)).toHaveAttribute( - "aria-disabled", - "true" - ); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "true"); }); }); - it("when base task is passing, all dropdown items generate the same link.", async () => { + it("when base task is passing, all dropdown items generate the same link", async () => { const user = userEvent.setup(); const { Component } = RenderFakeToastContext( - + ); renderWithRouterMatch(); + await screen.findByRole("button", { name: "Previous commits" }); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "false"); + await user.click(screen.getByRole("button", { name: "Previous commits" })); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - baseTaskHref - ); + expect(screen.getByRole("menu")).toBeVisible(); }); - await user.click(screen.getByRole("button", select)); - await user.click( - screen.getByRole("option", { name: "Go to last passing version" }) - ); - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - baseTaskHref - ); - await user.click(screen.getByRole("button", select)); - await user.click( - screen.getByRole("option", { name: "Go to last executed version" }) - ); - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - baseTaskHref - ); + + expect( + screen.getByRole("menuitem", { name: "Go to base commit" }) + ).toHaveAttribute("href", baseTaskHref); + expect( + screen.getByRole("menuitem", { name: "Go to last passing version" }) + ).toHaveAttribute("href", baseTaskHref); + expect( + screen.getByRole("menuitem", { name: "Go to last executed version" }) + ).toHaveAttribute("href", baseTaskHref); }); it("when base task is failing, 'Go to base commit' and 'Go to last executed' dropdown items generate the same link and 'Go to last passing version' will be different.", async () => { @@ -166,30 +128,24 @@ describe("previous commits", () => { ); renderWithRouterMatch(); + await screen.findByRole("button", { name: "Previous commits" }); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "false"); + await user.click(screen.getByRole("button", { name: "Previous commits" })); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - baseTaskHref - ); - }); - await user.click(screen.getByRole("button", select)); - await user.click( - screen.getByRole("option", { name: "Go to last executed version" }) - ); - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - baseTaskHref - ); - await user.click(screen.getByRole("button", select)); - await user.click( - screen.getByRole("option", { name: "Go to last passing version" }) - ); - await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - "/task/last_passing_task" - ); + expect(screen.getByRole("menu")).toBeVisible(); }); + + expect( + screen.getByRole("menuitem", { name: "Go to base commit" }) + ).toHaveAttribute("href", baseTaskHref); + expect( + screen.getByRole("menuitem", { name: "Go to last passing version" }) + ).toHaveAttribute("href", "/task/last_passing_task"); + expect( + screen.getByRole("menuitem", { name: "Go to last executed version" }) + ).toHaveAttribute("href", baseTaskHref); }); it("when base task is not in a finished state, the last executed & passing task is not the same as the base commit", async () => { @@ -207,28 +163,24 @@ describe("previous commits", () => { ); renderWithRouterMatch(); + await screen.findByRole("button", { name: "Previous commits" }); + expect( + screen.getByRole("button", { name: "Previous commits" }) + ).toHaveAttribute("aria-disabled", "false"); + await user.click(screen.getByRole("button", { name: "Previous commits" })); await waitFor(() => { - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - baseTaskHref - ); + expect(screen.getByRole("menu")).toBeVisible(); }); - await user.click(screen.getByRole("button", select)); - await user.click( - screen.getByRole("option", { name: "Go to last executed version" }) - ); - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - "/task/last_executed_task" - ); - await user.click(screen.getByRole("button", select)); - await user.click( - screen.getByRole("option", { name: "Go to last passing version" }) - ); - expect(screen.getByRole("link", goButton)).toHaveAttribute( - "href", - "/task/last_passing_task" - ); + + expect( + screen.getByRole("menuitem", { name: "Go to base commit" }) + ).toHaveAttribute("href", baseTaskHref); + expect( + screen.getByRole("menuitem", { name: "Go to last passing version" }) + ).toHaveAttribute("href", "/task/last_passing_task"); + expect( + screen.getByRole("menuitem", { name: "Go to last executed version" }) + ).toHaveAttribute("href", "/task/last_executed_task"); }); }); diff --git a/src/pages/task/actionButtons/previousCommits/index.tsx b/src/pages/task/actionButtons/previousCommits/index.tsx index bca43f6812..770add33a5 100644 --- a/src/pages/task/actionButtons/previousCommits/index.tsx +++ b/src/pages/task/actionButtons/previousCommits/index.tsx @@ -1,13 +1,12 @@ -import { useReducer, useEffect } from "react"; -import { useLazyQuery, useQuery } from "@apollo/client"; -import styled from "@emotion/styled"; -import { Option, Select } from "@leafygreen-ui/select"; +import { useMemo } from "react"; +import { useQuery } from "@apollo/client"; +import Button, { Size } from "@leafygreen-ui/button"; +import { Menu, MenuItem } from "@leafygreen-ui/menu"; import Tooltip from "@leafygreen-ui/tooltip"; +import { Link } from "react-router-dom"; import { useTaskAnalytics } from "analytics"; -import { LoadingButton } from "components/Buttons"; -import { ConditionalWrapper } from "components/ConditionalWrapper"; +import Icon from "components/Icon"; import { finishedTaskStatuses } from "constants/task"; -import { size } from "constants/tokens"; import { useToastContext } from "context/toast"; import { BaseVersionAndTaskQuery, @@ -16,32 +15,19 @@ import { LastMainlineCommitQueryVariables, } from "gql/generated/types"; import { BASE_VERSION_AND_TASK, LAST_MAINLINE_COMMIT } from "gql/queries"; -import { useLGButtonRouterLink } from "hooks/useLGButtonRouterLink"; import { TaskStatus } from "types/task"; -import { string } from "utils"; -import { reportError } from "utils/errorReporting"; -import { initialState, reducer } from "./reducer"; -import { CommitTask, CommitType } from "./types"; +import { statuses, string } from "utils"; +import { CommitType } from "./types"; +import { getLinks, getTaskFromMainlineCommitsQuery } from "./utils"; const { applyStrictRegex } = string; +const { isFinishedTaskStatus } = statuses; interface PreviousCommitsProps { taskId: string; } export const PreviousCommits: React.FC = ({ taskId }) => { const { sendEvent } = useTaskAnalytics(); - const [ - { - disableButton, - hasFetchedLastExecuted, - hasFetchedLastPassing, - link, - selectState, - shouldFetchLastExecuted, - shouldFetchLastPassing, - }, - dispatch, - ] = useReducer(reducer, initialState); const dispatchToast = useToastContext(); const { data: taskData } = useQuery< @@ -51,44 +37,62 @@ export const PreviousCommits: React.FC = ({ taskId }) => { variables: { taskId }, }); - // We don't error for this query because it is the default query that is run when the page loads. - // If it errors it probably means there is no base version, which is fine. - const [fetchParentTask, { loading: parentLoading }] = useLazyQuery< + const { baseTask, buildVariant, displayName, versionMetadata } = + taskData?.task ?? {}; + const { order: skipOrderNumber, projectIdentifier } = + versionMetadata?.baseVersion ?? {}; + const bvOptionsBase = { + tasks: [applyStrictRegex(displayName)], + variants: [applyStrictRegex(buildVariant)], + }; + + const { data: parentTaskData, loading: parentLoading } = useQuery< LastMainlineCommitQuery, LastMainlineCommitQueryVariables >(LAST_MAINLINE_COMMIT, { - onCompleted: (data) => { - dispatch({ - type: "setParentTask", - task: getTaskFromMainlineCommitsQuery(data), - }); + skip: !versionMetadata || versionMetadata.isPatch, + variables: { + projectIdentifier, + skipOrderNumber, + buildVariantOptions: { + ...bvOptionsBase, + }, }, }); + const parentTask = + getTaskFromMainlineCommitsQuery(parentTaskData) ?? baseTask; - const [fetchLastPassing, { loading: passingLoading }] = useLazyQuery< + const { data: lastPassingTaskData, loading: passingLoading } = useQuery< LastMainlineCommitQuery, LastMainlineCommitQueryVariables >(LAST_MAINLINE_COMMIT, { - onCompleted: (data) => { - dispatch({ - type: "setLastPassingTask", - task: getTaskFromMainlineCommitsQuery(data), - }); + skip: !parentTask || parentTask.status === TaskStatus.Succeeded, + variables: { + projectIdentifier, + skipOrderNumber, + buildVariantOptions: { + ...bvOptionsBase, + statuses: [TaskStatus.Succeeded], + }, }, onError: (err) => { dispatchToast.error(`Last passing version unavailable: '${err.message}'`); }, }); + const lastPassingTask = getTaskFromMainlineCommitsQuery(lastPassingTaskData); - const [fetchLastExecuted, { loading: executedLoading }] = useLazyQuery< + const { data: lastExecutedTaskData, loading: executedLoading } = useQuery< LastMainlineCommitQuery, LastMainlineCommitQueryVariables >(LAST_MAINLINE_COMMIT, { - onCompleted: (data) => { - dispatch({ - type: "setLastExecutedTask", - task: getTaskFromMainlineCommitsQuery(data), - }); + skip: !parentTask || isFinishedTaskStatus(parentTask.status), + variables: { + projectIdentifier, + skipOrderNumber, + buildVariantOptions: { + ...bvOptionsBase, + statuses: finishedTaskStatuses, + }, }, onError: (err) => { dispatchToast.error( @@ -96,159 +100,83 @@ export const PreviousCommits: React.FC = ({ taskId }) => { ); }, }); + const lastExecutedTask = + getTaskFromMainlineCommitsQuery(lastExecutedTaskData); + + const linkObject = useMemo( + () => + getLinks({ + parentTask, + lastPassingTask, + lastExecutedTask, + }), + [parentTask, lastPassingTask, lastExecutedTask] + ); - const { baseTask, buildVariant, displayName, versionMetadata } = - taskData?.task ?? {}; - const { order: skipOrderNumber, projectIdentifier } = - versionMetadata?.baseVersion ?? {}; - const bvOptionsBase = { - tasks: [applyStrictRegex(displayName)], - variants: [applyStrictRegex(buildVariant)], - }; - const loading = parentLoading || passingLoading || executedLoading; + const menuDisabled = !baseTask || !parentTask; - // Hook to determine the parent task. If mainline commit, use fetchParentTask function to get task from - // previous mainline commit. Otherwise, just extract the base task from the task data. - useEffect(() => { - if (versionMetadata) { - if (!versionMetadata.isPatch) { - fetchParentTask({ - variables: { - projectIdentifier, - skipOrderNumber, - buildVariantOptions: { - ...bvOptionsBase, - }, - }, - }); - } else { - dispatch({ type: "setParentTask", task: baseTask }); + return menuDisabled ? ( + } + size={Size.Small} + > + Previous commits + } - } - }, [versionMetadata]); // eslint-disable-line react-hooks/exhaustive-deps - - // Hook that triggers fetching the last passing task if it needs to be fetched. - useEffect(() => { - if (!hasFetchedLastPassing && shouldFetchLastPassing) { - fetchLastPassing({ - variables: { - projectIdentifier, - skipOrderNumber, - buildVariantOptions: { - ...bvOptionsBase, - statuses: [TaskStatus.Succeeded], - }, - }, - }); - } - }, [shouldFetchLastPassing]); // eslint-disable-line react-hooks/exhaustive-deps - - // Hook that triggers fetching the last executed task if it needs to be fetched. - useEffect(() => { - if (!hasFetchedLastExecuted && shouldFetchLastExecuted) { - fetchLastExecuted({ - variables: { - projectIdentifier, - skipOrderNumber, - buildVariantOptions: { - ...bvOptionsBase, - statuses: finishedTaskStatuses, - }, - }, - }); - } - }, [shouldFetchLastExecuted]); // eslint-disable-line react-hooks/exhaustive-deps - - const Link = useLGButtonRouterLink(link); - - return ( - - - dispatch({ type: "setSelectState", selectState: v }) + > + No previous versions available. + + ) : ( + } size={Size.Small}> + Previous commits + + } + > + + sendEvent({ + name: "Submit Previous Commit Selector", + type: CommitType.Base, + }) } - value={selectState} - disabled={!versionMetadata?.baseVersion} + to={linkObject[CommitType.Base]} > - - - - - - ( - - {loading - ? `Fetching...` - : `There is no version that satisfies this criteria.`} - - )} + Go to {versionMetadata?.isPatch ? "base" : "previous"} commit + + + sendEvent({ + name: "Submit Previous Commit Selector", + type: CommitType.LastPassing, + }) + } + to={linkObject[CommitType.LastPassing]} > - - sendEvent({ - name: "Submit Previous Commit Selector", - type: selectState, - }) - } - size="small" - > - Go - - - + Go to last passing version + + + sendEvent({ + name: "Submit Previous Commit Selector", + type: CommitType.LastExecuted, + }) + } + to={linkObject[CommitType.LastExecuted]} + > + Go to last executed version + + ); }; - -// The return value from GetLastMainlineCommitQuery has a lot of nested fields that may or may -// not exist. The logic to extract the task from it is written in this function. -const getTaskFromMainlineCommitsQuery = ( - data: LastMainlineCommitQuery -): CommitTask => { - const buildVariants = - data?.mainlineCommits.versions.find(({ version }) => version)?.version - .buildVariants ?? []; - if (buildVariants.length > 1) { - reportError( - new Error("Multiple build variants matched previous commit search.") - ).warning(); - } - if (buildVariants[0]?.tasks.length > 1) { - reportError( - new Error("Multiple tasks matched previous commit search.") - ).warning(); - } - return buildVariants[0]?.tasks[0]; -}; - -const PreviousCommitsWrapper = styled.div` - display: flex; - align-items: flex-start; -`; - -// @ts-expect-error -const StyledSelect = styled(Select)` - width: 220px; - margin-right: ${size.xs}; - margin-top: -23px; -`; diff --git a/src/pages/task/actionButtons/previousCommits/reducer.ts b/src/pages/task/actionButtons/previousCommits/reducer.ts deleted file mode 100644 index 3c88dcec87..0000000000 --- a/src/pages/task/actionButtons/previousCommits/reducer.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { getTaskRoute } from "constants/routes"; -import { TaskStatus } from "types/task"; -import { statuses } from "utils"; -import { CommitTask, CommitType } from "./types"; - -const { isFinishedTaskStatus } = statuses; - -interface State { - parentTask: CommitTask; - lastPassingTask: CommitTask; - lastExecutedTask: CommitTask; - selectState: CommitType; - disableButton: boolean; - link: string; - shouldFetchLastPassing: boolean; - hasFetchedLastPassing: boolean; - shouldFetchLastExecuted: boolean; - hasFetchedLastExecuted: boolean; -} - -// a link cannot be null, so it's common to use # as a substitute. -const nullLink = "#"; - -// Note that the state doesn't include a field for shouldFetchParent / hasFetchedParent as the parent task -// is always fetched on render. -export const initialState: State = { - parentTask: null, - lastPassingTask: null, - lastExecutedTask: null, - selectState: CommitType.Base, - disableButton: true, - link: nullLink, - shouldFetchLastPassing: false, - hasFetchedLastPassing: false, - shouldFetchLastExecuted: false, - hasFetchedLastExecuted: false, -}; - -type Action = - | { type: "setParentTask"; task: CommitTask } - | { type: "setLastPassingTask"; task: CommitTask } - | { type: "setLastExecutedTask"; task: CommitTask } - | { type: "setSelectState"; selectState: CommitType }; - -export const reducer = (state: State, action: Action): State => { - const { - hasFetchedLastExecuted, - hasFetchedLastPassing, - lastExecutedTask, - lastPassingTask, - parentTask, - } = state; - - switch (action.type) { - case "setParentTask": { - if (action.task) { - return { - ...state, - parentTask: action.task, - link: getTaskRoute(action.task.id), - disableButton: false, - }; - } - return { - ...state, - disableButton: true, - }; - } - case "setLastPassingTask": { - if (action.task) { - return { - ...state, - lastPassingTask: action.task, - hasFetchedLastPassing: true, - shouldFetchLastPassing: false, - link: getTaskRoute(action.task.id), - disableButton: false, - }; - } - return { - ...state, - hasFetchedLastPassing: true, - shouldFetchLastPassing: false, - disableButton: true, - }; - } - case "setLastExecutedTask": { - if (action.task) { - return { - ...state, - lastExecutedTask: action.task, - hasFetchedLastExecuted: true, - shouldFetchLastExecuted: false, - link: getTaskRoute(action.task.id), - disableButton: false, - }; - } - return { - ...state, - hasFetchedLastExecuted: true, - shouldFetchLastExecuted: false, - disableButton: true, - }; - } - case "setSelectState": { - const newSelectState = action.selectState; - - // If the user has selected the Last Passing option, set shouldFetchPassing to true if the task needs - // to be fetched. This will trigger the query in the useEffect hook. - if (newSelectState === CommitType.LastPassing) { - const triggerFetchLastPassing = shouldFetchLastPassingTask( - parentTask, - lastPassingTask - ); - - if (!hasFetchedLastPassing && triggerFetchLastPassing) { - return { - ...state, - selectState: newSelectState, - shouldFetchLastPassing: true, - link: nullLink, - disableButton: true, - }; - } - } - - // If the user has selected the Last Executed option, set shouldFetchExecuted to true if the task - // needs to be fetched. This will trigger the query in the useEffect hook. - if (newSelectState === CommitType.LastExecuted) { - const triggerFetchLastExecuted = shouldFetchLastExecutedTask( - parentTask, - lastExecutedTask - ); - if (!hasFetchedLastExecuted && triggerFetchLastExecuted) { - return { - ...state, - selectState: newSelectState, - shouldFetchLastExecuted: true, - link: nullLink, - disableButton: true, - }; - } - } - - // If the selectState has changed and nothing needs to be fetched, just update the link and determine - // if the GO button should be disabled. - const newLink = determineNewLink( - parentTask, - lastPassingTask, - lastExecutedTask, - newSelectState - ); - - const lastPassingTaskDoesNotExist = - newSelectState === CommitType.LastPassing && - hasFetchedLastPassing && - !lastPassingTask; - - const lastExecutedTaskDoesNotExist = - newSelectState === CommitType.LastExecuted && - hasFetchedLastExecuted && - !lastExecutedTask; - - return { - ...state, - selectState: newSelectState, - link: newLink, - disableButton: - newLink === nullLink || - lastPassingTaskDoesNotExist || - lastExecutedTaskDoesNotExist, - }; - } - default: - throw new Error(`Unknown reducer action ${action}`); - } -}; - -const shouldFetchLastPassingTask = ( - parentTask: CommitTask, - lastPassingTask: CommitTask -): boolean => { - if ( - parentTask && - parentTask.status !== TaskStatus.Succeeded && - !lastPassingTask - ) { - return true; - } - return false; -}; - -const shouldFetchLastExecutedTask = ( - parentTask: CommitTask, - lastExecutedTask: CommitTask -): boolean => { - if ( - parentTask && - !isFinishedTaskStatus(parentTask.status) && - !lastExecutedTask - ) { - return true; - } - return false; -}; - -// Determine the value of the link which will be used to redirect the user when they -// press the GO button. -const determineNewLink = ( - parentTask: CommitTask, - lastPassingTask: CommitTask, - lastExecutedTask: CommitTask, - selectState: CommitType -): string => { - if (parentTask) { - switch (selectState) { - case CommitType.Base: - return getTaskRoute(parentTask.id); - // If a parent task succeeded, the last passing commit is the parent task. - case CommitType.LastPassing: - return getTaskRoute(lastPassingTask?.id || parentTask.id); - // If a parent task has finished, the last executed commit is the parent task. - case CommitType.LastExecuted: - return getTaskRoute(lastExecutedTask?.id || parentTask.id); - default: - break; - } - } - return nullLink; -}; diff --git a/src/pages/task/actionButtons/previousCommits/utils.ts b/src/pages/task/actionButtons/previousCommits/utils.ts new file mode 100644 index 0000000000..118c8facbd --- /dev/null +++ b/src/pages/task/actionButtons/previousCommits/utils.ts @@ -0,0 +1,56 @@ +import { getTaskRoute } from "constants/routes"; +import { LastMainlineCommitQuery } from "gql/generated/types"; +import { reportError } from "utils/errorReporting"; +import { CommitTask, CommitType } from "./types"; + +// a link cannot be null, so it's common to use # as a substitute. +const nullLink = "#"; + +export const getLinks = ({ + lastExecutedTask, + lastPassingTask, + parentTask, +}: { + lastExecutedTask: CommitTask; + lastPassingTask: CommitTask; + parentTask: CommitTask; +}) => { + if (!parentTask) { + return { + [CommitType.Base]: nullLink, + [CommitType.LastPassing]: nullLink, + [CommitType.LastExecuted]: nullLink, + }; + } + + return { + [CommitType.Base]: getTaskRoute(parentTask.id), + [CommitType.LastPassing]: getTaskRoute( + lastPassingTask?.id || parentTask.id + ), + [CommitType.LastExecuted]: getTaskRoute( + lastExecutedTask?.id || parentTask.id + ), + }; +}; + +// The return value from GetLastMainlineCommitQuery has a lot of nested fields that may or may +// not exist. The logic to extract the task from it is written in this function. +export const getTaskFromMainlineCommitsQuery = ( + data: LastMainlineCommitQuery +): CommitTask => { + const buildVariants = + data?.mainlineCommits.versions.find(({ version }) => version)?.version + .buildVariants ?? []; + if (buildVariants.length > 1) { + reportError( + new Error("Multiple build variants matched previous commit search.") + ).warning(); + } + if (buildVariants[0]?.tasks.length > 1) { + reportError( + new Error("Multiple tasks matched previous commit search.") + ).warning(); + } + return buildVariants[0]?.tasks[0]; +};