qunitx-cli 0.1.1 → 0.5.0

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 (44) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.env +1 -0
  3. package/Makefile +35 -0
  4. package/README.md +120 -49
  5. package/cli.js +1 -1
  6. package/cliff.toml +23 -0
  7. package/demo/demo.gif +0 -0
  8. package/demo/demo.tape +59 -0
  9. package/demo/example-test.js +53 -0
  10. package/demo/failing-test.js +22 -0
  11. package/flake.lock +4 -4
  12. package/flake.nix +33 -4
  13. package/lib/boilerplates/default-project-config-values.js +2 -2
  14. package/lib/boilerplates/test.js +5 -4
  15. package/lib/commands/generate.js +6 -8
  16. package/lib/commands/help.js +8 -8
  17. package/lib/commands/init.js +35 -25
  18. package/lib/commands/run/tests-in-browser.js +97 -67
  19. package/lib/commands/run.js +165 -55
  20. package/lib/servers/http.js +53 -42
  21. package/lib/setup/bind-server-to-port.js +3 -12
  22. package/lib/setup/browser.js +26 -18
  23. package/lib/setup/config.js +8 -10
  24. package/lib/setup/file-watcher.js +23 -6
  25. package/lib/setup/fs-tree.js +29 -27
  26. package/lib/setup/keyboard-events.js +7 -4
  27. package/lib/setup/test-file-paths.js +25 -23
  28. package/lib/setup/web-server.js +87 -61
  29. package/lib/setup/write-output-static-files.js +4 -1
  30. package/lib/tap/display-final-result.js +2 -2
  31. package/lib/tap/display-test-result.js +32 -14
  32. package/lib/utils/find-chrome.js +16 -0
  33. package/lib/utils/find-internal-assets-from-html.js +7 -5
  34. package/lib/utils/find-project-root.js +1 -2
  35. package/lib/utils/indent-string.js +6 -6
  36. package/lib/utils/listen-to-keyboard-key.js +6 -2
  37. package/lib/utils/parse-cli-flags.js +34 -31
  38. package/lib/utils/resolve-port-number-for.js +3 -3
  39. package/lib/utils/run-user-module.js +5 -3
  40. package/lib/utils/search-in-parent-directories.js +4 -1
  41. package/lib/utils/time-counter.js +2 -2
  42. package/package.json +21 -36
  43. package/vendor/qunit.css +7 -7
  44. package/vendor/qunit.js +3772 -3324
@@ -7,9 +7,15 @@ import HTTPServer, { MIME_TYPES } from '../servers/http.js';
7
7
 
8
8
  const fsPromise = fs.promises;
9
9
 
10
- export default async function setupWebServer(config = {
11
- port: 1234, debug: false, watch: false, timeout: 10000
12
- }, cachedContent) {
10
+ export default async function setupWebServer(
11
+ config = {
12
+ port: 1234,
13
+ debug: false,
14
+ watch: false,
15
+ timeout: 10000,
16
+ },
17
+ cachedContent,
18
+ ) {
13
19
  let STATIC_FILES_PATH = path.join(config.projectRoot, config.output);
14
20
  let server = new HTTPServer();
15
21
 
@@ -17,14 +23,21 @@ export default async function setupWebServer(config = {
17
23
  socket.on('message', function message(data) {
18
24
  const { event, details, abort } = JSON.parse(data);
19
25
 
20
- if (event === "connection") {
21
- console.log('TAP version 13');
22
- } else if ((event === 'testEnd') && !abort) {
26
+ if (event === 'connection') {
27
+ if (!config._groupMode) console.log('TAP version 13');
28
+ } else if (event === 'testEnd' && !abort) {
23
29
  if (details.status === 'failed') {
24
30
  config.lastFailedTestFiles = config.lastRanTestFiles;
25
31
  }
26
32
 
27
- TAPDisplayTestResult(config.COUNTER, details)
33
+ TAPDisplayTestResult(config.COUNTER, details);
34
+ } else if (event === 'done') {
35
+ // Signal test completion. TCP ordering guarantees all testEnd messages
36
+ // preceding this on the same connection are already processed by Node.js.
37
+ if (typeof config._testRunDone === 'function') {
38
+ config._testRunDone();
39
+ config._testRunDone = null;
40
+ }
28
41
  }
29
42
  });
30
43
  });
@@ -32,29 +45,43 @@ export default async function setupWebServer(config = {
32
45
  server.get('/', async (req, res) => {
33
46
  let TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
34
47
  let htmlContent = escapeAndInjectTestsToHTML(
35
- replaceAssetPaths(cachedContent.mainHTML.html, cachedContent.mainHTML.filePath, config.projectRoot),
48
+ replaceAssetPaths(
49
+ cachedContent.mainHTML.html,
50
+ cachedContent.mainHTML.filePath,
51
+ config.projectRoot,
52
+ ),
36
53
  TEST_RUNTIME_TO_INJECT,
37
- cachedContent.allTestCode
54
+ cachedContent.allTestCode,
38
55
  );
39
56
 
40
57
  res.write(htmlContent);
41
58
  res.end();
42
59
 
43
- return await fsPromise.writeFile(`${config.projectRoot}/${config.output}/index.html`, htmlContent);
60
+ return await fsPromise.writeFile(
61
+ `${config.projectRoot}/${config.output}/index.html`,
62
+ htmlContent,
63
+ );
44
64
  });
45
65
 
46
66
  server.get('/qunitx.html', async (req, res) => {
47
67
  let TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
48
68
  let htmlContent = escapeAndInjectTestsToHTML(
49
- replaceAssetPaths(cachedContent.mainHTML.html, cachedContent.mainHTML.filePath, config.projectRoot),
69
+ replaceAssetPaths(
70
+ cachedContent.mainHTML.html,
71
+ cachedContent.mainHTML.filePath,
72
+ config.projectRoot,
73
+ ),
50
74
  TEST_RUNTIME_TO_INJECT,
51
- cachedContent.filteredTestCode
75
+ cachedContent.filteredTestCode,
52
76
  );
53
77
 
54
78
  res.write(htmlContent);
55
79
  res.end();
56
80
 
57
- return await fsPromise.writeFile(`${config.projectRoot}/${config.output}/qunitx.html`, htmlContent);
81
+ return await fsPromise.writeFile(
82
+ `${config.projectRoot}/${config.output}/qunitx.html`,
83
+ htmlContent,
84
+ );
58
85
  });
59
86
 
60
87
  server.get('/*', async (req, res) => {
@@ -64,31 +91,35 @@ export default async function setupWebServer(config = {
64
91
  let htmlContent = escapeAndInjectTestsToHTML(
65
92
  possibleDynamicHTML,
66
93
  TEST_RUNTIME_TO_INJECT,
67
- cachedContent.allTestCode
94
+ cachedContent.allTestCode,
68
95
  );
69
96
 
70
97
  res.write(htmlContent);
71
98
  res.end();
72
99
 
73
- return await fsPromise.writeFile(`${config.projectRoot}/${config.output}${req.path}`, htmlContent);
100
+ return await fsPromise.writeFile(
101
+ `${config.projectRoot}/${config.output}${req.path}`,
102
+ htmlContent,
103
+ );
74
104
  }
75
105
 
76
106
  let url = req.url;
77
107
  let requestStartedAt = new Date();
78
- let filePath = (url.endsWith("/") ? [STATIC_FILES_PATH, url, "index.html"] : [STATIC_FILES_PATH, url]).join('');
79
- let statusCode = await pathExists(filePath) ? 200 : 404;
108
+ let filePath = (
109
+ url.endsWith('/') ? [STATIC_FILES_PATH, url, 'index.html'] : [STATIC_FILES_PATH, url]
110
+ ).join('');
111
+ let statusCode = (await pathExists(filePath)) ? 200 : 404;
80
112
 
81
113
  res.writeHead(statusCode, {
82
- "Content-Type": req.headers.accept?.includes('text/html')
114
+ 'Content-Type': req.headers.accept?.includes('text/html')
83
115
  ? MIME_TYPES.html
84
- : MIME_TYPES[path.extname(filePath).substring(1).toLowerCase()] || MIME_TYPES.html
116
+ : MIME_TYPES[path.extname(filePath).substring(1).toLowerCase()] || MIME_TYPES.html,
85
117
  });
86
118
 
87
119
  if (statusCode === 404) {
88
120
  res.end();
89
121
  } else {
90
- fs.createReadStream(filePath)
91
- .pipe(res);
122
+ fs.createReadStream(filePath).pipe(res);
92
123
  }
93
124
 
94
125
  console.log(`# [HTTPServer] GET ${url} ${statusCode} - ${new Date() - requestStartedAt}ms`);
@@ -99,7 +130,7 @@ export default async function setupWebServer(config = {
99
130
 
100
131
  function replaceAssetPaths(html, htmlPath, projectRoot) {
101
132
  let assetPaths = findInternalAssetsFromHTML(html);
102
- let htmlDirectory = htmlPath.split('/').slice(0, -1).join('/')
133
+ let htmlDirectory = htmlPath.split('/').slice(0, -1).join('/');
103
134
 
104
135
  return assetPaths.reduce((result, assetPath) => {
105
136
  let normalizedFullAbsolutePath = path.normalize(`${htmlDirectory}/${assetPath}`);
@@ -116,29 +147,46 @@ function testRuntimeToInject(port, config) {
116
147
  }, 1000);
117
148
 
118
149
  (function() {
119
- function setupWebsocket() {
120
- window.socket = new WebSocket('ws://localhost:${port}');
150
+ let wsRetryCount = 0;
151
+ const WS_MAX_RETRIES = 50; // 500ms total before giving up
152
+
153
+ function setupWebSocket() {
154
+ try {
155
+ window.socket = new WebSocket('ws://localhost:${port}');
156
+ } catch (error) {
157
+ console.log(error);
158
+ retryOrFail();
159
+ return;
160
+ }
161
+
162
+ window.socket.addEventListener('open', function() {
163
+ setupQUnit();
164
+ });
165
+ window.socket.addEventListener('error', function() {
166
+ retryOrFail();
167
+ });
121
168
  window.socket.addEventListener('message', function(messageEvent) {
122
169
  if (!window.IS_PUPPETEER && messageEvent.data === 'refresh') {
123
170
  window.location.reload(true);
124
171
  } else if (window.IS_PUPPETEER && messageEvent.data === 'abort') {
125
172
  window.abortQUnit = true;
126
173
  window.QUnit.config.queue.length = 0;
127
- window.socket.send(JSON.stringify({ event: 'abort' }))
174
+ window.socket.send(JSON.stringify({ event: 'abort' }));
128
175
  }
129
176
  });
130
177
  }
131
178
 
132
- function trySetupWebsocket() {
133
- try {
134
- setupWebsocket();
135
- } catch(error) {
136
- console.log(error);
137
- window.setTimeout(() => trySetupWebsocket(), 10);
179
+ function retryOrFail() {
180
+ wsRetryCount++;
181
+ if (wsRetryCount > WS_MAX_RETRIES) {
182
+ console.log('WebSocket connection failed after ' + WS_MAX_RETRIES + ' retries');
183
+ window.testTimeout = ${config.timeout};
184
+ return;
138
185
  }
186
+ window.setTimeout(setupWebSocket, 10);
139
187
  }
140
188
 
141
- trySetupWebsocket();
189
+ setupWebSocket();
142
190
  })();
143
191
 
144
192
  {{allTestCode}}
@@ -163,13 +211,8 @@ function testRuntimeToInject(port, config) {
163
211
  function setupQUnit() {
164
212
  window.QUNIT_RESULT = { totalTests: 0, finishedTests: 0, currentTest: '' };
165
213
 
166
- if (!window.socket ) {
167
- return window.setTimeout(() => setupQUnit(), 10);
168
- } else if (!window.QUnit || !window.QUnit.moduleStart || window.QUnit.config.queue === 0) {
169
- if (socket.readyState == 0) {
170
- return window.setTimeout(() => setupQUnit(), 10);
171
- }
172
-
214
+ if (!window.QUnit) {
215
+ console.log('QUnit not found after WebSocket connected');
173
216
  window.testTimeout = ${config.timeout};
174
217
  return;
175
218
  }
@@ -211,31 +254,14 @@ function testRuntimeToInject(port, config) {
211
254
  }, 75);
212
255
  });
213
256
 
214
- if ([1, 3].includes(window.socket.readyState)) {
215
- return window.setTimeout(() => window.QUnit.start(), 25);
216
- } else {
217
- let connectionTrialCount = 0;
218
- let connectionInterval = window.setInterval(() => {
219
- if ([1, 3].includes(window.socket.readyState) || connectionTrialCount > 25) {
220
- window.clearInterval(connectionInterval);
221
-
222
- return window.setTimeout(() => window.QUnit.start(), 25);
223
- }
224
-
225
- connectionTrialCount = connectionTrialCount + 1;
226
- }, 10);
227
- }
257
+ window.setTimeout(() => window.QUnit.start(), 25);
228
258
  }
229
-
230
- setupQUnit();
231
259
  </script>`;
232
260
  }
233
261
 
234
262
  function escapeAndInjectTestsToHTML(html, testRuntimeCode, testContentCode) {
235
- return html
236
- .replace('{{content}}',
237
- testRuntimeCode
238
- .replace('{{allTestCode}}', testContentCode)
239
- .replace('</script>', '<\/script>') // NOTE: remove this when simple-html-tokenizer PR gets merged
240
- );
263
+ return html.replace(
264
+ '{{content}}',
265
+ testRuntimeCode.replace('{{allTestCode}}', testContentCode).replace('</script>', '<\/script>'), // NOTE: remove this when simple-html-tokenizer PR gets merged
266
+ );
241
267
  }
@@ -5,7 +5,10 @@ export default async function writeOutputStaticFiles({ projectRoot, output }, ca
5
5
  let htmlRelativePath = staticHTMLKey.replace(`${projectRoot}/`, '');
6
6
 
7
7
  await ensureFolderExists(`${projectRoot}/${output}/${htmlRelativePath}`);
8
- await fs.writeFile(`${projectRoot}/${output}/${htmlRelativePath}`, cachedContent.staticHTMLs[staticHTMLKey]);
8
+ await fs.writeFile(
9
+ `${projectRoot}/${output}/${htmlRelativePath}`,
10
+ cachedContent.staticHTMLs[staticHTMLKey],
11
+ );
9
12
  });
10
13
  let assetPromises = Array.from(cachedContent.assets).map(async (assetAbsolutePath) => {
11
14
  let assetRelativePath = assetAbsolutePath.replace(`${projectRoot}/`, '');
@@ -1,4 +1,4 @@
1
- export default function({ testCount, passCount, skipCount, failCount }, timeTaken) {
1
+ export default function ({ testCount, passCount, skipCount, failCount }, timeTaken) {
2
2
  console.log('');
3
3
  console.log(`1..${testCount}`);
4
4
  console.log(`# tests ${testCount}`);
@@ -12,4 +12,4 @@ export default function({ testCount, passCount, skipCount, failCount }, timeTake
12
12
  console.log(`# duration ${timeTaken}`);
13
13
  console.log('');
14
14
  }
15
- // console.log(details.timeTaken); // runtime
15
+ // console.log(details.timeTaken); // runtime
@@ -1,9 +1,10 @@
1
- import yaml from 'js-yaml'
1
+ import yaml from 'js-yaml';
2
2
  import indentString from '../utils/indent-string.js';
3
3
 
4
4
  // tape TAP output: ['operator', 'stack', 'at', 'expected', 'actual']
5
5
  // ava TAP output: ['message', 'name', 'at', 'assertion', 'values'] // Assertion #5, message
6
- export default function(COUNTER, details) { // NOTE: https://github.com/qunitjs/qunit/blob/master/src/html-reporter/diff.js
6
+ export default function (COUNTER, details) {
7
+ // NOTE: https://github.com/qunitjs/qunit/blob/master/src/html-reporter/diff.js
7
8
  COUNTER.testCount++;
8
9
 
9
10
  if (details.status === 'skipped') {
@@ -13,21 +14,34 @@ export default function(COUNTER, details) { // NOTE: https://github.com/qunitjs/
13
14
  console.log(`not ok ${COUNTER.testCount}`, details.fullName.join(' | '), '# skip');
14
15
  } else if (details.status === 'failed') {
15
16
  COUNTER.failCount++;
16
- console.log(`not ok ${COUNTER.testCount}`, details.fullName.join(' | '), `# (${details.runtime.toFixed(0)} ms)`);
17
+ console.log(
18
+ `not ok ${COUNTER.testCount}`,
19
+ details.fullName.join(' | '),
20
+ `# (${details.runtime.toFixed(0)} ms)`,
21
+ );
17
22
  details.assertions.reduce((errorCount, assertion, index) => {
18
23
  if (!assertion.passed && assertion.todo === false) {
19
24
  COUNTER.errorCount++;
20
25
  let stack = assertion.stack?.match(/\(.+\)/g);
21
26
 
22
27
  console.log(' ---');
23
- console.log(indentString(yaml.dump({
24
- name: `Assertion #${index + 1}`, // TODO: check what happens on runtime errors
25
- actual: assertion.actual ? JSON.parse(JSON.stringify(assertion.actual, getCircularReplacer())) : assertion.actual,
26
- expected: assertion.expected ? JSON.parse(JSON.stringify(assertion.expected, getCircularReplacer())) : assertion.expected,
27
- message: assertion.message || null,
28
- stack: assertion.stack || null,
29
- at: stack ? stack[0].replace('(file://', '').replace(')', '') : null
30
- }), 4));
28
+ console.log(
29
+ indentString(
30
+ yaml.dump({
31
+ name: `Assertion #${index + 1}`, // TODO: check what happens on runtime errors
32
+ actual: assertion.actual
33
+ ? JSON.parse(JSON.stringify(assertion.actual, getCircularReplacer()))
34
+ : assertion.actual,
35
+ expected: assertion.expected
36
+ ? JSON.parse(JSON.stringify(assertion.expected, getCircularReplacer()))
37
+ : assertion.expected,
38
+ message: assertion.message || null,
39
+ stack: assertion.stack || null,
40
+ at: stack ? stack[0].replace('(file://', '').replace(')', '') : null,
41
+ }),
42
+ 4,
43
+ ),
44
+ );
31
45
  console.log(' ...');
32
46
  }
33
47
 
@@ -35,21 +49,25 @@ export default function(COUNTER, details) { // NOTE: https://github.com/qunitjs/
35
49
  }, 0);
36
50
  } else if (details.status === 'passed') {
37
51
  COUNTER.passCount++;
38
- console.log(`ok ${COUNTER.testCount}`, details.fullName.join(' | '), `# (${details.runtime.toFixed(0)} ms)`);
52
+ console.log(
53
+ `ok ${COUNTER.testCount}`,
54
+ details.fullName.join(' | '),
55
+ `# (${details.runtime.toFixed(0)} ms)`,
56
+ );
39
57
  }
40
58
  }
41
59
 
42
60
  function getCircularReplacer() {
43
61
  const ancestors = [];
44
62
  return function (key, value) {
45
- if (typeof value !== "object" || value === null) {
63
+ if (typeof value !== 'object' || value === null) {
46
64
  return value;
47
65
  }
48
66
  while (ancestors.length > 0 && ancestors.at(-1) !== this) {
49
67
  ancestors.pop();
50
68
  }
51
69
  if (ancestors.includes(value)) {
52
- return "[Circular]";
70
+ return '[Circular]';
53
71
  }
54
72
  ancestors.push(value);
55
73
  return value;
@@ -0,0 +1,16 @@
1
+ import { exec } from 'node:child_process';
2
+
3
+ const CANDIDATES = ['google-chrome-stable', 'google-chrome', 'chromium', 'chromium-browser'];
4
+
5
+ export default async function findChrome() {
6
+ if (process.env.CHROME_BIN) return process.env.CHROME_BIN;
7
+
8
+ return Promise.any(
9
+ CANDIDATES.map(
10
+ (name) =>
11
+ new Promise((resolve, reject) =>
12
+ exec(`which ${name}`, (err, stdout) => (err ? reject() : resolve(stdout.trim()))),
13
+ ),
14
+ ),
15
+ ).catch(() => null);
16
+ }
@@ -1,16 +1,18 @@
1
- import cheerio from 'cheerio';
1
+ import { load } from 'cheerio';
2
2
 
3
3
  const ABSOLUTE_URL_REGEX = new RegExp('^(?:[a-z]+:)?//', 'i');
4
4
 
5
5
  export default function findInternalAssetsFromHTML(htmlContent) {
6
- const $ = cheerio.load(htmlContent);
7
- const internalJSFiles = $('script[src]').toArray()
6
+ const $ = load(htmlContent);
7
+ const internalJSFiles = $('script[src]')
8
+ .toArray()
8
9
  .map((scriptNode) => $(scriptNode).attr('src'))
9
10
  .filter((uri) => !ABSOLUTE_URL_REGEX.test(uri));
10
- const internalCSSFiles = $('link[href]').toArray()
11
+ const internalCSSFiles = $('link[href]')
12
+ .toArray()
11
13
  .map((scriptNode) => $(scriptNode).attr('href'))
12
14
  .filter((uri) => !ABSOLUTE_URL_REGEX.test(uri));
13
15
 
14
16
  return internalCSSFiles.concat(internalJSFiles);
15
- // TODO: maybe needs normalization ? .map((fileReferencePath) => fileReferencePath.replace('/assets', `${projectRoot}/tmp/assets`));
17
+ // TODO: maybe needs normalization ? .map((fileReferencePath) => fileReferencePath.replace('/assets', `${projectRoot}/tmp/assets`));
16
18
  }
@@ -1,7 +1,7 @@
1
1
  import process from 'node:process';
2
2
  import searchInParentDirectories from './search-in-parent-directories.js';
3
3
 
4
- export default async function() {
4
+ export default async function () {
5
5
  try {
6
6
  let absolutePath = await searchInParentDirectories('.', 'package.json');
7
7
  if (!absolutePath.includes('package.json')) {
@@ -14,4 +14,3 @@ export default async function() {
14
14
  process.exit(1);
15
15
  }
16
16
  }
17
-
@@ -1,11 +1,11 @@
1
1
  export default function indentString(string, count = 1, options = {}) {
2
- const { indent = ' ', includeEmptyLines = false } = options;
2
+ const { indent = ' ', includeEmptyLines = false } = options;
3
3
 
4
- if (count <= 0) {
5
- return string;
6
- }
4
+ if (count <= 0) {
5
+ return string;
6
+ }
7
7
 
8
- const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
8
+ const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
9
9
 
10
- return string.replace(regex, indent.repeat(count));
10
+ return string.replace(regex, indent.repeat(count));
11
11
  }
@@ -5,12 +5,16 @@ let targetInputs = {};
5
5
  let inputs = [];
6
6
  let listenerAdded = false;
7
7
 
8
- export default function listenToKeyboardKey(inputString, closure, options = { caseSensitive: false }) {
8
+ export default function listenToKeyboardKey(
9
+ inputString,
10
+ closure,
11
+ options = { caseSensitive: false },
12
+ ) {
9
13
  stdin.setRawMode(true);
10
14
  stdin.resume();
11
15
  stdin.setEncoding('utf8');
12
16
  if (!listenerAdded) {
13
- stdin.on('data', function(key){
17
+ stdin.on('data', function (key) {
14
18
  if (key === '\u0003') {
15
19
  process.exit(); // so node process doesnt trap Control-C
16
20
  }
@@ -1,44 +1,47 @@
1
1
  // { inputs: [], debug: true, watch: true, failFast: true, htmlPaths: [], output }
2
- export default async function(projectRoot) {
3
- const providedFlags = process.argv.slice(2).reduce((result, arg) => {
4
- if (arg.startsWith('--debug')) {
5
- return Object.assign(result, { debug: parseBoolean(arg.split('=')[1]) });
6
- } else if (arg.startsWith('--watch')) {
7
- return Object.assign(result, { watch: parseBoolean(arg.split('=')[1]) });
8
- } else if (arg.startsWith('--failfast') || arg.startsWith('--failFast')) {
9
- return Object.assign(result, { failFast: parseBoolean(arg.split('=')[1]) });
10
- } else if (arg.startsWith('--timeout')) {
11
- return Object.assign(result, { timeout: arg.split('=')[1] || 10000 });
12
- } else if (arg.startsWith('--output')) {
13
- return Object.assign(result, { output: arg.split('=')[1] });
14
- } else if (arg.endsWith('.html')) {
15
- if (result.htmlPaths) {
16
- result.htmlPaths.push(arg);
17
- } else {
18
- result.htmlPaths = [arg];
2
+ export default function (projectRoot) {
3
+ const providedFlags = process.argv.slice(2).reduce(
4
+ (result, arg) => {
5
+ if (arg.startsWith('--debug')) {
6
+ return Object.assign(result, { debug: parseBoolean(arg.split('=')[1]) });
7
+ } else if (arg.startsWith('--watch')) {
8
+ return Object.assign(result, { watch: parseBoolean(arg.split('=')[1]) });
9
+ } else if (arg.startsWith('--failfast') || arg.startsWith('--failFast')) {
10
+ return Object.assign(result, { failFast: parseBoolean(arg.split('=')[1]) });
11
+ } else if (arg.startsWith('--timeout')) {
12
+ return Object.assign(result, { timeout: arg.split('=')[1] || 10000 });
13
+ } else if (arg.startsWith('--output')) {
14
+ return Object.assign(result, { output: arg.split('=')[1] });
15
+ } else if (arg.endsWith('.html')) {
16
+ if (result.htmlPaths) {
17
+ result.htmlPaths.push(arg);
18
+ } else {
19
+ result.htmlPaths = [arg];
20
+ }
21
+
22
+ return result;
23
+ } else if (arg.startsWith('--port')) {
24
+ return Object.assign(result, { port: Number(arg.split('=')[1]) });
25
+ } else if (arg.startsWith('--before')) {
26
+ return Object.assign(result, { before: parseModule(arg.split('=')[1]) });
27
+ } else if (arg.startsWith('--after')) {
28
+ return Object.assign(result, { after: parseModule(arg.split('=')[1]) });
19
29
  }
20
30
 
21
- return result;
22
- } else if (arg.startsWith('--port')) {
23
- return Object.assign(result, { port: Number(arg.split('=')[1]) });
24
- } else if (arg.startsWith('--before')) {
25
- return Object.assign(result, { before: parseModule(arg.split('=')[1]) });
26
- } else if (arg.startsWith('--after')) {
27
- return Object.assign(result, { after: parseModule(arg.split('=')[1]) });
28
- }
29
-
30
- // maybe set watch depth via micromatch(so incl metadata)
31
- result.inputs.add(arg.startsWith(projectRoot) ? arg : `${process.cwd()}/${arg}`);
31
+ // maybe set watch depth via micromatch(so incl metadata)
32
+ result.inputs.add(arg.startsWith(projectRoot) ? arg : `${process.cwd()}/${arg}`);
32
33
 
33
- return result;
34
- }, { inputs: new Set([]) });
34
+ return result;
35
+ },
36
+ { inputs: new Set([]) },
37
+ );
35
38
 
36
39
  providedFlags.inputs = Array.from(providedFlags.inputs);
37
40
 
38
41
  return providedFlags;
39
42
  }
40
43
 
41
- function parseBoolean(result, defaultValue=true) {
44
+ function parseBoolean(result, defaultValue = true) {
42
45
  if (result === 'true') {
43
46
  return true;
44
47
  } else if (result === 'false') {
@@ -3,7 +3,7 @@ export default async function resolvePortNumberFor(portNumber) {
3
3
  return portNumber;
4
4
  }
5
5
 
6
- return (await resolvePortNumberFor(portNumber + 1));
6
+ return await resolvePortNumberFor(portNumber + 1);
7
7
  }
8
8
 
9
9
  function portIsAvailable(portNumber) {
@@ -11,13 +11,13 @@ function portIsAvailable(portNumber) {
11
11
  const net = await import('net');
12
12
  const server = net.createServer();
13
13
 
14
- server.once('error', function(err) {
14
+ server.once('error', function (err) {
15
15
  if (err.code === 'EADDRINUSE') {
16
16
  resolve(false);
17
17
  }
18
18
  });
19
19
 
20
- server.once('listening', function() {
20
+ server.once('listening', function () {
21
21
  server.close();
22
22
  resolve(true);
23
23
  });
@@ -4,9 +4,11 @@ export default async function runUserModule(modulePath, params, scriptPosition)
4
4
  try {
5
5
  let func = await import(modulePath);
6
6
  if (func) {
7
- func.default ?
8
- await func.default(params) :
9
- typeof func === 'function' ? await func(params) : null;
7
+ func.default
8
+ ? await func.default(params)
9
+ : typeof func === 'function'
10
+ ? await func(params)
11
+ : null;
10
12
  }
11
13
  } catch (error) {
12
14
  console.log('#', kleur.red(`QUnitX ${scriptPosition} script failed:`));
@@ -9,7 +9,10 @@ async function searchInParentDirectories(directory, targetEntry) {
9
9
  return;
10
10
  }
11
11
 
12
- return await searchInParentDirectories(directory.slice(0, directory.lastIndexOf('/')), targetEntry);
12
+ return await searchInParentDirectories(
13
+ directory.slice(0, directory.lastIndexOf('/')),
14
+ targetEntry,
15
+ );
13
16
  }
14
17
 
15
18
  export default searchInParentDirectories;
@@ -1,8 +1,8 @@
1
- export default function() {
1
+ export default function () {
2
2
  const startTime = new Date();
3
3
 
4
4
  return {
5
5
  startTime: startTime,
6
- stop: () => +(new Date()) - (+startTime)
6
+ stop: () => +new Date() - +startTime,
7
7
  };
8
8
  }