diff --git a/graphql-codegen.yml b/graphql-codegen.yml index c70f4e72a2..5bc8361b3e 100644 --- a/graphql-codegen.yml +++ b/graphql-codegen.yml @@ -57,7 +57,7 @@ generates: nonOptionalTypename: true - 'packages/frontend-shared/script/codegen-type-map.js' - './packages/frontend-shared/cypress/support/generated/test-cloud-graphql-types.gen.ts': + './packages/graphql/src/gen/test-cloud-graphql-types.gen.ts': schema: 'packages/graphql/schemas/cloud.graphql' plugins: - add: diff --git a/guides/e2e-open-testing.md b/guides/e2e-open-testing.md index e68c9b7278..ff2e7e3643 100644 --- a/guides/e2e-open-testing.md +++ b/guides/e2e-open-testing.md @@ -47,12 +47,8 @@ When we hit the remote GraphQL server, we mock against the same mocked schema we cy.remoteGraphQLIntercept(async (obj) => { // Currently, all remote requests go through here, we want to use this to modify the // remote request before it's used and avoid touching the login query - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - if (proj.runs?.nodes) { - proj.runs.nodes = [] - } - } + if (obj.result.data?.cloudProjectBySlug.runs?.nodes) { + obj.result.data.cloudProjectBySlug.runs.nodes = [] } return obj.result diff --git a/package.json b/package.json index d63ffccfd5..c9954e45eb 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,6 @@ "@graphql-codegen/typescript": "2.4.2", "@graphql-codegen/typescript-operations": "2.2.3", "@graphql-codegen/typescript-urql-graphcache": "2.2.3", - "@graphql-tools/batch-delegate": "8.1.0", "@graphql-tools/delegate": "8.2.1", "@graphql-tools/utils": "8.2.3", "@graphql-tools/wrap": "8.1.1", diff --git a/packages/app/cypress/e2e/runs.cy.ts b/packages/app/cypress/e2e/runs.cy.ts index fb450748f1..0dda1323a4 100644 --- a/packages/app/cypress/e2e/runs.cy.ts +++ b/packages/app/cypress/e2e/runs.cy.ts @@ -219,18 +219,18 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.remoteGraphQLIntercept(async (obj) => { // Currently, all remote requests go through here, we want to use this to modify the // remote request before it's used and avoid touching the login query + if (obj.result.data?.cloudProjectBySlug && obj.variables._v0_slug === 'abcdef42') { + const proj = obj.result.data.cloudProjectBySlug - if (obj.result.data?.cloudProjectsBySlugs && obj.variables._v0_slugs.includes('abcdef42')) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - proj.__typename = 'CloudProjectNotFound' - proj.message = 'Cloud Project Not Found' - } + proj.__typename = 'CloudProjectNotFound' + proj.message = 'Cloud Project Not Found' } if (obj.result.data?.cloudViewer?.organizations?.nodes) { const projectNodes = obj.result.data?.cloudViewer.organizations.nodes[0].projects.nodes projectNodes.push({ + __typename: 'CloudProject', id: '1', slug: 'ghijkl', name: 'Mock Project', @@ -270,12 +270,12 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { it('if project Id is specified in config file that is not accessible, shows call to action', () => { cy.remoteGraphQLIntercept(async (obj) => { - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - proj.__typename = 'CloudProjectUnauthorized' - proj.message = 'Cloud Project Unauthorized' - proj.hasRequestedAccess = false - } + if (obj.result.data?.cloudProjectBySlug) { + const proj = obj.result.data.cloudProjectBySlug + + proj.__typename = 'CloudProjectUnauthorized' + proj.message = 'Cloud Project Unauthorized' + proj.hasRequestedAccess = false } return obj.result @@ -299,12 +299,12 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { return obj.result } - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - proj.__typename = 'CloudProjectUnauthorized' - proj.message = 'Cloud Project Unauthorized' - proj.hasRequestedAccess = false - } + if (obj.result.data?.cloudProjectBySlug) { + const proj = obj.result.data.cloudProjectBySlug + + proj.__typename = 'CloudProjectUnauthorized' + proj.message = 'Cloud Project Unauthorized' + proj.hasRequestedAccess = false } return obj.result @@ -321,13 +321,13 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { it('updates the button text when the request access button is clicked', () => { cy.remoteGraphQLIntercept(async (obj, testState) => { - if (obj.operationName === 'Runs_currentProject_cloudProject_batched') { - for (const proj of obj!.result!.data!.cloudProjectsBySlugs) { - proj.__typename = 'CloudProjectUnauthorized' - proj.message = 'Cloud Project Unauthorized' - proj.hasRequestedAccess = false - testState.project = proj - } + if (obj.operationName === 'Runs_currentProject_cloudProject_cloudProjectBySlug') { + const proj = obj!.result!.data!.cloudProjectBySlug + + proj.__typename = 'CloudProjectUnauthorized' + proj.message = 'Cloud Project Unauthorized' + proj.hasRequestedAccess = false + testState.project = proj } else if (obj.operationName === 'RunsErrorRenderer_RequestAccess_cloudProjectRequestAccess') { obj!.result!.data!.cloudProjectRequestAccess = { ...testState.project, @@ -355,12 +355,12 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { // Currently, all remote requests go through here, we want to use this to modify the // remote request before it's used and avoid touching the login query - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - proj.__typename = 'CloudProjectUnauthorized' - proj.message = 'Cloud Project Unauthorized' - proj.hasRequestedAccess = true - } + if (obj.result.data?.cloudProjectBySlug) { + const proj = obj.result.data.cloudProjectBySlug + + proj.__typename = 'CloudProjectUnauthorized' + proj.message = 'Cloud Project Unauthorized' + proj.hasRequestedAccess = true } return obj.result @@ -386,12 +386,8 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.remoteGraphQLIntercept(async (obj) => { // Currently, all remote requests go through here, we want to use this to modify the // remote request before it's used and avoid touching the login query - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - if (proj.runs?.nodes) { - proj.runs.nodes = [] - } - } + if (obj.result.data?.cloudProjectBySlug?.runs?.nodes) { + obj.result.data.cloudProjectBySlug.runs.nodes = [] } return obj.result @@ -409,12 +405,8 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.loginUser() cy.remoteGraphQLIntercept(async (obj) => { - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - if (proj.runs?.nodes) { - proj.runs.nodes = [] - } - } + if (obj.result.data?.cloudProjectBySlug?.runs?.nodes) { + obj.result.data.cloudProjectBySlug.runs.nodes = [] } return obj.result @@ -438,12 +430,8 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.loginUser() cy.remoteGraphQLIntercept(async (obj) => { - if (obj.result.data?.cloudProjectsBySlugs) { - for (const proj of obj.result.data.cloudProjectsBySlugs) { - if (proj.runs?.nodes) { - proj.runs.nodes = [] - } - } + if (obj.result.data?.cloudProjectBySlug?.runs?.nodes) { + obj.result.data.cloudProjectBySlug.runs.nodes = [] } return obj.result @@ -480,17 +468,17 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.get('[href="http://dummy.cypress.io/runs/0"]').first().within(() => { cy.findByText('fix: make gql work CANCELLED') - cy.get('[data-cy="run-card-icon"]') + cy.get('[data-cy="run-card-icon-CANCELLED"]') }) cy.get('[href="http://dummy.cypress.io/runs/1"]').first().within(() => { cy.findByText('fix: make gql work ERRORED') - cy.get('[data-cy="run-card-icon"]') + cy.get('[data-cy="run-card-icon-ERRORED"]') }) cy.get('[href="http://dummy.cypress.io/runs/2"]').first().within(() => { cy.findByText('fix: make gql work FAILED') - cy.get('[data-cy="run-card-icon"]') + cy.get('[data-cy="run-card-icon-FAILED"]') }) cy.get('[href="http://dummy.cypress.io/runs/0"]').first().as('firstRun') @@ -565,4 +553,110 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.get('[data-cy=warning-alert]').should('not.exist') }) }) + + describe('refetching', () => { + let obj: {toCall?: Function} = {} + const RUNNING_COUNT = 3 + + beforeEach(() => { + cy.scaffoldProject('component-tests') + cy.openProject('component-tests') + cy.startAppServer('component') + cy.loginUser() + cy.remoteGraphQLIntercept((obj, testState) => { + if (obj.result.data?.cloudProjectBySlug?.runs?.nodes.length) { + obj.result.data.cloudProjectBySlug.runs.nodes.map((run) => { + run.status = 'RUNNING' + }) + + obj.result.data.cloudProjectBySlug.runs.nodes = obj.result.data.cloudProjectBySlug.runs.nodes.slice(0, 3) + } + + return obj.result + }) + + cy.visitApp('/runs', { + onBeforeLoad (win) { + const setTimeout = win.setTimeout + + // @ts-expect-error + win.setTimeout = function (fn, time) { + if (fn.name === 'fetchNewerRuns') { + obj.toCall = fn + } else { + setTimeout(fn, time) + } + } + }, + }) + }) + + it('should re-query for executing runs', () => { + cy.get('[data-cy="run-card-icon-RUNNING"]').should('have.length', RUNNING_COUNT).should('be.visible') + + cy.remoteGraphQLIntercept(async (obj) => { + await new Promise((resolve) => setTimeout(resolve, 100)) + + if (obj.result.data?.cloudNode?.newerRuns?.nodes) { + obj.result.data.cloudNode.newerRuns.nodes = [] + } + + if (obj.result.data?.cloudNodesByIds) { + obj.result.data?.cloudNodesByIds.map((node) => { + node.status = 'RUNNING' + }) + + obj.result.data.cloudNodesByIds[0].status = 'PASSED' + } + + return obj.result + }) + + function completeNext (passed) { + cy.wrap(obj).invoke('toCall').then(() => { + cy.get('[data-cy="run-card-icon-PASSED"]').should('have.length', passed).should('be.visible') + if (passed < RUNNING_COUNT) { + completeNext(passed + 1) + } + }) + } + + completeNext(1) + }) + + it('should fetch newer runs and maintain them when navigating', () => { + cy.get('[data-cy="run-card-icon-RUNNING"]').should('have.length', RUNNING_COUNT).should('be.visible') + + cy.remoteGraphQLIntercept(async (obj) => { + await new Promise((resolve) => setTimeout(resolve, 100)) + + if (obj.result.data?.cloudNodesByIds) { + obj.result.data?.cloudNodesByIds.map((node) => { + node.status = 'PASSED' + node.totalPassed = 100 + }) + } + + return obj.result + }) + + cy.get('[data-cy="run-card-icon-RUNNING"]').should('have.length', 3).should('be.visible') + cy.wrap(obj).invoke('toCall') + + cy.get('[data-cy="run-card-icon-PASSED"]').should('have.length', 3).should('be.visible').within(() => { + cy.get('[data-cy="runResults-passed-count"]').should('contain', 100) + }) + + cy.get('[data-cy="run-card-icon-RUNNING"]').should('have.length', 2).should('be.visible') + + // If we navigate away & back, we should see the same runs + cy.get('[href="#/settings"]').click() + cy.remoteGraphQLIntercept((obj) => obj.result) + + cy.get('[href="#/runs"]').click() + + cy.get('[data-cy="run-card-icon-PASSED"]').should('have.length', 3).should('be.visible') + cy.get('[data-cy="run-card-icon-RUNNING"]').should('have.length', 2).should('be.visible') + }) + }) }) diff --git a/packages/app/cypress/e2e/subscriptions/createCloudOrgModal-subscription.cy.ts b/packages/app/cypress/e2e/subscriptions/createCloudOrgModal-subscription.cy.ts index f5c3996af3..4765c7bc57 100644 --- a/packages/app/cypress/e2e/subscriptions/createCloudOrgModal-subscription.cy.ts +++ b/packages/app/cypress/e2e/subscriptions/createCloudOrgModal-subscription.cy.ts @@ -17,8 +17,9 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.loginUser() cy.visitApp() + // Simulate no orgs cy.remoteGraphQLIntercept(async (obj) => { - if ((obj.operationName === 'CheckCloudOrganizations_cloudViewerChange_cloudViewer' || obj.operationName === 'Runs_cloudViewer') && obj.callCount < 2) { + if ((obj.operationName === 'CheckCloudOrganizations_cloudViewerChange_cloudViewer' || obj.operationName === 'Runs_cloudViewer')) { if (obj.result.data?.cloudViewer?.organizations?.nodes) { obj.result.data.cloudViewer.organizations.nodes = [] } @@ -41,6 +42,11 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.contains('button', defaultMessages.runs.connect.modal.createOrg.waitingButton).should('be.visible') cy.contains('a', defaultMessages.links.needHelp).should('have.attr', 'href', 'https://on.cypress.io/adding-new-project') + // Clear the current intercept to simulate a response with orgs + cy.remoteGraphQLIntercept((obj) => { + return obj.result + }) + cy.withCtx(async (ctx) => { await ctx.util.fetch(`http://127.0.0.1:${ctx.gqlServerPort}/cloud-notification?operationName=orgCreated`) }) diff --git a/packages/app/cypress/e2e/top-nav.cy.ts b/packages/app/cypress/e2e/top-nav.cy.ts index 0f05737d0c..e1178f3553 100644 --- a/packages/app/cypress/e2e/top-nav.cy.ts +++ b/packages/app/cypress/e2e/top-nav.cy.ts @@ -366,8 +366,8 @@ describe('App Top Nav Workflows', () => { cy.startAppServer('component') cy.remoteGraphQLIntercept((obj) => { - if (obj.result.data?.cloudProjectsBySlugs) { - throw new Error('Unauthorized') + if (obj.result.data?.cloudProjectBySlug) { + return new obj.Response('Unauthorized', { status: 401 }) } return obj.result diff --git a/packages/app/src/pages/Runs.vue b/packages/app/src/pages/Runs.vue index 6632500498..a8801d1023 100644 --- a/packages/app/src/pages/Runs.vue +++ b/packages/app/src/pages/Runs.vue @@ -5,18 +5,20 @@ diff --git a/packages/app/src/runs/CloudConnectButton.cy.tsx b/packages/app/src/runs/CloudConnectButton.cy.tsx index 0cadb8d961..7a52d1ae83 100644 --- a/packages/app/src/runs/CloudConnectButton.cy.tsx +++ b/packages/app/src/runs/CloudConnectButton.cy.tsx @@ -1,6 +1,6 @@ import CloudConnectButton from './CloudConnectButton.vue' import { CloudConnectButtonFragmentDoc } from '../generated/graphql-test' -import { CloudUserStubs } from '@packages/frontend-shared/cypress/support/mock-graphql/stubgql-CloudTypes' +import { CloudUserStubs } from '@packages/graphql/test/stubCloudTypes' describe('', () => { it('show user connect if not connected', () => { diff --git a/packages/app/src/runs/RunCard.cy.tsx b/packages/app/src/runs/RunCard.cy.tsx index 0c929140b5..b62e0f015f 100644 --- a/packages/app/src/runs/RunCard.cy.tsx +++ b/packages/app/src/runs/RunCard.cy.tsx @@ -1,4 +1,4 @@ -import { CloudRunStubs } from '@packages/frontend-shared/cypress/support/mock-graphql/stubgql-CloudTypes' +import { CloudRunStubs } from '@packages/graphql/test/stubCloudTypes' import { RunCardFragmentDoc } from '../generated/graphql-test' import RunCard from './RunCard.vue' diff --git a/packages/app/src/runs/RunCard.vue b/packages/app/src/runs/RunCard.vue index b2e499ef64..61d29b1131 100644 --- a/packages/app/src/runs/RunCard.vue +++ b/packages/app/src/runs/RunCard.vue @@ -8,7 +8,7 @@ >