Browse Source

Refactor Test implementation

* Do away with underscore prefixes. They were used inconsistently, and
are generally not worth it

* Keep `_test` prefixed in ExecutionContext, since that is exposed to
calling code

* Reorder methods

* Assume all arguments are passed and are correct. They're already
validated in `test-collection.js`

*Pass metadata when instantiating Tests

* Rewrite test finish logic. There's still a fair bit of interleaving
due to the `test.cb()` API, but it's now a lot easier to understand.

Due to the last change, tests with `t.end()` and only synchronous
assertions end immediately. Previously they would end asynchronously
due to a promise being in the completion chain.

Similarly, returning a promise or observable for a `test.cb()` test
immediately fails.
master
Mark Wubben 8 years ago
parent
commit
d3748b4aa7
  1. 12
      lib/test-collection.js
  2. 369
      lib/test.js
  3. 24
      test/observable.js
  4. 20
      test/promise.js
  5. 273
      test/test.js

12
lib/test-collection.js

@ -124,22 +124,14 @@ class TestCollection extends EventEmitter {
context = null;
}
const test = new Test(title, hook.fn, context, this._emitTestResult);
test.metadata = hook.metadata;
return test;
return new Test(hook.metadata, title, hook.fn, context, this._emitTestResult);
}
_buildTest(test, context) {
if (!context) {
context = null;
}
const metadata = test.metadata;
test = new Test(test.title, test.fn, context, this._emitTestResult);
test.metadata = metadata;
return test;
return new Test(test.metadata, test.title, test.fn, context, this._emitTestResult);
}
_buildTestWithHooks(test) {
if (test.metadata.skipped || test.metadata.todo) {

369
lib/test.js

@ -1,8 +1,6 @@
'use strict';
const isGeneratorFn = require('is-generator-fn');
const maxTimeout = require('max-timeout');
const Promise = require('bluebird');
const fnName = require('fn-name');
const co = require('co-with-promise');
const observableToPromise = require('observable-to-promise');
const isPromise = require('is-promise');
@ -33,26 +31,31 @@ class ExecutionContext {
this._test = test;
this.skip = new SkipApi(test);
}
plan(ct) {
this._test.plan(ct, captureStack(this.plan));
}
get end() {
const end = this._test.end;
const end = this._test.bindEndCallback();
const endFn = err => end(err, captureStack(endFn));
return endFn;
}
get title() {
return this._test.title;
}
get context() {
const contextRef = this._test.contextRef;
return contextRef && contextRef.context;
}
set context(context) {
const contextRef = this._test.contextRef;
if (!contextRef) {
this._test._setAssertError(new Error(`\`t.context\` is not available in ${this._test.metadata.type} tests`));
this._test.saveFirstError(new Error(`\`t.context\` is not available in ${this._test.metadata.type} tests`));
return;
}
@ -64,21 +67,21 @@ Object.defineProperty(ExecutionContext.prototype, 'context', {enumerable: true})
{
const assertions = assert.wrapAssertions({
pass(executionContext) {
executionContext._test._assertionPassed();
executionContext._test.countPassedAssertion();
},
pending(executionContext, promise) {
executionContext._test._assertionPending(promise);
executionContext._test.addPendingAssertion(promise);
},
fail(executionContext, error) {
executionContext._test._assertionFailed(error);
executionContext._test.addFailedAssertion(error);
}
});
Object.assign(ExecutionContext.prototype, assertions);
function skipFn() {
this._test._assertionPassed();
this._test.countPassedAssertion();
}
Object.keys(assertions).forEach(el => {
SkipApi.prototype[el] = skipFn;
@ -86,61 +89,78 @@ Object.defineProperty(ExecutionContext.prototype, 'context', {enumerable: true})
}
class Test {
constructor(title, fn, contextRef, report) {
if (typeof title === 'function') {
contextRef = fn;
fn = title;
title = null;
}
if (typeof fn !== 'function') {
throw new TypeError('You must provide a callback');
}
this.title = title || fnName(fn) || '[anonymous]';
constructor(metadata, title, fn, contextRef, onResult) { // eslint-disable-line max-params
this.metadata = metadata;
this.title = title;
this.fn = isGeneratorFn(fn) ? co.wrap(fn) : fn;
this.pendingAssertions = [];
this.contextRef = contextRef;
this.onResult = onResult;
this.assertCount = 0;
this.planCount = null;
this.duration = null;
this.assertError = undefined;
this.sync = true;
this.contextRef = contextRef;
this.report = report;
this.threwSync = false;
this.calledEnd = false;
this.duration = null;
this.endCallbackFinisher = null;
this.pendingAssertions = [];
this.planCount = null;
this.startedAt = 0;
}
// TODO(jamestalmage): Make this an optional constructor arg instead of having Runner set it after the fact.
// metadata should just always exist, otherwise it requires a bunch of ugly checks all over the place.
this.metadata = {};
bindEndCallback() {
if (this.metadata.callback) {
return (err, stack) => {
this.endCallback(err, stack);
};
}
// Store the time point before test execution
// to calculate the total time spent in test
this._timeStart = null;
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)`');
}
endCallback(err, stack) {
if (this.calledEnd) {
this.saveFirstError(new Error('`t.end()` called more than once'));
return;
}
this.calledEnd = true;
if (err) {
this.saveFirstError(new assert.AssertionError({
actual: err,
message: 'Callback called with an error',
stack,
values: [formatAssertError.formatWithLabel('Error:', err)]
}));
}
// Workaround for Babel giving anonymous functions a name
if (this.title === 'callee$0$0') {
this.title = '[anonymous]';
if (this.endCallbackFinisher) {
this.endCallbackFinisher();
}
}
_assertionPassed() {
createExecutionContext() {
return new ExecutionContext(this);
}
countPassedAssertion() {
this.assertCount++;
}
_assertionPending(promise) {
this.sync = false;
addPendingAssertion(promise) {
this.assertCount++;
this.pendingAssertions.push(promise);
}
_assertionFailed(error) {
this._setAssertError(error);
addFailedAssertion(error) {
this.assertCount++;
this.saveFirstError(error);
}
_setAssertError(err) {
if (this.assertError !== undefined) {
return;
}
this.assertError = err;
saveFirstError(err) {
if (!this.assertError) {
this.assertError = err;
}
}
plan(count, planStack) {
if (typeof count !== 'number') {
throw new TypeError('Expected a number');
@ -148,103 +168,141 @@ class Test {
this.planCount = count;
// In case the `planCount` doesn't match `assertCount,
// we need the stack of this function to throw with a useful stack
// In case the `planCount` doesn't match `assertCount, we need the stack of
// this function to throw with a useful stack.
this.planStack = planStack;
}
_run() {
let ret;
verifyPlan() {
if (!this.assertError && this.planCount !== null && this.planCount !== this.assertCount) {
this.saveFirstError(new assert.AssertionError({
assertion: 'plan',
message: `Planned for ${this.planCount} ${plur('assertion', this.planCount)}, but got ${this.assertCount}.`,
operator: '===',
stack: this.planStack
}));
}
}
callFn() {
try {
ret = this.fn(this._createExecutionContext());
return {
ok: true,
retval: this.fn(this.createExecutionContext())
};
} catch (err) {
this.threwSync = true;
throwsHelper(err);
let error = err;
if (!(err instanceof assert.AssertionError)) {
error = new assert.AssertionError({
message: 'Error thrown in test',
stack: err instanceof Error && err.stack,
values: [formatAssertError.formatWithLabel('Error:', err)]
});
}
this._setAssertError(error);
return {
ok: false,
error: err
};
}
return ret;
}
promise() {
if (!this._promise) {
this._promise = {};
this._promise.promise = new Promise((resolve, reject) => {
this._promise.resolve = resolve;
this._promise.reject = reject;
}).then(result => {
if (this.report) {
this.report(result);
}
return result.passed;
});
}
return this._promise;
}
run() {
if (this.metadata.callback) {
this.sync = false;
}
this._timeStart = globals.now();
this.startedAt = globals.now();
// Wait until all assertions are complete
this._timeout = globals.setTimeout(() => {}, maxTimeout);
this.timeoutHandle = globals.setTimeout(() => {}, maxTimeout);
let ret = this._run();
let asyncType = 'promises';
const result = this.callFn();
if (!result.ok) {
throwsHelper(result.error);
if (isObservable(ret)) {
asyncType = 'observables';
ret = observableToPromise(ret);
this.saveFirstError(new assert.AssertionError({
message: 'Error thrown in test',
stack: result.error instanceof Error && result.error.stack,
values: [formatAssertError.formatWithLabel('Error:', result.error)]
}));
return this.finish();
}
if (isPromise(ret)) {
this.sync = false;
const returnedObservable = isObservable(result.retval);
const returnedPromise = isPromise(result.retval);
if (this.metadata.callback) {
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(...)\``));
let promise;
if (returnedObservable) {
promise = observableToPromise(result.retval);
} else if (returnedPromise) {
// `retval` can be any thenable, so convert to a proper promise.
promise = Promise.resolve(result.retval);
}
if (this.metadata.callback) {
if (returnedObservable || returnedPromise) {
const asyncType = returnedObservable ? 'observables' : 'promises';
this.saveFirstError(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(...)\``));
return this.finish();
}
if (this.calledEnd) {
return this.finish();
}
// Convert to a Bluebird promise
return Promise.resolve(ret).then(
() => this.exit(),
return new Promise(resolve => {
this.endCallbackFinisher = () => {
resolve(this.finishPromised());
};
});
}
if (promise) {
return promise.then(
() => this.finish(),
err => {
throwsHelper(err);
if (!(err instanceof assert.AssertionError)) {
err = new assert.AssertionError({
message: 'Rejected promise returned by test',
stack: err instanceof Error && err.stack,
values: [formatAssertError.formatWithLabel('Rejection reason:', err)]
});
}
this._setAssertError(err);
return this.exit();
this.saveFirstError(new assert.AssertionError({
message: 'Rejected promise returned by test',
stack: err instanceof Error && err.stack,
values: [formatAssertError.formatWithLabel('Rejection reason:', err)]
}));
return this.finish();
}
);
}
if (this.metadata.callback && !this.threwSync) {
return this.promise().promise;
return this.finish();
}
finish() {
if (this.pendingAssertions.length === 0) {
return this.finishImmediately();
}
return this.exit();
this.verifyPlan();
// Consume errors, ensuring there are no unhandled rejections.
const consumedErrors = Promise.all(this.pendingAssertions)
.catch(err => this.saveFirstError(err));
// Don't wait if there is an error.
if (this.assertError) {
return this.completeFinish();
}
// Finish after potential errors from pending assertions have been consumed.
// Note that the plan must be verified again in case a new assertion was
// added.
return consumedErrors.then(() => this.finishImmediately());
}
_result() {
finishPromised() {
return new Promise(resolve => {
resolve(this.finish());
});
}
finishImmediately() {
this.verifyPlan();
return this.completeFinish();
}
completeFinish() {
this.duration = globals.now() - this.startedAt;
globals.clearTimeout(this.timeoutHandle);
let reason = this.assertError;
let passed = reason === undefined;
let passed = !reason;
if (this.metadata.failing) {
passed = !passed;
@ -256,90 +314,13 @@ class Test {
}
}
return {
this.onResult({
passed,
result: this,
reason
};
}
get end() {
if (this.metadata.callback) {
return this._end.bind(this);
}
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)`');
}
_end(err, stack) {
if (err) {
if (!(err instanceof assert.AssertionError)) {
err = new assert.AssertionError({
actual: err,
message: 'Callback called with an error',
stack,
values: [formatAssertError.formatWithLabel('Error:', err)]
});
}
});
this._setAssertError(err);
this.exit();
return;
}
if (this.endCalled) {
this._setAssertError(new Error('`t.end()` called more than once'));
return;
}
this.endCalled = true;
this.exit();
}
_checkPlanCount() {
if (this.assertError === undefined && this.planCount !== null && this.planCount !== this.assertCount) {
this._setAssertError(new assert.AssertionError({
assertion: 'plan',
message: `Planned for ${this.planCount} ${plur('assertion', this.planCount)}, but got ${this.assertCount}.`,
operator: '===',
stack: this.planStack
}));
}
}
exit() {
this._checkPlanCount();
if (this.sync || this.threwSync) {
this.duration = globals.now() - this._timeStart;
globals.clearTimeout(this._timeout);
const result = this._result();
if (this.report) {
this.report(result);
}
return result.passed;
}
Promise.all(this.pendingAssertions)
.catch(err => {
this._setAssertError(err);
})
.finally(() => {
// Calculate total time spent in test
this.duration = globals.now() - this._timeStart;
// Stop infinite timer
globals.clearTimeout(this._timeout);
this._checkPlanCount();
this.promise().resolve(this._result());
});
return this.promise().promise;
}
_createExecutionContext() {
return new ExecutionContext(this);
return passed;
}
}

24
test/observable.js

@ -4,38 +4,34 @@ const Test = require('../lib/test');
const Observable = require('zen-observable'); // eslint-disable-line import/order
function ava(fn, onResult) {
const a = new Test(fn, null, null, onResult);
a.metadata = {callback: false};
return a;
return new Test({callback: false}, '[anonymous]', fn, null, onResult);
}
ava.cb = function (fn, onResult) {
const a = new Test(fn, null, null, onResult);
a.metadata = {callback: true};
return a;
return new Test({callback: true}, '[anonymous]', fn, null, onResult);
};
test('returning an observable from a legacy async fn is an error', t => {
let result;
ava.cb(a => {
const passed = ava.cb(a => {
a.plan(2);
const observable = Observable.of();
setTimeout(() => {
setImmediate(() => {
a.pass();
a.pass();
a.end();
}, 200);
});
return observable;
}, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.match(result.reason.message, /Do not return observables/);
t.end();
});
}).run();
t.is(passed, false);
t.match(result.reason.message, /Do not return observables/);
t.end();
});
test('handle throws with thrown observable', t => {

20
test/promise.js

@ -5,15 +5,11 @@ const formatValue = require('../lib/format-assert-error').formatValue;
const Test = require('../lib/test');
function ava(fn, onResult) {
const a = new Test(fn, null, null, onResult);
a.metadata = {callback: false};
return a;
return new Test({callback: false}, '[anonymous]', fn, null, onResult);
}
ava.cb = function (fn, onResult) {
const a = new Test(fn, null, null, onResult);
a.metadata = {callback: true};
return a;
return new Test({callback: true}, '[anonymous]', fn, null, onResult);
};
function pass() {
@ -32,7 +28,7 @@ function fail() {
test('returning a promise from a legacy async fn is an error', t => {
let result;
ava.cb(a => {
const passed = ava.cb(a => {
a.plan(1);
return Promise.resolve(true).then(() => {
@ -41,11 +37,11 @@ test('returning a promise from a legacy async fn is an error', t => {
});
}, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.match(result.reason.message, /Do not return promises/);
t.end();
});
}).run();
t.is(passed, false);
t.match(result.reason.message, /Do not return promises/);
t.end();
});
test('assertion plan is tested after returned promise resolves', t => {

273
test/test.js

@ -6,51 +6,26 @@ const formatValue = require('../lib/format-assert-error').formatValue;
const Test = require('../lib/test');
const failingTestHint = 'Test was expected to fail, but succeeded, you should stop marking the test as failing';
const noop = () => {};
function ava(title, fn, contextRef, onResult) {
const t = new Test(title, fn, contextRef, onResult);
t.metadata = {
callback: false
};
return t;
function ava(fn, contextRef, onResult) {
return new Test({callback: false}, '[anonymous]', fn, contextRef, onResult || noop);
}
ava.failing = (title, fn, contextRef, onResult) => {
const t = new Test(title, fn, contextRef, onResult);
t.metadata = {
callback: false,
failing: true
};
return t;
ava.failing = (fn, contextRef, onResult) => {
return new Test({callback: false, failing: true}, '[anonymous]', fn, contextRef, onResult || noop);
};
ava.cb = (title, fn, contextRef, onResult) => {
const t = new Test(title, fn, contextRef, onResult);
t.metadata = {
callback: true
};
return t;
ava.cb = (fn, contextRef, onResult) => {
return new Test({callback: true}, '[anonymous]', fn, contextRef, onResult || noop);
};
ava.cb.failing = (title, fn, contextRef, onResult) => {
const t = new Test(title, fn, contextRef, onResult);
t.metadata = {
callback: true,
failing: true
};
return t;
ava.cb.failing = (fn, contextRef, onResult) => {
return new Test({callback: true, failing: true}, '[anonymous]', fn, contextRef, onResult || noop);
};
test('run test', t => {
const passed = ava('foo', a => {
const passed = ava(a => {
a.fail();
}).run();
@ -58,51 +33,13 @@ test('run test', t => {
t.end();
});
test('title is optional', t => {
let result;
const passed = ava(a => {
a.pass();
}, null, null, r => {
result = r;
}).run();
t.is(passed, true);
t.is(result.result.title, '[anonymous]');
t.end();
});
test('callback is required', t => {
t.throws(() => {
ava();
}, /You must provide a callback/);
t.throws(() => {
ava('title');
}, /You must provide a callback/);
t.end();
});
test('infer name from function', t => {
let result;
const passed = ava(function foo(a) { // eslint-disable-line func-names, prefer-arrow-callback
a.pass();
}, null, null, r => {
result = r;
}).run();
t.is(passed, true);
t.is(result.result.title, 'foo');
t.end();
});
test('multiple asserts', t => {
let result;
const passed = ava(a => {
a.pass();
a.pass();
a.pass();
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -117,7 +54,7 @@ test('plan assertions', t => {
a.plan(2);
a.pass();
a.pass();
}, null, null, r => {
}, null, r => {
result = r;
}).run();
t.is(passed, true);
@ -133,7 +70,7 @@ test('run more assertions than planned', t => {
a.pass();
a.pass();
a.pass();
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -149,7 +86,7 @@ test('wrap non-assertion errors', t => {
let result;
const passed = ava(() => {
throw err;
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -172,34 +109,34 @@ test('end can be used as callback without maintaining thisArg', t => {
test('end can be used as callback with error', t => {
const err = new Error('failed');
let result;
ava.cb(a => {
const passed = ava.cb(a => {
a.end(err);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.is(result.reason.message, 'Callback called with an error');
t.is(result.reason.name, 'AssertionError');
t.same(result.reason.values, [{label: 'Error:', formatted: formatValue(err)}]);
t.end();
});
}).run();
t.is(passed, false);
t.is(result.reason.message, 'Callback called with an error');
t.is(result.reason.name, 'AssertionError');
t.same(result.reason.values, [{label: 'Error:', formatted: formatValue(err)}]);
t.end();
});
test('end can be used as callback with a non-error as its error argument', t => {
const nonError = {foo: 'bar'};
let result;
ava.cb(a => {
const passed = ava.cb(a => {
a.end(nonError);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.ok(result.reason);
t.is(result.reason.message, 'Callback called with an error');
t.is(result.reason.name, 'AssertionError');
t.same(result.reason.values, [{label: 'Error:', formatted: formatValue(nonError)}]);
t.end();
});
}).run();
t.is(passed, false);
t.ok(result.reason);
t.is(result.reason.message, 'Callback called with an error');
t.is(result.reason.name, 'AssertionError');
t.same(result.reason.values, [{label: 'Error:', formatted: formatValue(nonError)}]);
t.end();
});
test('handle non-assertion errors even when planned', t => {
@ -208,7 +145,7 @@ test('handle non-assertion errors even when planned', t => {
const passed = ava(a => {
a.plan(1);
throw err;
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -222,7 +159,7 @@ test('handle testing of arrays', t => {
let result;
const passed = ava(a => {
a.deepEqual(['foo', 'bar'], ['foo', 'bar']);
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -235,7 +172,7 @@ test('handle falsy testing of arrays', t => {
let result;
const passed = ava(a => {
a.notDeepEqual(['foo', 'bar'], ['foo', 'bar', 'cat']);
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -254,7 +191,7 @@ test('handle testing of objects', t => {
foo: 'foo',
bar: 'bar'
});
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -274,7 +211,7 @@ test('handle falsy testing of objects', t => {
bar: 'bar',
cat: 'cake'
});
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -283,35 +220,6 @@ test('handle falsy testing of objects', t => {
t.end();
});
test('run functions after last planned assertion', t => {
let i = 0;
const passed = ava(a => {
a.plan(1);
a.pass();
i++;
}).run();
t.is(i, 1);
t.is(passed, true);
t.end();
});
test('run async functions after last planned assertion', t => {
let i = 0;
ava.cb(a => {
a.plan(1);
a.pass();
a.end();
i++;
}).run().then(passed => {
t.is(i, 1);
t.is(passed, true);
t.end();
});
});
test('planned async assertion', t => {
let result;
ava.cb(a => {
@ -321,7 +229,7 @@ test('planned async assertion', t => {
a.pass();
a.end();
}, 100);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -337,7 +245,7 @@ test('async assertion with `.end()`', t => {
a.pass();
a.end();
}, 100);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -352,7 +260,7 @@ test('more assertions than planned should emit an assertion error', t => {
a.plan(1);
a.pass();
a.pass();
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -370,7 +278,7 @@ test('record test duration', t => {
a.true(true);
a.end();
}, 1234);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -387,7 +295,7 @@ test('wait for test to end', t => {
a.plan(1);
avaTest = a;
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -409,7 +317,7 @@ test('fails with the first assertError', t => {
a.plan(2);
a.is(1, 2);
a.is(3, 4);
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -426,7 +334,7 @@ test('fails with thrown falsy value', t => {
let result;
const passed = ava(() => {
throw 0; // eslint-disable-line no-throw-literal
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -442,7 +350,7 @@ test('fails with thrown non-error object', t => {
let result;
const passed = ava(() => {
throw obj;
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -459,7 +367,7 @@ test('skipped assertions count towards the plan', t => {
a.plan(2);
a.pass();
a.skip.fail();
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -478,7 +386,7 @@ test('throws and notThrows work with promises', t => {
a.notThrows(delay(20).then(() => {
asyncCalled = true;
}));
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -491,16 +399,16 @@ test('throws and notThrows work with promises', t => {
test('end should not be called multiple times', t => {
let result;
ava.cb(a => {
const passed = ava.cb(a => {
a.end();
a.end();
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.is(result.reason.message, '`t.end()` called more than once');
t.end();
});
}).run();
t.is(passed, false);
t.is(result.reason.message, '`t.end()` called more than once');
t.end();
});
test('cb test that throws sync', t => {
@ -508,7 +416,7 @@ test('cb test that throws sync', t => {
const err = new Error('foo');
const passed = ava.cb(() => {
throw err;
}, null, null, r => {
}, null, r => {
result = r;
}).run();
@ -524,7 +432,7 @@ test('waits for t.throws to resolve after t.end is called', t => {
a.plan(1);
a.notThrows(delay(10), 'foo');
a.end();
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -540,7 +448,7 @@ test('waits for t.throws to reject after t.end is called', t => {
a.plan(1);
a.throws(delay.reject(10, new Error('foo')), 'foo');
a.end();
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -556,7 +464,7 @@ test('waits for t.throws to resolve after the promise returned from the test res
a.plan(1);
a.notThrows(delay(10), 'foo');
return Promise.resolve();
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -572,7 +480,7 @@ test('waits for t.throws to reject after the promise returned from the test reso
a.plan(1);
a.throws(delay.reject(10, new Error('foo')), 'foo');
return Promise.resolve();
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -590,7 +498,7 @@ test('multiple resolving and rejecting promises passed to t.throws/t.notThrows',
a.throws(delay.reject(10, new Error('foo')), 'foo');
a.notThrows(delay(10), 'foo');
}
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
@ -609,7 +517,7 @@ test('number of assertions matches t.plan when the test exits, but before all pr
setTimeout(() => {
a.throws(Promise.reject(new Error('foo')), 'foo');
}, 5);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
@ -621,21 +529,21 @@ test('number of assertions matches t.plan when the test exits, but before all pr
test('number of assertions doesn\'t match plan when the test exits, but before all promises resolve another is added', t => {
let result;
ava(a => {
const passed = ava(a => {
a.plan(3);
a.throws(delay.reject(10, new Error('foo')), 'foo');
a.notThrows(delay(10), 'foo');
setTimeout(() => {
a.throws(Promise.reject(new Error('foo')), 'foo');
}, 5);
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.is(result.reason.assertion, 'plan');
t.is(result.reason.operator, '===');
t.end();
});
}).run();
t.is(passed, false);
t.is(result.reason.assertion, 'plan');
t.is(result.reason.operator, '===');
t.end();
});
test('assertions return promises', t => {
@ -650,12 +558,13 @@ test('assertions return promises', t => {
});
test('contextRef', t => {
new Test('foo',
new Test({}, 'foo',
a => {
t.strictDeepEqual(a.context, {foo: 'bar'});
t.end();
},
{context: {foo: 'bar'}}
{context: {foo: 'bar'}},
() => {}
).run();
});
@ -663,7 +572,7 @@ test('it is an error to set context in a hook', t => {
let result;
const avaTest = ava(a => {
a.context = 'foo';
}, null, null, r => {
}, null, r => {
result = r;
});
avaTest.metadata.type = 'foo';
@ -685,21 +594,17 @@ test('failing tests should fail', t => {
test('failing callback tests should end without error', t => {
const err = new Error('failed');
let result;
ava.cb.failing(a => {
const passed = ava.cb.failing(a => {
a.end(err);
}, null, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, true);
t.is(result.reason, undefined);
t.end();
});
}).run();
t.is(passed, true);
t.end();
});
test('failing tests must not pass', t => {
let result;
const passed = ava.failing('foo', a => {
const passed = ava.failing(a => {
a.pass();
}, null, r => {
result = r;
@ -711,23 +616,19 @@ test('failing tests must not pass', t => {
});
test('failing callback tests must not pass', t => {
let result;
ava.cb.failing(a => {
const passed = ava.cb.failing(a => {
a.end();
}, null, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.is(result.reason.message, failingTestHint);
t.end();
});
}).run();
t.is(passed, false);
t.end();
});
test('failing tests must not return a fulfilled promise', t => {
let result;
ava.failing(() => {
return Promise.resolve();
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
@ -760,7 +661,7 @@ test('failing tests fail with `t.notThrows(throws)`', t => {
let result;
ava.failing(a => {
a.notThrows(Promise.resolve('foo'));
}, null, null, r => {
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);

Loading…
Cancel
Save