Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

DEVPROD-4608 Add stepback to the metadata of tasks #2299

Merged
merged 28 commits into from
Mar 21, 2024

Conversation

ZackarySantana
Copy link
Contributor

@ZackarySantana ZackarySantana commented Mar 12, 2024

DEVPROD-4608

Description

This adds the metadata item for stepback to show "In progress" or "completed" and an info icon accompanying it. I implemented according to the design except I added a "loading" state while the graphql query completes.

This also isolates and extracts the different relevant commit queries in to each their own hook: 'useParentTask', 'useLastPassingTask', 'useBreakingCommit', and 'useLastExecutedTask'. The first three are re-used (within each other, in the relevant commits dropdown, and the metadata card) while the last one is only used in the relevant commits dropdown- but it figured it would be better to extract and test that logic separately to be consistent (it removes bloat from the relevant commits dropdown component).

I separated my commits to be distinct so anyone who reviews can look over the commits for an easier time!

Screenshots

In progress

Screenshot 2024-03-12 at 12 01 31 PM

Completed

Screenshot 2024-03-12 at 12 02 29 PM

Hovering dropdown

Screenshot 2024-03-12 at 12 02 59 PM

Testing

I tested the buttons on multiple different tasks on staging- before and after this change. All the buttons respond the exact same- and as for the metadata item I also tested on staging making sure it shows the correct state.

I plan to add similar testing for all the new hooks if the reviewer thinks the tests for the hook I added is good! Please let me know- it shouldn't be too long to add them!

@ZackarySantana ZackarySantana self-assigned this Mar 12, 2024
Copy link

cypress bot commented Mar 12, 2024

5 flaky tests on run #16261 ↗︎

0 549 7 0 Flakiness 5
⚠️ You've recorded test results over your free plan limit.
Upgrade your plan to view test results.

Details:

feat: switch from tsx to ts
Project: Spruce Commit: 2f988c73b7
Status: Passed Duration: 21:04 💡
Started: Mar 19, 2024 6:07 PM Ended: Mar 19, 2024 6:28 PM

Review all test suite changes for PR #2299 ↗︎

@ZackarySantana ZackarySantana changed the title DEVPROD-4608 Add stepback to the metadata of tasks [not ready for review] DEVPROD-4608 Add stepback to the metadata of tasks Mar 18, 2024
@ZackarySantana ZackarySantana requested a review from a team March 18, 2024 13:56
@ZackarySantana
Copy link
Contributor Author

Could anyone on UI help me with the hooks tests? I defined the mocks and I'm using the renderHook with a wrapper that is a provider with the mocks- but it does not get any of the mocks.

Here's a quick view of it but it's available in the useBreakingTask hook and test file.

Test usage part:

   const { result } = renderHook(() => useBreakingTask("t1"), {
      wrapper: ({ children }) =>
        ProviderWrapper({
          children,
          mocks: [
            getPatchTaskWithFailingBaseTask,
            getLastPassingVersion,
            getBreakingCommit,
          ],
        }),
    });

The mock I'm providing:

const getPatchTaskWithFailingBaseTask: ApolloMock<
  BaseVersionAndTaskQuery,
  BaseVersionAndTaskQueryVariables
> = {
  request: {
    query: BASE_VERSION_AND_TASK,
    variables: {
      taskId: "t1",
    },
  },
  result: {
    data: {
      task: {
      ...

The query I'm expecting the mock to fill (but it prints undefined)

  const { data: taskData } = useQuery<
  BaseVersionAndTaskQuery,
  BaseVersionAndTaskQueryVariables
>(BASE_VERSION_AND_TASK, {
  variables: { taskId },
});

const { buildVariant, displayName, projectIdentifier, status } =
  taskData?.task ?? {};
console.log(taskId);
console.log(taskData?.task);

taskId properly puts out t1 but the taskData?.task is undefined.

Copy link
Contributor

@minnakt minnakt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test structure lgtm 😎 feel free to add the rest! and great reorganization with the hooks!

@@ -0,0 +1,180 @@
import { ProviderWrapper } from "components/HistoryTable/hooks/test-utils";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this ProviderWrapper is specifically for the HistoryTable component. You probably want to define your own ProviderWrapper in this file instead!

Comment on lines 37 to 38

expect(result.current.task).toBeDefined();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should be able to get this to pass if you wrap it in await waitFor(() => { // expect here })

You may also want to add an assertion like expect(result.current.task.id).toBe("breaking_commit"); after this one


expect(result.current.task).toBeDefined();
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(optional) another test case you may want to test is the error case — would require creating an additional GraphQL mock that returns an error. in this test, you can assert that the error toast is dispatched as expected

* It should only be used in favor of RenderFakeToastContext when the component
* requires children to be rendered.
*/
const MockToastContext = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, I think you can achieve the same effect in your tests with the existing function RenderFakeToastContext:

    const { dispatchToast } = RenderFakeToastContext();

    const { result } = renderHook(() => useBreakingTask("t1"), {
      wrapper: ({ children }) => ProviderWrapper({ children }),
    });

(see example hook test with toast mocks in Toast.test.tsx)

then you can make assertions in your test that dispatchToast is or is not called :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this for the ones that dispatch a toast notification but excluded it for those that don't!

Comment on lines 27 to 28
// nextStepbackTaskId is set when the next task in stepback in known, in the beginning
// of stepback, it is known right away. In the rest of stepback, it is not.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. there might be a few typos in this comment e.g. in stepback in known. in the beginning of... seems like it should be its own sentence as well

Comment on lines 50 to 64
<Tooltip
align="top"
justify="middle"
trigger={
<IconContainer>
<Icon glyph="InfoWithCircle" size="small" />
</IconContainer>
}
triggerEvent="hover"
>
<Body>
When Stepback is completed you can access the breaking commit via the
relevant commits dropdown.
</Body>
</Tooltip>{" "}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be able to replace this block with the InfoSprinkle component

relevant commits dropdown.
</Body>
</Tooltip>{" "}
{loading && <Badge variant="lightgray">Loading</Badge>}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having Loading in a badge makes it seem like it's a state of stepback, rather than an indication that we're still querying for a response. Perhaps a Skeleton Loader or Spinner could be a good replacement? (a skeleton loader is good for longer-term loading operations > 300ms, a spinner is good for shorter-term loading operations < 300ms)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to guage how fast this might take. I based it on relevant commits since it uses the same queries, and guessed on average it would be a longer operation so I did the skeleton loader for the badge!

@ZackarySantana
Copy link
Contributor Author

New loading state!:
Screenshot 2024-03-20 at 12 26 53 PM

Using new sprinkle, looks near identical:
Screenshot 2024-03-20 at 12 27 33 PM

Copy link
Contributor

@minnakt minnakt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added tests look great!

);

describe("useBreakingTask", () => {
it("no breaking task is found when task is not found", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. You can remove async since this test doesn't await anything (applies to other test files as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! I wonder why the linter didn't catch it...

* @param task The task to check if it is in stepback.
* @returns Whether the task is in stepback.
*/
export function inStepback(task: TaskQuery["task"]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. I'd maybe rename this function to something like isInStepback to more clearly indicate that it returns a boolean

Comment on lines 22 to 24
const stepback =
task?.stepbackInfo?.lastFailingStepbackTaskId !== undefined &&
task?.stepbackInfo?.lastFailingStepbackTaskId !== "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest a more boolean-y name, and I think you can simplify it to something like this:

Suggested change
const stepback =
task?.stepbackInfo?.lastFailingStepbackTaskId !== undefined &&
task?.stepbackInfo?.lastFailingStepbackTaskId !== "";
const hasLastStepback = task?.stepbackInfo?.lastFailingStepbackTaskId?.length > 0;

Comment on lines 29 to 31
const beginningStepback =
task?.stepbackInfo?.nextStepbackTaskId !== undefined &&
task?.stepbackInfo?.nextStepbackTaskId !== "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment!

Suggested change
const beginningStepback =
task?.stepbackInfo?.nextStepbackTaskId !== undefined &&
task?.stepbackInfo?.nextStepbackTaskId !== "";
const isBeginningStepback = task?.stepbackInfo?.nextStepbackTaskId?.length > 0;

Comment on lines 41 to 45
// The last stepback task has an undefined last passing task (it is passing itself).
const isLastStepbackTask = lastPassingTask === undefined;

// The stepback is finished if there is a breaking task or we are on the last stepback task.
const finished = breakingTask !== undefined || isLastStepbackTask;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to check lastPassingTask? I feel like we always expect there to be a breaking task as long as stepback is finished — if breaking task was not defined then users wouldn't be able to access it via the Relevant Commits dropdown as the tooltip instructs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are on the breaking task, stepback should still be counted as finished- the breaking task is when lastPassingTask === undefined is true. I'll redo the comment!

Comment on lines 48 to 57
<StepbackWrapper>
Stepback:
<InfoSprinkle baseFontSize={BaseFontSize.Body1}>
When Stepback is completed you can access the breaking commit via the
relevant commits dropdown.
</InfoSprinkle>
{loading && <Skeleton size="small" />}
{!loading && !finished && <Badge variant="lightgray">In progress</Badge>}
{!loading && finished && <Badge variant="green">Complete</Badge>}
</StepbackWrapper>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only reservation about this is that now this doesn't have the correct font styles from MetadataItem (if you look closely the font size is wrong). Perhaps we can change the underlying HTML of MetadataItem into a <div> and then rewrite it as the following:

Suggested change
<StepbackWrapper>
Stepback:
<InfoSprinkle baseFontSize={BaseFontSize.Body1}>
When Stepback is completed you can access the breaking commit via the
relevant commits dropdown.
</InfoSprinkle>
{loading && <Skeleton size="small" />}
{!loading && !finished && <Badge variant="lightgray">In progress</Badge>}
{!loading && finished && <Badge variant="green">Complete</Badge>}
</StepbackWrapper>
<MetadataItem>
<StepbackWrapper>
// content goes here
</StepbackWrapper>
</MetadataItem>

versionMetadata,
} = taskData?.task ?? {};
const { order: skipOrderNumber } = versionMetadata?.baseVersion ?? {};
const { baseTask, versionMetadata } = taskData?.task ?? {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that there doesn't seem to be a reason to perform the BaseVersionAndTaskQuery at all; it looks like the parent component of this component (ActionButtons.tsx) can pass these values down as props 🤔

@@ -254,7 +254,7 @@ export const ActionButtons: React.FC<Props> = ({
<PageButtonRow>
{!isExecutionTask && (
<>
<RelevantCommits taskId={taskId} />
<RelevantCommits taskId={taskId} task={task} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the taskId prop if it can be accessed from the task!

children,
"data-cy": dataCy,
}) => <Item data-cy={dataCy}>{children}</Item>;
}) => (
<Item data-cy={dataCy} as={as}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cool! The only issue with this is that the margin from :not(:last-of-type) selector doesn't quite work as intended anymore due to the mix of <p> and <div> tags.

Maybe we can change :not(:last-of-type) -> :not(:last-child)

};

const StepbackWrapper = styled.div`
margin-top: ${size.xxs};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can remove this margin-top style if you get the margin from the style change mentioned in the previous comment

Comment on lines 29 to 31
const isBeginningStepback =
task?.stepbackInfo?.nextStepbackTaskId !== undefined &&
task?.stepbackInfo?.nextStepbackTaskId !== "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be rewritten with length as well, or did you run into a problem? 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope! Just got confused but I updated it to use the length trick too

Copy link
Contributor

@minnakt minnakt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great work!

Comment on lines 391 to 393
data-cy="t
task: patchTaskWithSuccessfulBaseTask,
ask-metrics-link"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this was an unintentional change

When Stepback is completed you can access the breaking commit via the
relevant commits dropdown.
</InfoSprinkle>
{loading && <Skeleton size="small" />}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. loading state looks like it might need a small style tweak

Screenshot 2024-03-21 at 12 52 29 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-03-21 at 2 01 00 PM

Added no wrap and it looks like this now!

@ZackarySantana ZackarySantana merged commit fdf7d25 into evergreen-ci:main Mar 21, 2024
4 checks passed
@ZackarySantana ZackarySantana deleted the DEVPROD-4608 branch March 21, 2024 19:53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants