Skip to content
This repository has been archived by the owner on Sep 20, 2018. It is now read-only.

Commit

Permalink
[added] unified API
Browse files Browse the repository at this point in the history
  • Loading branch information
jquense committed Oct 11, 2015
1 parent abfdfed commit 13ffd4d
Show file tree
Hide file tree
Showing 17 changed files with 840 additions and 507 deletions.
3 changes: 2 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"stage": 0,
"loose": ["all"]
"loose": ["all"],
"plugins": ["object-assign"]
}
198 changes: 198 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,204 @@ A simple jquery like api wrapper for the React TestUtils to make them a bit frie
Updated for react 0.14; works seamlessly with Stateless Components and you can find and filter on DOM components
as well.

## API

Like jQuery the exported function creates a collection of nodes, except in this case you select ReactElements instead
of DOM nodes.

```js
import $ from 'react-testutil-query';

let $div = $(<div/>);

$div.length // 1
$div[0] // ReactElement{ type: 'div', props: {} ... }
```

Since there is no globally accessible "document" of ReactElements like there is of DOM nodes, you need
to start by selecting a tree. Once you have a tree you can query it with css selectors and jQuery-like methods.

```js
let elements = (
<MyComponent>
<MyInput/>
<MyInput/>
<div className='fun-div'>
</MyComponent>
);

var $elements = $(elements);

$elements.find('div.fun-div').length // 1
$elements.find(MyInput).length // 2
```

`react-testutil-query` actually supports _two_ types of collections, we've already seen Element Collections,
but you can also work with Component _instance_ collections as well for querying rendered components.

```js
let instance = ReactDOM.render(<Component/>, mountNode)

let $instance = $(instance);

$instance.dom() // HTMLElemen
```

There is even a quick way to switch between them.

```js
let elements = (
<MyComponent>
<MyInput/>
<MyInput/>
<div className='fun-div'>
</MyComponent>
);

var $elements = $(elements).render(); // renders `<MyComponent/>` into the DOM and returns an InstanceCollection

$elements.find(MyInput).dom() // HTMLElement{ tagName: 'input' ... }

$elements.unmount() // removes the mounted component and returns an ElementCollection
```

### Common Collection methods

The methods are shared by both Element and Instance Collections.

#### $.selector -> selector

Selector creation function.

#### $.fn.find(selector)

Search all descendants of the current collection, matching against
the provided selector.

```js
$(<ul><li>item 1</li></ul>).find('ul > li')
```

#### $.fn.filter(selector)

Filter the current collection matching against the provided
selector.

#### $.fn.is(selector) -> Bool

Test if each item in the collection matches the provided
selector.

#### $.fn.first([selector])

return the first item in a collection, alternatively search all
collection descendants matching the provided selector and return
the first match.

#### $.fn.last([selector])

return the last item in a collection, alternatively search all
collection descendants matching the provided selector and return
the last match.

#### $.fn.only()

Assert that the current collection as only one item.

#### $.fn.single(selector)

Find and assert that only item matches the provided selector.

#### $.fn.text()

Return the text content of the matched Collection.

```js
$(<div>Hello <strong>John</strong></div).text() // "Hello John"
```

### ElementCollection API

ElementCollections are created when selecting ReactElements. They
also have all the above "common" methods

#### $(ReactElement element) -> ElementCollection

Create an ElementCollection from an Element or array of Elements.

#### $.fn.render([Bool renderIntoDocument, HTMLElement mountPoint ]) -> InstanceCollection

Renders the first element of the ElementCollection into the DOM using `ReactDom.render`. By default
the component won't be added to the page `document`, you can pass `true` as the first parameter to render into the
document.body. Additional you can provide your own DOM node to mount the component into.

`render()` returns a new _InstanceCollection_

```js
let elements = (
<MyComponent>
<div className='fun-div'>
</MyComponent>
);

var $elements = $(elements).render();

$elements = $(elements).render(true); //accessible by document.querySelectorAll

$elements = $(elements).render(true, document.createElement('span')); //mounts the component to the <span/>
```

#### $.fn.shallowRender(props) -> ElementCollection

Use the React shallow renderer utilities to _shallowly_ render the first element of the collection.

```js
let MyComponent ()=> <div>Hi there!</div>

$(<MyComponent/>).find('div').length // 0

$(<MyComponent/>).shallowRender().is('div') // true
```

### $.fn.children([selector])

Return the children of the current selection, optionally filtered by those matching a provided selector.

```js
let $list = $(
<ul>
<li>1</li>
<li className='foo'>2</li>
<li>3</li>
</ul>
);

$list.children().length // 3

$list.children('.foo').length // 1
```

### InstanceCollection

InstanceCollections are created when selecting Component instances, such as
the result of a `ReactDOM.render()` call.

The public "instances" for components differ. DOM component instances
are the DOM nodes themselves, and Stateless Components technically don't have any
(we use the DOM node though). One key advantage to over the normal React
test utils is that here you can continue to chain `find` and `filter` on
DOM and Stateless components.

#### $.fn.dom -> HTMLElement

Returns the DOM nodes for each item in the Collection, if the exist

#### $.fn.unmount -> HTMLElement

Unmount the current tree and remove it from the DOM. `unmount()` returns an
ElementCollection of the _root_ component element.


### using selectors

The selector syntax is subset of normal css selectors. You can query by tag: `'div > li'` or
Expand Down
5 changes: 3 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = function (config) {
reporters: ['mocha'],

files: [
'./test/*.js'
'webpack.tests.js'
],

port: 9876,
Expand All @@ -22,7 +22,7 @@ module.exports = function (config) {
browsers: ['Chrome'],

preprocessors: {
'test/*.js': ['webpack']
'webpack.tests.js': ['webpack']
},

webpack: {
Expand All @@ -32,6 +32,7 @@ module.exports = function (config) {
},

webpackServer: {
stats: { progress: true, modules: false },
noInfo: true
}

Expand Down
22 changes: 20 additions & 2 deletions lib/shallow.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ var ShallowCollection = (function () {
}), this.root);
};

ShallowCollection.prototype.is = function is(selector) {
return this.filter(selector).length === this.length;
};

ShallowCollection.prototype.first = function first(selector) {
return selector ? this.find(selector).first() : new ShallowCollection(this[0], this.root);
};
Expand All @@ -150,11 +154,25 @@ var ShallowCollection = (function () {
return selector ? this.find(selector).last() : new ComponentCollection(this[this.length - 1], this.root);
};

ShallowCollection.prototype.is = function is(selector) {
return this.filter(selector).length === this.length;
ShallowCollection.prototype.text = function text() {
var str = '';

this.each(function (element) {
return traverse(element, function (el) {
return typeof el === 'string' && (str += el);
});
});
return str;
};

return ShallowCollection;
})();

function traverse(element, cb) {
cb(element);

if (element && element.props) _react2['default'].Children.forEach(element.props.children, function (child) {
traverse(child, cb);
});
}
module.exports = exports['default'];
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"devDependencies": {
"babel-core": "^5.8.25",
"babel-loader": "^5.3.2",
"babel-plugin-object-assign": "^1.2.1",
"chai": "^3.3.0",
"cpy": "^3.4.1",
"karma": "^0.13.10",
Expand All @@ -50,8 +51,10 @@
"webpack": "^1.12.2"
},
"dependencies": {
"bill": "^1.1.0",
"bill": "^1.3.2",
"dom-helpers": "^2.4.0",
"gulp-babel-helpers": "^2.2.1",
"lodash": "^3.10.1",
"react-addons-test-utils": "^0.14.0-rc1"
},
"release-script": {
Expand Down
54 changes: 54 additions & 0 deletions src/QueryCollection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import common from './common';


export default function(match, selector, init){

function QueryCollection(...args){

return new QueryCollection.fn.init(...args)
}

Object.assign(QueryCollection, {
match,
selector,
s: selector,
isQueryCollection(inst){
return !!inst._isQueryCollection
}
})

QueryCollection.fn =
QueryCollection.prototype = {
constructor: QueryCollection,
}

createInit(QueryCollection)
common(QueryCollection)

return QueryCollection

function createInit($){

$.fn.init = function $init(element, context, ...args){
let elements = element == null ? [] : [].concat(element);

if ($.isQueryCollection(element)) {
return new element.constructor(element.get(), element.context)
}
else {
this.context = (context && context.context) || context || element;
elements = init.call(this, elements, context, ...args);
}

if ($.isQueryCollection(elements))
return elements

elements.forEach((el, idx)=> this[idx] = el)

this._isQueryCollection = true
this.length = elements.length;
}

$.fn.init.prototype = $.fn
}
}
Loading

0 comments on commit 13ffd4d

Please sign in to comment.