test.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import { run } from 'node:test';
  2. import { spec } from 'node:test/reporters';
  3. import fs from 'node:fs/promises';
  4. import path from 'node:path';
  5. import { pathToFileURL } from 'node:url';
  6. import arg from 'arg';
  7. import glob from 'tiny-glob';
  8. const isCI = !!process.env.CI;
  9. const defaultTimeout = isCI ? 1200000 : 600000;
  10. export default async function test() {
  11. const args = arg({
  12. '--match': String, // aka --test-name-pattern: https://nodejs.org/api/test.html#filtering-tests-by-name
  13. '--only': Boolean, // aka --test-only: https://nodejs.org/api/test.html#only-tests
  14. '--parallel': Boolean, // aka --test-concurrency: https://nodejs.org/api/test.html#test-runner-execution-model
  15. '--watch': Boolean, // experimental: https://nodejs.org/api/test.html#watch-mode
  16. '--timeout': Number, // Test timeout in milliseconds (default: 30000ms)
  17. '--setup': String, // Test setup file
  18. // Aliases
  19. '-m': '--match',
  20. '-o': '--only',
  21. '-p': '--parallel',
  22. '-w': '--watch',
  23. '-t': '--timeout',
  24. '-s': '--setup',
  25. });
  26. const pattern = args._[1];
  27. if (!pattern) throw new Error('Missing test glob pattern');
  28. const files = await glob(pattern, { filesOnly: true, absolute: true });
  29. // For some reason, the `only` option does not work and we need to explicitly set the CLI flag instead.
  30. // Node.js requires opt-in to run .only tests :(
  31. // https://nodejs.org/api/test.html#only-tests
  32. if (args['--only']) {
  33. process.env.NODE_OPTIONS ??= '';
  34. process.env.NODE_OPTIONS += ' --test-only';
  35. }
  36. if (!args['--parallel']) {
  37. // If not parallel, we create a temporary file that imports all the test files
  38. // so that it all runs in a single process.
  39. const tempTestFile = path.resolve('./node_modules/.astro/test.mjs');
  40. await fs.mkdir(path.dirname(tempTestFile), { recursive: true });
  41. await fs.writeFile(
  42. tempTestFile,
  43. files.map((f) => `import ${JSON.stringify(pathToFileURL(f).toString())};`).join('\n')
  44. );
  45. files.length = 0;
  46. files.push(tempTestFile);
  47. }
  48. // https://nodejs.org/api/test.html#runoptions
  49. run({
  50. files,
  51. testNamePatterns: args['--match'],
  52. concurrency: args['--parallel'],
  53. only: args['--only'],
  54. setup: args['--setup'],
  55. watch: args['--watch'],
  56. timeout: args['--timeout'] ?? defaultTimeout, // Node.js defaults to Infinity, so set better fallback
  57. })
  58. .on('test:fail', () => {
  59. // For some reason, a test fail using the JS API does not set an exit code of 1,
  60. // so we set it here manually
  61. process.exitCode = 1;
  62. })
  63. .pipe(new spec())
  64. .pipe(process.stdout);
  65. }