├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
├── src
└── index.js
└── test
└── react-jsdom.js
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Node
2 |
3 | #npm5
4 | package-lock.json
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules
37 | jspm_packages
38 |
39 | # Optional npm cache directory
40 | .npm
41 |
42 | # Optional eslint cache
43 | .eslintcache
44 |
45 | # Optional REPL history
46 | .node_repl_history
47 |
48 | ## OS X
49 |
50 | *.DS_Store
51 | .AppleDouble
52 | .LSOverride
53 |
54 | # Icon must end with two \r
55 | Icon
56 |
57 |
58 | # Thumbnails
59 | ._*
60 |
61 | # Files that might appear in the root of a volume
62 | .DocumentRevisions-V100
63 | .fseventsd
64 | .Spotlight-V100
65 | .TemporaryItems
66 | .Trashes
67 | .VolumeIcon.icns
68 | .com.apple.timemachine.donotpresent
69 |
70 | # Directories potentially created on remote AFP share
71 | .AppleDB
72 | .AppleDesktop
73 | Network Trash Folder
74 | Temporary Items
75 | .apdisk
76 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '8'
4 | - '6'
5 | script: npm test
6 | after_success: npm run coverage
7 | notifications:
8 | email:
9 | on_success: never
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Luke Childs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-jsdom
2 |
3 | > Render React components to actual DOM nodes in Node.js
4 |
5 | [](https://travis-ci.org/lukechilds/react-jsdom)
6 | [](https://coveralls.io/github/lukechilds/react-jsdom?branch=master)
7 | [](https://www.npmjs.com/package/react-jsdom)
8 |
9 | Makes testing simple React components super easy with any Node.js test framework.
10 |
11 | ## Install
12 |
13 | ```
14 | npm install --save-dev react-jsdom
15 | ```
16 |
17 | ## Usage
18 |
19 | ```js
20 | const React = require('react');
21 | const ReactJSDOM = require('react-jsdom');
22 |
23 | class Hi extends React.Component {
24 | render() {
25 | return (
26 |
27 | hi
28 | {this.props.person}
29 |
30 | );
31 | }
32 |
33 | componentDidMount() {
34 | console.log('I mounted!');
35 | }
36 | }
37 |
38 | const elem = ReactJSDOM.render();
39 | // console: 'I mounted!'
40 |
41 | elem.constructor.name
42 | // 'HTMLDivElement'
43 | elem.nodeName;
44 | // 'DIV');
45 | elem.querySelector('span:last-child').textContent;
46 | // 'mum'
47 | elem.outerHTML;
48 | //
49 | // hi
50 | // mum
51 | //
52 | ```
53 |
54 | ## License
55 |
56 | MIT © Luke Childs
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-jsdom",
3 | "version": "3.0.0",
4 | "description": "Render React components to actual DOM nodes in Node.js",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "xo && nyc ava",
8 | "coverage": "nyc report --reporter=text-lcov | coveralls"
9 | },
10 | "xo": {
11 | "extends": [
12 | "xo-lukechilds",
13 | "xo-react"
14 | ]
15 | },
16 | "ava": {
17 | "babel": {
18 | "presets": [
19 | "@ava/stage-4",
20 | "react"
21 | ]
22 | }
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/lukechilds/react-jsdom.git"
27 | },
28 | "keywords": [
29 | "react",
30 | "dom",
31 | "jsdom",
32 | "test"
33 | ],
34 | "author": "Luke Childs (http://lukechilds.co.uk)",
35 | "license": "MIT",
36 | "bugs": {
37 | "url": "https://github.com/lukechilds/react-jsdom/issues"
38 | },
39 | "homepage": "https://github.com/lukechilds/react-jsdom",
40 | "dependencies": {
41 | "react-dom": "^16.3.1",
42 | "window": "^4.1.1"
43 | },
44 | "devDependencies": {
45 | "ava": "^0.25.0",
46 | "babel-preset-react": "^6.24.1",
47 | "coveralls": "^3.0.0",
48 | "eslint-config-xo-lukechilds": "^1.0.0",
49 | "eslint-config-xo-react": "^0.16.0",
50 | "eslint-plugin-react": "^7.2.0",
51 | "nyc": "^11.0.3",
52 | "react": "^16.3.1",
53 | "this": "^1.0.2",
54 | "xo": "^0.20.3"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Window = require('window');
4 | const ReactDOM = require('react-dom');
5 |
6 | const ReactJSDOM = {
7 | version: ReactDOM.version,
8 | render: (component, window) => {
9 | window = window || new Window();
10 | const document = window.document;
11 |
12 | const origGlobals = {
13 | window: global.window,
14 | document: global.document
15 | };
16 | global.window = window;
17 | global.document = document;
18 |
19 | const container = document.createElement('div');
20 | container.id = 'root';
21 | document.body.appendChild(container);
22 |
23 | ReactDOM.render(component, container);
24 |
25 | Object.keys(origGlobals).forEach(prop => {
26 | global[prop] = origGlobals[prop];
27 | });
28 |
29 | return container.childNodes[0];
30 | }
31 |
32 | };
33 |
34 | module.exports = ReactJSDOM;
35 |
--------------------------------------------------------------------------------
/test/react-jsdom.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types, react/jsx-no-bind */
2 |
3 | import test from 'ava';
4 | import Window from 'window';
5 | import React from 'react';
6 | import ReactJSDOM from 'this';
7 |
8 | class TestComponent extends React.Component {
9 | render() {
10 | return hi
;
11 | }
12 |
13 | componentDidMount() {
14 | if (typeof this.props.componentDidMount === 'function') {
15 | this.props.componentDidMount();
16 | }
17 | }
18 | }
19 |
20 | test('ReactJSDOM is a object', t => {
21 | t.is(typeof ReactJSDOM, 'object');
22 | });
23 |
24 | test('ReactJSDOM cleans up globals', t => {
25 | global.window = 'foo';
26 | global.document = 'bar';
27 | ReactJSDOM.render();
28 | t.is(global.window, 'foo');
29 | t.is(global.document, 'bar');
30 | });
31 |
32 | test('ReactJSDOM renders a React Component', t => {
33 | const elem = ReactJSDOM.render();
34 | t.is(elem.nodeName, 'DIV');
35 | t.is(elem.textContent, 'hi');
36 | });
37 |
38 | test('ReactJSDOM renders a React Fragment', t => {
39 | const elem = ReactJSDOM.render((
40 |
41 | ));
42 | t.is(elem.nodeName, 'DIV');
43 | t.is(elem.textContent, 'hi');
44 | });
45 |
46 | test('ReactJSDOM renders a Text String', t => {
47 | const elem = ReactJSDOM.render('Hello world');
48 | t.is(elem.nodeName, '#text');
49 | t.is(elem.textContent, 'Hello world');
50 | });
51 |
52 | test('ReactJSDOM renders a Fragment wrapping a text string', t => {
53 | const elem = ReactJSDOM.render(Hello world);
54 | t.is(elem.nodeName, '#text');
55 | t.is(elem.textContent, 'Hello world');
56 | });
57 |
58 | test('ReactJSDOM allows window instance to be passed in', t => {
59 | const window = new Window();
60 | const elem = ReactJSDOM.render(, window);
61 | t.is(elem, window.document.getElementById('root').children[0]);
62 | t.is(elem.nodeName, 'DIV');
63 | t.is(elem.textContent, 'hi');
64 | });
65 |
66 | test('componentDidMount fires', t => {
67 | ReactJSDOM.render( t.pass()}/>);
68 | });
69 |
--------------------------------------------------------------------------------