base.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. const KarmaEventEmitter = require('../events').EventEmitter
  2. const EventEmitter = require('events').EventEmitter
  3. const Promise = require('bluebird')
  4. const log = require('../logger').create('launcher')
  5. const helper = require('../helper')
  6. const BEING_CAPTURED = 'BEING_CAPTURED'
  7. const CAPTURED = 'CAPTURED'
  8. const BEING_KILLED = 'BEING_KILLED'
  9. const FINISHED = 'FINISHED'
  10. const RESTARTING = 'RESTARTING'
  11. const BEING_FORCE_KILLED = 'BEING_FORCE_KILLED'
  12. /**
  13. * Base launcher that any custom launcher extends.
  14. */
  15. function BaseLauncher (id, emitter) {
  16. if (this.start) {
  17. return
  18. }
  19. // TODO(vojta): figure out how to do inheritance with DI
  20. Object.keys(EventEmitter.prototype).forEach(function (method) {
  21. this[method] = EventEmitter.prototype[method]
  22. }, this)
  23. this.bind = KarmaEventEmitter.prototype.bind.bind(this)
  24. this.emitAsync = KarmaEventEmitter.prototype.emitAsync.bind(this)
  25. this.id = id
  26. this._state = null
  27. Object.defineProperty(this, 'state', {
  28. get: () => {
  29. return this._state
  30. },
  31. set: (toState) => {
  32. log.debug(`${this._state} -> ${toState}`)
  33. this._state = toState
  34. }
  35. })
  36. this.error = null
  37. let killingPromise
  38. let previousUrl
  39. this.start = function (url) {
  40. previousUrl = url
  41. this.error = null
  42. this.state = BEING_CAPTURED
  43. this.emit('start', url + '?id=' + this.id + (helper.isDefined(this.displayName) ? '&displayName=' + encodeURIComponent(this.displayName) : ''))
  44. }
  45. this.kill = function () {
  46. // Already killed, or being killed.
  47. if (killingPromise) {
  48. return killingPromise
  49. }
  50. killingPromise = this.emitAsync('kill').then(() => {
  51. this.state = FINISHED
  52. })
  53. this.state = BEING_KILLED
  54. return killingPromise
  55. }
  56. this.forceKill = function () {
  57. this.kill()
  58. this.state = BEING_FORCE_KILLED
  59. return killingPromise
  60. }
  61. this.restart = function () {
  62. if (this.state === BEING_FORCE_KILLED) {
  63. return
  64. }
  65. if (!killingPromise) {
  66. killingPromise = this.emitAsync('kill')
  67. }
  68. killingPromise.then(() => {
  69. if (this.state === BEING_FORCE_KILLED) {
  70. this.state = FINISHED
  71. } else {
  72. killingPromise = null
  73. log.debug(`Restarting ${this.name}`)
  74. this.start(previousUrl)
  75. }
  76. })
  77. this.state = RESTARTING
  78. }
  79. this.markCaptured = function () {
  80. if (this.state === BEING_CAPTURED) {
  81. this.state = CAPTURED
  82. }
  83. }
  84. this.isCaptured = function () {
  85. return this.state === CAPTURED
  86. }
  87. this.toString = function () {
  88. return this.name
  89. }
  90. this._done = function (error) {
  91. killingPromise = killingPromise || Promise.resolve()
  92. this.error = this.error || error
  93. this.emit('done')
  94. if (this.error && this.state !== BEING_FORCE_KILLED && this.state !== RESTARTING) {
  95. emitter.emit('browser_process_failure', this)
  96. }
  97. this.state = FINISHED
  98. }
  99. this.STATE_BEING_CAPTURED = BEING_CAPTURED
  100. this.STATE_CAPTURED = CAPTURED
  101. this.STATE_BEING_KILLED = BEING_KILLED
  102. this.STATE_FINISHED = FINISHED
  103. this.STATE_RESTARTING = RESTARTING
  104. this.STATE_BEING_FORCE_KILLED = BEING_FORCE_KILLED
  105. }
  106. BaseLauncher.decoratorFactory = function (id, emitter) {
  107. return function (launcher) {
  108. BaseLauncher.call(launcher, id, emitter)
  109. }
  110. }
  111. module.exports = BaseLauncher