tape-six 0.12.0 → 0.12.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.
package/README.md CHANGED
@@ -32,6 +32,7 @@ If you are familiar with other TAP-based libraries you'll feel right at home.
32
32
 
33
33
  The most recent releases:
34
34
 
35
+ * 0.12.1 *Minor Deno-related refactoring, fixed the way tests are triggered.*
35
36
  * 0.12.0 *Removed data to avoid serializing non-serializable objects.*
36
37
  * 0.11.0 *Minor improvements to the server: temporary redirects, a hyperlink to the web app.*
37
38
  * 0.10.0 *Refactored test runners, refactored stopping tests on failure, added JSONL reporter, fixed bugs.*
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+
3
+ import process from 'node:process';
4
+ import {fileURLToPath} from 'node:url';
5
+
6
+ import {resolveTests, resolvePatterns} from '../src/utils/config.js';
7
+
8
+ import {getReporter, setReporter} from '../src/test.js';
9
+ import State, {StopTest} from '../src/State.js';
10
+ import TapReporter from '../src/TapReporter.js';
11
+ import {selectTimer} from '../src/utils/timer.js';
12
+
13
+ import TestWorker from '../src/node/TestWorker.js';
14
+
15
+ const options = {},
16
+ rootFolder = process.cwd();
17
+
18
+ let flags = '',
19
+ parallel = '',
20
+ files = [];
21
+
22
+ const showSelf = () => {
23
+ const self = new URL(import.meta.url);
24
+ if (self.protocol === 'file:') {
25
+ console.log(fileURLToPath(self));
26
+ } else {
27
+ console.log(self);
28
+ }
29
+ process.exit(0);
30
+ };
31
+
32
+ const config = () => {
33
+ if (process.argv.includes('--self')) showSelf();
34
+
35
+ const optionNames = {
36
+ f: 'failureOnly',
37
+ t: 'showTime',
38
+ b: 'showBanner',
39
+ d: 'showData',
40
+ o: 'failOnce',
41
+ n: 'showAssertNumber',
42
+ c: 'hasColors'
43
+ };
44
+
45
+ let flagIsSet = false,
46
+ parIsSet = false;
47
+
48
+ for (let i = 2; i < process.argv.length; ++i) {
49
+ const arg = process.argv[i];
50
+ if (arg == '-f' || arg == '--flags') {
51
+ if (++i < process.argv.length) {
52
+ flags = process.argv[i];
53
+ flagIsSet = true;
54
+ }
55
+ continue;
56
+ }
57
+ if (arg == '-p' || arg == '--par') {
58
+ if (++i < process.argv.length) {
59
+ parallel = process.argv[i];
60
+ parIsSet = true;
61
+ if (!parallel || isNaN(parallel)) {
62
+ parallel = '';
63
+ parIsSet = false;
64
+ }
65
+ }
66
+ continue;
67
+ }
68
+ files.push(arg);
69
+ }
70
+
71
+ if (!flagIsSet) {
72
+ flags = process.env.TAPE6_FLAGS || flags;
73
+ }
74
+ for (let i = 0; i < flags.length; ++i) {
75
+ const option = flags[i].toLowerCase(),
76
+ name = optionNames[option];
77
+ if (typeof name == 'string') options[name] = option !== flags[i];
78
+ }
79
+
80
+ if (!parIsSet) {
81
+ parallel = process.env.TAPE6_PAR || parallel;
82
+ }
83
+ if (parallel) {
84
+ parallel = Math.max(0, +parallel);
85
+ if (parallel === Infinity) parallel = 0;
86
+ } else {
87
+ parallel = 0;
88
+ }
89
+ if (!parallel) parallel = navigator.hardwareConcurrency;
90
+ };
91
+
92
+ const init = async () => {
93
+ let reporter = getReporter();
94
+ if (!reporter) {
95
+ if (process.env.TAPE6_JSONL) {
96
+ const JSONLReporter = (await import('../src/JSONLReporter.js')).default,
97
+ jsonlReporter = new JSONLReporter(options);
98
+ reporter = jsonlReporter.report.bind(jsonlReporter);
99
+ } else if (!process.env.TAPE6_TAP) {
100
+ const TTYReporter = (await import('../src/TTYReporter.js')).default,
101
+ ttyReporter = new TTYReporter(options);
102
+ ttyReporter.testCounter = -2;
103
+ ttyReporter.technicalDepth = 1;
104
+ reporter = ttyReporter.report.bind(ttyReporter);
105
+ }
106
+ if (!reporter) {
107
+ const tapReporter = new TapReporter({useJson: true});
108
+ reporter = tapReporter.report.bind(tapReporter);
109
+ }
110
+ setReporter(reporter);
111
+ }
112
+
113
+ if (files.length) {
114
+ files = await resolvePatterns(rootFolder, files);
115
+ } else {
116
+ files = await resolveTests(rootFolder, 'node');
117
+ }
118
+ };
119
+
120
+ const safeEmit = rootState => event => {
121
+ try {
122
+ rootState.emit(event);
123
+ } catch (error) {
124
+ if (!(error instanceof StopTest)) throw error;
125
+ }
126
+ };
127
+
128
+ const main = async () => {
129
+ config();
130
+ await init();
131
+ await selectTimer();
132
+
133
+ process.on('uncaughtException', (error, origin) =>
134
+ console.error('UNHANDLED ERROR:', origin, error)
135
+ );
136
+
137
+ const rootState = new State(null, {callback: getReporter(), failOnce: options.failOnce}),
138
+ worker = new TestWorker(safeEmit(rootState), parallel, options);
139
+
140
+ rootState.emit({type: 'test', test: 0, time: rootState.timer.now()});
141
+
142
+ await new Promise(resolve => {
143
+ worker.done = () => resolve();
144
+ worker.execute(files);
145
+ });
146
+
147
+ rootState.emit({
148
+ type: 'end',
149
+ test: 0,
150
+ time: rootState.timer.now(),
151
+ fail: rootState.failed > 0,
152
+ data: rootState
153
+ });
154
+
155
+ process.exit(rootState.failed > 0 ? 1 : 0);
156
+ };
157
+
158
+ main().catch(error => console.error('ERROR:', error));
@@ -5,11 +5,12 @@ import {fileURLToPath} from 'node:url';
5
5
 
6
6
  const requestedRuntime =
7
7
  {
8
- node: 'tape6.js',
8
+ node: 'tape6-node.js',
9
9
  deno: 'tape6-deno.js',
10
10
  bun: 'tape6-bun.js',
11
11
  server: 'tape6-server.js',
12
- runner: 'tape6-runner.js'
12
+ runner: 'tape6-runner.js',
13
+ main: 'tape6.js'
13
14
  }[process.argv[2]];
14
15
 
15
16
  const runtime = requestedRuntime || 'tape6.js',
package/bin/tape6.js CHANGED
@@ -3,156 +3,21 @@
3
3
  import process from 'node:process';
4
4
  import {fileURLToPath} from 'node:url';
5
5
 
6
- import {resolveTests, resolvePatterns} from '../src/utils/config.js';
7
-
8
- import {getReporter, setReporter} from '../src/test.js';
9
- import State, {StopTest} from '../src/State.js';
10
- import TapReporter from '../src/TapReporter.js';
11
- import {selectTimer} from '../src/utils/timer.js';
12
-
13
- import TestWorker from '../src/node/TestWorker.js';
14
-
15
- const options = {},
16
- rootFolder = process.cwd();
17
-
18
- let flags = '',
19
- parallel = '',
20
- files = [];
21
-
22
- const showSelf = () => {
6
+ if (process.argv.includes('--self')) {
23
7
  const self = new URL(import.meta.url);
24
8
  if (self.protocol === 'file:') {
25
9
  console.log(fileURLToPath(self));
26
10
  } else {
27
11
  console.log(self);
28
12
  }
29
- process.exit(0);
30
- };
31
-
32
- const config = () => {
33
- if (process.argv.includes('--self')) showSelf();
34
-
35
- const optionNames = {
36
- f: 'failureOnly',
37
- t: 'showTime',
38
- b: 'showBanner',
39
- d: 'showData',
40
- o: 'failOnce',
41
- n: 'showAssertNumber',
42
- c: 'hasColors'
43
- };
44
-
45
- let flagIsSet = false,
46
- parIsSet = false;
47
-
48
- for (let i = 2; i < process.argv.length; ++i) {
49
- const arg = process.argv[i];
50
- if (arg == '-f' || arg == '--flags') {
51
- if (++i < process.argv.length) {
52
- flags = process.argv[i];
53
- flagIsSet = true;
54
- }
55
- continue;
56
- }
57
- if (arg == '-p' || arg == '--par') {
58
- if (++i < process.argv.length) {
59
- parallel = process.argv[i];
60
- parIsSet = true;
61
- if (!parallel || isNaN(parallel)) {
62
- parallel = '';
63
- parIsSet = false;
64
- }
65
- }
66
- continue;
67
- }
68
- files.push(arg);
69
- }
70
-
71
- if (!flagIsSet) {
72
- flags = process.env.TAPE6_FLAGS || flags;
73
- }
74
- for (let i = 0; i < flags.length; ++i) {
75
- const option = flags[i].toLowerCase(),
76
- name = optionNames[option];
77
- if (typeof name == 'string') options[name] = option !== flags[i];
78
- }
79
-
80
- if (!parIsSet) {
81
- parallel = process.env.TAPE6_PAR || parallel;
82
- }
83
- if (parallel) {
84
- parallel = Math.max(0, +parallel);
85
- if (parallel === Infinity) parallel = 0;
13
+ } else {
14
+ if (typeof Deno == 'object') {
15
+ await import('./tape6-deno.js');
16
+ } else if (typeof Bun == 'object') {
17
+ await import('./tape6-bun.js');
18
+ } else if (typeof process == 'object' && process.versions?.node) {
19
+ await import('./tape6-node.js');
86
20
  } else {
87
- parallel = 0;
21
+ throw new Error('tape6 is not supported in this environment');
88
22
  }
89
- if (!parallel) parallel = navigator.hardwareConcurrency;
90
- };
91
-
92
- const init = async () => {
93
- let reporter = getReporter();
94
- if (!reporter) {
95
- if (process.env.TAPE6_JSONL) {
96
- const JSONLReporter = (await import('../src/JSONLReporter.js')).default,
97
- jsonlReporter = new JSONLReporter(options);
98
- reporter = jsonlReporter.report.bind(jsonlReporter);
99
- } else if (!process.env.TAPE6_TAP) {
100
- const TTYReporter = (await import('../src/TTYReporter.js')).default,
101
- ttyReporter = new TTYReporter(options);
102
- ttyReporter.testCounter = -2;
103
- ttyReporter.technicalDepth = 1;
104
- reporter = ttyReporter.report.bind(ttyReporter);
105
- }
106
- if (!reporter) {
107
- const tapReporter = new TapReporter({useJson: true});
108
- reporter = tapReporter.report.bind(tapReporter);
109
- }
110
- setReporter(reporter);
111
- }
112
-
113
- if (files.length) {
114
- files = await resolvePatterns(rootFolder, files);
115
- } else {
116
- files = await resolveTests(rootFolder, 'node');
117
- }
118
- };
119
-
120
- const safeEmit = rootState => event => {
121
- try {
122
- rootState.emit(event);
123
- } catch (error) {
124
- if (!(error instanceof StopTest)) throw error;
125
- }
126
- };
127
-
128
- const main = async () => {
129
- config();
130
- await init();
131
- await selectTimer();
132
-
133
- process.on('uncaughtException', (error, origin) =>
134
- console.error('UNHANDLED ERROR:', origin, error)
135
- );
136
-
137
- const rootState = new State(null, {callback: getReporter(), failOnce: options.failOnce}),
138
- worker = new TestWorker(safeEmit(rootState), parallel, options);
139
-
140
- rootState.emit({type: 'test', test: 0, time: rootState.timer.now()});
141
-
142
- await new Promise(resolve => {
143
- worker.done = () => resolve();
144
- worker.execute(files);
145
- });
146
-
147
- rootState.emit({
148
- type: 'end',
149
- test: 0,
150
- time: rootState.timer.now(),
151
- fail: rootState.failed > 0,
152
- data: rootState
153
- });
154
-
155
- process.exit(rootState.failed > 0 ? 1 : 0);
156
- };
157
-
158
- main().catch(error => console.error('ERROR:', error));
23
+ }
package/index.js CHANGED
@@ -5,7 +5,8 @@ import {
5
5
  getReporter,
6
6
  setReporter,
7
7
  runTests,
8
- getConfiguredFlag
8
+ getConfiguredFlag,
9
+ registerNotifyCallback
9
10
  } from './src/test.js';
10
11
  import defer from './src/utils/defer.js';
11
12
  import State from './src/State.js';
@@ -21,9 +22,7 @@ const optionNames = {
21
22
  c: 'hasColors'
22
23
  };
23
24
 
24
- defer(async () => {
25
- if (getConfiguredFlag()) return; // bail out => somebody else is running the show
26
-
25
+ const init = async () => {
27
26
  const isNode = typeof process == 'object' && process.versions?.node,
28
27
  isDeno = typeof Deno == 'object',
29
28
  isBun = typeof Bun == 'object',
@@ -100,9 +99,19 @@ defer(async () => {
100
99
  setReporter(reporter);
101
100
  }
102
101
 
103
- const rootState = new State(null, {callback: reporter, failOnce: options.failOnce});
102
+ return {reporter, options};
103
+ };
104
+
105
+ let settings = null;
106
+
107
+ const testCallback = async () => {
108
+ if (!settings) settings = await init();
109
+
110
+ const {reporter, options} = settings,
111
+ rootState = new State(null, {callback: reporter, failOnce: options.failOnce});
104
112
 
105
113
  rootState.emit({type: 'test', test: 0, time: rootState.timer.now()});
114
+
106
115
  for (;;) {
107
116
  const tests = getTests();
108
117
  if (!tests.length) break;
@@ -111,6 +120,7 @@ defer(async () => {
111
120
  if (!canContinue) break;
112
121
  await new Promise(resolve => defer(resolve));
113
122
  }
123
+
114
124
  rootState.emit({
115
125
  type: 'end',
116
126
  test: 0,
@@ -119,15 +129,22 @@ defer(async () => {
119
129
  data: rootState
120
130
  });
121
131
 
122
- if (isDeno) {
123
- Deno.exit(rootState.failed > 0 ? 1 : 0);
124
- } else if (isBun) {
125
- process.exit(rootState.failed > 0 ? 1 : 0);
126
- } else if (isNode) {
127
- process.exit(rootState.failed > 0 ? 1 : 0);
132
+ if (typeof Deno == 'object') {
133
+ rootState.failed > 0 && Deno.exit(1);
134
+ } else if (typeof Bun == 'object') {
135
+ rootState.failed > 0 && process.exit(1);
136
+ } else if (typeof process == 'object' && process.versions?.node) {
137
+ rootState.failed > 0 && process.exit(1);
128
138
  } else if (typeof __tape6_reportResults == 'function') {
129
139
  __tape6_reportResults(rootState.failed > 0 ? 'failure' : 'success');
130
140
  }
131
- });
141
+
142
+ registerNotifyCallback(testCallback); // register self again
143
+ };
144
+
145
+ if (!getConfiguredFlag()) {
146
+ // if nobody is running the show
147
+ registerNotifyCallback(testCallback);
148
+ }
132
149
 
133
150
  export default test;
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "tape-six",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "TAP the test harness for the modern JavaScript (ES6).",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "module": "index.js",
8
8
  "exports": {
9
- ".": "./index.js"
9
+ ".": "./index.js",
10
+ "./bin/*": "./bin/*"
10
11
  },
11
12
  "bin": {
12
13
  "tape6": "bin/tape6.js",
@@ -62,6 +63,6 @@
62
63
  }
63
64
  },
64
65
  "devDependencies": {
65
- "puppeteer": "^23.4.0"
66
+ "puppeteer": "^23.4.1"
66
67
  }
67
68
  }
@@ -22,12 +22,7 @@ export default class TestWorker extends EventServer {
22
22
  try {
23
23
  this.report(id, msg);
24
24
  } catch (error) {
25
- if (error instanceof StopTest) {
26
- console.error('# immediate StopTest:', error.message || 'StopTest is activated');
27
- worker.terminate();
28
- process.exit(1);
29
- }
30
- throw error;
25
+ if (!(error instanceof StopTest)) throw error;
31
26
  }
32
27
  if (msg.type === 'end' && msg.test === 0) {
33
28
  this.close(id);
@@ -26,12 +26,7 @@ export default class TestWorker extends EventServer {
26
26
  try {
27
27
  this.report(id, msg);
28
28
  } catch (error) {
29
- if (error instanceof StopTest) {
30
- console.error('# immediate StopTest:', error.message || 'StopTest is activated');
31
- worker.terminate();
32
- Deno.exit(1);
33
- }
34
- throw error;
29
+ if (!(error instanceof StopTest)) throw error;
35
30
  }
36
31
  if (msg.type === 'end' && msg.test === 0) {
37
32
  this.close(id);
package/src/test.js CHANGED
@@ -4,15 +4,25 @@ import Tester from './Tester.js';
4
4
  import Deferred from './utils/Deferred.js';
5
5
  import timeout from './utils/timeout.js';
6
6
  import {formatTime} from './utils/formatters.js';
7
+ import defer from './utils/defer.js';
7
8
 
8
9
  let tests = [],
9
10
  reporter = null,
10
11
  testCounter = 0,
11
- isConfigured = false;
12
+ isConfigured = false,
13
+ notifyCallback = null;
12
14
 
13
15
  export const getConfiguredFlag = () => isConfigured;
14
16
  export const setConfiguredFlag = value => (isConfigured = !!value);
15
17
 
18
+ export const registerNotifyCallback = callback => {
19
+ if (tests.length) {
20
+ defer(callback);
21
+ } else {
22
+ notifyCallback = callback;
23
+ }
24
+ };
25
+
16
26
  const processArgs = (name, options, testFn) => {
17
27
  // normalize arguments
18
28
  if (typeof name == 'function') {
@@ -55,7 +65,11 @@ export const test = async (name, options, testFn) => {
55
65
  isTimerSet = true;
56
66
  }
57
67
  const deferred = new Deferred();
58
- tests.push({options, deferred});
68
+ if (tests.push({options, deferred}) === 1 && notifyCallback) {
69
+ defer(notifyCallback);
70
+ notifyCallback = null;
71
+ }
72
+
59
73
  return deferred.promise;
60
74
  };
61
75