#!/usr/bin/env node const inquirer = require('inquirer'); const program = require('commander'); const childProcess = require('child_process'); const chalk = require('chalk'); const log = require('npmlog'); const path = require('path'); log.heading = 'storybook'; const prefix = 'test'; log.addLevel('aborted', 3001, { fg: 'red', bold: true }); const spawn = command => { const out = childProcess.spawnSync(`${command}`, { shell: true, stdio: 'inherit', }); if (out.status !== 0) { process.exit(out.status); } return out; }; const main = program.version('3.0.0').option('--all', `Test everything ${chalk.gray('(all)')}`); const createProject = ({ defaultValue, option, name, projectLocation, isJest }) => ({ value: false, defaultValue: defaultValue || false, option: option || undefined, name: name || 'unnamed task', projectLocation, isJest, }); const createOption = ({ defaultValue, option, name, extraParam }) => ({ value: false, defaultValue: defaultValue || false, option: option || undefined, name: name || 'unnamed task', extraParam, }); const tasks = { core: createProject({ name: `Core & React & Vue ${chalk.gray('(core)')}`, defaultValue: true, option: '--core', projectLocation: path.join(__dirname, '..'), isJest: true, }), 'react-native-vanilla': createProject({ name: `React-Native example ${chalk.gray('(react-native-vanilla)')}`, defaultValue: true, option: '--reactnative', projectLocation: path.join(__dirname, '..', 'examples/react-native-vanilla'), isJest: true, }), integration: createProject({ name: `Screenshots of running apps ${chalk.gray('(integration)')}`, defaultValue: false, option: '--integration', projectLocation: path.join(__dirname, '..', 'integration'), isJest: true, }), // 'crna-kitchen-sink': createProject({ // name: `React-Native-App example ${chalk.gray('(crna-kitchen-sink)')} ${chalk.red( // '[not implemented yet]' // )}`, // defaultValue: false, // option: '--reactnativeapp', // projectLocation: './examples/crna-kitchen-sink', // isJest: true, // }), cli: createProject({ name: `Command Line Interface ${chalk.gray('(cli)')}`, defaultValue: false, option: '--cli', projectLocation: './lib/cli', }), watchmode: createOption({ name: `Run in watch-mode ${chalk.gray('(watchmode)')}`, defaultValue: false, option: '--watch', extraParam: '--watch', }), coverage: createOption({ name: `Output coverage reports ${chalk.gray('(coverage)')}`, defaultValue: false, option: '--coverage', extraParam: '--coverage', }), runInBand: createOption({ name: `Run all tests serially in the current process ${chalk.gray('(runInBand)')}`, defaultValue: false, option: '--runInBand', extraParam: '--runInBand', }), update: createOption({ name: `Update all snapshots ${chalk.gray('(update)')}`, defaultValue: false, option: '--update', extraParam: '-u', }), }; const getProjects = list => { const filtered = list.filter(key => key.projectLocation); if (filtered.length > 0) { return filtered; } // if list would have been empty, we run with default projects return Object.keys(tasks) .map(key => tasks[key]) .filter(key => key.projectLocation && key.defaultValue); }; const getExtraParams = list => list.filter(key => key.extraParam).map(key => key.extraParam); Object.keys(tasks) .reduce((acc, key) => acc.option(tasks[key].option, tasks[key].name), main) .parse(process.argv); Object.keys(tasks).forEach(key => { tasks[key].value = program[tasks[key].option.replace('--', '')] || (program.all && tasks[key].projectLocation); }); let selection; if ( !Object.keys(tasks) .map(key => tasks[key].value) .filter(Boolean).length ) { selection = inquirer .prompt([ { type: 'checkbox', message: 'Select which tests to run', name: 'todo', pageSize: 8, choices: Object.keys(tasks) .map(key => tasks[key]) .filter(key => key.projectLocation) .map(key => ({ name: key.name, checked: key.defaultValue, })) .concat(new inquirer.Separator()) .concat( Object.keys(tasks) .map(key => tasks[key]) .filter(key => key.extraParam) .map(key => ({ name: key.name, checked: key.defaultValue, })) ), }, ]) .then(({ todo }) => todo.map(name => tasks[Object.keys(tasks).find(i => tasks[i].name === name)]) ); } else { selection = Promise.resolve( Object.keys(tasks) .map(key => tasks[key]) .filter(item => item.value === true) ); } selection .then(list => { if (list.length === 0) { log.warn(prefix, 'Nothing to test'); } else { const projects = getProjects(list); const jestProjects = projects.filter(key => key.isJest).map(key => key.projectLocation); const nonJestProjects = projects.filter(key => !key.isJest); const extraParams = getExtraParams(list).join(' '); if (jestProjects.length > 0) { spawn(`jest --projects ${jestProjects.join(' ')} ${extraParams}`); } nonJestProjects.forEach(key => spawn(`npm --prefix ${key.projectLocation} test -- ${extraParams}`) ); process.stdout.write('\x07'); } }) .catch(e => { log.aborted(prefix, chalk.red(e.message)); log.silly(prefix, e); process.exit(1); });