Skip to content

Commit

Permalink
Merge branch 'develop' into master-merge-up
Browse files Browse the repository at this point in the history
  • Loading branch information
elevatebart committed Feb 17, 2021
2 parents 4aea5e4 + f6233af commit 585649c
Show file tree
Hide file tree
Showing 20 changed files with 305 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -39,6 +39,9 @@ packages/server/test/support/fixtures/server/libs
/npm/react/bin/*
/npm/react/cypress/videos

# from runner-ct
/packages/runner-ct/cypress/screenshots

# Building app binary
scripts/support
package-lock.json
Expand Down
9 changes: 9 additions & 0 deletions circle.yml
Expand Up @@ -198,6 +198,15 @@ commands:
PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_ID \
PERCY_PARALLEL_TOTAL=-1 \
$cmd yarn workspace @packages/runner-ct run cypress:run --browser <<parameters.browser>>
- run:
command: |
if [[ <<parameters.percy>> == 'true' ]]; then
PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_ID \
PERCY_PARALLEL_TOTAL=-1 \
yarn percy upload packages/runner-ct/cypress/screenshots/screenshot.spec.tsx/percy
else
echo "skipping percy screenshots uploading"
fi
- store_test_results:
path: /tmp/cypress
- store_artifacts:
Expand Down
7 changes: 5 additions & 2 deletions npm/webpack-dev-server/src/measureWebpackPerformance.ts
Expand Up @@ -13,7 +13,7 @@ export function measureWebpackPerformance (webpackConfig: webpack.Configuration)
const compareWithPrevious = process.env.WEBPACK_PERF_MEASURE_COMPARE

function percentageDiff (a: number, b: number) {
return 100 * (a - b) / ((a + b) / 2)
return ((a - b) / a) * 100
}

const compareOutput = (output: string) => {
Expand All @@ -31,7 +31,10 @@ export function measureWebpackPerformance (webpackConfig: webpack.Configuration)
const delimiter = new Array(process.stdout.columns).fill('═').join('')

console.log(delimiter)
console.log(`${chalk.bold('WEBPACK_PERF_MEASURE:')} ${result}`)
console.log(`${chalk.bold('WEBPACK_PERF_MEASURE')}`)
console.log(`Before: ${chalk.bold(oldStats.misc.compileTime / 1000)}s`)
console.log(`After: ${chalk.bold(newStats.misc.compileTime / 1000)}s`)
console.log(result)
console.log(delimiter)
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -163,6 +163,7 @@
"mocha-junit-reporter": "2.0.0",
"mocha-multi-reporters": "1.1.7",
"mock-fs": "4.9.0",
"odiff-bin": "2.1.0",
"parse-github-repo-url": "1.4.1",
"patch-package": "6.2.2",
"percy": "0.26.9",
Expand Down
4 changes: 3 additions & 1 deletion packages/reporter/src/main.tsx
Expand Up @@ -22,6 +22,7 @@ import Runnables from './runnables/runnables'

interface BaseReporterProps {
appState: AppState
className?: string
runnablesStore: RunnablesStore
runner: Runner
scroller: Scroller
Expand Down Expand Up @@ -78,6 +79,7 @@ class Reporter extends Component<SingleReporterProps | MultiReporterProps> {
render () {
const {
appState,
className,
runMode,
runnablesStore,
scroller,
Expand All @@ -89,7 +91,7 @@ class Reporter extends Component<SingleReporterProps | MultiReporterProps> {
} = this.props

return (
<div className={cs('reporter', {
<div className={cs(className, 'reporter', {
multiSpecs: runMode === 'multi',
'experimental-studio-enabled': experimentalStudioEnabled,
'studio-active': appState.studioActive,
Expand Down
5 changes: 4 additions & 1 deletion packages/runner-ct/cypress.json
@@ -1 +1,4 @@
{}
{
"testFiles": "**/*spec.{ts,tsx}",
"video": false
}
67 changes: 67 additions & 0 deletions packages/runner-ct/cypress/component/screenshot.spec.tsx
@@ -0,0 +1,67 @@
import * as React from 'react'
import { mount } from '@cypress/react'

const styles = `
body {
margin: 0;
}
#wrapper {
display: flex;
flex-direction: column;
height: 100vh;
}
#body {
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
}
#header, #footer {
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background: silver;
}
`

const Layout: React.FC = () => {
return (
<div id='wrapper'>
<div id='header'>Header</div>
<div id='body'>Body</div>
<div id='footer'>Footer</div>
</div>
)
}

describe('screenshot', () => {
it('takes a standard screenshot', () => {
cy.viewport(500, 500)
mount(<Layout />, {
styles,
})

cy.screenshot('percy/component_testing_takes_a_screenshot')
})

it('takes a screenshot with a custom viewport', () => {
cy.viewport(750, 750)
mount(<Layout />, {
styles,
})

cy.screenshot('percy/component_testing_screenshot_custom_viewport_screenshot')
})

// TODO: This will technically pass, but the screenshot is not correct.
// AUT transform appears to be buggy for extreme viewports.
xit('screenshot with a really long viewport', () => {
cy.viewport(200, 2000)
mount(<Layout />, {
styles,
})

cy.screenshot('percy/component_testing_screenshot_long_viewport')
})
})
1 change: 1 addition & 0 deletions packages/runner-ct/cypress/plugins/index.js
Expand Up @@ -22,6 +22,7 @@ function injectStylesInlineForPercyInPlace (webpackConfig) {
*/
module.exports = (on, config) => {
on('task', percyHealthCheck)

on('dev-server:start', (options) => {
/** @type {import('webpack').Configuration} */
const { default: webpackConfig } = require(path.resolve(__dirname, '..', '..', 'webpack.config.ts'))
Expand Down
12 changes: 12 additions & 0 deletions packages/runner-ct/src/app/RunnerCt.scss
Expand Up @@ -12,6 +12,10 @@ main.app-ct {
font-size: $font-size;
}

.display-none {
display: none !important;
}

.menu-toggle {
z-index: 2;
:hover {
Expand Down Expand Up @@ -70,6 +74,14 @@ main.app-ct {
margin-inline: $specs-list-offset;
}

.app-wrapper-screenshotting {
margin-inline: 0;
}

.screenshotting {
box-shadow: none;
}

.runner-ct {
left: 0;

Expand Down
29 changes: 22 additions & 7 deletions packages/runner-ct/src/app/RunnerCt.tsx
@@ -1,6 +1,7 @@
import cs from 'classnames'
import { observer } from 'mobx-react'
import * as React from 'react'

import { Reporter } from '@packages/reporter/src/main'

import errorMessages from '../errors/error-messages'
Expand All @@ -21,6 +22,7 @@ import { useGlobalHotKey } from '../lib/useHotKey'

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

// Cypress.ConfigOptions only appears to have internal options.
// TODO: figure out where the "source of truth" should be for
Expand All @@ -42,6 +44,7 @@ const VIEWPORT_SIDE_MARGIN = 40 + 17
const App: React.FC<AppProps> = observer(
function App (props: AppProps) {
const searchRef = React.useRef<HTMLInputElement>(null)
const splitPaneRef = React.useRef<{ splitPane: HTMLDivElement }>(null)
const pluginRootContainer = React.useRef<null | HTMLDivElement>(null)

const { state, eventManager, config } = props
Expand Down Expand Up @@ -86,6 +89,12 @@ const App: React.FC<AppProps> = observer(
monitorWindowResize()
}, [])

useScreenshotHandler({
state,
eventManager,
splitPaneRef,
})

function focusSpecsList () {
setIsSpecsListOpen(true)

Expand Down Expand Up @@ -116,7 +125,12 @@ const App: React.FC<AppProps> = observer(
<>
<main className="app-ct">
<div
className="specs-list-drawer"
className={cs(
'specs-list-drawer',
{
'display-none': state.screenshotting,
},
)}
style={{
transform: isSpecsListOpen ? `translateX(0)` : `translateX(-${drawerWidth - 20}px)`,
}}
Expand Down Expand Up @@ -150,14 +164,15 @@ const App: React.FC<AppProps> = observer(
/>
</ResizableBox>
</div>
<div className="app-wrapper">
<div className={cs('app-wrapper', { 'app-wrapper-screenshotting': state.screenshotting })}>
<SplitPane
split="vertical"
primary="first"
minSize={100}
ref={splitPaneRef}
minSize={state.screenshotting ? 0 : 100}
// calculate maxSize of IFRAMES preview to not cover specs list and command log
maxSize={400}
defaultSize={355}
maxSize={state.screenshotting ? 0 : 400}
defaultSize={state.screenshotting ? 0 : 355}
onDragStarted={() => setIsResizing(true)}
onDragFinished={() => setIsResizing(false)}
onChange={onSplitPaneChange}
Expand All @@ -171,10 +186,10 @@ const App: React.FC<AppProps> = observer(
<Reporter
runMode={state.runMode}
runner={eventManager.reporterBus}
className={cs({ 'display-none': state.screenshotting })}
spec={state.spec}
specRunId={state.specRunId}
allSpecs={state.multiSpecs}
// @ts-ignore
error={errorMessages.reporterError(state.scriptError, state.spec.relative)}
firefoxGcInterval={config.firefoxGcInterval}
resetStatsOnSpecChange={state.runMode === 'single'}
Expand Down Expand Up @@ -202,7 +217,7 @@ const App: React.FC<AppProps> = observer(
: state.isAnyPluginToShow ? 30 : 0
}
>
<div className="runner runner-ct container">
<div className={cs('runner runner-ct container', { screenshotting: state.screenshotting })}>
<Header {...props} ref={headerRef}/>
{!state.spec ? (
<NoSpecSelected onSelectSpecRequest={focusSpecsList}>
Expand Down
60 changes: 60 additions & 0 deletions packages/runner-ct/src/app/useScreenshotHandler.tsx
@@ -0,0 +1,60 @@
import * as React from 'react'
import { runInAction } from 'mobx'
import EventManager from '../lib/event-manager'
import State from '../lib/state'

/**
* SplitPane hierarchy looks like this:
* ```jsx
* <div class="SplitPane">
* <div class="Pane vertical Pane1 ">..</div>
* <span role="presentation" class="Resizer vertical ">...</span>
* </div>
*```
* we need to set these to display: none during cy.screenshot.
*/
export function useScreenshotHandler ({ eventManager, state, splitPaneRef } : {
eventManager: typeof EventManager, state: State, splitPaneRef: React.MutableRefObject<{ splitPane: HTMLDivElement }>
}) {
const showPane = () => {
if (!splitPaneRef.current) {
return
}

splitPaneRef.current.splitPane.firstElementChild.classList.remove('d-none')
splitPaneRef.current.splitPane.querySelector('[role="presentation"]').classList.remove('d-none')
}

const hidePane = () => {
if (!splitPaneRef.current) {
return
}

splitPaneRef.current.splitPane.firstElementChild.classList.add('d-none')
splitPaneRef.current.splitPane.querySelector('[role="presentation"]').classList.add('d-none')
}

React.useEffect(() => {
eventManager.on('before:screenshot', (config) => {
runInAction(() => {
state.setScreenshotting(true)
hidePane()
})
})

const revertFromScreenshotting = () => {
runInAction(() => {
state.setScreenshotting(false)
showPane()
})
}

eventManager.on('after:screenshot', (config) => {
revertFromScreenshotting()
})

eventManager.on('run:start', () => {
revertFromScreenshotting()
})
}, [])
}
Expand Up @@ -4,17 +4,23 @@ import { action, observable } from 'mobx'
import { observer } from 'mobx-react'
import React, { Component } from 'react'
import Tooltip from '@cypress/react-tooltip'
import { $ } from '@packages/driver'
import State from '../lib/state'

import { configFileFormatted } from '../lib/config-file-formatted'
import SelectorPlayground from '../selector-playground/selector-playground'
import selectorPlaygroundModel from '../selector-playground/selector-playground-model'
import { ExtendedConfigOptions } from '../app/RunnerCt'

interface HeaderProps {
state: State
config: ExtendedConfigOptions
}

@observer
export default class Header extends Component {
export default class Header extends Component<HeaderProps> {
headerRef = React.createRef()

@observable showingViewportMenu = false;
@observable showingViewportMenu = false

render () {
const { state, config } = this.props
Expand All @@ -24,6 +30,7 @@ export default class Header extends Component {
ref={this.headerRef}
className={cs({
'showing-selector-playground': selectorPlaygroundModel.isOpen,
'display-none': state.screenshotting,
})}
>
<div className='sel-url-wrap'>
Expand Down
7 changes: 5 additions & 2 deletions packages/runner-ct/src/iframe/iframes.jsx
Expand Up @@ -22,10 +22,13 @@ export default class Iframes extends Component {
containerRef = null

render () {
const { height, width, scriptError, scale } = this.props.state
const { height, width, scriptError, scale, screenshotting } = this.props.state

return (
<div className={cs('iframes-ct-container', { 'has-error': !!scriptError })}>
<div className={cs('iframes-ct-container', {
'has-error': !!scriptError,
'iframes-ct-container-screenshotting': screenshotting,
})}>
<div
ref={(container) => this.containerRef = container}
className='size-container'
Expand Down

0 comments on commit 585649c

Please sign in to comment.