index.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import * as path from 'node:path';
  2. import {fileURLToPath} from 'node:url';
  3. import chokidar from 'chokidar';
  4. import {colors, logError, logInfo, logWarn, parseOptions} from '#cli';
  5. import find, {bindFind, getAllFindSpecs} from '#find';
  6. import {isMain} from '#node-utils';
  7. import {getContextAssignments} from '#repl';
  8. import {bindOpts, showAggregate} from '#sugar';
  9. import {quickLoadAllFromYAML} from '#yaml';
  10. async function main() {
  11. const miscOptions = await parseOptions(process.argv.slice(2), {
  12. 'data-path': {
  13. type: 'value',
  14. },
  15. });
  16. const dataPath = miscOptions['data-path'] || process.env.HSMUSIC_DATA;
  17. if (!dataPath) {
  18. logError`Expected --data-path option or HSMUSIC_DATA to be set`;
  19. return;
  20. }
  21. console.log(`HSMusic automated data tests`);
  22. console.log(`${colors.bright(colors.yellow(`:star:`))} Now featuring quick-reloading! ${colors.bright(colors.cyan(`:earth:`))}`);
  23. // Watch adjacent files in data-tests directory
  24. const metaPath = fileURLToPath(import.meta.url);
  25. const metaDirname = path.dirname(metaPath);
  26. const watcher = chokidar.watch(metaDirname);
  27. const wikiData = await quickLoadAllFromYAML(dataPath, {
  28. find,
  29. bindFind,
  30. getAllFindSpecs,
  31. showAggregate: bindOpts(showAggregate, {
  32. showTraces: false,
  33. }),
  34. });
  35. const context = await getContextAssignments({
  36. wikiData,
  37. });
  38. let resolveNext;
  39. const queue = [];
  40. watcher.on('all', (event, path) => {
  41. if (!['add', 'change'].includes(event)) return;
  42. if (path === metaPath) return;
  43. if (resolveNext) {
  44. resolveNext(path);
  45. } else if (!queue.includes(path)) {
  46. queue.push(path);
  47. }
  48. });
  49. logInfo`Awaiting file changes.`;
  50. /* eslint-disable-next-line no-constant-condition */
  51. while (true) {
  52. const testPath = (queue.length
  53. ? queue.shift()
  54. : await new Promise(resolve => {
  55. resolveNext = resolve;
  56. }));
  57. resolveNext = null;
  58. const shortPath = path.basename(testPath);
  59. logInfo`Path updated: ${shortPath} - running this test!`;
  60. let imp;
  61. try {
  62. imp = await import(`${testPath}?${Date.now()}`)
  63. } catch (error) {
  64. logWarn`Failed to import ${shortPath} - ${error.constructor.name} details below:`;
  65. console.error(error);
  66. continue;
  67. }
  68. const {default: testFn} = imp;
  69. if (!testFn) {
  70. logWarn`No default export for ${shortPath}`;
  71. logWarn`Skipping this test for now!`;
  72. continue;
  73. }
  74. if (typeof testFn !== 'function') {
  75. logWarn`Default export for ${shortPath} is ${typeof testFn}, not function`;
  76. logWarn`Skipping this test for now!`;
  77. continue;
  78. }
  79. try {
  80. await testFn(context);
  81. } catch (error) {
  82. showAggregate(error, {
  83. pathToFileURL: f => path.relative(metaDirname, fileURLToPath(f)),
  84. });
  85. }
  86. }
  87. }
  88. if (isMain(import.meta.url)) {
  89. main().catch((error) => {
  90. if (error instanceof AggregateError) {
  91. showAggregate(error);
  92. } else {
  93. console.error(error);
  94. }
  95. });
  96. }