Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: avajs/ava
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.8.2
Choose a base ref
...
head repository: avajs/ava
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.9.0
Choose a head ref
  • 11 commits
  • 100 files changed
  • 7 contributors

Commits on May 12, 2020

  1. Reporter improvements

    * Always show stats last
    * Don't print known failing tests after stats
    * Tweak empty lines and separators
    
    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    m5x5 and novemberborn authored May 12, 2020

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    baaf99a View commit details

Commits on May 24, 2020

  1. Update endpoint testing recipe to use correct options for got

    Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
    paulrobertlloyd and sindresorhus authored May 24, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4b79740 View commit details
  2. Update dependencies

    * Update dev dependencies
    
    * Test with TypeScript 3.9
    
    * Update dependencies
    
    This includes a major update to Concordance, letting us print missing or extraneous objects in diffs with extra depth. See concordancejs/concordance#62.
    
    * Rebuild package-lock
    novemberborn authored May 24, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    novemberborn Mark Wubben
    Copy the full SHA
    34a134a View commit details

Commits on Jun 12, 2020

  1. Copy the full SHA
    a901672 View commit details

Commits on Jun 13, 2020

  1. Copy the full SHA
    f5e1f94 View commit details

Commits on Jun 14, 2020

  1. Merge mini and verbose reporter implementations

    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    m5x5 and novemberborn authored Jun 14, 2020
    Copy the full SHA
    b3866b6 View commit details
  2. Experimentally reverse teardown order

    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    Marc and novemberborn authored Jun 14, 2020
    Copy the full SHA
    952a017 View commit details
  3. Experimentally implement t.like() assertion

    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    futpib and novemberborn authored Jun 14, 2020
    Copy the full SHA
    19c4f35 View commit details
  4. Copy the full SHA
    27d2c6a View commit details
  5. Update dependencies; supported Node.js versions

    * Remove Node.js 13 support
    
    * Denote Node.js 12.17 as the future minimal Node.js 12 version
    
    * Update dev dependencies
    
    * Upgrade XO
    
    * Update dependencies
    
    * Rebuild lockfile
    novemberborn authored Jun 14, 2020
    Copy the full SHA
    f06c506 View commit details
  6. 3.9.0

    novemberborn committed Jun 14, 2020
    Copy the full SHA
    78cfaa1 View commit details
Showing with 2,704 additions and 2,248 deletions.
  1. +2 −2 .github/workflows/ci.yml
  2. +25 −1 docs/02-execution-context.md
  3. +49 −0 docs/03-assertions.md
  4. +3 −4 docs/recipes/endpoint-testing.md
  5. +1 −1 docs/recipes/puppeteer.md
  6. +12 −1 index.d.ts
  7. +58 −1 lib/assert.js
  8. +4 −11 lib/cli.js
  9. +1 −1 lib/code-excerpt.js
  10. +37 −0 lib/like-selector.js
  11. +1 −1 lib/load-config.js
  12. +834 −0 lib/reporters/default.js
  13. +0 −619 lib/reporters/mini.js
  14. +1 −1 lib/reporters/tap.js
  15. +0 −463 lib/reporters/verbose.js
  16. +2 −2 lib/runner.js
  17. +9 −2 lib/test.js
  18. +1 −1 lib/worker/subprocess.js
  19. +651 −670 package-lock.json
  20. +15 −15 package.json
  21. +17 −0 test-d/like.ts
  22. +207 −0 test-tap/assert.js
  23. +1 −1 test-tap/fixture/improper-t-throws/caught.js
  24. +26 −0 test-tap/fixture/report/regular/nested-objects.js
  25. +3 −3 test-tap/helper/report.js
  26. +2 −2 test-tap/helper/tty-stream.js
  27. +2 −2 test-tap/integration/assorted.js
  28. +2 −2 test-tap/integration/snapshots.js
  29. +5 −3 test-tap/reporters/mini.edgecases.v10.log
  30. +5 −3 test-tap/reporters/mini.edgecases.v12.log
  31. +5 −3 test-tap/reporters/mini.edgecases.v13.log
  32. +5 −3 test-tap/reporters/mini.edgecases.v14.log
  33. +3 −6 test-tap/reporters/mini.failfast.v10.log
  34. +3 −6 test-tap/reporters/mini.failfast.v12.log
  35. +3 −6 test-tap/reporters/mini.failfast.v13.log
  36. +3 −6 test-tap/reporters/mini.failfast.v14.log
  37. +3 −6 test-tap/reporters/mini.failfast2.v10.log
  38. +3 −6 test-tap/reporters/mini.failfast2.v12.log
  39. +3 −6 test-tap/reporters/mini.failfast2.v13.log
  40. +3 −6 test-tap/reporters/mini.failfast2.v14.log
  41. +3 −2 test-tap/reporters/mini.js
  42. +1 −3 test-tap/reporters/mini.only.v10.log
  43. +1 −3 test-tap/reporters/mini.only.v12.log
  44. +1 −3 test-tap/reporters/mini.only.v13.log
  45. +1 −3 test-tap/reporters/mini.only.v14.log
  46. +59 −32 test-tap/reporters/mini.regular.v10.log
  47. +58 −32 test-tap/reporters/mini.regular.v12.log
  48. +58 −32 test-tap/reporters/mini.regular.v13.log
  49. +58 −32 test-tap/reporters/mini.regular.v14.log
  50. +3 −6 test-tap/reporters/mini.watch.v10.log
  51. +3 −6 test-tap/reporters/mini.watch.v12.log
  52. +3 −6 test-tap/reporters/mini.watch.v13.log
  53. +3 −6 test-tap/reporters/mini.watch.v14.log
  54. +49 −29 test-tap/reporters/tap.regular.v10.log
  55. +47 −29 test-tap/reporters/tap.regular.v12.log
  56. +47 −29 test-tap/reporters/tap.regular.v13.log
  57. +47 −29 test-tap/reporters/tap.regular.v14.log
  58. +1 −1 test-tap/reporters/verbose.edgecases.v10.log
  59. +1 −1 test-tap/reporters/verbose.edgecases.v12.log
  60. +1 −1 test-tap/reporters/verbose.edgecases.v13.log
  61. +1 −1 test-tap/reporters/verbose.edgecases.v14.log
  62. +3 −3 test-tap/reporters/verbose.failfast.v10.log
  63. +3 −3 test-tap/reporters/verbose.failfast.v12.log
  64. +3 −3 test-tap/reporters/verbose.failfast.v13.log
  65. +3 −3 test-tap/reporters/verbose.failfast.v14.log
  66. +3 −3 test-tap/reporters/verbose.failfast2.v10.log
  67. +3 −3 test-tap/reporters/verbose.failfast2.v12.log
  68. +3 −3 test-tap/reporters/verbose.failfast2.v13.log
  69. +3 −3 test-tap/reporters/verbose.failfast2.v14.log
  70. +4 −2 test-tap/reporters/verbose.js
  71. +1 −1 test-tap/reporters/verbose.only.v10.log
  72. +1 −1 test-tap/reporters/verbose.only.v12.log
  73. +1 −1 test-tap/reporters/verbose.only.v13.log
  74. +1 −1 test-tap/reporters/verbose.only.v14.log
  75. +36 −11 test-tap/reporters/verbose.regular.v10.log
  76. +35 −11 test-tap/reporters/verbose.regular.v12.log
  77. +35 −11 test-tap/reporters/verbose.regular.v13.log
  78. +35 −11 test-tap/reporters/verbose.regular.v14.log
  79. +1 −1 test-tap/reporters/verbose.timeoutinmultiplefiles.v10.log
  80. +1 −1 test-tap/reporters/verbose.timeoutinmultiplefiles.v12.log
  81. +1 −1 test-tap/reporters/verbose.timeoutinmultiplefiles.v13.log
  82. +1 −1 test-tap/reporters/verbose.timeoutinmultiplefiles.v14.log
  83. +1 −1 test-tap/reporters/verbose.timeoutinsinglefile.v10.log
  84. +1 −1 test-tap/reporters/verbose.timeoutinsinglefile.v12.log
  85. +1 −1 test-tap/reporters/verbose.timeoutinsinglefile.v13.log
  86. +1 −1 test-tap/reporters/verbose.timeoutinsinglefile.v14.log
  87. +1 −1 test-tap/reporters/verbose.timeoutwithmatch.v10.log
  88. +1 −1 test-tap/reporters/verbose.timeoutwithmatch.v12.log
  89. +1 −1 test-tap/reporters/verbose.timeoutwithmatch.v13.log
  90. +1 −1 test-tap/reporters/verbose.timeoutwithmatch.v14.log
  91. +3 −0 test-tap/reporters/verbose.watch.v10.log
  92. +3 −0 test-tap/reporters/verbose.watch.v12.log
  93. +3 −0 test-tap/reporters/verbose.watch.v13.log
  94. +3 −0 test-tap/reporters/verbose.watch.v14.log
  95. +29 −9 test-tap/test.js
  96. +16 −0 test/assertions/fixtures/happy-path.js
  97. +4 −1 test/assertions/fixtures/package.json
  98. +1 −0 test/assertions/snapshots/test.js.md
  99. BIN test/assertions/snapshots/test.js.snap
  100. +1 −0 xo.config.js
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [^10.18.0, ^12.14.0, ^13.5.0, ^14.0.0]
node-version: [^10.18.0, ^12.14.0, ^14.0.0]
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v1
@@ -41,7 +41,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
ts-version: [~3.7.5, ~3.8]
ts-version: [~3.7.5, ~3.8, ~3.9]
steps:
- uses: actions/checkout@v1
with:
26 changes: 25 additions & 1 deletion docs/02-execution-context.md
Original file line number Diff line number Diff line change
@@ -40,12 +40,36 @@ Plan how many assertion there are in the test. The test will fail if the actual

## `t.teardown(fn)`

Registers the `fn` function to be run after the test has finished. You can register multiple functions and they'll run in order. You can use asynchronous functions: only one will run at a time.
Registers the `fn` function to be run after the test has finished. You can register multiple functions and they'll run in order<sup>†</sup>. You can use asynchronous functions: only one will run at a time.

You cannot perform assertions using the `t` object or register additional functions from inside `fn`.

You cannot use `t.teardown()` in hooks either.

<sup>†</sup> In the next major release we'll change this so teardown functions run in reverse order. The last registered function will be called first. You can opt in to this behavior now by enabling the `reverseTeardowns` experiment.

**`package.json`**:

```json
{
"ava": {
"nonSemVerExperiments": {
"reverseTeardowns": true
}
}
}
```

**`ava.config.js`**:

```js
export default {
nonSemVerExperiments: {
reverseTeardowns: true
}
}
```

## `t.timeout(ms)`

Set a timeout for the test, in milliseconds. The test will fail if this timeout is exceeded. The timeout is reset each time an assertion is made.
49 changes: 49 additions & 0 deletions docs/03-assertions.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 3 additions & 4 deletions docs/recipes/endpoint-testing.md
Original file line number Diff line number Diff line change
@@ -17,21 +17,20 @@ const app = require('../app');

test.before(async t => {
t.context.server = http.createServer(app);
t.context.baseUrl = await listen(t.context.server);
t.context.prefixUrl = await listen(t.context.server);
});

test.after.always(t => {
t.context.server.close();
});

test.serial('get /user', async t => {
const res = await got('/user', { baseUrl: t.context.baseUrl, json: true });
t.is(res.body.email, 'ava@rocks.com');
const {email} = await got('user', {prefixUrl: t.context.prefixUrl}).json();
t.is(email, 'ava@rocks.com');
});
```

Other libraries you may find useful:

- [`supertest`](https://github.com/visionmedia/supertest)
- [`get-port`](https://github.com/sindresorhus/get-port)

2 changes: 1 addition & 1 deletion docs/recipes/puppeteer.md
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ The first step is setting up a helper to configure the environment:
```js
const puppeteer = require('puppeteer');

export default async function withPage(t, run) {
module.exports = async (t, run) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
try {
13 changes: 12 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -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: Record<string, unknown>, message?: string): void;

/** Skip this assertion. */
skip(value: any, selector: any, message?: string): void;
}

export interface FailAssertion {
/** Fail the test. */
(message?: string): void;
@@ -422,7 +433,7 @@ export interface CbExecutionContext<Context = unknown> extends ExecutionContext<
end(error?: any): void;
}

export type ImplementationResult = PromiseLike<void> | Subscribable | void;
export type ImplementationResult = PromiseLike<void> | Subscribable | void; // eslint-disable-line @typescript-eslint/no-invalid-void-type
export type Implementation<Context = unknown> = (t: ExecutionContext<Context>) => ImplementationResult;
export type CbImplementation<Context = unknown> = (t: CbExecutionContext<Context>) => ImplementationResult;

59 changes: 58 additions & 1 deletion lib/assert.js
Original file line number Diff line number Diff line change
@@ -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
15 changes: 4 additions & 11 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -259,8 +259,7 @@ exports.run = async () => { // eslint-disable-line complexity

const ciParallelVars = require('ci-parallel-vars');
const Api = require('./api');
const VerboseReporter = require('./reporters/verbose');
const MiniReporter = require('./reporters/mini');
const DefaultReporter = require('./reporters/default');
const TapReporter = require('./reporters/tap');
const Watcher = require('./watcher');
const normalizeExtensions = require('./extensions');
@@ -391,19 +390,13 @@ exports.run = async () => { // eslint-disable-line complexity
reportStream: process.stdout,
stdStream: process.stderr
});
} else if (debug !== null || combined.verbose || isCi || !process.stdout.isTTY) {
reporter = new VerboseReporter({
projectDir,
reportStream: process.stdout,
stdStream: process.stderr,
watching: combined.watch
});
} else {
reporter = new MiniReporter({
reporter = new DefaultReporter({
projectDir,
reportStream: process.stdout,
stdStream: process.stderr,
watching: combined.watch
watching: combined.watch,
verbose: debug !== null || combined.verbose || isCi || !process.stdout.isTTY
});
}

2 changes: 1 addition & 1 deletion lib/code-excerpt.js
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ module.exports = (source, options = {}) => {
let contents;
try {
contents = fs.readFileSync(file, 'utf8');
} catch (_) {
} catch {
return null;
}

37 changes: 37 additions & 0 deletions lib/like-selector.js
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion lib/load-config.js
Original file line number Diff line number Diff line change
@@ -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();
const EXPERIMENTS = new Set(['likeAssertion', 'reverseTeardowns']);

// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
const evaluateJsConfig = configFile => {
Loading