├── .gitignore ├── .travis.yml ├── LICENSE ├── package.json ├── src ├── jsdomify.js └── polyfills │ └── classList.js ├── test └── test.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | lib 4 | coverage 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | cache: 4 | directories: 5 | - node_modules 6 | node_js: 7 | - "0.10" 8 | - "0.12" 9 | script: "npm run-script travis" 10 | after_success: "", 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/podio/jsdomify-compat/issues" 46 | }, 47 | "homepage": "https://github.com/podio/jsdomify-compat" 48 | } 49 | -------------------------------------------------------------------------------- /src/jsdomify.js: -------------------------------------------------------------------------------- 1 | 2 | import {jsdom} from 'jsdom'; 3 | let actualDOM; 4 | let documentRef; 5 | 6 | let create = (domString) => { 7 | 8 | actualDOM = domString || ''; 9 | global.document = jsdom(actualDOM); 10 | global.window = document.parentWindow; 11 | global.location = window.location; 12 | global.Element = window.Element; 13 | global.navigator = { 14 | userAgent: 'node.js' 15 | }; 16 | 17 | // shim document.classList 18 | require('./polyfills/classList')(global.window); 19 | 20 | documentRef = document; 21 | }; 22 | 23 | let clear = () => { 24 | destroy(); 25 | create(actualDOM); 26 | }; 27 | 28 | let destroy = (clearRequireCache) => { 29 | 30 | if(typeof clearRequireCache === 'undefined') { 31 | clearRequireCache = true; 32 | } 33 | 34 | window.close(); 35 | delete global.window; 36 | delete global.location; 37 | delete global.Element; 38 | delete global.navigator; 39 | delete global.document; 40 | documentRef = undefined; 41 | 42 | if (clearRequireCache) { 43 | Object.keys(require.cache).forEach((key) => { 44 | if (key.indexOf('require') !== -1) { 45 | delete require.cache[key]; 46 | } 47 | }); 48 | } 49 | 50 | }; 51 | 52 | let getDocument = () => { 53 | if (typeof documentRef === 'undefined') { 54 | throw new Error(`document is undefined\nTry calling jsdomify.create() before requesting it\n`); 55 | } 56 | 57 | return documentRef; 58 | }; 59 | 60 | 61 | export default { 62 | create: create, 63 | clear: clear, 64 | destroy: destroy, 65 | getDocument: getDocument 66 | }; -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import expect from 'unexpected'; 4 | 5 | import jsdomify from '../src/jsdomify'; 6 | jsdomify.create(); 7 | 8 | describe('jsdomify API', () => { 9 | 10 | describe('general behaviour', () => { 11 | 12 | it('should append a child to the body', () => { 13 | 14 | var par = document.createElement("P"); 15 | var text = document.createTextNode("some text"); 16 | par.appendChild(text); 17 | document.body.appendChild(par); 18 | 19 | expect(document.body.innerHTML, 'not to be empty'); 20 | }); 21 | 22 | it('should have cleared the window from any child', () => { 23 | 24 | jsdomify.clear(); 25 | expect(document.body.innerHTML, 'to be empty'); 26 | }); 27 | 28 | it('should have destroyed the DOM', () => { 29 | 30 | jsdomify.destroy(); 31 | let createElement = () => { 32 | document.createElement("P") 33 | }; 34 | 35 | expect(createElement, 'to throw exception'); 36 | }); 37 | 38 | it('should keep the DOM string after clear()', () => { 39 | 40 | jsdomify.create('

par1

par2

'); 41 | 42 | let par = document.createElement("P"); 43 | let text = document.createTextNode("par3"); 44 | par.appendChild(text); 45 | document.body.appendChild(par); 46 | 47 | let parCount = document.getElementsByTagName("P"); 48 | expect(parCount.length, 'to be', 3); 49 | 50 | jsdomify.clear(); 51 | parCount = document.getElementsByTagName("P"); 52 | expect(parCount.length, 'to be', 2); 53 | }); 54 | 55 | it('should get a reference to the document when using .getDocument', () => { 56 | jsdomify.create('
'); 57 | var docReference = jsdomify.getDocument(); 58 | 59 | expect(docReference.getElementById('test-document'), 'to be defined'); 60 | }); 61 | 62 | it('should throw if trying to access the document after .destroy', () => { 63 | jsdomify.create('
'); 64 | jsdomify.destroy(); 65 | 66 | expect(jsdomify.getDocument, 'to throw'); 67 | }); 68 | 69 | }); 70 | 71 | 72 | describe('Isolation test', () => { 73 | 74 | before(() => { 75 | jsdomify.create(); 76 | }); 77 | 78 | beforeEach(() => { 79 | jsdomify.clear(); 80 | }); 81 | 82 | after(() => { 83 | jsdomify.destroy(); 84 | }); 85 | 86 | it('should append a child to the body', () => { 87 | 88 | let par = document.createElement("P"); 89 | let text = document.createTextNode("some text"); 90 | par.appendChild(text); 91 | document.body.appendChild(par); 92 | let parCount = document.getElementsByTagName("P"); 93 | 94 | expect(document.body.innerHTML, 'not to be empty'); 95 | expect(parCount.length, 'to be', 1); 96 | }); 97 | 98 | it('should not find the previously appended child', () => { 99 | 100 | let parCount = document.getElementsByTagName("P"); 101 | 102 | expect(document.body.innerHTML, 'to be empty'); 103 | expect(parCount.length, 'to be', 0); 104 | }); 105 | 106 | }); 107 | 108 | 109 | }); -------------------------------------------------------------------------------- /src/polyfills/classList.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Source: https://github.com/remy/polyfills 3 | * 4 | * Copyright (c) 2010 Remy Sharp, http://remysharp.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | */ 26 | module.exports = function(window) { 27 | 28 | if (typeof window.Element === "undefined" || "classList" in window.document.documentElement) return; 29 | 30 | var prototype = Array.prototype, 31 | push = prototype.push, 32 | splice = prototype.splice, 33 | join = prototype.join; 34 | 35 | function DOMTokenList(el) { 36 | this.el = el; 37 | // The className needs to be trimmed and split on whitespace 38 | // to retrieve a list of classes. 39 | var classes = el.className.replace(/^\s+|\s+$/g, '').split(/\s+/); 40 | for (var i = 0; i < classes.length; i++) { 41 | push.call(this, classes[i]); 42 | } 43 | }; 44 | 45 | DOMTokenList.prototype = { 46 | add: function(token) { 47 | if (this.contains(token)) return; 48 | push.call(this, token); 49 | this.el.className = this.toString(); 50 | }, 51 | contains: function(token) { 52 | return this.el.className.indexOf(token) != -1; 53 | }, 54 | item: function(index) { 55 | return this[index] || null; 56 | }, 57 | remove: function(token) { 58 | if (!this.contains(token)) return; 59 | for (var i = 0; i < this.length; i++) { 60 | if (this[i] == token) break; 61 | } 62 | splice.call(this, i, 1); 63 | this.el.className = this.toString(); 64 | }, 65 | toString: function() { 66 | return join.call(this, ' '); 67 | }, 68 | toggle: function(token) { 69 | if (!this.contains(token)) { 70 | this.add(token); 71 | } else { 72 | this.remove(token); 73 | } 74 | 75 | return this.contains(token); 76 | } 77 | }; 78 | 79 | window.DOMTokenList = DOMTokenList; 80 | 81 | function defineElementGetter(obj, prop, getter) { 82 | if (Object.defineProperty) { 83 | Object.defineProperty(obj, prop, { 84 | get: getter 85 | }); 86 | } else { 87 | obj.__defineGetter__(prop, getter); 88 | } 89 | } 90 | 91 | defineElementGetter(window.Element.prototype, 'classList', function() { 92 | return new DOMTokenList(this); 93 | }); 94 | 95 | }; 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsdomify-compat 2 | [![Build Status](https://travis-ci.org/podio/jsdomify-compat.svg)](https://travis-ci.org/podio/jsdomify-compat) 3 | [![Coverage Status](https://coveralls.io/repos/podio/jsdomify-compat/badge.svg?branch=master&service=github)](https://coveralls.io/github/podio/jsdomify-compat?branch=master) 4 | 5 | A ready to use DOM instance right at your finger tips for easy and fast testing without any browser in node.js environment 6 | (with [mocha](http://mochajs.org/), for example) 7 | 8 | This is the compatibility version of [jsdomify](https://github.com/podio/jsdomify) which can run in node.js version 9 | 0.10 and 0.12, using JSDOM version 3.1.2 10 | 11 | ## Getting started 12 | 13 | ``` 14 | npm install --save-dev jsdomify-compat 15 | ``` 16 | 17 | ## Usage 18 | 19 | You can create a new jsdom instance simply with 20 | 21 | ```javascript 22 | var jsdomify = require('jsdomify-compat').create(); 23 | ``` 24 | 25 | Or you can provide a valid HTML string that will be used as your DOM 26 | 27 | ```javascript 28 | var jsdomify = require('jsdomify').create( 29 | 'hello' 30 | ); 31 | ``` 32 | 33 | ## Methods 34 | 35 | `jsdomify` expose some useful methods that can be used to control the DOM instance 36 | 37 | ### create(domString) 38 | 39 | ```javascript 40 | jsdomify.create(); 41 | ``` 42 | 43 | Create a new DOM instance (with or without the optional DOM string). 44 | 45 | ### clear() 46 | 47 | ```javascript 48 | jsdomify.clear(); 49 | ``` 50 | 51 | Clear the current instance and recreate a new one using the same DOM string (basically clearing up the DOM). 52 | 53 | ### destroy() 54 | 55 | ```javascript 56 | jsdomify.destroy([clearRequireCache]); 57 | ``` 58 | 59 | Close the window and destroy the document. 60 | Can be used to isolate the tests and prevent leaking from one test suite to another. 61 | 62 | If `clearRequireCache === true` all the cached node require modules will be purged (defaults to `true`). 63 | This is needed in order to use ReactJS with MochaJS. 64 | 65 | Related issues: 66 | * [React](https://github.com/facebook/react/issues/4025 "React issue 4025") 67 | * [Mocha](https://github.com/mochajs/mocha/issues/1722 "Mocha issue 1722") 68 | 69 | 70 | ### getDocument() 71 | 72 | ```javascript 73 | var documentRef = jsdomify.getDocument(); 74 | var elm = documentRef.getElementById('whatever'); 75 | ``` 76 | 77 | Get a reference to the document that has been created as a `global`. 78 | Useful when running with strict linting that doesn't allow globals but still want to test things on the document itself. 79 | 80 | ## Usage examples 81 | 82 | From our very own test suite 83 | 84 | ```javascript 85 | describe('Isolation test', function() { 86 | 87 | before(function() { 88 | jsdomify.create(); 89 | }); 90 | 91 | beforeEach(function() { 92 | jsdomify.clear(); 93 | }); 94 | 95 | after(function() { 96 | jsdomify.destroy(); 97 | }); 98 | 99 | it('should append a child to the body', function() { 100 | 101 | var par = document.createElement("P"); 102 | var text = document.createTextNode("some text"); 103 | par.appendChild(text); 104 | document.body.appendChild(par); 105 | var parCount = document.getElementsByTagName("P"); 106 | 107 | expect(document.body.innerHTML, 'not to be empty'); 108 | expect(parCount.length, 'to be', 1); 109 | }); 110 | 111 | it('should not find the previously appended child', function() { 112 | 113 | var parCount = document.getElementsByTagName("P"); 114 | 115 | expect(document.body.innerHTML, 'to be empty'); 116 | expect(parCount.length, 'to be', 0); 117 | }); 118 | 119 | }); 120 | ``` 121 | 122 | ## Test 123 | 124 | ``` 125 | npm test 126 | ``` 127 | 128 | ## License 129 | MIT --------------------------------------------------------------------------------