From 19c4f35fbae740268dedeeb4d450f813f2d5e85e Mon Sep 17 00:00:00 2001 From: futpib Date: Sun, 14 Jun 2020 16:45:26 +0300 Subject: [PATCH] Experimentally implement `t.like()` assertion Co-authored-by: Mark Wubben --- docs/03-assertions.md | 49 +++++ index.d.ts | 11 + lib/assert.js | 59 ++++- lib/like-selector.js | 37 ++++ lib/load-config.js | 2 +- lib/test.js | 3 +- test-d/like.ts | 17 ++ test-tap/assert.js | 207 ++++++++++++++++++ .../fixture/report/regular/nested-objects.js | 26 +++ test-tap/helper/report.js | 4 +- test-tap/reporters/mini.regular.v10.log | 70 ++++-- test-tap/reporters/mini.regular.v12.log | 69 ++++-- test-tap/reporters/mini.regular.v13.log | 69 ++++-- test-tap/reporters/mini.regular.v14.log | 69 ++++-- test-tap/reporters/tap.regular.v10.log | 78 ++++--- test-tap/reporters/tap.regular.v12.log | 76 ++++--- test-tap/reporters/tap.regular.v13.log | 76 ++++--- test-tap/reporters/tap.regular.v14.log | 76 ++++--- test-tap/reporters/verbose.regular.v10.log | 28 ++- test-tap/reporters/verbose.regular.v12.log | 27 ++- test-tap/reporters/verbose.regular.v13.log | 27 ++- test-tap/reporters/verbose.regular.v14.log | 27 ++- test-tap/test.js | 19 +- test/assertions/fixtures/happy-path.js | 16 ++ test/assertions/fixtures/package.json | 5 +- test/assertions/snapshots/test.js.md | 1 + test/assertions/snapshots/test.js.snap | Bin 415 -> 484 bytes 27 files changed, 930 insertions(+), 218 deletions(-) create mode 100644 lib/like-selector.js create mode 100644 test-d/like.ts diff --git a/docs/03-assertions.md b/docs/03-assertions.md index 0f60c88c3..1e0a46dd4 100644 --- a/docs/03-assertions.md +++ b/docs/03-assertions.md @@ -207,6 +207,55 @@ Assert that `value` is deeply equal to `expected`. See [Concordance](https://git Assert that `value` is not deeply equal to `expected`. The inverse of `.deepEqual()`. +### `.like(value, selector, message?)` + +Assert that `value` is like `selector`. This is a variant of `.deepEqual()`, however `selector` does not need to have the same enumerable properties as `value` does. + +Instead AVA derives a *comparable* object from `value`, based on the deeply-nested properties of `selector`. This object is then compared to `selector` using `.deepEqual()`. + +Any values in `selector` that are not regular objects should be deeply equal to the corresponding values in `value`. + +This is an experimental assertion for the time being. You need to enable it: + +**`package.json`**: + +```json +{ + "ava": { + "nonSemVerExperiments": { + "likeAssertion": true + } + } +} +``` + +**`ava.config.js`**: + +```js +export default { + nonSemVerExperiments: { + likeAssertion: true + } +} +``` + +In the following example, the `map` property of `value` must be deeply equal to that of `selector`. However `nested.qux` is ignored, because it's not in `selector`. + +```js +t.like({ + map: new Map([['foo', 'bar']]), + nested: { + baz: 'thud', + qux: 'quux' + } +}, { + map: new Map([['foo', 'bar']]), + nested: { + baz: 'thud', + } +}) +``` + ### `.throws(fn, expectation?, message?)` Assert that an error is thrown. `fn` must be a function which should throw. The thrown value *must* be an error. It is returned so you can run more assertions against it. diff --git a/index.d.ts b/index.d.ts index 835be8035..f839a9867 100644 --- a/index.d.ts +++ b/index.d.ts @@ -45,6 +45,9 @@ export interface Assertions { /** Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to `expected`. */ deepEqual: DeepEqualAssertion; + /** Assert that `actual` is like `expected`. */ + like: LikeAssertion; + /** Fail the test. */ fail: FailAssertion; @@ -125,6 +128,14 @@ export interface DeepEqualAssertion { skip(actual: any, expected: any, message?: string): void; } +export interface LikeAssertion { + /** Assert that `value` is like `selector`. */ + (value: any, selector: object, message?: string): void; + + /** Skip this assertion. */ + skip(value: any, selector: any, message?: string): void; +} + export interface FailAssertion { /** Fail the test. */ (message?: string): void; diff --git a/lib/assert.js b/lib/assert.js index 24f570522..39b3f0d5a 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -3,6 +3,7 @@ const concordance = require('concordance'); const isError = require('is-error'); const isPromise = require('is-promise'); const concordanceOptions = require('./concordance-options').default; +const {CIRCULAR_SELECTOR, isLikeSelector, selectComparable} = require('./like-selector'); const snapshotManager = require('./snapshot-manager'); function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) { @@ -241,7 +242,8 @@ class Assertions { fail = notImplemented, skip = notImplemented, compareWithSnapshot = notImplemented, - powerAssert + powerAssert, + experiments = {} } = {}) { const withSkip = assertionFn => { assertionFn.skip = skip; @@ -386,6 +388,61 @@ class Assertions { } }); + this.like = withSkip((actual, selector, message) => { + if (!experiments.likeAssertion) { + fail(new AssertionError({ + assertion: 'like', + improperUsage: true, + message: 'You must enable the `likeAssertion` experiment in order to use `t.like()`' + })); + return; + } + + if (!checkMessage('like', message)) { + return; + } + + if (!isLikeSelector(selector)) { + fail(new AssertionError({ + assertion: 'like', + improperUsage: true, + message: '`t.like()` selector must be a non-empty object', + values: [formatWithLabel('Called with:', selector)] + })); + return; + } + + let comparable; + try { + comparable = selectComparable(actual, selector); + } catch (error) { + if (error === CIRCULAR_SELECTOR) { + fail(new AssertionError({ + assertion: 'like', + improperUsage: true, + message: '`t.like()` selector must not contain circular references', + values: [formatWithLabel('Called with:', selector)] + })); + return; + } + + throw error; + } + + const result = concordance.compare(comparable, selector, concordanceOptions); + if (result.pass) { + pass(); + } else { + const actualDescriptor = result.actual || concordance.describe(comparable, concordanceOptions); + const expectedDescriptor = result.expected || concordance.describe(selector, concordanceOptions); + fail(new AssertionError({ + assertion: 'like', + message, + values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)] + })); + } + }); + 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 diff --git a/lib/like-selector.js b/lib/like-selector.js new file mode 100644 index 000000000..fc720a59a --- /dev/null +++ b/lib/like-selector.js @@ -0,0 +1,37 @@ +'use strict'; +function isLikeSelector(selector) { + return selector !== null && + typeof selector === 'object' && + Reflect.getPrototypeOf(selector) === Object.prototype && + Reflect.ownKeys(selector).length > 0; +} + +exports.isLikeSelector = isLikeSelector; + +const CIRCULAR_SELECTOR = new Error('Encountered a circular selector'); +exports.CIRCULAR_SELECTOR = CIRCULAR_SELECTOR; + +function selectComparable(lhs, selector, circular = new Set()) { + if (circular.has(selector)) { + throw CIRCULAR_SELECTOR; + } + + circular.add(selector); + + if (lhs === null || typeof lhs !== 'object') { + return lhs; + } + + const comparable = {}; + for (const [key, rhs] of Object.entries(selector)) { + if (isLikeSelector(rhs)) { + comparable[key] = selectComparable(Reflect.get(lhs, key), rhs, circular); + } else { + comparable[key] = Reflect.get(lhs, key); + } + } + + return comparable; +} + +exports.selectComparable = selectComparable; diff --git a/lib/load-config.js b/lib/load-config.js index ef6659ba1..c815344c7 100644 --- a/lib/load-config.js +++ b/lib/load-config.js @@ -7,7 +7,7 @@ const pkgConf = require('pkg-conf'); const NO_SUCH_FILE = Symbol('no ava.config.js file'); const MISSING_DEFAULT_EXPORT = Symbol('missing default export'); -const EXPERIMENTS = new Set(['reverseTeardowns']); +const EXPERIMENTS = new Set(['likeAssertion', 'reverseTeardowns']); // *Very* rudimentary support for loading ava.config.js files containing an `export default` statement. const evaluateJsConfig = configFile => { diff --git a/lib/test.js b/lib/test.js index cc26034b1..e75a0e09a 100644 --- a/lib/test.js +++ b/lib/test.js @@ -39,7 +39,8 @@ class ExecutionContext extends assert.Assertions { compareWithSnapshot: options => { return test.compareWithSnapshot(options); }, - powerAssert: test.powerAssert + powerAssert: test.powerAssert, + experiments: test.experiments }); testMap.set(this, test); diff --git a/test-d/like.ts b/test-d/like.ts new file mode 100644 index 000000000..d5b9a18b0 --- /dev/null +++ b/test-d/like.ts @@ -0,0 +1,17 @@ +import test from '..'; + +test('like', t => { + t.like({ + map: new Map([['foo', 'bar']]), + nested: { + baz: 'thud', + qux: 'quux' + } + }, { + map: new Map([['foo', 'bar']]), + nested: { + baz: 'thud' + } + }); +}); + diff --git a/test-tap/assert.js b/test-tap/assert.js index e33ed8062..d817945ce 100644 --- a/test-tap/assert.js +++ b/test-tap/assert.js @@ -31,6 +31,9 @@ const assertions = new class extends assert.Assertions { lastFailure = error; }, skip: () => {}, + experiments: { + likeAssertion: true + }, ...overwrites }); } @@ -783,6 +786,210 @@ test('.notDeepEqual()', t => { t.end(); }); +test('.like()', t => { + fails(t, () => { + assertions.like({a: false}, {a: 0}); + }); + + passes(t, () => { + assertions.like({ + a: 'a', + b: 'b' + }, { + b: 'b', + a: 'a' + }); + }); + + passes(t, () => { + const {like} = assertions; + like({a: 'a', b: 'b'}, {b: 'b', a: 'a'}); + }); + + passes(t, () => { + assertions.like({ + a: 'a', + b: 'b', + c: { + d: 'd', + x: 'x' + }, + x: 'x' + }, { + c: { + d: 'd' + }, + b: 'b', + a: 'a' + }); + }); + + fails(t, () => { + assertions.like([1, 2, 3], [1, 2, 3, 4]); + }); + + fails(t, () => { + assertions.like({ + a: [1, 2, 3] + }, { + a: [1, 2, 3, 4] + }); + }); + + passes(t, () => { + assertions.like({ + a: [1, 2, 3], + x: 'x' + }, { + a: [1, 2, 3] + }); + }); + + passes(t, () => { + const actual = { + a: 'a', + extra: 'irrelevant' + }; + actual.circular = actual; + + const likePattern = { + a: 'a' + }; + + assertions.like(actual, likePattern); + }); + + fails(t, () => { + const fnA = a => a; + const fnB = a => a; + assertions.like(fnA, fnB); + }); + + fails(t, () => { + const fnA = a => a; + const fnB = a => a; + assertions.like({ + fn: fnA + }, { + fn: fnB + }); + }); + + fails(t, () => { + function Foo(a) { + this.a = a; + } + + function Bar(a) { + this.a = a; + } + + const x = new Foo(1); + const y = new Bar(1); + + assertions.like(x, y); + }); + + passes(t, () => { + assertions.like({a: 'a'}, {a: 'a'}); + }); + + passes(t, () => { + assertions.like({a: 'a', b: 'b'}, {a: 'a'}); + }); + + passes(t, () => { + assertions.like({ab: ['a', 'b']}, {ab: ['a', 'b']}); + }); + + passes(t, () => { + assertions.like({ab: ['a', 'b'], c: 'c'}, {ab: ['a', 'b']}); + }); + + fails(t, () => { + assertions.like({a: 'a'}, {a: 'b'}); + }); + + fails(t, () => { + assertions.like({a: 'a', b: 'b'}, {a: 'b'}); + }); + + fails(t, () => { + assertions.like({ab: ['a', 'b']}, {ab: ['a', 'a']}); + }); + + fails(t, () => { + assertions.like({ab: ['a', 'b'], c: 'c'}, {ab: ['a', 'a']}); + }); + + fails(t, () => { + assertions.like([['a', 'b'], 'c'], [['a', 'b'], 'd']); + }); + + fails(t, () => { + const circular = ['a', 'b']; + circular.push(circular); + assertions.like([circular, 'c'], [circular, 'd']); + }); + + fails(t, () => { + const circular = ['a', 'b']; + circular.push(circular); + assertions.like({xc: [circular, 'c']}, {xc: [circular, 'd']}); + }); + + failsWith(t, () => { + assertions.like({a: 'a'}, {}); + }, { + assertion: 'like', + message: '`t.like()` selector must be a non-empty object', + values: [{label: 'Called with:', formatted: '{}'}] + }); + + failsWith(t, () => { + assertions.like('foo', 'bar'); + }, { + assertion: 'like', + message: '`t.like()` selector must be a non-empty object', + values: [{label: 'Called with:', formatted: '\'bar\''}] + }); + + failsWith(t, () => { + const likePattern = { + a: 'a' + }; + likePattern.circular = likePattern; + + assertions.like({}, likePattern); + }, { + assertion: 'like', + message: '`t.like()` selector must not contain circular references', + values: [{label: 'Called with:', formatted: '{\n a: \'a\',\n circular: [Circular],\n}'}] + }); + + failsWith(t, () => { + assertions.like({}, {}, null); + }, { + assertion: 'like', + improperUsage: true, + message: 'The assertion message must be a string', + values: [{ + label: 'Called with:', + formatted: /null/ + }] + }); + + failsWith(t, () => { + assertions.like({a: 'foo', b: 'irrelevant'}, {a: 'bar'}); + }, { + assertion: 'like', + message: '', + values: [{label: 'Difference:', formatted: /{\n-\s*a: 'foo',\n\+\s*a: 'bar',\n\s*}/}] + }); + + t.end(); +}); + test('.throws()', gather(t => { // Fails because function doesn't throw. failsWith(t, () => { diff --git a/test-tap/fixture/report/regular/nested-objects.js b/test-tap/fixture/report/regular/nested-objects.js index 596ef9542..3acef04c2 100644 --- a/test-tap/fixture/report/regular/nested-objects.js +++ b/test-tap/fixture/report/regular/nested-objects.js @@ -27,3 +27,29 @@ test('format with max depth 4', t => { }; t.deepEqual(exp, act); }); + +test('format like with max depth 4', t => { + const pattern = { + a: { + b: { + foo: 'qux' + } + } + }; + const actual = { + a: { + b: { + foo: 'bar', + extra: 'irrelevant' + } + }, + c: { + d: { + e: { + foo: 'bar' + } + } + } + }; + t.like(actual, pattern); +}); diff --git a/test-tap/helper/report.js b/test-tap/helper/report.js index ccb267b20..08c865828 100644 --- a/test-tap/helper/report.js +++ b/test-tap/helper/report.js @@ -52,7 +52,7 @@ exports.assert = (t, logFile, buffer) => { // Log the entire actual and expected values, so they can be diffed // manually. TAP's diff output is really confusing in this situation. console.dir({actual, expected}); - t.fail('Output did not match expectation'); + t.fail(`Output did not match expectation: ${logFile}`); } }; @@ -88,7 +88,7 @@ const run = (type, reporter, {match = [], filter} = {}) => { serial: type === 'failFast' || type === 'failFast2', require: [], cacheEnabled: true, - experiments: {}, + experiments: {likeAssertion: true}, match, providers, projectDir, diff --git a/test-tap/reporters/mini.regular.v10.log b/test-tap/reporters/mini.regular.v10.log index 4d8fa611e..9a5cea281 100644 --- a/test-tap/reporters/mini.regular.v10.log +++ b/test-tap/reporters/mini.regular.v10.log @@ -7,27 +7,31 @@ 1 test failed---tty-stream-chunk-separator ---tty-stream-chunk-separator +* nested-objects › format like with max depth 4 + + 2 tests failed---tty-stream-chunk-separator +---tty-stream-chunk-separator * output-in-hook › passing test 1 passed - 1 test failed---tty-stream-chunk-separator + 2 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * output-in-hook › failing test 1 passed - 2 tests failed---tty-stream-chunk-separator + 3 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › passes 2 passed - 2 tests failed + 3 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › fails 2 passed - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -35,7 +39,7 @@ 2 passed 1 known failure - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -43,7 +47,7 @@ 2 passed 1 known failure - 4 tests failed + 5 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -51,7 +55,7 @@ 2 passed 1 known failure - 5 tests failed + 6 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -59,7 +63,7 @@ 2 passed 1 known failure - 6 tests failed + 7 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -67,7 +71,7 @@ 2 passed 1 known failure - 7 tests failed + 8 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -75,7 +79,7 @@ 2 passed 1 known failure - 8 tests failed + 9 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -83,7 +87,7 @@ 2 passed 1 known failure - 9 tests failed + 10 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -91,7 +95,7 @@ 2 passed 1 known failure - 10 tests failed + 11 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -99,7 +103,7 @@ 2 passed 1 known failure - 11 tests failed + 12 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -107,7 +111,7 @@ 2 passed 1 known failure - 12 tests failed + 13 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -115,7 +119,7 @@ 2 passed 1 known failure - 13 tests failed + 14 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -123,7 +127,7 @@ 2 passed 1 known failure - 14 tests failed + 15 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -131,7 +135,7 @@ 2 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -139,7 +143,7 @@ 3 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -147,7 +151,7 @@ 4 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -155,7 +159,7 @@ 5 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -193,6 +197,30 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › t (test-tap/fixture/report/regular/nested-objects.js:54:4) + › process._tickCallback (internal/process/next_tick.js:68:7) + + + output-in-hook › failing test output-in-hook.js:34 @@ -518,7 +546,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/mini.regular.v12.log b/test-tap/reporters/mini.regular.v12.log index 8fb66a639..af1efa390 100644 --- a/test-tap/reporters/mini.regular.v12.log +++ b/test-tap/reporters/mini.regular.v12.log @@ -7,27 +7,31 @@ 1 test failed---tty-stream-chunk-separator ---tty-stream-chunk-separator +* nested-objects › format like with max depth 4 + + 2 tests failed---tty-stream-chunk-separator +---tty-stream-chunk-separator * output-in-hook › passing test 1 passed - 1 test failed---tty-stream-chunk-separator + 2 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * output-in-hook › failing test 1 passed - 2 tests failed---tty-stream-chunk-separator + 3 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › passes 2 passed - 2 tests failed + 3 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › fails 2 passed - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -35,7 +39,7 @@ 2 passed 1 known failure - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -43,7 +47,7 @@ 2 passed 1 known failure - 4 tests failed + 5 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -51,7 +55,7 @@ 2 passed 1 known failure - 5 tests failed + 6 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -59,7 +63,7 @@ 2 passed 1 known failure - 6 tests failed + 7 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -67,7 +71,7 @@ 2 passed 1 known failure - 7 tests failed + 8 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -75,7 +79,7 @@ 2 passed 1 known failure - 8 tests failed + 9 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -83,7 +87,7 @@ 2 passed 1 known failure - 9 tests failed + 10 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -91,7 +95,7 @@ 2 passed 1 known failure - 10 tests failed + 11 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -99,7 +103,7 @@ 2 passed 1 known failure - 11 tests failed + 12 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -107,7 +111,7 @@ 2 passed 1 known failure - 12 tests failed + 13 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -115,7 +119,7 @@ 2 passed 1 known failure - 13 tests failed + 14 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -123,7 +127,7 @@ 2 passed 1 known failure - 14 tests failed + 15 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -131,7 +135,7 @@ 2 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -139,7 +143,7 @@ 3 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -147,7 +151,7 @@ 4 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -155,7 +159,7 @@ 5 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -192,6 +196,29 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › test-tap/fixture/report/regular/nested-objects.js:54:4 + + + output-in-hook › failing test output-in-hook.js:34 @@ -502,7 +529,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/mini.regular.v13.log b/test-tap/reporters/mini.regular.v13.log index 8fb66a639..af1efa390 100644 --- a/test-tap/reporters/mini.regular.v13.log +++ b/test-tap/reporters/mini.regular.v13.log @@ -7,27 +7,31 @@ 1 test failed---tty-stream-chunk-separator ---tty-stream-chunk-separator +* nested-objects › format like with max depth 4 + + 2 tests failed---tty-stream-chunk-separator +---tty-stream-chunk-separator * output-in-hook › passing test 1 passed - 1 test failed---tty-stream-chunk-separator + 2 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * output-in-hook › failing test 1 passed - 2 tests failed---tty-stream-chunk-separator + 3 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › passes 2 passed - 2 tests failed + 3 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › fails 2 passed - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -35,7 +39,7 @@ 2 passed 1 known failure - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -43,7 +47,7 @@ 2 passed 1 known failure - 4 tests failed + 5 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -51,7 +55,7 @@ 2 passed 1 known failure - 5 tests failed + 6 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -59,7 +63,7 @@ 2 passed 1 known failure - 6 tests failed + 7 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -67,7 +71,7 @@ 2 passed 1 known failure - 7 tests failed + 8 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -75,7 +79,7 @@ 2 passed 1 known failure - 8 tests failed + 9 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -83,7 +87,7 @@ 2 passed 1 known failure - 9 tests failed + 10 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -91,7 +95,7 @@ 2 passed 1 known failure - 10 tests failed + 11 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -99,7 +103,7 @@ 2 passed 1 known failure - 11 tests failed + 12 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -107,7 +111,7 @@ 2 passed 1 known failure - 12 tests failed + 13 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -115,7 +119,7 @@ 2 passed 1 known failure - 13 tests failed + 14 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -123,7 +127,7 @@ 2 passed 1 known failure - 14 tests failed + 15 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -131,7 +135,7 @@ 2 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -139,7 +143,7 @@ 3 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -147,7 +151,7 @@ 4 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -155,7 +159,7 @@ 5 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -192,6 +196,29 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › test-tap/fixture/report/regular/nested-objects.js:54:4 + + + output-in-hook › failing test output-in-hook.js:34 @@ -502,7 +529,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/mini.regular.v14.log b/test-tap/reporters/mini.regular.v14.log index 8fb66a639..af1efa390 100644 --- a/test-tap/reporters/mini.regular.v14.log +++ b/test-tap/reporters/mini.regular.v14.log @@ -7,27 +7,31 @@ 1 test failed---tty-stream-chunk-separator ---tty-stream-chunk-separator +* nested-objects › format like with max depth 4 + + 2 tests failed---tty-stream-chunk-separator +---tty-stream-chunk-separator * output-in-hook › passing test 1 passed - 1 test failed---tty-stream-chunk-separator + 2 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * output-in-hook › failing test 1 passed - 2 tests failed---tty-stream-chunk-separator + 3 tests failed---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › passes 2 passed - 2 tests failed + 3 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator * test › fails 2 passed - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -35,7 +39,7 @@ 2 passed 1 known failure - 3 tests failed + 4 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -43,7 +47,7 @@ 2 passed 1 known failure - 4 tests failed + 5 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -51,7 +55,7 @@ 2 passed 1 known failure - 5 tests failed + 6 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -59,7 +63,7 @@ 2 passed 1 known failure - 6 tests failed + 7 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -67,7 +71,7 @@ 2 passed 1 known failure - 7 tests failed + 8 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -75,7 +79,7 @@ 2 passed 1 known failure - 8 tests failed + 9 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -83,7 +87,7 @@ 2 passed 1 known failure - 9 tests failed + 10 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -91,7 +95,7 @@ 2 passed 1 known failure - 10 tests failed + 11 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -99,7 +103,7 @@ 2 passed 1 known failure - 11 tests failed + 12 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -107,7 +111,7 @@ 2 passed 1 known failure - 12 tests failed + 13 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -115,7 +119,7 @@ 2 passed 1 known failure - 13 tests failed + 14 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -123,7 +127,7 @@ 2 passed 1 known failure - 14 tests failed + 15 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -131,7 +135,7 @@ 2 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -139,7 +143,7 @@ 3 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -147,7 +151,7 @@ 4 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -155,7 +159,7 @@ 5 passed 1 known failure - 15 tests failed + 16 tests failed 1 skipped 1 todo---tty-stream-chunk-separator ---tty-stream-chunk-separator @@ -192,6 +196,29 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › test-tap/fixture/report/regular/nested-objects.js:54:4 + + + output-in-hook › failing test output-in-hook.js:34 @@ -502,7 +529,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/tap.regular.v10.log b/test-tap/reporters/tap.regular.v10.log index c8a217caa..09ad84f4c 100644 --- a/test-tap/reporters/tap.regular.v10.log +++ b/test-tap/reporters/tap.regular.v10.log @@ -37,6 +37,26 @@ not ok 3 - nested-objects › format with max depth 4 process._tickCallback (internal/process/next_tick.js:68:7) ... ---tty-stream-chunk-separator +# nested-objects › format like with max depth 4 +not ok 4 - nested-objects › format like with max depth 4 + --- + name: AssertionError + assertion: like + values: + 'Difference:': |2- + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + at: |- + t (test-tap/fixture/report/regular/nested-objects.js:54:4) + process._tickCallback (internal/process/next_tick.js:68:7) + ... +---tty-stream-chunk-separator # output-in-hook › before hook ---tty-stream-chunk-separator # output-in-hook › before hook @@ -52,10 +72,10 @@ not ok 3 - nested-objects › format with max depth 4 # beforeEach ---tty-stream-chunk-separator # output-in-hook › passing test -ok 4 - output-in-hook › passing test +ok 5 - output-in-hook › passing test ---tty-stream-chunk-separator # output-in-hook › failing test -not ok 5 - output-in-hook › failing test +not ok 6 - output-in-hook › failing test --- name: AssertionError message: Test failed via `t.fail()` @@ -82,16 +102,16 @@ not ok 5 - output-in-hook › failing test # afterAlways ---tty-stream-chunk-separator # test › skip -ok 6 - test › skip # SKIP +ok 7 - test › skip # SKIP ---tty-stream-chunk-separator # test › todo -not ok 7 - test › todo # TODO +not ok 8 - test › todo # TODO ---tty-stream-chunk-separator # test › passes -ok 8 - test › passes +ok 9 - test › passes ---tty-stream-chunk-separator # test › fails -not ok 9 - test › fails +not ok 10 - test › fails --- name: AssertionError message: Test failed via `t.fail()` @@ -102,10 +122,10 @@ not ok 9 - test › fails ... ---tty-stream-chunk-separator # test › known failure -ok 10 - test › known failure +ok 11 - test › known failure ---tty-stream-chunk-separator # test › no longer failing -not ok 11 - test › no longer failing +not ok 12 - test › no longer failing --- name: Error message: >- @@ -115,7 +135,7 @@ not ok 11 - test › no longer failing ... ---tty-stream-chunk-separator # test › logs -not ok 12 - test › logs +not ok 13 - test › logs * hello * world --- @@ -128,7 +148,7 @@ not ok 12 - test › logs ... ---tty-stream-chunk-separator # test › formatted -not ok 13 - test › formatted +not ok 14 - test › formatted --- name: AssertionError assertion: deepEqual @@ -142,7 +162,7 @@ not ok 13 - test › formatted ... ---tty-stream-chunk-separator # test › power-assert -not ok 14 - test › power-assert +not ok 15 - test › power-assert --- name: AssertionError assertion: assert @@ -155,7 +175,7 @@ not ok 14 - test › power-assert ... ---tty-stream-chunk-separator # test › bad throws -not ok 15 - test › bad throws +not ok 16 - test › bad throws --- name: AssertionError message: Improper usage of `t.throws()` detected @@ -172,7 +192,7 @@ not ok 15 - test › bad throws ... ---tty-stream-chunk-separator # test › bad notThrows -not ok 16 - test › bad notThrows +not ok 17 - test › bad notThrows --- name: AssertionError message: Improper usage of `t.notThrows()` detected @@ -189,7 +209,7 @@ not ok 16 - test › bad notThrows ... ---tty-stream-chunk-separator # test › implementation throws non-error -not ok 17 - test › implementation throws non-error +not ok 18 - test › implementation throws non-error --- name: AssertionError message: Error thrown in test @@ -199,7 +219,7 @@ not ok 17 - test › implementation throws non-error ... ---tty-stream-chunk-separator # traces-in-t-throws › throws -not ok 18 - traces-in-t-throws › throws +not ok 19 - traces-in-t-throws › throws --- name: AssertionError assertion: throws @@ -221,7 +241,7 @@ not ok 18 - traces-in-t-throws › throws ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrows -not ok 19 - traces-in-t-throws › notThrows +not ok 20 - traces-in-t-throws › notThrows --- name: AssertionError assertion: notThrows @@ -242,7 +262,7 @@ not ok 19 - traces-in-t-throws › notThrows ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrowsAsync -not ok 20 - traces-in-t-throws › notThrowsAsync +not ok 21 - traces-in-t-throws › notThrowsAsync --- name: AssertionError assertion: notThrowsAsync @@ -259,7 +279,7 @@ not ok 20 - traces-in-t-throws › notThrowsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync -not ok 21 - traces-in-t-throws › throwsAsync +not ok 22 - traces-in-t-throws › throwsAsync --- name: AssertionError assertion: throwsAsync @@ -276,7 +296,7 @@ not ok 21 - traces-in-t-throws › throwsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync different error -not ok 22 - traces-in-t-throws › throwsAsync different error +not ok 23 - traces-in-t-throws › throwsAsync different error --- name: AssertionError assertion: throwsAsync @@ -296,10 +316,10 @@ not ok 22 - traces-in-t-throws › throwsAsync different erro ... ---tty-stream-chunk-separator # uncaught-exception › passes -ok 23 - uncaught-exception › passes +ok 24 - uncaught-exception › passes ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 24 - Error: Can’t catch me +not ok 25 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -307,16 +327,16 @@ not ok 24 - Error: Can’t catch me ... ---tty-stream-chunk-separator # uncaught-exception.js exited with a non-zero exit code: 1 -not ok 25 - uncaught-exception.js exited with a non-zero exit code: 1 +not ok 26 - uncaught-exception.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator # unhandled-rejection › passes -ok 26 - unhandled-rejection › passes +ok 27 - unhandled-rejection › passes ---tty-stream-chunk-separator # unhandled-rejection › unhandled non-error rejection -ok 27 - unhandled-rejection › unhandled non-error rejection +ok 28 - unhandled-rejection › unhandled non-error rejection ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 28 - Error: Can’t catch me +not ok 29 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -326,17 +346,17 @@ not ok 28 - Error: Can’t catch me ... ---tty-stream-chunk-separator # unhandled-rejection -not ok 29 - unhandled-rejection +not ok 30 - unhandled-rejection --- message: Non-error object formatted: 'null' ... ---tty-stream-chunk-separator -1..23 -# tests 22 +1..24 +# tests 23 # pass 6 # skip 1 -# fail 22 +# fail 23 ---tty-stream-chunk-separator diff --git a/test-tap/reporters/tap.regular.v12.log b/test-tap/reporters/tap.regular.v12.log index 79e93cc86..92dabd285 100644 --- a/test-tap/reporters/tap.regular.v12.log +++ b/test-tap/reporters/tap.regular.v12.log @@ -35,6 +35,24 @@ not ok 3 - nested-objects › format with max depth 4 at: 'test-tap/fixture/report/regular/nested-objects.js:28:4' ... ---tty-stream-chunk-separator +# nested-objects › format like with max depth 4 +not ok 4 - nested-objects › format like with max depth 4 + --- + name: AssertionError + assertion: like + values: + 'Difference:': |2- + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + at: 'test-tap/fixture/report/regular/nested-objects.js:54:4' + ... +---tty-stream-chunk-separator # output-in-hook › before hook ---tty-stream-chunk-separator # output-in-hook › before hook @@ -50,10 +68,10 @@ not ok 3 - nested-objects › format with max depth 4 # beforeEach ---tty-stream-chunk-separator # output-in-hook › passing test -ok 4 - output-in-hook › passing test +ok 5 - output-in-hook › passing test ---tty-stream-chunk-separator # output-in-hook › failing test -not ok 5 - output-in-hook › failing test +not ok 6 - output-in-hook › failing test --- name: AssertionError message: Test failed via `t.fail()` @@ -78,16 +96,16 @@ not ok 5 - output-in-hook › failing test # afterAlways ---tty-stream-chunk-separator # test › skip -ok 6 - test › skip # SKIP +ok 7 - test › skip # SKIP ---tty-stream-chunk-separator # test › todo -not ok 7 - test › todo # TODO +not ok 8 - test › todo # TODO ---tty-stream-chunk-separator # test › passes -ok 8 - test › passes +ok 9 - test › passes ---tty-stream-chunk-separator # test › fails -not ok 9 - test › fails +not ok 10 - test › fails --- name: AssertionError message: Test failed via `t.fail()` @@ -96,10 +114,10 @@ not ok 9 - test › fails ... ---tty-stream-chunk-separator # test › known failure -ok 10 - test › known failure +ok 11 - test › known failure ---tty-stream-chunk-separator # test › no longer failing -not ok 11 - test › no longer failing +not ok 12 - test › no longer failing --- name: Error message: >- @@ -109,7 +127,7 @@ not ok 11 - test › no longer failing ... ---tty-stream-chunk-separator # test › logs -not ok 12 - test › logs +not ok 13 - test › logs * hello * world --- @@ -120,7 +138,7 @@ not ok 12 - test › logs ... ---tty-stream-chunk-separator # test › formatted -not ok 13 - test › formatted +not ok 14 - test › formatted --- name: AssertionError assertion: deepEqual @@ -132,7 +150,7 @@ not ok 13 - test › formatted ... ---tty-stream-chunk-separator # test › power-assert -not ok 14 - test › power-assert +not ok 15 - test › power-assert --- name: AssertionError assertion: assert @@ -143,7 +161,7 @@ not ok 14 - test › power-assert ... ---tty-stream-chunk-separator # test › bad throws -not ok 15 - test › bad throws +not ok 16 - test › bad throws --- name: AssertionError message: Improper usage of `t.throws()` detected @@ -159,7 +177,7 @@ not ok 15 - test › bad throws ... ---tty-stream-chunk-separator # test › bad notThrows -not ok 16 - test › bad notThrows +not ok 17 - test › bad notThrows --- name: AssertionError message: Improper usage of `t.notThrows()` detected @@ -175,7 +193,7 @@ not ok 16 - test › bad notThrows ... ---tty-stream-chunk-separator # test › implementation throws non-error -not ok 17 - test › implementation throws non-error +not ok 18 - test › implementation throws non-error --- name: AssertionError message: Error thrown in test @@ -185,7 +203,7 @@ not ok 17 - test › implementation throws non-error ... ---tty-stream-chunk-separator # traces-in-t-throws › throws -not ok 18 - traces-in-t-throws › throws +not ok 19 - traces-in-t-throws › throws --- name: AssertionError assertion: throws @@ -202,7 +220,7 @@ not ok 18 - traces-in-t-throws › throws ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrows -not ok 19 - traces-in-t-throws › notThrows +not ok 20 - traces-in-t-throws › notThrows --- name: AssertionError assertion: notThrows @@ -218,7 +236,7 @@ not ok 19 - traces-in-t-throws › notThrows ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrowsAsync -not ok 20 - traces-in-t-throws › notThrowsAsync +not ok 21 - traces-in-t-throws › notThrowsAsync --- name: AssertionError assertion: notThrowsAsync @@ -234,7 +252,7 @@ not ok 20 - traces-in-t-throws › notThrowsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync -not ok 21 - traces-in-t-throws › throwsAsync +not ok 22 - traces-in-t-throws › throwsAsync --- name: AssertionError assertion: throwsAsync @@ -253,7 +271,7 @@ not ok 21 - traces-in-t-throws › throwsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync different error -not ok 22 - traces-in-t-throws › throwsAsync different error +not ok 23 - traces-in-t-throws › throwsAsync different error --- name: AssertionError assertion: throwsAsync @@ -271,10 +289,10 @@ not ok 22 - traces-in-t-throws › throwsAsync different erro ... ---tty-stream-chunk-separator # uncaught-exception › passes -ok 23 - uncaught-exception › passes +ok 24 - uncaught-exception › passes ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 24 - Error: Can’t catch me +not ok 25 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -285,16 +303,16 @@ not ok 24 - Error: Can’t catch me ... ---tty-stream-chunk-separator # uncaught-exception.js exited with a non-zero exit code: 1 -not ok 25 - uncaught-exception.js exited with a non-zero exit code: 1 +not ok 26 - uncaught-exception.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator # unhandled-rejection › passes -ok 26 - unhandled-rejection › passes +ok 27 - unhandled-rejection › passes ---tty-stream-chunk-separator # unhandled-rejection › unhandled non-error rejection -ok 27 - unhandled-rejection › unhandled non-error rejection +ok 28 - unhandled-rejection › unhandled non-error rejection ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 28 - Error: Can’t catch me +not ok 29 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -302,17 +320,17 @@ not ok 28 - Error: Can’t catch me ... ---tty-stream-chunk-separator # unhandled-rejection -not ok 29 - unhandled-rejection +not ok 30 - unhandled-rejection --- message: Non-error object formatted: 'null' ... ---tty-stream-chunk-separator -1..23 -# tests 22 +1..24 +# tests 23 # pass 6 # skip 1 -# fail 22 +# fail 23 ---tty-stream-chunk-separator diff --git a/test-tap/reporters/tap.regular.v13.log b/test-tap/reporters/tap.regular.v13.log index 79e93cc86..92dabd285 100644 --- a/test-tap/reporters/tap.regular.v13.log +++ b/test-tap/reporters/tap.regular.v13.log @@ -35,6 +35,24 @@ not ok 3 - nested-objects › format with max depth 4 at: 'test-tap/fixture/report/regular/nested-objects.js:28:4' ... ---tty-stream-chunk-separator +# nested-objects › format like with max depth 4 +not ok 4 - nested-objects › format like with max depth 4 + --- + name: AssertionError + assertion: like + values: + 'Difference:': |2- + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + at: 'test-tap/fixture/report/regular/nested-objects.js:54:4' + ... +---tty-stream-chunk-separator # output-in-hook › before hook ---tty-stream-chunk-separator # output-in-hook › before hook @@ -50,10 +68,10 @@ not ok 3 - nested-objects › format with max depth 4 # beforeEach ---tty-stream-chunk-separator # output-in-hook › passing test -ok 4 - output-in-hook › passing test +ok 5 - output-in-hook › passing test ---tty-stream-chunk-separator # output-in-hook › failing test -not ok 5 - output-in-hook › failing test +not ok 6 - output-in-hook › failing test --- name: AssertionError message: Test failed via `t.fail()` @@ -78,16 +96,16 @@ not ok 5 - output-in-hook › failing test # afterAlways ---tty-stream-chunk-separator # test › skip -ok 6 - test › skip # SKIP +ok 7 - test › skip # SKIP ---tty-stream-chunk-separator # test › todo -not ok 7 - test › todo # TODO +not ok 8 - test › todo # TODO ---tty-stream-chunk-separator # test › passes -ok 8 - test › passes +ok 9 - test › passes ---tty-stream-chunk-separator # test › fails -not ok 9 - test › fails +not ok 10 - test › fails --- name: AssertionError message: Test failed via `t.fail()` @@ -96,10 +114,10 @@ not ok 9 - test › fails ... ---tty-stream-chunk-separator # test › known failure -ok 10 - test › known failure +ok 11 - test › known failure ---tty-stream-chunk-separator # test › no longer failing -not ok 11 - test › no longer failing +not ok 12 - test › no longer failing --- name: Error message: >- @@ -109,7 +127,7 @@ not ok 11 - test › no longer failing ... ---tty-stream-chunk-separator # test › logs -not ok 12 - test › logs +not ok 13 - test › logs * hello * world --- @@ -120,7 +138,7 @@ not ok 12 - test › logs ... ---tty-stream-chunk-separator # test › formatted -not ok 13 - test › formatted +not ok 14 - test › formatted --- name: AssertionError assertion: deepEqual @@ -132,7 +150,7 @@ not ok 13 - test › formatted ... ---tty-stream-chunk-separator # test › power-assert -not ok 14 - test › power-assert +not ok 15 - test › power-assert --- name: AssertionError assertion: assert @@ -143,7 +161,7 @@ not ok 14 - test › power-assert ... ---tty-stream-chunk-separator # test › bad throws -not ok 15 - test › bad throws +not ok 16 - test › bad throws --- name: AssertionError message: Improper usage of `t.throws()` detected @@ -159,7 +177,7 @@ not ok 15 - test › bad throws ... ---tty-stream-chunk-separator # test › bad notThrows -not ok 16 - test › bad notThrows +not ok 17 - test › bad notThrows --- name: AssertionError message: Improper usage of `t.notThrows()` detected @@ -175,7 +193,7 @@ not ok 16 - test › bad notThrows ... ---tty-stream-chunk-separator # test › implementation throws non-error -not ok 17 - test › implementation throws non-error +not ok 18 - test › implementation throws non-error --- name: AssertionError message: Error thrown in test @@ -185,7 +203,7 @@ not ok 17 - test › implementation throws non-error ... ---tty-stream-chunk-separator # traces-in-t-throws › throws -not ok 18 - traces-in-t-throws › throws +not ok 19 - traces-in-t-throws › throws --- name: AssertionError assertion: throws @@ -202,7 +220,7 @@ not ok 18 - traces-in-t-throws › throws ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrows -not ok 19 - traces-in-t-throws › notThrows +not ok 20 - traces-in-t-throws › notThrows --- name: AssertionError assertion: notThrows @@ -218,7 +236,7 @@ not ok 19 - traces-in-t-throws › notThrows ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrowsAsync -not ok 20 - traces-in-t-throws › notThrowsAsync +not ok 21 - traces-in-t-throws › notThrowsAsync --- name: AssertionError assertion: notThrowsAsync @@ -234,7 +252,7 @@ not ok 20 - traces-in-t-throws › notThrowsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync -not ok 21 - traces-in-t-throws › throwsAsync +not ok 22 - traces-in-t-throws › throwsAsync --- name: AssertionError assertion: throwsAsync @@ -253,7 +271,7 @@ not ok 21 - traces-in-t-throws › throwsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync different error -not ok 22 - traces-in-t-throws › throwsAsync different error +not ok 23 - traces-in-t-throws › throwsAsync different error --- name: AssertionError assertion: throwsAsync @@ -271,10 +289,10 @@ not ok 22 - traces-in-t-throws › throwsAsync different erro ... ---tty-stream-chunk-separator # uncaught-exception › passes -ok 23 - uncaught-exception › passes +ok 24 - uncaught-exception › passes ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 24 - Error: Can’t catch me +not ok 25 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -285,16 +303,16 @@ not ok 24 - Error: Can’t catch me ... ---tty-stream-chunk-separator # uncaught-exception.js exited with a non-zero exit code: 1 -not ok 25 - uncaught-exception.js exited with a non-zero exit code: 1 +not ok 26 - uncaught-exception.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator # unhandled-rejection › passes -ok 26 - unhandled-rejection › passes +ok 27 - unhandled-rejection › passes ---tty-stream-chunk-separator # unhandled-rejection › unhandled non-error rejection -ok 27 - unhandled-rejection › unhandled non-error rejection +ok 28 - unhandled-rejection › unhandled non-error rejection ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 28 - Error: Can’t catch me +not ok 29 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -302,17 +320,17 @@ not ok 28 - Error: Can’t catch me ... ---tty-stream-chunk-separator # unhandled-rejection -not ok 29 - unhandled-rejection +not ok 30 - unhandled-rejection --- message: Non-error object formatted: 'null' ... ---tty-stream-chunk-separator -1..23 -# tests 22 +1..24 +# tests 23 # pass 6 # skip 1 -# fail 22 +# fail 23 ---tty-stream-chunk-separator diff --git a/test-tap/reporters/tap.regular.v14.log b/test-tap/reporters/tap.regular.v14.log index 79e93cc86..92dabd285 100644 --- a/test-tap/reporters/tap.regular.v14.log +++ b/test-tap/reporters/tap.regular.v14.log @@ -35,6 +35,24 @@ not ok 3 - nested-objects › format with max depth 4 at: 'test-tap/fixture/report/regular/nested-objects.js:28:4' ... ---tty-stream-chunk-separator +# nested-objects › format like with max depth 4 +not ok 4 - nested-objects › format like with max depth 4 + --- + name: AssertionError + assertion: like + values: + 'Difference:': |2- + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + at: 'test-tap/fixture/report/regular/nested-objects.js:54:4' + ... +---tty-stream-chunk-separator # output-in-hook › before hook ---tty-stream-chunk-separator # output-in-hook › before hook @@ -50,10 +68,10 @@ not ok 3 - nested-objects › format with max depth 4 # beforeEach ---tty-stream-chunk-separator # output-in-hook › passing test -ok 4 - output-in-hook › passing test +ok 5 - output-in-hook › passing test ---tty-stream-chunk-separator # output-in-hook › failing test -not ok 5 - output-in-hook › failing test +not ok 6 - output-in-hook › failing test --- name: AssertionError message: Test failed via `t.fail()` @@ -78,16 +96,16 @@ not ok 5 - output-in-hook › failing test # afterAlways ---tty-stream-chunk-separator # test › skip -ok 6 - test › skip # SKIP +ok 7 - test › skip # SKIP ---tty-stream-chunk-separator # test › todo -not ok 7 - test › todo # TODO +not ok 8 - test › todo # TODO ---tty-stream-chunk-separator # test › passes -ok 8 - test › passes +ok 9 - test › passes ---tty-stream-chunk-separator # test › fails -not ok 9 - test › fails +not ok 10 - test › fails --- name: AssertionError message: Test failed via `t.fail()` @@ -96,10 +114,10 @@ not ok 9 - test › fails ... ---tty-stream-chunk-separator # test › known failure -ok 10 - test › known failure +ok 11 - test › known failure ---tty-stream-chunk-separator # test › no longer failing -not ok 11 - test › no longer failing +not ok 12 - test › no longer failing --- name: Error message: >- @@ -109,7 +127,7 @@ not ok 11 - test › no longer failing ... ---tty-stream-chunk-separator # test › logs -not ok 12 - test › logs +not ok 13 - test › logs * hello * world --- @@ -120,7 +138,7 @@ not ok 12 - test › logs ... ---tty-stream-chunk-separator # test › formatted -not ok 13 - test › formatted +not ok 14 - test › formatted --- name: AssertionError assertion: deepEqual @@ -132,7 +150,7 @@ not ok 13 - test › formatted ... ---tty-stream-chunk-separator # test › power-assert -not ok 14 - test › power-assert +not ok 15 - test › power-assert --- name: AssertionError assertion: assert @@ -143,7 +161,7 @@ not ok 14 - test › power-assert ... ---tty-stream-chunk-separator # test › bad throws -not ok 15 - test › bad throws +not ok 16 - test › bad throws --- name: AssertionError message: Improper usage of `t.throws()` detected @@ -159,7 +177,7 @@ not ok 15 - test › bad throws ... ---tty-stream-chunk-separator # test › bad notThrows -not ok 16 - test › bad notThrows +not ok 17 - test › bad notThrows --- name: AssertionError message: Improper usage of `t.notThrows()` detected @@ -175,7 +193,7 @@ not ok 16 - test › bad notThrows ... ---tty-stream-chunk-separator # test › implementation throws non-error -not ok 17 - test › implementation throws non-error +not ok 18 - test › implementation throws non-error --- name: AssertionError message: Error thrown in test @@ -185,7 +203,7 @@ not ok 17 - test › implementation throws non-error ... ---tty-stream-chunk-separator # traces-in-t-throws › throws -not ok 18 - traces-in-t-throws › throws +not ok 19 - traces-in-t-throws › throws --- name: AssertionError assertion: throws @@ -202,7 +220,7 @@ not ok 18 - traces-in-t-throws › throws ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrows -not ok 19 - traces-in-t-throws › notThrows +not ok 20 - traces-in-t-throws › notThrows --- name: AssertionError assertion: notThrows @@ -218,7 +236,7 @@ not ok 19 - traces-in-t-throws › notThrows ... ---tty-stream-chunk-separator # traces-in-t-throws › notThrowsAsync -not ok 20 - traces-in-t-throws › notThrowsAsync +not ok 21 - traces-in-t-throws › notThrowsAsync --- name: AssertionError assertion: notThrowsAsync @@ -234,7 +252,7 @@ not ok 20 - traces-in-t-throws › notThrowsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync -not ok 21 - traces-in-t-throws › throwsAsync +not ok 22 - traces-in-t-throws › throwsAsync --- name: AssertionError assertion: throwsAsync @@ -253,7 +271,7 @@ not ok 21 - traces-in-t-throws › throwsAsync ... ---tty-stream-chunk-separator # traces-in-t-throws › throwsAsync different error -not ok 22 - traces-in-t-throws › throwsAsync different error +not ok 23 - traces-in-t-throws › throwsAsync different error --- name: AssertionError assertion: throwsAsync @@ -271,10 +289,10 @@ not ok 22 - traces-in-t-throws › throwsAsync different erro ... ---tty-stream-chunk-separator # uncaught-exception › passes -ok 23 - uncaught-exception › passes +ok 24 - uncaught-exception › passes ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 24 - Error: Can’t catch me +not ok 25 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -285,16 +303,16 @@ not ok 24 - Error: Can’t catch me ... ---tty-stream-chunk-separator # uncaught-exception.js exited with a non-zero exit code: 1 -not ok 25 - uncaught-exception.js exited with a non-zero exit code: 1 +not ok 26 - uncaught-exception.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator # unhandled-rejection › passes -ok 26 - unhandled-rejection › passes +ok 27 - unhandled-rejection › passes ---tty-stream-chunk-separator # unhandled-rejection › unhandled non-error rejection -ok 27 - unhandled-rejection › unhandled non-error rejection +ok 28 - unhandled-rejection › unhandled non-error rejection ---tty-stream-chunk-separator # Error: Can’t catch me -not ok 28 - Error: Can’t catch me +not ok 29 - Error: Can’t catch me --- name: Error message: Can’t catch me @@ -302,17 +320,17 @@ not ok 28 - Error: Can’t catch me ... ---tty-stream-chunk-separator # unhandled-rejection -not ok 29 - unhandled-rejection +not ok 30 - unhandled-rejection --- message: Non-error object formatted: 'null' ... ---tty-stream-chunk-separator -1..23 -# tests 22 +1..24 +# tests 23 # pass 6 # skip 1 -# fail 22 +# fail 23 ---tty-stream-chunk-separator diff --git a/test-tap/reporters/verbose.regular.v10.log b/test-tap/reporters/verbose.regular.v10.log index 02f5c7769..8c0b7b721 100644 --- a/test-tap/reporters/verbose.regular.v10.log +++ b/test-tap/reporters/verbose.regular.v10.log @@ -16,6 +16,8 @@ ✖ bad-test-chain.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator ✖ nested-objects › format with max depth 4 +---tty-stream-chunk-separator + ✖ nested-objects › format like with max depth 4 ---tty-stream-chunk-separator output-in-hook › before hook ℹ before @@ -152,6 +154,30 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › t (test-tap/fixture/report/regular/nested-objects.js:54:4) + › process._tickCallback (internal/process/next_tick.js:68:7) + + + output-in-hook › failing test output-in-hook.js:34 @@ -428,7 +454,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/verbose.regular.v12.log b/test-tap/reporters/verbose.regular.v12.log index 9f528a96e..dcc88f811 100644 --- a/test-tap/reporters/verbose.regular.v12.log +++ b/test-tap/reporters/verbose.regular.v12.log @@ -16,6 +16,8 @@ ✖ bad-test-chain.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator ✖ nested-objects › format with max depth 4 +---tty-stream-chunk-separator + ✖ nested-objects › format like with max depth 4 ---tty-stream-chunk-separator output-in-hook › before hook ℹ before @@ -152,6 +154,29 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › test-tap/fixture/report/regular/nested-objects.js:54:4 + + + output-in-hook › failing test output-in-hook.js:34 @@ -412,7 +437,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/verbose.regular.v13.log b/test-tap/reporters/verbose.regular.v13.log index 9f528a96e..dcc88f811 100644 --- a/test-tap/reporters/verbose.regular.v13.log +++ b/test-tap/reporters/verbose.regular.v13.log @@ -16,6 +16,8 @@ ✖ bad-test-chain.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator ✖ nested-objects › format with max depth 4 +---tty-stream-chunk-separator + ✖ nested-objects › format like with max depth 4 ---tty-stream-chunk-separator output-in-hook › before hook ℹ before @@ -152,6 +154,29 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › test-tap/fixture/report/regular/nested-objects.js:54:4 + + + output-in-hook › failing test output-in-hook.js:34 @@ -412,7 +437,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/reporters/verbose.regular.v14.log b/test-tap/reporters/verbose.regular.v14.log index 9f528a96e..dcc88f811 100644 --- a/test-tap/reporters/verbose.regular.v14.log +++ b/test-tap/reporters/verbose.regular.v14.log @@ -16,6 +16,8 @@ ✖ bad-test-chain.js exited with a non-zero exit code: 1 ---tty-stream-chunk-separator ✖ nested-objects › format with max depth 4 +---tty-stream-chunk-separator + ✖ nested-objects › format like with max depth 4 ---tty-stream-chunk-separator output-in-hook › before hook ℹ before @@ -152,6 +154,29 @@ + nested-objects › format like with max depth 4 + + nested-objects.js:54 + + 53: }; +  54: t.like(actual, pattern); + 55: }); + + Difference: + + { + a: { + b: { + - foo: 'bar', + + foo: 'qux', + }, + }, + } + + › test-tap/fixture/report/regular/nested-objects.js:54:4 + + + output-in-hook › failing test output-in-hook.js:34 @@ -412,7 +437,7 @@ ─ - 15 tests failed + 16 tests failed 1 known failure 1 test skipped 1 test todo diff --git a/test-tap/test.js b/test-tap/test.js index 591e1c75f..af70bcf1c 100644 --- a/test-tap/test.js +++ b/test-tap/test.js @@ -360,13 +360,14 @@ test('fails with thrown non-error object', t => { test('skipped assertions count towards the plan', t => { const instance = ava(a => { - a.plan(15); + a.plan(16); a.pass.skip(); a.fail.skip(); a.is.skip(1, 1); a.not.skip(1, 2); a.deepEqual.skip({foo: 'bar'}, {foo: 'bar'}); a.notDeepEqual.skip({foo: 'bar'}, {baz: 'thud'}); + a.like.skip({foo: 'bar'}, {foo: 'bar'}); a.throws.skip(() => { throw new Error(); // eslint-disable-line unicorn/error-message }); @@ -381,20 +382,21 @@ test('skipped assertions count towards the plan', t => { }); return instance.run().then(result => { t.is(result.passed, true); - t.is(instance.planCount, 15); - t.is(instance.assertCount, 15); + t.is(instance.planCount, 16); + t.is(instance.assertCount, 16); }); }); test('assertion.skip() is bound', t => { const instance = ava(a => { - a.plan(15); + a.plan(16); (a.pass.skip)(); (a.fail.skip)(); (a.is.skip)(1, 1); (a.not.skip)(1, 2); (a.deepEqual.skip)({foo: 'bar'}, {foo: 'bar'}); (a.notDeepEqual.skip)({foo: 'bar'}, {baz: 'thud'}); + (a.like.skip)({foo: 'bar'}, {foo: 'bar'}); (a.throws.skip)(() => { throw new Error(); // eslint-disable-line unicorn/error-message }); @@ -409,8 +411,8 @@ test('assertion.skip() is bound', t => { }); return instance.run().then(result => { t.is(result.passed, true); - t.is(instance.planCount, 15); - t.is(instance.assertCount, 15); + t.is(instance.planCount, 16); + t.is(instance.assertCount, 16); }); }); @@ -646,13 +648,14 @@ test('log from tests', t => { test('assertions are bound', t => { // This does not test .fail() and .snapshot(). It'll suffice. - return ava(a => { - (a.plan)(13); + return withExperiments({likeAssertion: true})(a => { + (a.plan)(14); (a.pass)(); (a.is)(1, 1); (a.not)(1, 2); (a.deepEqual)({foo: 'bar'}, {foo: 'bar'}); (a.notDeepEqual)({foo: 'bar'}, {baz: 'thud'}); + (a.like)({foo: 'bar'}, {foo: 'bar'}); (a.throws)(() => { throw new Error(); // eslint-disable-line unicorn/error-message }); diff --git a/test/assertions/fixtures/happy-path.js b/test/assertions/fixtures/happy-path.js index 41c1ee54c..6047ac9aa 100644 --- a/test/assertions/fixtures/happy-path.js +++ b/test/assertions/fixtures/happy-path.js @@ -11,6 +11,22 @@ test(passes, 'is', 1, 1); test(passes, 'not', 1, 2); test(passes, 'deepEqual', {foo: 'bar'}, {foo: 'bar'}); test(passes, 'notDeepEqual', {foo: 'bar'}, {foo: 'baz'}); +test(passes, 'like', { + foo: 'bar', + deep: { + buz: 'qux', + extra: 'irrelevant' + }, + extra: 'irrelevant', + deepExtra: { + extra: 'irrelevant' + } +}, { + foo: 'bar', + deep: { + buz: 'qux' + } +}); test(passes, 'throws', () => { throw new Error('error'); }); diff --git a/test/assertions/fixtures/package.json b/test/assertions/fixtures/package.json index ac13570c1..48bb5275b 100644 --- a/test/assertions/fixtures/package.json +++ b/test/assertions/fixtures/package.json @@ -2,7 +2,10 @@ "ava": { "files": [ "*.js" - ] + ], + "nonSemVerExperiments": { + "likeAssertion": true + } }, "dependencies": { "ava": "file:../../.." diff --git a/test/assertions/snapshots/test.js.md b/test/assertions/snapshots/test.js.md index d98073e06..5706ef80c 100644 --- a/test/assertions/snapshots/test.js.md +++ b/test/assertions/snapshots/test.js.md @@ -14,6 +14,7 @@ Generated by [AVA](https://avajs.dev). 't.false(false) passes', 't.falsy("") passes', 't.is(1, 1) passes', + 't.like({"foo":"bar","deep":{"buz":"qux","extra":"irrelevant"},"extra":"irrelevant","deepExtra":{"extra":"irrelevant"}}, {"foo":"bar","deep":{"buz":"qux"}}) passes', 't.not(1, 2) passes', 't.notDeepEqual({"foo":"bar"}, {"foo":"baz"}) passes', 't.notRegex("bar", {}) passes', diff --git a/test/assertions/snapshots/test.js.snap b/test/assertions/snapshots/test.js.snap index 30f1537db4768de27703e5ad3ead0fdf5553dc3a..7989dc9530dfea02521650b325a4fac5bac4df25 100644 GIT binary patch literal 484 zcmV(xy?3V75JDIYwu`5a=bzJ;*M{}5dv}bHbA3e+ilDgL z#|fP6!hHbT0`GtV+8%HMd;>&5r~yoYXW#>fLyRI&18#sP;0?&)SQpERNS1^{&fiP;6b}PNU+171Rw=7a3Rg;lP zURxOYBPcS$hA`@8n^WCw{aVD6j3bP6Lj2B>bkf3TalMe&0{KSgigJ&H(QaMQg{pCzi<0ee~6IoG~uGV7Y_ek&=hEss^^qt0bO zU6HCP3}+=Eq&%_MEIFr|u;f;ystJR4%r0-j#{Kyru6e#55nhWbl})LH7qTwBEO@L> ab*J%5Y0s&`x@eAmf3F{+=lCud1ONc4w&0Ke literal 415 zcmV;Q0bu??RzV$(*V|pT_PZEiD%-SSfb1_W=+gAICld?wK5C?umETR7Rk_{ zf;wQv#FBJeAv`WwZPgltOYyBBDra3fC!RNGo(|m}yRuupZ*SU1F1N3%*00xOf~FGY zq|{9#j^NUabY*( ziYqGBl8)%R1JR)m_vf8X$1XyS?`1|Q^GX@th@rePd3@-xa7aqCv0J5m*$ICB)KB7s J5wu?e003QNy}tke