base.js 3.2 KB

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