• Stars
    star
    200
  • Rank 195,325 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 9 years ago
  • Updated about 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Test assertion helpers for use with React's shallowRender test utils

skin-deep

Testing helpers for use with React's shallowRender test utils.

npm version Build Status Coverage Status MIT Licensed

Install

npm install skin-deep

This lib should work on any version of React since 0.14. To allow for greater flexibility by users, no peerDependencies are included in the package.json. Your dependencies need to be as follows:

For React < 15.5:

  • react
  • react-addons-test-utils

For React >= 15.5:

  • react
  • react-dom
  • react-test-renderer

Quick Start

var React = require('react');

var MyComponent = React.createClass({
  displayName: 'MyComponent',
  render: function() {
    return (
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/abc">Draw</a></li>
        <li><a href="/def">Away</a></li>
      </ul>
    )
  }
});

var assert = require('assert');
var sd = require('skin-deep');

var tree = sd.shallowRender(<MyComponent />);

var homeLink = tree.subTree('a', { href: '/' });

assert.equal(homeLink.type, 'a');
assert.equal(homeLink.props.href, '/');
assert.equal(homeLink.text(), 'Home');

Breaking Changes in 1.0

  • React 0.13 is no longer supported
  • Rendering a function (which would be called) is no longer supported, this only existed for React 0.13's owner-based context
  • subTreeLike has been renamed to subTree
  • everySubTreeLike has been renamed to everySubTree
  • The original subTree has been removed, exact can be used to get this behaviour back, but I don't recommend you do.
  • The original subTreeLike has been removed, exact can be used to get this behaviour back, but I don't recommend you do.
  • removed findNode, use subTree instead
  • removed textIn, use subTree().text() instead
  • removed fillField, use subTree().props.onChange instead
  • removed findComponent, use subTree() instead
  • removed findComponentLike, use subTree() instead
  • toString() uses a completely different approach now. Previously it would use React's string rendering and produce the HTML including expanded children. Now it produces a pretty-printed representation of the result of the render.
  • reRender() now takes props instead of a ReactElement.

Usage

The goal of skin-deep is to provide higher level functionality built on top of the Shallow Rendering test utilities provided by React.

By default, shallow rendering gives you a way to see what a component would render without continuing along into rendering its children. This is a very powerful baseline, but in my opinion it isn't enough to create good UI tests. You either have to assert on the whole rendered component, or manually traverse the tree like this:

assert(rendered.props.children[1].props.children[2].children, 'Click Here');

By their nature user interfaces change a lot - sometimes these changes are to behaviour, but sometimes they're simply changes to wording or minor display changes. Ideally, we'd want non-brittle UI tests which can survive these superficial changes, but still check that the application behaves as expected.

Rendering

Use the shallowRender function to get a tree you can interact with.

var sd = require('skin-deep');

var tree = sd.shallowRender(<MyComponent />);

You can now inspect the the tree to see its contents

tree.getRenderOutput();
// -> ReactElement, same as normal shallow rendering

tree.type;
// -> The component type of the root element

tree.props;
// -> The props of the root element

The real benefits of skin deep come from the ability to extract small portions of the tree with a jQuery-esque API. You can then assert only on these sub-trees.

Extracting portions of the result

Extraction methods all take a CSS-esque selector as the first argument. This is commonly a component or tag name, but can also be a class or ID selector. The special value '*' can be used to match anything.

The second (optional) argument is the matcher, this can be an object to match against props, or a predicate function which will be passed each node and can decide whether to include it.

tree.subTree('Button');
// -> the first Button component

tree.everySubTree('Button');
// -> all the button components

tree.subTree('.button-primary');
// -> the first component with class of button-primary

tree.subTree('#submit-button');
// -> the first component with id of submit-button

tree.subTree('Button', { type: 'submit' });
// -> the first Button component with type=submit

tree.subTree('*', { type: 'button' });
// -> All components / elements with type=button

tree.subTree('*', function(node) { return node.props.size > 20; });
// -> All components / elements with size prop above 20

Triggering events

There's no DOM involved, so events could be a bit tricky - but as we're just using data, we can call functions directly!

var MyButton = React.createClass({
  clicked: function(e) {
    console.log(e.target.innerHTML);
  },
  render: function() {
    return <button onClick={this.clicked}>Click {this.props.n}</button>;
  }
});
var tree = sd.shallowRender(<MyButton />);
tree.subTree('button').props.onClick({
  target: {
    innerHTML: 'Whatever you want!'
  }
});

Going deeper

Sometimes shallow rendering isn't enough - often you'll want to have some integration tests which can render a few layers of your application. I prefer not to have to use a full browser or jsdom for this sort of thing - so we introduced the dive method. This allows you to move down the tree, recursively shallow rendering as needed.

var MyList = React.createClass({
  render: function() {
    return <ul>{[1,2,3].map(function(n) { return <MyItem n={n} />; })}</ul>;
  }
});
var MyItem = React.createClass({
  render: function() {
    return <li><MyButton>{this.props.n}</MyButton></li>;
  }
});
var MyButton = React.createClass({
  render: function() {
    return <button>Click {this.props.n}</button>;
  }
});

var tree = sd.shallowRender(<MyList />);
var buttonElement = tree.dive(['MyItem', 'MyButton']);
assert(buttonElement.text(), 'Click 1');

Use with test frameworks

TODO: flesh this out a bit more

Skin deep doesn't care which test framework you use, it just gives you the data you need to make assertions.

If you want to take this further, should be pretty simple to extend your favorite assertion library to be skin-deep aware.

As we use tend to use chai, there's a chai plugin bundled inside this package. You can use it via chai.use(require('skin-deep/chai')).

API Docs

.shallowRender(element [, context])

Get a tree instance by shallow-rendering a renderable ReactElement.

  • element {ReactElement} - element to render
  • context {object} - optional context

Returns tree

tree

tree.type

Access the type of the rendered root element.

Returns ReactComponent class or string.

tree.props

Access the props of the rendered root element.

Returns object

tree.reRender(props [, context])

Re-render the element with new props into the same tree as the previous render. Useful for testing how a component changes over time in response to new props from its parent.

  • props {object} - the new props, which will replace the previous ones
  • context {object} - optional context

Returns null

tree.text()

Access the textual content of the rendered root element including any text of its children. This method doesn't understand CSS, or really anything about HTML rendering, so might include text which wouldn't be displayed to the user.

If any components are found in the tree, their textual representation will be a stub like <Widget />. This is because they could do anything with their props, and thus are not really suitable for text assertions. If you have any suggestions for how to make it easier to do text assertions on custom components, please let me know via issues.

Returns string

tree.toString()

Produce a friendly JSX-esque representation of the rendered tree.

This is not really suitable for asserting against as it will lead to very brittle tests. Its main purpose is supposed to be for printing out nice debugging information, eg "no <selector> found in <tree.toString()>".

Returns string

tree.getRenderOutput()

Access the rendered component tree. This is the same result you would get using shallow rendering without skin-deep.

You usually shouldn't need to use this.

Returns ReactElement

tree.getMountedInstance()

Access the mounted instance of the component.

Returns object

tree.subTree(selector [, matcher])

Extract a portion of the rendered component tree. If multiple nodes match the selector, will return the first.

  • selector {Selector} - how to find trees
  • matcher {Matcher} - optional additional conditions

Returns tree or false

tree.everySubTree(selector [, matcher])

Extract multiple portions of the rendered component tree.

  • selector {Selector} - how to find trees
  • matcher {Matcher} - optional additional conditions

Returns array of trees

tree.dive(path)

"Dive" into the rendered component tree, rendering the next level down as it goes. See Going Deeper for an example.

Returns tree Throws if the path cannot be found.

Using Selectors

TODO

Using Matchers

TODO

.exact(props)

Create a matcher which only accepts nodes that have exactly those props passed in - no extra props.

  • props {object} - to match against

Returns function

.any

A magic value which can be used in a prop matcher that will allow any value to be matched. It will still fail if the key doesn't exist

eg.

{ abc: sd.any }

// Will match each of the following
<Component abc="1" />
<Component abc={100} />
<Component abc={function(){}} />

// but not
<Component />
<Component def="1" />

.hasClass(node, className)

Helper function to check if a node has the HTML class specified. Exported in case you want to use this in a custom matcher.

More Repositories

1

ultimate-hot-reloading-example

Hot reload all the things!
JavaScript
962
star
2

transit-immutable-js

Transit serialisation for Immutable.js
JavaScript
250
star
3

node-fib

Fast non-blocking fibonacci server
JavaScript
129
star
4

devboard

An interactive visual feedback tool for developing user interface code
JavaScript
87
star
5

mocha-multi

A bit of a hack to get multiple reporters working with mocha
JavaScript
75
star
6

jenkins-groovy-examples

Some samples of groovy scripts used in jenkins automation
47
star
7

ReactNativeFlexboxPlayground

Demo app to make it easy to experiment with flexbox layouts
JavaScript
40
star
8

react-hotkey

A simple mixin for application hotkeys
JavaScript
35
star
9

fluctuations

Yet another flux implementation
JavaScript
26
star
10

checkers

Property-based testing for JavaScript via ClojureScript's test.check
JavaScript
21
star
11

pretty-immutable

Pretty printing for ImmutableJS
JavaScript
12
star
12

cljs-webpack-deps

Trying out using npm dependencies via webpack in clojurescript.
Clojure
10
star
13

terrafiddle

Mini ruby dsl for generating terraform files
Ruby
9
star
14

node_modules

A curated list of beautiful node_modules that I like to use.
Makefile
7
star
15

typescript-xunit-xml

Produce xUnit-style XML output from typescript compiler messages
JavaScript
6
star
16

nodespec

A light-weight RSpec-esque testing framework for NodeJS with pluggable extensibility. I now recommend Mocha
JavaScript
6
star
17

hack-react-app

Zero Configuration, or is it?
JavaScript
6
star
18

fourten

An opinionated Go HTTP Client.
Go
5
star
19

git-mnemonic

Speakable, rememberable translations for git ref hashes
Python
5
star
20

npm-t-shirt

Bringing t-shirts to npm, sort of
JavaScript
5
star
21

react-calendar

Calendar example in React
JavaScript
4
star
22

phptea

A simple, flexible test harness for specifying tests in a SpecBDD style.
PHP
4
star
23

azure-terraform-generate

Generate terraform config from deployed azure resources.
Python
4
star
24

babblog-royale

Babble Royale log parser and viewer
TypeScript
3
star
25

vagrant-provider

Vagrant plugin for provider swapping
Ruby
3
star
26

react-element-to-string

Convert a ReactElement into a nice string useful for debugging
JavaScript
3
star
27

react-testing-workshop

Code for React Testing Workshop at jsconfco 2016.
JavaScript
3
star
28

byte-board

Like a whiteboard, but with bytes.
Elm
3
star
29

webpack-hot-client-overlay

An overlay for webpack-hot-client
JavaScript
2
star
30

present-express

Presenters + Hogan (mustache) for express
JavaScript
2
star
31

node-tracey

Find out how you got somewhere
JavaScript
2
star
32

flow-stuck-repro

JavaScript
2
star
33

q

Fakes stubs and spies for Clojure testing
Clojure
2
star
34

kaniko-publish-orb

CircleCI Orb to build and publish container images to container registries *without* docker.
Dockerfile
2
star
35

node-intro

Presentation and example sources for a NodeJS introduction
JavaScript
2
star
36

connect-hopeful-body-parser

Attempt to parse body data without an Accept header
JavaScript
2
star
37

CanDB

Database for storing potential recruitment candidates
JavaScript
2
star
38

github-cookbook

Chef Library Cookbook for declaring resources on GitHub
Ruby
1
star
39

in-the-loop

Simple pub/sub on a single HTML page - handy for hackday activity feeds
JavaScript
1
star
40

nodeschool-learnyounode

Solutions to nodeschool.io's learnyounode tutorial
JavaScript
1
star
41

raspbian-kiosk-maker

Some ansible-powered steps to getting a super-simple information radiator on a raspberry pi
1
star
42

shared-user-agent

A simple way to click links on one computer and have them open on another
Python
1
star
43

zombie-server

A REST-like HTTP server wrapping zombie.js sessions
JavaScript
1
star
44

merb-breadcrumbs

A simple merb plugin to provide useful hooks for breadcrumb links.
Ruby
1
star
45

epic-trello

An attempt at providing an epic-level view across a trello board.
JavaScript
1
star
46

magic-of-hot-code

Code samples for talk
JavaScript
1
star
47

lein-new-reflection

Clojure
1
star
48

eslint-samples

My standard eslint configs
JavaScript
1
star
49

ribbit

Provides a Module to handle sending notifications to hoptoad.
Ruby
1
star
50

not-talking

Slides for presentation on "Scaling Agile by not Talking to Each Other"
CSS
1
star
51

circles-and-arrows

HTML Canvas based Super-simple State Machine Diagram Tool
JavaScript
1
star
52

defmulti

Flexible JavaScript polymorphism without requiring inheritance or object orientism
JavaScript
1
star
53

tel

Super simple DI for JavaScript, targetted mainly at spec test setup
JavaScript
1
star
54

node-intro-challenge

Short programming excercise as a companion to a NodeJS intro presentation
JavaScript
1
star
55

glenjamin.github.com

Github presence
JavaScript
1
star
56

confident-user-interface-programming

A talk at CodeMesh 2016 by Glen Mailer
1
star
57

emojifight

Hot Module Reloading demo application
JavaScript
1
star
58

video-voting

Video Voting App for youtube movie nights
JavaScript
1
star
59

BehatMultiFormatter

A Behat extension adding a multipage HTML formatter with index and summary
1
star
60

node-controlled-amd

An AMD loader for NodeJS that keeps you in control by using minimal magic. Aimed primarily at loading frontend modules for unit testing.
JavaScript
1
star