Coder Social home page Coder Social logo

tests's Introduction

##Legit Tests

This is a super friendly testing library for React, inspired by express middleware, it's easily extendable. Why did I make this when you can use React's Test Utils? Because who likes typing out scryRenderedDOMComponentsWithTag and the other method names on there. Not only that, but setting up the render process is also a hassle.

###Install

npm install legit-tests --save

##Example

import Test from 'legit-tests'
//or
import Test from 'legit-tests/no-dom' //don't include jsdom

import { expect } from 'chai'
import sinon from 'sinon'
import TestComponent from './TestComponent'

let spy = sinon.spy()


//Calling a prop
Test(<TestComponent onClick={spy}/>)
.find('button')
.simulate({method: 'click', element: 'button'})
.test(() => {
  expect(spy.called).to.be.true
})

//finding an element
Test(<TestComponent/>)
.find('.box')
.elements('.box', (box) => {
  expect(box.props.children).to.be.equal('found me!')
})

##Middleware

Current list of Middleware

You can write middleware to do anything you repeatedly use. You can pass .use a function, along with an object that it will take in. Each function will be injected with the current instance which includes:

  • component - the actual component itself
  • instance - the rendered component instance
  • helpers - an array you can add on to with data for the end function

Example:

  • See mixin below, this syntax may soon be deprecated

This is the setState function used above.

Test(<TestComponent onClick={spy}/>)
.use(SetState, {})

...

export default function setState(state){
  this.instance.setState(state)
}

##test

The .test function will be given the component instance and the helpers array. You can use a regular function to reference this or an arrow function:

.test(({helpers, instance}) => { ... })
.test(function() {
  //this.instance, this.helpers
})

##element

Use .element if you're just testing an element you found with the .find method. The syntax is a little smaller:

Test(<TestComponent/>)
.find('.box')
.element(box => {
  expect(box.props.children).to.be.equal('found me!')
})
//or specify the element

.find('.box')
.find('div')
.element('.box', box => {
  expect(box.props.children).to.be.equal('found me!')
})

##mixin

Use .mixin if you want to add new middleware as methods to Test. This gives a more natural way of using middleware:

// In this example, CustomFind middleware was added to Test by mixin
// and used if as it was a method on Test itself.

Test(<TestComponent />)
.mixin({
  customFind: CustomFind
})
.customFind('cells', 'table td')
.element('cells', cells => {
  expect(cells.length).to.be.equal(10)
})

##DOM rendering Shallow -- uses React shallow rendering (no DOM)

Test(<TestComponent onClick={spy}/>, {shallow: true})
.find('button')
.simulate({method: 'click', element: 'button'})
.test(() => {
  expect(spy.called).to.be.true
})

Normal -- React render into document fragment

Test(<TestComponent onClick={spy}/>)
.find('button')
.simulate({method: 'click', element: 'button'})
.test(() => {
  expect(spy.called).to.be.true
})

fullDOM -- ReactDOM render into document.body.div of jsdom

Test(<section />, {fullDOM: true})
.test(function() {
  expect(global.window.document.querySelector('section'))
  .to.be.okay
})
.clean() // restores the document.body to empty

You can see more examples in the tests directory.

##Testing Alt Stores

You can now test Alt stores using the same API.

import TestStore from 'legit-tests/alt/store'

TestStore(MyStore, MyActions)
.setInitialState({ todos: todos })
.addTodo({ title: "Get Beer", complete: false })
.test(({ state }) => {
  expect(state.todos).to.eql(expected);
})

You can see the full documentation on the Wiki

tests's People

Contributors

jasonblanchard avatar kenwheeler avatar nickstefan avatar vramana avatar zackify avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tests's Issues

Issue with Webpack

As my components require some assets like Sass files, JSON etc, they are compiled by Webpack through karma-webpack. When I try to import legit-tests I get a ton of errors which I think are related to JSDom saying I'm missing various modules (fs, child_process, etc). Any idea how to solve that?

referencing snake-cased class name selectors from .simulate()

If I have something like:

Test(<MyComponent />)
  .find('.thumbs-up-button')
  .simulate({ method: 'click', element: '.thumbs-up-button' })

It's unclear from the docs how exactly to reference that class name selector from .simulate or .test, since the only class name examples are one word (e.g. .box). Is the above code example supposed to work?

README doesn't work as expected with the .test callback

Hi there.

Your README examples feature you using arrow functions for your callbacks, however when using arrow functions for .test, this.instance is undefined etc. For example:

Passes:

Test(...)
  .test(function() {
    expect(this.instance).toBeDefined();
  });

Fails:

Test(...)
  .test(() => {
    expect(this.instance).toBeDefined();
  });

Just thought I'd let you know. :)

Cleanup globals after tests

Hi there!

Thank you so much for the lib, I love the middleware idea and I'm planning to use it on a massive scale.
In the meantime, I noticed some problems in my test suite because globals are not cleaned up after a test.
The problem is that some libraries test for the presence of window or document to know if they run in node or in the browser.

If you take a look at what mocha-jsdom does : they have a hook to restore the global values after each test.

Another problem is react testing the ExecutionEnvironment.canUseDOM boolean.
In my experience, you can either put this to true like you did or try to initialise a fake DOM before all your test suites (with mocha --require flag for instance).
Mocha-jsdom fails miserably at this because in case of nested component re-rendering, this flag will be reinitialised.

I just wanted to open the discussion to know if you have any insight about this.

React error when testing components that include refs

We have a React class that uses a ref property. It renders perfectly fine in our application and does not have any errors. However, when using Test from legit-tests to run unit tests, they all fail. If the ref property in the original file is commented out or removed, the tests run as expected. With the ref property intact, however, the following error occurs on all the tests:

screen shot 2016-05-05 at 1 42 51 pm

This issue is reproduced when running unit tests using Test from legit-tests on any React components whose properties include refs. We are running React version 15.0.1.

Usage in browser?

I'm trying to use this project in the browser using JSPM and it looks like it uses child_process which isn't supported in the browser. Is there anyway around this?

Add wrapper for testing Alt stores

@zackify Per our conversation:

store:

import alt from '../alt';
import BaseStore from './base_store';
import immutable from 'alt/utils/ImmutableUtil';
import Immutable from 'immutable';
import actions from '../actions/edit_subscription_actions';

@immutable
export class EditSubscriptionStore extends BaseStore {
  constructor() {
    super();
    this.bindActions(actions);
  }

  updateAlsoEnergy(response) {

  }

  revokeMembership(response) {
    let index = this.state.get('members').findIndex((x) => {
      return Immutable.Map(x).getIn(['data', 'id']) === response.data.id;
    });

    this.setState(this.state.updateIn(['members'], (x) => {
      return x.set(index, response);
    }));
  }

  activateMembership(response) {
    let index = this.state.get('members').findIndex((x) => {
      return Immutable.Map(x).getIn(['data', 'id']) === response.data.id;
    });


    this.setState(this.state.updateIn(['members'], (x) => {
      return x.set(index, response);
    }));
  }

  setSavingState(state) {
    this.setState({ savingStatus: state });
  }
}

export default alt.createStore(EditSubscriptionStore, 'EditSubscriptionStore');

dphaener [11:32 PM]
test:

import alt from '../../../app/assets/javascripts/alt';
import { expect } from 'chai';
import AltTestingUtils from 'alt/utils/AltTestingUtils';

import { EditSubscriptionStore } from '../../../app/assets/javascripts/stores/edit_subscription_store';

describe('EditSubscriptionStore', () => {
  describe('#revokeMembership', () => {
    let members = [
      {
        data: {
          id: "foo",
          attributes: {
            status: "active"
          }
        }
      },
      {
        data: {
          id: "bar",
          attributes: {
            status: "active"
          }
        }
      }
    ];

    it('should update the members list with the updated member', () => {
      let response = {
        data: {
          id: "foo",
          attributes: {
            status: "revoked"
          }
        }
      };

      let unwrappedStore = AltTestingUtils.makeStoreTestable(alt, EditSubscriptionStore);

      unwrappedStore.setInitialState({ members: members });
      setTimeout(() => {
        unwrappedStore.revokeMembership(response);

        let members = unwrappedStore.state.get('members'),
            updatedMember = members[0];

        expect(updateMember.attributes.status).to.equal("revoked");
      }, 5000);

    });
  });
});

dphaener [11:33 PM]
So, Alt does have some test utils. Which is cool. But, things are still happening asynchronously, so setting initial state needs to happen, and then everything else has to happen in a promise, instead of the super hacky set timeout

dphaener [11:34 PM]
That test passes, but it’s dumb

jsdom and node 12 vs node 4

love the library, but doesn't the jsdom dependency make this node 4 only?

Could we possibly make jsDom a peerDependency?

That way one could use a node 12 compatable jsDom such as 3.x.x?

harmony-reflect should be in dependencies

Hello! This library looks great, but I haven't yet successfully used it. After running npm i -D legit-tests, I set up my first test—a test of an alt store. And I encountered this:

ERROR in ./~/legit-tests/alt/store.js
Module not found: Error: Cannot resolve module 'harmony-reflect' in /.../node_modules/legit-tests/alt

I assume I can also add harmony-reflect to my package to get around this.

I see that y'all have that library listed as a devDependency. It should probably be a regular dependency, yeah?

improved CSS selectors for the find middleware?

I understand that the library is very flexible, and it's easier to make plugins than make breaking changes to the .find middleware.

However, I think that we could support more css selectors than the current find middleware does.

This is by no means perfect, but it's my first pass at it:

/*
 * For now only handles things like:
 *     'span#bob.single-theater-container[data-bob-thing]'
 * CANNOT handle
 *     'span#bob .single-theater-container[data-bob-thing]'
 * CANNOT handle
 *     'span#bob > .single-theater-container[data-bob-thing]'
 */

import * as ReactTestUtils from 'react-addons-test-utils';
import _ from 'lodash-compat';

function findSelector(selector, fn){
    var self = this;
    var foundElements = [];
    var tokens = selector.split(/(?=\.)|(?=#)|(?=\[)/);

    tokens
    .forEach(function(subselector, key, collection){
        var els;
        switch (subselector[0]){
            // class
            case '.':
                els = ReactTestUtils.scryRenderedDOMComponentsWithClass(self.instance, subselector.slice(1));
                foundElements.push( Array.isArray(els) ? els : [els] );
                break;

            // id
            case '#':
                els = ReactTestUtils.findAllInRenderedTree(self.instance, function(component){
                    if (component.id === subselector.slice(1)){
                        return true;
                    }
                });
                foundElements.push( Array.isArray(els) ? els : [els] );
                break;

            // data attribute
            case '[':
                els = ReactTestUtils.findAllInRenderedTree(self.instance, function(component){
                    if (component.getAttribute) {
                        return component.getAttribute(subselector.slice(1,-1));
                    }
                });
                foundElements.push( Array.isArray(els) ? els : [els] );
                break;

            // tag
            default:
                els = ReactTestUtils.scryRenderedDOMComponentsWithTag(self.instance, subselector);
                foundElements.push( Array.isArray(els) ? els : [els] );
                break;
        }
    });

    var elements = _.intersection.apply(_, foundElements);

    if (Array.isArray(elements) && elements.length === 1) {
        this.elements[selector] = elements[0];
    } else {
        this.elements[selector] = elements;
    }
}

Any ideas for how we could support even more selectors? Sizzle?

Improve the API

Hi guys,

I've given it some thoughts and here is what I'd love to see in legit tests:

  • Error handling with exceptions
  • Be able to differentiate between collections of elements and single elements
  • Keep the current result of find() in the context so we are able to find elements and execute actions in that context

Error Handling

It would be easy to throw if somebody tries to call simulate(), for instance, without having called find() beforehand.
Likewise, we could throw an error if no matching element is found when calling find().

I believe this would make the API easier to apprehend and avoid debugging sessions.

Multi selection and current context

For the two other proposals, let me give you some examples and you can tell me if you think that's heading in the right direction.

Let's say that we have the following ButtonComponent:

<div>
  <div className="main">
    <button value="ok" onClick={this.props.action1} />
  </div>
  <div className="other">
    <button value="submit" onClick={this.props.action2} />
  </div>
</div>

If we kept the current element found by the find() method as our root, we could do something like this:

Test(<ButtonComponent/>)
.find('.main', {root: true}) // this would be implicit because the root will be the component at the beginning
.find('button', {root: false}) // this would be implicit because the root is now 'main'
.simulate('click') // click is implicitly done on the root 
.test(({component}) => {
  expect(component.props.action1).to.have.been.called
})

For the multi-component selections, maybe we could let the user decide wether he expects a single or an array of elements. The following is inspired by the mongo api, where you can explicitly say you want a single or a "multi" result:

<div className="component">
  <table className="table1">
    <tr><td>1</td><td>2</td></tr>
    <tr><td>3</td><td>4</td></tr>
 </table>
 <table className="table2">
    <tr><td>5</td><td>6</td></tr>
    <tr><td>7</td><td>8</td></tr>
 </table>
</div>

We could then either write this:

Test(<TableComponent/>)
.find('.table1')
.find('td', {multi: true})
.element('td', td => {
  expect(td.length).toEqual(4)
})

Or:

Test(<TableComponent/>)
.find('table', {multi: false}) // this can be the default behavior
.find('td', {multi: false})
.element(td => {
  expect(td.getDOMNode().textContent).to.equal('1')
})

What do you think?

Support for react 15 in 1.1.x (or when is 2.0 expected?)

It looks like this will be happening in v2.0 so I suppose the question is which will happen first?

Could you just bump the dependency in 1.1.x to allow react 15?
or should we wait for 2.0? and what is the release date for that?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.