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 +29 -0
- package/README.md +4 -1
- package/help.json +1 -0
- package/lib/cli.js +6 -0
- package/lib/operators.mjs +19 -3
- package/lib/run-tests.js +40 -29
- package/lib/supertape.js +13 -3
- package/lib/validator.js +33 -17
- package/package.json +1 -1
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 =
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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 (
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|