Repository: facebook/jest
File: packages/jest-core/src/testSchedulerHelper.ts (TypeScript)

Code before:

import type {Test} from '@jest/test-result';
import type {Config} from '@jest/types';

const SLOW_TEST_TIME = 1000;

export function shouldRunInBand(
  tests: Array<Test>,
  timings: Array<number>,
  {
    detectOpenHandles,
    maxWorkers,
    watch,
    watchAll,
    workerIdleMemoryLimit,
  }: Config.GlobalConfig,
): boolean {
  // detectOpenHandles makes no sense without runInBand, because it cannot detect leaks in workers
  if (detectOpenHandles) {
    return true;
  }

  /*
   * If we are using watch/watchAll mode, don't schedule anything in the main
   * thread to keep the TTY responsive and to keep the watcher running even if
   * the test crashes.
   */
  const isWatchMode = watch || watchAll;
  if (isWatchMode) {
    return false;
  }

  /*
   * Otherwise, run in band if we only have one test or one worker available.
   * Also, if we are confident from previous runs that the tests will finish
   * quickly we also run in band to reduce the overhead of spawning workers.
   * https://github.com/facebook/jest/blob/700e0dadb85f5dc8ff5dac6c7e98956690049734/packages/jest-config/src/getMaxWorkers.js#L14-L17
   */
  const areFastTests = timings.every(timing => timing < SLOW_TEST_TIME);
  const oneWorkerOrLess = maxWorkers <= 1;
  const oneTestOrLess = tests.length <= 1;

  return (
    // When specifying a memory limit, workers should be used
    !workerIdleMemoryLimit &&
    (oneWorkerOrLess ||
      oneTestOrLess ||
      (tests.length <= 20 && timings.length > 0 && areFastTests))
  );
}


Review comment (bug):
Hm.. Perhaps the comment should say: "and prevents watch mode crashes caused by tests leaking due to improper teardown". Or something that way?

It isn’t that workers do prevent watch mode to crash due to fatal errors in tests, that is more about preventing leaks / open handles or debugging them (as you pointed out in the issue).

There is no mechanism to deal with this problem if tests run in band. Leaks are handled more gracefully in parallel run, because worker farm gets killed after tests run is finished:

https://github.com/facebook/jest/blob/470d72ee74e50291bf8593954f24eb75b5621351/packages/jest-runner/src/index.ts#L180-L194



Code after:

import type {Test} from '@jest/test-result';
import type {Config} from '@jest/types';

const SLOW_TEST_TIME = 1000;

export function shouldRunInBand(
  tests: Array<Test>,
  timings: Array<number>,
  {
    detectOpenHandles,
    maxWorkers,
    watch,
    watchAll,
    workerIdleMemoryLimit,
  }: Config.GlobalConfig,
): boolean {
  // detectOpenHandles makes no sense without runInBand, because it cannot detect leaks in workers
  if (detectOpenHandles) {
    return true;
  }

  /*
   * If we are using watch/watchAll mode, don't schedule anything in the main
   * thread to keep the TTY responsive and to prevent watch mode crashes caused
   * by leaks (improper test teardown).
   */
  if (watch || watchAll) {
    return false;
  }

  /*
   * Otherwise, run in band if we only have one test or one worker available.
   * Also, if we are confident from previous runs that the tests will finish
   * quickly we also run in band to reduce the overhead of spawning workers.
   * Finally, the user can provide the runInBand argument in the CLI to
   * force running in band, which sets maxWorkers to 1 here:
   * https://github.com/facebook/jest/blob/d106643a8ee0ffa9c2f11c6bb2d12094e99135aa/packages/jest-config/src/getMaxWorkers.ts#L27-L28
   */
  const areFastTests = timings.every(timing => timing < SLOW_TEST_TIME);
  const oneWorkerOrLess = maxWorkers <= 1;
  const oneTestOrLess = tests.length <= 1;

  return (
    // When specifying a memory limit, workers should be used
    !workerIdleMemoryLimit &&
    (oneWorkerOrLess ||
      oneTestOrLess ||
      (tests.length <= 20 && timings.length > 0 && areFastTests))
  );
}