Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: fix deprecation warnings and refactor react reporter to use hooks #31284

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions packages/driver/cypress/support/utils.ts
Original file line number Diff line number Diff line change
@@ -13,15 +13,17 @@ export const getCommandLogWithText = (command, type?) => {
.closest('.command')
}

export const findReactInstance = function (dom) {
// This work around is super hacky to get the appState from the Test Mobx Observable Model
// this is needed to pause the runner to assert on the test
export const findAppStateFromTest = function (dom) {
let key = _.keys(dom).find((key) => key.startsWith('__reactFiber')) as string
let internalInstance = dom[key]

if (internalInstance == null) return null

return internalInstance._debugOwner
? internalInstance._debugOwner.stateNode
: internalInstance.return.stateNode
? internalInstance._debugOwner.memoizedProps.model.store.appState
: internalInstance.return.memoizedProps.model.store.appState
}

export const clickCommandLog = (sel, type?) => {
@@ -31,13 +33,17 @@ export const clickCommandLog = (sel, type?) => {
.then(() => {
const commandLogEl = getCommandLogWithText(sel, type)

const reactCommandInstance = findReactInstance(commandLogEl[0])
const activeTestEl = commandLogEl[0].closest('li.test.runnable.runnable-active')

if (!reactCommandInstance) {
// We are manually manipulating the state of the appState to stop the runner.
// This does NOT happen in the wild and is only for testing purposes.
const appStateInstance = findAppStateFromTest(activeTestEl)

if (!appStateInstance) {
assert(false, 'failed to get command log React instance')
}

reactCommandInstance.props.appState.isRunning = false
appStateInstance.isRunning = false
const inner = $(commandLogEl).find('.command-wrapper-text')

inner.get(0).click()
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/agents.cy.ts
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ describe('agents', () => {
})

start = () => {
cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/aliases.cy.ts
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ describe('aliases', () => {
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/commands.cy.ts
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ describe('commands', { viewportHeight: 1000 }, () => {
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
addCommand(runner, {
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/header.cy.ts
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ describe('header', () => {
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', { ...runnables, testFilter: opts?.testFilter, totalUnfilteredTests: opts?.totalUnfilteredTests })
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/hooks.cy.ts
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ describe('hooks', () => {
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/memory.cy.ts
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ function visitAndRenderReporter (studioEnabled: boolean = false, studioActive: b
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', { studioActive })
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/routes.cy.ts
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ describe('routes', () => {
})

start = () => {
cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/runnables.cy.ts
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ describe('runnables', () => {
start = (renderProps?: Partial<BaseReporterProps>) => {
render(renderProps)

return cy.get('.reporter').then(() => {
return cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/shortcuts.cy.ts
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ describe('shortcuts', function () {
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', this.runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/spec_title.cy.ts
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ describe('spec title', () => {
win.render({ runner, runnerStore: { spec } })
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', {})
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/suites.cy.ts
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ describe('suites', () => {
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/test_errors.cy.ts
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ describe('test errors', () => {
// @ts-ignore
runnablesWithErr.suites[0].tests[0].err = err

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnablesWithErr)
runner.emit('reporter:start', {})
})
2 changes: 1 addition & 1 deletion packages/reporter/cypress/e2e/tests.cy.ts
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ function visitAndRenderReporter (studioEnabled: boolean = false, studioActive: b
})
})

cy.get('.reporter').then(() => {
cy.get('.reporter.mounted').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', { studioActive })
})
51 changes: 19 additions & 32 deletions packages/reporter/src/attempts/attempts.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cs from 'classnames'
import { observer } from 'mobx-react'
import React, { Component } from 'react'
import React from 'react'

import type { TestState } from '@packages/types'
import Agents from '../agents/agents'
@@ -52,7 +52,7 @@ function renderAttemptContent (model: AttemptModel, studioActive: boolean) {
<Sessions model={model.sessions} />
<Agents model={model} />
<Routes model={model} />
<div ref='commands' className='runnable-commands-region'>
<div className='runnable-commands-region'>
{model.hasCommands ? <Hooks model={model} /> : <NoCommands />}
</div>
{model.state === 'failed' && (
@@ -71,37 +71,24 @@ interface AttemptProps {
studioActive: boolean
}

@observer
class Attempt extends Component<AttemptProps> {
componentDidUpdate () {
this.props.scrollIntoView()
}

render () {
const { model, studioActive } = this.props

// HACK: causes component update when command log is added
model.commands.length

return (
<li
key={model.id}
className={cs('attempt-item', `attempt-state-${model.state}`)}
ref="container"
const Attempt: React.FC<AttemptProps> = observer(({ model, scrollIntoView, studioActive }) => {
return (
<li
key={model.id}
className={cs('attempt-item', `attempt-state-${model.state}`)}
>
<Collapsible
header={<AttemptHeader index={model.id} state={model.state} />}
hideExpander
headerClass='attempt-name'
contentClass='attempt-content'
isOpen={model.isOpen}
>
<Collapsible
header={<AttemptHeader index={model.id} state={model.state} />}
hideExpander
headerClass='attempt-name'
contentClass='attempt-content'
isOpen={model.isOpen}
>
{renderAttemptContent(model, studioActive)}
</Collapsible>
</li>
)
}
}
{renderAttemptContent(model, studioActive)}
</Collapsible>
</li>
)
})

const Attempts = observer(({ test, scrollIntoView, studioActive }: {test: TestModel, scrollIntoView: Function, studioActive: boolean}) => {
return (<ul className={cs('attempts', {
103 changes: 40 additions & 63 deletions packages/reporter/src/collapsible/collapsible.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,69 @@
import cs from 'classnames'
import React, { Component, CSSProperties, MouseEvent, ReactNode, RefObject } from 'react'
import React, { CSSProperties, MouseEvent, ReactNode, RefObject, useEffect, useState } from 'react'

import { onEnterOrSpace } from '../lib/util'

import ChevronIcon from '@packages/frontend-shared/src/assets/icons/chevron-down-small_x8.svg'

interface Props {
interface CollapsibleProps {
isOpen?: boolean
headerClass?: string
headerStyle?: CSSProperties
header?: ReactNode
headerExtras?: ReactNode
containerRef?: RefObject<HTMLDivElement>
contentClass?: string
hideExpander: boolean
hideExpander?: boolean
children: React.ReactNode
}

interface State {
isOpen: boolean
}

class Collapsible extends Component<Props, State> {
static defaultProps = {
isOpen: false,
headerClass: '',
headerStyle: {},
contentClass: '',
hideExpander: false,
}
const Collapsible: React.FC<CollapsibleProps> = ({ isOpen: isOpenAsProp = false, header, headerClass = '', headerStyle = {}, headerExtras, contentClass = '', hideExpander = false, containerRef = null, toggleOpen = () => undefined, children }) => {
const [isOpen, setIsOpen] = useState(isOpenAsProp)

constructor (props: Props) {
super(props)
useEffect(() => {
setIsOpen(isOpenAsProp)
}, [isOpenAsProp])

this.state = { isOpen: props.isOpen || false }
const _onClick = (e: MouseEvent) => {
e.stopPropagation()
setIsOpen(!isOpen)
}

componentDidUpdate (prevProps: Props) {
if (this.props.isOpen != null && this.props.isOpen !== prevProps.isOpen) {
this.setState({ isOpen: this.props.isOpen })
}
const _onKeyPress = () => {
setIsOpen(!isOpen)
}

render () {
return (
<div className={cs('collapsible', { 'is-open': this.state.isOpen })} ref={this.props.containerRef}>
<div className={cs('collapsible-header-wrapper', this.props.headerClass)}>
return (
<div className={cs('collapsible', { 'is-open': isOpen })} ref={containerRef}>
<div className={cs('collapsible-header-wrapper', headerClass)}>
<div
aria-expanded={isOpen}
className='collapsible-header'
onClick={_onClick}
onKeyUp={onEnterOrSpace(_onKeyPress)}
role='button'
tabIndex={0}
>
<div
aria-expanded={this.state.isOpen}
className='collapsible-header'
onClick={this._onClick}
onKeyPress={onEnterOrSpace(this._onKeyPress)}
role='button'
tabIndex={0}
className='collapsible-header-inner'
style={headerStyle}
tabIndex={-1}
>
<div
className='collapsible-header-inner'
style={this.props.headerStyle}
tabIndex={-1}
>
{!this.props.hideExpander && <ChevronIcon className='collapsible-indicator' />}
<span className='collapsible-header-text'>
{this.props.header}
</span>
</div>
{!hideExpander && <ChevronIcon className='collapsible-indicator' />}
<span className='collapsible-header-text'>
{header}
</span>
</div>
{this.props.headerExtras}
</div>
{this.state.isOpen && (
<div className={cs('collapsible-content', this.props.contentClass)}>
{this.props.children}
</div>
)}
{headerExtras}
</div>
)
}

_toggleOpen = () => {
this.setState({ isOpen: !this.state.isOpen })
}

_onClick = (e: MouseEvent) => {
e.stopPropagation()
this._toggleOpen()
}

_onKeyPress = () => {
this._toggleOpen()
}
{isOpen && (
<div className={cs('collapsible-content', contentClass)}>
{children}
</div>
)}
</div>
)
}

export default Collapsible
Loading