ОБНОВЛЕНИЕ: скоро весь этот пост (https://github.com/facebook/jest/pull/5601) больше не нужен, и то же самое можно сделать, используя: jest --coverage --changedSince=master

Если вы используете Jest сегодня и запускаете весь свой пакет тестирования при каждом PR (запрос на вытягивание) для создания отчетов о покрытии кода, прекратите то, что вы делаете. Потратьте 5 минут на чтение этого сообщения, это будет лучшим вложением, которое вы сделали за все лето!

На первый взгляд совет использовать отчеты о покрытии для запросов на вытягивание и тестировать только измененные файлы может показаться несовместимым друг с другом.

Au contraire! То, как происходит волшебство, держится в секрете.

Если вы хотите обеспечить определенный порог охвата для каждого PR, без необходимости запускать весь комплект тестирования, Jest поможет нам.

Этот пост проведет вас через следующие шаги:

  1. Как перечислить все изменения между двумя ветками git, относящиеся к Jest.
  2. Использование Jest API для поиска всех связанных тестов.
  3. Попросите Jest исключить неизмененные файлы из отчета о покрытии кода.

Что изменилось

Давайте использовать execa, чтобы мы могли легко связать наши команды cli.

Сначала мы просим git сообщить нам, какие файлы изменились между базовой веткой (remotes / origin / develop) и нашей собственной веткой (на travis имя TRAVIS_BRANCH, как вы поняли).

Затем с помощью команды grep мы получаем только файлы .js, чтобы отфильтровать непроверенные файлы.

const execa = require("execa");
const branch = process.env.CIRCLE_BRANCH;
execa
  .shell(
    `git diff --name-only remotes/origin/master...${branch} | grep .js$`
  )
  .then(({ stdout, stderr }) => {
    if (stderr) {
      throw new Error(stderr);
    }
    return stdout.replace(/\n/g, " ");
  });

Найдите все связанные тесты

—-findRelatedTests - одна из лучших функций Jest. В сочетании с --listTests и 🎉 у вас есть полный список всех тестов, связанных с вашими измененными файлами.

Добавляем этот фрагмент кода к предыдущему:

.then(changedFiles =>
  execa
    .shell(
      `jest --listTests --findRelatedTests ${changedFiles}`
    )
    .then(({ stdout, stderr }) => {
      if (stderr) {
        throw new Error(stderr);
      }
      return {
        changedFiles: changedFiles.split(" "),
        relatedTests: JSON.parse(stdout)
      };
    })
);

changedFiles будет использоваться, чтобы сообщить Jest, что мы хотим проверить на предмет покрытия тестами. relatedTests будет использоваться, чтобы решить, какие тесты запускать.

Запуск тестов

Используя --collectCoverageFrom, мы переключаем Jest с проверки всех js-файлов в вашем проекте на только измененные.

В конце мы просто указываем, какие тесты запускать.

.then(ctx => {
  if (ctx.relatedTests.length < 1) {
    // wow, nothing to test!
    return;
  }
  const collectCoverageFrom = ctx.changedFiles
    .map(from => `--collectCoverageFrom "${from}"`)
    .join(" ");
  const testFiles = ctx.relatedTests
    .map(testFile => path.relative(process.cwd(), testFile))
    .join(" ");
  const coverageCommand = `jest --coverage ${collectCoverageFrom} ${testFiles}`;
  return execa
    .shell(coverageCommand, { stdio: "inherit" })
    .catch(() => {
      process.exitCode = 1;
    });
});

Вот и все 😄.

Полный пример

const execa = require("execa");
const path = require("path");
const branch = process.env.CIRCLE_BRANCH;
execa
  .shell(
    `git diff --name-only remotes/origin/master...${branch} | grep .js$`
  )
  .then(({ stdout, stderr }) => {
    if (stderr) {
      throw new Error(stderr);
    }
    return stdout.replace(/\n/g, " ");
  })
  .then(changedFiles =>
    execa
      .shell(
        `${jest} --listTests --findRelatedTests ${changedFiles}`
      )
      .then(({ stdout, stderr }) => {
        if (stderr) {
          throw new Error(stderr);
        }
        return {
          changedFiles: changedFiles.split(" "),
          relatedTests: JSON.parse(stdout)
        };
      })
  )
  .then(ctx => {
    if (ctx.relatedTests.length < 1) {
      // wow, nothing to test!
      return;
    }
    const collectCoverageFrom = ctx.changedFiles
      .map(from => `--collectCoverageFrom "${from}"`)
      .join(" ");
    const testFiles = ctx.relatedTests
      .map(testFile =>
        path.relative(process.cwd(), testFile)
      )
      .join(" ");
    const coverageCommand = `jest --coverage ${collectCoverageFrom} ${testFiles}`;
    return execa
      .shell(coverageCommand, { stdio: "inherit" })
      .catch(() => {
        process.exitCode = 1;
      });
  });