forked from DevExpress/testcafe
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d9607ab
commit fa5dee2
Showing
28 changed files
with
1,336 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import path from 'path'; | ||
import Module from 'module'; | ||
import Bootstrapper from '../runner/bootstrapper'; | ||
import Compiler from '../compiler'; | ||
|
||
const originalRequire = Module.prototype.require; | ||
|
||
class LiveModeBootstrapper extends Bootstrapper { | ||
constructor (runner, browserConnectionGateway) { | ||
super(browserConnectionGateway); | ||
|
||
this.runner = runner; | ||
} | ||
|
||
_getTests () { | ||
this._mockRequire(); | ||
|
||
return super._getTests() | ||
.then(result => { | ||
this._restoreRequire(); | ||
|
||
return result; | ||
}) | ||
.catch(err => { | ||
this._restoreRequire(); | ||
|
||
Compiler.cleanUp(); | ||
|
||
this.runner.setBootstrappingError(err); | ||
}); | ||
} | ||
|
||
_mockRequire () { | ||
const runner = this.runner; | ||
|
||
// NODE: we replace the `require` method to add required files to watcher | ||
Module.prototype.require = function (filePath) { | ||
const filename = Module._resolveFilename(filePath, this, false); | ||
|
||
if (path.isAbsolute(filename) || /^\.\.?[/\\]/.test(filename)) | ||
runner.emit(runner.REQUIRED_MODULE_FOUND_EVENT, { filename }); | ||
|
||
|
||
return originalRequire.apply(this, arguments); | ||
}; | ||
} | ||
|
||
_restoreRequire () { | ||
Module.prototype.require = originalRequire; | ||
} | ||
} | ||
|
||
export default LiveModeBootstrapper; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import EventEmitter from 'events'; | ||
import FileWatcher from './file-watcher'; | ||
import Logger from './logger'; | ||
import process from 'process'; | ||
import keypress from 'keypress'; | ||
import Promise from 'pinkie'; | ||
|
||
const REQUIRED_MODULE_FOUND_EVENT = 'require-module-found'; | ||
const LOCK_KEY_PRESS_TIMEOUT = 1000; | ||
|
||
class LiveModeController extends EventEmitter { | ||
constructor (runner) { | ||
super(); | ||
|
||
this.src = null; | ||
this.running = false; | ||
this.restarting = false; | ||
this.watchingPaused = false; | ||
this.stopping = false; | ||
this.logger = new Logger(); | ||
this.runner = runner; | ||
this.lockKeyPress = false; | ||
} | ||
|
||
init (files) { | ||
this._prepareProcessStdin(); | ||
this._listenKeyPress(); | ||
this._initFileWatching(files); | ||
this._listenTestRunnerEvents(); | ||
this._setRunning(); | ||
|
||
return Promise.resolve() | ||
.then(() => this.logger.writeIntroMessage(files)); | ||
} | ||
|
||
_toggleWatching () { | ||
this.watchingPaused = !this.watchingPaused; | ||
|
||
this.logger.writeToggleWatchingMessage(!this.watchingPaused); | ||
} | ||
|
||
_stop () { | ||
if (!this.runner || !this.running) { | ||
this.logger.writeNothingToStopMessage(); | ||
|
||
return Promise.resolve(); | ||
} | ||
|
||
this.logger.writeStopRunningMessage(); | ||
|
||
return this.runner.stop() | ||
.then(() => { | ||
this.restarting = false; | ||
this.running = false; | ||
}); | ||
} | ||
|
||
_restart () { | ||
if (this.restarting || this.watchingPaused) | ||
return Promise.resolve(); | ||
|
||
this.restarting = true; | ||
|
||
if (this.running) { | ||
return this._stop() | ||
.then(() => this.logger.writeTestsFinishedMessage()) | ||
.then(() => this._runTests()); | ||
} | ||
|
||
return this._runTests(); | ||
} | ||
|
||
_exit () { | ||
if (this.stopping) | ||
return Promise.resolve(); | ||
|
||
this.logger.writeExitMessage(); | ||
|
||
this.stopping = true; | ||
|
||
return this.runner ? this.runner.exit() : Promise.resolve(); | ||
} | ||
|
||
_createFileWatcher (src) { | ||
return new FileWatcher(src); | ||
} | ||
|
||
_prepareProcessStdin () { | ||
if (process.stdout.isTTY) | ||
process.stdin.setRawMode(true); | ||
} | ||
|
||
_listenKeyPress () { | ||
// Listen commands | ||
keypress(process.stdin); | ||
|
||
process.stdin.on('keypress', (ch, key) => { | ||
if (this.lockKeyPress) | ||
return null; | ||
|
||
this.lockKeyPress = true; | ||
|
||
setTimeout(() => { | ||
this.lockKeyPress = false; | ||
}, LOCK_KEY_PRESS_TIMEOUT); | ||
|
||
if (key && key.ctrl) { | ||
switch (key.name) { | ||
case 's': | ||
return this._stop(); | ||
case 'r': | ||
return this._restart(); | ||
case 'c': | ||
return this._exit(); | ||
case 'w': | ||
return this._toggleWatching(); | ||
} | ||
} | ||
|
||
return null; | ||
}); | ||
} | ||
|
||
_listenTestRunnerEvents () { | ||
this.runner.on(this.runner.TEST_RUN_DONE_EVENT, e => { | ||
this.running = false; | ||
|
||
if (!this.restarting) | ||
this.logger.writeTestsFinishedMessage(); | ||
|
||
if (e.err) | ||
this.logger.err(`ERROR: ${e.err}`); | ||
}); | ||
|
||
this.runner.on(this.runner.REQUIRED_MODULE_FOUND_EVENT, e => { | ||
this.emit(REQUIRED_MODULE_FOUND_EVENT, e); | ||
}); | ||
} | ||
|
||
_initFileWatching (src) { | ||
const fileWatcher = this._createFileWatcher(src); | ||
|
||
this.on(REQUIRED_MODULE_FOUND_EVENT, e => fileWatcher.addFile(e.filename)); | ||
|
||
fileWatcher.on(fileWatcher.FILE_CHANGED_EVENT, () => this._runTests(true)); | ||
} | ||
|
||
_setRunning () { | ||
this.running = true; | ||
this.restarting = false; | ||
} | ||
|
||
_runTests (sourceChanged) { | ||
if (this.watchingPaused || this.running) | ||
return Promise.resolve(); | ||
|
||
this._setRunning(); | ||
|
||
this.logger.writeRunTestsMessage(sourceChanged); | ||
|
||
return this.runner.runTests(); | ||
} | ||
} | ||
|
||
export default LiveModeController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import EventEmitter from 'events'; | ||
import fs from 'fs'; | ||
import ModulesGraph from './modules-graph'; | ||
|
||
const WATCH_LOCKED_TIMEOUT = 700; | ||
|
||
export default class FileWatcher extends EventEmitter { | ||
constructor (files) { | ||
super(); | ||
|
||
this.FILE_CHANGED_EVENT = 'file-changed'; | ||
|
||
this.watchers = {}; | ||
this.lockedFiles = {}; | ||
this.modulesGraph = null; | ||
this.lastChangedFiles = []; | ||
|
||
files.forEach(f => this.addFile(f)); | ||
} | ||
|
||
_onChanged (file) { | ||
const cache = require.cache; | ||
|
||
if (!this.modulesGraph) { | ||
this.modulesGraph = new ModulesGraph(); | ||
this.modulesGraph.build(cache, Object.keys(this.watchers)); | ||
} | ||
else { | ||
this.lastChangedFiles.forEach(changedFile => this.modulesGraph.rebuildNode(cache, changedFile)); | ||
this.lastChangedFiles = []; | ||
} | ||
|
||
this.lastChangedFiles.push(file); | ||
this.modulesGraph.clearParentsCache(cache, file); | ||
|
||
this.emit(this.FILE_CHANGED_EVENT, { file }); | ||
} | ||
|
||
_watch (file) { | ||
if (this.lockedFiles[file]) | ||
return; | ||
|
||
this.lockedFiles[file] = setTimeout(() => { | ||
this._onChanged(file); | ||
|
||
delete this.lockedFiles[file]; | ||
}, WATCH_LOCKED_TIMEOUT); | ||
} | ||
|
||
addFile (file) { | ||
if (!this.watchers[file] && file.indexOf('node_modules') < 0) { | ||
if (this.modulesGraph) { | ||
this.lastChangedFiles.push(file); | ||
this.modulesGraph.addNode(file, require.cache); | ||
} | ||
|
||
this.watchers[file] = fs.watch(file, () => this._watch(file)); | ||
} | ||
} | ||
} |
Oops, something went wrong.