blob: 9e0756e90967785dba1f76e05ff189470c118387 [file] [log] [blame]
// Load our dependencies
var stringify = require('../common/stringify')
// Define our context Karma constructor
function ContextKarma (callParentKarmaMethod) {
// Define local variables
var hasError = false
var self = this
var isLoaded = false
// Define our loggers
// DEV: These are intentionally repeated in client and context
this.log = function (type, args) {
var values = []
for (var i = 0; i < args.length; i++) {
values.push(this.stringify(args[i], 3))
}
this.info({ log: values.join(', '), type: type })
}
this.stringify = stringify
// Define our proxy error handler
// DEV: We require one in our context to track `hasError`
this.error = function () {
hasError = true
callParentKarmaMethod('error', [].slice.call(arguments))
return false
}
// Define our start handler
function UNIMPLEMENTED_START () {
this.error('You need to include some adapter that implements __karma__.start method!')
}
// all files loaded, let's start the execution
this.loaded = function () {
// has error -> cancel
if (!hasError && !isLoaded) {
isLoaded = true
try {
this.start(this.config)
} catch (error) {
this.error(error.stack || error.toString())
}
}
// remove reference to child iframe
this.start = UNIMPLEMENTED_START
}
// supposed to be overriden by the context
// TODO(vojta): support multiple callbacks (queue)
this.start = UNIMPLEMENTED_START
// Define proxy methods
// DEV: This is a closured `for` loop (same as a `forEach`) for IE support
var proxyMethods = ['complete', 'info', 'result']
for (var i = 0; i < proxyMethods.length; i++) {
(function bindProxyMethod (methodName) {
self[methodName] = function boundProxyMethod () {
callParentKarmaMethod(methodName, [].slice.call(arguments))
}
}(proxyMethods[i]))
}
// Define bindings for context window
this.setupContext = function (contextWindow) {
// If we clear the context after every run and we already had an error
// then stop now. Otherwise, carry on.
if (self.config.clearContext && hasError) {
return
}
// Perform window level bindings
// DEV: We return `self.error` since we want to `return false` to ignore errors
contextWindow.onerror = function () {
return self.error.apply(self, arguments)
}
// DEV: We must defined a function since we don't want to pass the event object
contextWindow.onbeforeunload = function (e, b) {
callParentKarmaMethod('onbeforeunload', [])
}
contextWindow.dump = function () {
self.log('dump', arguments)
}
var _confirm = contextWindow.confirm
var _prompt = contextWindow.prompt
contextWindow.alert = function (msg) {
self.log('alert', [msg])
}
contextWindow.confirm = function (msg) {
self.log('confirm', [msg])
return _confirm(msg)
}
contextWindow.prompt = function (msg, defaultVal) {
self.log('prompt', [msg, defaultVal])
return _prompt(msg, defaultVal)
}
// If we want to overload our console, then do it
function getConsole (currentWindow) {
return currentWindow.console || {
log: function () {},
info: function () {},
warn: function () {},
error: function () {},
debug: function () {}
}
}
if (self.config.captureConsole) {
// patch the console
var localConsole = contextWindow.console = getConsole(contextWindow)
var logMethods = ['log', 'info', 'warn', 'error', 'debug']
var patchConsoleMethod = function (method) {
var orig = localConsole[method]
if (!orig) {
return
}
localConsole[method] = function () {
self.log(method, arguments)
try {
return Function.prototype.apply.call(orig, localConsole, arguments)
} catch (error) {
self.log('warn', ['Console method ' + method + ' threw: ' + error])
}
}
}
for (var i = 0; i < logMethods.length; i++) {
patchConsoleMethod(logMethods[i])
}
}
}
}
// Define call/proxy methods
ContextKarma.getDirectCallParentKarmaMethod = function (parentWindow) {
return function directCallParentKarmaMethod (method, args) {
// If the method doesn't exist, then error out
if (!parentWindow.karma[method]) {
parentWindow.karma.error('Expected Karma method "' + method + '" to exist but it doesn\'t')
return
}
// Otherwise, run our method
parentWindow.karma[method].apply(parentWindow.karma, args)
}
}
ContextKarma.getPostMessageCallParentKarmaMethod = function (parentWindow) {
return function postMessageCallParentKarmaMethod (method, args) {
parentWindow.postMessage({ __karmaMethod: method, __karmaArguments: args }, window.location.origin)
}
}
// Export our module
module.exports = ContextKarma