qunitx-cli 0.1.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +120 -49
  2. package/cli.js +1 -1
  3. package/lib/boilerplates/default-project-config-values.js +2 -2
  4. package/lib/boilerplates/test.js +5 -4
  5. package/lib/commands/generate.js +6 -8
  6. package/lib/commands/help.js +8 -8
  7. package/lib/commands/init.js +35 -25
  8. package/lib/commands/run/tests-in-browser.js +97 -67
  9. package/lib/commands/run.js +165 -55
  10. package/lib/servers/http.js +59 -44
  11. package/lib/setup/bind-server-to-port.js +3 -12
  12. package/lib/setup/browser.js +26 -18
  13. package/lib/setup/config.js +8 -10
  14. package/lib/setup/file-watcher.js +23 -6
  15. package/lib/setup/fs-tree.js +29 -27
  16. package/lib/setup/keyboard-events.js +7 -4
  17. package/lib/setup/test-file-paths.js +25 -23
  18. package/lib/setup/web-server.js +87 -61
  19. package/lib/setup/write-output-static-files.js +4 -1
  20. package/lib/tap/display-final-result.js +2 -2
  21. package/lib/tap/display-test-result.js +32 -14
  22. package/lib/utils/find-chrome.js +16 -0
  23. package/lib/utils/find-internal-assets-from-html.js +7 -5
  24. package/lib/utils/find-project-root.js +1 -2
  25. package/lib/utils/indent-string.js +6 -6
  26. package/lib/utils/listen-to-keyboard-key.js +6 -2
  27. package/lib/utils/parse-cli-flags.js +34 -31
  28. package/lib/utils/resolve-port-number-for.js +3 -3
  29. package/lib/utils/run-user-module.js +5 -3
  30. package/lib/utils/search-in-parent-directories.js +4 -1
  31. package/lib/utils/time-counter.js +2 -2
  32. package/package.json +21 -35
  33. package/vendor/qunit.css +7 -7
  34. package/vendor/qunit.js +3772 -3324
  35. package/flake.lock +0 -64
  36. package/flake.nix +0 -26
@@ -1,103 +1,200 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { normalize, dirname } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
+ import { availableParallelism } from 'node:os';
5
+ import Puppeteer from 'puppeteer';
4
6
  import kleur from 'kleur';
5
- import runTestsInBrowser from './run/tests-in-browser.js';
7
+ import runTestsInBrowser, { buildTestBundle } from './run/tests-in-browser.js';
6
8
  import setupBrowser from '../setup/browser.js';
7
9
  import fileWatcher from '../setup/file-watcher.js';
8
10
  import findInternalAssetsFromHTML from '../utils/find-internal-assets-from-html.js';
9
11
  import runUserModule from '../utils/run-user-module.js';
10
12
  import setupKeyboardEvents from '../setup/keyboard-events.js';
11
13
  import writeOutputStaticFiles from '../setup/write-output-static-files.js';
14
+ import timeCounter from '../utils/time-counter.js';
15
+ import TAPDisplayFinalResult from '../tap/display-final-result.js';
16
+ import findChrome from '../utils/find-chrome.js';
12
17
 
13
18
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
19
 
15
- export default async function(config) {
16
- let cachedContent = await buildCachedContent(config, config.htmlPaths);
17
- let [connections, _] = await Promise.all([
18
- setupBrowser(config, cachedContent),
19
- writeOutputStaticFiles(config, cachedContent)
20
- ]);
21
- config.expressApp = connections.server;
20
+ export default async function (config) {
21
+ const cachedContent = await buildCachedContent(config, config.htmlPaths);
22
22
 
23
23
  if (config.watch) {
24
+ // WATCH MODE: single browser, all test files bundled together.
25
+ // The HTTP server stays alive so the user can browse http://localhost:PORT
26
+ // and see all tests running in a single QUnit view.
27
+ const [connections] = await Promise.all([
28
+ setupBrowser(config, cachedContent),
29
+ writeOutputStaticFiles(config, cachedContent),
30
+ ]);
31
+ config.expressApp = connections.server;
24
32
  setupKeyboardEvents(config, cachedContent, connections);
25
- }
26
33
 
27
- if (config.before) {
28
- await runUserModule(`${process.cwd()}/${config.before}`, config, 'before');
29
- }
34
+ if (config.before) {
35
+ await runUserModule(`${process.cwd()}/${config.before}`, config, 'before');
36
+ }
30
37
 
31
- await runTestsInBrowser(config, cachedContent, connections);
38
+ try {
39
+ await runTestsInBrowser(config, cachedContent, connections);
40
+ } catch (error) {
41
+ await Promise.all([
42
+ connections.server && connections.server.close(),
43
+ connections.browser && connections.browser.close(),
44
+ ]);
45
+ throw error;
46
+ }
32
47
 
33
- if (config.watch) {
34
48
  logWatcherAndKeyboardShortcutInfo(config, connections.server);
35
49
 
36
50
  await fileWatcher(
37
51
  config.testFileLookupPaths,
38
52
  config,
39
53
  async (event, file) => {
40
- if (event === 'addDir') {
41
- return;
42
- } else if (['unlink', 'unlinkDir'].includes(event)) {
54
+ if (event === 'addDir') return;
55
+ if (['unlink', 'unlinkDir'].includes(event)) {
43
56
  return await runTestsInBrowser(config, cachedContent, connections);
44
57
  }
45
-
46
58
  await runTestsInBrowser(config, cachedContent, connections, [file]);
47
59
  },
48
- (path, event) => connections.server.publish('refresh', 'refresh')
60
+ (_path, _event) => connections.server.publish('refresh', 'refresh'),
49
61
  );
50
- }
51
- }
62
+ } else {
63
+ // CONCURRENT MODE: split test files across N groups = availableParallelism().
64
+ // All group bundles are built while Chrome is starting up, so esbuild time
65
+ // is hidden behind the ~1.2s Chrome launch. Each group then gets its own
66
+ // HTTP server and Puppeteer page inside one shared browser instance.
67
+ const allFiles = Object.keys(config.fsTree);
68
+ const groupCount = Math.min(allFiles.length, availableParallelism());
69
+ const groups = splitIntoGroups(allFiles, groupCount);
70
+
71
+ // Shared COUNTER so TAP test numbers are globally sequential across all groups.
72
+ config.COUNTER = { testCount: 0, failCount: 0, skipCount: 0, passCount: 0 };
73
+ config.lastRanTestFiles = allFiles;
74
+
75
+ const groupConfigs = groups.map((groupFiles, i) => ({
76
+ ...config,
77
+ fsTree: Object.fromEntries(groupFiles.map((f) => [f, config.fsTree[f]])),
78
+ // Single group keeps the root output dir for backward-compatible file paths.
79
+ output: groupCount === 1 ? config.output : `${config.output}/group-${i}`,
80
+ _groupMode: true,
81
+ }));
82
+ const groupCachedContents = groups.map(() => ({ ...cachedContent }));
83
+
84
+ // Build all group bundles and write static files while Chrome is starting up.
85
+ const [browser] = await Promise.all([
86
+ findChrome().then((chromePath) =>
87
+ Puppeteer.launch({
88
+ args: [
89
+ '--no-sandbox',
90
+ '--disable-gpu',
91
+ '--remote-debugging-port=0',
92
+ '--window-size=1440,900',
93
+ ],
94
+ executablePath: chromePath,
95
+ headless: true,
96
+ }),
97
+ ),
98
+ Promise.all(
99
+ groupConfigs.map((groupConfig, i) =>
100
+ Promise.all([
101
+ buildTestBundle(groupConfig, groupCachedContents[i]),
102
+ writeOutputStaticFiles(groupConfig, groupCachedContents[i]),
103
+ ]),
104
+ ),
105
+ ),
106
+ ]);
107
+
108
+ console.log('TAP version 13');
109
+ const TIME_COUNTER = timeCounter();
110
+ let hasFatalError = false;
111
+
112
+ await Promise.allSettled(
113
+ groupConfigs.map(async (groupConfig, i) => {
114
+ const connections = await setupBrowser(groupConfig, groupCachedContents[i], browser);
115
+ groupConfig.expressApp = connections.server;
116
+
117
+ if (config.before) {
118
+ await runUserModule(`${process.cwd()}/${config.before}`, groupConfig, 'before');
119
+ }
52
120
 
53
- async function buildCachedContent(config, htmlPaths) {
54
- let htmlBuffers = await Promise.all(config.htmlPaths.map((htmlPath) => fs.readFile(htmlPath))); // TODO: remove this and read it from the fsTree, should be cached?
55
- let cachedContent = htmlPaths.reduce((result, htmlPath, index) => {
56
- let filePath = config.htmlPaths[index];
57
- let html = htmlBuffers[index].toString();
121
+ try {
122
+ await runTestsInBrowser(groupConfig, groupCachedContents[i], connections);
123
+ } catch {
124
+ hasFatalError = true;
125
+ } finally {
126
+ await Promise.all([
127
+ connections.server && connections.server.close(),
128
+ connections.page && connections.page.close(),
129
+ ]);
130
+ }
131
+ }),
132
+ );
58
133
 
59
- if (html.includes('{{content}}')) { // TODO: here I could do html analysis to see which static js certain html points to? Complex algorithm
60
- result.dynamicContentHTMLs[filePath] = html;
134
+ await browser.close();
61
135
 
62
- let relativePath = filePath.replace(config.projectRoot, '');
136
+ TAPDisplayFinalResult(config.COUNTER, TIME_COUNTER.stop());
63
137
 
64
- result.htmlPathsToRunTests.push(relativePath);
65
- } else {
66
- console.log('#', kleur.yellow(`WARNING: Static html file with no {{content}} detected. Therefore ignoring ${filePath}`));
67
- result.staticHTMLs[filePath] = html;
138
+ if (config.after) {
139
+ await runUserModule(`${process.cwd()}/${config.after}`, config.COUNTER, 'after');
68
140
  }
69
141
 
70
- findInternalAssetsFromHTML(html).forEach((key) => {
71
- result.assets.add(normalizeInternalAssetPathFromHTML(config.projectRoot, key, filePath))
72
- });
142
+ process.exit(config.COUNTER.failCount > 0 || hasFatalError ? 1 : 0);
143
+ }
144
+ }
73
145
 
74
- return result;
75
- }, {
76
- allTestCode: null,
77
- assets: new Set(),
78
- htmlPathsToRunTests: [],
79
- mainHTML: { filePath: null, html: null },
80
- staticHTMLs: {},
81
- dynamicContentHTMLs: {}
82
- });
146
+ async function buildCachedContent(config, htmlPaths) {
147
+ const htmlBuffers = await Promise.all(config.htmlPaths.map((htmlPath) => fs.readFile(htmlPath)));
148
+ const cachedContent = htmlPaths.reduce(
149
+ (result, htmlPath, index) => {
150
+ const filePath = config.htmlPaths[index];
151
+ const html = htmlBuffers[index].toString();
152
+
153
+ if (html.includes('{{content}}')) {
154
+ result.dynamicContentHTMLs[filePath] = html;
155
+ result.htmlPathsToRunTests.push(filePath.replace(config.projectRoot, ''));
156
+ } else {
157
+ console.log(
158
+ '#',
159
+ kleur.yellow(
160
+ `WARNING: Static html file with no {{content}} detected. Therefore ignoring ${filePath}`,
161
+ ),
162
+ );
163
+ result.staticHTMLs[filePath] = html;
164
+ }
165
+
166
+ findInternalAssetsFromHTML(html).forEach((key) => {
167
+ result.assets.add(normalizeInternalAssetPathFromHTML(config.projectRoot, key, filePath));
168
+ });
169
+
170
+ return result;
171
+ },
172
+ {
173
+ allTestCode: null,
174
+ assets: new Set(),
175
+ htmlPathsToRunTests: [],
176
+ mainHTML: { filePath: null, html: null },
177
+ staticHTMLs: {},
178
+ dynamicContentHTMLs: {},
179
+ },
180
+ );
83
181
 
84
182
  if (cachedContent.htmlPathsToRunTests.length === 0) {
85
183
  cachedContent.htmlPathsToRunTests = ['/'];
86
184
  }
87
185
 
88
- return await addCachedContentMainHTML(config.projectRoot, cachedContent);
186
+ return addCachedContentMainHTML(config.projectRoot, cachedContent);
89
187
  }
90
188
 
91
189
  async function addCachedContentMainHTML(projectRoot, cachedContent) {
92
- let mainHTMLPath = Object.keys(cachedContent.dynamicContentHTMLs)[0];
190
+ const mainHTMLPath = Object.keys(cachedContent.dynamicContentHTMLs)[0];
93
191
  if (mainHTMLPath) {
94
192
  cachedContent.mainHTML = {
95
193
  filePath: mainHTMLPath,
96
- html: cachedContent.dynamicContentHTMLs[mainHTMLPath]
194
+ html: cachedContent.dynamicContentHTMLs[mainHTMLPath],
97
195
  };
98
196
  } else {
99
- let html = (await fs.readFile(`${__dirname}/../boilerplates/setup/tests.hbs`)).toString();
100
-
197
+ const html = (await fs.readFile(`${__dirname}/../boilerplates/setup/tests.hbs`)).toString();
101
198
  cachedContent.mainHTML = { filePath: `${projectRoot}/test/tests.html`, html };
102
199
  cachedContent.assets.add(`${projectRoot}/node_modules/qunitx/vendor/qunit.css`);
103
200
  }
@@ -105,14 +202,27 @@ async function addCachedContentMainHTML(projectRoot, cachedContent) {
105
202
  return cachedContent;
106
203
  }
107
204
 
108
- function logWatcherAndKeyboardShortcutInfo(config, server) {
109
- console.log('#', kleur.blue(`Watching files... You can browse the tests on http://localhost:${config.port} ...`)); // NOTE: maybe add also qx to exit
110
- console.log('#', kleur.blue(`Shortcuts: Press "qq" to abort running tests, "qa" to run all the tests, "qf" to run last failing test, "ql" to repeat last test`)); // NOTE: maybe add also qx to test specific
205
+ function splitIntoGroups(files, groupCount) {
206
+ const groups = Array.from({ length: groupCount }, () => []);
207
+ files.forEach((file, i) => groups[i % groupCount].push(file));
208
+ return groups.filter((g) => g.length > 0);
111
209
  }
112
210
 
113
- function normalizeInternalAssetPathFromHTML(projectRoot, assetPath, htmlPath) { // NOTE: maybe normalize ..
114
- let currentDirectory = htmlPath ? htmlPath.split('/').slice(0, -1).join('/') : projectRoot;
211
+ function logWatcherAndKeyboardShortcutInfo(config, server) {
212
+ console.log(
213
+ '#',
214
+ kleur.blue(`Watching files... You can browse the tests on http://localhost:${config.port} ...`),
215
+ );
216
+ console.log(
217
+ '#',
218
+ kleur.blue(
219
+ `Shortcuts: Press "qq" to abort running tests, "qa" to run all the tests, "qf" to run last failing test, "ql" to repeat last test`,
220
+ ),
221
+ );
222
+ }
115
223
 
224
+ function normalizeInternalAssetPathFromHTML(projectRoot, assetPath, htmlPath) {
225
+ const currentDirectory = htmlPath ? htmlPath.split('/').slice(0, -1).join('/') : projectRoot;
116
226
  return assetPath.startsWith('./')
117
227
  ? normalize(`${currentDirectory}/${assetPath.slice(2)}`)
118
228
  : normalize(`${currentDirectory}/${assetPath}`);
@@ -3,14 +3,14 @@ import WebSocket, { WebSocketServer } from 'ws';
3
3
  import bindServerToPort from '../setup/bind-server-to-port.js';
4
4
 
5
5
  export const MIME_TYPES = {
6
- html: "text/html; charset=UTF-8",
7
- js: "application/javascript",
8
- css: "text/css",
9
- png: "image/png",
10
- jpg: "image/jpg",
11
- gif: "image/gif",
12
- ico: "image/x-icon",
13
- svg: "image/svg+xml",
6
+ html: 'text/html; charset=UTF-8',
7
+ js: 'application/javascript',
8
+ css: 'text/css',
9
+ png: 'image/png',
10
+ jpg: 'image/jpg',
11
+ gif: 'image/gif',
12
+ ico: 'image/x-icon',
13
+ svg: 'image/svg+xml',
14
14
  };
15
15
 
16
16
  export default class HTTPServer {
@@ -23,13 +23,15 @@ export default class HTTPServer {
23
23
  return handler(req, res);
24
24
  });
25
25
  server = server;
26
- server.on('error', (error) => {
27
- onError(error);
28
- reject(error);
29
- }).once('listening', () => {
30
- onListen(Object.assign({ hostname: '127.0.0.1', server }, config));
31
- resolve(server);
32
- })
26
+ server
27
+ .on('error', (error) => {
28
+ onError(error);
29
+ reject(error);
30
+ })
31
+ .once('listening', () => {
32
+ onListen(Object.assign({ hostname: '127.0.0.1', server }, config));
33
+ resolve(server);
34
+ });
33
35
 
34
36
  server.wss = new WebSocketServer({ server });
35
37
  server.wss.on('error', (error) => {
@@ -37,7 +39,7 @@ export default class HTTPServer {
37
39
  console.trace(error);
38
40
  });
39
41
 
40
- bindServerToPort(server, config)
42
+ bindServerToPort(server, config);
41
43
  });
42
44
  }
43
45
 
@@ -46,7 +48,7 @@ export default class HTTPServer {
46
48
  GET: {},
47
49
  POST: {},
48
50
  DELETE: {},
49
- PUT: {}
51
+ PUT: {},
50
52
  };
51
53
  this.middleware = [];
52
54
  this._server = http.createServer((req, res) => {
@@ -78,13 +80,17 @@ export default class HTTPServer {
78
80
 
79
81
  listen(port = 0, callback = () => {}) {
80
82
  return new Promise((resolve, reject) => {
81
- this._server.listen(port, (error) => {
82
- if (error) {
83
- reject(error);
84
- } else {
85
- resolve(callback());
86
- }
87
- });
83
+ const onError = (err) => {
84
+ this._server.off('listening', onListening);
85
+ reject(err);
86
+ };
87
+ const onListening = () => {
88
+ this._server.off('error', onError);
89
+ resolve(callback());
90
+ };
91
+ this._server.once('error', onError);
92
+ this._server.once('listening', onListening);
93
+ this._server.listen(port);
88
94
  });
89
95
  }
90
96
 
@@ -121,16 +127,20 @@ export default class HTTPServer {
121
127
  path,
122
128
  handler,
123
129
  paramNames: this.extractParamNames(path),
124
- isWildcard: path === '/*'
130
+ isWildcard: path === '/*',
125
131
  };
126
132
  }
127
133
 
128
134
  handleRequest(req, res) {
129
135
  const { method, url } = req;
130
- const matchingRoute = this.findRouteHandler(method, url);
136
+ const urlObj = new URL(url, 'http://localhost');
137
+ const pathname = urlObj.pathname;
138
+ req.path = pathname;
139
+ req.query = Object.fromEntries(urlObj.searchParams);
140
+ const matchingRoute = this.findRouteHandler(method, pathname);
131
141
 
132
142
  if (matchingRoute) {
133
- req.params = this.extractParams(matchingRoute, url);
143
+ req.params = this.extractParams(matchingRoute, pathname);
134
144
  this.runMiddleware(req, res, matchingRoute.handler);
135
145
  } else {
136
146
  res.statusCode = 404;
@@ -159,27 +169,32 @@ export default class HTTPServer {
159
169
  return null;
160
170
  }
161
171
 
162
- return routes[url] || Object.values(routes).find(route => {
163
- const { path, isWildcard } = route;
172
+ return (
173
+ routes[url] ||
174
+ Object.values(routes).find((route) => {
175
+ const { path, isWildcard } = route;
164
176
 
165
- if (!isWildcard && !path.includes(':')) {
166
- return false;
167
- }
177
+ if (!isWildcard && !path.includes(':')) {
178
+ return false;
179
+ }
168
180
 
169
- if (isWildcard || this.matchPathSegments(path, url)) {
170
- if (route.paramNames.length > 0) {
171
- const regexPattern = this.buildRegexPattern(path, route.paramNames);
172
- const regex = new RegExp(`^${regexPattern}$`);
173
- const regexMatches = regex.exec(url);
174
- if (regexMatches) {
175
- route.paramValues = regexMatches.slice(1);
181
+ if (isWildcard || this.matchPathSegments(path, url)) {
182
+ if (route.paramNames.length > 0) {
183
+ const regexPattern = this.buildRegexPattern(path, route.paramNames);
184
+ const regex = new RegExp(`^${regexPattern}$`);
185
+ const regexMatches = regex.exec(url);
186
+ if (regexMatches) {
187
+ route.paramValues = regexMatches.slice(1);
188
+ }
176
189
  }
190
+ return true;
177
191
  }
178
- return true;
179
- }
180
192
 
181
- return false;
182
- }) || routes['/*'] || null;
193
+ return false;
194
+ }) ||
195
+ routes['/*'] ||
196
+ null
197
+ );
183
198
  }
184
199
 
185
200
  matchPathSegments(path, url) {
@@ -217,7 +232,7 @@ export default class HTTPServer {
217
232
  const paramRegex = /:(\w+)/g;
218
233
  const paramMatches = path.match(paramRegex);
219
234
 
220
- return paramMatches ? paramMatches.map(match => match.slice(1)) : [];
235
+ return paramMatches ? paramMatches.map((match) => match.slice(1)) : [];
221
236
  }
222
237
 
223
238
  extractParams(route, url) {
@@ -1,14 +1,5 @@
1
- import resolvePortNumberFor from '../utils/resolve-port-number-for.js';
2
-
3
- // NOTE: there was a race condition between socket.connection and server.listen, check if nanoexpress fixes it
4
1
  export default async function bindServerToPort(server, config) {
5
- try {
6
- let port = await resolvePortNumberFor(config.port);
7
-
8
- await server.listen(port);
9
-
10
- return server;
11
- } catch(e) {
12
- return await bindServerToPort(server, Object.assign(config, { port: config.port + 1 }));
13
- }
2
+ await server.listen(0);
3
+ config.port = server._server.address().port;
4
+ return server;
14
5
  }
@@ -1,23 +1,35 @@
1
1
  import Puppeteer from 'puppeteer';
2
2
  import setupWebServer from './web-server.js';
3
3
  import bindServerToPort from './bind-server-to-port.js';
4
+ import findChrome from '../utils/find-chrome.js';
4
5
 
5
- export default async function setupBrowser(config = {
6
- port: 1234, debug: false, watch: false, timeout: 10000
7
- }, cachedContent) {
6
+ export default async function setupBrowser(
7
+ config = {
8
+ port: 1234,
9
+ debug: false,
10
+ watch: false,
11
+ timeout: 10000,
12
+ },
13
+ cachedContent,
14
+ existingBrowser = null,
15
+ ) {
8
16
  let [server, browser] = await Promise.all([
9
17
  setupWebServer(config, cachedContent),
10
- Puppeteer.launch({
11
- debugger: config.debug || false,
12
- args: ['--no-sandbox', '--disable-gpu', '--remote-debugging-port=0', '--window-size=1440,900'],
13
- executablePath: process.env.CHROME_BIN || null,
14
- headless: 'new',
15
- }),
16
- ]);
17
- let [page, _] = await Promise.all([
18
- browser.newPage(),
19
- bindServerToPort(server, config)
18
+ existingBrowser
19
+ ? Promise.resolve(existingBrowser)
20
+ : Puppeteer.launch({
21
+ debugger: config.debug || false,
22
+ args: [
23
+ '--no-sandbox',
24
+ '--disable-gpu',
25
+ '--remote-debugging-port=0',
26
+ '--window-size=1440,900',
27
+ ],
28
+ executablePath: await findChrome(),
29
+ headless: true,
30
+ }),
20
31
  ]);
32
+ let [page, _] = await Promise.all([browser.newPage(), bindServerToPort(server, config)]);
21
33
 
22
34
  page.on('console', async (msg) => {
23
35
  if (config.debug) {
@@ -28,7 +40,7 @@ export default async function setupBrowser(config = {
28
40
  });
29
41
  page.on('error', (msg) => {
30
42
  try {
31
- throw error;
43
+ throw msg;
32
44
  } catch (e) {
33
45
  console.error(e, e.stack);
34
46
  console.log(e, e.stack);
@@ -49,7 +61,3 @@ export default async function setupBrowser(config = {
49
61
  function turnToObjects(jsHandle) {
50
62
  return jsHandle.jsonValue();
51
63
  }
52
-
53
- // function turnMStoSecond(timeInMS) {
54
- // return (timeInMS / 1000).toFixed(2);
55
- // }
@@ -7,23 +7,21 @@ import parseCliFlags from '../utils/parse-cli-flags.js';
7
7
 
8
8
  export default async function setupConfig() {
9
9
  let projectRoot = await findProjectRoot();
10
- let [projectPackageJSON, cliConfigFlags] = await Promise.all([
11
- readConfigFromPackageJSON(projectRoot),
12
- parseCliFlags(projectRoot)
13
- ]);
10
+ let cliConfigFlags = parseCliFlags(projectRoot);
11
+ let projectPackageJSON = await readConfigFromPackageJSON(projectRoot);
14
12
  let inputs = cliConfigFlags.inputs.concat(readInputsFromPackageJSON(projectPackageJSON));
15
13
  let config = {
16
- projectRoot,
17
- htmlPaths: [],
18
- lastFailedTestFiles: null,
19
- lastRanTestFiles: null,
20
14
  ...defaultProjectConfigValues,
15
+ htmlPaths: [],
21
16
  ...projectPackageJSON.qunitx,
22
17
  ...cliConfigFlags,
23
- inputs
18
+ projectRoot,
19
+ inputs,
20
+ testFileLookupPaths: setupTestFilePaths(projectRoot, inputs),
21
+ lastFailedTestFiles: null,
22
+ lastRanTestFiles: null,
24
23
  };
25
24
  config.htmlPaths = normalizeHTMLPaths(config.projectRoot, config.htmlPaths);
26
- config.testFileLookupPaths = setupTestFilePaths(config.projectRoot, config.inputs);
27
25
  config.fsTree = await setupFSTree(config.testFileLookupPaths, config);
28
26
 
29
27
  return config;
@@ -1,7 +1,12 @@
1
1
  import chokidar from 'chokidar';
2
2
  import kleur from 'kleur';
3
3
 
4
- export default async function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFunc) {
4
+ export default async function setupFileWatchers(
5
+ testFileLookupPaths,
6
+ config,
7
+ onEventFunc,
8
+ onFinishFunc,
9
+ ) {
5
10
  let extensions = ['js', 'ts'];
6
11
  let fileWatchers = testFileLookupPaths.reduce((watcher, watchPath) => {
7
12
  return Object.assign(watcher, {
@@ -9,14 +14,26 @@ export default async function setupFileWatchers(testFileLookupPaths, config, onE
9
14
  if (extensions.some((extension) => path.endsWith(extension))) {
10
15
  mutateFSTree(config.fsTree, event, path);
11
16
 
12
- console.log('#', kleur.magenta().bold('=================================================================='));
17
+ console.log(
18
+ '#',
19
+ kleur
20
+ .magenta()
21
+ .bold('=================================================================='),
22
+ );
13
23
  console.log('#', getEventColor(event), path.split(config.projectRoot)[1]);
14
- console.log('#', kleur.magenta().bold('=================================================================='));
24
+ console.log(
25
+ '#',
26
+ kleur
27
+ .magenta()
28
+ .bold('=================================================================='),
29
+ );
15
30
 
16
31
  if (!global.chokidarBuild) {
17
32
  global.chokidarBuild = true;
18
33
 
19
- let result = extensions.some((extension) => path.endsWith(extension)) ? onEventFunc(event, path) : null;
34
+ let result = extensions.some((extension) => path.endsWith(extension))
35
+ ? onEventFunc(event, path)
36
+ : null;
20
37
 
21
38
  if (!(result instanceof Promise)) {
22
39
  global.chokidarBuild = false;
@@ -35,7 +52,7 @@ export default async function setupFileWatchers(testFileLookupPaths, config, onE
35
52
  .finally(() => (global.chokidarBuild = false));
36
53
  }
37
54
  }
38
- })
55
+ }),
39
56
  });
40
57
  }, {});
41
58
 
@@ -45,7 +62,7 @@ export default async function setupFileWatchers(testFileLookupPaths, config, onE
45
62
  Object.keys(fileWatchers).forEach((watcherKey) => fileWatchers[watcherKey].close());
46
63
 
47
64
  return fileWatchers;
48
- }
65
+ },
49
66
  };
50
67
  }
51
68