Skip to content

Instantly share code, notes, and snippets.

@rpominov
Last active November 1, 2015 11:43
Show Gist options
  • Save rpominov/f84e15104adc3cdda2f0 to your computer and use it in GitHub Desktop.
Save rpominov/f84e15104adc3cdda2f0 to your computer and use it in GitHub Desktop.
React testing ideas

Stateles components

// mark relevant parts of result tree with __tetsId & __testClass
const TestTarget = props => 
  <div>
    <Foo foo={props.foo} __tetsId="foo" />
    <ul>
      <li num={1} __testClass="baz" />
      <li num={2} __testClass="baz" />
    </ul>
  </div>


// (tree, testId) -> null | element
const getByTestID = ...
getByID(tree, 'foo') = <Foo foo="bar" __tetsId="foo" />


// (tree, testClass) -> [element]
const getByTestClass = ...
getByTestClass(tree, 'baz') = [<li num={1} __testClass="baz" />, <li num={2} __testClass="baz" />]


// (tree, ids, classes) -> {[id|class]: null | element | [element]}
const getRelevant = ...
getRelevant(tree, ['foo'], ['baz']) = {
  foo: <Foo foo="bar" __tetsId="foo" />, 
  baz: [<li num={1} __testClass="baz" />, <li num={2} __testClass="baz" />]
}


// ({[id|class]: null | element | [element]}) -> {[id|class]: null | props | [props]}
const getProps = ...
getProps(relevantElements) = {foo: {foo: 'bar'}, baz: [{num: 1}, {num: 2}]}


const render = element =>
  TestUtils.createRenderer().render(element).getRenderOutput()

const renderToRelevant = (Comp, ids, classes) => props =>
  getRelevant(render(<Comp {...props} />), ids, classes)

const renderToRelevantProps = (Comp, ids, classes) => props =>
  getProps(renderToRelevant(Comp, ids, classes)(props))


// Example

render = renderToRelevantProps(TestTarget, ['foo'], ['baz'])

expect(render({foo: 'bar'}).foo.foo).toBe('bar')
expect(render({foo: 'lol'}).foo.foo).toBe('lol')

Statefull

const treeToRelevantProps = (ids, classes) => tree => getProps(getRelevant(tree, ids, classes))


run(TestTarget, treeToRelevantProps(ids, classes), addToLog => {
  const events
})

// We get log, which contains all versions of the tree & any items we add manualy using addTolog()
[tree1, 'click'] = run(Button, treeToRelevantProps(ids, classes), addTolog => {

  // We return list of input events
  return [
    // An {} is "update props" event, we should always start with initial props
    {onClick: () => {addTolog('click')}},

    // A function is "arbitrary action" event, we get 
    (relevantProps, latestTree, logSoFar) => {relevantProps.innerButton.onClick()}
  ]
})

// Now we can run some assertions agains the log
# Stateles
`props -> tree` or better `rootProps -> childProps`
# Stateful
[InputEvents] -> [OutputEvents]
1. InputEvents
1.1 initial props & props updates
1.2 calls of callbacks passed as props to child components
1.3 calls of callbacks passed to some external API (which may be passed as a prop/context or be global)
2. OutputEvents
2.1 result Tree updates
2.2 calls of callbacks passed as props
2.3 calls of some external API methods
Problems:
In 2.2 we want to check calls of callbacks that provided in 1.1
In 1.2 we want to make calls of callbacks that provided in 2.1
InputEvent type 1.1: NextProps
InputEvent type 1.2: (LatestTree) -> void
InputEvent type 1.3: (logSoFar) -> void? but we dont want to have callbacks (not serializable/comparable data) inside OutputEvents log, do we?
* LatestTree is part of logSoFar
OutputEvent type 2.1: NextTree
OutputEvent type 2.2: in 1.1 we may pass framework.namedCallbak(name), then here we will have {name, args} struct
OutputEvent type 2.3: create a Mock; make sure component uses the Mock; merge Mock's method calls to [OutputEvents] stream/array
arrayOfOutputEvents = run(Comp, addToLog => {
// create mock for 2.3 here
return [{props...} | (logSoFar) -> void]
})
['click'] = run(Button, treeToState, log => {
return [
{onClick: () => {log('click')}},
(state) => {state.button.onClick()}
]
})
@rpominov
Copy link
Author

run() should also be partially applicable — run = (Comp, getRelevantProps) => runner => log
Maybe run = (Comp, ids, classes) => (addToLog => events) => log

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment