supertape 6.7.4 → 6.9.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.25, v6.9.0
2
+
3
+ feature:
4
+ - (supertape) fail when couple t.end() operators found
5
+
6
+
7
+ 2021.09.25, v6.8.2
8
+
9
+ feature:
10
+ - (supertape) check that no assertions called after t.end() in async operators
11
+
12
+
13
+ 2021.09.24, v6.8.1
14
+
15
+ fix:
16
+ - (supertape) ability to disable check-assertions-count per test
17
+
18
+
19
+ 2021.09.24, v6.8.0
20
+
21
+ fix:
22
+ - (supertape) check that no assertions called after t.end()
23
+
24
+ feature:
25
+ - (supertape) add support of check assertions count
26
+ - (@supertape/operator-stub) add calledBefore, calledAfter
27
+ - (@supertape/operator-stub) notCalled: add additional check: called with args
28
+
29
+
1
30
  2021.09.20, v6.7.4
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') => ({
@@ -165,16 +164,68 @@ const initOperator = (runnerState) => (name) => {
165
164
 
166
165
  if (isAsync(fn))
167
166
  return async (...a) => {
167
+ const [valid, end] = validateEnd({
168
+ name,
169
+ operators,
170
+ runnerState,
171
+ });
172
+
173
+ if (!valid)
174
+ return end;
175
+
168
176
  const testState = await fn(...a);
169
177
  return run(name, runnerState, testState);
170
178
  };
171
179
 
172
180
  return (...a) => {
181
+ const [valid, end] = validateEnd({
182
+ name,
183
+ operators,
184
+ runnerState,
185
+ });
186
+
187
+ if (!valid)
188
+ return end;
189
+
173
190
  const testState = fn(...a);
174
191
  return run(name, runnerState, testState);
175
192
  };
176
193
  };
177
194
 
195
+ const VALID = true;
196
+ const INVALID = false;
197
+
198
+ function validateEnd({name, operators, runnerState}) {
199
+ const {
200
+ isEnded,
201
+ incAssertionsCount,
202
+ } = runnerState;
203
+
204
+ incAssertionsCount();
205
+
206
+ if (name === 'end' && isEnded())
207
+ return [INVALID, run(
208
+ 'fail',
209
+ runnerState,
210
+ operators.fail(`Cannot use a couple 't.end()' operators in one test`),
211
+ )];
212
+
213
+ if (name === 'end') {
214
+ isEnded(true);
215
+ return [INVALID, end];
216
+ }
217
+
218
+ if (isEnded()) {
219
+ return [INVALID, run(
220
+ 'fail',
221
+ runnerState,
222
+ operators.fail(`Cannot run assertions after 't.end()' called`),
223
+ )];
224
+ }
225
+
226
+ return [VALID];
227
+ }
228
+
178
229
  const validate = (a) => {
179
230
  if (isFn(a))
180
231
  return fail('looks like operator returns function, it will always fail');
@@ -223,13 +274,15 @@ function run(name, {formatter, count, incCount, incPassed, incFailed}, testState
223
274
  });
224
275
  }
225
276
 
226
- export const initOperators = ({formatter, count, incCount, incPassed, incFailed, extensions}) => {
277
+ export const initOperators = ({formatter, count, incCount, incPassed, incFailed, incAssertionsCount, isEnded, extensions}) => {
227
278
  const operator = initOperator({
228
279
  formatter,
229
280
  count,
230
281
  incCount,
231
282
  incPassed,
232
283
  incFailed,
284
+ isEnded,
285
+ incAssertionsCount,
233
286
  });
234
287
 
235
288
  const extendedOperators = {};
@@ -251,7 +304,7 @@ export const initOperators = ({formatter, count, incCount, incPassed, incFailed,
251
304
  comment: comment({formatter}),
252
305
  match: operator('match'),
253
306
  notMatch: operator('notMatch'),
254
- end,
307
+ end: operator('end'),
255
308
 
256
309
  ...extendedOperators,
257
310
  };
package/lib/run-tests.js CHANGED
@@ -3,24 +3,16 @@
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
6
  const isDebug = require('./is-debug');
8
7
 
9
8
  const {createValidator} = require('./validator');
10
9
 
11
10
  const inc = wraptile((store) => store(store() + 1));
12
-
13
11
  const isOnly = ({only}) => only;
14
-
15
12
  const isSkip = ({skip}) => skip;
16
-
17
13
  const notSkip = ({skip}) => !skip;
18
14
 
19
- const getInitOperators = once(async () => {
20
- const {initOperators} = await import('./operators.mjs');
21
-
22
- return initOperators;
23
- });
15
+ const getInitOperators = async () => await import('./operators.mjs');
24
16
 
25
17
  const {
26
18
  SUPERTAPE_TIMEOUT = 3000,
@@ -83,7 +75,7 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
83
75
  tests,
84
76
  });
85
77
 
86
- for (const {fn, message, extensions, at} of tests) {
78
+ for (const {fn, message, extensions, at, validations} of tests) {
87
79
  if (wasStop())
88
80
  break;
89
81
 
@@ -104,6 +96,7 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
104
96
  incFailed,
105
97
  incPassed,
106
98
  getValidationMessage,
99
+ validations,
107
100
 
108
101
  extensions: {
109
102
  ...operators,
@@ -127,12 +120,18 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
127
120
  };
128
121
  }
129
122
 
130
- async function runOneTest({message, at, 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
+
131
129
  formatter.emit('test', {
132
130
  test: message,
133
131
  });
134
132
 
135
- const initOperators = await getInitOperators();
133
+ const {initOperators} = await getInitOperators();
134
+ const {checkIfEnded} = validations;
136
135
 
137
136
  const t = initOperators({
138
137
  formatter,
@@ -140,16 +139,35 @@ async function runOneTest({message, at, fn, extensions, formatter, count, total,
140
139
  incCount,
141
140
  incPassed,
142
141
  incFailed,
142
+ assertionsCount,
143
+ incAssertionsCount,
144
+ isEnded,
143
145
  extensions,
146
+ checkIfEnded,
144
147
  });
145
148
 
146
- const [timer, stopTimer] = timeout(SUPERTAPE_TIMEOUT, ['timeout']);
147
- const [error] = await Promise.race([tryToCatch(fn, t), timer]);
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, at);
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, at, fn, extensions, formatter, count, total,
158
176
  test: message,
159
177
  failed: failed(),
160
178
  });
161
-
162
- const [validationMessage, atLine] = getValidationMessage(message);
163
-
164
- if (atLine) {
165
- t.fail(validationMessage, atLine);
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
 
@@ -111,6 +113,8 @@ function test(message, fn, options = {}) {
111
113
  isStop,
112
114
  checkDuplicates,
113
115
  checkScopes,
116
+ checkAssertionsCount,
117
+ checkIfEnded,
114
118
  } = {
115
119
  ...defaultOptions,
116
120
  ...initedOptions,
@@ -120,6 +124,8 @@ function test(message, fn, options = {}) {
120
124
  const validations = {
121
125
  checkDuplicates,
122
126
  checkScopes,
127
+ checkAssertionsCount,
128
+ checkIfEnded,
123
129
  };
124
130
 
125
131
  setValidations(validations);
package/lib/validator.js CHANGED
@@ -14,11 +14,13 @@ const processedList = new Set();
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 {
@@ -33,16 +35,17 @@ const findByMessage = (msg, tests) => {
33
35
  return filtered;
34
36
  };
35
37
 
36
- module.exports.setValidations = ({checkDuplicates, checkScopes}) => {
38
+ module.exports.setValidations = ({checkDuplicates, checkScopes, checkAssertionsCount}) => {
37
39
  assign(validations, {
38
40
  checkDuplicates,
39
41
  checkScopes,
42
+ checkAssertionsCount,
40
43
  });
41
44
  };
42
45
 
43
46
  const isValidationEnabled = (a) => values(a).filter(Boolean).length;
44
47
 
45
- module.exports.createValidator = ({tests}) => (msg) => {
48
+ module.exports.createValidator = ({tests}) => (msg, options) => {
46
49
  if (!isValidationEnabled(validations))
47
50
  return [];
48
51
 
@@ -55,7 +58,7 @@ module.exports.createValidator = ({tests}) => (msg) => {
55
58
  if (!filtered.length)
56
59
  throw Error('☝️Looks like message cannot be find in tests, this should never happen');
57
60
 
58
- const [message, at] = validators[name](msg, filtered);
61
+ const [message, at] = validators[name](msg, filtered, options);
59
62
 
60
63
  if (at)
61
64
  return [message, at];
@@ -65,14 +68,6 @@ module.exports.createValidator = ({tests}) => (msg) => {
65
68
  };
66
69
 
67
70
  module.exports.getAt = () => {
68
- const {
69
- checkDuplicates,
70
- checkScopes,
71
- } = validations;
72
-
73
- if (!checkDuplicates && !checkScopes)
74
- return '';
75
-
76
71
  return getFileName();
77
72
  };
78
73
 
@@ -91,6 +86,19 @@ function getFileName() {
91
86
  return '';
92
87
  }
93
88
 
89
+ function checkAssertionsCount(msg, filtered, options) {
90
+ const {assertionsCount} = options;
91
+ const [, at] = filtered[0];
92
+
93
+ if (!isEnabled(filtered, 'checkAssertionsCount'))
94
+ return [];
95
+
96
+ if (assertionsCount > 1)
97
+ return [`Only one assertion per test allowed, looks like you have more`, at];
98
+
99
+ return [];
100
+ }
101
+
94
102
  function checkScopes(msg, filtered) {
95
103
  const [message, at] = filtered[0];
96
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supertape",
3
- "version": "6.7.4",
3
+ "version": "6.9.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",