Skip to content

Commit

Permalink
feat(component-testing): Component testing runner splash screen (#15091)
Browse files Browse the repository at this point in the history
* Fix  scrollbars

* Fix resizer styles

* Make no-spec selected filler for splash screen

* Create no specs filler for reporter

* Fix keyboard navigation when specs are filtered

* Add test for filtered search list keyboard navigation

* Remove fs-extra

* Remove useless code

* Update with related tests to the right values WITHOUT SCROLLBARS
  • Loading branch information
dmtrKovalenko authored Feb 15, 2021
1 parent 10fdd7b commit 1d878d2
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 27 deletions.
6 changes: 3 additions & 3 deletions packages/runner-ct/cypress/component/RunnerCt.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe('RunnerCt', () => {

cy.get('[data-cy=resizer]').trigger('mouseup', 'center')

cy.get('[data-cy=specs-list-resize-box').should('have.css', 'width', '429px')
cy.get('[data-cy=specs-list-resize-box').should('have.css', 'width', '435px')
})

it('restore specs list width after closing and reopen', () => {
Expand All @@ -117,14 +117,14 @@ describe('RunnerCt', () => {
})

cy.get('[data-cy=resizer]').trigger('mouseup', 'center')
cy.get('[data-cy=specs-list-resize-box').should('have.css', 'width', '479px')
cy.get('[data-cy=specs-list-resize-box').should('have.css', 'width', '485px')

cy.get('[aria-label="Open the menu"').click()
assertSpecsListIs('closed')

cy.get('[aria-label="Open the menu"').click()

cy.get('[data-cy=specs-list-resize-box').should('have.css', 'width', '479px')
cy.get('[data-cy=specs-list-resize-box').should('have.css', 'width', '485px')
})
})
})
11 changes: 11 additions & 0 deletions packages/runner-ct/cypress/component/SpecList/SpecList.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,16 @@ describe('SpecList', () => {
.parent()
.should('be.focused')
})

it('Allows to navigate between files when spec list is searched', () => {
cy.get('input').type('bar')
cy.realPress('ArrowDown')

cy
.get('[role=radio]')
.contains('bar.js')
.parent()
.should('be.focused')
})
})
})
6 changes: 3 additions & 3 deletions packages/runner-ct/src/SpecList/SpecList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const SpecList: React.FC<SpecsListProps> = observer((props) => {
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
const selectSpecByIndex = (index: number) => {
const spec = typeof index !== 'number' || index < 0
? props.specs[0]
: props.specs[index]
? filteredSpecs[0]
: filteredSpecs[index]

const specElement = document.querySelector(`[data-spec="${spec.relative}"]`) as HTMLDivElement

Expand All @@ -34,7 +34,7 @@ export const SpecList: React.FC<SpecsListProps> = observer((props) => {
}
}

const selectedSpecIndex = props.specs.findIndex((spec) =>
const selectedSpecIndex = filteredSpecs.findIndex((spec) =>
spec.relative === (document.activeElement as HTMLElement)?.dataset?.spec)

if (e.key === 'ArrowUp') {
Expand Down
7 changes: 0 additions & 7 deletions packages/runner-ct/src/SpecList/components/SearchSpec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react'
import hotkeys from 'hotkeys-js'
import './SearchSpec.scss'

interface SearchSpecProps extends React.RefAttributes<HTMLInputElement> {
Expand All @@ -8,12 +7,6 @@ interface SearchSpecProps extends React.RefAttributes<HTMLInputElement> {
}

export const SearchSpec: React.FC<SearchSpecProps> = React.forwardRef((props, ref) => {
// const ignoreSlashInput

React.useEffect(() => {
return () => hotkeys.unbind('/')
}, [])

return (
<div className="specs-list-search-input-container">
<input
Expand Down
65 changes: 65 additions & 0 deletions packages/runner-ct/src/app/NoSpecSelected.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.no-spec {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
flex-direction: column;
color: #555;
font-family: "Muli", "Helvetica Neue", "Arial", sans-serif;
font-size: 13px;

.no-spec-content-container {
display: flex;
flex-basis: 45%;
flex-direction: column;
align-items: center;

a {
color: #3386D4;
cursor: pointer;

&:hover {
text-decoration: underline;
}
}

.no-spec-title {
margin-top: 16px;
margin-bottom: 8px;
}

.no-spec-custom-children {
margin-top: 32px;
}
}

.keyboard-helper {
width: 224px;
.keyboard-shortcut {
display: flex;
margin-top: 8px;
height: 23px;
justify-content: space-between;

.shortcut {
display: flex;

.key {
display: flex;
font-family: sans-serif; // display keys symbols correctly
justify-content: center;
align-items: center;
border: 1px solid rgba(255, 255, 255, 0.4);
height: 23px;
min-width: 23px;
margin-right: 4px;
padding: 0px 4px;
font-size: 0.8125rem;
border-radius: 4px;
pointer-events: none;
background-color: #ddd;
}
}
}
}
}
53 changes: 53 additions & 0 deletions packages/runner-ct/src/app/NoSpecSelected.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as React from 'react'
import './NoSpecSelected.scss'

const KeyboardShortcut: React.FC<{ shortcut: string[], description: string }> = ({
shortcut,
description,
}) => {
const metaSymbol = window.navigator.platform.includes('Mac') ? '⌘' : 'Ctrl'

return (
<li className="keyboard-shortcut">
<p> {description} </p>
<div className="shortcut">
{shortcut.map((key) => (
<div className="key"> {key === 'Meta' ? metaSymbol : key} </div>
))}
</div>
</li>
)
}

export const KeyboardHelper = () => {
return (
<ul className="keyboard-helper">
<KeyboardShortcut shortcut={['/']} description="Search spec" />
<KeyboardShortcut shortcut={['Meta', 'B']} description="Toggle specs list" />
</ul>
)
}

interface NoSpecSelectedProps {
onSelectSpecRequest: () => void;
}

export const NoSpecSelected: React.FC<NoSpecSelectedProps> = ({ onSelectSpecRequest, children }) => {
return (
<div className="no-spec">
<div className="no-spec-content-container">
<svg width="40" height="50" viewBox="0 0 40 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M33.3333 49H6.66659C5.2521 49 3.89554 48.4381 2.89535 47.4379C1.89515 46.4377 1.33325 45.0812 1.33325 43.6667V6.33333C1.33325 4.91885 1.89515 3.56229 2.89535 2.5621C3.89554 1.5619 5.2521 1 6.66659 1H21.5626C22.2698 1.00015 22.9479 1.2812 23.4479 1.78133L37.8853 16.2187C38.3854 16.7186 38.6664 17.3968 38.6666 18.104V43.6667C38.6666 45.0812 38.1047 46.4377 37.1045 47.4379C36.1043 48.4381 34.7477 49 33.3333 49Z" stroke="#B4B5BC" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<h2 className="no-spec-title"> No spec selected. </h2>
<a onClick={onSelectSpecRequest}> Select Spec </a>

{children && (
<div className="no-spec-custom-children">
{children}
</div>
)}
</div>
</div>
)
}
9 changes: 9 additions & 0 deletions packages/runner-ct/src/app/ReporterHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import { observer } from 'mobx-react'
import { ReporterHeaderProps } from '@packages/reporter/src/header/header'
import Stats from '@packages/reporter/src/header/stats'
import Controls from '@packages/reporter/src/header/controls'
import { StatsStore } from '@packages/reporter/src/header/stats-store'

export const EmptyReporterHeader: React.FC = () => {
return (
<header>
<Stats stats={{ numPassed: 0, numFailed: 0, numPending: 0, duration: 0 } as StatsStore} />
</header>
)
}

export const ReporterHeader: React.FC<ReporterHeaderProps> = observer(
function ReporterHeader ({ statsStore, appState }) {
Expand Down
11 changes: 6 additions & 5 deletions packages/runner-ct/src/app/RunnerCt.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ main.app-ct {
box-shadow: $box-shadow-closest;
transition: ease-in-out 0.2s;
padding-left: $specs-list-padding;

overflow: hidden;

.specs-list-container {
display: flex;
height: calc(100vh - 20px);
height: calc(100vh);
padding-top: $specs-list-padding;
box-sizing: border-box;

.specs-list-focus-container {
width: 100%;
Expand Down Expand Up @@ -136,14 +138,13 @@ main.app-ct {
}
}


// styles for react-split-pane and custom ResizableBox
.Resizer {
$resize-thickness: 12px;
$resize-thickness: 11px;
border: 5px solid transparent;
margin: -5px;

background: transparent;
background: #ddd;
transition: background-color .3s ease-in-out;
z-index: 1;
box-sizing: border-box;
Expand Down
35 changes: 26 additions & 9 deletions packages/runner-ct/src/app/RunnerCt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SplitPane from 'react-split-pane'
import Header from '../header/header'
import Iframes from '../iframe/iframes'
import Message from '../message/message'
import { ReporterHeader } from './ReporterHeader'
import { EmptyReporterHeader, ReporterHeader } from './ReporterHeader'
import EventManager from '../lib/event-manager'
import { Hidden } from '../lib/Hidden'
import { SpecList } from '../SpecList'
Expand All @@ -20,6 +20,7 @@ import { useWindowSize } from '../lib/useWindowSize'
import { useGlobalHotKey } from '../lib/useHotKey'

import './RunnerCt.scss'
import { KeyboardHelper, NoSpecSelected } from './NoSpecSelected'

// Cypress.ConfigOptions only appears to have internal options.
// TODO: figure out where the "source of truth" should be for
Expand Down Expand Up @@ -85,20 +86,22 @@ const App: React.FC<AppProps> = observer(
monitorWindowResize()
}, [])

useGlobalHotKey('ctrl+b,command+b', () => {
setIsSpecsListOpen((isOpenNow) => !isOpenNow)
})

useGlobalHotKey('/', () => {
function focusSpecsList () {
setIsSpecsListOpen(true)

// a little trick to focus field on the next tick of event loop
// to prevent the handled keydown/keyup event to fill input with "/"
setTimeout(() => {
searchRef.current?.focus()
}, 0)
}

useGlobalHotKey('ctrl+b,command+b', () => {
setIsSpecsListOpen((isOpenNow) => !isOpenNow)
})

useGlobalHotKey('/', focusSpecsList)

function onSplitPaneChange (newWidth: number) {
setLeftSideOfSplitPaneWidth(newWidth)
state.updateWindowDimensions({
Expand Down Expand Up @@ -158,10 +161,13 @@ const App: React.FC<AppProps> = observer(
onDragStarted={() => setIsResizing(true)}
onDragFinished={() => setIsResizing(false)}
onChange={onSplitPaneChange}
// For some reason on each dom snapshot restoring the viewport is jumping up to 4 pixels
// It causes a weird white line in the bottom, so here is a fix which is not ideal
paneStyle={{ height: 'calc(100vh + 4px)' }}
className={cs('reporter-pane', { 'is-reporter-resizing': isResizing })}
>
<div>
{state.spec && (
<div style={{ height: '100%' }}>
{state.spec ? (
<Reporter
runMode={state.runMode}
runner={eventManager.reporterBus}
Expand All @@ -175,6 +181,11 @@ const App: React.FC<AppProps> = observer(
renderReporterHeader={(props) => <ReporterHeader {...props} />}
experimentalStudioEnabled={false}
/>
) : (
<div className="reporter">
<EmptyReporterHeader />
<NoSpecSelected onSelectSpecRequest={focusSpecsList} />
</div>
)}
</div>
<SplitPane
Expand All @@ -193,7 +204,13 @@ const App: React.FC<AppProps> = observer(
>
<div className="runner runner-ct container">
<Header {...props} ref={headerRef}/>
<Iframes {...props} />
{!state.spec ? (
<NoSpecSelected onSelectSpecRequest={focusSpecsList}>
<KeyboardHelper />
</NoSpecSelected>
) : (
<Iframes {...props} />
)}
<Message state={state}/>
</div>

Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29583,6 +29583,13 @@ rollup@^2.35.1:
optionalDependencies:
fsevents "~2.1.2"

rollup@^2.38.5:
version "2.39.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.39.0.tgz#be4f98c9e421793a8fec82c854fb567c35e22ab6"
integrity sha512-+WR3bttcq7zE+BntH09UxaW3bQo3vItuYeLsyk4dL2tuwbeSKJuvwiawyhEnvRdRgrII0Uzk00FpctHO/zB1kw==
optionalDependencies:
fsevents "~2.3.1"

rst-selector-parser@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
Expand Down

4 comments on commit 1d878d2

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1d878d2 Feb 15, 2021

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/6.5.0/circle-develop-1d878d2f81be54203ea903a760f7e45ce8388349/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1d878d2 Feb 15, 2021

Choose a reason for hiding this comment

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

AppVeyor has built the win32 ia32 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/6.5.0/appveyor-develop-1d878d2f81be54203ea903a760f7e45ce8388349/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1d878d2 Feb 15, 2021

Choose a reason for hiding this comment

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

AppVeyor has built the win32 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/6.5.0/appveyor-develop-1d878d2f81be54203ea903a760f7e45ce8388349/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1d878d2 Feb 15, 2021

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 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/6.5.0/circle-develop-1d878d2f81be54203ea903a760f7e45ce8388349/cypress.tgz

Please sign in to comment.