ava 0.16.0 → 0.18.2

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/lib/test.js CHANGED
@@ -1,398 +1,369 @@
1
1
  'use strict';
2
- var inspect = require('util').inspect;
3
- var isGeneratorFn = require('is-generator-fn');
4
- var maxTimeout = require('max-timeout');
5
- var Promise = require('bluebird');
6
- var fnName = require('fn-name');
7
- var co = require('co-with-promise');
8
- var observableToPromise = require('observable-to-promise');
9
- var isPromise = require('is-promise');
10
- var isObservable = require('is-observable');
11
- var plur = require('plur');
12
- var assert = require('./assert');
13
- var enhanceAssert = require('./enhance-assert');
14
- var globals = require('./globals');
15
- var throwsHelper = require('./throws-helper');
16
- var formatter = enhanceAssert.formatter();
17
-
18
- function Test(title, fn, contextRef, report) {
19
- if (!(this instanceof Test)) {
20
- throw new TypeError('Class constructor Test cannot be invoked without \'new\'');
21
- }
22
-
23
- if (typeof title === 'function') {
24
- contextRef = fn;
25
- fn = title;
26
- title = null;
27
- }
28
-
29
- assert.is(typeof fn, 'function', 'you must provide a callback');
30
-
31
- this.title = title || fnName(fn) || '[anonymous]';
32
- this.fn = isGeneratorFn(fn) ? co.wrap(fn) : fn;
33
- this.assertions = [];
34
- this.planCount = null;
35
- this.duration = null;
36
- this.assertError = undefined;
37
- this.sync = true;
38
- this.contextRef = contextRef;
39
- this.report = report;
40
- this.threwSync = false;
41
-
42
- // TODO(jamestalmage): make this an optional constructor arg instead of having Runner set it after the fact.
43
- // metadata should just always exist, otherwise it requires a bunch of ugly checks all over the place.
44
- this.metadata = {};
45
-
46
- // store the time point before test execution
47
- // to calculate the total time spent in test
48
- this._timeStart = null;
49
-
50
- // workaround for Babel giving anonymous functions a name
51
- if (this.title === 'callee$0$0') {
52
- this.title = '[anonymous]';
2
+ const inspect = require('util').inspect;
3
+ const isGeneratorFn = require('is-generator-fn');
4
+ const maxTimeout = require('max-timeout');
5
+ const Promise = require('bluebird');
6
+ const fnName = require('fn-name');
7
+ const co = require('co-with-promise');
8
+ const observableToPromise = require('observable-to-promise');
9
+ const isPromise = require('is-promise');
10
+ const isObservable = require('is-observable');
11
+ const plur = require('plur');
12
+ const assert = require('./assert');
13
+ const enhanceAssert = require('./enhance-assert');
14
+ const globals = require('./globals');
15
+ const throwsHelper = require('./throws-helper');
16
+
17
+ const formatter = enhanceAssert.formatter();
18
+
19
+ class SkipApi {
20
+ constructor(test) {
21
+ this._test = test;
53
22
  }
54
23
  }
55
24
 
56
- module.exports = Test;
25
+ function skipFn() {
26
+ return this._test._assert(null);
27
+ }
57
28
 
58
- Object.defineProperty(Test.prototype, 'assertCount', {
59
- enumerable: true,
60
- get: function () {
61
- return this.assertions.length;
62
- }
29
+ Object.keys(assert).forEach(el => {
30
+ SkipApi.prototype[el] = skipFn;
63
31
  });
64
32
 
65
- Test.prototype._assert = function (promise) {
66
- if (isPromise(promise)) {
67
- this.sync = false;
33
+ class PublicApi {
34
+ constructor(test) {
35
+ this._test = test;
36
+ this.skip = new SkipApi(test);
68
37
  }
69
-
70
- this.assertions.push(promise);
71
- };
72
-
73
- Test.prototype._setAssertError = function (err) {
74
- throwsHelper(err);
75
- if (this.assertError !== undefined) {
76
- return;
38
+ plan(ct) {
39
+ const limitBefore = Error.stackTraceLimit;
40
+ Error.stackTraceLimit = 1;
41
+ const obj = {};
42
+ Error.captureStackTrace(obj, this.plan);
43
+ Error.stackTraceLimit = limitBefore;
44
+ this._test.plan(ct, obj.stack);
77
45
  }
78
-
79
- this.assertError = err;
80
- };
81
-
82
- Test.prototype.plan = function (count, planStack) {
83
- if (typeof count !== 'number') {
84
- throw new TypeError('Expected a number');
46
+ get context() {
47
+ const contextRef = this._test.contextRef;
48
+ return contextRef && contextRef.context;
85
49
  }
50
+ set context(context) {
51
+ const contextRef = this._test.contextRef;
86
52
 
87
- this.planCount = count;
88
-
89
- // in case the `planCount` doesn't match `assertCount,
90
- // we need the stack of this function to throw with a useful stack
91
- this.planStack = planStack;
92
- };
93
-
94
- Test.prototype._run = function () {
95
- var ret;
96
-
97
- try {
98
- ret = this.fn(this._publicApi());
99
- } catch (err) {
100
- this.threwSync = true;
101
- if (err instanceof Error) {
102
- this._setAssertError(err);
103
- } else {
104
- this._setAssertError(new assert.AssertionError({
105
- actual: err,
106
- message: 'Non-error thrown with value: ' + inspect(err, {depth: null}),
107
- operator: 'catch'
108
- }));
53
+ if (!contextRef) {
54
+ this._test._setAssertError(new Error(`t.context is not available in ${this._test.metadata.type} tests`));
55
+ return;
109
56
  }
110
- }
111
-
112
- return ret;
113
- };
114
-
115
- Test.prototype.promise = function () {
116
- var self = this;
117
57
 
118
- if (!this._promise) {
119
- this._promise = {};
120
-
121
- this._promise.promise = new Promise(function (resolve, reject) { // eslint-disable-line no-use-extend-native/no-use-extend-native
122
- self._promise.resolve = resolve;
123
- self._promise.reject = reject;
124
- }).tap(function (result) {
125
- if (self.report) {
126
- self.report(result);
127
- }
128
- });
58
+ contextRef.context = context;
129
59
  }
60
+ }
130
61
 
131
- return this._promise;
132
- };
133
-
134
- Test.prototype.run = function () {
135
- if (this.metadata.callback) {
136
- this.sync = false;
62
+ function onAssertionEvent(event) {
63
+ if (event.assertionThrew) {
64
+ if (event.powerAssertContext) {
65
+ event.error.statements = formatter(event.powerAssertContext);
66
+ event.error.message = event.originalMessage || '';
67
+ }
68
+ this._test._setAssertError(event.error);
69
+ this._test._assert(null);
70
+ return null;
137
71
  }
138
72
 
139
- var self = this;
140
-
141
- this._timeStart = globals.now();
142
-
143
- // wait until all assertions are complete
144
- this._timeout = globals.setTimeout(function () {}, maxTimeout);
145
-
146
- var ret = this._run();
147
-
148
- var asyncType = 'promises';
73
+ let ret = event.returnValue;
149
74
 
150
75
  if (isObservable(ret)) {
151
- asyncType = 'observables';
152
76
  ret = observableToPromise(ret);
153
77
  }
154
78
 
155
79
  if (isPromise(ret)) {
156
- this.sync = false;
80
+ const promise = ret.then(null, err => {
81
+ err.originalMessage = event.originalMessage;
82
+ throw err;
83
+ });
157
84
 
158
- if (this.metadata.callback) {
159
- self._setAssertError(new Error('Do not return ' + asyncType + ' from tests declared via `test.cb(...)`, if you want to return a promise simply declare the test via `test(...)`'));
160
- }
85
+ this._test._assert(promise);
161
86
 
162
- ret.then(
163
- function () {
164
- self.exit();
165
- },
166
- function (err) {
167
- if (!(err instanceof Error)) {
168
- err = new assert.AssertionError({
169
- actual: err,
170
- message: 'Promise rejected with: ' + inspect(err, {depth: null}),
171
- operator: 'promise'
172
- });
173
- }
87
+ return promise;
88
+ }
174
89
 
175
- self._setAssertError(err);
176
- self.exit();
177
- });
90
+ this._test._assert(null);
178
91
 
179
- return this.promise().promise;
180
- }
92
+ return ret;
93
+ }
181
94
 
182
- if (this.metadata.callback && !this.threwSync) {
183
- return this.promise().promise;
184
- }
95
+ Object.assign(PublicApi.prototype, enhanceAssert({
96
+ assert,
97
+ onSuccess: onAssertionEvent,
98
+ onError: onAssertionEvent
99
+ }));
185
100
 
186
- return this.exit();
187
- };
101
+ // Getters
102
+ [
103
+ 'assertCount',
104
+ 'title',
105
+ 'end'
106
+ ]
107
+ .forEach(name => {
108
+ Object.defineProperty(PublicApi.prototype, name, {
109
+ get() {
110
+ return this._test[name];
111
+ }
112
+ });
113
+ });
188
114
 
189
- Test.prototype._result = function () {
190
- var reason = this.assertError;
191
- var passed = reason === undefined;
115
+ Object.defineProperty(PublicApi.prototype, 'context', {enumerable: true});
192
116
 
193
- if (this.metadata.failing) {
194
- passed = !passed;
117
+ class Test {
118
+ constructor(title, fn, contextRef, report) {
119
+ if (typeof title === 'function') {
120
+ contextRef = fn;
121
+ fn = title;
122
+ title = null;
123
+ }
195
124
 
196
- if (passed) {
197
- reason = undefined;
198
- } else {
199
- reason = new Error('Test was expected to fail, but succeeded, you should stop marking the test as failing');
125
+ assert.is(typeof fn, 'function', 'You must provide a callback');
126
+
127
+ this.title = title || fnName(fn) || '[anonymous]';
128
+ this.fn = isGeneratorFn(fn) ? co.wrap(fn) : fn;
129
+ this.assertions = [];
130
+ this.planCount = null;
131
+ this.duration = null;
132
+ this.assertError = undefined;
133
+ this.sync = true;
134
+ this.contextRef = contextRef;
135
+ this.report = report;
136
+ this.threwSync = false;
137
+
138
+ // TODO(jamestalmage): Make this an optional constructor arg instead of having Runner set it after the fact.
139
+ // metadata should just always exist, otherwise it requires a bunch of ugly checks all over the place.
140
+ this.metadata = {};
141
+
142
+ // Store the time point before test execution
143
+ // to calculate the total time spent in test
144
+ this._timeStart = null;
145
+
146
+ // Workaround for Babel giving anonymous functions a name
147
+ if (this.title === 'callee$0$0') {
148
+ this.title = '[anonymous]';
200
149
  }
201
150
  }
151
+ get assertCount() {
152
+ return this.assertions.length;
153
+ }
154
+ _assert(promise) {
155
+ if (isPromise(promise)) {
156
+ this.sync = false;
157
+ }
202
158
 
203
- return {
204
- passed: passed,
205
- result: this,
206
- reason: reason
207
- };
208
- };
159
+ this.assertions.push(promise);
160
+ }
161
+ _setAssertError(err) {
162
+ throwsHelper(err);
209
163
 
210
- Object.defineProperty(Test.prototype, 'end', {
211
- get: function () {
212
- if (this.metadata.callback) {
213
- return this._end.bind(this);
164
+ if (this.assertError !== undefined) {
165
+ return;
214
166
  }
215
167
 
216
- throw new Error('t.end is not supported in this context. To use t.end as a callback, you must use "callback mode" via `test.cb(testName, fn)`');
168
+ this.assertError = err;
217
169
  }
218
- });
219
-
220
- Test.prototype._end = function (err) {
221
- if (err) {
222
- if (!(err instanceof Error)) {
223
- err = new assert.AssertionError({
224
- actual: err,
225
- message: 'Callback called with an error: ' + inspect(err, {depth: null}),
226
- operator: 'callback'
227
- });
170
+ plan(count, planStack) {
171
+ if (typeof count !== 'number') {
172
+ throw new TypeError('Expected a number');
228
173
  }
229
174
 
230
- this._setAssertError(err);
231
- this.exit();
175
+ this.planCount = count;
232
176
 
233
- return;
177
+ // In case the `planCount` doesn't match `assertCount,
178
+ // we need the stack of this function to throw with a useful stack
179
+ this.planStack = planStack;
234
180
  }
181
+ _run() {
182
+ let ret;
183
+
184
+ try {
185
+ ret = this.fn(this._publicApi());
186
+ } catch (err) {
187
+ this.threwSync = true;
188
+
189
+ if (err instanceof Error) {
190
+ this._setAssertError(err);
191
+ } else {
192
+ this._setAssertError(new assert.AssertionError({
193
+ actual: err,
194
+ message: `Non-error thrown with value: ${inspect(err, {depth: null})}`,
195
+ operator: 'catch'
196
+ }));
197
+ }
198
+ }
235
199
 
236
- if (this.endCalled) {
237
- this._setAssertError(new Error('.end() called more than once'));
238
- return;
200
+ return ret;
239
201
  }
202
+ promise() {
203
+ if (!this._promise) {
204
+ this._promise = {};
205
+
206
+ this._promise.promise = new Promise((resolve, reject) => {
207
+ this._promise.resolve = resolve;
208
+ this._promise.reject = reject;
209
+ }).tap(result => {
210
+ if (this.report) {
211
+ this.report(result);
212
+ }
213
+ });
214
+ }
240
215
 
241
- this.endCalled = true;
242
- this.exit();
243
- };
244
-
245
- Test.prototype._checkPlanCount = function () {
246
- if (this.assertError === undefined && this.planCount !== null && this.planCount !== this.assertions.length) {
247
- this._setAssertError(new assert.AssertionError({
248
- actual: this.assertions.length,
249
- expected: this.planCount,
250
- message: 'Planned for ' + this.planCount + plur(' assertion', this.planCount) + ', but got ' + this.assertions.length + '.',
251
- operator: 'plan'
252
- }));
253
-
254
- this.assertError.stack = this.planStack;
216
+ return this._promise;
255
217
  }
256
- };
257
-
258
- Test.prototype.exit = function () {
259
- var self = this;
218
+ run() {
219
+ if (this.metadata.callback) {
220
+ this.sync = false;
221
+ }
260
222
 
261
- this._checkPlanCount();
223
+ this._timeStart = globals.now();
262
224
 
263
- if (this.sync || this.threwSync) {
264
- self.duration = globals.now() - self._timeStart;
265
- globals.clearTimeout(self._timeout);
225
+ // Wait until all assertions are complete
226
+ this._timeout = globals.setTimeout(() => {}, maxTimeout);
266
227
 
267
- var result = this._result();
228
+ let ret = this._run();
229
+ let asyncType = 'promises';
268
230
 
269
- if (this.report) {
270
- this.report(result);
231
+ if (isObservable(ret)) {
232
+ asyncType = 'observables';
233
+ ret = observableToPromise(ret);
271
234
  }
272
235
 
273
- return result;
274
- }
275
-
276
- Promise.all(this.assertions)
277
- .catch(function (err) {
278
- self._setAssertError(err);
279
- })
280
- .finally(function () {
281
- // calculate total time spent in test
282
- self.duration = globals.now() - self._timeStart;
283
-
284
- // stop infinite timer
285
- globals.clearTimeout(self._timeout);
236
+ if (isPromise(ret)) {
237
+ this.sync = false;
286
238
 
287
- self._checkPlanCount();
239
+ if (this.metadata.callback) {
240
+ this._setAssertError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`, if you want to return a promise simply declare the test via \`test(...)\``));
241
+ }
288
242
 
289
- self.promise().resolve(self._result());
290
- });
243
+ // Convert to a Bluebird promise
244
+ return Promise.resolve(ret).then(
245
+ () => this.exit(),
246
+ err => {
247
+ if (!(err instanceof Error)) {
248
+ err = new assert.AssertionError({
249
+ actual: err,
250
+ message: `Promise rejected with: ${inspect(err, {depth: null})}`,
251
+ operator: 'promise'
252
+ });
253
+ }
254
+
255
+ this._setAssertError(err);
256
+ return this.exit();
257
+ }
258
+ );
259
+ }
291
260
 
292
- return self.promise().promise;
293
- };
261
+ if (this.metadata.callback && !this.threwSync) {
262
+ return this.promise().promise;
263
+ }
294
264
 
295
- Test.prototype._publicApi = function () {
296
- return new PublicApi(this);
297
- };
265
+ return this.exit();
266
+ }
267
+ _result() {
268
+ let reason = this.assertError;
269
+ let passed = reason === undefined;
298
270
 
299
- function PublicApi(test) {
300
- this._test = test;
301
- this.skip = new SkipApi(test);
302
- }
271
+ if (this.metadata.failing) {
272
+ passed = !passed;
303
273
 
304
- function onAssertionEvent(event) {
305
- if (event.assertionThrew) {
306
- if (event.powerAssertContext) {
307
- event.error.message = formatter(event.powerAssertContext);
308
- if (event.originalMessage) {
309
- event.error.message = event.originalMessage + ' ' + event.error.message;
274
+ if (passed) {
275
+ reason = undefined;
276
+ } else {
277
+ reason = new Error('Test was expected to fail, but succeeded, you should stop marking the test as failing');
310
278
  }
311
279
  }
312
- this._test._setAssertError(event.error);
313
- this._test._assert(null);
314
- return null;
315
- }
316
280
 
317
- var ret = event.returnValue;
281
+ return {
282
+ passed,
283
+ result: this,
284
+ reason
285
+ };
286
+ }
287
+ get end() {
288
+ if (this.metadata.callback) {
289
+ return this._end.bind(this);
290
+ }
318
291
 
319
- if (isObservable(ret)) {
320
- ret = observableToPromise(ret);
292
+ throw new Error('t.end is not supported in this context. To use t.end as a callback, you must use "callback mode" via `test.cb(testName, fn)`');
321
293
  }
294
+ _end(err) {
295
+ if (err) {
296
+ if (!(err instanceof Error)) {
297
+ err = new assert.AssertionError({
298
+ actual: err,
299
+ message: 'Callback called with an error: ' + inspect(err, {depth: null}),
300
+ operator: 'callback'
301
+ });
302
+ }
322
303
 
323
- if (isPromise(ret)) {
324
- var promise = ret.then(null, function (err) {
325
- err.originalMessage = event.originalMessage;
326
- throw err;
327
- });
304
+ this._setAssertError(err);
305
+ this.exit();
328
306
 
329
- this._test._assert(promise);
307
+ return;
308
+ }
330
309
 
331
- return promise;
332
- }
310
+ if (this.endCalled) {
311
+ this._setAssertError(new Error('.end() called more than once'));
312
+ return;
313
+ }
333
314
 
334
- this._test._assert(null);
315
+ this.endCalled = true;
316
+ this.exit();
317
+ }
318
+ _checkPlanCount() {
319
+ if (this.assertError === undefined && this.planCount !== null && this.planCount !== this.assertions.length) {
320
+ this._setAssertError(new assert.AssertionError({
321
+ actual: this.assertions.length,
322
+ expected: this.planCount,
323
+ message: `Planned for ${this.planCount} ${plur('assertion', this.planCount)}, but got ${this.assertions.length}.`,
324
+ operator: 'plan'
325
+ }));
335
326
 
336
- return ret;
337
- }
327
+ this.assertError.stack = this.planStack;
328
+ }
329
+ }
330
+ exit() {
331
+ this._checkPlanCount();
338
332
 
339
- PublicApi.prototype = enhanceAssert({
340
- assert: assert,
341
- onSuccess: onAssertionEvent,
342
- onError: onAssertionEvent
343
- });
333
+ if (this.sync || this.threwSync) {
334
+ this.duration = globals.now() - this._timeStart;
335
+ globals.clearTimeout(this._timeout);
344
336
 
345
- PublicApi.prototype.plan = function plan(ct) {
346
- var limitBefore = Error.stackTraceLimit;
347
- Error.stackTraceLimit = 1;
348
- var obj = {};
349
- Error.captureStackTrace(obj, plan);
350
- Error.stackTraceLimit = limitBefore;
351
- this._test.plan(ct, obj.stack);
352
- };
337
+ const result = this._result();
353
338
 
354
- // Getters
355
- [
356
- 'assertCount',
357
- 'title',
358
- 'end'
359
- ]
360
- .forEach(function (name) {
361
- Object.defineProperty(PublicApi.prototype, name, {
362
- enumerable: false,
363
- get: function () {
364
- return this._test[name];
339
+ if (this.report) {
340
+ this.report(result);
365
341
  }
366
- });
367
- });
368
342
 
369
- // Get / Set
370
- Object.defineProperty(PublicApi.prototype, 'context', {
371
- enumerable: true,
372
- get: function () {
373
- var contextRef = this._test.contextRef;
374
- return contextRef && contextRef.context;
375
- },
376
- set: function (context) {
377
- var contextRef = this._test.contextRef;
378
-
379
- if (!contextRef) {
380
- this._test._setAssertError(new Error('t.context is not available in ' + this._test.metadata.type + ' tests'));
381
- return;
343
+ return result;
382
344
  }
383
345
 
384
- contextRef.context = context;
385
- }
386
- });
346
+ Promise.all(this.assertions)
347
+ .catch(err => {
348
+ this._setAssertError(err);
349
+ })
350
+ .finally(() => {
351
+ // Calculate total time spent in test
352
+ this.duration = globals.now() - this._timeStart;
387
353
 
388
- function skipFn() {
389
- return this._test._assert(null);
390
- }
354
+ // Stop infinite timer
355
+ globals.clearTimeout(this._timeout);
356
+
357
+ this._checkPlanCount();
391
358
 
392
- function SkipApi(test) {
393
- this._test = test;
359
+ this.promise().resolve(this._result());
360
+ });
361
+
362
+ return this.promise().promise;
363
+ }
364
+ _publicApi() {
365
+ return new PublicApi(this);
366
+ }
394
367
  }
395
368
 
396
- Object.keys(assert).forEach(function (el) {
397
- SkipApi.prototype[el] = skipFn;
398
- });
369
+ module.exports = Test;