Skip to content

Commit

Permalink
Merge pull request #16 from chromaui/ghengeveld/ap-3351-trigger-appro…
Browse files Browse the repository at this point in the history
…priate-mutation-when-clicking-accept-or-batch

Implement accept and batch accept actions using `reviewTest` mutation
  • Loading branch information
ghengeveld committed Jul 20, 2023
2 parents 1175a32 + 656ef5d commit d0c744e
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/Tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const Tool = () => {
onClick={runDevBuild}
>
{state.isRunning ? (
<ProgressIcon onButton />
<ProgressIcon parentComponent="IconButton" style={{ marginRight: 6 }} />
) : (
<Icons icon="play" style={{ marginRight: 6 }} />
)}
Expand Down
8 changes: 4 additions & 4 deletions src/components/icons/ProgressIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { css, styled } from "@storybook/theming";

const { rotate360 } = animation;

export const ProgressIcon = styled.div<{ onButton?: boolean }>(
export const ProgressIcon = styled.div<{ parentComponent?: "Button" | "IconButton" }>(
({ theme }) => ({
width: 12,
height: 12,
Expand All @@ -20,10 +20,10 @@ export const ProgressIcon = styled.div<{ onButton?: boolean }>(
cursor: "progress",
transform: "translate3d(0, 0, 0)",
}),
({ onButton }) =>
onButton &&
({ parentComponent }) =>
parentComponent &&
css({
margin: "0 6px 0 0",
margin: parentComponent === "IconButton" ? 1 : 0,
borderWidth: 1,
borderLeftColor: "currentcolor",
borderBottomColor: "currentcolor",
Expand Down
5 changes: 5 additions & 0 deletions src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const documents = {
"\n query Build($hasBuildId: Boolean!, $buildId: ID!, $projectId: ID!, $branch: String!) {\n build(id: $buildId) @include(if: $hasBuildId) {\n ...BuildFields\n }\n project(id: $projectId) @skip(if: $hasBuildId) {\n name\n lastBuild(branches: [$branch]) {\n ...BuildFields\n }\n }\n }\n": types.BuildDocument,
"\n fragment BuildFields on Build {\n __typename\n id\n number\n branch\n commit\n status\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n startedAt\n tests {\n nodes {\n ...TestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n startedAt\n tests {\n nodes {\n ...TestFields\n }\n }\n }\n }\n": types.BuildFieldsFragmentDoc,
"\n fragment TestFields on Test {\n id\n status\n result\n webUrl\n comparisons {\n id\n result\n browser {\n id\n key\n name\n version\n }\n captureDiff {\n diffImage {\n imageUrl\n }\n }\n headCapture {\n captureImage {\n imageUrl\n }\n }\n viewport {\n id\n name\n width\n isDefault\n }\n }\n parameters {\n viewport {\n id\n name\n width\n isDefault\n }\n }\n story {\n storyId\n }\n }\n": types.TestFieldsFragmentDoc,
"\n mutation ReviewTest($input: ReviewTestInput!) {\n reviewTest(input: $input) {\n updatedTests {\n id\n status\n }\n userErrors {\n ... on UserError {\n __typename\n message\n }\n ... on BuildSupersededError {\n build {\n id\n }\n }\n ... on TestUnreviewableError {\n test {\n id\n }\n }\n }\n }\n }\n": types.ReviewTestDocument,
};

/**
Expand Down Expand Up @@ -54,6 +55,10 @@ export function graphql(source: "\n fragment BuildFields on Build {\n __type
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment TestFields on Test {\n id\n status\n result\n webUrl\n comparisons {\n id\n result\n browser {\n id\n key\n name\n version\n }\n captureDiff {\n diffImage {\n imageUrl\n }\n }\n headCapture {\n captureImage {\n imageUrl\n }\n }\n viewport {\n id\n name\n width\n isDefault\n }\n }\n parameters {\n viewport {\n id\n name\n width\n isDefault\n }\n }\n story {\n storyId\n }\n }\n"): (typeof documents)["\n fragment TestFields on Test {\n id\n status\n result\n webUrl\n comparisons {\n id\n result\n browser {\n id\n key\n name\n version\n }\n captureDiff {\n diffImage {\n imageUrl\n }\n }\n headCapture {\n captureImage {\n imageUrl\n }\n }\n viewport {\n id\n name\n width\n isDefault\n }\n }\n parameters {\n viewport {\n id\n name\n width\n isDefault\n }\n }\n story {\n storyId\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation ReviewTest($input: ReviewTestInput!) {\n reviewTest(input: $input) {\n updatedTests {\n id\n status\n }\n userErrors {\n ... on UserError {\n __typename\n message\n }\n ... on BuildSupersededError {\n build {\n id\n }\n }\n ... on TestUnreviewableError {\n test {\n id\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation ReviewTest($input: ReviewTestInput!) {\n reviewTest(input: $input) {\n updatedTests {\n id\n status\n }\n userErrors {\n ... on UserError {\n __typename\n message\n }\n ... on BuildSupersededError {\n build {\n id\n }\n }\n ... on TestUnreviewableError {\n test {\n id\n }\n }\n }\n }\n }\n"];

export function graphql(source: string) {
return (documents as any)[source] ?? {};
Expand Down
10 changes: 9 additions & 1 deletion src/gql/graphql.ts

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions src/screens/VisualTests/BuildInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,23 @@ export const BuildInfo = ({
{isOutdated && (
<Col push>
<Button small secondary onClick={runDevBuild} disabled={isRunning}>
{isRunning ? <ProgressIcon onButton /> : <Icons icon="play" />}
{isRunning ? (
<ProgressIcon parentComponent="Button" style={{ marginRight: 6 }} />
) : (
<Icons icon="play" />
)}
Run tests
</Button>
</Col>
)}
{status === BuildStatus.Failed && (
<Col push>
<Button small secondary onClick={runDevBuild} disabled={isRunning}>
{isRunning ? <ProgressIcon onButton /> : <Icons icon="play" />}
{isRunning ? (
<ProgressIcon parentComponent="Button" style={{ marginRight: 6 }} />
) : (
<Icons icon="play" />
)}
Rerun tests
</Button>
</Col>
Expand Down
46 changes: 41 additions & 5 deletions src/screens/VisualTests/SnapshotComparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import React, { ComponentProps, useState } from "react";

import { BrowserSelector } from "../../components/BrowserSelector";
import { IconButton } from "../../components/IconButton";
import { ProgressIcon } from "../../components/icons/ProgressIcon";
import { Bar, Col } from "../../components/layout";
import { Placeholder } from "../../components/Placeholder";
import { SnapshotImage } from "../../components/SnapshotImage";
import { TooltipMenu } from "../../components/TooltipMenu";
import { ViewportSelector } from "../../components/ViewportSelector";
import { ComparisonResult, TestFieldsFragment } from "../../gql/graphql";
import { ComparisonResult, ReviewTestBatch, TestFieldsFragment } from "../../gql/graphql";

const Divider = styled.div(({ theme }) => ({
backgroundColor: theme.appBorderColor,
Expand All @@ -19,17 +21,21 @@ const Divider = styled.div(({ theme }) => ({
interface SnapshotSectionProps {
test?: TestFieldsFragment;
changeCount: number;
isAccepting: boolean;
isInProgress: boolean;
browserResults: ComponentProps<typeof BrowserSelector>["browserResults"];
viewportResults: ComponentProps<typeof ViewportSelector>["viewportResults"];
onAccept: (testId: TestFieldsFragment["id"], batch?: ReviewTestBatch) => void;
}

export const SnapshotComparison = ({
test,
changeCount,
isAccepting,
isInProgress,
browserResults,
viewportResults,
onAccept,
}: SnapshotSectionProps) => {
const [diffVisible, setDiffVisible] = useState(true);

Expand Down Expand Up @@ -76,12 +82,42 @@ export const SnapshotComparison = ({
{changeCount > 0 && (
<>
<Col push>
<IconButton secondary>Accept</IconButton>
<IconButton secondary onClick={() => onAccept(test.id)}>
Accept
</IconButton>
</Col>
<Col>
<IconButton secondary>
<Icons icon="batchaccept" />
</IconButton>
<TooltipMenu
placement="bottom"
links={[
{
id: "logout",
title: "Accept all viewports",
center: "Accept all unreviewed changes to this story",
onClick: () => onAccept(test.id, ReviewTestBatch.Spec),
disabled: isAccepting,
loading: isAccepting,
},
{
id: "learn",
title: "Accept this component",
center: "Accept all unreviewed changes for this component",
onClick: () => onAccept(test.id, ReviewTestBatch.Component),
disabled: isAccepting,
loading: isAccepting,
},
]}
>
{(active) => (
<IconButton secondary active={active}>
{isAccepting ? (
<ProgressIcon parentComponent="IconButton" />
) : (
<Icons icon="batchaccept" />
)}
</IconButton>
)}
</TooltipMenu>
</Col>
</>
)}
Expand Down
26 changes: 26 additions & 0 deletions src/screens/VisualTests/VisualTests.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ const withGraphQLQuery = (...args: Parameters<typeof graphql.query>) => ({
},
});

const withGraphQLMutation = (...args: Parameters<typeof graphql.mutation>) => ({
msw: {
handlers: [graphql.mutation(...args)],
},
});

const withBuild = (build: AnnouncedBuild | PublishedBuild | StartedBuild | CompletedBuild) =>
withGraphQLQuery("Build", (req, res, ctx) => res(ctx.data({ build } as BuildQuery)));

Expand Down Expand Up @@ -338,6 +344,26 @@ export const Pending: Story = {
},
};

export const Accepting: Story = {
parameters: {
msw: {
handlers: [
...withBuild(pendingBuild).msw.handlers,
...withGraphQLMutation("ReviewTest", (req, res, ctx) =>
res(ctx.status(200), ctx.data({}), ctx.delay("infinite"))
).msw.handlers,
],
},
...withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=508-304718&t=0rxMQnkxsVpVj1qy-4"
),
},
play: playAll(async ({ canvasElement }) => {
const button = await findByRole(canvasElement, "button", { name: "Accept" });
await fireEvent.click(button);
}),
};

export const Accepted: Story = {
parameters: {
...withBuild(acceptedBuild),
Expand Down
43 changes: 41 additions & 2 deletions src/screens/VisualTests/VisualTests.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Loader } from "@storybook/components";
import { Icon } from "@storybook/design-system";
import React, { useEffect, useState } from "react";
import { useQuery } from "urql";
import React, { useCallback, useEffect, useState } from "react";
import { useMutation, useQuery } from "urql";

import { IconButton } from "../../components/IconButton";
import { Bar, Col, Row, Section, Sections, Text } from "../../components/layout";
Expand All @@ -10,6 +10,8 @@ import { getFragment, graphql } from "../../gql";
import {
BuildQuery,
BuildQueryVariables,
ReviewTestBatch,
ReviewTestInputStatus,
TestFieldsFragment,
TestResult,
TestStatus,
Expand Down Expand Up @@ -117,6 +119,33 @@ const FragmentTestFields = graphql(/* GraphQL */ `
}
`);

const MutationReviewTest = graphql(/* GraphQL */ `
mutation ReviewTest($input: ReviewTestInput!) {
reviewTest(input: $input) {
updatedTests {
id
status
}
userErrors {
... on UserError {
__typename
message
}
... on BuildSupersededError {
build {
id
}
}
... on TestUnreviewableError {
test {
id
}
}
}
}
}
`);

interface VisualTestsProps {
projectId: string;
branch?: string;
Expand Down Expand Up @@ -156,6 +185,14 @@ export const VisualTests = ({
},
});

const [{ fetching: isAccepting }, reviewTest] = useMutation(MutationReviewTest);

const onAccept = useCallback(
(testId: string, batch: ReviewTestBatch) =>
reviewTest({ input: { testId, status: ReviewTestInputStatus.Accepted, batch } }),
[reviewTest]
);

useEffect(() => {
if (isRunning && data?.build && "result" in data.build) {
setIsOutdated(false);
Expand Down Expand Up @@ -309,10 +346,12 @@ export const VisualTests = ({
{...{
test,
changeCount,
isAccepting,
isInProgress,
isOutdated,
browserResults,
viewportResults,
onAccept,
}}
/>
</Section>
Expand Down

0 comments on commit d0c744e

Please sign in to comment.