summaryrefslogtreecommitdiff
path: root/scripts/frontend/test.js
blob: dab7176f8c18053bba85682e8e4c242c5ad63d5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env node

const { spawn } = require('child_process');
const { EOL } = require('os');
const program = require('commander');
const chalk = require('chalk');

const JEST_ROUTE = 'spec/frontend';
const KARMA_ROUTE = 'spec/javascripts';
const COMMON_ARGS = ['--colors'];
const JEST_ARGS = ['--passWithNoTests'];
const KARMA_ARGS = ['--no-fail-on-empty-test-suite'];
const SUCCESS_CODE = 0;

program
  .version('0.1.0')
  .usage('[options] <file ...>')
  .option('-p, --parallel', 'Run tests suites in parallel')
  .parse(process.argv);

const isSuccess = code => code === SUCCESS_CODE;

const combineExitCodes = codes => {
  const firstFail = codes.find(x => !isSuccess(x));

  return firstFail === undefined ? SUCCESS_CODE : firstFail;
};

const skipIfFail = fn => code => (isSuccess(code) ? fn() : code);

const endWithEOL = str => (str[str.length - 1] === '\n' ? str : `${str}${EOL}`);

const runTests = paths => {
  if (program.parallel) {
    return Promise.all([runJest(paths), runKarma(paths)]).then(combineExitCodes);
  } else {
    return runJest(paths).then(skipIfFail(() => runKarma(paths)));
  }
};

const spawnYarnScript = (cmd, args) => {
  return new Promise((resolve, reject) => {
    const proc = spawn('yarn', ['run', cmd, ...args]);
    const output = data => {
      const text = data
        .toString()
        .split(/\r?\n/g)
        .map((line, idx, { length }) =>
          idx === length - 1 && !line ? line : `${chalk.gray(cmd)}: ${line}`,
        )
        .join(EOL);

      return endWithEOL(text);
    };

    proc.stdout.on('data', data => {
      process.stdout.write(output(data));
    });

    proc.stderr.on('data', data => {
      process.stderr.write(output(data));
    });

    proc.on('close', code => {
      process.stdout.write(output(`exited with code ${code}`));

      // We resolve even on a failure code because a `reject` would cause
      // Promise.all to reject immediately (without waiting for other promises)
      // to finish.
      resolve(code);
    });
  });
};

const runJest = args => {
  return spawnYarnScript('jest', [...JEST_ARGS, ...COMMON_ARGS, ...toJestArgs(args)]);
};

const runKarma = args => {
  return spawnYarnScript('karma', [...KARMA_ARGS, ...COMMON_ARGS, ...toKarmaArgs(args)]);
};

const replacePath = to => path =>
  path
    .replace(JEST_ROUTE, to)
    .replace(KARMA_ROUTE, to)
    .replace('app/assets/javascripts', to);

const replacePathForJest = replacePath(JEST_ROUTE);

const replacePathForKarma = replacePath(KARMA_ROUTE);

const toJestArgs = paths => paths.map(replacePathForJest);

const toKarmaArgs = paths =>
  paths.reduce((acc, path) => acc.concat('-f', replacePathForKarma(path)), []);

const main = paths => {
  runTests(paths).then(code => {
    console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
    if (isSuccess(code)) {
      console.log(chalk.bgGreen(chalk.black('All tests passed :)')));
    } else {
      console.log(chalk.bgRed(chalk.white(`Some tests failed :(`)));
    }
    console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');

    if (!isSuccess(code)) {
      process.exit(code);
    }
  });
};

main(program.args);