run-status.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. 'use strict';
  2. const cloneDeep = require('lodash.clonedeep');
  3. const Emittery = require('./emittery');
  4. class RunStatus extends Emittery {
  5. constructor(files) {
  6. super();
  7. this.stats = {
  8. byFile: new Map(),
  9. declaredTests: 0,
  10. failedHooks: 0,
  11. failedTests: 0,
  12. failedWorkers: 0,
  13. files,
  14. finishedWorkers: 0,
  15. internalErrors: 0,
  16. remainingTests: 0,
  17. passedKnownFailingTests: 0,
  18. passedTests: 0,
  19. selectedTests: 0,
  20. skippedTests: 0,
  21. timeouts: 0,
  22. todoTests: 0,
  23. uncaughtExceptions: 0,
  24. unhandledRejections: 0
  25. };
  26. }
  27. observeWorker(worker, testFile) {
  28. this.stats.byFile.set(testFile, {
  29. declaredTests: 0,
  30. failedHooks: 0,
  31. failedTests: 0,
  32. internalErrors: 0,
  33. remainingTests: 0,
  34. passedKnownFailingTests: 0,
  35. passedTests: 0,
  36. selectedTests: 0,
  37. skippedTests: 0,
  38. todoTests: 0,
  39. uncaughtExceptions: 0,
  40. unhandledRejections: 0
  41. });
  42. worker.onStateChange(data => this.emitStateChange(data));
  43. }
  44. emitStateChange(evt) {
  45. const stats = this.stats;
  46. const fileStats = this.stats.byFile.get(evt.testFile);
  47. let changedStats = true;
  48. switch (evt.type) {
  49. case 'declared-test':
  50. stats.declaredTests++;
  51. fileStats.declaredTests++;
  52. break;
  53. case 'hook-failed':
  54. stats.failedHooks++;
  55. fileStats.failedHooks++;
  56. break;
  57. case 'internal-error':
  58. stats.internalErrors++;
  59. if (evt.testFile) {
  60. fileStats.internalErrors++;
  61. }
  62. break;
  63. case 'selected-test':
  64. stats.selectedTests++;
  65. fileStats.selectedTests++;
  66. if (evt.skip) {
  67. stats.skippedTests++;
  68. fileStats.skippedTests++;
  69. } else if (evt.todo) {
  70. stats.todoTests++;
  71. fileStats.todoTests++;
  72. } else {
  73. stats.remainingTests++;
  74. fileStats.remainingTests++;
  75. }
  76. break;
  77. case 'test-failed':
  78. stats.failedTests++;
  79. fileStats.failedTests++;
  80. stats.remainingTests--;
  81. fileStats.remainingTests--;
  82. break;
  83. case 'test-passed':
  84. if (evt.knownFailing) {
  85. stats.passedKnownFailingTests++;
  86. fileStats.passedKnownFailingTests++;
  87. } else {
  88. stats.passedTests++;
  89. fileStats.passedTests++;
  90. }
  91. stats.remainingTests--;
  92. fileStats.remainingTests--;
  93. break;
  94. case 'timeout':
  95. stats.timeouts++;
  96. break;
  97. case 'uncaught-exception':
  98. stats.uncaughtExceptions++;
  99. fileStats.uncaughtExceptions++;
  100. break;
  101. case 'unhandled-rejection':
  102. stats.unhandledRejections++;
  103. fileStats.unhandledRejections++;
  104. break;
  105. case 'worker-failed':
  106. stats.failedWorkers++;
  107. break;
  108. case 'worker-finished':
  109. stats.finishedWorkers++;
  110. break;
  111. default:
  112. changedStats = false;
  113. break;
  114. }
  115. if (changedStats) {
  116. this.emit('stateChange', {type: 'stats', stats: cloneDeep(stats)});
  117. }
  118. this.emit('stateChange', evt);
  119. }
  120. suggestExitCode(circumstances) {
  121. if (circumstances.matching && this.stats.selectedTests === 0) {
  122. return 1;
  123. }
  124. if (
  125. this.stats.declaredTests === 0 ||
  126. this.stats.internalErrors > 0 ||
  127. this.stats.failedHooks > 0 ||
  128. this.stats.failedTests > 0 ||
  129. this.stats.failedWorkers > 0 ||
  130. this.stats.timeouts > 0 ||
  131. this.stats.uncaughtExceptions > 0 ||
  132. this.stats.unhandledRejections > 0
  133. ) {
  134. return 1;
  135. }
  136. return 0;
  137. }
  138. }
  139. module.exports = RunStatus;