Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: run failures enhancements #25234

Merged
merged 8 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 40 additions & 28 deletions packages/app/src/debug/DebugContainer.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DebugSpecsFragmentDoc } from '../generated/graphql-test'
import { DebugSpecListGroupsFragment, DebugSpecListSpecFragment, DebugSpecListTestsFragment, DebugSpecsFragmentDoc } from '../generated/graphql-test'
import DebugContainer from './DebugContainer.vue'
import { defaultMessages } from '@cy/i18n'
import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store'
Expand Down Expand Up @@ -83,69 +83,81 @@ describe('<DebugContainer />', () => {
})

describe('testing util function: debugMapping', () => {
const createSpecs = (idArr: string[]) => {
const acc: {id: string}[] = []

idArr.forEach((val) => {
acc.push({ id: val })
})

return acc
}

it('maps correctly for a single spec', () => {
const spec = createSpecs(['a1c'])
const specs = [
{ id: 'a1c', groupIds: ['a'] },
] as DebugSpecListSpecFragment[]
const tests = [
{ specId: 'a1c', id: 'random1' },
{ specId: 'a1c', id: 'random2' },
]
] as DebugSpecListTestsFragment[]
const groups = [
{ id: 'a', testingType: 'e2e' },
{ id: 'b', testingType: 'e2e' },
] as DebugSpecListGroupsFragment[]

const debugMappingArray = specsList(spec, tests)
const debugMappingArray = specsList({ specs, tests, groups, localSpecs: [], currentTestingType: 'e2e' })
Copy link
Contributor

@lmiller1990 lmiller1990 Dec 22, 2022

Choose a reason for hiding this comment

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

Now we are on Vite 4 you should have an easier time adding vitest to the code base. Not needed for this PR to merge, but I'd like to start using Vitest more for non UI tests - as much as I like dogfooding Cypress, it really doesn't feel like the right tool for this class of test.

I know we are on the same page about this, just noting it here since we couldn't add Vitest before, but now we should be able to. Feel free to add next time you see a chance! It's fast ⚡


expect(debugMappingArray).to.have.length(1)
expect(debugMappingArray[0]).to.deep.equal({ spec: { id: 'a1c' }, tests: [{ specId: 'a1c', id: 'random1' }, { specId: 'a1c', id: 'random2' }] })
expect(debugMappingArray[0]).to.deep.equal({ spec: { id: 'a1c', groupIds: ['a'] }, tests: [{ specId: 'a1c', id: 'random1' }, { specId: 'a1c', id: 'random2' }], groups: [{ id: 'a', testingType: 'e2e' }], foundLocally: false, testingType: 'e2e', matchesCurrentTestingType: true })
})

it('maps correctly for multiple specs and test', () => {
const specs = createSpecs(['123', '456', '789'])
const specs = [
{ id: '123', groupIds: ['a'] },
{ id: '456', groupIds: ['b'] },
{ id: '789', groupIds: ['a', 'b'] },
] as DebugSpecListSpecFragment[]
const tests = [
{ specId: '123', id: 'random1' },
{ specId: '456', id: 'random2' },
{ specId: '456', id: 'random3' },
{ specId: '789', id: 'random4' },
{ specId: '123', id: 'random6' },
]
] as DebugSpecListTestsFragment[]
const groups = [
{ id: 'a', testingType: 'e2e' },
{ id: 'b', testingType: 'e2e' },
] as DebugSpecListGroupsFragment[]

const debugMappingArray = specsList(specs, tests)
const debugMappingArray = specsList({ specs, tests, localSpecs: [], currentTestingType: 'e2e', groups })

const expected = [
{ spec: { id: '123' }, tests: [{ specId: '123', id: 'random1' }, { specId: '123', id: 'random6' }] },
{ spec: { id: '456' }, tests: [{ specId: '456', id: 'random2' }, { specId: '456', id: 'random3' }] },
{ spec: { id: '789' }, tests: [{ specId: '789', id: 'random4' }] },
{ spec: { id: '123', groupIds: ['a'] }, tests: [{ specId: '123', id: 'random1' }, { specId: '123', id: 'random6' }], groups: [{ id: 'a', testingType: 'e2e' }], foundLocally: false, testingType: 'e2e', matchesCurrentTestingType: true },
{ spec: { id: '456', groupIds: ['b'] }, tests: [{ specId: '456', id: 'random2' }, { specId: '456', id: 'random3' }], groups: [{ id: 'b', testingType: 'e2e' }], foundLocally: false, testingType: 'e2e', matchesCurrentTestingType: true },
{ spec: { id: '789', groupIds: ['a', 'b'] }, tests: [{ specId: '789', id: 'random4' }], groups: [{ id: 'a', testingType: 'e2e' }, { id: 'b', testingType: 'e2e' }], foundLocally: false, testingType: 'e2e', matchesCurrentTestingType: true },
]

expect(debugMappingArray).to.deep.equal(expected)
})

it('maps does not show specs that do not have tests', () => {
const specs = createSpecs(['123', '456', '789'])
const tests = [{ specId: '123', id: 'random1' }]
const specs = [
{ id: '123', groupIds: ['a'] },
{ id: '456', groupIds: ['a'] },
{ id: '789', groupIds: ['a'] },
] as DebugSpecListSpecFragment[]
const tests = [{ specId: '123', id: 'random1' }] as DebugSpecListTestsFragment[]
const groups = [{ id: 'a', testingType: 'e2e' }] as DebugSpecListGroupsFragment[]

const debugMappingArray = specsList(specs, tests)
const debugMappingArray = specsList({ specs, tests, localSpecs: [], currentTestingType: 'e2e', groups })

expect(debugMappingArray).to.have.length(1)
expect(debugMappingArray).to.deep.equal([{ spec: { id: '123' }, tests: [{ specId: '123', id: 'random1' }] }])
expect(debugMappingArray).to.deep.equal([{ spec: { id: '123', groupIds: ['a'] }, tests: [{ specId: '123', id: 'random1' }], groups: [{ id: 'a', testingType: 'e2e' }], foundLocally: false, testingType: 'e2e', matchesCurrentTestingType: true }])
})

it('throws an error when a test does not map to a spec', () => {
const specs = createSpecs(['123'])
const specs = [
{ id: '123', groupIds: ['a'] },
] as DebugSpecListSpecFragment[]
const tests = [
{ specId: '123', id: 'random1' },
{ specId: '456', id: 'random2' },
]
] as DebugSpecListTestsFragment[]
const groups = [{ id: 'a' }] as DebugSpecListGroupsFragment[]

const specsListWrapper = () => {
return specsList(specs, tests)
return specsList({ specs, tests, groups, localSpecs: [], currentTestingType: 'e2e' })
}

expect(specsListWrapper).to.throw('Could not find spec for id 456')
Expand Down
36 changes: 31 additions & 5 deletions packages/app/src/debug/DebugContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<script setup lang="ts">
import { gql } from '@urql/vue'
import { computed } from '@vue/reactivity'
import type { DebugSpecsFragment } from '../generated/graphql'
import type { DebugSpecsFragment, TestingTypeEnum } from '../generated/graphql'
import { useLoginConnectStore } from '@packages/frontend-shared/src/store/login-connect-store'
import DebugPageHeader from './DebugPageHeader.vue'
import DebugSpecList from './DebugSpecList.vue'
Expand All @@ -46,6 +46,14 @@ import { specsList } from './utils/DebugMapping'

const { t } = useI18n()

gql`
fragment DebugLocalSpecs on Spec {
id
absolute
relative
}
`

gql`
fragment DebugSpecs on Query {
currentProject {
Expand All @@ -54,7 +62,7 @@ fragment DebugSpecs on Query {
__typename
... on CloudProject {
id
runByNumber(runNumber: 6) {
runByNumber(runNumber: 11) {
...DebugPage
id
runNumber
Expand All @@ -69,9 +77,18 @@ fragment DebugSpecs on Query {
id
...DebugSpecListSpec
}
groups {
id,
...DebugSpecListGroups
}
}
}
}
specs {
id
...DebugLocalSpecs
}
currentTestingType
}
}
`
Expand All @@ -87,14 +104,23 @@ const run = computed(() => {
})

const debugSpecsArray = computed(() => {
if (run.value) {
if (run.value && props.gql.currentProject) {
const specs = run.value.specs || []
const tests = run.value.testsForReview || []
const groups = run.value.groups || []
// Will be defined so ignore the possibility of null for testingType
const currentTestingType = props.gql.currentProject.currentTestingType as TestingTypeEnum
const localSpecs = props.gql.currentProject.specs

return specsList(specs, tests)
return specsList({
specs,
tests,
groups,
localSpecs,
currentTestingType,
})
}

return []
})

</script>
93 changes: 76 additions & 17 deletions packages/app/src/debug/DebugSpec.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Spec, TestResults } from './DebugSpec.vue'
import DebugSpec from './DebugSpec.vue'
import DebugSpec, { Spec, TestResults } from './DebugSpec.vue'
import { defaultMessages } from '@cy/i18n'

describe('<DebugSpec/> with multiple test results', () => {
Expand All @@ -8,6 +7,7 @@ describe('<DebugSpec/> with multiple test results', () => {
path: 'cypress/tests/',
fileName: 'auth',
fileExtension: '.spec.ts',
fullPath: 'cypress/tests/auth.spec.ts',
}

const testResults: TestResults[] = [
Expand All @@ -23,31 +23,20 @@ describe('<DebugSpec/> with multiple test results', () => {

it('mounts correctly', () => {
cy.mount(() => (
<DebugSpec spec={spec} testResults={testResults} />
<DebugSpec spec={spec} testResults={testResults} foundLocally={true} testingType={'e2e'} matchesCurrentTestingType={true} />
))

cy.findByTestId('debug-spec-item').children().should('have.length', 3)
cy.findByTestId('spec-contents').children().should('have.length', 2)
cy.findByTestId('spec-path').should('have.text', 'cypress/tests/auth.spec.ts')
cy.contains('auth').should('be.visible')
cy.findByTestId('run-failures').should('not.be.disabled')
.contains(defaultMessages.debugPage.runFailures)
.contains(defaultMessages.debugPage.runFailures.btn)

cy.findAllByTestId('test-group').should('have.length', 2)

cy.percySnapshot()
})

it('renders correctly with disabled run-failures button', () => {
cy.mount(() => (
<DebugSpec spec={spec} testResults={testResults} isDisabled={true} />
))

cy.findByTestId('run-failures').should('have.attr', 'aria-disabled', 'disabled')
.should('not.have.attr', 'href')

cy.percySnapshot()
})
})

describe('<DebugSpec/> responsive UI', () => {
Expand All @@ -68,10 +57,11 @@ describe('<DebugSpec/> responsive UI', () => {
path: 'cypress/tests/',
fileName: 'AlertBar',
fileExtension: '.spec.ts',
fullPath: 'cypress/tests/AlertBar.spec.ts',
}

cy.mount(() => (
<DebugSpec spec={spec} testResults={testResults} />
<DebugSpec spec={spec} testResults={testResults} foundLocally={true} testingType={'e2e'} matchesCurrentTestingType={true} />
))

cy.findByTestId('spec-contents').children().should('have.length', 2)
Expand All @@ -88,10 +78,11 @@ describe('<DebugSpec/> responsive UI', () => {
path: 'src/shared/frontend/cow/packages/foo/cypress/tests/e2e/components',
fileName: 'AlertBar',
fileExtension: '.spec.ts',
fullPath: 'src/shared/frontend/cow/packages/foo/cypress/tests/e2e/components/AlertBar.spec.ts',
}

cy.mount(() => (
<DebugSpec spec={spec} testResults={testResults} />
<DebugSpec spec={spec} testResults={testResults} foundLocally={true} testingType={'e2e'} matchesCurrentTestingType={true} />
))

cy.findByTestId('spec-path').should('have.css', 'text-overflow', 'ellipsis')
Expand All @@ -100,3 +91,71 @@ describe('<DebugSpec/> responsive UI', () => {
cy.percySnapshot()
})
})

describe('Run Failures button', () => {
const spec: Spec = {
id: '1',
path: 'cypress/tests/',
fileName: 'auth',
fileExtension: '.spec.ts',
fullPath: 'cypress/tests/auth.spec.ts',
}

const testResults: TestResults[] = [
{
id: '2',
titleParts: ['Login', 'Should redirect unauthenticated user to signin page'],
},
{
id: '3',
titleParts: ['Login', 'redirects to stored path after login'],
},
]

it('is disabled if spec is not found locally', () => {
cy.mount(() => <DebugSpec spec={spec} testResults={testResults} foundLocally={false} testingType={'e2e'} matchesCurrentTestingType={true}/>)

cy.findByTestId('run-failures')
.should('have.attr', 'aria-disabled', 'disabled')
.should('not.have.attr', 'href')

cy.findByTestId('run-failures').realHover()

cy.findByTestId('run-all-failures-tooltip').should('be.visible').contains('Spec was not found locally')

cy.percySnapshot()
})

it('is disabled if run testing-type differs from the current testing-type', () => {
cy.mount(() => (
<DebugSpec
spec={spec}
testResults={testResults}
foundLocally={true}
testingType='e2e'
matchesCurrentTestingType={false}
onSwitchTestingType={cy.spy().as('switchTestingType')}/>
))

cy.findByTestId('run-failures')
.should('have.attr', 'aria-disabled', 'disabled')
.should('not.have.attr', 'href')

cy.findByTestId('run-failures').realHover()

cy.findByTestId('run-all-failures-tooltip').should('be.visible').within(() => {
cy.contains('span', 'There are 2 e2e tests failing in this spec. To run them locally switch to e2e testing.')
cy.contains('button', 'Switch to e2e testing').click()

cy.get('@switchTestingType').should('have.been.calledWith', 'e2e')
})
})

it('is enabled if found locally and same testing type', () => {
cy.mount(() => <DebugSpec spec={spec} testResults={testResults} foundLocally={true} testingType={'e2e'} matchesCurrentTestingType={true}/>)

cy.findByTestId('run-failures')
.should('have.attr', 'href', '#/specs/runner?file=cypress/tests/auth.spec.ts')
.and('not.have.attr', 'aria-disabled')
})
})
Loading