supertape 6.7.1 → 6.8.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.
package/ChangeLog CHANGED
@@ -1,3 +1,32 @@
1
+ 2021.09.24, v6.8.0
2
+
3
+ fix:
4
+ - (supertape) check that no assertions called after t.end()
5
+
6
+ feature:
7
+ - (supertape) add support of check assertions count
8
+ - (@supertape/operator-stub) add calledBefore, calledAfter
9
+ - (@supertape/operator-stub) notCalled: add additional check: called with args
10
+
11
+
12
+ 2021.09.20, v6.7.4
13
+
14
+ fix:
15
+ - (supertape) line number when tested function throws
16
+
17
+
18
+ 2021.09.20, v6.7.3
19
+
20
+ fix:
21
+ - (supertape) test should have ability to override validations, like checkDuplicates
22
+
23
+
24
+ 2021.09.19, v6.7.2
25
+
26
+ fix:
27
+ - (supertape) validator: check scope: add support of slash
28
+
29
+
1
30
  2021.09.18, v6.7.1
2
31
 
3
32
  fix:
package/README.md CHANGED
@@ -46,6 +46,8 @@ Options
46
46
  -v, --version output version information and exit
47
47
  -f, --format use a specific output format - default: progress-bar/tap on CI
48
48
  -r, --require require module
49
+ --check-scopes check that messages contains scope: 'scope: message'
50
+ --check-assertions-count check that assertion count is no more then 1
49
51
  --no-check-duplicates do not check messages for duplicates
50
52
  ```
51
53
 
@@ -53,7 +55,8 @@ Options
53
55
 
54
56
  - `SUPERTAPE_TIMEOUT` - timeout for long running processes;
55
57
  - `SUPERTAPE_CHECK_DUPLICATES` - toggle check duplicates;
56
- - `SUPERTAPE_CHECK_SCOPES` - check that test message has a scope: `scope: subject`
58
+ - `SUPERTAPE_CHECK_SCOPES` - check that test message has a scope: `scope: subject`;
59
+ - `SUPERTAPE_CHECK_ASSERTIONS_COUNT` - check that assertion count is no more then 1;
57
60
 
58
61
  ```js
59
62
  test('tape: error', (t) => {
package/help.json CHANGED
@@ -4,5 +4,6 @@
4
4
  "-f, --format ": "use a specific output format - default: progress-bar/tap on CI",
5
5
  "-r, --require ": "require module",
6
6
  "--check-scopes ": "check that messages contains scope: 'scope: message'",
7
+ "--check-assertions-count ": "check that assertion count is no more then 1",
7
8
  "--no-check-duplicates ": "do not check messages for duplicates"
8
9
  }
package/lib/cli.js CHANGED
@@ -33,6 +33,7 @@ const filesCount = fullstore(0);
33
33
  const {
34
34
  SUPERTAPE_CHECK_DUPLICATES = '1',
35
35
  SUPERTAPE_CHECK_SCOPES = '0',
36
+ SUPERTAPE_CHECK_ASSERTIONS_COUNT = '0',
36
37
  } = process.env;
37
38
 
38
39
  module.exports = async ({argv, cwd, stdout, stderr, exit}) => {
@@ -89,6 +90,7 @@ const yargsOptions = {
89
90
  'help',
90
91
  'check-duplicates',
91
92
  'check-scopes',
93
+ 'check-assertions-count',
92
94
  ],
93
95
  alias: {
94
96
  version: 'v',
@@ -97,12 +99,14 @@ const yargsOptions = {
97
99
  require: 'r',
98
100
  checkDuplicates: 'd',
99
101
  checkScopes: 's',
102
+ checkAssertionsCount: 'a',
100
103
  },
101
104
  default: {
102
105
  format: 'progress-bar',
103
106
  require: [],
104
107
  checkDuplicates: SUPERTAPE_CHECK_DUPLICATES !== '0',
105
108
  checkScopes: SUPERTAPE_CHECK_SCOPES === '1',
109
+ checkAssertionsCount: SUPERTAPE_CHECK_ASSERTIONS_COUNT === '1',
106
110
  },
107
111
  };
108
112
 
@@ -148,6 +152,7 @@ async function cli({argv, cwd, stdout, isStop}) {
148
152
  format,
149
153
  checkDuplicates,
150
154
  checkScopes,
155
+ checkAssertionsCount,
151
156
  } = args;
152
157
 
153
158
  supertape.init({
@@ -157,6 +162,7 @@ async function cli({argv, cwd, stdout, isStop}) {
157
162
  isStop,
158
163
  checkDuplicates,
159
164
  checkScopes,
165
+ checkAssertionsCount,
160
166
  });
161
167
 
162
168
  supertape.createStream().pipe(stdout);
package/lib/operators.mjs CHANGED
@@ -14,7 +14,6 @@ const isFn = (a) => typeof a === 'function';
14
14
  const isStr = (a) => typeof a === 'string';
15
15
  const isObj = (a) => typeof a === 'object';
16
16
 
17
- // backward compatibility or maybe formatters support
18
17
  const end = () => {};
19
18
 
20
19
  const ok = (actual, message = 'should be truthy') => ({
@@ -170,6 +169,21 @@ const initOperator = (runnerState) => (name) => {
170
169
  };
171
170
 
172
171
  return (...a) => {
172
+ if (name === 'end') {
173
+ runnerState.isEnded(true);
174
+ return end;
175
+ }
176
+
177
+ runnerState.incAssertionsCount();
178
+
179
+ if (runnerState.isEnded()) {
180
+ return run(
181
+ 'fail',
182
+ runnerState,
183
+ operators.fail(`Cannot run assertions after 't.end()' called`),
184
+ );
185
+ }
186
+
173
187
  const testState = fn(...a);
174
188
  return run(name, runnerState, testState);
175
189
  };
@@ -223,13 +237,15 @@ function run(name, {formatter, count, incCount, incPassed, incFailed}, testState
223
237
  });
224
238
  }
225
239
 
226
- export const initOperators = ({formatter, count, incCount, incPassed, incFailed, extensions}) => {
240
+ export const initOperators = ({formatter, count, incCount, incPassed, incFailed, incAssertionsCount, isEnded, extensions}) => {
227
241
  const operator = initOperator({
228
242
  formatter,
229
243
  count,
230
244
  incCount,
231
245
  incPassed,
232
246
  incFailed,
247
+ isEnded,
248
+ incAssertionsCount,
233
249
  });
234
250
 
235
251
  const extendedOperators = {};
@@ -251,7 +267,7 @@ export const initOperators = ({formatter, count, incCount, incPassed, incFailed,
251
267
  comment: comment({formatter}),
252
268
  match: operator('match'),
253
269
  notMatch: operator('notMatch'),
254
- end,
270
+ end: operator('end'),
255
271
 
256
272
  ...extendedOperators,
257
273
  };
package/lib/run-tests.js CHANGED
@@ -3,9 +3,8 @@
3
3
  const fullstore = require('fullstore');
4
4
  const wraptile = require('wraptile');
5
5
  const tryToCatch = require('try-to-catch');
6
- const once = require('once');
7
-
8
6
  const isDebug = require('./is-debug');
7
+
9
8
  const {createValidator} = require('./validator');
10
9
 
11
10
  const inc = wraptile((store) => store(store() + 1));
@@ -13,10 +12,7 @@ const isOnly = ({only}) => only;
13
12
  const isSkip = ({skip}) => skip;
14
13
  const notSkip = ({skip}) => !skip;
15
14
 
16
- const getInitOperators = once(async () => {
17
- const {initOperators} = await import('./operators.mjs');
18
- return initOperators;
19
- });
15
+ const getInitOperators = async () => await import('./operators.mjs');
20
16
 
21
17
  const {
22
18
  SUPERTAPE_TIMEOUT = 3000,
@@ -64,11 +60,9 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
64
60
  const count = fullstore(0);
65
61
  const failed = fullstore(0);
66
62
  const passed = fullstore(0);
67
-
68
63
  const incCount = inc(count);
69
64
  const incFailed = inc(failed);
70
65
  const incPassed = inc(passed);
71
-
72
66
  const total = tests.length;
73
67
 
74
68
  formatter.emit('start', {
@@ -76,11 +70,12 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
76
70
  });
77
71
 
78
72
  const wasStop = fullstore();
73
+
79
74
  const getValidationMessage = createValidator({
80
75
  tests,
81
76
  });
82
77
 
83
- for (const {fn, message, extensions} of tests) {
78
+ for (const {fn, message, extensions, at, validations} of tests) {
84
79
  if (wasStop())
85
80
  break;
86
81
 
@@ -90,6 +85,7 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
90
85
  }
91
86
 
92
87
  await runOneTest({
88
+ at,
93
89
  fn,
94
90
  message,
95
91
  formatter,
@@ -100,6 +96,7 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
100
96
  incFailed,
101
97
  incPassed,
102
98
  getValidationMessage,
99
+ validations,
103
100
 
104
101
  extensions: {
105
102
  ...operators,
@@ -123,33 +120,54 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
123
120
  };
124
121
  }
125
122
 
126
- async function runOneTest({message, fn, extensions, formatter, count, total, failed, incCount, incPassed, incFailed, getValidationMessage}) {
123
+ async function runOneTest({message, at, fn, extensions, formatter, count, total, failed, incCount, incPassed, incFailed, getValidationMessage, validations}) {
124
+ const isReturn = fullstore(false);
125
+ const assertionsCount = fullstore(0);
126
+ const isEnded = fullstore(false);
127
+ const incAssertionsCount = inc(assertionsCount);
128
+
127
129
  formatter.emit('test', {
128
130
  test: message,
129
131
  });
130
132
 
131
- const initOperators = await getInitOperators();
133
+ const {initOperators} = await getInitOperators();
134
+ const {checkIfEnded} = validations;
135
+
132
136
  const t = initOperators({
133
137
  formatter,
134
138
  count,
135
139
  incCount,
136
140
  incPassed,
137
141
  incFailed,
142
+ assertionsCount,
143
+ incAssertionsCount,
144
+ isEnded,
138
145
  extensions,
146
+ checkIfEnded,
139
147
  });
140
148
 
141
- const [timer, stopTimer] = timeout(SUPERTAPE_TIMEOUT, ['timeout']);
142
-
143
- const [error] = await Promise.race([
144
- tryToCatch(fn, t),
145
- timer,
146
- ]);
147
-
148
- stopTimer();
149
+ if (!isReturn()) {
150
+ const [timer, stopTimer] = timeout(SUPERTAPE_TIMEOUT, ['timeout']);
151
+ const [error] = await Promise.race([tryToCatch(fn, t), timer]);
152
+ stopTimer();
153
+ isEnded(false);
154
+
155
+ if (error) {
156
+ t.fail(error, at);
157
+ t.end();
158
+ isReturn(true);
159
+ }
160
+ }
149
161
 
150
- if (error) {
151
- t.fail(error);
152
- t.end();
162
+ if (!isReturn()) {
163
+ const [validationMessage, atLine] = getValidationMessage(message, {
164
+ assertionsCount: assertionsCount(),
165
+ });
166
+
167
+ if (atLine) {
168
+ t.fail(validationMessage, atLine);
169
+ t.end();
170
+ }
153
171
  }
154
172
 
155
173
  formatter.emit('test:end', {
@@ -158,11 +176,4 @@ async function runOneTest({message, fn, extensions, formatter, count, total, fai
158
176
  test: message,
159
177
  failed: failed(),
160
178
  });
161
-
162
- const [validationMessage, at] = getValidationMessage(message);
163
-
164
- if (at) {
165
- t.fail(validationMessage, at);
166
- t.end();
167
- }
168
179
  }
package/lib/supertape.js CHANGED
@@ -35,6 +35,8 @@ const defaultOptions = {
35
35
  getOperators,
36
36
  isStop: () => false,
37
37
  checkDuplicates: true,
38
+ checkIfEnded: true,
39
+ checkAssertionsCount: false,
38
40
  checkScopes: false,
39
41
  };
40
42
 
@@ -42,7 +44,7 @@ function _createEmitter({quiet, format, getOperators, isStop}) {
42
44
  const tests = [];
43
45
  const emitter = new EventEmitter();
44
46
 
45
- emitter.on('test', (message, fn, {skip, only, extensions, at}) => {
47
+ emitter.on('test', (message, fn, {skip, only, extensions, at, validations}) => {
46
48
  tests.push({
47
49
  message,
48
50
  fn,
@@ -50,6 +52,7 @@ function _createEmitter({quiet, format, getOperators, isStop}) {
50
52
  only,
51
53
  extensions,
52
54
  at,
55
+ validations,
53
56
  });
54
57
  });
55
58
 
@@ -110,16 +113,22 @@ function test(message, fn, options = {}) {
110
113
  isStop,
111
114
  checkDuplicates,
112
115
  checkScopes,
116
+ checkAssertionsCount,
117
+ checkIfEnded,
113
118
  } = {
114
119
  ...defaultOptions,
115
120
  ...initedOptions,
116
121
  ...options,
117
122
  };
118
123
 
119
- setValidations({
124
+ const validations = {
120
125
  checkDuplicates,
121
126
  checkScopes,
122
- });
127
+ checkAssertionsCount,
128
+ checkIfEnded,
129
+ };
130
+
131
+ setValidations(validations);
123
132
 
124
133
  const at = getAt();
125
134
 
@@ -137,6 +146,7 @@ function test(message, fn, options = {}) {
137
146
  only,
138
147
  extensions,
139
148
  at,
149
+ validations,
140
150
  });
141
151
 
142
152
  if (run)
package/lib/validator.js CHANGED
@@ -2,23 +2,25 @@
2
2
 
3
3
  const once = require('once');
4
4
  const StackTracey = require('stacktracey');
5
-
6
5
  const getDuplicatesMessage = ([, a]) => a;
7
- const getMessage = ({message, at}) => [message, at];
6
+
7
+ const getMessage = ({message, at, validations}) => [message, at, validations];
8
+
8
9
  const getMessagesList = (tests) => tests.map(getMessage);
9
10
  const compareMessage = (a) => ([b]) => a === b;
10
-
11
- const SCOPE_DEFINED = /^[\w-\d\s]+:.*/;
11
+ const SCOPE_DEFINED = /^[\w-/\d\s]+:.*/;
12
12
  const processedList = new Set();
13
13
 
14
14
  const validations = {
15
15
  checkDuplicates: true,
16
16
  checkScopes: false,
17
+ checkAssertionsCount: true,
17
18
  };
18
19
 
19
20
  const validators = {
20
21
  checkDuplicates,
21
22
  checkScopes,
23
+ checkAssertionsCount,
22
24
  };
23
25
 
24
26
  const {
@@ -30,20 +32,20 @@ const {
30
32
  const findByMessage = (msg, tests) => {
31
33
  const getMessages = once(getMessagesList);
32
34
  const filtered = getMessages(tests).filter(compareMessage(msg));
33
-
34
35
  return filtered;
35
36
  };
36
37
 
37
- module.exports.setValidations = ({checkDuplicates, checkScopes}) => {
38
+ module.exports.setValidations = ({checkDuplicates, checkScopes, checkAssertionsCount}) => {
38
39
  assign(validations, {
39
40
  checkDuplicates,
40
41
  checkScopes,
42
+ checkAssertionsCount,
41
43
  });
42
44
  };
43
45
 
44
46
  const isValidationEnabled = (a) => values(a).filter(Boolean).length;
45
47
 
46
- module.exports.createValidator = ({tests}) => (msg) => {
48
+ module.exports.createValidator = ({tests}) => (msg, options) => {
47
49
  if (!isValidationEnabled(validations))
48
50
  return [];
49
51
 
@@ -56,7 +58,7 @@ module.exports.createValidator = ({tests}) => (msg) => {
56
58
  if (!filtered.length)
57
59
  throw Error('☝️Looks like message cannot be find in tests, this should never happen');
58
60
 
59
- const [message, at] = validators[name](msg, filtered);
61
+ const [message, at] = validators[name](msg, filtered, options);
60
62
 
61
63
  if (at)
62
64
  return [message, at];
@@ -66,14 +68,6 @@ module.exports.createValidator = ({tests}) => (msg) => {
66
68
  };
67
69
 
68
70
  module.exports.getAt = () => {
69
- const {
70
- checkDuplicates,
71
- checkScopes,
72
- } = validations;
73
-
74
- if (!checkDuplicates && !checkScopes)
75
- return '';
76
-
77
71
  return getFileName();
78
72
  };
79
73
 
@@ -92,6 +86,16 @@ function getFileName() {
92
86
  return '';
93
87
  }
94
88
 
89
+ function checkAssertionsCount(msg, filtered, options) {
90
+ const {assertionsCount} = options;
91
+ const [, at] = filtered[0];
92
+
93
+ if (assertionsCount > 1)
94
+ return [`Only one assertion per test allowed, looks like you have more`, at];
95
+
96
+ return [];
97
+ }
98
+
95
99
  function checkScopes(msg, filtered) {
96
100
  const [message, at] = filtered[0];
97
101
 
@@ -101,17 +105,29 @@ function checkScopes(msg, filtered) {
101
105
  return [];
102
106
  }
103
107
 
108
+ const isEnabled = (tests, name) => {
109
+ for (const [,, validations] of tests) {
110
+ if (!validations[name]) {
111
+ return false;
112
+ }
113
+ }
114
+
115
+ return true;
116
+ };
117
+
104
118
  function checkDuplicates(msg, filtered) {
105
119
  if (filtered.length < 2)
106
120
  return [];
107
121
 
122
+ if (!isEnabled(filtered, 'checkDuplicates'))
123
+ return [];
124
+
108
125
  const [first, second] = filtered.map(getDuplicatesMessage);
109
126
 
110
127
  if (processedList.has(first))
111
128
  return [];
112
129
 
113
130
  processedList.add(first);
114
-
115
131
  return [`Duplicate ${first}`, second];
116
132
  }
117
133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supertape",
3
- "version": "6.7.1",
3
+ "version": "6.8.0",
4
4
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
5
5
  "description": "tape compatible test runner with superpowers",
6
6
  "homepage": "http://github.com/coderaiser/supertape",