@revoloo/cypress6
Version:
Cypress.io end to end testing tool
238 lines (205 loc) • 7.17 kB
JSX
import _ from 'lodash'
import cs from 'classnames'
import { action, observable } from 'mobx'
import { observer } from 'mobx-react'
import React, { Component } from 'react'
import Tooltip from '@cypress/react-tooltip'
import eventManager from '../lib/event-manager'
const defaultCopyText = 'Copy to clipboard'
const defaultPrintText = 'Print to console'
// mouseleave fires when entering a child element, so make sure we're
// actually leaving the button and not just hovering over a child
const fixMouseOut = (fn, getTarget) => (e) => {
if (
!e.relatedTarget
|| e.relatedTarget.parentNode === getTarget()
|| e.relatedTarget === getTarget()
) return
fn(e)
}
class SelectorPlayground extends Component {
copyText = defaultCopyText
printText = defaultPrintText
showingMethodPicker = false
render () {
const { model } = this.props
const selectorText = `cy.${model.method}('${model.selector}')`
return (
<div className={cs('header-popup selector-playground', `method-${model.method}`, {
'no-elements': !model.numElements,
'invalid-selector': !model.isValid,
})}>
<div className='selector'>
<Tooltip
title='Click an element to see a suggested selector'
className='cy-tooltip'
>
<button
className={`highlight-toggle ${model.isEnabled ? 'active' : ''}`}
onClick={this._toggleEnablingSelectorPlayground}>
<span className='fa-stack'>
<i className='far fa-square fa-stack-1x'></i>
<i className='fas fa-mouse-pointer fa-stack-1x'></i>
</span>
</button>
</Tooltip>
<div
className='wrap'
onMouseOver={this._setHighlight(true)}
>
{this._methodSelector()}
<span>(</span>
<span>{'\''}</span>
<div className='selector-input'>
<input
ref={(node) => this._input = node}
name={`${model.isEnabled}` /* fixes issue with not resizing when opening/closing selector playground */}
value={model.selector}
onChange={this._updateSelector}
onFocus={this._setHighlight(true)}
/>
</div>
<span>{'\''}</span>
<span>)</span>
<input ref='copyText' className='copy-backer' value={selectorText} readOnly />
<Tooltip title={model.infoHelp || ''} className='cy-tooltip'>
<span className='info num-elements'>
{model.isValid ?
model.numElements :
<i className='fas fa-exclamation-triangle'></i>
}
</span>
</Tooltip>
</div>
<Tooltip title={this.copyText || ''} updateCue={`${selectorText}${this.copyText}`} className='cy-tooltip'>
<button
ref={(node) => this._copyButton = node}
className='copy-to-clipboard'
onClick={this._copyToClipboard}
disabled={!model.numElements || !model.isValid}
onMouseOut={fixMouseOut(this._resetCopyText, () => this._copyButton)}
>
<i className='far fa-copy' />
</button>
</Tooltip>
<Tooltip title={this.printText || ''} updateCue={`${selectorText}${this.printText}`} className='cy-tooltip'>
<button
ref={(node) => this._printButton = node}
className='print-to-console'
onClick={this._printToConsole}
disabled={!model.numElements || !model.isValid}
onMouseOut={fixMouseOut(this._resetPrintText, () => this._printButton)}
>
<i className='fas fa-terminal' />
</button>
</Tooltip>
</div>
<a className='selector-info' href='https://on.cypress.io/selector-playground' target="_blank">
<i className='fas fa-question-circle'></i>{' '}
Learn more
</a>
<button className='close' onClick={this._togglePlaygroundOpen}>x</button>
</div>
)
}
componentDidMount () {
this._previousIsEnabled = this.props.model.isEnabled
this._previousMethod = this.props.model.method
document.body.addEventListener('click', this._onOutsideClick, false)
}
componentDidUpdate () {
if (
(this.props.model.isEnabled !== this._previousIsEnabled)
|| (this.props.model.method !== this._previousMethod)
) {
if (this.props.model.isEnabled) {
this._focusAndSelectInputText()
}
this._previousIsEnabled = this.props.model.isEnabled
this._previousMethod = this.props.model.method
}
}
componentWillUnmount () {
document.body.removeEventListener('click', this._onOutsideClick)
}
_methodSelector () {
const { model } = this.props
const methods = _.filter(model.methods, (method) => method !== model.method)
return (
<span className={cs('method', {
'is-showing': this.showingMethodPicker,
})}>
<button onClick={this._toggleMethodPicker}>
<i className='fas fa-caret-down'></i>{' '}
cy.{model.method}
</button>
<div className='method-picker'>
{_.map(methods, (method) => (
<div key={method} onClick={() => this._setMethod(method)}>
cy.{method}
</div>
))}
</div>
</span>
)
}
_focusAndSelectInputText () {
this._input.focus()
this._input.select()
}
_onOutsideClick = () => {
this._setShowingMethodPicker(false)
}
_toggleMethodPicker = () => {
this._setShowingMethodPicker(!this.showingMethodPicker)
}
_setShowingMethodPicker (isShowing) {
this.showingMethodPicker = isShowing
}
_setMethod (method) {
if (method !== this.props.model.method) {
this.props.model.setMethod(method)
}
}
_setHighlight = (isShowing) => () => {
this.props.model.setShowingHighlight(isShowing)
}
_copyToClipboard = () => {
try {
this.refs.copyText.select()
const successful = document.execCommand('copy')
this._setCopyText(successful ? 'Copied!' : 'Oops, unable to copy')
} catch (err) {
this._setCopyText('Oops, unable to copy')
}
}
_setCopyText (text) {
this.copyText = text
}
_resetCopyText = () => {
this._setCopyText(defaultCopyText)
}
_printToConsole = () => {
eventManager.emit('print:selector:elements:to:console')
this._setPrintText('Printed!')
}
_setPrintText (text) {
this.printText = text
}
_resetPrintText = () => {
this._setPrintText(defaultPrintText)
}
_toggleEnablingSelectorPlayground = () => {
this.props.model.toggleEnabled()
}
_togglePlaygroundOpen = () => {
this.props.model.toggleOpen()
}
_updateSelector = (e) => {
const { model } = this.props
model.setSelector(e.target.value)
model.setShowingHighlight(true)
}
}
export default SelectorPlayground