executor.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. 'use strict'
  2. const log = require('./logger').create()
  3. class Executor {
  4. constructor (capturedBrowsers, config, emitter) {
  5. this.capturedBrowsers = capturedBrowsers
  6. this.config = config
  7. this.emitter = emitter
  8. this.executionScheduled = false
  9. this.errorsScheduled = []
  10. this.pendingCount = 0
  11. this.runningBrowsers = null
  12. this.emitter.on('run_complete', () => this.onRunComplete())
  13. this.emitter.on('browser_complete', () => this.onBrowserComplete())
  14. }
  15. schedule () {
  16. if (this.capturedBrowsers.length === 0) {
  17. log.warn(`No captured browser, open ${this.config.protocol}//${this.config.hostname}:${this.config.port}${this.config.urlRoot}`)
  18. return false
  19. } else if (this.capturedBrowsers.areAllReady()) {
  20. log.debug('All browsers are ready, executing')
  21. log.debug(`Captured ${this.capturedBrowsers.length} browsers`)
  22. this.executionScheduled = false
  23. this.capturedBrowsers.clearResults()
  24. this.pendingCount = this.capturedBrowsers.length
  25. this.runningBrowsers = this.capturedBrowsers.clone()
  26. this.emitter.emit('run_start', this.runningBrowsers)
  27. this.socketIoSockets.emit('execute', this.config.client)
  28. return true
  29. } else {
  30. log.info('Delaying execution, these browsers are not ready: ' + this.capturedBrowsers.getNonReady().join(', '))
  31. this.executionScheduled = true
  32. return false
  33. }
  34. }
  35. /**
  36. * Schedule an error to be reported
  37. * @param {string} errorMessage
  38. * @returns {boolean} a boolean indicating whether or not the error was handled synchronously
  39. */
  40. scheduleError (errorMessage) {
  41. // We don't want to interfere with any running test.
  42. // Verify that no test is running before reporting the error.
  43. if (this.capturedBrowsers.areAllReady()) {
  44. log.warn(errorMessage)
  45. const errorResult = {
  46. success: 0,
  47. failed: 0,
  48. skipped: 0,
  49. error: errorMessage,
  50. exitCode: 1
  51. }
  52. const noBrowsersStartedTests = []
  53. this.emitter.emit('run_start', noBrowsersStartedTests) // A run cannot complete without being started
  54. this.emitter.emit('run_complete', noBrowsersStartedTests, errorResult)
  55. return true
  56. } else {
  57. this.errorsScheduled.push(errorMessage)
  58. return false
  59. }
  60. }
  61. onRunComplete () {
  62. if (this.executionScheduled) {
  63. this.schedule()
  64. }
  65. if (this.errorsScheduled.length) {
  66. const errorsToReport = this.errorsScheduled
  67. this.errorsScheduled = []
  68. errorsToReport.forEach((error) => this.scheduleError(error))
  69. }
  70. }
  71. onBrowserComplete () {
  72. this.pendingCount--
  73. if (!this.pendingCount) {
  74. // Ensure run_complete is emitted in the next tick
  75. // so it is never emitted before browser_complete
  76. setTimeout(() => {
  77. this.emitter.emit('run_complete', this.runningBrowsers, this.runningBrowsers.getResults())
  78. })
  79. }
  80. }
  81. }
  82. Executor.factory = function (capturedBrowsers, config, emitter) {
  83. return new Executor(capturedBrowsers, config, emitter)
  84. }
  85. module.exports = Executor