Skip to content

Commit

Permalink
Add setProps, setProp, and other cy.render methods (#4)
Browse files Browse the repository at this point in the history
* WIP

* Add setProp and setProps methods. Improve cy.render() + add docs

* Add RenderWrapper type for cy

* Add return types for RenderWrapper methods
  • Loading branch information
Jon Quach committed May 1, 2019
1 parent 7375b89 commit ded92ac
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 71 deletions.
7 changes: 0 additions & 7 deletions src/__tests__/render.test.js

This file was deleted.

2 changes: 2 additions & 0 deletions src/cy.ts
Expand Up @@ -2,6 +2,7 @@ import { Cy } from './types/Cy.types'
import Cyan from './cyan'
import { get, getByCy, getByText } from './utils/selector.utils'
import { typeCommand } from './utils/keyEvent.utils'
import cleanUp from './cleanUp'
import debug from './debug'
import delay from './delay'
import * as timerFunctions from './timers'
Expand All @@ -23,6 +24,7 @@ const cy: Cy = {
return new Cyan(get(selector))
},
type: typeCommand,
cleanUp,
debug,
delay,
render,
Expand Down
9 changes: 2 additions & 7 deletions src/debug.ts
@@ -1,13 +1,8 @@
import { pretty } from './utils/pretty.utils'
import { getRootNode } from './utils/render.utils'
import { getDocumentHTML } from './utils/render.utils'

const debug = (...args) => {
const root = getRootNode() as Element
const other = Array.from(document.body.children).filter(node => node !== root)

const html = root.innerHTML + other.map(node => node.outerHTML).join('')

console.log(pretty(html), ...args)
console.log(pretty(getDocumentHTML()), ...args)
}

export default debug
46 changes: 0 additions & 46 deletions src/render.js

This file was deleted.

86 changes: 86 additions & 0 deletions src/render/RenderWrapper.tsx
@@ -0,0 +1,86 @@
import * as React from 'react'
import ReactDOM from 'react-dom'
import cleanUp from '../cleanUp'
import domCleanUp from '../domCleanUp'
import debug from '../debug'
import { runAllTimers } from '../timers'
import wrapWithProvider from './wrapWithProvider'
import { createRootNode, getDocumentHTML } from '../utils/render.utils'
import { isDefined } from '../utils/is.utils'

class RenderWrapper {
Component: any
WrappedComponent: any
initialProps: any
root: HTMLElement

constructor(Component: any) {
this.setComponent(Component)
this.mount(Component)

return this
}

setComponent(Component) {
this.Component = Component && Component.type ? <Component.type /> : null
this.initialProps = Component && Component.props ? Component.props : {}
}

mount(Component = this.Component) {
this.cleanUp()
this.setComponent(Component)
// Create the root node for ReactDOM to mount to
this.root = createRootNode()
document.body.appendChild(this.root)

// Render the WrappedComponent into the root node
this.WrappedComponent = wrapWithProvider(this.Component)

runAllTimers()
this.render(this.initialProps)

return this
}

setProps(props = this.initialProps) {
this.render(props)
return this
}

setProp(prop, value) {
if (!isDefined(prop)) {
return this
}

this.setProps({ [prop]: value })
return this
}

debug() {
debug()
return this
}

html() {
return getDocumentHTML()
}

cleanUp() {
cleanUp()
domCleanUp()
return this
}

unmount() {
this.cleanUp()
return this
}

render(props) {
const Component = this.WrappedComponent
ReactDOM.render(<Component {...props} />, this.root)
return this
}
}

export default RenderWrapper
@@ -1,6 +1,6 @@
import React from 'react'
import { connect } from 'react-redux'
import { cy, setStoreState } from '../index'
import { cy, setStoreState } from '../../index'

describe('render/redux', () => {
test('Can render a Redux connected component, without setup', () => {
Expand Down
142 changes: 142 additions & 0 deletions src/render/__tests__/render.test.js
@@ -0,0 +1,142 @@
import React from 'react'
import { cy } from '../../index'

describe('render', () => {
let spy

beforeEach(() => {
spy = jest.spyOn(global.console, 'log').mockImplementation(() => {})
})

afterEach(() => {
spy.mockRestore()
})

test('Can render null', () => {
expect(cy.render()).toBeTruthy()
})

test('Can setProps after rendering', () => {
const Base = ({ title }) => <span>{title}</span>
const wrapper = cy.render(<Base title="Hello" />)

expect(cy.get('span').text()).toBe('Hello')

wrapper.setProps({ title: 'There' })

expect(cy.get('span').text()).toBe('There')
})

test('Can setProps without props', () => {
const Base = ({ title }) => <span>{title}</span>
const wrapper = cy.render(<Base title="Hello" />)

expect(cy.get('span').text()).toBe('Hello')

wrapper.setProps()

expect(cy.get('span').text()).toBe('Hello')
})

test('Can setProp without props', () => {
const Base = ({ title }) => <span>{title}</span>
const wrapper = cy.render(<Base title="Hello" />)

expect(cy.get('span').text()).toBe('Hello')

wrapper.setProp()

cy.debug()

expect(cy.get('span').text()).toBe('Hello')
})

test('Does not unmount previous Component', () => {
const mountSpy = jest.fn()
const unmountSpy = jest.fn()

class Base extends React.Component {
componentDidMount() {
mountSpy()
}

componentWillUnmount() {
unmountSpy()
}

render() {
const { title } = this.props
return <span>{title}</span>
}
}

const wrapper = cy.render(<Base title="Hello" />)

expect(mountSpy).toHaveBeenCalled()
expect(unmountSpy).not.toHaveBeenCalled()
expect(cy.get('span').text()).toBe('Hello')

wrapper.setProps({ title: 'There' })
expect(cy.get('span').text()).toBe('There')
expect(unmountSpy).not.toHaveBeenCalled()

wrapper.setProp('title', 'Howdy')
expect(cy.get('span').text()).toBe('Howdy')
expect(unmountSpy).not.toHaveBeenCalled()
})

test('Can retrieve document.body.innerHTML', () => {
const wrapper = cy.render(<div>Hello</div>)

expect(wrapper.html()).toBe('<div>Hello</div>')
})

test('Can log (debug) innerHTML', () => {
const wrapper = cy.render(<div>Hello</div>)

wrapper.debug()

expect(spy).toHaveBeenCalledWith('<div>Hello</div>')
})

test('Can cleanUp document.body', () => {
const wrapper = cy.render(<div>Hello</div>)

expect(cy.get('div').exists()).toBeTruthy()

wrapper.cleanUp()

expect(cy.get('div').exists()).toBeFalsy()
})

test('Can unmount component', () => {
const wrapper = cy.render(<div>Hello</div>)

expect(cy.get('div').exists()).toBeTruthy()

wrapper.unmount()

expect(cy.get('div').exists()).toBeFalsy()
})

test('Can unmount + re-mount component', () => {
const wrapper = cy.render(<div>Hello</div>)

wrapper.unmount()
expect(cy.get('div').exists()).toBeFalsy()

wrapper.mount()
expect(cy.get('div').exists()).toBeTruthy()
})

test('Can unmount + mount another component', () => {
const wrapper = cy.render(<div>Hello</div>)

wrapper.unmount()
expect(cy.get('div').exists()).toBeFalsy()

wrapper.mount(<span>There</span>)
expect(cy.get('div').exists()).not.toBeTruthy()
expect(cy.get('span').exists()).toBeTruthy()
})
})
3 changes: 3 additions & 0 deletions src/render/index.ts
@@ -0,0 +1,3 @@
import render from './render'

export default render
7 changes: 7 additions & 0 deletions src/render/render.ts
@@ -0,0 +1,7 @@
import RenderWrapper from './RenderWrapper'

const render = (WrappedComponent = null) => {
return new RenderWrapper(WrappedComponent)
}

export default render
24 changes: 24 additions & 0 deletions src/render/wrapWithProvider.js
@@ -0,0 +1,24 @@
import * as React from 'react'
import { MemoryRouter as Router } from 'react-router'
import { Provider } from 'react-redux'
import { getStore } from '../store'

const wrapWithProvider = WrappedComponent => {
const store = getStore()

class WrappedWithProvider extends React.Component {
render() {
if (!WrappedComponent) return null

return (
<Provider store={store}>
<Router>{React.cloneElement(WrappedComponent, this.props)}</Router>
</Provider>
)
}
}

return WrappedWithProvider
}

export default wrapWithProvider

0 comments on commit ded92ac

Please sign in to comment.