karma.js 4.6 KB

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