I’m going to do an experiment on a branch to see if I can make the interface for testing a little easier to read. I want something a bit more like assert_select in Rails.

Let’s see now. Pretending it’s magic…

  test('shows the first blog post', async () => {
    const component = await mount(<Application />)
    
    check(component).has(
        {
          '.site-name':   'Blogging', 
          '.post .title': 'React on Rails', 
          '.post .body':  'I can use React with Rails.', 
        }
    )
  })

Or, how about this?

  test('also shows the first blog post', async () => {
    const component = await mount(<Application />)
    assert_select(component, '.site-name',   'Blogging')
    assert_select(component, '.post .title', 'React on Rails')
    assert_select(component, '.post .body',  'I can use React with Rails.')
  })

assert_select in Rails allows nesting. Maybe something like this?

 test('shows the first blog post with nesting', async () => {
    const component = await mount(<Application />)

    assert_select(component, '.site-name',   'Blogging')
    assert_select(component, '.post', match => {
      assert_select(match, '.title', 'React on Rails')
      assert_select(match, '.body', 'I can use React with Rails.')
    })
  })

Let’s try the simple (non-nested) version for a while to see how it feels. Here’s a quick implementation.

export function assert_select(component, selector, expectation=1) {
  const selected = component.find(selector)
  switch(typeof(expectation)) {
    case 'string':
      expect(selected.text()).toEqual(expectation)
      break
    case 'number':
      expect(selected.length).toEqual(expectation)
      break
    default:
      expect(expectation).toEqual('string or number')
      break
  }
}

If the expectation is a string, we’ll see if text matches. If it’s a number, we’ll check the number of matches. If you skip the expectation, we’ll just check that there is a match.

Let’s see how it goes. I’ll extract this into a ReactHelper along with a wrapper function for mount() that I will call display().

// test/javascript/helpers/ReactHelper.jsx

import {configure, mount} from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

configure({ adapter: new Adapter() })

export function display(component) {
  return mount(component)
}

This hides away the fiddly code to initialise enzyme and (SPOILERS) it also provides a handy place to initialise global state should we need that later.

I’ve updated all the tests to use assert_select and the setup code in ReactHelper.

Tidy is good.