Skip to content

Commit

Permalink
Merge branch 'develop' into feature/ct-public-api
Browse files Browse the repository at this point in the history
  • Loading branch information
ZachJW34 authored Feb 13, 2023
2 parents b2f6343 + 2bfeb53 commit f61995b
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 37 deletions.
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ _Released 02/14/2023 (PENDING)_

- Fixed an issue with the Cloud project selection modal not showing the correct prompts. Fixes [#25520](https://github.com/cypress-io/cypress/issues/25520).
- Fixed an issue in middleware where error-handling code could itself generate an error and fail to report the original issue. Fixes [#22825](https://github.com/cypress-io/cypress/issues/22825).
- Fixed an issue that could cause the Debug page to display a different number of specs for in-progress runs than shown in Cypress Cloud. Fixes [#25647](https://github.com/cypress-io/cypress/issues/25647).

**Features:**

Expand Down
47 changes: 47 additions & 0 deletions packages/app/src/debug/DebugContainer.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ describe('<DebugContainer />', () => {
result.currentProject.cloudProject.runByNumber = {
...CloudRunStubs.running,
runNumber: 1,
completedInstanceCount: 2,
totalInstanceCount: 3,
} as typeof test
}
},
Expand All @@ -255,6 +257,51 @@ describe('<DebugContainer />', () => {
})
})

it('does not render DebugPendingRunSplash and DebugNewRelevantRunBar at the same time', () => {
cy.mountFragment(DebugSpecsFragmentDoc, {
variableTypes: DebugSpecVariableTypes,
variables: {
hasNextRun: false,
runNumber: 1,
nextRunNumber: -1,
},
onResult: (result) => {
if (result.currentProject?.cloudProject?.__typename === 'CloudProject') {
const test = result.currentProject.cloudProject.runByNumber

// Testing this to confirm we are "making impossible states impossible" in the UI,
// and document the expectation in this scenario. For clarity,
// we do not expect a 'RUNNING` current and next run at the same time, so
// the data below represents an invalid state.

result.currentProject.cloudProject.runByNumber = {
...CloudRunStubs.running,
runNumber: 1,
completedInstanceCount: 2,
totalInstanceCount: 3,
} as typeof test

result.currentProject.cloudProject.nextRun = {
...CloudRunStubs.running,
runNumber: 1,
completedInstanceCount: 5,
totalInstanceCount: 6,
} as typeof test
}
},
render: (gqlVal) => <DebugContainer gql={gqlVal} />,
})

cy.findByTestId('debug-header').should('be.visible')
cy.findByTestId('debug-pending-splash')
.should('be.visible')
.within(() => {
cy.findByTestId('debug-pending-counts').should('have.text', '0 of 0 specs completed')
})

cy.findByTestId('newer-relevant-run').should('not.exist')
})

it('renders specs and tests when completed run available', () => {
cy.mountFragment(DebugSpecsFragmentDoc, {
variableTypes: DebugSpecVariableTypes,
Expand Down
10 changes: 5 additions & 5 deletions packages/app/src/debug/DebugContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@
:gql="run"
:commits-ahead="props.commitsAhead"
/>
<DebugNewRelevantRunBar
v-if="newerRelevantRun"
:gql="newerRelevantRun"
/>

<DebugPendingRunSplash
v-if="isFirstPendingRun"
class="mt-12"
/>
<DebugNewRelevantRunBar
v-else-if="newerRelevantRun"
:gql="newerRelevantRun"
/>

<template v-else>
<DebugPageDetails
v-if="shouldDisplayDetails(run.status, run.isHidden)"
Expand Down
61 changes: 30 additions & 31 deletions packages/data-context/src/sources/RelevantRunSpecsDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import debugLib from 'debug'
import { isEqual } from 'lodash'

import type { DataContext } from '../DataContext'
import type { CloudSpecStatus, Query, RelevantRun, CurrentProjectRelevantRunSpecs, CloudSpecRun, CloudRun } from '../gen/graphcache-config.gen'
import type { Query, RelevantRun, CurrentProjectRelevantRunSpecs, CloudRun } from '../gen/graphcache-config.gen'
import { Poller } from '../polling'
import type { CloudRunStatus } from '@packages/graphql/src/gen/cloud-source-types.gen'

Expand All @@ -15,6 +15,8 @@ const RELEVANT_RUN_SPEC_OPERATION_DOC = gql`
id
runNumber
status
completedInstanceCount
totalInstanceCount
specs {
id
status
Expand Down Expand Up @@ -55,8 +57,6 @@ export const SPECS_EMPTY_RETURN: RunSpecReturn = {
statuses: {},
}

const INCOMPLETE_STATUSES: CloudSpecStatus[] = ['RUNNING', 'UNCLAIMED']

export type RunSpecReturn = {
runSpecs: CurrentProjectRelevantRunSpecs
statuses: {
Expand Down Expand Up @@ -86,23 +86,9 @@ export class RelevantRunSpecsDataSource {
return this.#cached.runSpecs
}

#calculateSpecMetadata (specs: CloudSpecRun[]) {
//mimic logic in Cloud to sum up the count of groups per spec to give the total spec counts
const countGroupsForSpec = (specs: CloudSpecRun[]) => {
return specs.map((spec) => spec.groupIds?.length || 0).reduce((acc, curr) => acc += curr, 0)
}

return {
totalSpecs: countGroupsForSpec(specs),
completedSpecs: countGroupsForSpec(specs.filter((spec) => !INCOMPLETE_STATUSES.includes(spec.status || 'UNCLAIMED'))),
}
}

/**
* Pulls runs from the current Cypress Cloud account and determines which runs are considered:
* - "current" the most recent completed run, or if not found, the most recent running run
* - "next" the most recent running run if a completed run is found
* @param shas list of Git commit shas to query the Cloud with for matching runs
* Pulls the specs that match the relevant run.
* @param runs - the current and (optionally) next relevant run
*/
async getRelevantRunSpecs (runs: RelevantRun): Promise<RunSpecReturn> {
const projectSlug = await this.ctx.project.projectId()
Expand Down Expand Up @@ -147,28 +133,40 @@ export class RelevantRunSpecsDataSource {
}
}

function isValidNumber (value: unknown): value is number {
return Number.isFinite(value)
}

if (cloudProject?.__typename === 'CloudProject') {
const runSpecsToReturn: RunSpecReturn = {
runSpecs: {},
statuses: {},
}

if (cloudProject.current && cloudProject.current.runNumber && cloudProject.current.status) {
runSpecsToReturn.runSpecs.current = {
...this.#calculateSpecMetadata(cloudProject.current.specs || []),
runNumber: cloudProject.current.runNumber,
const { current, next } = cloudProject

const formatCloudRunInfo = (cloudRunDetails: Partial<CloudRun>) => {
const { runNumber, totalInstanceCount, completedInstanceCount } = cloudRunDetails

if (runNumber && isValidNumber(totalInstanceCount) && isValidNumber(completedInstanceCount)) {
return {
totalSpecs: totalInstanceCount,
completedSpecs: completedInstanceCount,
runNumber,
}
}

runSpecsToReturn.statuses.current = cloudProject.current.status
return undefined
}

if (cloudProject.next && cloudProject.next.runNumber && cloudProject.next.status) {
runSpecsToReturn.runSpecs.next = {
...this.#calculateSpecMetadata(cloudProject.next.specs || []),
runNumber: cloudProject.next.runNumber,
}
if (current && current.status) {
runSpecsToReturn.runSpecs.current = formatCloudRunInfo(current)
runSpecsToReturn.statuses.current = current.status
}

runSpecsToReturn.statuses.next = cloudProject.next.status
if (next && next.status) {
runSpecsToReturn.runSpecs.next = formatCloudRunInfo(next)
runSpecsToReturn.statuses.next = next.status
}

return runSpecsToReturn
Expand All @@ -193,6 +191,7 @@ export class RelevantRunSpecsDataSource {

debug(`Spec data is `, specs)

const wasWatchingCurrentProject = this.#cached.statuses.current === 'RUNNING'
const specCountsChanged = !isEqual(specs.runSpecs, this.#cached.runSpecs)
const statusesChanged = !isEqual(specs.statuses, this.#cached.statuses)

Expand All @@ -208,7 +207,7 @@ export class RelevantRunSpecsDataSource {
debug('Run statuses changed')
const projectSlug = await this.ctx.project.projectId()

if (projectSlug) {
if (projectSlug && wasWatchingCurrentProject) {
debug(`Invalidate cloudProjectBySlug ${projectSlug}`)
await this.ctx.cloud.invalidate('Query', 'cloudProjectBySlug', { slug: projectSlug })
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { expect } from 'chai'
import sinon from 'sinon'

import { DataContext } from '../../../src'
import { createTestDataContext } from '../helper'
import { RelevantRunSpecsDataSource, SPECS_EMPTY_RETURN } from '../../../src/sources'
import { FAKE_PROJECT_ONE_RUNNING_RUN_ONE_COMPLETED_THREE_SPECS, FAKE_PROJECT_ONE_RUNNING_RUN_ONE_SPEC } from './fixtures/graphqlFixtures'

describe('RelevantRunSpecsDataSource', () => {
let ctx: DataContext
let dataSource: RelevantRunSpecsDataSource

beforeEach(() => {
ctx = createTestDataContext('open')
dataSource = new RelevantRunSpecsDataSource(ctx)
sinon.stub(ctx.project, 'projectId').resolves('test123')
})

describe('getRelevantRunSpecs()', () => {
it('returns no specs or statuses when no specs found for run', async () => {
const result = await dataSource.getRelevantRunSpecs({ current: 11111, next: 22222, commitsAhead: 0 })

expect(result).to.eql(SPECS_EMPTY_RETURN)
})

it('returns expected specs and statuses when one run is found', async () => {
sinon.stub(ctx.cloud, 'executeRemoteGraphQL').resolves(FAKE_PROJECT_ONE_RUNNING_RUN_ONE_SPEC)

const result = await dataSource.getRelevantRunSpecs({ current: 1, next: null, commitsAhead: 0 })

expect(result).to.eql({
runSpecs: {
current: {
runNumber: 1,
completedSpecs: 1,
totalSpecs: 1,
},
},
statuses: { current: 'RUNNING' },
})
})

it('returns expected specs and statuses when one run is completed and one is running', async () => {
sinon.stub(ctx.cloud, 'executeRemoteGraphQL').resolves(FAKE_PROJECT_ONE_RUNNING_RUN_ONE_COMPLETED_THREE_SPECS)

const result = await dataSource.getRelevantRunSpecs({ current: 1, next: null, commitsAhead: 0 })

expect(result).to.eql({
runSpecs: {
current: {
runNumber: 1,
completedSpecs: 3,
totalSpecs: 3,
},
next: {
runNumber: 2,
completedSpecs: 0,
totalSpecs: 3,
},
},
statuses: {
current: 'PASSED',
next: 'RUNNING',
},
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,42 @@ export const FAKE_PROJECT_NO_RUNS = { data: { cloudProjectBySlug: { __typename:

export const FAKE_PROJECT_ONE_RUNNING_RUN = { data: { cloudProjectBySlug: { __typename: 'CloudProject', runsByCommitShas: [{ runNumber: 1, status: 'RUNNING', commitInfo: { sha: FAKE_SHAS[0] } }] } } }

export const FAKE_PROJECT_MULTIPLE_COMPLETED = { data: { cloudProjectBySlug: { __typename: 'CloudProject', runsByCommitShas: [{ runNumber: 4, status: 'FAILED', commitInfo: { sha: FAKE_SHAS[1] } }, { runNumber: 1, status: 'PASSED', commitInfo: { sha: FAKE_SHAS[0] } }] } } }
export const FAKE_PROJECT_MULTIPLE_COMPLETED = { data: { cloudProjectBySlug: { __typename: 'CloudProject', runsByCommitShas: [
{ runNumber: 4, status: 'FAILED', commitInfo: { sha: FAKE_SHAS[1] } }, { runNumber: 1, status: 'PASSED', commitInfo: { sha: FAKE_SHAS[0] } },
] } } }

export const FAKE_PROJECT_MULTIPLE_COMPLETED_PLUS_RUNNING = { data: { cloudProjectBySlug: { __typename: 'CloudProject', runsByCommitShas: [{ runNumber: 5, status: 'RUNNING', commitInfo: { sha: FAKE_SHAS[2] } }, { runNumber: 4, status: 'FAILED', commitInfo: { sha: FAKE_SHAS[1] } }, { runNumber: 1, status: 'PASSED', commitInfo: { sha: FAKE_SHAS[0] } }] } } }

export const FAKE_PROJECT_ONE_RUNNING_RUN_ONE_SPEC = {
data: {
cloudProjectBySlug: {
__typename: 'CloudProject',
current: {
runNumber: 1,
completedInstanceCount: 1,
totalInstanceCount: 1,
status: 'RUNNING',
},
},
},
}

export const FAKE_PROJECT_ONE_RUNNING_RUN_ONE_COMPLETED_THREE_SPECS = {
data: {
cloudProjectBySlug: {
__typename: 'CloudProject',
current: {
runNumber: 1,
status: 'PASSED',
completedInstanceCount: 3,
totalInstanceCount: 3,
},
next: {
runNumber: 2,
status: 'RUNNING',
completedInstanceCount: 0,
totalInstanceCount: 3,
},
},
},
}
Loading

4 comments on commit f61995b

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on f61995b Feb 13, 2023

Choose a reason for hiding this comment

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

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.6.0/linux-arm64/feature/ct-public-api-f61995b564b6eb28cbac90796fb733d4f01641a0/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on f61995b Feb 13, 2023

Choose a reason for hiding this comment

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

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.6.0/linux-x64/feature/ct-public-api-f61995b564b6eb28cbac90796fb733d4f01641a0/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on f61995b Feb 13, 2023

Choose a reason for hiding this comment

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

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.6.0/darwin-x64/feature/ct-public-api-f61995b564b6eb28cbac90796fb733d4f01641a0/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on f61995b Feb 13, 2023

Choose a reason for hiding this comment

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

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.6.0/darwin-arm64/feature/ct-public-api-f61995b564b6eb28cbac90796fb733d4f01641a0/cypress.tgz

Please sign in to comment.