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

test: final ct-audit tests and component tweaks #19948

Merged
merged 45 commits into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0fb7178
update card markup to be more accessible
marktnoonan Jan 27, 2022
59ac5ef
give button disabled attr when card is disabled
marktnoonan Jan 27, 2022
a9d69c5
switch test from tabindex to checking button
marktnoonan Jan 27, 2022
d6d7248
add some card tests
marktnoonan Jan 27, 2022
6a7684e
add cypress-real-events
marktnoonan Jan 27, 2022
2805b34
add cypress-real-events
marktnoonan Jan 27, 2022
9cdc062
update modal i18n
marktnoonan Jan 28, 2022
058d92c
add standard modal component test
marktnoonan Jan 28, 2022
7b45a10
font tweak for header version to match rest of header
marktnoonan Jan 28, 2022
7b33872
fix recovery from no result state, improve keyboard behavior
marktnoonan Jan 28, 2022
99fa6b6
specs list tests and a11y tweaks
marktnoonan Jan 28, 2022
3c61c47
avoid chaining off realPress
marktnoonan Jan 28, 2022
8734f76
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Jan 28, 2022
f1f9518
merge 10.0-release
marktnoonan Jan 28, 2022
822596f
add assertion about copy button behavior when clicked
marktnoonan Jan 28, 2022
69a9ddd
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Jan 28, 2022
b0450d6
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Jan 28, 2022
4ae677f
update project-setup test
marktnoonan Jan 28, 2022
ce0f9c6
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Jan 28, 2022
ff36c01
update tests
marktnoonan Jan 28, 2022
c19ce91
remove copy button test
marktnoonan Jan 28, 2022
e5eb223
ignore 'ResizeObserver loop limit exceeded' error
marktnoonan Jan 28, 2022
619f616
empty commit for percy
marktnoonan Jan 28, 2022
b4d9f23
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Jan 31, 2022
50e8cb8
Update packages/app/src/specs/SpecsList.vue
marktnoonan Jan 31, 2022
50836bf
fix test typing
marktnoonan Jan 31, 2022
cc50696
fix tslint error
marktnoonan Jan 31, 2022
78cc375
test removal of full list snapshot
marktnoonan Jan 31, 2022
38a99a3
remove custom snapshot names
marktnoonan Jan 31, 2022
e567bd8
test percy name order
marktnoonan Jan 31, 2022
8862396
percy
marktnoonan Jan 31, 2022
7d55755
remove specs list snapshots
marktnoonan Jan 31, 2022
6765eb8
fix vlist issue, restore snapshot names
marktnoonan Jan 31, 2022
422844f
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Jan 31, 2022
3ecd0dd
ignore ResizeObserver error in CT
marktnoonan Jan 31, 2022
2e9359d
ignore ResizeObserver error in CT
marktnoonan Jan 31, 2022
317d759
minimize calls to calculateRange
marktnoonan Jan 31, 2022
bd5ebaa
slow down DOM changes in test
marktnoonan Jan 31, 2022
cb9404b
avoid resetting specs in beforeEach
marktnoonan Feb 1, 2022
d8bfbd4
move exception handler
marktnoonan Feb 1, 2022
db89bcc
restore old screenshot naming
marktnoonan Feb 1, 2022
e583107
Merge branch '10.0-release' into UNIFY-693-last-component-tests
marktnoonan Feb 1, 2022
556e0e2
fix failing test
marktnoonan Feb 1, 2022
9a5d20f
update comment
marktnoonan Feb 1, 2022
4135fe1
avoid unnamed snapshots in same test
marktnoonan Feb 1, 2022
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
51 changes: 20 additions & 31 deletions packages/app/cypress/e2e/index.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ describe('App: Index', () => {
// With no specs present, the page renders two cards, one for scaffolding example specs,
// another for creating a new blank spec.
cy.findAllByTestId('card').eq(0).as('ScaffoldCard')
.should('have.attr', 'tabindex', '0')
.within(() => {
cy.findByRole('heading', {
level: 2, name: defaultMessages.createSpec.e2e.importFromScaffold.header,
cy.findByRole('button', {
name: defaultMessages.createSpec.e2e.importFromScaffold.header,
}).should('be.visible')
.and('not.be.disabled')

cy.contains(defaultMessages.createSpec.e2e.importFromScaffold.description)
.should('be.visible')
})

cy.findAllByTestId('card').eq(1).as('EmptySpecCard')
.should('have.attr', 'tabindex', '0')
.within(() => {
cy.findByRole('heading', {
level: 2, name: defaultMessages.createSpec.e2e.importEmptySpec.header,
cy.findByRole('button', {
name: defaultMessages.createSpec.e2e.importEmptySpec.header,
}).should('be.visible')
.and('not.be.disabled')

cy.contains(defaultMessages.createSpec.e2e.importEmptySpec.description)
.should('be.visible')
Expand Down Expand Up @@ -259,11 +259,9 @@ describe('App: Index', () => {

cy.findByRole('dialog', { name: defaultMessages.createSpec.newSpecModalTitle }).within(() => {
cy.findAllByTestId('card').eq(0)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.e2e.importFromScaffold.header)

cy.findAllByTestId('card').eq(1)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.e2e.importEmptySpec.header)
})
})
Expand Down Expand Up @@ -305,24 +303,22 @@ describe('App: Index', () => {
// With no specs present, the page renders two cards, one for creating from found components,
// another for creating from found stories.
cy.findAllByTestId('card').eq(0).as('ComponentCard')
.should('have.attr', 'tabindex', '0')
.within(() => {
cy.findByRole('heading', {
level: 2,
cy.findByRole('button', {
name: defaultMessages.createSpec.component.importFromComponent.header,
}).should('be.visible')
.and('not.be.disabled')

cy.contains(defaultMessages.createSpec.component.importFromComponent.description)
.should('be.visible')
})

cy.findAllByTestId('card').eq(1).as('StoryCard')
.should('have.attr', 'tabindex', '0')
.within(() => {
cy.findByRole('heading', {
level: 2,
cy.findByRole('button', {
name: defaultMessages.createSpec.component.importFromStory.header,
}).should('be.visible')
.and('not.be.disabled')

cy.contains(defaultMessages.createSpec.component.importFromStory.description)
.should('be.visible')
Expand Down Expand Up @@ -458,11 +454,10 @@ describe('App: Index', () => {
name: defaultMessages.createSpec.newSpecModalTitle,
}).within(() => {
cy.findAllByTestId('card').eq(0)
.should('have.attr', 'tabindex', '0')

// the storybook card remains enabled here
cy.findAllByTestId('card').eq(1)
.should('have.attr', 'tabindex', '0')
cy.contains('button', defaultMessages.createSpec.component.importFromStory.header)
.should('not.be.disabled')
})
})
})
Expand All @@ -479,22 +474,22 @@ describe('App: Index', () => {
// another for creating from found stories. The story card is disabled due to storybook not
// being configured for the scaffolded project.
cy.findAllByTestId('card').eq(0).as('ComponentCard')
.should('have.attr', 'tabindex', '0')
.within(() => {
cy.findByRole('heading', {
level: 2, name: defaultMessages.createSpec.component.importFromComponent.header,
cy.findByRole('button', {
name: defaultMessages.createSpec.component.importFromComponent.header,
}).should('be.visible')
.and('not.be.disabled')

cy.contains(defaultMessages.createSpec.component.importFromComponent.description)
.should('be.visible')
})

cy.findAllByTestId('card').eq(1).as('StoryCard')
.should('have.attr', 'tabindex', '-1')
.within(() => {
cy.findByRole('heading', {
level: 2, name: defaultMessages.createSpec.component.importFromStory.header,
cy.findByRole('button', {
name: defaultMessages.createSpec.component.importFromStory.header,
}).should('be.visible')
.and('be.disabled')

cy.contains(defaultMessages.createSpec.component.importFromStory.notSetupDescription)
.should('be.visible')
Expand Down Expand Up @@ -614,11 +609,10 @@ describe('App: Index', () => {
// another spec.
cy.findByRole('dialog', { name: defaultMessages.createSpec.newSpecModalTitle }).within(() => {
cy.findAllByTestId('card').eq(0)
.should('have.attr', 'tabindex', '0')

// the storybook card remains disabled here
cy.findAllByTestId('card').eq(1)
.should('have.attr', 'tabindex', '-1')
cy.contains('button', defaultMessages.createSpec.component.importFromStory.header)
.should('be.disabled')
})
})

Expand Down Expand Up @@ -702,11 +696,9 @@ describe('App: Index', () => {

cy.findByRole('dialog', { name: defaultMessages.createSpec.newSpecModalTitle }).within(() => {
cy.findAllByTestId('card').eq(0)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.component.importFromComponent.description)

cy.findAllByTestId('card').eq(1)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.component.importFromStory.description)
})
})
Expand All @@ -716,7 +708,6 @@ describe('App: Index', () => {

cy.findByRole('dialog', { name: defaultMessages.createSpec.newSpecModalTitle }).within(() => {
cy.findAllByTestId('card').eq(0)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.component.importFromComponent.description).click()
})

Expand All @@ -735,7 +726,6 @@ describe('App: Index', () => {

cy.findByRole('dialog', { name: defaultMessages.createSpec.newSpecModalTitle }).within(() => {
cy.findAllByTestId('card').eq(0)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.component.importFromComponent.description).click()
})

Expand Down Expand Up @@ -766,7 +756,6 @@ describe('App: Index', () => {

cy.findByRole('dialog', { name: defaultMessages.createSpec.newSpecModalTitle }).within(() => {
cy.findAllByTestId('card').eq(1)
.should('have.attr', 'tabindex', '0')
.and('contain', defaultMessages.createSpec.component.importFromStory.description).click()
})

Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/settings/project/RecordKey.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ describe('<RecordKey />', () => {
.should('be.visible')
.and('not.be.disabled')

cy.percySnapshot('RecordKey appearance')
cy.percySnapshot()
})
})
8 changes: 5 additions & 3 deletions packages/app/src/specs/RowDirectory.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template>
<div
class="h-full grid gap-8px grid-cols-[14px,16px,auto] items-center"
<button
class="h-full grid gap-8px grid-cols-[14px,16px,auto] items-center focus:outline-transparent"
:data-cy="`row-directory-depth-${depth}`"
:aria-expanded="expanded"
>
<i-cy-chevron-down-small_x16
class="
Expand All @@ -20,7 +21,8 @@
class="font-medium text-gray-600"
highlight-classes="text-gray-1000"
/>
</div>
<span class="sr-only">{{ expanded ? 'collapse' : 'expand' }}</span>
</button>
</template>

<script lang="ts" setup>
Expand Down
67 changes: 63 additions & 4 deletions packages/app/src/specs/SpecsList.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,25 @@ let specs: Array<SpecsListFragment> = []
describe('<SpecsList />', { keystrokeDelay: 0 }, () => {
context('when testingType is unset', () => {
beforeEach(() => {
const showCreateSpecModalSpy = cy.spy().as('showCreateSpecModalSpy')

cy.mountFragment(Specs_SpecsListFragmentDoc, {
onResult: (ctx) => {
specs = ctx.currentProject?.specs || []

return ctx
},
render: (gqlVal) => {
return <SpecsList gql={gqlVal} />
return <SpecsList gql={gqlVal} onShowCreateSpecModal={showCreateSpecModalSpy} />
},
})
})

it('should filter specs', () => {
// make sure things have rendered for snapshot
cy.get('[data-cy="specs-list-row"]').should('have.length.above', 2)
cy.percySnapshot()

const longestSpec = specs.reduce((acc, spec) =>
acc.relative.length < spec.relative.length ? spec : acc
, specs[0])
Expand All @@ -49,28 +55,81 @@ describe('<SpecsList />', { keystrokeDelay: 0 }, () => {
.should('not.exist')

cy.contains(`${defaultMessages.specPage.noResultsMessage} garbage 🗑`)

cy.percySnapshot()
cy.get('[data-cy="no-results-clear"]').click()
cy.get('@specsListInput').invoke('val').should('be.empty')

// validate that something re-populated in the specs list
cy.get('[data-cy="specs-list-row"]').should('have.length.above', 2)

cy.get('@specsListInput').type(longestSpec.fileName)
cy.get(rowSelector).first().should('contain', longestSpec.relative.replace(`/${longestSpec.fileName}${longestSpec.specFileExtension}`, ''))
cy.get(rowSelector).last().should('contain', `${longestSpec.fileName}${longestSpec.specFileExtension}`)
cy.get(rowSelector).last().within(() => {
cy.contains('a', longestSpec.baseName)
.should('be.visible')
.and('have.attr', 'href', `#/specs/runner?file=${longestSpec.relative}`)
})

const directory = longestSpec.relative.slice(0, longestSpec.relative.lastIndexOf('/'))

cy.get('@specsListInput').clear().type(directory)
cy.get(rowSelector).first().should('contain', directory)

cy.percySnapshot()
})

it('should close directories', () => {
it('should close directories with click', () => {
// close all directories
const directories: string[] = Array.from(new Set(specs.map((spec) => spec.relative.split('/')[0]))).sort()

directories.forEach((dir) => {
cy.get('[data-cy="row-directory-depth-0"]').contains(dir).click()
cy.contains('button[data-cy="row-directory-depth-0"]', dir)
.should('have.attr', 'aria-expanded', 'true')
.click()
.should('have.attr', 'aria-expanded', 'false')
})

cy.get('[data-cy="spec-item"]').should('not.exist')

cy.percySnapshot()
})

it('should close directories with Enter', () => {
const directories: string[] = Array.from(new Set(specs.map((spec) => spec.relative.split('/')[0]))).sort()

directories.forEach((dir) => {
cy.contains('button[data-cy="row-directory-depth-0"]', dir)
.should('have.attr', 'aria-expanded', 'true')
.focus()
.realPress('Enter')

cy.contains('button[data-cy="row-directory-depth-0"]', dir)
.should('have.attr', 'aria-expanded', 'false')
})

cy.get('[data-cy="spec-item"]').should('not.exist')
})

it('should close directories with Space', () => {
const directories: string[] = Array.from(new Set(specs.map((spec) => spec.relative.split('/')[0]))).sort()

directories.forEach((dir) => {
cy.contains('button[data-cy="row-directory-depth-0"]', dir)
.should('have.attr', 'aria-expanded', 'true')
.focus()
.realPress('Space')

cy.contains('button[data-cy="row-directory-depth-0"]', dir)
.should('have.attr', 'aria-expanded', 'false')
})

cy.get('[data-cy="spec-item"]').should('not.exist')
})

it('should emit an event to open new spec modal', () => {
cy.contains(defaultMessages.createSpec.newSpec).click()
cy.get('@showCreateSpecModalSpy').should('have.been.calledOnce')
})
})

Expand Down
26 changes: 22 additions & 4 deletions packages/app/src/specs/SpecsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
@close="showSpecPatternModal = false"
/>

<template
v-if="specs.length"
<div
Copy link
Contributor

Choose a reason for hiding this comment

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

How can a conditional with v-if be stable in the first place (what does stable mean, exactly?)

If we need a DOM node that will never change, we could use the v-once directive.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good point, "stable" was vague. I've moved the comment to be better placed, and updated the wording. The part with v-if is OK coming and going, it's the next element that shouldn't follow the v-if rule. We would have liked to use v-show but it clashed with tailwind's grid class.

Maybe it's overdoing it to have this long comment in the first place, I think some "here be dragons" is useful for future developers, but there are tests now to catch regressions of the specific stuff this fixes.

Copy link
Contributor

Choose a reason for hiding this comment

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

I still don't fully understand the problem, I guess I need to pull and try it out - but that's okay, the comment should suffice. Hopefully I don't need to edit this anytime soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The specific problem that this change fixes is that if the container element is not present in the DOM with when this code runs, it doesn't update the virtualized list, so "clearing the search" from the no-results state was broken, as the old v-if had removed the element needed. @ZachJW34 has some ideas for making this more robust in general when we get a chance. But in this PR I just wanted to fix the bug I found when adding the coverage for that behavior, and give a bit of a here-be-dragons, because some of my first changes seemed to work great locally, but had actually broken the virtualization.

v-show="specs.length"
>
<div
class="grid grid-cols-2 children:font-medium children:text-gray-800"
Expand All @@ -41,13 +41,17 @@
>
<SpecsListRowItem
v-for="row in list"
:id="getIdIfDirectory(row)"
:key="row.index"
>
<template #file>
<RouterLink
v-if="row.data.isLeaf && row.data"
:key="row.data.data?.absolute"
class="focus:outline-transparent"
:to="{ path: '/specs/runner', query: { file: row.data.data?.relative } }"
@click.meta.prevent="handleCtrlClick"
@click.ctrl.prevent="handleCtrlClick"
>
<SpecItem
:file-name="row.data.data?.fileName || row.data.name"
Expand All @@ -64,6 +68,7 @@
:depth="row.data.depth - 2"
:style="{ paddingLeft: `${((row.data.depth - 2) * 10) + 16}px` }"
:indexes="getDirIndexes(row.data)"
:aria-controls="getIdIfDirectory(row)"
Copy link
Contributor

@tbiethman tbiethman Jan 28, 2022

Choose a reason for hiding this comment

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

Just a general Vue question, is there a way to execute arbitrary code from within a template loop? Say, if getIdIfDirectory() were a lot more expensive than it currently is and you only want to execute it once. Or would you just compute your view data ahead of time and iterate over that instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, you'd have a computed that uses map or whatever to make a new set of rows with the extra attribute, and that would be cached until, say, the data updates when a file is added.

@click="row.data.toggle"
/>
</template>
Expand All @@ -77,9 +82,9 @@
</SpecsListRowItem>
</div>
</div>
</template>
</div>
<NoResults
v-else
v-show="!specs.length"
:search="search"
:message="t('specPage.noResultsMessage')"
class="mt-56px"
Expand Down Expand Up @@ -175,6 +180,19 @@ const { containerProps, list, wrapperProps, scrollTo } = useVirtualList(treeSpec
// If you are scrolled down the virtual list and list changes,
// reset scroll position to top of list
watch(() => treeSpecList.value, () => scrollTo(0))

function handleCtrlClick () {
// noop intended to reduce the chances of opening tests multiple tabs
// which is not a supported state in Cypress
}

function getIdIfDirectory (row) {
if (row.data.isLeaf && row.data) {
return undefined
}

return `speclist-${row.data.data.relative.replace(row.data.data.baseName, '')}`
}
</script>

<style scoped>
Expand Down
4 changes: 1 addition & 3 deletions packages/app/src/specs/SpecsListRowItem.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<template>
<div
class="grid grid-cols-2 outline-none children:cursor-pointer group h-full"
class="h-full outline-none ring-inset grid grid-cols-2 group focus-within:ring-indigo-300 focus-within:ring-1 children:cursor-pointer"
data-cy="specs-list-row"
role="link"
tabindex="0"
>
<div>
<slot name="file" />
Expand Down
2 changes: 2 additions & 0 deletions packages/data-context/src/sources/migration/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* eslint-disable padding-line-between-statements */
// created by autobarrel, do not modify directly

export * from './autoRename'
export * from './regexps'
export * from './shouldShowSteps'
2 changes: 2 additions & 0 deletions packages/frontend-shared/cypress/e2e/support/e2eSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ function validateExternalLink (subject, options: ValidateExternalLinkOptions | s
})
}

Cypress.on('uncaught:exception', (err) => !err.message.includes('ResizeObserver loop limit exceeded'))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is here because in CI there were 16 tests failing due to the error. The error itself is safe to ignore in that it doesn't represent something that blows up the UI and it's debatable that it should be an error instead of a warning. I'm open to suggestions for handling this differently though.

Copy link
Contributor

Choose a reason for hiding this comment

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

This exception has been a pain in the butt forever.


Cypress.Commands.add('scaffoldProject', scaffoldProject)
Cypress.Commands.add('addProject', addProject)
Cypress.Commands.add('openGlobalMode', openGlobalMode)
Expand Down
Loading