siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
159 lines (117 loc) • 5.19 kB
JavaScript
/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
!function () {
const child_process = require('child_process')
const { LogLevel } = require('../src/generic/util/role/CanLog.js')
const { NodeJsPageRpcFunctionCall } = require('../src/nodejs/launcher/NodeJsPageRpc')
const { NodeJsPageParentEndPoint } = require('../src/nodejs/launcher/NodeJsPageRpcParent')
Class('Siesta.Launcher.Page.NodeJS', {
isa : Siesta.Launcher.Page.BasePage,
does : [
],
has : {
nodeJsProcess : null,
channel : null
},
methods : {
openHarness : function (projectUrl, callback) {
// avoid normalization to http url
return this.SUPER(this.dispatcher.options[ 'project-url' ], callback)
},
open : function (url, callback) {
var me = this
var options = me.dispatcher.options
// prepare browser args
var nodeJsArgs = options[ 'node-arg' ] || []
if (!(nodeJsArgs instanceof Array)) nodeJsArgs = [ nodeJsArgs ]
var stdio = options[ 'isolate-console' ] ? 'ignore' : 'inherit'
var inspect = options[ 'inspect' ] || options[ 'inspect-brk' ]
let nodeJsProcess = me.nodeJsProcess = child_process.fork(
process.argv[ 1 ],
[ 'AS_CONNECTOR' ].concat(process.argv.slice(2)),
{
stdio : [ stdio, stdio, stdio, 'ipc' ],
execArgv : nodeJsArgs.concat(
inspect
?
inspect instanceof Boolean ? '--inspect' : '--inspect=' + inspect
:
[]
)
}
)
nodeJsProcess.on('exit', () => {
me.nodeJsProcess = null
})
me.channel = new NodeJsPageParentEndPoint({ logLevel : LogLevel.debug })
me.channel.connect(nodeJsProcess).then(() => {
// in debugging mode, the inspector need some time to "attach" to external debugging UI,
// so we wait 300ms by default
// otherwise, some of the `debugger` breakpoints can be ignored
// https://github.com/nodejs/node/issues/25215
return !inspect ? Promise.resolve() : new Promise(resolve => setTimeout(resolve, options[ 'inspect-delay' ] || 300))
}).then(
() => callback(), e => callback(e)
)
},
gracefullyKillDuring : function (subProcess, timeout) {
return new Promise(resolve => {
subProcess.once('exit', () => {
clearTimeout(handler)
resolve()
})
subProcess.kill('SIGTERM')
var handler = setTimeout(() => {
subProcess.kill('SIGKILL')
setTimeout(resolve, 10)
}, timeout)
})
},
close : function () {
this.channel.disconnect()
var nodeJsProcess = this.nodeJsProcess
// child process has already exit
if (!nodeJsProcess) return this.SUPER()
var me = this
var options = me.dispatcher.options
var inspect = options[ 'inspect' ] || options[ 'inspect-brk' ]
if (inspect) {
// in debugging mode, need to make sure previous nodejs processes have been closed
// otherwise the debugger port will be busy and opening new session will fail
// so we wait for `gracefullyKillDuring` promise to resolve
return Promise.all([
this.SUPER(),
this.gracefullyKillDuring(nodeJsProcess, 100)
])
} else {
nodeJsProcess.kill('SIGTERM')
// in normal mode, we just initiate the page close and return immediately, w/o waiting for close to complete
return this.SUPER()
}
},
executeSmallScriptPromised : function (text, ignoreException) {
var me = this
return this.channel.sendRpcCall(
new NodeJsPageRpcFunctionCall({ func : '(function() {' + text + '})' }).toJson()
).then(
res => res,
e => {
if (!ignoreException) {
me.print("<Exception from launcher>")
me.print(" While running small script: " + text.substring(0, 150))
me.print(" Exception: " + e)
me.print("</Exception from launcher>")
return Promise.reject(e)
} else
return Promise.resolve(null)
}
)
}
}
// eof methods
})
}();