diff --git a/docs/03-assertions.md b/docs/03-assertions.md index f61c3e2d8..5d1039bf5 100644 --- a/docs/03-assertions.md +++ b/docs/03-assertions.md @@ -84,7 +84,7 @@ test('skip assertion', t => { ## Enhanced assertion messages -AVA comes with [`power-assert`](https://github.com/power-assert-js/power-assert) built-in, giving you more descriptive assertion messages. It reads your test and tries to infer more information from the code. +AVA comes with [`power-assert`](https://github.com/power-assert-js/power-assert) built-in, giving you more descriptive assertion messages. Let's take this example, using Node's standard [`assert` library](https://nodejs.org/api/assert.html): @@ -101,24 +101,48 @@ If you paste that into a Node REPL it'll return: AssertionError: false == true ``` -In AVA however, this test: +With AVA's `assert` assertion however, this test: ```js test('enhanced assertions', t => { const a = /foo/; const b = 'bar'; const c = 'baz'; - t.true(a.test(b) || b === c); + t.assert(a.test(b) || b === c); }); ``` Will output: ``` -t.true(a.test(b) || b === c) - | | | | - | "bar" "bar" "baz" - false +6: const c = 'baz'; +7: t.assert(a.test(b) || b === c); +8: }); + +Value is not truthy: + +false + +a.test(b) || b === c +=> false + +b === c +=> false + +c +=> 'baz' + +b +=> 'bar' + +a.test(b) +=> false + +b +=> 'bar' + +a +=> /foo/ ``` ## Custom assertions @@ -147,6 +171,10 @@ Passing assertion. Failing assertion. +### `.assert(value, [message])` + +Asserts that `value` is truthy. This is [`power-assert`](#enhanced-assertion-messages) enabled. + ### `.truthy(value, [message])` Assert that `value` is truthy. diff --git a/docs/06-configuration.md b/docs/06-configuration.md index cae316d76..e86f1d140 100644 --- a/docs/06-configuration.md +++ b/docs/06-configuration.md @@ -57,8 +57,8 @@ Arguments passed to the CLI will always take precedence over the CLI options con - `tap`: if `true`, enables the [TAP reporter](./05-command-line.md#tap-reporter) - `verbose`: if `true`, enables verbose output - `snapshotDir`: specifies a fixed location for storing snapshot files. Use this if your snapshots are ending up in the wrong location -- `compileEnhancements`: if `false`, disables [power-assert](https://github.com/power-assert-js/power-assert) — which otherwise helps provide more descriptive error messages — and detection of improper use of the `t.throws()` assertion -- `extensions`: extensions of test files that are not precompiled using AVA's Babel presets. Note that files are still compiled to enable power-assert and other features, so you may also need to set `compileEnhancements` to `false` if your files are not valid JavaScript. Setting this overrides the default `"js"` value, so make sure to include that extension in the list, as long as it's not included in `babel.extensions` +- `compileEnhancements`: if `false`, disables [`power-assert`](./03-assertions.md#enhanced-assertion-messages) — which otherwise helps provide more descriptive error messages — and detection of improper use of the `t.throws()` assertion +- `extensions`: extensions of test files that are not precompiled using AVA's Babel presets. Note that files are still compiled to enable `power-assert` and other features, so you may also need to set `compileEnhancements` to `false` if your files are not valid JavaScript. Setting this overrides the default `"js"` value, so make sure to include that extension in the list, as long as it's not included in `babel.extensions` - `require`: extra modules to require before tests are run. Modules are required in the [worker processes](./01-writing-tests.md#process-isolation) - `babel`: test file specific Babel options. See our [Babel recipe](./recipes/babel.md#configuring-babel) for more details - `babel.extensions`: extensions of test files that will be precompiled using AVA's Babel presets. Setting this overrides the default `"js"` value, so make sure to include that extension in the list diff --git a/docs/08-common-pitfalls.md b/docs/08-common-pitfalls.md index 902c8a2ab..a9f51c91c 100644 --- a/docs/08-common-pitfalls.md +++ b/docs/08-common-pitfalls.md @@ -75,11 +75,11 @@ AVA [can't trace uncaught exceptions](https://github.com/avajs/ava/issues/214) b ### Why are the enhanced assertion messages not shown? -Ensure that the first parameter passed into your test is named `t`. This is a requirement of [`power-assert`](https://github.com/power-assert-js/power-assert), the library that provides the enhanced messages. +Ensure that the first parameter passed into your test is named `t`. This is a requirement of [`power-assert`](https://github.com/power-assert-js/power-assert), the library that provides the [enhanced messages](./03-assertions.md#enhanced-assertion-messages). ```js test('one is one', t => { - t.is(1, 1); + t.assert(1 === 1); }); ``` diff --git a/lib/assert.js b/lib/assert.js index 3e0cb7037..1a9dccf11 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -589,110 +589,139 @@ function wrapAssertions(callbacks) { message: message || 'No snapshot available, run with --update-snapshots' })); } - } - }; + }, - const enhancedAssertions = enhanceAssert(pass, fail, { truthy(actual, message) { - if (!actual) { - throw new AssertionError({ + if (actual) { + pass(this); + } else { + fail(this, new AssertionError({ assertion: 'truthy', message, operator: '!!', values: [formatWithLabel('Value is not truthy:', actual)] - }); + })); } }, falsy(actual, message) { if (actual) { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'falsy', message, operator: '!', values: [formatWithLabel('Value is not falsy:', actual)] - }); + })); + } else { + pass(this); } }, true(actual, message) { - if (actual !== true) { - throw new AssertionError({ + if (actual === true) { + pass(this); + } else { + fail(this, new AssertionError({ assertion: 'true', message, values: [formatWithLabel('Value is not `true`:', actual)] - }); + })); } }, false(actual, message) { - if (actual !== false) { - throw new AssertionError({ + if (actual === false) { + pass(this); + } else { + fail(this, new AssertionError({ assertion: 'false', message, values: [formatWithLabel('Value is not `false`:', actual)] - }); + })); } }, regex(string, regex, message) { if (typeof string !== 'string') { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'regex', improperUsage: true, message: '`t.regex()` must be called with a string', values: [formatWithLabel('Called with:', string)] - }); + })); + return; } if (!(regex instanceof RegExp)) { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'regex', improperUsage: true, message: '`t.regex()` must be called with a regular expression', values: [formatWithLabel('Called with:', regex)] - }); + })); + return; } if (!regex.test(string)) { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'regex', message, values: [ formatWithLabel('Value must match expression:', string), formatWithLabel('Regular expression:', regex) ] - }); + })); + return; } + + pass(this); }, notRegex(string, regex, message) { if (typeof string !== 'string') { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'notRegex', improperUsage: true, message: '`t.notRegex()` must be called with a string', values: [formatWithLabel('Called with:', string)] - }); + })); + return; } if (!(regex instanceof RegExp)) { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'notRegex', improperUsage: true, message: '`t.notRegex()` must be called with a regular expression', values: [formatWithLabel('Called with:', regex)] - }); + })); + return; } if (regex.test(string)) { - throw new AssertionError({ + fail(this, new AssertionError({ assertion: 'notRegex', message, values: [ formatWithLabel('Value must not match expression:', string), formatWithLabel('Regular expression:', regex) ] + })); + 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)] }); } } diff --git a/lib/enhance-assert.js b/lib/enhance-assert.js index ee8db37c2..88ace2290 100644 --- a/lib/enhance-assert.js +++ b/lib/enhance-assert.js @@ -8,12 +8,7 @@ const concordanceOptions = require('./concordance-options').default; // 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.truthy(value, [message])', - 't.falsy(value, [message])', - 't.true(value, [message])', - 't.false(value, [message])', - 't.regex(contents, regex, [message])', - 't.notRegex(contents, regex, [message])' + 't.assert(value, [message])' ]; const computeStatement = node => generate(node).code; diff --git a/media/magic-assert-combined.png b/media/magic-assert-combined.png index d4e7a81cc..cde62565c 100644 Binary files a/media/magic-assert-combined.png and b/media/magic-assert-combined.png differ diff --git a/media/magic-assert-nested.png b/media/magic-assert-nested.png deleted file mode 100644 index bef4ef1d5..000000000 Binary files a/media/magic-assert-nested.png and /dev/null differ diff --git a/media/power-assert.png b/media/power-assert.png new file mode 100644 index 000000000..b6b119ff4 Binary files /dev/null and b/media/power-assert.png differ diff --git a/media/screenshot-fixtures/magic-assert-nested.js b/media/screenshot-fixtures/magic-assert-nested.js deleted file mode 100644 index dd0033f30..000000000 --- a/media/screenshot-fixtures/magic-assert-nested.js +++ /dev/null @@ -1,11 +0,0 @@ -import test from 'ava'; - -test('nested', t => { - const actual = { - a: { - b: false - } - }; - - t.true(actual.a.b); -}); diff --git a/media/screenshot-fixtures/package.json b/media/screenshot-fixtures/package.json new file mode 100644 index 000000000..d0aed5198 --- /dev/null +++ b/media/screenshot-fixtures/package.json @@ -0,0 +1,11 @@ +{ + "name": "screenshot-fixtures", + "version": "1.0.0", + "description": "", + "main": "magic-assert-buffers.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "" +} diff --git a/media/screenshot-fixtures/power-assert.js b/media/screenshot-fixtures/power-assert.js new file mode 100644 index 000000000..065fd7251 --- /dev/null +++ b/media/screenshot-fixtures/power-assert.js @@ -0,0 +1,8 @@ +import test from 'ava'; + +test('power-assert', t => { + const a = /foo/; + const b = 'bar'; + const c = 'baz'; + t.assert(a.test(b) || b === c); +}); diff --git a/package-lock.json b/package-lock.json index 32a1a26b2..f85275939 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,9 +24,9 @@ } }, "@ava/babel-preset-transform-test-files": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@ava/babel-preset-transform-test-files/-/babel-preset-transform-test-files-4.0.1.tgz", - "integrity": "sha512-D7Z92B8Rgsj35JZveKJGwpUDuBKLiRKH6eyKpNmDHy7TJjr8y3VSDr3bUK+O456F3SkkBXrUihQuMrr39nWQhQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@ava/babel-preset-transform-test-files/-/babel-preset-transform-test-files-5.0.0.tgz", + "integrity": "sha512-rqgyQwkT0+j2JzYP51dOv80u33rzAvjBtXRzUON+7+6u26mjoudRXci2+1s18rat8r4uOlZfbzm114YS6pwmYw==", "requires": { "@ava/babel-plugin-throws-helper": "^3.0.0", "babel-plugin-espower": "^3.0.1" diff --git a/package.json b/package.json index 0991f2378..10e8dbf78 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ ], "dependencies": { "@ava/babel-preset-stage-4": "^2.0.0", - "@ava/babel-preset-transform-test-files": "^4.0.1", + "@ava/babel-preset-transform-test-files": "^5.0.0", "@ava/write-file-atomic": "^2.2.0", "@babel/core": "^7.3.4", "@babel/generator": "^7.3.4", diff --git a/test/api.js b/test/api.js index f828f1bb0..9aa99826d 100644 --- a/test/api.js +++ b/test/api.js @@ -433,6 +433,7 @@ test('enhanced assertion formatting necessary whitespace and empty strings', t = /foo/ ], [ + /!\(new Object\(foo\) instanceof Object\)/, /new Object\(foo\) instanceof Object/, /Object/, /new Object\(foo\)/, @@ -447,7 +448,7 @@ test('enhanced assertion formatting necessary whitespace and empty strings', t = ] ]; - t.plan(14); + t.plan(15); const api = apiCreator(); const errors = []; api.on('run', plan => { diff --git a/test/assert.js b/test/assert.js index 40cb2c4e1..83566ce63 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1513,6 +1513,7 @@ test('.regex() fails if passed a bad value', t => { assertions.regex(42, /foo/); }, { assertion: 'regex', + improperUsage: true, message: '`t.regex()` must be called with a string', values: [{label: 'Called with:', formatted: /42/}] }); @@ -1577,3 +1578,30 @@ test('.notRegex() fails if passed a bad value', t => { t.end(); }); + +test('.assert()', t => { + failsWith(t, () => { + assertions.assert(0); + }, { + assertion: 'assert', + message: '', + operator: '!!', + values: [{label: 'Value is not truthy:', formatted: /0/}] + }); + + failsWith(t, () => { + assertions.assert(false, 'my message'); + }, { + assertion: 'assert', + message: 'my message', + operator: '!!', + values: [{label: 'Value is not truthy:', formatted: /false/}] + }); + + passes(t, () => { + assertions.assert(1); + assertions.assert(true); + }); + + t.end(); +}); diff --git a/test/fixture/enhanced-assertion-formatting.js b/test/fixture/enhanced-assertion-formatting.js index 748de1d6f..db544cb73 100644 --- a/test/fixture/enhanced-assertion-formatting.js +++ b/test/fixture/enhanced-assertion-formatting.js @@ -4,16 +4,16 @@ const foo = 'foo'; test('fails with multiple empty string expressions and mixed quotes', t => { // eslint-disable-next-line quotes, yoda - t.true(foo === '' && "" === foo); + t.assert(foo === '' && "" === foo); }); test('fails with "instanceof" expression', t => { // eslint-disable-next-line no-new-object - t.false(new Object(foo) instanceof Object); + t.assert(!(new Object(foo) instanceof Object)); }); test('fails with multiple lines', t => { - t.true( + t.assert( [foo].filter(item => { return item === 'bar'; }).length > 0 diff --git a/test/fixture/just-enhancement-compilation/custom-extension/power-assert.foo b/test/fixture/just-enhancement-compilation/custom-extension/power-assert.foo index acbf11ade..44bfdab7a 100644 --- a/test/fixture/just-enhancement-compilation/custom-extension/power-assert.foo +++ b/test/fixture/just-enhancement-compilation/custom-extension/power-assert.foo @@ -4,5 +4,5 @@ const test = require('../../../..'); test('test', t => { const bool = false; - t.true(bool); + t.assert(bool); }); diff --git a/test/fixture/just-enhancement-compilation/power-assert.js b/test/fixture/just-enhancement-compilation/power-assert.js index ea21f47f2..a36700d80 100644 --- a/test/fixture/just-enhancement-compilation/power-assert.js +++ b/test/fixture/just-enhancement-compilation/power-assert.js @@ -4,5 +4,5 @@ const test = require('../../..'); test('test', t => { const bool = false; - t.true(bool); + t.assert(bool); }); diff --git a/test/fixture/no-babel-compilation/no-power-assert.js b/test/fixture/no-babel-compilation/no-power-assert.js index ea21f47f2..a36700d80 100644 --- a/test/fixture/no-babel-compilation/no-power-assert.js +++ b/test/fixture/no-babel-compilation/no-power-assert.js @@ -4,5 +4,5 @@ const test = require('../../..'); test('test', t => { const bool = false; - t.true(bool); + t.assert(bool); }); diff --git a/test/fixture/report/regular/test.js b/test/fixture/report/regular/test.js index 86feeac82..57ce82793 100644 --- a/test/fixture/report/regular/test.js +++ b/test/fixture/report/regular/test.js @@ -26,8 +26,8 @@ test('formatted', t => { }); test('power-assert', t => { - const foo = 'bar'; - t.falsy(foo); + const foo = ''; + t.assert(foo); }); test('bad throws', t => { diff --git a/test/reporters/mini.regular.log b/test/reporters/mini.regular.log index 0974b6767..57861c9af 100644 --- a/test/reporters/mini.regular.log +++ b/test/reporters/mini.regular.log @@ -325,16 +325,16 @@ stderr ~/test/fixture/report/regular/test.js:30 - 29: const foo = 'bar'; -  30: t.falsy(foo);  - 31: }); + 29: const foo = ''; +  30: t.assert(foo);  + 31: }); - Value is not falsy: + Value is not truthy: - 'bar' + '' foo - => 'bar' + => '' diff --git a/test/reporters/tap.regular.log b/test/reporters/tap.regular.log index 4c54560ff..bd6701e6f 100644 --- a/test/reporters/tap.regular.log +++ b/test/reporters/tap.regular.log @@ -174,11 +174,11 @@ not ok 22 - test › formatted not ok 23 - test › power-assert --- name: AssertionError - assertion: falsy - operator: '!' + assertion: assert + operator: '!!' values: - 'Value is not falsy:': '''bar''' - at: 'falsy (test.js:30:4)' + 'Value is not truthy:': '''''' + at: 'assert (test.js:30:4)' ... ---tty-stream-chunk-separator # test › bad throws diff --git a/test/reporters/verbose.regular.log b/test/reporters/verbose.regular.log index 6379a8493..b0a0fd050 100644 --- a/test/reporters/verbose.regular.log +++ b/test/reporters/verbose.regular.log @@ -286,16 +286,16 @@ stderr ~/test/fixture/report/regular/test.js:30 - 29: const foo = 'bar'; -  30: t.falsy(foo);  - 31: }); + 29: const foo = ''; +  30: t.assert(foo);  + 31: }); - Value is not falsy: + Value is not truthy: - 'bar' + '' foo - => 'bar' + => ''