Skip to content

Commit

Permalink
fix: run-all UI tweaks and keyboard nav (#24757)
Browse files Browse the repository at this point in the history
Closes undefined
  • Loading branch information
ZachJW34 committed Nov 21, 2022
1 parent 30163cb commit 3328bf4
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 67 deletions.
3 changes: 3 additions & 0 deletions packages/app/cypress/component/support/index.ts
Expand Up @@ -26,8 +26,10 @@ import { createPinia } from '../../../src/store'
import { setActivePinia } from 'pinia'
import type { Pinia } from 'pinia'
import 'cypress-real-events/support'
import 'cypress-plugin-tab'

import { installCustomPercyCommand } from '@packages/frontend-shared/cypress/support/customPercyCommand'
import { tabUntil } from '@packages/frontend-shared/cypress/support/tab-until'

let pinia: Pinia

Expand All @@ -50,3 +52,4 @@ registerMountFn({ plugins: [() => createRouter(), () => pinia] })
installCustomPercyCommand()

Cypress.on('uncaught:exception', (err) => !err.message.includes('ResizeObserver loop limit exceeded'))
Cypress.Commands.add('tabUntil', tabUntil)
4 changes: 2 additions & 2 deletions packages/app/src/specs/InlineRunAllSpecs.vue
Expand Up @@ -9,8 +9,8 @@
size="16"
stroke-color="gray-700"
fill-color="transparent"
hover-stroke-color="indigo-500"
hover-fill-color="indigo-100"
hocus-stroke-color="indigo-500"
hocus-fill-color="indigo-100"
class="inline-flex align-text-bottom"
data-cy="play-button"
@click.stop="emits('runAllSpecs')"
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/specs/InlineSpecListTree.vue
Expand Up @@ -58,7 +58,7 @@
v-if="isRunAllSpecsAllowed"
data-cy="run-all-specs"
:directory="row.data.name"
class="run-all hidden"
class="opacity-0 run-all"
:spec-number="directoryChildren[row.data.id].length"
@runAllSpecs="onRunAllSpecs(row.data.id)"
/>
Expand Down Expand Up @@ -202,8 +202,8 @@ a::before {
}
/** For run all specs group hover to work */
[data-cy=directory-item]:hover .run-all {
display: block !important;
[data-cy=spec-row-item]:hover .run-all, [data-cy=spec-row-item]:focus-within .run-all {
opacity: 1 !important;
}
</style>
6 changes: 3 additions & 3 deletions packages/app/src/specs/RowDirectory.vue
@@ -1,7 +1,7 @@
<template>
<div class="flex items-center h-full">
<div class="flex h-full items-center">
<button
class="grid gap-8px grid-cols-[14px,16px,auto,auto] items-center group focus:outline-none"
class="h-full grid gap-8px grid-cols-[14px,16px,auto] items-center group focus:outline-none"
:data-cy="`row-directory-depth-${depth}`"
:aria-expanded="expanded"
@click.stop="emits('toggle')"
Expand All @@ -16,7 +16,7 @@
<IconFolder class="icon-dark-white icon-light-gray-200" />
<div
:title="name"
class="text-gray-600 truncate flex"
class="flex text-gray-600 truncate"
>
<HighlightedText
:text="name"
Expand Down
29 changes: 21 additions & 8 deletions packages/app/src/specs/SpecsList.cy.tsx
Expand Up @@ -2,14 +2,6 @@ import SpecsList from './SpecsList.vue'
import { Specs_SpecsListFragmentDoc, SpecsListFragment, TestingTypeEnum, SpecFilter_SetPreferencesDocument } from '../generated/graphql-test'
import { defaultMessages } from '@cy/i18n'

const hoverRunAllSpecs = (directory: string, specNumber: number) => {
cy.contains('[data-cy=spec-item-directory]', directory).realHover().then(() => {
cy.get(`[data-cy="run-all-specs-for-${directory}"]`).should('contain.text', ` Run ${specNumber} specs `)
cy.get('[data-cy="play-button"]').should('exist')
cy.percySnapshot()
})
}

describe('<SpecsList />', { keystrokeDelay: 0 }, () => {
let specs: Array<SpecsListFragment>

Expand Down Expand Up @@ -294,17 +286,38 @@ describe('<SpecsList />', { keystrokeDelay: 0 }, () => {
})

describe('Run all Specs', () => {
const hoverRunAllSpecs = (directory: string, specNumber: number) => {
cy.contains('[data-cy=spec-item-directory]', directory).realHover().then(() => {
cy.get(`[data-cy="run-all-specs-for-${directory}"]`).should('contain.text', `Run ${specNumber} spec${specNumber > 1 ? 's' : ''}`)
cy.get('[data-cy="play-button"]').should('exist')
})
}

it('displays runAllSpecs when hovering over a spec-list directory row', () => {
mountWithTestingType({ experimentalRunAllSpecs: true })
hoverRunAllSpecs('__test__', 5)
hoverRunAllSpecs('frontend', 11)
hoverRunAllSpecs('components', 6)

cy.percySnapshot()
})

it('checks if functionality works after a search', () => {
mountWithTestingType({ experimentalRunAllSpecs: true, specFilter: 'base' })
hoverRunAllSpecs('__test__', 2)
hoverRunAllSpecs('frontend/components', 2)
hoverRunAllSpecs('Cell/test', 1)
})

it('can tab into run-all', () => {
mountWithTestingType({ experimentalRunAllSpecs: true })
cy.get('[data-cy=run-all-specs-for-__test__]').should('not.be.visible')

cy.tabUntil(($el) => {
return $el.text().includes('Run 5 specs')
})

cy.get('[data-cy=run-all-specs-for-__test__]').should('be.visible')
})
})
})
13 changes: 10 additions & 3 deletions packages/app/src/specs/SpecsList.vue
Expand Up @@ -83,7 +83,7 @@
:data-cy-row="row.data.data?.baseName"
:is-leaf="row.data.isLeaf"
:is-project-connected="projectConnectionStatus === 'CONNECTED'"
:grid-columns="tableGridColumns"
:grid-columns="row.data.isLeaf ? tableGridColumns : 'grid-cols-[1fr]'"
:route="{ path: '/specs/runner', query: { file: row.data.data?.relative?.replace(/\\/g, '/') } }"
@toggleRow="row.data.toggle"
>
Expand All @@ -110,7 +110,6 @@
:expanded="treeSpecList[row.index].expanded.value"
:depth="row.data.depth - 2"
:style="{ paddingLeft: `${(row.data.depth - 2) * 10}px` }"
class="group"
:indexes="row.data.highlightIndexes"
:is-run-all-specs-allowed="isRunAllSpecsAllowed"
:aria-controls="getIdIfDirectory(row)"
Expand All @@ -119,7 +118,7 @@
<SpecsRunAllSpecs
v-if="isRunAllSpecsAllowed"
:directory="row.data.name"
class="run-all hidden group-hover:block"
class="opacity-0 run-all"
:spec-number="directoryChildren[row.data.id].length"
@runAllSpecs="onRunAllSpecs(row.data.id)"
/>
Expand Down Expand Up @@ -464,4 +463,12 @@ function onRunAllSpecs (rowId: string) {
.spec-list-container {
height: calc(100% - 112px)
}
/**
* Can't put a group on the parent element as it has downstream effects on the styling of child components
* that have individual group stylings.
*/
[data-cy=spec-list-directory]:hover .run-all, [data-cy=spec-list-directory]:focus-within .run-all {
opacity: 1 !important;
}
</style>
4 changes: 2 additions & 2 deletions packages/app/src/specs/SpecsListRowItem.vue
Expand Up @@ -16,7 +16,7 @@
<slot name="file" />
</div>
<template
v-if="lazyRender"
v-if="lazyRender && isLeaf"
>
<div
data-cy="specs-list-row-git-info"
Expand All @@ -42,7 +42,7 @@
<SpecsListHoverCell
data-cy="specs-list-row-average-duration"
:is-hover-disabled="isProjectConnected"
class="hidden md:block group"
class="hidden group md:block"
>
<template #content>
<slot name="average-duration" />
Expand Down
13 changes: 8 additions & 5 deletions packages/app/src/specs/SpecsRunAllSpecs.vue
@@ -1,30 +1,33 @@
<template>
<button
class="group hover:text-indigo-700 space-x-2 items-center ml-28px text-gray-600 whitespace-nowrap"
class="h-full space-x-2 ml-24px text-gray-600 group items-center whitespace-nowrap hocus:text-indigo-700"
:data-cy="`run-all-specs-for-${directory}`"
@click.stop="emits('runAllSpecs')"
>
<IconActionPlaySmall
size="16"
stroke-color="gray-300"
fill-color="gray-50"
hover-stroke-color="indigo-500"
hover-fill-color="indigo-100"
hocus-stroke-color="indigo-500"
hocus-fill-color="indigo-100"
interactive-colors-on-group
class="group-hover: inline-flex align-text-bottom"
class="align-text-bottom group-hover: inline-flex "
data-cy="play-button"
/>
<span
class="font-normal text-sm"
data-cy="run-all-specs-text"
>
Run {{ specNumber }} specs
{{ t('specPage.runAllSpecs', specNumber) }}
</span>
</button>
</template>

<script lang="ts" setup>
import { IconActionPlaySmall } from '@cypress-design/vue-icon'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
defineProps<{
specNumber: number
Expand Down
42 changes: 2 additions & 40 deletions packages/frontend-shared/cypress/support/e2e.ts
Expand Up @@ -17,6 +17,8 @@ import type { E2ETaskMap } from '../e2e/e2ePluginSetup'
import { installCustomPercyCommand } from './customPercyCommand'
import i18n from '../../src/locales/en-US.json'
import { addNetworkCommands } from './onlineNetwork'
import { logInternal } from './utils'
import { tabUntil } from './tab-until'

configure({ testIdAttribute: 'data-cy' })

Expand Down Expand Up @@ -158,10 +160,6 @@ declare global {
* and asserts that it triggers the appropriate mutation when clicked.
*/
validateExternalLink(options: ValidateExternalLinkOptions | string): Chainable<JQuery<HTMLElement>>
/**
* Tabs until the result of fn is true
*/
tabUntil(fn: ($el: JQuery) => boolean, limit?: number): Chainable<any>
/**
* Get the AUT <iframe>. Useful for Cypress in Cypress tests.
*/
Expand Down Expand Up @@ -479,18 +477,6 @@ function taskInternal<T extends keyof E2ETaskMap> (name: T, arg: Parameters<E2ET
return cy.task<Resolved<ReturnType<E2ETaskMap[T]>>>(name, arg, { log: isDebugging, timeout: options.timeout ?? (isDebugging ? NO_TIMEOUT : TEN_SECONDS) })
}

function logInternal<T> (name: string | Partial<Cypress.LogConfig>, cb: (log: Cypress.Log | undefined) => Cypress.Chainable<T>, opts: Partial<Cypress.Loggable> = {}): Cypress.Chainable<T> {
const _log = typeof name === 'string'
? Cypress.log({ name, message: '' })
: Cypress.log(name)

return cb(_log).then<T>((val) => {
_log?.end()

return val
})
}

/**
* Finds a link with the provided text and href, either globally or within a chained subject,
* and asserts that it triggers the appropriate mutation when clicked.
Expand Down Expand Up @@ -520,30 +506,6 @@ function validateExternalLink (subject, options: ValidateExternalLinkOptions | s
})
}

function tabUntil (fn: (el: JQuery<HTMLElement>) => boolean, limit: number = 10) {
function _tabUntil (step: number) {
return cy.tab().focused({ log: false }).then((el) => {
const pass = fn(el)

if (pass) {
return el
}

if (step > limit) {
throw new Error(`Unable to step to element in ${fn.toString()} in ${limit} steps`)
}

return _tabUntil(step + 1)
})
}

return logInternal('tabUntil', () => {
cy.get('body')

return _tabUntil(0)
})
}

function getAutIframe () {
return cy.get('iframe.aut-iframe').its('0.contentDocument.documentElement').then(cy.wrap) as Cypress.Chainable<JQuery<HTMLIFrameElement>>
}
Expand Down
36 changes: 36 additions & 0 deletions packages/frontend-shared/cypress/support/tab-until.ts
@@ -0,0 +1,36 @@
import { logInternal } from './utils'

declare global {
namespace Cypress {
interface Chainable {
/**
* Tabs until the result of fn is true
*/
tabUntil(fn: ($el: JQuery) => boolean, limit?: number): Chainable<any>
}
}
}

export function tabUntil (fn: (el: JQuery<HTMLElement>) => boolean, limit: number = 10) {
function _tabUntil (step: number) {
return cy.tab().focused({ log: false }).then((el) => {
const pass = fn(el)

if (pass) {
return el
}

if (step > limit) {
throw new Error(`Unable to step to element in ${fn.toString()} in ${limit} steps`)
}

return _tabUntil(step + 1)
})
}

return logInternal('tabUntil', () => {
cy.get('body')

return _tabUntil(0)
})
}
11 changes: 11 additions & 0 deletions packages/frontend-shared/cypress/support/utils.ts
@@ -0,0 +1,11 @@
export function logInternal<T> (name: string | Partial<Cypress.LogConfig>, cb: (log: Cypress.Log | undefined) => Cypress.Chainable<T>, opts: Partial<Cypress.Loggable> = {}): Cypress.Chainable<T> {
const _log = typeof name === 'string'
? Cypress.log({ name, message: '' })
: Cypress.log(name)

return cb(_log).then<T>((val) => {
_log?.end()

return val
})
}
3 changes: 2 additions & 1 deletion packages/frontend-shared/src/locales/en-US.json
Expand Up @@ -217,7 +217,8 @@
"title": "Record your first run",
"content": "Record a run to see your test results in Cypress Cloud. You can then optimize your test suite, debug failing and flaky tests, and integrate with your favorite tools."
}
}
},
"runAllSpecs": "Run {n} spec | Run {n} specs"
},
"noResults": {
"defaultMessage": "No results matched your search:",
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend-shared/vite.config.mjs
Expand Up @@ -151,6 +151,8 @@ export const makeConfig = (config = {}, plugins = {}) => {
'process.env': {
CYPRESS_INTERNAL_ENV: 'development',
},
// Fix to get cypress-plugin-tab to work in CT
'process.version': '99',
'setImmediate': {},
},
...config,
Expand Down

2 comments on commit 3328bf4

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3328bf4 Nov 21, 2022

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 platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/11.2.0/linux-arm64/feature/run-all-specs-3328bf412d426ba7609b2b93c8f74dfd2b3bfe0b/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3328bf4 Nov 21, 2022

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 platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/11.2.0/linux-x64/feature/run-all-specs-3328bf412d426ba7609b2b93c8f74dfd2b3bfe0b/cypress.tgz

Please sign in to comment.