diff --git a/lib/assert.js b/lib/assert.js
index 9868d6224..33dd64693 100644
--- a/lib/assert.js
+++ b/lib/assert.js
@@ -1,5 +1,6 @@
'use strict';
const concordance = require('concordance');
+const empower = require('empower-core');
const isError = require('is-error');
const isPromise = require('is-promise');
const concordanceOptions = require('./concordance-options').default;
@@ -27,6 +28,10 @@ function formatWithLabel(label, value) {
}
const hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
+const noop = () => {};
+const notImplemented = () => {
+ throw new Error('not implemented');
+};
class AssertionError extends Error {
constructor(opts) {
@@ -229,39 +234,65 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
}
}
-function wrapAssertions(callbacks) {
- const {pass, pending, fail} = callbacks;
- const noop = () => {};
+class Assertions {
+ constructor({
+ pass = notImplemented,
+ pending = notImplemented,
+ fail = notImplemented,
+ skip = notImplemented,
+ compareWithSnapshot = notImplemented
+ } = {}) {
+ const withSkip = assertionFn => {
+ assertionFn.skip = skip;
+ return assertionFn;
+ };
+
+ // When adding new enhanced functions with new patterns, don't forget to add to
+ // https://github.com/avajs/babel-preset-transform-test-files/blob/master/espower-patterns.json
+ // Then release a new version of that preset and bump the SemVer range here.
+ const withPowerAssert = (pattern, assertionFn) => empower(assertionFn, {
+ onError: event => {
+ if (event.powerAssertContext) {
+ event.error.statements = enhanceAssert.formatter(event.powerAssertContext);
+ }
- const assertions = {
- pass() {
- pass(this);
- },
+ fail(event.error);
+ },
+ onSuccess: () => {
+ pass();
+ },
+ bindReceiver: false,
+ patterns: [pattern]
+ });
- fail(message) {
- fail(this, new AssertionError({
+ this.pass = withSkip(() => {
+ pass();
+ });
+
+ this.fail = withSkip(message => {
+ fail(new AssertionError({
assertion: 'fail',
message: message || 'Test failed via `t.fail()`'
}));
- },
+ });
- is(actual, expected, message) {
+ this.is = withSkip((actual, expected, message) => {
if (Object.is(actual, expected)) {
- pass(this);
+ pass();
} else {
const result = concordance.compare(actual, expected, concordanceOptions);
const actualDescriptor = result.actual || concordance.describe(actual, concordanceOptions);
const expectedDescriptor = result.expected || concordance.describe(expected, concordanceOptions);
if (result.pass) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'is',
message,
raw: {actual, expected},
values: [formatDescriptorWithLabel('Values are deeply equal to each other, but they are not the same:', actualDescriptor)]
}));
} else {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'is',
message,
raw: {actual, expected},
@@ -269,55 +300,59 @@ function wrapAssertions(callbacks) {
}));
}
}
- },
+ });
- not(actual, expected, message) {
+ this.not = withSkip((actual, expected, message) => {
if (Object.is(actual, expected)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'not',
message,
raw: {actual, expected},
values: [formatWithLabel('Value is the same as:', actual)]
}));
} else {
- pass(this);
+ pass();
}
- },
+ });
- deepEqual(actual, expected, message) {
+ this.deepEqual = withSkip((actual, expected, message) => {
const result = concordance.compare(actual, expected, concordanceOptions);
if (result.pass) {
- pass(this);
+ pass();
} else {
const actualDescriptor = result.actual || concordance.describe(actual, concordanceOptions);
const expectedDescriptor = result.expected || concordance.describe(expected, concordanceOptions);
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'deepEqual',
message,
raw: {actual, expected},
values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)]
}));
}
- },
+ });
- notDeepEqual(actual, expected, message) {
+ this.notDeepEqual = withSkip((actual, expected, message) => {
const result = concordance.compare(actual, expected, concordanceOptions);
if (result.pass) {
const actualDescriptor = result.actual || concordance.describe(actual, concordanceOptions);
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notDeepEqual',
message,
raw: {actual, expected},
values: [formatDescriptorWithLabel('Value is deeply equal:', actualDescriptor)]
}));
} else {
- pass(this);
+ pass();
}
- },
+ });
- throws(fn, expectations, message) {
+ this.throws = withSkip((...args) => {
+ // Since arrow functions do not support 'arguments', we are using rest
+ // operator, so we can determine the total number of arguments passed
+ // to the function.
+ let [fn, expectations, message] = args;
if (typeof fn !== 'function') {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'throws',
improperUsage: true,
message: '`t.throws()` must be called with a function',
@@ -327,23 +362,20 @@ function wrapAssertions(callbacks) {
}
try {
- expectations = validateExpectations('throws', expectations, arguments.length);
+ expectations = validateExpectations('throws', expectations, args.length);
} catch (error) {
- fail(this, error);
+ fail(error);
return;
}
let retval;
- let actual;
- let threw = false;
+ let actual = null;
try {
retval = fn();
if (isPromise(retval)) {
- try {
- retval.catch(noop);
- } catch (_) {}
-
- fail(this, new AssertionError({
+ // Here isPromise() checks if something is "promise like". Cast to an actual promise.
+ Promise.resolve(retval).catch(noop);
+ fail(new AssertionError({
assertion: 'throws',
message,
values: [formatWithLabel('Function returned a promise. Use `t.throwsAsync()` instead:', retval)]
@@ -352,11 +384,10 @@ function wrapAssertions(callbacks) {
}
} catch (error) {
actual = error;
- threw = true;
}
- if (!threw) {
- fail(this, new AssertionError({
+ if (!actual) {
+ fail(new AssertionError({
assertion: 'throws',
message,
values: [formatWithLabel('Function returned:', retval)]
@@ -372,16 +403,17 @@ function wrapAssertions(callbacks) {
message,
prefix: 'Function threw'
});
- pass(this);
+ pass();
return actual;
} catch (error) {
- fail(this, error);
+ fail(error);
}
- },
+ });
- throwsAsync(thrower, expectations, message) {
+ this.throwsAsync = withSkip((...args) => {
+ let [thrower, expectations, message] = args;
if (typeof thrower !== 'function' && !isPromise(thrower)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'throwsAsync',
improperUsage: true,
message: '`t.throwsAsync()` must be called with a function or promise',
@@ -391,16 +423,17 @@ function wrapAssertions(callbacks) {
}
try {
- expectations = validateExpectations('throwsAsync', expectations, arguments.length);
+ expectations = validateExpectations('throwsAsync', expectations, args.length);
} catch (error) {
- fail(this, error);
+ fail(error);
return Promise.resolve();
}
const handlePromise = (promise, wasReturned) => {
// Record stack before it gets lost in the promise chain.
const stack = getStack();
- const intermediate = promise.then(value => { // eslint-disable-line promise/prefer-await-to-then
+ // Handle "promise like" objects by casting to a real Promise.
+ const intermediate = Promise.resolve(promise).then(value => { // eslint-disable-line promise/prefer-await-to-then
throw new AssertionError({
assertion: 'throwsAsync',
message,
@@ -419,7 +452,7 @@ function wrapAssertions(callbacks) {
return reason;
});
- pending(this, intermediate);
+ pending(intermediate);
// Don't reject the returned promise, even if the assertion fails.
return intermediate.catch(noop);
};
@@ -429,17 +462,15 @@ function wrapAssertions(callbacks) {
}
let retval;
- let actual;
- let threw = false;
+ let actual = null;
try {
retval = thrower();
} catch (error) {
actual = error;
- threw = true;
}
- if (threw) {
- fail(this, new AssertionError({
+ if (actual) {
+ fail(new AssertionError({
assertion: 'throwsAsync',
message,
actualStack: actual.stack,
@@ -452,17 +483,17 @@ function wrapAssertions(callbacks) {
return handlePromise(retval, true);
}
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'throwsAsync',
message,
values: [formatWithLabel('Function returned:', retval)]
}));
return Promise.resolve();
- },
+ });
- notThrows(fn, message) {
+ this.notThrows = withSkip((fn, message) => {
if (typeof fn !== 'function') {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notThrows',
improperUsage: true,
message: '`t.notThrows()` must be called with a function',
@@ -474,7 +505,7 @@ function wrapAssertions(callbacks) {
try {
fn();
} catch (error) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notThrows',
message,
actualStack: error.stack,
@@ -483,12 +514,12 @@ function wrapAssertions(callbacks) {
return;
}
- pass(this);
- },
+ pass();
+ });
- notThrowsAsync(nonThrower, message) {
+ this.notThrowsAsync = withSkip((nonThrower, message) => {
if (typeof nonThrower !== 'function' && !isPromise(nonThrower)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notThrowsAsync',
improperUsage: true,
message: '`t.notThrowsAsync()` must be called with a function or promise',
@@ -500,15 +531,16 @@ function wrapAssertions(callbacks) {
const handlePromise = (promise, wasReturned) => {
// Record stack before it gets lost in the promise chain.
const stack = getStack();
- const intermediate = promise.then(noop, reason => { // eslint-disable-line promise/prefer-await-to-then
+ // Handle "promise like" objects by casting to a real Promise.
+ const intermediate = Promise.resolve(promise).then(noop, error => { // eslint-disable-line promise/prefer-await-to-then
throw new AssertionError({
assertion: 'notThrowsAsync',
message,
actualStack: stack,
- values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} rejected with:`, reason)]
+ values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} rejected with:`, error)]
});
});
- pending(this, intermediate);
+ pending(intermediate);
// Don't reject the returned promise, even if the assertion fails.
return intermediate.catch(noop);
};
@@ -521,7 +553,7 @@ function wrapAssertions(callbacks) {
try {
retval = nonThrower();
} catch (error) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notThrowsAsync',
message,
actualStack: error.stack,
@@ -531,7 +563,7 @@ function wrapAssertions(callbacks) {
}
if (!isPromise(retval)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notThrowsAsync',
message,
values: [formatWithLabel('Function did not return a promise. Use `t.notThrows()` instead:', retval)]
@@ -540,9 +572,9 @@ function wrapAssertions(callbacks) {
}
return handlePromise(retval, true);
- },
+ });
- snapshot(expected, optionsOrMessage, message) {
+ this.snapshot = withSkip((expected, optionsOrMessage, message) => {
const options = {};
if (typeof optionsOrMessage === 'string') {
message = optionsOrMessage;
@@ -555,7 +587,7 @@ function wrapAssertions(callbacks) {
let result;
try {
- result = this.compareWithSnapshot(options);
+ result = compareWithSnapshot(options);
} catch (error) {
if (!(error instanceof snapshotManager.SnapshotError)) {
throw error;
@@ -567,7 +599,7 @@ function wrapAssertions(callbacks) {
improperUsage.expectedVersion = error.expectedVersion;
}
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'snapshot',
message: message || 'Could not compare snapshot',
improperUsage
@@ -576,74 +608,74 @@ function wrapAssertions(callbacks) {
}
if (result.pass) {
- pass(this);
+ pass();
} else if (result.actual) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'snapshot',
message: message || 'Did not match snapshot',
values: [formatDescriptorDiff(result.actual, result.expected, {invert: true})]
}));
} else {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'snapshot',
message: message || 'No snapshot available, run with --update-snapshots'
}));
}
- },
+ });
- truthy(actual, message) {
+ this.truthy = withSkip((actual, message) => {
if (actual) {
- pass(this);
+ pass();
} else {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'truthy',
message,
operator: '!!',
values: [formatWithLabel('Value is not truthy:', actual)]
}));
}
- },
+ });
- falsy(actual, message) {
+ this.falsy = withSkip((actual, message) => {
if (actual) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'falsy',
message,
operator: '!',
values: [formatWithLabel('Value is not falsy:', actual)]
}));
} else {
- pass(this);
+ pass();
}
- },
+ });
- true(actual, message) {
+ this.true = withSkip((actual, message) => {
if (actual === true) {
- pass(this);
+ pass();
} else {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'true',
message,
values: [formatWithLabel('Value is not `true`:', actual)]
}));
}
- },
+ });
- false(actual, message) {
+ this.false = withSkip((actual, message) => {
if (actual === false) {
- pass(this);
+ pass();
} else {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'false',
message,
values: [formatWithLabel('Value is not `false`:', actual)]
}));
}
- },
+ });
- regex(string, regex, message) {
+ this.regex = withSkip((string, regex, message) => {
if (typeof string !== 'string') {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'regex',
improperUsage: true,
message: '`t.regex()` must be called with a string',
@@ -653,7 +685,7 @@ function wrapAssertions(callbacks) {
}
if (!(regex instanceof RegExp)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'regex',
improperUsage: true,
message: '`t.regex()` must be called with a regular expression',
@@ -663,7 +695,7 @@ function wrapAssertions(callbacks) {
}
if (!regex.test(string)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'regex',
message,
values: [
@@ -674,12 +706,12 @@ function wrapAssertions(callbacks) {
return;
}
- pass(this);
- },
+ pass();
+ });
- notRegex(string, regex, message) {
+ this.notRegex = withSkip((string, regex, message) => {
if (typeof string !== 'string') {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notRegex',
improperUsage: true,
message: '`t.notRegex()` must be called with a string',
@@ -689,7 +721,7 @@ function wrapAssertions(callbacks) {
}
if (!(regex instanceof RegExp)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notRegex',
improperUsage: true,
message: '`t.notRegex()` must be called with a regular expression',
@@ -699,7 +731,7 @@ function wrapAssertions(callbacks) {
}
if (regex.test(string)) {
- fail(this, new AssertionError({
+ fail(new AssertionError({
assertion: 'notRegex',
message,
values: [
@@ -710,24 +742,22 @@ function wrapAssertions(callbacks) {
return;
}
- pass(this);
- }
- };
-
- const enhancedAssertions = enhanceAssert(pass, fail, {
- assert(actual, message) {
- if (!actual) {
- throw new AssertionError({
- assertion: 'assert',
- message,
- operator: '!!',
- values: [formatWithLabel('Value is not truthy:', actual)]
- });
- }
- }
- });
+ pass();
+ });
- return Object.assign(assertions, enhancedAssertions);
+ this.assert = withSkip(withPowerAssert(
+ 'assert(value, [message])',
+ (actual, message) => {
+ if (!actual) {
+ throw new AssertionError({
+ assertion: 'assert',
+ message,
+ operator: '!!',
+ values: [formatWithLabel('Value is not truthy:', actual)]
+ });
+ }
+ })
+ );
+ }
}
-
-exports.wrapAssertions = wrapAssertions;
+exports.Assertions = Assertions;
diff --git a/lib/enhance-assert.js b/lib/enhance-assert.js
index 88ace2290..e15f3ec95 100644
--- a/lib/enhance-assert.js
+++ b/lib/enhance-assert.js
@@ -4,13 +4,6 @@ const dotProp = require('dot-prop');
const generate = require('@babel/generator').default;
const concordanceOptions = require('./concordance-options').default;
-// When adding patterns, don't forget to add to
-// https://github.com/avajs/babel-preset-transform-test-files/blob/master/espower-patterns.json
-// Then release a new version of that preset and bump the SemVer range here.
-const PATTERNS = [
- 't.assert(value, [message])'
-];
-
const computeStatement = node => generate(node).code;
const getNode = (ast, path) => dotProp.get(ast, path.replace(/\//g, '.'));
@@ -27,24 +20,4 @@ const formatter = context => {
.reverse();
};
-const enhanceAssert = (pass, fail, assertions) => {
- const empower = require('empower-core');
- return empower(assertions, {
- destructive: true,
- onError(event) {
- const {error} = event;
- if (event.powerAssertContext) { // Context may be missing in internal tests.
- error.statements = formatter(event.powerAssertContext);
- }
-
- fail(this, error);
- },
- onSuccess() {
- pass(this);
- },
- patterns: PATTERNS,
- bindReceiver: false
- });
-};
-
-module.exports = enhanceAssert;
+module.exports.formatter = formatter;
diff --git a/lib/test.js b/lib/test.js
index dd7e9828f..a24884bcb 100644
--- a/lib/test.js
+++ b/lib/test.js
@@ -22,65 +22,51 @@ const captureStack = start => {
return obj.stack;
};
-const assertions = assert.wrapAssertions({
- pass(test) {
- test.countPassedAssertion();
- },
-
- pending(test, promise) {
- test.addPendingAssertion(promise);
- },
-
- fail(test, error) {
- test.addFailedAssertion(error);
- }
-});
-const assertionNames = Object.keys(assertions);
-
-function log(...inputArgs) {
- const args = inputArgs.map(value => {
- return typeof value === 'string' ?
- value :
- concordance.format(value, concordanceOptions);
- });
-
- if (args.length > 0) {
- this.addLog(args.join(' '));
- }
-}
-
-function plan(count) {
- this.plan(count, captureStack(this.plan));
-}
-
-function timeout(ms) {
- this.timeout(ms);
-}
-
const testMap = new WeakMap();
-class ExecutionContext {
+class ExecutionContext extends assert.Assertions {
constructor(test) {
+ super({
+ pass: () => {
+ test.countPassedAssertion();
+ },
+ pending: promise => {
+ test.addPendingAssertion(promise);
+ },
+ fail: err => {
+ test.addFailedAssertion(err);
+ },
+ skip: () => {
+ test.countPassedAssertion();
+ },
+ compareWithSnapshot: options => {
+ return test.compareWithSnapshot(options);
+ }
+ });
testMap.set(this, test);
- const skip = () => {
- test.countPassedAssertion();
+ this.snapshot.skip = () => {
+ test.skipSnapshot();
};
- const boundPlan = plan.bind(test);
- boundPlan.skip = () => {};
-
- Object.defineProperties(this, assertionNames.reduce((props, name) => {
- props[name] = {value: assertions[name].bind(test)};
- props[name].value.skip = skip;
- return props;
- }, {
- log: {value: log.bind(test)},
- plan: {value: boundPlan},
- timeout: {value: timeout.bind(test)}
- }));
+ this.log = (...inputArgs) => {
+ const args = inputArgs.map(value => {
+ return typeof value === 'string' ?
+ value :
+ concordance.format(value, concordanceOptions);
+ });
+ if (args.length > 0) {
+ test.addLog(args.join(' '));
+ }
+ };
- this.snapshot.skip = () => {
- test.skipSnapshot();
+ this.plan = count => {
+ test.plan(count, captureStack(test.plan));
+ };
+
+ this.plan.skip = () => {};
+
+ this.timeout = ms => {
+ test.timeout(ms);
};
}
diff --git a/test/assert.js b/test/assert.js
index 83566ce63..1ed0da746 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -9,40 +9,32 @@ const renderer = require('react-test-renderer');
const {test} = require('tap');
const assert = require('../lib/assert');
const snapshotManager = require('../lib/snapshot-manager');
-const Test = require('../lib/test');
const HelloMessage = require('./fixture/hello-message');
let lastFailure = null;
let lastPassed = false;
-const assertions = assert.wrapAssertions({
- pass(testObj) {
- if (testObj !== assertions && !(testObj instanceof Test)) {
- throw new Error('Expected testObj');
- }
-
- lastPassed = true;
- },
-
- pending(testObj, promise) {
- if (testObj !== assertions && !(testObj instanceof Test)) {
- throw new Error('Expected testObj');
- }
- promise.then(() => {
- lastPassed = true;
- }, err => {
- lastFailure = err;
+const assertions = new class extends assert.Assertions {
+ constructor(overwrites = {}) {
+ super({
+ pass: () => {
+ lastPassed = true;
+ },
+ pending: promise => {
+ promise.then(() => {
+ lastPassed = true;
+ }, err => {
+ lastFailure = err;
+ });
+ },
+ fail: error => {
+ lastFailure = error;
+ },
+ skip: () => {},
+ ...overwrites
});
- },
-
- fail(testObj, error) {
- if (testObj !== assertions && !(testObj instanceof Test)) {
- throw new Error('Expected testObj');
- }
-
- lastFailure = error;
}
-});
+}();
function assertFailure(t, subset) {
if (!lastFailure) {
@@ -178,6 +170,11 @@ test('.pass()', t => {
assertions.pass();
});
+ passes(t, () => {
+ const {pass} = assertions;
+ pass();
+ });
+
t.end();
});
@@ -196,6 +193,14 @@ test('.fail()', t => {
message: 'my message'
});
+ failsWith(t, () => {
+ const {fail} = assertions;
+ fail();
+ }, {
+ assertion: 'fail',
+ message: 'Test failed via `t.fail()`'
+ });
+
t.end();
});
@@ -204,6 +209,11 @@ test('.is()', t => {
assertions.is('foo', 'foo');
});
+ passes(t, () => {
+ const {is} = assertions;
+ is('foo', 'foo');
+ });
+
passes(t, () => {
assertions.is('', '');
});
@@ -381,6 +391,11 @@ test('.not()', t => {
assertions.not('foo', 'bar');
});
+ passes(t, () => {
+ const {not} = assertions;
+ not('foo', 'bar');
+ });
+
fails(t, () => {
assertions.not(NaN, NaN);
});
@@ -427,6 +442,11 @@ test('.deepEqual()', t => {
});
});
+ passes(t, () => {
+ const {deepEqual} = assertions;
+ deepEqual({a: 'a', b: 'b'}, {b: 'b', a: 'a'});
+ });
+
passes(t, () => {
assertions.deepEqual({
a: 'a',
@@ -670,6 +690,11 @@ test('.notDeepEqual()', t => {
assertions.notDeepEqual({a: 'a'}, {a: 'b'});
});
+ passes(t, () => {
+ const {notDeepEqual} = assertions;
+ notDeepEqual({a: 'a'}, {a: 'b'});
+ });
+
passes(t, () => {
assertions.notDeepEqual(['a', 'b'], ['c', 'd']);
});
@@ -708,6 +733,15 @@ test('.throws()', gather(t => {
values: [{label: 'Function returned:', formatted: /undefined/}]
});
+ failsWith(t, () => {
+ const {throws} = assertions;
+ throws(() => {});
+ }, {
+ assertion: 'throws',
+ message: '',
+ values: [{label: 'Function returned:', formatted: /undefined/}]
+ });
+
// Fails because function doesn't throw. Asserts that 'my message' is used
// as the assertion message (*not* compared against the error).
failsWith(t, () => {
@@ -866,6 +900,27 @@ test('.throws()', gather(t => {
throw err;
}, {code: 42});
});
+
+ // Regression test for https://github.com/avajs/ava/issues/1676
+ fails(t, () => {
+ assertions.throws(() => {
+ throw new Error('foo');
+ }, false);
+ });
+
+ // Regression test for https://github.com/avajs/ava/issues/1676
+ passes(t, () => {
+ assertions.throws(() => {
+ throw new Error('foo');
+ }, null);
+ });
+
+ // Regression test for https://github.com/avajs/ava/issues/1676
+ fails(t, () => {
+ assertions.throws(() => {
+ throw new Error('foo');
+ }, undefined);
+ });
}));
test('.throws() returns the thrown error', t => {
@@ -887,6 +942,15 @@ test('.throwsAsync()', gather(t => {
values: [{label: 'Promise resolved with:', formatted: /'foo'/}]
});
+ eventuallyFailsWith(t, () => {
+ const {throwsAsync} = assertions;
+ return throwsAsync(Promise.resolve('foo'));
+ }, {
+ assertion: 'throwsAsync',
+ message: '',
+ values: [{label: 'Promise resolved with:', formatted: /'foo'/}]
+ });
+
// Fails because the promise is resolved with an Error
eventuallyFailsWith(t, () => assertions.throwsAsync(Promise.resolve(new Error())), {
assertion: 'throwsAsync',
@@ -1127,6 +1191,11 @@ test('.notThrows()', gather(t => {
assertions.notThrows(() => {});
});
+ passes(t, () => {
+ const {notThrows} = assertions;
+ notThrows(() => {});
+ });
+
// Fails because the function throws.
failsWith(t, () => {
assertions.notThrows(() => {
@@ -1155,6 +1224,11 @@ test('.notThrowsAsync()', gather(t => {
// Passes because the promise is resolved
eventuallyPasses(t, () => assertions.notThrowsAsync(Promise.resolve()));
+ eventuallyPasses(t, () => {
+ const {notThrowsAsync} = assertions;
+ return notThrowsAsync(Promise.resolve());
+ });
+
// Fails because the promise is rejected
eventuallyFailsWith(t, () => assertions.notThrowsAsync(Promise.reject(new Error())), {
assertion: 'notThrowsAsync',
@@ -1245,28 +1319,57 @@ test('.snapshot()', t => {
fixedLocation: null,
updating
});
- const setup = title => {
- return new Test({
- title,
- compareTestSnapshot: options => manager.compare(options)
- });
+ const setup = _title => {
+ return new class extends assertions.constructor {
+ constructor(title) {
+ super({
+ compareWithSnapshot: assertionOptions => {
+ return manager.compare({
+ belongsTo: assertionOptions.id || this.title,
+ expected: assertionOptions.expected,
+ index: assertionOptions.id ? 0 : this.snapshotInvocationCount++,
+ label: assertionOptions.id ? '' : assertionOptions.message || `Snapshot ${this.snapshotInvocationCount}`
+ });
+ }
+ });
+ this.title = title;
+ this.snapshotInvocationCount = 0;
+ }
+ }(_title);
};
- passes(t, () => {
- const testInstance = setup('passes');
- assertions.snapshot.call(testInstance, {foo: 'bar'});
- assertions.snapshot.call(testInstance, {foo: 'bar'}, {id: 'fixed id'}, 'message not included in snapshot report');
- assertions.snapshot.call(testInstance, React.createElement(HelloMessage, {name: 'Sindre'}));
- assertions.snapshot.call(testInstance, renderer.create(React.createElement(HelloMessage, {name: 'Sindre'})).toJSON());
- });
+ {
+ const assertions = setup('passes');
+
+ passes(t, () => {
+ assertions.snapshot({foo: 'bar'});
+ });
+
+ passes(t, () => {
+ const {snapshot} = assertions;
+ snapshot({foo: 'bar'});
+ });
+
+ passes(t, () => {
+ assertions.snapshot({foo: 'bar'}, {id: 'fixed id'}, 'message not included in snapshot report');
+ });
+
+ passes(t, () => {
+ assertions.snapshot(React.createElement(HelloMessage, {name: 'Sindre'}));
+ });
+
+ passes(t, () => {
+ assertions.snapshot(renderer.create(React.createElement(HelloMessage, {name: 'Sindre'})).toJSON());
+ });
+ }
{
- const testInstance = setup('fails');
+ const assertions = setup('fails');
if (updating) {
- assertions.snapshot.call(testInstance, {foo: 'bar'});
+ assertions.snapshot({foo: 'bar'});
} else {
failsWith(t, () => {
- assertions.snapshot.call(testInstance, {foo: 'not bar'});
+ assertions.snapshot({foo: 'not bar'});
}, {
assertion: 'snapshot',
message: 'Did not match snapshot',
@@ -1276,8 +1379,8 @@ test('.snapshot()', t => {
}
failsWith(t, () => {
- const testInstance = setup('fails (fixed id)');
- assertions.snapshot.call(testInstance, {foo: 'not bar'}, {id: 'fixed id'}, 'different message, also not included in snapshot report');
+ const assertions = setup('fails (fixed id)');
+ assertions.snapshot({foo: 'not bar'}, {id: 'fixed id'}, 'different message, also not included in snapshot report');
}, {
assertion: 'snapshot',
message: 'different message, also not included in snapshot report',
@@ -1285,12 +1388,12 @@ test('.snapshot()', t => {
});
{
- const testInstance = setup('fails');
+ const assertions = setup('fails');
if (updating) {
- assertions.snapshot.call(testInstance, {foo: 'bar'}, 'my message');
+ assertions.snapshot({foo: 'bar'}, 'my message');
} else {
failsWith(t, () => {
- assertions.snapshot.call(testInstance, {foo: 'not bar'}, 'my message');
+ assertions.snapshot({foo: 'not bar'}, 'my message');
}, {
assertion: 'snapshot',
message: 'my message',
@@ -1300,23 +1403,23 @@ test('.snapshot()', t => {
}
{
- const testInstance = setup('rendered comparison');
+ const assertions = setup('rendered comparison');
if (updating) {
- assertions.snapshot.call(testInstance, renderer.create(React.createElement(HelloMessage, {name: 'Sindre'})).toJSON());
+ assertions.snapshot(renderer.create(React.createElement(HelloMessage, {name: 'Sindre'})).toJSON());
} else {
passes(t, () => {
- assertions.snapshot.call(testInstance, React.createElement('div', null, 'Hello ', React.createElement('mark', null, 'Sindre')));
+ assertions.snapshot(React.createElement('div', null, 'Hello ', React.createElement('mark', null, 'Sindre')));
});
}
}
{
- const testInstance = setup('rendered comparison');
+ const assertions = setup('rendered comparison');
if (updating) {
- assertions.snapshot.call(testInstance, renderer.create(React.createElement(HelloMessage, {name: 'Sindre'})).toJSON());
+ assertions.snapshot(renderer.create(React.createElement(HelloMessage, {name: 'Sindre'})).toJSON());
} else {
failsWith(t, () => {
- assertions.snapshot.call(testInstance, renderer.create(React.createElement(HelloMessage, {name: 'Vadim'})).toJSON());
+ assertions.snapshot(renderer.create(React.createElement(HelloMessage, {name: 'Vadim'})).toJSON());
}, {
assertion: 'snapshot',
message: 'Did not match snapshot',
@@ -1326,12 +1429,12 @@ test('.snapshot()', t => {
}
{
- const testInstance = setup('element comparison');
+ const assertions = setup('element comparison');
if (updating) {
- assertions.snapshot.call(testInstance, React.createElement(HelloMessage, {name: 'Sindre'}));
+ assertions.snapshot(React.createElement(HelloMessage, {name: 'Sindre'}));
} else {
failsWith(t, () => {
- assertions.snapshot.call(testInstance, React.createElement(HelloMessage, {name: 'Vadim'}));
+ assertions.snapshot(React.createElement(HelloMessage, {name: 'Vadim'}));
}, {
assertion: 'snapshot',
message: 'Did not match snapshot',
@@ -1368,6 +1471,12 @@ test('.truthy()', t => {
assertions.truthy(true);
});
+ passes(t, () => {
+ const {truthy} = assertions;
+ truthy(1);
+ truthy(true);
+ });
+
t.end();
});
@@ -1395,6 +1504,12 @@ test('.falsy()', t => {
assertions.falsy(false);
});
+ passes(t, () => {
+ const {falsy} = assertions;
+ falsy(0);
+ falsy(false);
+ });
+
t.end();
});
@@ -1435,6 +1550,11 @@ test('.true()', t => {
assertions.true(true);
});
+ passes(t, () => {
+ const {true: trueFn} = assertions;
+ trueFn(true);
+ });
+
t.end();
});
@@ -1475,6 +1595,11 @@ test('.false()', t => {
assertions.false(false);
});
+ passes(t, () => {
+ const {false: falseFn} = assertions;
+ falseFn(false);
+ });
+
t.end();
});
@@ -1483,6 +1608,11 @@ test('.regex()', t => {
assertions.regex('abc', /^abc$/);
});
+ passes(t, () => {
+ const {regex} = assertions;
+ regex('abc', /^abc$/);
+ });
+
failsWith(t, () => {
assertions.regex('foo', /^abc$/);
}, {
@@ -1534,6 +1664,11 @@ test('.notRegex()', t => {
assertions.notRegex('abc', /def/);
});
+ passes(t, () => {
+ const {notRegex} = assertions;
+ notRegex('abc', /def/);
+ });
+
failsWith(t, () => {
assertions.notRegex('abc', /abc/);
}, {
@@ -1603,5 +1738,11 @@ test('.assert()', t => {
assertions.assert(true);
});
+ passes(t, () => {
+ const {assert} = assertions;
+ assert(1);
+ assert(true);
+ });
+
t.end();
});
diff --git a/test/fixture/assert.js.md b/test/fixture/assert.js.md
index effe453d7..a3d865488 100644
--- a/test/fixture/assert.js.md
+++ b/test/fixture/assert.js.md
@@ -1,4 +1,4 @@
-# Snapshot report for `test/assert.js`
+# Snapshot report for `assert.js`
The actual snapshot is saved in `assert.js.snap`.
@@ -36,11 +36,17 @@ Generated by [AVA](https://ava.li).
> Snapshot 2
+ {
+ foo: 'bar',
+ }
+
+> Snapshot 3
+