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 overridden 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. contextWindow.onbeforeunload = function () {
  70. return self.error('Some of your tests did a full page reload!')
  71. }
  72. contextWindow.dump = function () {
  73. self.log('dump', arguments)
  74. }
  75. var _confirm = contextWindow.confirm
  76. var _prompt = contextWindow.prompt
  77. contextWindow.alert = function (msg) {
  78. self.log('alert', [msg])
  79. }
  80. contextWindow.confirm = function (msg) {
  81. self.log('confirm', [msg])
  82. return _confirm(msg)
  83. }
  84. contextWindow.prompt = function (msg, defaultVal) {
  85. self.log('prompt', [msg, defaultVal])
  86. return _prompt(msg, defaultVal)
  87. }
  88. // If we want to overload our console, then do it
  89. function getConsole (currentWindow) {
  90. return currentWindow.console || {
  91. log: function () {},
  92. info: function () {},
  93. warn: function () {},
  94. error: function () {},
  95. debug: function () {}
  96. }
  97. }
  98. if (self.config.captureConsole) {
  99. // patch the console
  100. var localConsole = contextWindow.console = getConsole(contextWindow)
  101. var logMethods = ['log', 'info', 'warn', 'error', 'debug']
  102. var patchConsoleMethod = function (method) {
  103. var orig = localConsole[method]
  104. if (!orig) {
  105. return
  106. }
  107. localConsole[method] = function () {
  108. self.log(method, arguments)
  109. try {
  110. return Function.prototype.apply.call(orig, localConsole, arguments)
  111. } catch (error) {
  112. self.log('warn', ['Console method ' + method + ' threw: ' + error])
  113. }
  114. }
  115. }
  116. for (var i = 0; i < logMethods.length; i++) {
  117. patchConsoleMethod(logMethods[i])
  118. }
  119. }
  120. }
  121. }
  122. // Define call/proxy methods
  123. ContextKarma.getDirectCallParentKarmaMethod = function (parentWindow) {
  124. return function directCallParentKarmaMethod (method, args) {
  125. // If the method doesn't exist, then error out
  126. if (!parentWindow.karma[method]) {
  127. parentWindow.karma.error('Expected Karma method "' + method + '" to exist but it doesn\'t')
  128. return
  129. }
  130. // Otherwise, run our method
  131. parentWindow.karma[method].apply(parentWindow.karma, args)
  132. }
  133. }
  134. ContextKarma.getPostMessageCallParentKarmaMethod = function (parentWindow) {
  135. return function postMessageCallParentKarmaMethod (method, args) {
  136. parentWindow.postMessage({ __karmaMethod: method, __karmaArguments: args }, window.location.origin)
  137. }
  138. }
  139. // Export our module
  140. module.exports = ContextKarma