├── .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 | [![Build Status](https://travis-ci.org/lukechilds/react-jsdom.svg?branch=master)](https://travis-ci.org/lukechilds/react-jsdom) 6 | [![Coverage Status](https://coveralls.io/repos/github/lukechilds/react-jsdom/badge.svg?branch=master)](https://coveralls.io/github/lukechilds/react-jsdom?branch=master) 7 | [![npm](https://img.shields.io/npm/v/react-jsdom.svg)](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 | --------------------------------------------------------------------------------