diff --git a/package.json b/package.json index 6de8e63..c21c4c0 100644 --- a/package.json +++ b/package.json @@ -33,17 +33,20 @@ "watch-glob": "0.1.3" }, "devDependencies": { - "describe-with-dom": "^1.0.0", "expect": "^1.14.0", + "jsdom": "^8.1.0", + "jsdom-global": "^1.7.0", "mocha": "^2.4.5", "npm-run-all": "^1.5.2", "react": "^0.14.7", "react-dom": "^0.14.7", - "semistandard": "^7.0.5" + "semistandard": "^7.0.5", + "temp": "^0.8.3" }, "semistandard": { "ignore": [ "test/fixtures/**", + "test/views/**", "test-integration/views/**" ] } diff --git a/src/transforms/custom-require-visitor.js b/src/transforms/custom-require-visitor.js index ea34625..5a4c04b 100644 --- a/src/transforms/custom-require-visitor.js +++ b/src/transforms/custom-require-visitor.js @@ -1,10 +1,10 @@ var utils = require('jstransform/src/utils'); var globalUtils = require('./utils'); -var requireRegister = require.resolve('../proxies').replace(/\\/g, '/'); +var proxiesPath = require.resolve('../proxies').replace(/\\/g, '/'); function requireVisitor (traverse, node, path, state) { if (!state.g.alreadyAddedElectronHotRequire) { - utils.append("var __electronHot__ = require('" + requireRegister + "');\n", state); + utils.append("var __electronHot__ = require('" + proxiesPath + "');\n", state); state.g.alreadyAddedElectronHotRequire = true; } diff --git a/test-integration/hot-reload.spec.js b/test-integration/hot-reload.spec.js new file mode 100644 index 0000000..35d7818 --- /dev/null +++ b/test-integration/hot-reload.spec.js @@ -0,0 +1,96 @@ +/* global describe,it,before,after */ +'use strict'; + +const expect = require('expect'); +var jsdom = require('jsdom-global'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const temp = require('temp').track(); + +let rootDir; +let cleanup; + +describe('loadJsx', function () { + this.timeout(10000); + + before(() => { + cleanup = jsdom(); + const electronHot = require('../src/index'); + electronHot.install(); + + rootDir = temp.mkdirSync('electron-hot'); + createTempJsx('./views/index.jsx'); + createTempJsx('./views/App.jsx'); + + // Strange bug on OSX + const cwd = os.platform() === 'darwin' ? '/private' + rootDir : rootDir; + electronHot.watchJsx(['**/*.jsx'], {cwd: cwd, delay: 0}); + }); + + it('should hot reload JSX files', (done) => { + const indexJsx = path.join(rootDir, 'index.jsx'); + const appJsx = path.join(rootDir, 'App.jsx'); + + require(indexJsx); + + // our html contains Hello + expect(getHtmlBody()).toContain('Hello'); + + doLater(() => changeFile(appJsx, content => content.replace('Hello', 'Hi'))) + .then(() => waitFor(() => getHtmlBody().match('Hi'))) + .then(done); + }); + + after(() => { + cleanup(); + delete require.extensions['.jsx']; + temp.cleanupSync(); + }); +}); + +function waitFor (condition) { + return new Promise((resolve) => { + setTimeout(() => { + const interval = setInterval(() => { + if (condition()) { + clearInterval(interval); + resolve(); + } + }, 100); + }, 0); + }); +} + +function doLater (thing) { + return new Promise((resolve) => { + setTimeout(() => { + thing(); + resolve(); + }, 500); + }); +} + +function changeFile (filePath, transform) { + let content = fs.readFileSync(filePath).toString(); + content = transform(content); + fs.writeFileSync(filePath, content); +} + +function getHtmlBody () { + return document.defaultView.document.documentElement.outerHTML; +} + +function createTempJsx (source) { + let content = fs.readFileSync(require.resolve(source)).toString(); + + const reactPath = require.resolve('react').replace(/\\/g, '/'); + content = content.replace("require('react')", `require('${reactPath}')`); + + const reactDOMPath = require.resolve('react-dom').replace(/\\/g, '/'); + content = content.replace("require('react-dom')", `require('${reactDOMPath}')`); + + const sourceFileName = path.basename(source); + fs.writeFileSync(path.join(rootDir, sourceFileName), content); +} + diff --git a/test-integration/mocha.opts b/test-integration/mocha.opts index 8dfda54..178dfe3 100644 --- a/test-integration/mocha.opts +++ b/test-integration/mocha.opts @@ -1,5 +1,4 @@ -u bdd ---recursive ./test-integration/**/*spec.jsx +--recursive ./test-integration/**/*spec.js --full-trace --reporter spec ---require ./compiler \ No newline at end of file diff --git a/test-integration/views/index.jsx b/test-integration/views/index.jsx new file mode 100644 index 0000000..04c360e --- /dev/null +++ b/test-integration/views/index.jsx @@ -0,0 +1,9 @@ +const React = require('react'); +const ReactDOM = require('react-dom'); + +console.debug = () => {}; + +const App = require('./App.jsx'); +const element = document.createElement('div'); +document.body.appendChild(element); +ReactDOM.render(, element); diff --git a/test-integration/load.spec.jsx b/test/spec/error.spec.js similarity index 59% rename from test-integration/load.spec.jsx rename to test/spec/error.spec.js index 22f9bbe..0e9f199 100644 --- a/test-integration/load.spec.jsx +++ b/test/spec/error.spec.js @@ -1,34 +1,29 @@ -/* global it */ +/* global it,describe,before,after */ 'use strict'; const expect = require('expect'); -const describeWithDom = require('describe-with-dom'); -const React = require('react'); -const ReactDOM = require('react-dom'); - -describeWithDom('loadJsx', () => { - it('should load a JSX file', () => { - const App = require('./views/App.jsx'); - const element = document.createElement('div'); - document.body.appendChild(element); - ReactDOM.render(, element); - - var window = document.defaultView; - expect(window.document.documentElement.outerHTML).toContain('Hello'); +describe('loadJsx', () => { + before(() => { + const electronHot = require('../../src/index'); + electronHot.install(); }); it('should throw when a component contains an error', () => { - const exceptionMessage = getExceptionMessage(() => require('./views/ErrorComponent.jsx')); + const exceptionMessage = getExceptionMessage(() => require('./../views/ErrorComponent.jsx')); expect(exceptionMessage) .toMatch(/^Error compiling [\w\/\-\\:]+?ErrorComponent\.jsx: Parse Error: Line 10: Unexpected token }/); }); it('should throw a simple error when a component contains a component which contains an error', () => { - const exceptionMessage = getExceptionMessage(() => require('./views/AppUsingErrorComponent.jsx')); + const exceptionMessage = getExceptionMessage(() => require('./../views/AppUsingErrorComponent.jsx')); expect(exceptionMessage) .toMatch(/^Error compiling [\w\/\-\\:]+?ErrorComponent\.jsx: Parse Error: Line 10: Unexpected token }/); }); + + after(() => { + delete require.extensions['.jsx']; + }); }); function getExceptionMessage (executor) { diff --git a/test-integration/views/AppUsingErrorComponent.jsx b/test/views/AppUsingErrorComponent.jsx similarity index 100% rename from test-integration/views/AppUsingErrorComponent.jsx rename to test/views/AppUsingErrorComponent.jsx diff --git a/test-integration/views/ErrorComponent.jsx b/test/views/ErrorComponent.jsx similarity index 100% rename from test-integration/views/ErrorComponent.jsx rename to test/views/ErrorComponent.jsx