siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
302 lines (219 loc) • 10.6 kB
JavaScript
/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
Class('Siesta.Launcher.Page.BasePage', {
does : [
JooseX.Observable,
Siesta.Util.Role.CanParseBrowser,
Siesta.Util.Role.HasUniqueGeneratedId,
Siesta.Launcher.Role.CanPrintWithLauncher,
Siesta.Launcher.Script.CanExecute
],
has : {
runner : { required : true },
launcher : { required : true },
dispatcher : { required : true },
pollInterval : 1000,
pollTimeout : null,
width : 1280,
height : 1024,
getStateExceptionCounter : 0,
activityTimeout : null,
// the timestamp of the last change of the "lastActivityToken" property
lastActivity : null,
// the timestamp of the last activity inside of test, the timestamp does not neccessary match the clock
// inside Rhino, so we track its updates
lastActivityToken : null,
isClosed : false,
sessionId : { is : 'rw', init : null },
chunkTask : null
},
methods : {
getOptions : function () {
return this.dispatcher.options
},
open : function (url, callback) {
throw new Error("Abstract method call: `open`")
},
close : function () {
this.debug("Page close started: " + this.id)
clearTimeout(this.pollTimeout)
this.isClosed = true
this.fireEvent('close')
if (this.wsServer)
return this.wsServer.stop()
else
return Promise.resolve()
},
// this method should wait until the `Harness.start` method is called
// (which assigns the Siesta.my.activeHarness value)
// this can happen with delay if project starts with the `startFromUrl`
getConfigInfo : function (inc, exc, filter, callback, i) {
i = i || 0
var me = this
if (i > 10)
callback('Timeout while waiting for `project.start()` method')
else
this.executeHarnessMethod('getConfigInfo', [ inc, exc, filter, this.dispatcher.projectConfig ]).then(function (result) {
if (result == 'not_started')
// repeat after 1.5s
setTimeout(function () {
me.getConfigInfo(inc, exc, filter, callback, i++)
}, 1500)
else
callback(null, result)
}, function (e) {
callback(e)
})
},
executeHarnessMethod : function (methodName, args) {
var func = function (args) {
var me = Siesta.my.activeHarness
return me ? me[ METHOD_NAME ].apply(me, args) : 'not_started'
}
args = JSON.stringify(args || [])
var start = new Date()
var options = this.dispatcher.options
var me = this
this.debug('PageId: ' + this.id + ', method : ' + methodName + ' starting, args size: ' + args.length)
return this.executeScript(
'return (' + func.toString().replace(/METHOD_NAME/, "'" + methodName + "'") + ')(' + args + ')'
).then(function (result) {
var message = 'PageId: ' + me.id + ', method : ' + methodName + ', took ' + (new Date() - start) / 1000 + 's'
if (Number(options.debug) > 1)
message += ', result : ' + JSON.stringify(result)
else
if (options.debug)
message += ', result size : ' + JSON.stringify(result).length
me.debug(message)
return result
})
},
pageShouldNotBeUsedAfterException : function (e) {
return false
},
doPoll : function (callback) {
var me = this
this.executeHarnessMethod('getAutomationState', []).then(function (state) {
if (me.shouldStopScriptExecution()) return
me.getStateExceptionCounter = 0
// `state.activityTimeout` will be `null` until the start of the 1st test
// allow 3 min for that
me.activityTimeout = state.activityTimeout || 180000
var newActivityToken = state.lastActivity
// state has not changed
if (me.lastActivityToken && newActivityToken == me.lastActivityToken) {
if (new Date() - me.lastActivity > me.activityTimeout) {
me.dispatcher.onPageInactivityTimeout(state.activeTestAutomationId, me.activityTimeout)
callback('inactivity_timeout', state.notLaunched)
return
}
} else {
// state has changed (or no previous state)
me.lastActivityToken = newActivityToken
me.lastActivity = new Date()
}
me.dispatcher.consumeTestStateUpdate(state, me).then(function (res) {
if (res == 'all_done' || res == 'force_exit') {
callback(null)
} else if (res == 'focus_lost') {
callback(null, state.notLaunched)
} else {
me.pollTimeout = setTimeout(function () { me.doPoll(callback) }, me.pollInterval)
}
})
}, function (e) {
if (me.shouldStopScriptExecution()) return
me.getStateExceptionCounter++
me.debug("Exception #" + me.getStateExceptionCounter + " during `doPoll`, pageId=" + me.id + ": " + e)
// allow up to 2 exceptions from the `getAutomationState` method before really fail
// (weirdly happens sometimes, guess in which browser)
if (me.getStateExceptionCounter >= 2 || me.pageShouldNotBeUsedAfterException(e))
callback(e)
else
me.pollTimeout = setTimeout(function () { me.doPoll(callback) }, me.pollInterval)
})
},
runChunk : function (chunkTask, callback) {
var me = this
this.chunkTask = chunkTask
var dispatcher = this.dispatcher
var options = this.getOptions()
var coverage = options.coverage
var params = {
pause : options.pause,
streamAssertions : dispatcher.streamAssertions,
showCursor : Boolean(options[ 'show-cursor' ]),
restartOnBlur : Boolean(options[ 'restart-on-blur' ]),
projectConfig : Joose.O.copy(dispatcher.projectConfig)
}
var wsServer = this.wsServer || dispatcher.wsServer
if (wsServer) {
params.projectConfig.simulatorPort = wsServer.port
}
// SauceLabs testing
// if (wsServer) {
// // params.projectConfig.simulatorPort = wsServer.port
// params.projectConfig.simulatorConnectionTimeout = 1500000
// }
if (coverage) {
Joose.O.extend(params, {
enableCodeCoverage : true,
manuallyProcessCoverageResults : this.launcher.manuallyProcessCoverageResults
})
}
var onChunkProcessed = function (e, notLaunchedById) {
me.runner.onPageCompleted(me, e, notLaunchedById).then(function () {
callback(e, notLaunchedById)
})
}
this.executeHarnessMethod('launchAutomatedTests', [ chunkTask, params ]).then(function (result) {
me.debug("Page polling has started")
me.pollTimeout = setTimeout(function () { me.doPoll(onChunkProcessed) }, me.pollInterval)
}, function (e) {
me.debug("Page polling failed to start")
onChunkProcessed(e)
})
},
openHarness : function (projectUrl, callback) {
var me = this
this.open(projectUrl, function (e) {
if (e) { callback(e); return }
var errorCodes = {
'no-siesta' : "Can't find Siesta on the project page - page loading failed?",
'no-automation' : "The project page you are targeting contains Siesta Lite distribution. To use automation facilities, \nmake sure project page uses `siesta-all.js` from Standard or Trial packages"
}
var sanityChecker = function () {
if (typeof Siesta == 'undefined') return 'no-siesta'
try {
if (typeof Siesta.Project.Browser.Automation == 'undefined') return 'no-automation'
} catch (e) {
return 'no-automation'
}
return 'success'
}
me.executeSmallScriptPromised(
'return (' + sanityChecker.toString() + ')()',
true
).then(function (result) {
// IE can just throw exception when trying execute script on 404 page, result will be null/undefined in this case
if (result != 'success') {
var error = errorCodes[ result ]
if (error) me.printError(error)
callback(5)
return
}
callback()
})
})
},
// promised method
executeSmallScriptPromised : function (text, ignoreException) {
throw new Error("Abstract method call: `executeSmallScriptPromised`")
}
}
// eof methods
})