Skip to content

Commit

Permalink
chore(runner-ct): TS and React improvements (#16614)
Browse files Browse the repository at this point in the history
  • Loading branch information
lmiller1990 authored May 25, 2021
1 parent c0fc23a commit 6384b5d
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 160 deletions.
2 changes: 1 addition & 1 deletion packages/runner-ct/src/app/SpecContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react'
import SplitPane from 'react-split-pane'

import Header from '../header/header'
import Iframes from '../iframe/iframes'
import { Iframes } from '../iframe/iframes'
import { animationFrameDebounce } from '../lib/debounce'
import { Message } from '../message/message'
import { KeyboardHelper } from './KeyboardHelper'
Expand Down
3 changes: 1 addition & 2 deletions packages/runner-ct/src/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ 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
config: Cypress.RuntimeConfigOptions
}

@observer
Expand Down
286 changes: 140 additions & 146 deletions packages/runner-ct/src/iframe/iframes.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import cs from 'classnames'
import { action, when, autorun } from 'mobx'
import { observer } from 'mobx-react'
import React, { Component } from 'react'
import React, { useRef, useEffect } from 'react'
import { default as $Cypress } from '@packages/driver'
import State from '../../src/lib/state'

import State from '../../src/lib/state'
import AutIframe from './aut-iframe'
import { ScriptError } from '../errors/script-error'
import SnapshotControls from './snapshot-controls'

import IframeModel from './iframe-model'
import selectorPlaygroundModel from '../selector-playground/selector-playground-model'
import styles from '../app/RunnerCt.module.scss'
import './iframes.scss'
import eventManager from '../lib/event-manager'
import { namedObserver } from '../lib/mobx'
import './iframes.scss'

export function getSpecUrl ({ namespace, spec }, prefix = '') {
return spec ? `${prefix}/${namespace}/iframes/${spec.absolute}` : ''
Expand All @@ -22,134 +21,75 @@ export function getSpecUrl ({ namespace, spec }, prefix = '') {
interface IFramesProps {
state: State
eventManager: typeof eventManager
config: any
config: Cypress.RuntimeConfigOptions
}

@observer
export default class Iframes extends Component<IFramesProps> {
_disposers = []
containerRef = null
autIframe: AutIframe
iframeModel: IframeModel

render () {
const { viewportHeight, viewportWidth, scriptError, scale, screenshotting } = this.props.state

return (
<div
style={{
display: this.props.state.screenshotting ? 'inherit' : 'grid',
}}
className={cs('iframes-ct-container', {
'has-error': !!scriptError,
'iframes-ct-container-screenshotting': screenshotting,
})}
>
<div
ref={(container) => this.containerRef = container}
className={
cs('size-container', {
[styles.noSpecAut]: !this.props.state.spec,
})
}
style={{
height: viewportHeight,
width: viewportWidth,
transform: `scale(${screenshotting ? 1 : scale})`,
}}
/>
<ScriptError error={scriptError} />
<div className='cover' />
</div>
)
}

componentDidMount () {
const config = this.props.config
export const Iframes = namedObserver('Iframes', ({
config,
state,
eventManager,
}: IFramesProps) => {
const containerRef = useRef<HTMLDivElement>(null)
const autIframe = useRef(new AutIframe(config))

this.autIframe = new AutIframe(config)
const _toggleSnapshotHighlights = (snapshotProps) => {
state.setShowSnapshotHighlight(!state.snapshot.showingHighlights)

this.props.eventManager.on('visit:failed', this.autIframe.showVisitFailure)
this.props.eventManager.on('before:screenshot', this.autIframe.beforeScreenshot)
this.props.eventManager.on('after:screenshot', this.autIframe.afterScreenshot)
this.props.eventManager.on('script:error', this._setScriptError)
if (state.snapshot.showingHighlights) {
const snapshot = snapshotProps.snapshots[state.snapshot.stateIndex]

// TODO: need to take headless mode into account
// may need to not display reporter if more than 200 tests
this.props.eventManager.on('restart', () => {
this._run(this.props.state.spec, config)
})

this.props.eventManager.on('print:selector:elements:to:console', this._printSelectorElementsToConsole)

this._disposers.push(autorun(() => {
this.autIframe.toggleSelectorPlayground(selectorPlaygroundModel.isEnabled)
}))

this._disposers.push(autorun(() => {
this.autIframe.toggleSelectorHighlight(selectorPlaygroundModel.isShowingHighlight)
}))

this.props.eventManager.start(this.props.config)

this.iframeModel = new IframeModel({
state: this.props.state,
restoreDom: this.autIframe.restoreDom,
highlightEl: this.autIframe.highlightEl,
detachDom: this.autIframe.detachDom,
snapshotControls: (snapshotProps) => (
<SnapshotControls
eventManager={this.props.eventManager}
snapshotProps={snapshotProps}
state={this.props.state}
onToggleHighlights={this._toggleSnapshotHighlights}
onStateChange={this._changeSnapshotState}
/>
),
})
autIframe.current.highlightEl(snapshot, snapshotProps)
} else {
autIframe.current.removeHighlights()
}
}

this.iframeModel.listen()
const _changeSnapshotState = (snapshotProps, index) => {
const snapshot = snapshotProps.snapshots[index]

this._disposers.push(autorun(() => {
const spec = this.props.state.spec
state.setSnapshotIndex(index)
autIframe.current.restoreDom(snapshot)

if (spec) {
this._run(spec, config)
}
}))
if (state.snapshot.showingHighlights && snapshotProps.$el) {
autIframe.current.highlightEl(snapshot, snapshotProps)
} else {
autIframe.current.removeHighlights()
}
}

@action _setScriptError = (err: string | undefined) => {
this.props.state.scriptError = err
}
const _setScriptError = action((err: string | undefined) => {
state.scriptError = err
})

_run = (spec, config) => {
const _run = (spec, config) => {
config.spec = spec

// this.props.eventManager.notifyRunningSpec(specPath)
// logger.clearLog()
this._setScriptError(undefined)
_setScriptError(undefined)

this.props.eventManager.setup(config)
eventManager.setup(config)

// This is extremely required to not run test till devtools registered
when(() => this.props.state.readyToRunTests, () => {
window.Cypress.on('window:before:load', this.props.state.registerDevtools)
when(() => state.readyToRunTests, () => {
window.Cypress.on('window:before:load', state.registerDevtools)

const $autIframe = this._loadIframes(spec)
const $autIframe = _loadIframes(spec)

this.props.eventManager.initialize($autIframe, config)
eventManager.initialize($autIframe, config)
})
}

// jQuery is a better fit for managing these iframes, since they need to get
// wiped out and reset on re-runs and the snapshots are from dom we don't control
_loadIframes (spec) {
const specSrc = getSpecUrl({ namespace: this.props.config.namespace, spec })
const $container = $Cypress.$(this.containerRef).empty()
const $autIframe = this.autIframe.create().appendTo($container)
const _loadIframes = (spec: Cypress.Cypress['spec']): JQuery<HTMLIFrameElement> => {
if (containerRef.current === null) {
return
}

this.autIframe.showBlankContents()
const specSrc = getSpecUrl({ namespace: config.namespace, spec })
const $container = $Cypress.$(containerRef.current).empty()
const $autIframe: JQuery<HTMLIFrameElement> = autIframe.current.create().appendTo($container)

autIframe.current.showBlankContents()

// In mount mode we need to render something right from spec file
// So load application tests to the aut frame
Expand All @@ -158,49 +98,103 @@ export default class Iframes extends Component<IFramesProps> {
return $autIframe
}

_toggleSnapshotHighlights = (snapshotProps) => {
this.props.state.snapshot.showingHighlights = !this.props.state.snapshot.showingHighlights

if (this.props.state.snapshot.showingHighlights) {
const snapshot = snapshotProps.snapshots[this.props.state.snapshot.stateIndex]
useEffect(() => {
eventManager.on('visit:failed', autIframe.current.showVisitFailure)
eventManager.on('before:screenshot', autIframe.current.beforeScreenshot)
eventManager.on('after:screenshot', autIframe.current.afterScreenshot)
eventManager.on('script:error', _setScriptError)

this.autIframe.highlightEl(snapshot, snapshotProps)
} else {
this.autIframe.removeHighlights()
}
}
// TODO: need to take headless mode into account
// may need to not display reporter if more than 200 tests
eventManager.on('restart', () => {
_run(state.spec, config)
})

_changeSnapshotState = (snapshotProps, index) => {
const snapshot = snapshotProps.snapshots[index]
eventManager.on('print:selector:elements:to:console', autIframe.current.printSelectorElementsToConsole)

eventManager.start(config)

const disposers = [
autorun(() => {
autIframe.current.toggleSelectorPlayground(selectorPlaygroundModel.isEnabled)
}),
autorun(() => {
autIframe.current.toggleSelectorHighlight(selectorPlaygroundModel.isShowingHighlight)
}),
autorun(() => {
if (state.spec) {
_run(state.spec, config)
}
}),
]

const iframeModel = new IframeModel({
state,
restoreDom: autIframe.current.restoreDom,
highlightEl: autIframe.current.highlightEl,
detachDom: autIframe.current.detachDom,
snapshotControls: (snapshotProps) => (
<SnapshotControls
eventManager={eventManager}
snapshotProps={snapshotProps}
state={state}
onToggleHighlights={_toggleSnapshotHighlights}
onStateChange={_changeSnapshotState}
/>
),
})

this.props.state.snapshot.stateIndex = index
this.autIframe.restoreDom(snapshot)
iframeModel.listen()

if (this.props.state.snapshot.showingHighlights && snapshotProps.$el) {
this.autIframe.highlightEl(snapshot, snapshotProps)
} else {
this.autIframe.removeHighlights()
return () => {
eventManager.notifyRunningSpec(null)
eventManager.stop()
disposers.forEach((dispose) => {
dispose()
})
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

componentDidUpdate () {
this.props.state.callbackAfterUpdate?.()
}
const isFirstRenderRef = useRef(true)

_printSelectorElementsToConsole = () => {
this.autIframe.printSelectorElementsToConsole()
}
useEffect(() => {
if (isFirstRenderRef.current) {
isFirstRenderRef.current = false

componentWillUnmount () {
this.props.eventManager.notifyRunningSpec(null)
this.props.eventManager.stop()
this._disposers.forEach((dispose) => {
dispose()
})
}
return
}

getSizeContainer () {
// eslint-disable-next-line react/no-string-refs
return this.refs.container
}
}
state.callbackAfterUpdate?.()
})

const { viewportHeight, viewportWidth, scriptError, scale, screenshotting } = state

return (
<div
style={{
display: state.screenshotting ? 'inherit' : 'grid',
}}
className={cs('iframes-ct-container', {
'has-error': !!scriptError,
'iframes-ct-container-screenshotting': screenshotting,
})}
>
<div
ref={containerRef}
className={
cs('size-container', {
[styles.noSpecAut]: !state.spec,
})
}
style={{
height: viewportHeight,
width: viewportWidth,
transform: `scale(${screenshotting ? 1 : scale})`,
}}
/>
<ScriptError error={scriptError} />
<div className='cover' />
</div>
)
})
10 changes: 10 additions & 0 deletions packages/runner-ct/src/lib/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,16 @@ export default class State {
}))
}

@action
setShowSnapshotHighlight = (showingHighlights: boolean) => {
this.snapshot.showingHighlights = showingHighlights
}

@action
setSnapshotIndex = (stateIndex: number) => {
this.snapshot.stateIndex = stateIndex
}

@action
initializePlugins = (config: Cypress.RuntimeConfigOptions & Cypress.ResolvedConfigOptions) => {
if (config.env.reactDevtools && !config.isTextTerminal) {
Expand Down
Loading

3 comments on commit 6384b5d

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 6384b5d May 25, 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/7.5.0/appveyor-develop-6384b5d92863f81e5a6bbb91bd57d9ff170287d5/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 6384b5d May 25, 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/7.5.0/appveyor-develop-6384b5d92863f81e5a6bbb91bd57d9ff170287d5/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 6384b5d May 25, 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/7.5.0/circle-develop-6384b5d92863f81e5a6bbb91bd57d9ff170287d5/cypress.tgz

Please sign in to comment.