+
+);
+
+var $elements = $(elements).render();
+
+$elements = $(elements).render(true); //accessible by document.querySelectorAll
+
+$elements = $(elements).render(true, document.createElement('span')); //mounts the component to the
+```
+
+#### $.fn.shallowRender(props) -> ElementCollection
+
+Use the React shallow renderer utilities to _shallowly_ render the first element of the collection.
+
+```js
+let MyComponent ()=>
Hi there!
+
+$(
).find('div').length // 0
+
+$(
).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 = $(
+
+);
+
+$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
diff --git a/karma.conf.js b/karma.conf.js
index 04de6c5..726d954 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -9,7 +9,7 @@ module.exports = function (config) {
reporters: ['mocha'],
files: [
- './test/*.js'
+ 'webpack.tests.js'
],
port: 9876,
@@ -22,7 +22,7 @@ module.exports = function (config) {
browsers: ['Chrome'],
preprocessors: {
- 'test/*.js': ['webpack']
+ 'webpack.tests.js': ['webpack']
},
webpack: {
@@ -32,6 +32,7 @@ module.exports = function (config) {
},
webpackServer: {
+ stats: { progress: true, modules: false },
noInfo: true
}
diff --git a/lib/shallow.js b/lib/shallow.js
index 896162e..f04e5f0 100644
--- a/lib/shallow.js
+++ b/lib/shallow.js
@@ -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);
};
@@ -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'];
\ No newline at end of file
diff --git a/package.json b/package.json
index f39212d..302c915 100644
--- a/package.json
+++ b/package.json
@@ -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",
@@ -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": {
diff --git a/src/QueryCollection.js b/src/QueryCollection.js
new file mode 100644
index 0000000..fc1e510
--- /dev/null
+++ b/src/QueryCollection.js
@@ -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
+ }
+}
diff --git a/src/shallow.js b/src/_shallow.js
similarity index 80%
rename from src/shallow.js
rename to src/_shallow.js
index adaee26..76027d1 100644
--- a/src/shallow.js
+++ b/src/_shallow.js
@@ -15,40 +15,39 @@ function match(selector, tree, includeSelf){
return _match(selector, tree, includeSelf)
}
-function render(element){
- let root = element;
-
- if (!(typeof root.type === 'string' && root.type.toLowerCase() === root.type)){
- let renderer = TestUtils.createRenderer()
- renderer.render(element)
- root = renderer.getRenderOutput();
- }
-
- return {
- root,
- setProps(props){
- return render(cloneElement(element, props))
- }
- }
-}
function rtq(element) {
var context, rerender;
if (TestUtils.isElement(element)) {
- let { root, setProps } = render(element)
- element = context = root
- rerender = setProps
+ element = context = element
}
else if (isRtq(element)) {
context = element.root
element = element.get();
}
- return new ShallowCollection(element, context, rerender)
+ return new ShallowCollection(element, context)
+}
+
+rtq.render = function render(element, props) {
+ let isDomElement = element
+ && typeof element.type === 'string'
+ && element.type.toLowerCase() === element.type;
+
+ if (props)
+ element = cloneElement(element, props)
+
+ if (isDomElement)
+ return rtq(element)
+
+ let renderer = TestUtils.createRenderer()
+ renderer.render(element)
+ return rtq(renderer.getRenderOutput());
}
class ShallowCollection {
+
constructor(elements, root, rerender){
elements = [].concat(elements).filter(el => isValidElement(el))
@@ -57,7 +56,6 @@ class ShallowCollection {
while( ++idx < elements.length)
this[idx] = elements[idx]
- this._rerender = rerender
this.length = elements.length
this.root = root
}
@@ -95,7 +93,7 @@ class ShallowCollection {
find(selector) {
return this.reduce((result, element) => {
- return result.concat(match(selector, element))
+ return result.concat(match(selector, element, false))
}, [])
}
@@ -109,7 +107,7 @@ class ShallowCollection {
if (!selector)
return this
- let matches = match(selector, this.root);
+ let matches = match(selector, this.root, true);
return new ShallowCollection([].filter.call(this, el => {
return matches.indexOf(el) !== -1
@@ -118,6 +116,7 @@ class ShallowCollection {
is(selector) {
+
return this.filter(selector).length === this.length
}
diff --git a/src/common.js b/src/common.js
new file mode 100644
index 0000000..4cd6094
--- /dev/null
+++ b/src/common.js
@@ -0,0 +1,99 @@
+import * as utils from './utils';
+
+export default function($){
+
+ Object.assign($, {
+ dom(component){
+ return utils.findDOMNode(component)
+ }
+ })
+
+ Object.assign($.fn, {
+ _subjects: $.fn.get,
+
+ _reduce: $.fn.reduce,
+
+ _map(cb){
+ var result = []
+ this.each((...args) => result.push(cb(...args)))
+ return result
+ },
+
+ each(cb, thisArg) {
+ var idx = -1, len = this.length;
+ while (++idx < len) cb.call(thisArg, this[idx], idx, this)
+ return this
+ },
+
+ get() {
+ var result = []
+ this.each(el => result.push(el))
+ return result
+ },
+
+ reduce(cb, initial){
+ return $([].reduce.call(this, cb, initial), this)
+ },
+
+ map(cb) {
+ return this.reduce((result, ...args) => {
+ result.push(cb(...args))
+ return result
+ }, [])
+ },
+
+ find(selector) {
+ return this._reduce((result, element) => {
+ return result.concat($.match(selector, element, false))
+ }, [])
+ },
+
+ traverse(test){
+ return this._reduce((result, element) => {
+ return result.concat(utils.traverse(element, test))
+ }, [])
+ },
+
+ filter(selector) {
+ if (!selector) return this
+
+ let matches = $.match(selector, this.context, true);
+
+ return this._reduce((result, element) => {
+ if (matches.indexOf(element) !== -1)
+ result.push(element);
+
+ return result
+ }, [])
+ },
+
+ is(selector) {
+ return this.filter(selector).length === this.length
+ },
+
+ first(selector){
+ return selector
+ ? this.find(selector).first()
+ : $(this[0], this)
+ },
+
+ last(selector){
+ return selector
+ ? this.find(selector).last()
+ : $(this[this.length - 1], this)
+ },
+
+ only(){
+ if (this.length !== 1)
+ throw new Error('The query found: ' + this.length + ' items not 1 ')
+
+ return this.first()
+ },
+
+ single(selector) {
+ return selector
+ ? this.find(selector).only()
+ : this.only()
+ }
+ })
+}
diff --git a/src/element.js b/src/element.js
new file mode 100644
index 0000000..b393636
--- /dev/null
+++ b/src/element.js
@@ -0,0 +1,71 @@
+import React, { isValidElement, cloneElement } from 'react';
+import ReactDOM from 'react-dom';
+import ReactTestUtils from'react-addons-test-utils';
+import createQueryCollection from './QueryCollection';
+import iQuery from './instance'
+import * as utils from './utils';
+import { selector } from 'bill';
+
+let isComponent = el => utils.isDOMComponent(el) || utils.isCompositeComponent(el)
+
+let eQuery = createQueryCollection(utils.match, selector, function init(elements, context){
+ let first = elements.filter(e => !!e)[0];
+ if (first && isComponent(first))
+ return iQuery(elements);
+
+ return elements.filter(el => isValidElement(el))
+})
+
+Object.assign(eQuery.fn, {
+
+ _reduce: eQuery.fn.reduce,
+
+ render(intoDocument, mountPoint){
+ var mount = mountPoint || document.createElement('div')
+ , element = this[0];
+
+ if (intoDocument)
+ document.body.appendChild(mount)
+
+ let instance = ReactDOM.render(element, mount);
+
+ if (instance === null)
+ instance = ReactDOM.render(utils.wrapStateless(element), mount)
+
+ return iQuery(instance, utils.getInternalInstance(instance), mount);
+ },
+
+ shallowRender(props) {
+ if (!this.length) return this
+
+ let element = this[0];
+ let isDomElement = typeof element.type === 'string' && element.type.toLowerCase() === element.type;
+
+ if (props)
+ element = cloneElement(element, props)
+
+ if (isDomElement)
+ return eQuery(element)
+
+ let renderer = ReactTestUtils.createRenderer()
+ renderer.render(element)
+ return eQuery(renderer.getRenderOutput());
+ },
+
+ children(selector) {
+ return this
+ .reduce((result, element) => result.concat(element.props.children || []), [])
+ .filter(selector)
+ },
+
+ text(){
+ let isText = el => typeof el === 'string';
+
+ return this.get().reduce((str, element)=> {
+ return str + utils.traverse(element, isText).join('')
+ }, '')
+ }
+
+})
+
+export default eQuery;
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index ae076f9..0000000
--- a/src/index.js
+++ /dev/null
@@ -1,252 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import ReactInstanceMap from 'react/lib/ReactInstanceMap';
-import utils from 'react-addons-test-utils';
-import { match as _match, selector as sel } from './instance-selector';
-import hasClass from 'dom-helpers/class/hasClass';
-
-var $r = module.exports = rtq
-
-let isRtq = item => item && item._isRTQ
-
-rtq.react = React;
-rtq.s = rtq.selector = sel;
-
-function rtq(element, mount, renderIntoDocument = (mount === true)) {
- var context;
-
- if (!mount || mount === true)
- mount = document.createElement('div')
-
- if (utils.isElement(element))
- context = element = render(element, mount, renderIntoDocument)
- else if (utils.isDOMComponent(element) || utils.isCompositeComponent(element)){
- context = element
- mount = rtq.dom(element).parentNode
- }
- else if (isRtq(element)) {
- mount = element._mountPoint
- context = element.context
- element = element.get();
- }
- else
- throw new TypeError('Wrong type: must either be ReactElement or a Component Instance')
-
- return new ComponentCollection(element, context, mount)
-}
-
-function render(element, mountPoint, renderIntoDocument = false) {
- var mount = mountPoint;
-
- if (renderIntoDocument)
- document.body.appendChild(mount)
-
- let instance = ReactDOM.render(element, mount);
-
- if (instance === null) {
- instance = ReactDOM.render(wrapStateless(element), mount)
- }
-
- if (!instance.renderWithProps) {
- instance.renderWithProps = newProps => render(
- React.cloneElement(element, newProps)
- , mount
- , renderIntoDocument
- )
- }
-
- return instance;
-}
-
-function match(selector, tree, includeSelf){
- if (typeof selector === 'function')
- selector = sel`${selector}`
-
- return _match(selector, tree, includeSelf)
-}
-
-rtq.dom = function(component){
- return component instanceof HTMLElement ? component : ReactDOM.findDOMNode(component)
-}
-
-
-class ComponentCollection {
-
- constructor(_components, context, mountPoint, selector){
- var components = _components == null ? [] : [].concat(_components)
- , idx = -1, len = components.length
-
- this._privateInstances = Object.create(null)
-
- while (++idx < len) {
- var component = components[idx]
-
- if (component.getPublicInstance) {
- this._privateInstances[idx] = component
- component = component.getPublicInstance();
-
- //stateless
- if (component === null)
- component = ReactDOM.findDOMNode(this._privateInstances[idx]._instance)
- }
- // if this a root Stateless component
- else if (component && component.__isRTQstatelessWrapper){
- let wrapperInstance = ReactInstanceMap.get(component);
- this._privateInstances[idx] = wrapperInstance._renderedComponent;
- component = ReactDOM.findDOMNode(component)
- }
- else {
- this._privateInstances[idx] = ReactInstanceMap.get(component) || component._reactInternalComponent
- }
-
- this[idx] = component
- }
-
- this.length = len
- this.context = context
- this.selector = selector
- this._mountPoint = mountPoint
- this._isRTQ = true
- }
-
- _root(){
- return this.context._reactInternalComponent || this.context
- }
-
- unmount(){
- let inBody = !!this.context.parentNode;
- ReactDOM.unmountComponentAtNode(this._mountPoint)
-
- if (inBody)
- document.body.removeChild(this._mountPoint)
-
- this.context = null
- }
-
- setProps(newProps){
- return this.mapInPlace(element => element.renderWithProps(newProps))
- }
-
- each(cb, thisArg) {
- var idx = -1, len = this.length;
- while( ++idx < len ) cb.call(thisArg, this[idx], idx, this)
- return this
- }
-
- mapInPlace(cb, thisArg) {
- return this.each((el, idx, list)=> this[idx] = cb(el, idx, list))
- }
-
- map(cb, thisArg) {
- var idx = -1, len = this.length, result = []
- while (++idx < len) result.push(cb.call(thisArg, this[idx], idx, this))
- return result
- }
-
- _reduce(cb, initial){
- return new ComponentCollection(
- this._getInstances().reduce(cb, initial)
- , this.context
- , this._mountPount
- , this.selector
- )
- }
-
- reduce(cb, initial){
- return new ComponentCollection(
- [].reduce.call(this, cb, initial)
- , this.context
- , this._mountPount
- , this.selector
- )
- }
-
- _getInstances(){
- return this.map((_, idx) => {
- return this._privateInstances[idx]
- })
- }
-
- get(){
- return unwrap(this.map(component => component))
- }
-
- dom() {
- return unwrap(this.map(rtq.dom))
- }
-
- find(selector){
- return this._reduce((result, instance) => {
- return result.concat(match(selector, instance, false))
- }, [])
- }
-
- filter(selector) {
- if (!selector) return this
-
- let matches = match(selector, this._root());
-
- return this._reduce((result, el) => {
- return matches.indexOf(el) !== -1 ? result.concat(el) : result
- }, [])
- }
-
- only(){
- if (this.length !== 1) throw Error('`' + this.selector +'` found: ' + this.length + ' not 1 ')
- return this.first()
- }
-
- single(selector) {
- return selector
- ? this.find(selector).only()
- : this.only()
- }
-
- first(selector){
- return selector
- ? this.find(selector).first()
- : new ComponentCollection(this[0], this.context, this._mountPount, this.selector)
- }
-
- last(selector){
- return selector
- ? this.find(selector).last()
- : new ComponentCollection(this[this.length - 1], this.context, this._mountPount, this.selector)
- }
-
- is(selector) {
- return this.filter(selector).length === this.length
- }
-
- trigger(event, data){
- data = data || {}
-
- if (event.substr(0, 2) === 'on' )
- event = event.substr(2, 1).toLowerCase() + event.substr(3)
-
- if (!(event in utils.Simulate))
- throw new TypeError( '"' + event + '" is not a supported DOM event')
-
- return this.each(component =>
- utils.Simulate[event]($r.dom(component), data))
- }
-}
-
-
-function unwrap(arr){
- return arr && arr.length === 1 ? arr[0] : arr
-}
-
-function wrapStateless(Element){
- class StatelessWrapper extends React.Component {
- constructor(){
- super()
- this.__isRTQstatelessWrapper = true
- }
- render(){
- return Element
- }
- }
-
- return
-}
diff --git a/src/instance-selector.js b/src/instance-selector.js
deleted file mode 100644
index 9bf5ecd..0000000
--- a/src/instance-selector.js
+++ /dev/null
@@ -1,85 +0,0 @@
-var React = require('react');
-var ReactDOM = require('react-dom')
-var ReactInstanceMap = require('react/lib/ReactInstanceMap');
-var ReactTestUtils = require('react-addons-test-utils')
-
-import { create as createCompiler, parse } from 'bill/compiler';
-import { isCompositeComponent } from './utils';
-
-let compiler = createCompiler()
-
-compiler.registerPseudo('has', function(compiledSelector) {
- return (root, inst) => {
- let matches = findAll(inst, compiledSelector)
- return !!matches.length
- }
-})
-
-compiler.registerPseudo('dom', function() {
- return (root, inst) => {
- return typeof root.type === 'string' && root.type.toLowerCase() === root.type
- }
-})
-
-compiler.registerPseudo('composite', function() {
- return (root, inst) => {
- return typeof root.type === 'function'
- }
-})
-
-compiler.registerNesting('any', test => anyParent.bind(null, test))
-
-compiler.registerNesting('>', test => directParent.bind(null, test))
-
-
-function findAll(inst, test, getParent = ()=> ({ parent: null }), excludeSelf = true) {
- let found = [];
-
- if (!inst || !inst.getPublicInstance)
- return found;
-
- let publicInst = inst.getPublicInstance()
- , element = inst._currentElement
- , parent = ()=> ({ parent: element, getParent });
-
- if (!excludeSelf && test(element, inst, getParent))
- found = found.concat(inst)
-
- if (ReactTestUtils.isDOMComponent(publicInst)) {
- let renderedChildren = inst._renderedChildren || {};
-
- Object.keys(renderedChildren).forEach(key => {
- found = found.concat(
- findAll(renderedChildren[key], test, parent, false)
- );
- })
- }
- else if (isCompositeComponent(publicInst)) {
- found = found.concat(findAll(inst._renderedComponent, test, parent, false));
- }
-
- return found;
-}
-
-function anyParent(test, element, inst, parentNode){
- do {
- var { getParent, parent } = parentNode();
- element = parent
- parentNode = getParent
- } while(element && !test(element, test, getParent))
-
- return !!element
-}
-
-function directParent(test, element, inst, parentNode) {
- element = parentNode().parent
- return !!(element && test(element, parentNode().getParent))
-}
-
-export function match(selector, inst, includeSelf = true) {
- let tree = inst.getPublicInstance ? inst : ReactInstanceMap.get(inst)
-
- return findAll(tree, compiler.compile(selector), undefined, !includeSelf)
-}
-
-export let { compile, compileRule, selector } = compiler
diff --git a/src/instance.js b/src/instance.js
new file mode 100644
index 0000000..933888a
--- /dev/null
+++ b/src/instance.js
@@ -0,0 +1,96 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ReactInstanceMap from 'react/lib/ReactInstanceMap';
+import ReactTestUtils from'react-addons-test-utils';
+
+import closest from 'dom-helpers/query/closest';
+import createQueryCollection from './QueryCollection';
+import * as utils from './utils';
+import { match } from './utils';
+import selector from 'bill';
+
+let $ = createQueryCollection(match, selector, function init(components, context, mount){
+ let first = components[0]
+
+ mount = mount || (context && context._mountPoint) || utils.getMountPoint(first);
+
+ this.context = (context && context.context)
+ || context
+ || utils.getInternalInstance(utils.getRootInstance(mount))
+
+ this._mountPoint = mount;
+ this._privateInstances = Object.create(null)
+
+ return components.map((component, idx) => {
+ let instances = utils.getInstances(component);
+ this._privateInstances[idx] = instances.private
+ return instances.public
+ })
+})
+
+Object.assign($, {
+ dom(component){
+ return utils.findDOMNode(component)
+ }
+})
+
+Object.assign($.fn, {
+
+ _subjects(){
+ return [].map.call(this,
+ (_, idx) => this._privateInstances[idx])
+ },
+
+ _reduce(cb, initial){
+ return $(this._subjects().reduce(cb, initial), this)
+ },
+
+ unmount(){
+ let inBody = this._mountPoint.parentNode
+ , nextContext = this.context._currentElement;
+
+ ReactDOM.unmountComponentAtNode(this._mountPoint)
+
+ if (inBody)
+ document.body.removeChild(this._mountPoint)
+
+ this.context = null
+
+ return eQuery(nextContext)
+ },
+
+ dom(){
+ return unwrap(this._map($.dom))
+ },
+
+ text(){
+ let isText = el => typeof el === 'string';
+
+ return this._subjects().reduce((str, element)=> {
+ return str + utils.traverse(element, isText)
+ .map(inst => inst._currentElement || inst)
+ .join('')
+ }, '')
+ },
+
+ trigger(event, data){
+ data = data || {}
+
+ if (event.substr(0, 2) === 'on' )
+ event = event.substr(2, 1).toLowerCase() + event.substr(3)
+
+ if (!(event in ReactTestUtils.Simulate))
+ throw new TypeError( '"' + event + '" is not a supported DOM event')
+
+ return this.each(component =>
+ ReactTestUtils.Simulate[event]($.dom(component), data))
+ }
+})
+
+function unwrap(arr){
+ return arr && arr.length === 1 ? arr[0] : arr
+}
+
+export default $;
+
+import eQuery from './element';
diff --git a/src/utils.js b/src/utils.js
index a6a25da..21b4fd0 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,4 +1,18 @@
-var ReactTestUtils = require('react-addons-test-utils')
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ReactInstanceMap from 'react/lib/ReactInstanceMap';
+import {
+ getID, getNode, findReactContainerForID
+ , getReactRootID, _instancesByReactRootID } from 'react/lib/ReactMount';
+import ReactTestUtils from'react-addons-test-utils';
+
+import closest from 'dom-helpers/query/closest';
+import { match as _match, selector as s } from 'bill';
+
+import { findAll as instanceTraverse } from 'bill/instance-selector';
+import { findAll as elementTraverse } from 'bill/element-selector';
+
+export let isDOMComponent = ReactTestUtils.isDOMComponent;
export function isCompositeComponent(inst) {
if (ReactTestUtils.isDOMComponent(inst)) {
@@ -8,3 +22,81 @@ export function isCompositeComponent(inst) {
}
return inst === null || typeof inst.render === 'function' && typeof inst.setState === 'function';
}
+
+export function getInstances(component){
+ let _public = component
+ , _private = getInternalInstance(component);
+
+ if (component.getPublicInstance) {
+ _public = component.getPublicInstance();
+
+ //stateless
+ if (_public === null)
+ _public = ReactDOM.findDOMNode(_private._instance)
+ }
+ // if this a root Stateless component
+ else if (component.__isStatelessWrapper)
+ _public = ReactDOM.findDOMNode(component)
+
+ return { private: _private, public: _public }
+}
+
+export function getInternalInstance(component){
+ if (!component) return
+
+ if (component.getPublicInstance)
+ return component
+
+ if (component.__isStatelessWrapper)
+ return ReactInstanceMap.get(component)._renderedComponent
+
+ if (component._reactInternalComponent)
+ return component._reactInternalComponent
+
+ return ReactInstanceMap.get(component)
+}
+
+export function wrapStateless(Element){
+ class StatelessWrapper extends React.Component {
+ constructor(){
+ super()
+ this.__isStatelessWrapper = true
+ }
+ render(){
+ return Element
+ }
+ }
+
+ return
+}
+
+export function getMountPoint(instance){
+ var id = getID(findDOMNode(instance));
+ return findReactContainerForID(id);
+}
+
+export function getRootInstance(mountPoint){
+ return _instancesByReactRootID[getReactRootID(mountPoint)];
+}
+
+export function findDOMNode(component){
+ return component instanceof HTMLElement
+ ? component
+ : component && component._rootID
+ ? getNode(component._rootID)
+ : ReactDOM.findDOMNode(component)
+}
+
+export function match(selector, tree, includeSelf){
+ if (typeof selector === 'function')
+ selector = s`${selector}`
+
+ return _match(selector, tree, includeSelf)
+}
+
+export function traverse(tree, test, includeSelf = true){
+ if (React.isValidElement(tree))
+ return elementTraverse(tree, test, includeSelf)
+
+ return instanceTraverse(tree, test, includeSelf)
+}
diff --git a/test/common.js b/test/common.js
new file mode 100644
index 0000000..767fed2
--- /dev/null
+++ b/test/common.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import { unmountComponentAtNode, render } from 'react-dom';
+import $ from '../src/element';
+
+describe('common utils', ()=> {
+ let Stateless = props =>
{props.children}
+
+ class List extends React.Component {
+ render(){
+ return (
+
+
+
+
+
+ )
+ }
+ }
+
+ it('should create collection', ()=>{
+ $(
).length.should.equal(1)
+ $(
)[0].type.should.equal('div')
+ })
+
+ it('should get', ()=>{
+ Array.isArray($(
).get()).should.equal(true)
+ })
+
+ it('should render element', ()=> {
+ let instance = $(
).render()
+
+ instance._mountPoint.querySelectorAll('.test').length.should.equal(1)
+ expect(instance._mountPoint.parentNode).to.not.exist
+ })
+
+ it('should render element at mountPoint', ()=> {
+ let mount = document.createElement('div')
+ let instance = $(
).render(false, mount)
+
+ mount.children[0].classList.contains('test').should.equal(true)
+ instance._mountPoint.should.equal(mount)
+ })
+
+ it('should render into document', ()=> {
+ let instance = $(
).render(true)
+
+ document.querySelectorAll('.test').length.should.equal(1)
+
+ unmountComponentAtNode(instance._mountPoint)
+ })
+
+ it('should render mount into document', ()=> {
+ let mount = document.createElement('div')
+ let instance = $(
).render(true, mount)
+
+ document.querySelectorAll('.test').length.should.equal(1)
+ instance._mountPoint.should.equal(mount)
+
+ unmountComponentAtNode(instance._mountPoint)
+ })
+
+ it('should work with Stateless components as root', ()=>{
+ let instance = $(
).render()
+ instance.length.should.equal(1)
+ })
+
+ it('should shallow render', ()=>{
+ let instance = $(
).shallowRender()
+
+ instance.length.should.equal(1)
+ instance[0].type.should.equal('div')
+ })
+})
diff --git a/test/dom.js b/test/dom.js
index c8dd125..72b9c92 100644
--- a/test/dom.js
+++ b/test/dom.js
@@ -1,10 +1,7 @@
import React from 'react';
import { unmountComponentAtNode, render } from 'react-dom';
-import $ from '../src/index';
-import { match, selector as sel } from '../src/instance-selector';
-
-chai.use(require('sinon-chai'))
-
+import $ from '../src/element';
+import * as utils from '../src/utils';
describe('DOM rendering', ()=> {
let Stateless = props =>
{props.children}
@@ -12,6 +9,7 @@ describe('DOM rendering', ()=> {
render(){
return (
+ Hello there
@@ -38,34 +36,18 @@ describe('DOM rendering', ()=> {
}
}
- describe('css selector parsing', ()=>{
-
- it('should match nested', ()=>{
- let inst = $(
).get();
-
- match('.list-wrapper', inst).length.should.equal(1)
-
- match(sel`div.list-wrapper > ${List}`, inst).length.should.equal(1)
-
- match(sel`${Stateless}`, inst).length.should.equal(1)
-
- match(sel`.list-wrapper:has(${List})`, inst).length.should.equal(1)
-
- match(sel`span:has(${List})`, inst).length.should.equal(0)
- })
-
- })
-
it('should wrap existing mounted component', ()=> {
- let instance = render(
, document.createElement('div'))
+ let mount = document.createElement('div')
+ , instance = render(
, mount)
let $inst = $(instance);
expect($inst[0]).to.equal(instance);
- expect($inst.context).to.equal(instance)
+ //expect($inst.context).to.equal(utils.getInternalInstance(instance))
+ expect($inst._mountPoint).to.equal(mount)
})
- it('should recreate rtq object', ()=> {
+ it('should recreate $ object', ()=> {
let instance = $(
)
let instanceB = $(instance);
@@ -74,60 +56,23 @@ describe('DOM rendering', ()=> {
expect(instance[0]).to.equal(instanceB[0])
})
- it('should render element', ()=> {
- let instance = $(
)
-
- instance.context.tagName.should.equal('DIV')
- expect(instance._mountPoint.parentNode).to.not.exist
- })
-
- it('should render element at mountPoint', ()=> {
- let mount = document.createElement('div')
- let instance = $(
, mount)
-
- mount.children[0].classList.contains('test').should.equal(true)
- instance._mountPoint.should.equal(mount)
- })
-
- it('should render into document', ()=> {
- let instance = $(
, true)
-
- document.querySelectorAll('.test').length.should.equal(1)
-
- unmountComponentAtNode(instance._mountPoint)
- })
-
- it('should render mount into document', ()=> {
- let mount = document.createElement('div')
- let instance = $(
, mount, true)
-
- document.querySelectorAll('.test').length.should.equal(1)
- instance._mountPoint.should.equal(mount)
-
- unmountComponentAtNode(instance._mountPoint)
- })
-
- it('should work with Stateless components as root', ()=>{
- let instance = $(
)
-
- instance.length.should.equal(1)
- })
-
it('should unmount', ()=> {
let mount = document.createElement('div')
- let instance = $(
, mount, true)
+ let instance = $(
).render(true, mount)
document.querySelectorAll('.test').length.should.equal(1)
- instance.unmount()
+ let next = instance.unmount()
document.querySelectorAll('.test').length.should.equal(0)
expect(instance.context).to.not.exist
expect(mount.parentNode).to.not.exist
+
+ expect(next[0].type).to.equal('div')
})
it('should return DOM node from Component', ()=> {
- let instance = $(
)
+ let instance = $(
).render()
instance.dom().should.be.an.instanceof(HTMLElement)
})
@@ -140,66 +85,66 @@ describe('DOM rendering', ()=> {
it('should `get()` underlying element', ()=> {
let instance = $(
)
- instance.get().should.equal(instance[0])
+ instance.get()[0].should.equal(instance[0])
})
-
- it('should set props', ()=> {
- let instance = $(
)
-
- instance.setProps({ min: 5 })
-
- instance[0].props.min.should.equal(5)
- })
-
+//
+// it('should set props', ()=> {
+// let instance = $(
)
+//
+// instance.setProps({ min: 5 })
+//
+// instance[0].props.min.should.equal(5)
+// })
+//
describe('querying', ()=> {
describe('find', ()=> {
it('should find by Component Types', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
- instance.find(List).get().should.be.an.instanceof(List);
+ instance.find(List)[0].should.be.an.instanceof(List);
})
it('should find by Component Stateless Types', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.find(Stateless).length.should.equal(1);
})
it('should find by :composite', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
let result = instance.find(':composite')
- result.length.should.equal(2);
+ result.length.should.equal(3);
})
it('should find by :dom', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
let result = instance.find(':dom')
result.length.should.equal(10);
})
- it('should return stateless component dom nodes', ()=>{
- let instance = $(
)
+ it('should return stateless component DOM nodes', ()=>{
+ let instance = $(
).render()
- instance.find(Stateless).get().should.be.an.instanceof(HTMLElement);
+ instance.find(Stateless)[0].should.be.an.instanceof(HTMLElement);
})
it('should find by className', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
- instance.find('.list-wrapper').get().tagName.should.equal('DIV');
+ instance.find('.list-wrapper')[0].tagName.should.equal('DIV');
})
it('should find by tag', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.find('li').length.should.equal(3);
})
it('should allow find chaining', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
let items = instance
.find('.list-wrapper')
@@ -222,55 +167,55 @@ describe('DOM rendering', ()=> {
describe('is', ()=> {
it('should recognize Component Types', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.is(Component).should.equal(true);
})
it('should recognize Stateless Types', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.is(Stateless).should.equal(true);
})
it('should recognize className', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.is('.test').should.equal(true);
})
it('should recognize tags', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.is('span').should.equal(true);
})
it('should recognize :composite', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.is(':composite').should.equal(true);
})
it('should recognize :dom', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.is(':dom').should.equal(true);
})
it('should work with find', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.find(Stateless).is(Stateless).should.equal(true);
})
it('should work with multiple matches', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.find('li').is('li').should.equal(true);
})
it('should work with chaining', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.find('li').is('.item').should.equal(false);
@@ -279,60 +224,66 @@ describe('DOM rendering', ()=> {
})
it('should: filter()', ()=>{
- let items = $(
).find('li')
+ let items = $(
).render().find('li')
items.length.should.equal(3)
items.filter('.item').length.should.equal(1)
- $(
).find('div > *').filter(List).length.should.equal(1)
+ $(
).render().find('div > *').filter(List).length.should.equal(1)
})
it('an empty filter should be a noop', ()=>{
- let instance = $(
)
+ let instance = $(
).render()
instance.filter().should.equal(instance)
})
+ it('should get first', ()=> {
+ let instance = $(
).render()
+
+ instance.first('li')[0].textContent.should.equal('hi 1')
+
+ instance.find('li').first()[0].textContent.should.equal('hi 1')
+ })
+
+ it('should get last', ()=> {
+ let instance = $(
).render()
+
+ instance.last('li')[0].textContent.should.equal('hi 3');
+ instance.find('li').last()[0].textContent.should.equal('hi 3')
+ })
+
it('should find single', ()=> {
- let instance = $(
)
+ let instance = $(
).render()
instance.single(Stateless).length.should.equal(1)
})
it('should throw when single returns more than one', ()=> {
- let instance = $(
)
+ let instance = $(
).render()
;(()=> instance.single('li')).should.throw()
})
it('should throw when single returns none', ()=> {
- let instance = $(
)
+ let instance = $(
).render()
;(()=> instance.single('article')).should.throw()
})
- it('should get first', ()=> {
- let instance = $(
)
-
- instance.first('li')[0].textContent.should.equal('hi 1')
-
- instance.find('li').first()[0].textContent.should.equal('hi 1')
- })
-
- it('should get last', ()=> {
- let instance = $(
)
+ it('text content', ()=>{
+ $(
).render().text().should.equal('Hello therehi 1hi 2hi 3')
- instance.last('li')[0].textContent.should.equal('hi 3');
- instance.find('li').last()[0].textContent.should.equal('hi 3')
+ $(
hi {'john'}
).render().text().should.equal('hi john')
})
it('should trigger event', ()=> {
let clickSpy = sinon.spy();
- let instance = $(
)
+ let instance = $().render()
instance.find(List).trigger('click', { clickedYo: true })
clickSpy.should.have.been.calledOnce
})
- })
+ })
})
diff --git a/test/shallow.js b/test/shallow.js
index dbd43dd..b0ba6f0 100644
--- a/test/shallow.js
+++ b/test/shallow.js
@@ -1,10 +1,8 @@
import React, { cloneElement } from 'react';
-import $ from '../src/shallow';
-import { selector as sel } from 'bill';
+import $ from '../src/element';
-chai.use(require('sinon-chai'))
-describe('Shallow rendering', ()=> {
+describe.only('Shallow rendering', ()=> {
let Stateless = props => {props.children}
let List = class extends React.Component {
render(){
@@ -23,10 +21,10 @@ describe('Shallow rendering', ()=> {
}
}
- it('create rtq object', ()=>{
+ it('create element collection', ()=>{
let instance = $()
- instance.root.type.should.equal('div')
+ instance.context.type.should.equal('div')
instance.length.should.equal(1)
})
@@ -34,15 +32,24 @@ describe('Shallow rendering', ()=> {
let el =
, instance = $(el)
- instance.root.should.equal(el)
+ instance.context.should.equal(el)
})
it('should render Composite Components', ()=>{
let el =
, Element = ()=> el
- , instance = $()
+ , instance = $().shallowRender()
- instance.root.should.equal(el)
+ instance.context.should.equal(el)
+ })
+
+ it.only('should query Composite Components', ()=>{
+ $()
+ .is(Element).should.equal(true)
+
+ $(
)
+ .find('div > span')
+ .length.should.equal(1)
})
it('should filter out invalid Elements', ()=>{
@@ -73,40 +80,41 @@ describe('Shallow rendering', ()=> {
)
it('should: find()', ()=>{
- $(
).find('li').length.should.equal(3)
+ $(
).shallowRender().find('li').length.should.equal(3)
})
it('should: children()', ()=> {
- $(
).find(FancyList).children().length.should.equal(3)
+ $(
).shallowRender().find(FancyList).children().length.should.equal(3)
- $(
).find(FancyList).children('.foo').length.should.equal(2)
+ $(
).shallowRender().find(FancyList).children('.foo').length.should.equal(2)
})
it('should: filter()', ()=>{
- let items = $(
).find('li')
+ let items = $(
).shallowRender().find('li')
items.length.should.equal(3)
items.filter('.foo').length.should.equal(2)
})
it('an empty filter should be a noop', ()=>{
- let instance = $(
)
+ let instance = $(
).shallowRender()
instance.filter().should.equal(instance)
})
it.only('text content', ()=>{
- let instance = $(
)
- instance.text().should.equal('hi 1hi 2hi 3')
+ $(
).shallowRender().text().should.equal('hi 1hi 2hi 3')
+
+ $(hi {'john'}
).text().should.equal('hi john')
})
it('should: is()', ()=>{
- $(
).find('.foo')
+ $(
).shallowRender().find('.foo')
.is('li').should.equal(true)
- $(
).find('.foo')
- .is(sel`${FancyList} > li`).should.equal(true)
+ $(
).shallowRender().find('.foo')
+ .is($.s`${FancyList} > li`).should.equal(true)
- $(
).find(FancyList)
+ $(
).shallowRender().find(FancyList)
.is('div').should.equal(false)
})
})
diff --git a/webpack.tests.js b/webpack.tests.js
new file mode 100644
index 0000000..ada098b
--- /dev/null
+++ b/webpack.tests.js
@@ -0,0 +1,6 @@
+
+chai.use(require('sinon-chai'))
+
+const testsContext = require.context('./test', true, /.+/)
+
+testsContext.keys().forEach(testsContext);