Jest: Delay option in jest --watch

Created on 10 Aug 2017  路  17Comments  路  Source: facebook/jest

Do you want to request a feature or report a bug?

Feature

What is the current behavior?

jest --watch will always re-run tests immediately after the files are changed. If you're working on a server API and the server is running watch nodemon or similar, this means tests will likely run before nodemon completed restarting the server. So you get a 404 on any request in your tests.

What is the expected behavior?

Would be nice to set a delay --watch --wait 1000 to delay the test a little while.

Most helpful comment

using jest with typescript and would appreciate a delay too. from time to time jest runs so quickly, that i get test errors basically while typing new code. dont get me wrong, the performance is amazing. but in watchmode (during development) such a delay would be helpful

All 17 comments

We won't be adding a delay, as things like that will hurt the perception of Jest as being fast. However, we will soon add a hook that you can run when a file changes. Feel free to stay up to date with pull requests coming from me over the next couple of weeks, but I'm gonna close this issue right now because as it is, it's not actionable. Thanks!

@cpojer Sorry, I explained poorly. This would be completely optional. The default delay would be zero. The delay would be specified in the terminal as an argument like --delay or --wait, or in the config as something like "watchDelay": 1000.

Yes, but if tools like create-react-app start using this it will delay watch mode for everyone depending on that tool, thus giving Jest a bad perception.

10 million seconds.

I have a use case for this in my lerna repo. I have end to end tests that should run after local packages have finished building on a separate thread. Even though im ignoring the source directories jest runs multiple times for a single change. A delay option would help batch up those changes

"separate thread", "delay" <- pick one. Delay is not an answer to your threading issues.

Never before have I seen a project with such a fragile sense of self worth that they worry folks will mistakenly perceive their code is slow because there鈥檚 an optional delay.

@jwickens it seems like we need a separate solution for that like @thymikee pointed out. A delay will only make your race condition less apparent.

@OKNoah Please appreciate that performance at scale is one of the most important features of the tools that we are building and that artificial delays do not fit into those constraints and rarely solve real problems. We have every intention to support pull requests sent by users that solve problems in a way that doesn't tape over issues like race conditions. From your original description the ideal solution would be a hook where one part of your system can notify Jest to continue. We are basically looking for a lock mechanism, not a delay. I apologize for not having found the time to add such a hook. I only get to spend a minimal amount of time on open source work on weekends at this time.

Ok so I did do a work around involving nodemon and that ameliorated the race condition. But it also was not great because it basically disabled all the useful jest interactive watch commands.

So now what've done used the moduleNameMapper options to point jest to the source dirs so it can control the compiling without worrying about a second build thread.

My use case is pretty similar to that stated above: jest --watchAll runs before webpack has a chance to update. This would be a really helpful feature to add, even if some randos on Twitter mistakenly complain about Jest being slow because of this feature being misapplied.

I suppose a custom nodemon would be the best solution, which is a shame since the built-in watch feature is so solid on its own.

This could be a custom watch mode plugin in Jest 23, and with that you could customize the delay or even link the delay to the server restart 馃殌

using jest with typescript and would appreciate a delay too. from time to time jest runs so quickly, that i get test errors basically while typing new code. dont get me wrong, the performance is amazing. but in watchmode (during development) such a delay would be helpful

For people ending here from a Google search: you can add a timeout/polling/whatever based waiting with the globalSetup option. See e.g. https://github.com/urish/firebase-server/issues/79#issuecomment-423167926

@OKNoah we had the same issue with testing the API server, we added this to the globalSetup script, and it's doing the job.

const sleep = time => new Promise(resolve => setTimeout(resolve, time))

module.exports = async function() {
  // wait a 1sec until the API server is UP!
  await sleep(1000)
}

Instead of hardcoding some timeout, I'd recommend trying to make a request to your server, and resolve the promise once it returns 200 or something. Just waiting for 1 sec seems both flaky and overkill (what if you didn't restart the server at all, then you're waiting 1 second for nothing). The overhead of a single http request on localhost shouldn't be too bad

Sample code, to make sure server is alive before sending requests:

const axios = require('axios');

// Make sure server is alive before starting tests
const MAX_REQUESTS = 20;
module.exports = async () => {
  let serverReady = false;
  for (let i = 0; i <= MAX_REQUESTS; i++) {
    try {
      await axios('http://localhost:3001');
      serverReady = true;
      break;
    } catch (err) {
      await new Promise(resolve => setTimeout(resolve, 100));
      continue;
    }
  }

  if (!serverReady) throw new Error(`Server never became ready.`);
};

something similar i'm doing, since i'm using tsc --watch to compile my code and tests, is the following:

import * as fs from 'fs';
import * as path from 'path';

import watch = require('sane');

// this is kinda hacky, but it works.
// Waits for tsc to finish transpiling the files before running the tests,
// by watching for changes to the tsconfig.tsbuildinfo, and only finishing
// the globalsetup once it has changed, which marks that tsc has finshed

let idle = true;
let p = Promise.resolve();
let done: () => void;
const watcher = watch(path.resolve(__dirname, '..'), {
  glob: ['**/*.ts', 'tsconfig.tsbuildinfo'],
});

watcher.on('change', (pth) => {
  if (idle) {
    if (pth.endsWith('.ts')) {
      // TODO: read the current tsconfig.tsbuildinfo, and dont wait if the hash
      // is the same
      idle = false;
      p = new Promise<void>((resolve) => {
        done = resolve;
      });
    }
  } else if (pth === 'tsconfig.tsbuildinfo') {
    idle = true;
    done();
  }
});

module.exports = () => p;
Was this page helpful?
0 / 5 - 0 ratings