<commit_before>import ms from 'ms';
import fs from 'fs-extra';
import test from 'ava';
import path from 'path';
import _execa from 'execa';
import fetch from 'node-fetch';
import sleep from 'then-sleep';
import retry from 'async-retry';
import { satisfies } from 'semver';
import { getDistTag } from '../../src/util/get-dist-tag';
import { version as cliVersion } from '../../package.json';

const isCanary = () => getDistTag(cliVersion) === 'canary';

let port = 3000;

const binaryPath = path.resolve(__dirname, `../../scripts/start.js`);
const fixture = name => path.join('test', 'dev', 'fixtures', name);

// For the Hugo executable
process.env.PATH = `${path.resolve(fixture('08-hugo'))}${path.delimiter}${
  process.env.PATH
}`;

let processCounter = 0;
const processList = new Map();

function execa(...args) {
  const procId = ++processCounter;
  const child = _execa(...args);

  processList.set(procId, child);
  child.on('exit', () => processList.delete(procId));

  return child;
}

function fetchWithRetry(url, retries = 3, opts = {}) {
  return retry(
    async () => {
      const res = await fetch(url, opts);

      if (!res.ok) {
        const text = await res.text();
        throw new Error(
          `Failed to fetch ${url} with status ${res.status}:` +
            `\n\n${text}\n\n`
        );
      }

      return res;
    },
    {
      retries,
      factor: 1,
    }
  );
}

function createResolver() {
  let resolver;
  const p = new Promise(res => (resolver = res));
  p.resolve = resolver;
  return p;
}

function formatOutput({ stderr, stdout }) {
  return `Received:\n"${stderr}"\n"${stdout}"`;
}

function printOutput(fixture, stdout, stderr) {
  const lines = (
    `\nOutput for "${fixture}"\n` +
    `\n----- stdout -----\n` +
    stdout +
    `\n----- stderr -----\n` +
    stderr
  ).split('\n');

  const getPrefix = nr => {
    return nr === 0 ? '╭' : nr === lines.length - 1 ? '╰' : '│';
  };

  console.log(
    lines.map((line, index) => ` ${getPrefix(index)} ${line}`).join('\n')
  );
}

function shouldSkip(t, name, versions) {
  if (!satisfies(process.version, versions)) {
    console.log(`Skipping "${name}" because it requires "${versions}".`);
    t.pass();
    return true;
  }

  return false;
}

function validateResponseHeaders(t, res) {
  t.is(res.headers.get('x-now-trace'), 'dev1');
  t.truthy(res.headers.get('cache-control').length > 0);
  t.truthy(
    /^dev1:[0-9a-z]{5}-[1-9][0-9]+-[a-f0-9]{12}$/.test(
      res.headers.get('x-now-id')
    )
  );
}

async function exec(directory, args = []) {
  return execa(binaryPath, ['dev', directory, ...args], {
    reject: false,
    shell: true,
    env: { __NOW_SKIP_DEV_COMMAND: 1 },
  });
}

async function runNpmInstall(fixturePath) {
  if (await fs.exists(path.join(fixturePath, 'package.json'))) {
    await execa('npm', ['install'], {
      cwd: fixturePath,
      shell: true,
      stdio: 'inherit',
    });
  }
}

async function getPackedBuilderPath(builderDirName) {
  const packagePath = path.join(__dirname, '..', '..', '..', builderDirName);
  const output = await execa('npm', ['pack'], {
    cwd: packagePath,
    shell: true,
  });

  if (output.exitCode !== 0 || output.stdout.trim() === '') {
    throw new Error(
      `Failed to pack ${builderDirName}: ${formatOutput(output)}`
    );
  }

  return path.join(packagePath, output.stdout.trim());
}

async function testPath(t, port, status, path, expectedText, headers = {}) {
  const opts = { redirect: 'manual-dont-change' };
  const res = await fetch(`http://localhost:${port}${path}`, opts);<commit_msg>[now-cli] Use `yarn` for `now dev` integration tests (#4175)<commit_after>import ms from 'ms';
import fs from 'fs-extra';
import test from 'ava';
import path from 'path';
import _execa from 'execa';
import fetch from 'node-fetch';
import sleep from 'then-sleep';
import retry from 'async-retry';
import { satisfies } from 'semver';
import { getDistTag } from '../../src/util/get-dist-tag';
import { version as cliVersion } from '../../package.json';

const isCanary = () => getDistTag(cliVersion) === 'canary';

let port = 3000;

const binaryPath = path.resolve(__dirname, `../../scripts/start.js`);
const fixture = name => path.join('test', 'dev', 'fixtures', name);

// For the Hugo executable
process.env.PATH = `${path.resolve(fixture('08-hugo'))}${path.delimiter}${
  process.env.PATH
}`;

let processCounter = 0;
const processList = new Map();

function execa(...args) {
  const procId = ++processCounter;
  const child = _execa(...args);

  processList.set(procId, child);
  child.on('exit', () => processList.delete(procId));

  return child;
}

function fetchWithRetry(url, retries = 3, opts = {}) {
  return retry(
    async () => {
      const res = await fetch(url, opts);

      if (!res.ok) {
        const text = await res.text();
        throw new Error(
          `Failed to fetch ${url} with status ${res.status}:` +
            `\n\n${text}\n\n`
        );
      }

      return res;
    },
    {
      retries,
      factor: 1,
    }
  );
}

function createResolver() {
  let resolver;
  const p = new Promise(res => (resolver = res));
  p.resolve = resolver;
  return p;
}

function formatOutput({ stderr, stdout }) {
  return `Received:\n"${stderr}"\n"${stdout}"`;
}

function printOutput(fixture, stdout, stderr) {
  const lines = (
    `\nOutput for "${fixture}"\n` +
    `\n----- stdout -----\n` +
    stdout +
    `\n----- stderr -----\n` +
    stderr
  ).split('\n');

  const getPrefix = nr => {
    return nr === 0 ? '╭' : nr === lines.length - 1 ? '╰' : '│';
  };

  console.log(
    lines.map((line, index) => ` ${getPrefix(index)} ${line}`).join('\n')
  );
}

function shouldSkip(t, name, versions) {
  if (!satisfies(process.version, versions)) {
    console.log(`Skipping "${name}" because it requires "${versions}".`);
    t.pass();
    return true;
  }

  return false;
}

function validateResponseHeaders(t, res) {
  t.is(res.headers.get('x-now-trace'), 'dev1');
  t.truthy(res.headers.get('cache-control').length > 0);
  t.truthy(
    /^dev1:[0-9a-z]{5}-[1-9][0-9]+-[a-f0-9]{12}$/.test(
      res.headers.get('x-now-id')
    )
  );
}

async function exec(directory, args = []) {
  return execa(binaryPath, ['dev', directory, ...args], {
    reject: false,
    shell: true,
    env: { __NOW_SKIP_DEV_COMMAND: 1 },
  });
}

async function runNpmInstall(fixturePath) {
  if (await fs.exists(path.join(fixturePath, 'package.json'))) {
    await execa('yarn', ['install'], {
      cwd: fixturePath,
      shell: true,
    });
  }
}

async function getPackedBuilderPath(builderDirName) {
  const packagePath = path.join(__dirname, '..', '..', '..', builderDirName);
  const output = await execa('npm', ['pack'], {
    cwd: packagePath,
    shell: true,
  });

  if (output.exitCode !== 0 || output.stdout.trim() === '') {
    throw new Error(
      `Failed to pack ${builderDirName}: ${formatOutput(output)}`
    );
  }

  return path.join(packagePath, output.stdout.trim());
}

async function testPath(t, port, status, path, expectedText, headers = {}) {
  const opts = { redirect: 'manual-dont-change' };
  const res = await fetch(`http://localhost:${port}${path}`, opts);
{'fraction_non_alphanumeric': 0.19755147468002227, 'fraction_numerical': 0.008069003895381191, 'mean_word_length': 3.1363636363636362, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3856703', 'n_tokens_mistral': 2532, 'n_tokens_neox': 2490, 'n_words': 585}