karma.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Load our dependencies
  2. var stringify = require('../common/stringify')
  3. // Define our context Karma constructor
  4. function ContextKarma (callParentKarmaMethod) {
  5. // Define local variables
  6. var hasError = false
  7. var self = this
  8. var isLoaded = false
  9. // Define our loggers
  10. // DEV: These are intentionally repeated in client and context
  11. this.log = function (type, args) {
  12. var values = []
  13. for (var i = 0; i < args.length; i++) {
  14. values.push(this.stringify(args[i], 3))
  15. }
  16. this.info({log: values.join(', '), type: type})
  17. }
  18. this.stringify = stringify
  19. // Define our proxy error handler
  20. // DEV: We require one in our context to track `hasError`
  21. this.error = function () {
  22. hasError = true
  23. callParentKarmaMethod('error', [].slice.call(arguments))
  24. return false
  25. }
  26. // Define our start handler
  27. function UNIMPLEMENTED_START () {
  28. this.error('You need to include some adapter that implements __karma__.start method!')
  29. }
  30. // all files loaded, let's start the execution
  31. this.loaded = function () {
  32. // has error -> cancel
  33. if (!hasError && !isLoaded) {
  34. isLoaded = true
  35. try {
  36. this.start(this.config)
  37. } catch (error) {
  38. this.error(error.stack || error.toString())
  39. }
  40. }
  41. // remove reference to child iframe
  42. this.start = UNIMPLEMENTED_START
  43. }
  44. // supposed to be overriden by the context
  45. // TODO(vojta): support multiple callbacks (queue)
  46. this.start = UNIMPLEMENTED_START
  47. // Define proxy methods
  48. // DEV: This is a closured `for` loop (same as a `forEach`) for IE support
  49. var proxyMethods = ['complete', 'info', 'result']
  50. for (var i = 0; i < proxyMethods.length; i++) {
  51. (function bindProxyMethod (methodName) {
  52. self[methodName] = function boundProxyMethod () {
  53. callParentKarmaMethod(methodName, [].slice.call(arguments))
  54. }
  55. }(proxyMethods[i]))
  56. }
  57. // Define bindings for context window
  58. this.setupContext = function (contextWindow) {
  59. // If we clear the context after every run and we already had an error
  60. // then stop now. Otherwise, carry on.
  61. if (self.config.clearContext && hasError) {
  62. return
  63. }
  64. // Perform window level bindings
  65. // DEV: We return `self.error` since we want to `return false` to ignore errors
  66. contextWindow.onerror = function () {
  67. return self.error.apply(self, arguments)
  68. }
  69. // DEV: We must defined a function since we don't want to pass the event object
  70. contextWindow.onbeforeunload = function (e, b) {
  71. callParentKarmaMethod('onbeforeunload', [])
  72. }
  73. contextWindow.dump = function () {
  74. self.log('dump', arguments)
  75. }
  76. var _confirm = contextWindow.confirm
  77. var _prompt = contextWindow.prompt
  78. contextWindow.alert = function (msg) {
  79. self.log('alert', [msg])
  80. }
  81. contextWindow.confirm = function (msg) {
  82. self.log('confirm', [msg])
  83. return _confirm(msg)
  84. }
  85. contextWindow.prompt = function (msg, defaultVal) {
  86. self.log('prompt', [msg, defaultVal])
  87. return _prompt(msg, defaultVal)
  88. }
  89. // If we want to overload our console, then do it
  90. function getConsole (currentWindow) {
  91. return currentWindow.console || {
  92. log: function () {},
  93. info: function () {},
  94. warn: function () {},
  95. error: function () {},
  96. debug: function () {}
  97. }
  98. }
  99. if (self.config.captureConsole) {
  100. // patch the console
  101. var localConsole = contextWindow.console = getConsole(contextWindow)
  102. var logMethods = ['log', 'info', 'warn', 'error', 'debug']
  103. var patchConsoleMethod = function (method) {
  104. var orig = localConsole[method]
  105. if (!orig) {
  106. return
  107. }
  108. localConsole[method] = function () {
  109. self.log(method, arguments)
  110. try {
  111. return Function.prototype.apply.call(orig, localConsole, arguments)
  112. } catch (error) {
  113. self.log('warn', ['Console method ' + method + ' threw: ' + error])
  114. }
  115. }
  116. }
  117. for (var i = 0; i < logMethods.length; i++) {
  118. patchConsoleMethod(logMethods[i])
  119. }
  120. }
  121. }
  122. }
  123. // Define call/proxy methods
  124. ContextKarma.getDirectCallParentKarmaMethod = function (parentWindow) {
  125. return function directCallParentKarmaMethod (method, args) {
  126. // If the method doesn't exist, then error out
  127. if (!parentWindow.karma[method]) {
  128. parentWindow.karma.error('Expected Karma method "' + method + '" to exist but it doesn\'t')
  129. return
  130. }
  131. // Otherwise, run our method
  132. parentWindow.karma[method].apply(parentWindow.karma, args)
  133. }
  134. }
  135. ContextKarma.getPostMessageCallParentKarmaMethod = function (parentWindow) {
  136. return function postMessageCallParentKarmaMethod (method, args) {
  137. parentWindow.postMessage({__karmaMethod: method, __karmaArguments: args}, window.location.origin)
  138. }
  139. }
  140. // Export our module
  141. module.exports = ContextKarma