|
| 1 | +import { mkdirSync, writeFileSync } from 'fs'; |
| 2 | +import * as path from 'path'; |
| 3 | + |
| 4 | +import { program } from 'commander'; |
| 5 | +import glob from 'glob'; |
| 6 | +import log from 'fancy-log'; |
| 7 | + |
| 8 | +import { buildJS, watchJS } from './rollup.js'; |
| 9 | + |
| 10 | +/** |
| 11 | + * Build a bundle of tests and run them using Karma. |
| 12 | + * |
| 13 | + * @param {object} options |
| 14 | + * @param {string} options.bootstrapFile - Entry point for the test bundle that initializes the environment |
| 15 | + * @param {string} options.rollupConfig - Rollup config that generates the test bundle using |
| 16 | + * `${outputDir}/test-inputs.js` as an entry point |
| 17 | + * @param {string} options.karmaConfig - Karma config file |
| 18 | + * @param {string} options.outputDir - Directory in which to generate test bundle. Defaults to |
| 19 | + * `build/scripts` |
| 20 | + * @param {string} options.testsPattern - Minimatch pattern that specifies which test files to |
| 21 | + * load |
| 22 | + * @return {Promise<void>} - Promise that resolves when test run completes |
| 23 | + */ |
| 24 | +export async function runTests({ |
| 25 | + bootstrapFile, |
| 26 | + rollupConfig, |
| 27 | + outputDir = 'build/scripts', |
| 28 | + karmaConfig, |
| 29 | + testsPattern, |
| 30 | +}) { |
| 31 | + // Parse command-line options for test execution. |
| 32 | + program |
| 33 | + .option( |
| 34 | + '--grep <pattern>', |
| 35 | + 'Run only tests where filename matches a regex pattern' |
| 36 | + ) |
| 37 | + .option('--watch', 'Continuously run tests (default: false)', false) |
| 38 | + .parse(process.argv); |
| 39 | + |
| 40 | + const { grep, watch } = program.opts(); |
| 41 | + const singleRun = !watch; |
| 42 | + |
| 43 | + // Generate an entry file for the test bundle. This imports all the test |
| 44 | + // modules, filtered by the pattern specified by the `--grep` CLI option. |
| 45 | + const testFiles = [ |
| 46 | + bootstrapFile, |
| 47 | + ...glob.sync(testsPattern).filter(path => (grep ? path.match(grep) : true)), |
| 48 | + ]; |
| 49 | + |
| 50 | + const testSource = testFiles |
| 51 | + .map(path => `import "../../${path}";`) |
| 52 | + .join('\n'); |
| 53 | + |
| 54 | + mkdirSync(outputDir, { recursive: true }); |
| 55 | + writeFileSync(`${outputDir}/test-inputs.js`, testSource); |
| 56 | + |
| 57 | + // Build the test bundle. |
| 58 | + log(`Building test bundle... (${testFiles.length} files)`); |
| 59 | + if (singleRun) { |
| 60 | + await buildJS(rollupConfig); |
| 61 | + } else { |
| 62 | + await watchJS(rollupConfig); |
| 63 | + } |
| 64 | + |
| 65 | + // Run the tests. |
| 66 | + log('Starting Karma...'); |
| 67 | + const { default: karma } = await import('karma'); |
| 68 | + const parsedConfig = await karma.config.parseConfig( |
| 69 | + path.resolve(karmaConfig), |
| 70 | + { singleRun } |
| 71 | + ); |
| 72 | + |
| 73 | + return new Promise((resolve, reject) => { |
| 74 | + new karma.Server(parsedConfig, exitCode => { |
| 75 | + if (exitCode === 0) { |
| 76 | + resolve(); |
| 77 | + } else { |
| 78 | + reject(new Error(`Karma run failed with status ${exitCode}`)); |
| 79 | + } |
| 80 | + }).start(); |
| 81 | + |
| 82 | + process.on('SIGINT', () => { |
| 83 | + // Give Karma a chance to handle SIGINT and cleanup, but forcibly |
| 84 | + // exit if it takes too long. |
| 85 | + setTimeout(() => { |
| 86 | + resolve(); |
| 87 | + process.exit(1); |
| 88 | + }, 5000); |
| 89 | + }); |
| 90 | + }); |
| 91 | +} |
0 commit comments