diff --git a/package.json b/package.json index dc89be6f..e151c8d4 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "dependencies": { "@storybook/csf-tools": "7.4.0", "@storybook/design-system": "^7.15.15", - "chromatic": "6.24.0", + "chromatic": "7.1.0-canary.3", "date-fns": "^2.30.0", "pluralize": "^8.0.0", "ts-dedent": "^2.2.0", diff --git a/src/Panel.tsx b/src/Panel.tsx index be1e6f12..81e69258 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -1,18 +1,16 @@ -import { logger } from "@storybook/client-logger"; +import { Spinner } from "@storybook/design-system"; import type { API } from "@storybook/manager-api"; import { useChannel, useStorybookState } from "@storybook/manager-api"; -// eslint-disable-next-line import/no-unresolved -import { GitInfo } from "chromatic/node"; import React, { useCallback, useState } from "react"; import { ADDON_ID, - BUILD_ANNOUNCED, - BUILD_STARTED, DEV_BUILD_ID_KEY, GIT_INFO, GitInfoPayload, PANEL_ID, + RUNNING_BUILD, + RunningBuildPayload, START_BUILD, } from "./constants"; import { Authentication } from "./screens/Authentication/Authentication"; @@ -20,6 +18,7 @@ import { LinkedProject } from "./screens/LinkProject/LinkedProject"; import { LinkingProjectFailed } from "./screens/LinkProject/LinkingProjectFailed"; import { LinkProject } from "./screens/LinkProject/LinkProject"; import { VisualTests } from "./screens/VisualTests/VisualTests"; +import { useAddonState } from "./useAddonState/manager"; import { client, Provider, useAccessToken } from "./utils/graphQLClient"; import { StatusUpdate } from "./utils/testsToStatusUpdate"; import { useProjectId } from "./utils/useProjectId"; @@ -29,50 +28,15 @@ interface PanelProps { api: API; } -const { - GIT_USER_EMAIL, - GIT_USER_EMAIL_HASH, - GIT_BRANCH, - GIT_SLUG, - GIT_COMMIT, - GIT_UNCOMMITTED_HASH, -} = process.env; -const initialGitInfo: GitInfoPayload = { - userEmail: GIT_USER_EMAIL, - userEmailHash: GIT_USER_EMAIL_HASH, - branch: GIT_BRANCH, - commit: GIT_COMMIT, - slug: GIT_SLUG, - uncommittedHash: GIT_UNCOMMITTED_HASH, -}; - -logger.debug("Initial Git info:", initialGitInfo); - const storedBuildId = localStorage.getItem(DEV_BUILD_ID_KEY); export const Panel = ({ active, api }: PanelProps) => { const [accessToken, setAccessToken] = useAccessToken(); const { storyId } = useStorybookState(); - const [isStarting, setIsStarting] = useState(false); - const [lastBuildId, setLastBuildId] = useState(storedBuildId); - const [gitInfo, setGitInfo] = useState(initialGitInfo); - - const emit = useChannel( - { - [START_BUILD]: () => setIsStarting(true), - [BUILD_STARTED]: () => setIsStarting(false), - [BUILD_ANNOUNCED]: (buildId: string) => { - setLastBuildId(buildId); - localStorage.setItem(DEV_BUILD_ID_KEY, buildId); - }, - [GIT_INFO]: (info: GitInfoPayload) => { - setGitInfo(info); - logger.debug("Updated Git info:", info); - }, - }, - [] - ); + const [gitInfo] = useAddonState(GIT_INFO); + const [runningBuild] = useAddonState(RUNNING_BUILD); + const emit = useChannel({}); const updateBuildStatus = useCallback( (update: StatusUpdate) => { @@ -81,6 +45,7 @@ export const Panel = ({ active, api }: PanelProps) => { [api] ); const { + loading: projectInfoLoading, projectId, projectToken, configDir, @@ -98,6 +63,11 @@ export const Panel = ({ active, api }: PanelProps) => { // Render the Authentication flow if the user is not signed in. if (!accessToken) return ; + // Momentarily wait on addonState (should be very fast) + if (projectInfoLoading || !gitInfo) { + return ; + } + if (!projectId) return ( @@ -128,14 +98,14 @@ export const Panel = ({ active, api }: PanelProps) => { ); } + return ( isStarting || emit(START_BUILD)} + runningBuild={runningBuild} + startDevBuild={() => emit(START_BUILD)} setAccessToken={setAccessToken} updateBuildStatus={updateBuildStatus} storyId={storyId} diff --git a/src/Tool.tsx b/src/Tool.tsx index 8a75c16c..cbbded31 100644 --- a/src/Tool.tsx +++ b/src/Tool.tsx @@ -2,27 +2,20 @@ import { useChannel } from "@storybook/manager-api"; import React, { useState } from "react"; import { RunTestsButton } from "./components/RunTestsButton"; -import { BUILD_STARTED, START_BUILD, TOOL_ID } from "./constants"; +import { RUNNING_BUILD, RunningBuildPayload, START_BUILD, TOOL_ID } from "./constants"; +import { useAddonState } from "./useAddonState/manager"; import { useAccessToken } from "./utils/graphQLClient"; import { useProjectId } from "./utils/useProjectId"; export const Tool = () => { - const [isStarting, setIsStarting] = useState(false); const { projectId } = useProjectId(); const [accessToken] = useAccessToken(); const isLoggedIn = !!accessToken; - const emit = useChannel( - { - [START_BUILD]: () => setIsStarting(true), - [BUILD_STARTED]: () => setIsStarting(false), - }, - [] - ); - - const startBuild = () => { - emit(START_BUILD); - }; + const [runningBuild] = useAddonState(RUNNING_BUILD); + const emit = useChannel({}); + const startBuild = () => emit(START_BUILD); + const isStarting = ["initializing"].includes(runningBuild?.step); return ; }; diff --git a/src/constants.ts b/src/constants.ts index e520ee54..ca545ef2 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,4 @@ -import type { GitInfo } from "chromatic/node"; +import type { GitInfo, TaskName } from "chromatic/node"; export const { CHROMATIC_INDEX_URL, @@ -15,24 +15,34 @@ export const ACCESS_TOKEN_KEY = `${ADDON_ID}/access-token/${CHROMATIC_BASE_URL}` export const DEV_BUILD_ID_KEY = `${ADDON_ID}/dev-build-id`; export const GIT_INFO = `${ADDON_ID}/gitInfo`; -export type GitInfoPayload = Omit; -export const START_BUILD = `${ADDON_ID}/startBuild`; -export const BUILD_ANNOUNCED = `${ADDON_ID}/buildAnnounced`; -export const BUILD_STARTED = `${ADDON_ID}/buildStarted`; +export type GitInfoPayload = Omit; -export const UPDATE_PROJECT = `${ADDON_ID}/updateProject`; -export type UpdateProjectPayload = { - projectId: string; - projectToken: string; +export const PROJECT_INFO = `${ADDON_ID}/projectInfo`; +export type ProjectInfoPayload = { + projectId?: string; + projectToken?: string; + written?: boolean; + configDir?: string; + mainPath?: string; }; -export const PROJECT_UPDATED = `${ADDON_ID}/projectUpdated`; -export type ProjectUpdatedPayload = { - mainPath: string; - configDir: string; -}; -export const PROJECT_UPDATING_FAILED = `${ADDON_ID}/projectUpdatingFailed`; -export type ProjectUpdatingFailedPayload = { - mainPath?: string; - configDir: string; +// The CLI may have other steps that we don't respond to, so we just ignore updates +// to those steps and focus on the ones we know. +type KnownTask = Extract; +export function isKnownTask(task: TaskName): task is KnownTask { + return ["initialize", "build", "upload", "verify", "snapshot"].includes(task); +} + +export const START_BUILD = `${ADDON_ID}/startBuild`; +export const RUNNING_BUILD = `${ADDON_ID}/runningBuild`; +export type RunningBuildPayload = { + // Possibly this should be a type exported by the CLI -- these correspond to tasks + /** The step of the build process we have reached */ + step: KnownTask | "complete"; + /** The id of the build, available after the initialize step */ + id?: string; + /** progress pertains to the current step, and may not be set */ + progress?: number; + /** total pertains to the current step, and may not be set */ + total?: number; }; diff --git a/src/gql/gql.ts b/src/gql/gql.ts index 2b1de85c..71636e37 100644 --- a/src/gql/gql.ts +++ b/src/gql/gql.ts @@ -15,8 +15,9 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ const documents = { "\n query SelectProjectsQuery {\n viewer {\n accounts {\n id\n name\n avatarUrl\n projects {\n id\n name\n webUrl\n projectToken\n lastBuild {\n branch\n number\n }\n }\n }\n }\n }\n": types.SelectProjectsQueryDocument, "\n query ProjectQuery($projectId: ID!) {\n project(id: $projectId) {\n id\n name\n webUrl\n lastBuild {\n branch\n number\n }\n }\n }\n": types.ProjectQueryDocument, - "\n query AddonVisualTestsBuild(\n $projectId: ID!\n $branch: String!\n $gitUserEmailHash: String!\n $slug: String\n $storyId: String!\n $testStatuses: [TestStatus!]!\n ) {\n project(id: $projectId) {\n name\n lastBuild(\n branches: [$branch]\n slug: $slug\n localBuilds: { localBuildEmailHash: $gitUserEmailHash }\n ) {\n ...BuildFields\n }\n }\n }\n": types.AddonVisualTestsBuildDocument, - "\n fragment BuildFields on Build {\n __typename\n id\n number\n branch\n commit\n uncommittedHash\n status\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n startedAt\n testsForStatus: tests(first: 1000, statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n startedAt\n testsForStatus: tests(statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n }\n": types.BuildFieldsFragmentDoc, + "\n query AddonVisualTestsBuild(\n $projectId: ID!\n $branch: String!\n $gitUserEmailHash: String!\n $slug: String\n $storyId: String!\n $testStatuses: [TestStatus!]!\n $storyBuildId: ID!\n $hasStoryBuildId: Boolean!\n ) {\n project(id: $projectId) {\n name\n lastBuild(\n branches: [$branch]\n slug: $slug\n localBuilds: { localBuildEmailHash: $gitUserEmailHash }\n ) {\n ...NextBuildFields\n ...StoryBuildFields @skip(if: $hasStoryBuildId)\n }\n }\n storyBuild: build(id: $storyBuildId) @include(if: $hasStoryBuildId) {\n ...StoryBuildFields\n }\n }\n": types.AddonVisualTestsBuildDocument, + "\n fragment NextBuildFields on Build {\n __typename\n id\n commit\n committedAt\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n testsForStatus: tests(first: 1000, statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n testsForStatus: tests(statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n }\n }\n": types.NextBuildFieldsFragmentDoc, + "\n fragment StoryBuildFields on Build {\n __typename\n id\n number\n branch\n uncommittedHash\n status\n ... on StartedBuild {\n startedAt\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n ... on CompletedBuild {\n startedAt\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n }\n": types.StoryBuildFieldsFragmentDoc, "\n fragment StatusTestFields on Test {\n id\n status\n story {\n storyId\n }\n }\n": types.StatusTestFieldsFragmentDoc, "\n fragment StoryTestFields 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 imageWidth\n }\n }\n headCapture {\n captureImage {\n imageUrl\n imageWidth\n }\n }\n baseCapture {\n captureImage {\n imageUrl\n imageWidth\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 name\n component {\n name\n }\n }\n }\n": types.StoryTestFieldsFragmentDoc, "\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, @@ -47,11 +48,15 @@ export function graphql(source: "\n query ProjectQuery($projectId: ID!) {\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 query AddonVisualTestsBuild(\n $projectId: ID!\n $branch: String!\n $gitUserEmailHash: String!\n $slug: String\n $storyId: String!\n $testStatuses: [TestStatus!]!\n ) {\n project(id: $projectId) {\n name\n lastBuild(\n branches: [$branch]\n slug: $slug\n localBuilds: { localBuildEmailHash: $gitUserEmailHash }\n ) {\n ...BuildFields\n }\n }\n }\n"): (typeof documents)["\n query AddonVisualTestsBuild(\n $projectId: ID!\n $branch: String!\n $gitUserEmailHash: String!\n $slug: String\n $storyId: String!\n $testStatuses: [TestStatus!]!\n ) {\n project(id: $projectId) {\n name\n lastBuild(\n branches: [$branch]\n slug: $slug\n localBuilds: { localBuildEmailHash: $gitUserEmailHash }\n ) {\n ...BuildFields\n }\n }\n }\n"]; +export function graphql(source: "\n query AddonVisualTestsBuild(\n $projectId: ID!\n $branch: String!\n $gitUserEmailHash: String!\n $slug: String\n $storyId: String!\n $testStatuses: [TestStatus!]!\n $storyBuildId: ID!\n $hasStoryBuildId: Boolean!\n ) {\n project(id: $projectId) {\n name\n lastBuild(\n branches: [$branch]\n slug: $slug\n localBuilds: { localBuildEmailHash: $gitUserEmailHash }\n ) {\n ...NextBuildFields\n ...StoryBuildFields @skip(if: $hasStoryBuildId)\n }\n }\n storyBuild: build(id: $storyBuildId) @include(if: $hasStoryBuildId) {\n ...StoryBuildFields\n }\n }\n"): (typeof documents)["\n query AddonVisualTestsBuild(\n $projectId: ID!\n $branch: String!\n $gitUserEmailHash: String!\n $slug: String\n $storyId: String!\n $testStatuses: [TestStatus!]!\n $storyBuildId: ID!\n $hasStoryBuildId: Boolean!\n ) {\n project(id: $projectId) {\n name\n lastBuild(\n branches: [$branch]\n slug: $slug\n localBuilds: { localBuildEmailHash: $gitUserEmailHash }\n ) {\n ...NextBuildFields\n ...StoryBuildFields @skip(if: $hasStoryBuildId)\n }\n }\n storyBuild: build(id: $storyBuildId) @include(if: $hasStoryBuildId) {\n ...StoryBuildFields\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 fragment BuildFields on Build {\n __typename\n id\n number\n branch\n commit\n uncommittedHash\n status\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n startedAt\n testsForStatus: tests(first: 1000, statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n startedAt\n testsForStatus: tests(statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n }\n"): (typeof documents)["\n fragment BuildFields on Build {\n __typename\n id\n number\n branch\n commit\n uncommittedHash\n status\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n startedAt\n testsForStatus: tests(first: 1000, statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n startedAt\n testsForStatus: tests(statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n }\n"]; +export function graphql(source: "\n fragment NextBuildFields on Build {\n __typename\n id\n commit\n committedAt\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n testsForStatus: tests(first: 1000, statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n testsForStatus: tests(statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n }\n }\n"): (typeof documents)["\n fragment NextBuildFields on Build {\n __typename\n id\n commit\n committedAt\n browsers {\n id\n key\n name\n }\n ... on StartedBuild {\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n testsForStatus: tests(first: 1000, statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\n }\n ... on CompletedBuild {\n result\n changeCount: testCount(results: [ADDED, CHANGED, FIXED])\n brokenCount: testCount(results: [CAPTURE_ERROR])\n testsForStatus: tests(statuses: $testStatuses) {\n nodes {\n ...StatusTestFields\n }\n }\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 fragment StoryBuildFields on Build {\n __typename\n id\n number\n branch\n uncommittedHash\n status\n ... on StartedBuild {\n startedAt\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n ... on CompletedBuild {\n startedAt\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n }\n"): (typeof documents)["\n fragment StoryBuildFields on Build {\n __typename\n id\n number\n branch\n uncommittedHash\n status\n ... on StartedBuild {\n startedAt\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n ... on CompletedBuild {\n startedAt\n testsForStory: tests(storyId: $storyId) {\n nodes {\n ...StoryTestFields\n }\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/src/gql/graphql.ts b/src/gql/graphql.ts index cd84cc3f..ce19f574 100644 --- a/src/gql/graphql.ts +++ b/src/gql/graphql.ts @@ -1261,49 +1261,78 @@ export type AddonVisualTestsBuildQueryVariables = Exact<{ slug?: InputMaybe; storyId: Scalars['String']['input']; testStatuses: Array | TestStatus; + storyBuildId: Scalars['ID']['input']; + hasStoryBuildId: Scalars['Boolean']['input']; }>; export type AddonVisualTestsBuildQuery = { __typename?: 'Query', project?: { __typename?: 'Project', name: string, lastBuild?: ( { __typename?: 'AnnouncedBuild' } - & { ' $fragmentRefs'?: { 'BuildFields_AnnouncedBuild_Fragment': BuildFields_AnnouncedBuild_Fragment } } + & { ' $fragmentRefs'?: { 'NextBuildFields_AnnouncedBuild_Fragment': NextBuildFields_AnnouncedBuild_Fragment;'StoryBuildFields_AnnouncedBuild_Fragment': StoryBuildFields_AnnouncedBuild_Fragment } } ) | ( { __typename?: 'CompletedBuild' } - & { ' $fragmentRefs'?: { 'BuildFields_CompletedBuild_Fragment': BuildFields_CompletedBuild_Fragment } } + & { ' $fragmentRefs'?: { 'NextBuildFields_CompletedBuild_Fragment': NextBuildFields_CompletedBuild_Fragment;'StoryBuildFields_CompletedBuild_Fragment': StoryBuildFields_CompletedBuild_Fragment } } ) | ( { __typename?: 'PreparedBuild' } - & { ' $fragmentRefs'?: { 'BuildFields_PreparedBuild_Fragment': BuildFields_PreparedBuild_Fragment } } + & { ' $fragmentRefs'?: { 'NextBuildFields_PreparedBuild_Fragment': NextBuildFields_PreparedBuild_Fragment;'StoryBuildFields_PreparedBuild_Fragment': StoryBuildFields_PreparedBuild_Fragment } } ) | ( { __typename?: 'PublishedBuild' } - & { ' $fragmentRefs'?: { 'BuildFields_PublishedBuild_Fragment': BuildFields_PublishedBuild_Fragment } } + & { ' $fragmentRefs'?: { 'NextBuildFields_PublishedBuild_Fragment': NextBuildFields_PublishedBuild_Fragment;'StoryBuildFields_PublishedBuild_Fragment': StoryBuildFields_PublishedBuild_Fragment } } ) | ( { __typename?: 'StartedBuild' } - & { ' $fragmentRefs'?: { 'BuildFields_StartedBuild_Fragment': BuildFields_StartedBuild_Fragment } } - ) | null } | null }; + & { ' $fragmentRefs'?: { 'NextBuildFields_StartedBuild_Fragment': NextBuildFields_StartedBuild_Fragment;'StoryBuildFields_StartedBuild_Fragment': StoryBuildFields_StartedBuild_Fragment } } + ) | null } | null, storyBuild?: ( + { __typename?: 'AnnouncedBuild' } + & { ' $fragmentRefs'?: { 'StoryBuildFields_AnnouncedBuild_Fragment': StoryBuildFields_AnnouncedBuild_Fragment } } + ) | ( + { __typename?: 'CompletedBuild' } + & { ' $fragmentRefs'?: { 'StoryBuildFields_CompletedBuild_Fragment': StoryBuildFields_CompletedBuild_Fragment } } + ) | ( + { __typename?: 'PreparedBuild' } + & { ' $fragmentRefs'?: { 'StoryBuildFields_PreparedBuild_Fragment': StoryBuildFields_PreparedBuild_Fragment } } + ) | ( + { __typename?: 'PublishedBuild' } + & { ' $fragmentRefs'?: { 'StoryBuildFields_PublishedBuild_Fragment': StoryBuildFields_PublishedBuild_Fragment } } + ) | ( + { __typename?: 'StartedBuild' } + & { ' $fragmentRefs'?: { 'StoryBuildFields_StartedBuild_Fragment': StoryBuildFields_StartedBuild_Fragment } } + ) | null }; + +type NextBuildFields_AnnouncedBuild_Fragment = { __typename: 'AnnouncedBuild', id: string, commit: string, committedAt: any, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'NextBuildFields_AnnouncedBuild_Fragment' }; + +type NextBuildFields_CompletedBuild_Fragment = { __typename: 'CompletedBuild', result: BuildResult, id: string, commit: string, committedAt: any, changeCount: number, brokenCount: number, testsForStatus?: { __typename?: 'CompletedBuildTestConnection', nodes: Array<( + { __typename?: 'Test' } + & { ' $fragmentRefs'?: { 'StatusTestFieldsFragment': StatusTestFieldsFragment } } + )> } | null, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'NextBuildFields_CompletedBuild_Fragment' }; -type BuildFields_AnnouncedBuild_Fragment = { __typename: 'AnnouncedBuild', id: string, number: number, branch: string, commit: string, uncommittedHash?: string | null, status: BuildStatus, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'BuildFields_AnnouncedBuild_Fragment' }; +type NextBuildFields_PreparedBuild_Fragment = { __typename: 'PreparedBuild', id: string, commit: string, committedAt: any, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'NextBuildFields_PreparedBuild_Fragment' }; -type BuildFields_CompletedBuild_Fragment = { __typename: 'CompletedBuild', result: BuildResult, startedAt: any, id: string, number: number, branch: string, commit: string, uncommittedHash?: string | null, status: BuildStatus, changeCount: number, brokenCount: number, testsForStatus?: { __typename?: 'CompletedBuildTestConnection', nodes: Array<( +type NextBuildFields_PublishedBuild_Fragment = { __typename: 'PublishedBuild', id: string, commit: string, committedAt: any, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'NextBuildFields_PublishedBuild_Fragment' }; + +type NextBuildFields_StartedBuild_Fragment = { __typename: 'StartedBuild', id: string, commit: string, committedAt: any, changeCount: number, brokenCount: number, testsForStatus?: { __typename?: 'StartedBuildTestConnection', nodes: Array<( { __typename?: 'Test' } & { ' $fragmentRefs'?: { 'StatusTestFieldsFragment': StatusTestFieldsFragment } } - )> } | null, testsForStory?: { __typename?: 'CompletedBuildTestConnection', nodes: Array<( + )> } | null, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'NextBuildFields_StartedBuild_Fragment' }; + +export type NextBuildFieldsFragment = NextBuildFields_AnnouncedBuild_Fragment | NextBuildFields_CompletedBuild_Fragment | NextBuildFields_PreparedBuild_Fragment | NextBuildFields_PublishedBuild_Fragment | NextBuildFields_StartedBuild_Fragment; + +type StoryBuildFields_AnnouncedBuild_Fragment = { __typename: 'AnnouncedBuild', id: string, number: number, branch: string, uncommittedHash?: string | null, status: BuildStatus } & { ' $fragmentName'?: 'StoryBuildFields_AnnouncedBuild_Fragment' }; + +type StoryBuildFields_CompletedBuild_Fragment = { __typename: 'CompletedBuild', startedAt: any, id: string, number: number, branch: string, uncommittedHash?: string | null, status: BuildStatus, testsForStory?: { __typename?: 'CompletedBuildTestConnection', nodes: Array<( { __typename?: 'Test' } & { ' $fragmentRefs'?: { 'StoryTestFieldsFragment': StoryTestFieldsFragment } } - )> } | null, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'BuildFields_CompletedBuild_Fragment' }; + )> } | null } & { ' $fragmentName'?: 'StoryBuildFields_CompletedBuild_Fragment' }; -type BuildFields_PreparedBuild_Fragment = { __typename: 'PreparedBuild', id: string, number: number, branch: string, commit: string, uncommittedHash?: string | null, status: BuildStatus, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'BuildFields_PreparedBuild_Fragment' }; +type StoryBuildFields_PreparedBuild_Fragment = { __typename: 'PreparedBuild', id: string, number: number, branch: string, uncommittedHash?: string | null, status: BuildStatus } & { ' $fragmentName'?: 'StoryBuildFields_PreparedBuild_Fragment' }; -type BuildFields_PublishedBuild_Fragment = { __typename: 'PublishedBuild', id: string, number: number, branch: string, commit: string, uncommittedHash?: string | null, status: BuildStatus, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'BuildFields_PublishedBuild_Fragment' }; +type StoryBuildFields_PublishedBuild_Fragment = { __typename: 'PublishedBuild', id: string, number: number, branch: string, uncommittedHash?: string | null, status: BuildStatus } & { ' $fragmentName'?: 'StoryBuildFields_PublishedBuild_Fragment' }; -type BuildFields_StartedBuild_Fragment = { __typename: 'StartedBuild', startedAt: any, id: string, number: number, branch: string, commit: string, uncommittedHash?: string | null, status: BuildStatus, changeCount: number, brokenCount: number, testsForStatus?: { __typename?: 'StartedBuildTestConnection', nodes: Array<( - { __typename?: 'Test' } - & { ' $fragmentRefs'?: { 'StatusTestFieldsFragment': StatusTestFieldsFragment } } - )> } | null, testsForStory?: { __typename?: 'StartedBuildTestConnection', nodes: Array<( +type StoryBuildFields_StartedBuild_Fragment = { __typename: 'StartedBuild', startedAt: any, id: string, number: number, branch: string, uncommittedHash?: string | null, status: BuildStatus, testsForStory?: { __typename?: 'StartedBuildTestConnection', nodes: Array<( { __typename?: 'Test' } & { ' $fragmentRefs'?: { 'StoryTestFieldsFragment': StoryTestFieldsFragment } } - )> } | null, browsers: Array<{ __typename?: 'BrowserInfo', id: string, key: Browser, name: string }> } & { ' $fragmentName'?: 'BuildFields_StartedBuild_Fragment' }; + )> } | null } & { ' $fragmentName'?: 'StoryBuildFields_StartedBuild_Fragment' }; -export type BuildFieldsFragment = BuildFields_AnnouncedBuild_Fragment | BuildFields_CompletedBuild_Fragment | BuildFields_PreparedBuild_Fragment | BuildFields_PublishedBuild_Fragment | BuildFields_StartedBuild_Fragment; +export type StoryBuildFieldsFragment = StoryBuildFields_AnnouncedBuild_Fragment | StoryBuildFields_CompletedBuild_Fragment | StoryBuildFields_PreparedBuild_Fragment | StoryBuildFields_PublishedBuild_Fragment | StoryBuildFields_StartedBuild_Fragment; export type StatusTestFieldsFragment = { __typename?: 'Test', id: string, status: TestStatus, story?: { __typename?: 'Story', storyId: string } | null } & { ' $fragmentName'?: 'StatusTestFieldsFragment' }; @@ -1317,9 +1346,10 @@ export type ReviewTestMutationVariables = Exact<{ export type ReviewTestMutation = { __typename?: 'Mutation', reviewTest?: { __typename?: 'ReviewTestPayload', updatedTests?: Array<{ __typename?: 'Test', id: string, status: TestStatus }> | null, userErrors: Array<{ __typename: 'BuildSupersededError', message: string, build: { __typename?: 'AnnouncedBuild', id: string } | { __typename?: 'CompletedBuild', id: string } | { __typename?: 'PreparedBuild', id: string } | { __typename?: 'PublishedBuild', id: string } | { __typename?: 'StartedBuild', id: string } } | { __typename: 'TestNotFoundError', message: string } | { __typename: 'TestUnreviewableError', message: string, test: { __typename?: 'Test', id: string } }> } | null }; export const StatusTestFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StatusTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}}]} as unknown as DocumentNode; +export const NextBuildFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NextBuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commit"}},{"kind":"Field","name":{"kind":"Name","value":"committedAt"}},{"kind":"Field","name":{"kind":"Name","value":"browsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StatusTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}}]} as unknown as DocumentNode; export const StoryTestFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"baseCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"component"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; -export const BuildFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"commit"}},{"kind":"Field","name":{"kind":"Name","value":"uncommittedHash"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"browsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StatusTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"baseCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"component"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; +export const StoryBuildFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryBuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"uncommittedHash"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"baseCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"component"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const SelectProjectsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SelectProjectsQuery"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"projectToken"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"number"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ProjectQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"number"}}]}}]}}]}}]} as unknown as DocumentNode; -export const AddonVisualTestsBuildDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AddonVisualTestsBuild"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branch"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"gitUserEmailHash"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TestStatus"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"branches"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"branch"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}},{"kind":"Argument","name":{"kind":"Name","value":"localBuilds"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"localBuildEmailHash"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gitUserEmailHash"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BuildFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StatusTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"baseCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"component"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"commit"}},{"kind":"Field","name":{"kind":"Name","value":"uncommittedHash"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"browsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const AddonVisualTestsBuildDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AddonVisualTestsBuild"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branch"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"gitUserEmailHash"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TestStatus"}}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"storyBuildId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hasStoryBuildId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"branches"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"branch"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}},{"kind":"Argument","name":{"kind":"Name","value":"localBuilds"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"localBuildEmailHash"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gitUserEmailHash"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NextBuildFields"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryBuildFields"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"skip"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasStoryBuildId"}}}]}]}]}}]}},{"kind":"Field","alias":{"kind":"Name","value":"storyBuild"},"name":{"kind":"Name","value":"build"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyBuildId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"include"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasStoryBuildId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryBuildFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StatusTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryTestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"baseCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"imageWidth"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"component"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NextBuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commit"}},{"kind":"Field","name":{"kind":"Name","value":"committedAt"}},{"kind":"Field","name":{"kind":"Name","value":"browsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"brokenCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"CAPTURE_ERROR"}]}}]},{"kind":"Field","alias":{"kind":"Name","value":"testsForStatus"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"statuses"},"value":{"kind":"Variable","name":{"kind":"Name","value":"testStatuses"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StatusTestFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StoryBuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"uncommittedHash"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","alias":{"kind":"Name","value":"testsForStory"},"name":{"kind":"Name","value":"tests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"storyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"storyId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StoryTestFields"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ReviewTestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ReviewTest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewTestInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewTest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updatedTests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"Field","name":{"kind":"Name","value":"userErrors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UserError"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BuildSupersededError"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"build"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TestUnreviewableError"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 90b76f48..fb66ea41 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,23 +1,21 @@ /* eslint-disable no-console */ import type { Channel } from "@storybook/channels"; // eslint-disable-next-line import/no-unresolved -import { getGitInfo, GitInfo, run } from "chromatic/node"; +import { Context, getGitInfo, GitInfo, run, TaskName } from "chromatic/node"; import { basename, relative } from "path"; import { - BUILD_ANNOUNCED, - BUILD_STARTED, CHROMATIC_BASE_URL, GIT_INFO, GitInfoPayload, - PROJECT_UPDATED, - PROJECT_UPDATING_FAILED, - ProjectUpdatedPayload, - ProjectUpdatingFailedPayload, + isKnownTask, + PROJECT_INFO, + ProjectInfoPayload, + RUNNING_BUILD, + RunningBuildPayload, START_BUILD, - UPDATE_PROJECT, - UpdateProjectPayload, } from "./constants"; +import { useAddonState } from "./useAddonState/server"; import { findConfig } from "./utils/storybook.config.utils"; import { updateMain } from "./utils/updateMain"; @@ -53,6 +51,7 @@ async function serverChannel( channel: Channel, { configDir, + projectId: initialProjectId, projectToken: initialProjectToken, // This is a small subset of the flags available to the CLI. @@ -61,21 +60,68 @@ async function serverChannel( zip, }: { configDir: string; + projectId: string; projectToken: string; buildScriptName?: string; debug?: boolean; zip?: boolean; } ) { - let projectToken = initialProjectToken; + // eslint-disable-next-line react-hooks/rules-of-hooks + const projectInfoState = useAddonState(channel, PROJECT_INFO); + projectInfoState.value = initialProjectId + ? { projectId: initialProjectId, projectToken: initialProjectToken } + : {}; + + let lastProjectToken = initialProjectToken; + projectInfoState.on("change", async ({ projectId, projectToken }) => { + if (projectToken === lastProjectToken) return; + lastProjectToken = projectToken; + + const relativeConfigDir = relative(process.cwd(), configDir); + let mainPath: string; + try { + mainPath = await findConfig(configDir, "main"); + await updateMain({ mainPath, projectId, projectToken }); + + projectInfoState.value = { + ...projectInfoState.value, + written: true, + mainPath: basename(mainPath), + configDir: relativeConfigDir, + }; + } catch (err) { + console.warn(`Failed to update your main configuration:\n\n ${err}`); + + projectInfoState.value = { + ...projectInfoState.value, + written: false, + mainPath: mainPath && basename(mainPath), + configDir: relativeConfigDir, + }; + } + }); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const runningBuildState = useAddonState(channel, RUNNING_BUILD); channel.on(START_BUILD, async () => { - let announced = false; - let started = false; + if (!projectInfoState.value.projectToken) throw new Error("No project token set"); + + const onStartOrProgress = ( + ctx: Context, + { progress, total }: { progress?: number; total?: number } = {} + ) => { + if (isKnownTask(ctx.task)) { + runningBuildState.value = { step: ctx.task, id: ctx.announcedBuild?.id, progress, total }; + } + }; + + runningBuildState.value = { step: "initialize" }; await run({ // Currently we have to have these flags. // We should move the checks to after flags have been parsed into options. flags: { - projectToken, + projectToken: projectInfoState.value.projectToken, buildScriptName, debug, zip, @@ -85,49 +131,22 @@ async function serverChannel( forceRebuild: true, // Builds initiated from the addon are always considered local isLocalBuild: true, + onTaskStart: onStartOrProgress, + onTaskProgress: onStartOrProgress, onTaskComplete(ctx) { - console.log(`Completed task '${ctx.title}'`); - if (!announced && ctx.announcedBuild) { - console.debug("emitting", BUILD_ANNOUNCED, ctx.announcedBuild.id); - channel.emit(BUILD_ANNOUNCED, ctx.announcedBuild.id); - announced = true; - } - if (announced && !started && ctx.build) { - console.debug("emitting", BUILD_STARTED, ctx.build.status); - channel.emit(BUILD_STARTED, ctx.build.status); - started = true; + if (ctx.task === "snapshot") { + runningBuildState.value = { step: "complete", id: ctx.announcedBuild?.id }; } }, - // as any due to CLI mistyping: https://github.com/chromaui/chromatic-cli/pull/800 }, }); }); - channel.on( - UPDATE_PROJECT, - async ({ projectId, projectToken: updatedProjectToken }: UpdateProjectPayload) => { - projectToken = updatedProjectToken; - - const relativeConfigDir = relative(process.cwd(), configDir); - let mainPath: string; - try { - mainPath = await findConfig(configDir, "main"); - await updateMain({ mainPath, projectId, projectToken }); - channel.emit(PROJECT_UPDATED, { - mainPath: basename(mainPath), - configDir: relativeConfigDir, - } satisfies ProjectUpdatedPayload); - } catch (err) { - console.warn(`Failed to update your main configuration:\n\n ${err}`); - channel.emit(PROJECT_UPDATING_FAILED, { - mainPath: mainPath && basename(mainPath), - configDir: relativeConfigDir, - } satisfies ProjectUpdatingFailedPayload); - } - } - ); - - observeGitInfo(5000, (info) => channel.emit(GIT_INFO, info as GitInfoPayload)); + // eslint-disable-next-line react-hooks/rules-of-hooks + const gitInfoState = useAddonState(channel, GIT_INFO); + observeGitInfo(5000, (info) => { + gitInfoState.value = info; + }); return channel; } @@ -137,21 +156,13 @@ const config = { experimental_serverChannel: serverChannel, env: async ( env: Record, - { projectId, configType }: { projectId: string; configType: "DEVELOPMENT" | "PRODUCTION" } + { configType }: { configType: "DEVELOPMENT" | "PRODUCTION" } ) => { if (configType === "PRODUCTION") return env; - const { userEmail, userEmailHash, branch, commit, slug, uncommittedHash } = await getGitInfo(); return { ...env, CHROMATIC_BASE_URL, - CHROMATIC_PROJECT_ID: projectId || "", - GIT_USER_EMAIL: userEmail, - GIT_USER_EMAIL_HASH: userEmailHash, - GIT_BRANCH: branch, - GIT_COMMIT: commit, - GIT_SLUG: slug, - GIT_UNCOMMITTED_HASH: uncommittedHash, }; }, }; diff --git a/src/screens/VisualTests/BuildProgress.stories.ts b/src/screens/VisualTests/BuildProgress.stories.ts new file mode 100644 index 00000000..6b69cd1f --- /dev/null +++ b/src/screens/VisualTests/BuildProgress.stories.ts @@ -0,0 +1,98 @@ +import { action } from "@storybook/addon-actions"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { withFigmaDesign } from "../../utils/withFigmaDesign"; +import { BuildProgress } from "./BuildProgress"; + +const meta = { + args: { + switchToNextBuild: action("switchToNextBuild"), + }, + component: BuildProgress, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Initialize: Story = { + args: { + runningBuild: { step: "initialize" }, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2303-353260&mode=design&t=vlcsXN2x67tQaQdy-0" + ), +}; + +export const Build: Story = { + args: { + runningBuild: { + step: "build", + }, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2303-353260&mode=design&t=vlcsXN2x67tQaQdy-0" + ), +}; + +export const Upload: Story = { + args: { + runningBuild: { + step: "upload", + progress: 500, + total: 1000, + }, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2303-370243&mode=design&t=vlcsXN2x67tQaQdy-0" + ), +}; + +export const Verify: Story = { + args: { + runningBuild: { + step: "verify", + }, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2303-371149&mode=design&t=vlcsXN2x67tQaQdy-0" + ), +}; + +export const Snapshot: Story = { + args: { + runningBuild: { + step: "snapshot", + progress: 25, + total: 50, + }, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2303-373686&mode=design&t=vlcsXN2x67tQaQdy-0" + ), +}; + +export const Complete: Story = { + args: { + runningBuild: { + step: "complete", + }, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2303-375342&mode=design&t=vlcsXN2x67tQaQdy-0" + ), +}; + +export const Latest: Story = { + // Could not find this state in the figma file, but it is in this video: https://chromaticqa.slack.com/archives/C051TQR6QLC/p1692372058786929?thread_ts=1692354384.352659&cid=C051TQR6QLC + // parameters: withFigmaDesign( + // ), +}; + +export const LatestNeedToUpdate: Story = { + args: { + switchToNextBuild: undefined, + }, + parameters: withFigmaDesign( + "https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2127-448761&mode=design&t=70EtYCn1H7hB8PAk-0" + ), +}; diff --git a/src/screens/VisualTests/BuildProgress.tsx b/src/screens/VisualTests/BuildProgress.tsx new file mode 100644 index 00000000..2a6f5bd4 --- /dev/null +++ b/src/screens/VisualTests/BuildProgress.tsx @@ -0,0 +1,72 @@ +import { Link } from "@storybook/components"; +import { styled } from "@storybook/theming"; +import React from "react"; + +import { RunningBuildPayload } from "../../constants"; + +export const Header = styled.div(({ theme }) => ({ + color: theme.color.darkest, + background: theme.background.app, + padding: "10px", + lineHeight: "18px", + position: "relative", +})); + +export const Bar = styled.div<{ percentage: number }>(({ theme, percentage }) => ({ + display: "block", + position: "absolute", + top: "0", + height: "100%", + left: "0", + width: `${percentage}%`, + transition: "all 150ms ease-out", + backgroundColor: "#E3F3FF", +})); + +export const Text = styled.div({ + position: "relative", + zIndex: 1, +}); + +type BuildProgressProps = { + runningBuild?: RunningBuildPayload; + switchToNextBuild?: () => void; +}; + +const messageMap: Record = { + initialize: "📦 Validating Storybook files...", + build: "📦 Validating Storybook files...", + upload: "📡 Uploading to Chromatic...", // TODO represent progress in bytes + verify: "🛠️ Initiating build...", // TODO build number + snapshot: "👀 Running visual tests...", // TODO count + complete: "🎉 Visual tests completed!", +}; + +export function BuildProgress({ runningBuild, switchToNextBuild }: BuildProgressProps) { + const percentage = + runningBuild && (runningBuild.total ? runningBuild.progress / runningBuild.total : 0.35) * 100; + + // eslint-disable-next-line no-nested-ternary + const text = runningBuild + ? messageMap[runningBuild.step] + : switchToNextBuild + ? "There's a newer snapshot with changes" + : "Reviewing is disabled because there's a newer snapshot on "; + + // We show the "go to next build" button if there's no build running or if the running build is complete + const showButton = switchToNextBuild && (!runningBuild || runningBuild.step === "complete"); + return ( +
+ {runningBuild &&  } + + {text}{" "} + {showButton && ( + // eslint-disable-next-line jsx-a11y/anchor-is-valid + + Switch to next build + + )} + +
+ ); +} diff --git a/src/screens/VisualTests/VisualTests.stories.tsx b/src/screens/VisualTests/VisualTests.stories.tsx index 220cbc3a..2c68984e 100644 --- a/src/screens/VisualTests/VisualTests.stories.tsx +++ b/src/screens/VisualTests/VisualTests.stories.tsx @@ -69,9 +69,7 @@ const announcedBuild: AnnouncedBuild = { id: "1", number: 1, branch: "feature-branch", - commit: "1234567", uncommittedHash: "", - browsers: [makeBrowserInfo(Browser.Chrome), makeBrowserInfo(Browser.Safari)], status: BuildStatus.Announced, }; @@ -180,11 +178,11 @@ const meta = { branch: "feature-branch", slug: "chromaui/addon-visual-tests", uncommittedHash: "", + committedAt: Date.now() - 1000, }, storyId: "button--primary", projectId: "Project:id123", startDevBuild: action("startDevBuild"), - isStarting: false, setAccessToken: action("setAccessToken"), updateBuildStatus: action("updateBuildStatus"), }, @@ -214,7 +212,12 @@ export const NoBuild: Story = { export const NoBuildStarting: Story = { ...NoBuild, args: { - isStarting: true, + ...NoBuild.args, + runningBuild: { + step: "initialize", + }, + }, + parameters: { ...withGraphQLQuery("AddonVisualTestsBuild", (req, res, ctx) => res(ctx.data({ build: null } as AddonVisualTestsBuildQuery)) ), @@ -251,23 +254,21 @@ export const OutdatedStarting: Story = { ...Outdated, args: { ...Outdated.args, - isStarting: true, + runningBuild: { + step: "initialize", + }, }, }; export const Announced: Story = { - args: { - isStarting: true, - }, + args: {}, parameters: { ...withBuild(announcedBuild), }, }; export const Published: Story = { - args: { - isStarting: true, - }, + args: {}, parameters: { ...withBuild(publishedBuild), }, @@ -302,6 +303,17 @@ export const Pending: Story = { }, }; +export const PendingWithSecondBuildInProgress: Story = { + ...Pending, + args: { + runningBuild: { + step: "upload", + progress: 1000, + total: 2000, + }, + }, +}; + export const ToggleSnapshot: Story = { parameters: { ...withBuild(pendingBuild), diff --git a/src/screens/VisualTests/VisualTests.tsx b/src/screens/VisualTests/VisualTests.tsx index 39f9415d..420c4700 100644 --- a/src/screens/VisualTests/VisualTests.tsx +++ b/src/screens/VisualTests/VisualTests.tsx @@ -13,6 +13,7 @@ import { IconButton } from "../../components/IconButton"; import { ProgressIcon } from "../../components/icons/ProgressIcon"; import { Bar, Col, Row, Section, Sections, Text } from "../../components/layout"; import { Text as CenterText } from "../../components/Text"; +import { GitInfoPayload, RunningBuildPayload } from "../../constants"; import { getFragment, graphql } from "../../gql"; import { AddonVisualTestsBuildQuery, @@ -24,6 +25,7 @@ import { TestStatus, } from "../../gql/graphql"; import { statusMap, StatusUpdate, testsToStatusUpdate } from "../../utils/testsToStatusUpdate"; +import { BuildProgress } from "./BuildProgress"; import { RenderSettings } from "./RenderSettings"; import { SnapshotComparison } from "./SnapshotComparison"; import { StoryInfo } from "./StoryInfo"; @@ -37,6 +39,8 @@ const QueryBuild = graphql(/* GraphQL */ ` $slug: String $storyId: String! $testStatuses: [TestStatus!]! + $storyBuildId: ID! + $hasStoryBuildId: Boolean! ) { project(id: $projectId) { name @@ -45,21 +49,22 @@ const QueryBuild = graphql(/* GraphQL */ ` slug: $slug localBuilds: { localBuildEmailHash: $gitUserEmailHash } ) { - ...BuildFields + ...NextBuildFields + ...StoryBuildFields @skip(if: $hasStoryBuildId) } } + storyBuild: build(id: $storyBuildId) @include(if: $hasStoryBuildId) { + ...StoryBuildFields + } } `); -const FragmentBuildFields = graphql(/* GraphQL */ ` - fragment BuildFields on Build { +const FragmentNextBuildFields = graphql(/* GraphQL */ ` + fragment NextBuildFields on Build { __typename id - number - branch commit - uncommittedHash - status + committedAt browsers { id key @@ -68,28 +73,43 @@ const FragmentBuildFields = graphql(/* GraphQL */ ` ... on StartedBuild { changeCount: testCount(results: [ADDED, CHANGED, FIXED]) brokenCount: testCount(results: [CAPTURE_ERROR]) - startedAt testsForStatus: tests(first: 1000, statuses: $testStatuses) { nodes { ...StatusTestFields } } - testsForStory: tests(storyId: $storyId) { - nodes { - ...StoryTestFields - } - } } ... on CompletedBuild { result changeCount: testCount(results: [ADDED, CHANGED, FIXED]) brokenCount: testCount(results: [CAPTURE_ERROR]) - startedAt testsForStatus: tests(statuses: $testStatuses) { nodes { ...StatusTestFields } } + } + } +`); + +const FragmentStoryBuildFields = graphql(/* GraphQL */ ` + fragment StoryBuildFields on Build { + __typename + id + number + branch + uncommittedHash + status + ... on StartedBuild { + startedAt + testsForStory: tests(storyId: $storyId) { + nodes { + ...StoryTestFields + } + } + } + ... on CompletedBuild { + startedAt testsForStory: tests(storyId: $storyId) { nodes { ...StoryTestFields @@ -196,9 +216,11 @@ const MutationReviewTest = graphql(/* GraphQL */ ` interface VisualTestsProps { projectId: string; - gitInfo: Pick; - isStarting: boolean; - lastDevBuildId?: string; + gitInfo: Pick< + GitInfoPayload, + "branch" | "slug" | "userEmailHash" | "committedAt" | "uncommittedHash" + >; + runningBuild?: RunningBuildPayload; startDevBuild: () => void; setAccessToken: (accessToken: string | null) => void; updateBuildStatus: (update: StatusUpdate) => void; @@ -206,8 +228,7 @@ interface VisualTestsProps { } export const VisualTests = ({ - isStarting, - lastDevBuildId, + runningBuild, startDevBuild, setAccessToken, updateBuildStatus, @@ -215,6 +236,18 @@ export const VisualTests = ({ gitInfo, storyId, }: VisualTestsProps) => { + const [settingsVisible, setSettingsVisible] = useState(false); + const [warningsVisible, setWarningsVisible] = useState(false); + const [baselineImageVisible, setBaselineImageVisible] = useState(false); + const toggleBaselineImage = () => setBaselineImageVisible(!baselineImageVisible); + + // The storyId and buildId that drive the test(s) we are currently looking at + // The user can choose when to change story (via sidebar) and build (via opting into new builds) + const [storyBuildInfo, setStoryBuildInfo] = useState<{ + storyId: string; + buildId: string; + }>(); + const [{ data, error }, rerun] = useQuery< AddonVisualTestsBuildQuery, AddonVisualTestsBuildQueryVariables @@ -227,6 +260,8 @@ export const VisualTests = ({ branch: gitInfo.branch || "", ...(gitInfo.slug ? { slug: gitInfo.slug } : {}), gitUserEmailHash: gitInfo.userEmailHash, + storyBuildId: storyBuildInfo?.buildId || "", + hasStoryBuildId: !!storyBuildInfo, }, }); @@ -257,13 +292,23 @@ export const VisualTests = ({ [reviewTest] ); - const build = getFragment(FragmentBuildFields, data?.project?.lastBuild); - const isOutdated = build && build.uncommittedHash !== gitInfo.uncommittedHash; + const nextBuild = getFragment(FragmentNextBuildFields, data?.project?.lastBuild); + // Before we set the storyInfo, we use the nextBuild for story data + const storyBuild = getFragment( + FragmentStoryBuildFields, + data?.storyBuild ?? data?.project?.lastBuild + ); + + // If the next build is *newer* than the current commit, we don't want to switch to the build + const nextBuildNewer = nextBuild && nextBuild.committedAt > gitInfo.committedAt; + const canSwitchToNextBuild = nextBuild && !nextBuildNewer; + // We always set status to the next build's status, as when we change to a new story we'll see + // the next builds const buildStatusUpdate = - build && - "testsForStatus" in build && - testsToStatusUpdate(getFragment(FragmentStatusTestFields, build.testsForStatus.nodes)); + canSwitchToNextBuild && + "testsForStatus" in nextBuild && + testsToStatusUpdate(getFragment(FragmentStatusTestFields, nextBuild.testsForStatus.nodes)); useEffect(() => { if (buildStatusUpdate) updateBuildStatus(buildStatusUpdate); @@ -271,12 +316,27 @@ export const VisualTests = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(buildStatusUpdate), updateBuildStatus]); - const [settingsVisible, setSettingsVisible] = useState(false); - const [warningsVisible, setWarningsVisible] = useState(false); - const [baselineImageVisible, setBaselineImageVisible] = useState(false); - const toggleBaselineImage = () => setBaselineImageVisible(!baselineImageVisible); + // Ensure we are holding the right story build + useEffect(() => { + setStoryBuildInfo((oldStoryBuildInfo) => { + return (!oldStoryBuildInfo || oldStoryBuildInfo.storyId !== storyId) && nextBuild?.id + ? { + storyId, + // If the next build is "too new" and we have an old build, stick to it. + buildId: (!canSwitchToNextBuild && oldStoryBuildInfo?.buildId) || nextBuild.id, + } + : oldStoryBuildInfo; + }); + }, [canSwitchToNextBuild, nextBuild?.id, storyId]); - if (!build || error) { + const switchToNextBuild = useCallback( + () => canSwitchToNextBuild && setStoryBuildInfo({ storyId, buildId: nextBuild.id }), + [canSwitchToNextBuild, nextBuild?.id, storyId] + ); + + const isRunningBuildStarting = runningBuild?.step === "initialize"; + + if (!nextBuild || error) { return (
@@ -288,7 +348,7 @@ export const VisualTests = ({ )} {!data && } - {data && !build && !error && ( + {data && !nextBuild && !error && ( Create a test baseline @@ -296,8 +356,8 @@ export const VisualTests = ({ as test baselines.
-