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.3.0
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.4.0
Choose a head ref
  • 13 commits
  • 23 files changed
  • 6 contributors

Commits on Feb 16, 2020

  1. Improve error message when test.cb() is used with promises

    Fixes #2386
    
    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    toddkcarlson and novemberborn authored Feb 16, 2020

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    f5a8c2b View commit details
  2. Update Vue recipe to use JSDom rather than browser-env

    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    Scrum and novemberborn authored Feb 16, 2020

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    3f9c616 View commit details
  3. Remove @types/node reference

    Fixes #2397.
    
    * Remove @types/node reference from our TypeScript definition
    
    * Ensure our TypeScript tests don't use any installed types
    
    * Adjust our TypeScript test target to ES2018
    novemberborn authored Feb 16, 2020

    Verified

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

    * Bump dev dependencies
    
    * Fix linting issue
    
    * Replace lolex with @sinonjs/fake-timers
    
    * Bump dependencies
    
    * Rebuild package-lock
    novemberborn authored Feb 16, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a69e4f2 View commit details
  5. Prefix attempt titles with that of parent test

    Since titles must be unique throughout the test file, it's easier if
    attempt titles are always prefixed with the already unique title of the
    parent test.
    novemberborn committed Feb 16, 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
    7ee3a0e View commit details
  6. Unverified

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

    * Bump dependencies
    
    * Rebuild package-lock
    
    * Fix lint errors
    novemberborn authored Feb 16, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    da52e5f View commit details
  8. Document t.try() (#2251)

    Co-authored-by: Mark Wubben <mark@novemberborn.net>
    qlonik and novemberborn authored Feb 16, 2020

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    324e45f View commit details

Commits on Feb 23, 2020

  1. Verified

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

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8831f54 View commit details
  3. Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    f4d4edd View commit details
  4. Ship t.try() without requiring opt-in

    This removes the tryAssertion experiment.
    novemberborn committed Feb 23, 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
    12ba7bc View commit details
  5. 3.4.0

    novemberborn committed Feb 23, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    novemberborn Mark Wubben
    Copy the full SHA
    8630636 View commit details
46 changes: 46 additions & 0 deletions docs/03-assertions.md
Original file line number Diff line number Diff line change
@@ -302,3 +302,49 @@ Assert that `contents` does not match `regex`.
Compares the `expected` value with a previously recorded snapshot. Snapshots are stored for each test, so ensure you give your tests unique titles. Alternatively pass an `options` object to select a specific snapshot, for instance `{id: 'my snapshot'}`.

Snapshot assertions cannot be skipped when snapshots are being updated.

### `.try(title?, implementation | macro | macro[], ...args?)`

`.try()` allows you to *try* assertions without causing the test to fail.

The implementation function behaves the same as any other test function. You can even use macros. The first title argument is always optional. Additional arguments are passed to the implemetation or macro function.

`.try()` is an asynchronous function. You must `await` it. The result object has `commit()` and `discard()` methods. You must decide whether to commit or discard the result. If you commit a failed result, your test will fail.

You can check whether the attempt passed using the `passed` property. Any assertion errors are available through the `errors` property. The attempt title is available through the `title` property.

Logs from `t.log()` are available through the `logs` property. You can choose to retain these logs as part of your test by passing `{retainLogs: true}` to the `commit()` and `discard()` methods.

The implementation function receives its own [execution context](./02-execution-context.md), just like a test function. You must be careful to only perform assertions using the attempt's execution context. At least one assertion must pass for your attempt to pass.

You may run multiple attempts concurrently, within a single test. However you can't use snapshots when you do so.

Example:

```js
const twoRandomIntegers = () => {
const rnd = Math.round(Math.random() * 100);
const x = rnd % 10;
const y = Math.floor(rnd / 10);
return [x, y];
};

test('flaky macro', async t => {
const firstTry = await t.try((tt, a, b) => {
tt.is(a, b);
}, ...randomIntegers());

if (firstTry.passed) {
firstTry.commit();
return;
}

firstTry.discard();
t.log(firstTry.errors);

const secondTry = await t.try((tt, a, b) => {
tt.is(a, b);
}, ...randomIntegers());
secondTry.commit();
});
```
11 changes: 0 additions & 11 deletions docs/06-configuration.md
Original file line number Diff line number Diff line change
@@ -213,17 +213,6 @@ export default {
};
```

You can opt in to the new `t.try()` assertion by specifying `tryAssertion`:

`ava.config.js`:
```js
export default {
nonSemVerExperiments: {
tryAssertion: true
}
};
```

## Node arguments

The `nodeArguments` configuration may be used to specify additional arguments for launching worker processes. These are combined with `--node-arguments` passed on the CLI and any arguments passed to the `node` binary when starting AVA.
11 changes: 8 additions & 3 deletions docs/recipes/vue.md
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@ Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/do
- [Require extension hooks](https://github.com/jackmellis/require-extension-hooks):
- `npm i --save-dev require-extension-hooks require-extension-hooks-vue require-extension-hooks-babel@beta`

- [browser-env](browser-testing.md)
- `npm i --save-dev browser-env`
- [jsdom-global](https://github.com/rstacruz/jsdom-global/blob/master/README.md)
- `npm i --save-dev jsdom jsdom-global`

- Optional: [babel-plugin-webpack-alias-7](https://github.com/shortminds/babel-plugin-webpack-alias-7) if you want to use [webpack aliases](https://webpack.js.org/configuration/resolve/#resolve-alias) or use them in your source files
- `npm i --save-dev babel-plugin-webpack-alias-7`
@@ -32,8 +32,13 @@ The first step is setting up a helper to configure the environment to transpile
```js
// ./test/_setup.js

// Set up JSDom.
require('jsdom-global')()

// Fix the Date object, see <https://github.com/vuejs/vue-test-utils/issues/936#issuecomment-415386167>.
window.Date = Date

// Setup browser environment
require('browser-env')();
const hooks = require('require-extension-hooks');
const Vue = require('vue');

9 changes: 1 addition & 8 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference types="node"/>

export interface Subscribable {
subscribe(observer: {
error(err: any): void,
@@ -247,16 +245,11 @@ export interface SnapshotAssertion {
}

export interface ThrowsAssertion {
/**
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
*/
<ThrownError extends Error>(fn: () => any, expectations?: null, message?: string): ThrownError;

/**
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
* The error must satisfy all expectations.
*/
<ThrownError extends Error>(fn: () => any, expectations: ThrowsExpectation, message?: string): ThrownError;
<ThrownError extends Error>(fn: () => any, expectations?: ThrowsExpectation | null, message?: string): ThrownError;

/** Skip this assertion. */
skip(fn: () => any, expectations?: any, message?: string): void;
4 changes: 2 additions & 2 deletions lib/assert.js
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ function getErrorWithLongStackTrace() {
}

function validateExpectations(assertion, expectations, numArgs) { // eslint-disable-line complexity
if (numArgs === 1 || expectations === null) {
if (numArgs === 1 || expectations === null || expectations === undefined) {
expectations = {};
} else if (
typeof expectations === 'function' ||
@@ -85,7 +85,7 @@ function validateExpectations(assertion, expectations, numArgs) { // eslint-disa
) {
throw new AssertionError({
assertion,
message: `The second argument to \`t.${assertion}()\` must be an expectation object or \`null\``,
message: `The second argument to \`t.${assertion}()\` must be an expectation object, \`null\` or \`undefined\``,
values: [formatWithLabel('Called with:', expectations)]
});
} else {
2 changes: 1 addition & 1 deletion lib/beautify-stack.js
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ let ignoreStackLines = [];

const avaInternals = /\/ava\/(?:lib\/|lib\/worker\/)?[\w-]+\.js:\d+:\d+\)?$/;
const avaDependencies = /\/node_modules\/(?:@ava\/babel|@ava\/require-precompiled|append-transform|empower-core|nyc|require-precompiled|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
const stackFrameLine = /^.+( \(.+:\d+:\d+\)|:\d+:\d+)$/; // eslint-disable-line prefer-named-capture-group
const stackFrameLine = /^.+( \(.+:\d+:\d+\)|:\d+:\d+)$/;

if (!debug.enabled) {
ignoreStackLines = StackUtils.nodeInternals();
2 changes: 0 additions & 2 deletions lib/esm-probe.mjs

This file was deleted.

39 changes: 5 additions & 34 deletions lib/load-config.js
Original file line number Diff line number Diff line change
@@ -7,42 +7,17 @@ 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(['tryAssertion']);

class LegacyCommonJsAccessError extends Error {
constructor(thing, fileForErrorMessage) {
super(`${thing} is not available in ${fileForErrorMessage}. Use a .cjs file instead`);
this.name = 'LegacyCommonJsAccessError';
}
}
const EXPERIMENTS = new Set();

// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
const evaluateJsConfig = (configFile, fileForErrorMessage) => {
const evaluateJsConfig = configFile => {
const contents = fs.readFileSync(configFile, 'utf8');
const script = new vm.Script(`'use strict';(()=>{let __export__;\n${contents.replace(/export default/g, '__export__ =')};return __export__;})()`, {
filename: configFile,
lineOffset: -1
});
return {
default: script.runInNewContext({
console,
process,
get __dirname() {
throw new LegacyCommonJsAccessError('__dirname', fileForErrorMessage);
},
get __filename() {
throw new LegacyCommonJsAccessError('__filename', fileForErrorMessage);
},
get module() {
throw new LegacyCommonJsAccessError('module', fileForErrorMessage);
},
get exports() {
throw new LegacyCommonJsAccessError('exports', fileForErrorMessage);
},
get require() {
throw new LegacyCommonJsAccessError('require()', fileForErrorMessage);
}
})
default: script.runInThisContext()
};
};

@@ -55,17 +30,13 @@ const loadJsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.confi

let config;
try {
({default: config = MISSING_DEFAULT_EXPORT} = evaluateJsConfig(configFile, fileForErrorMessage));
({default: config = MISSING_DEFAULT_EXPORT} = evaluateJsConfig(configFile));
} catch (error) {
if (error.code === 'ENOENT') {
return null;
}

if (error.name === 'LegacyCommonJsAccessError') {
throw error;
} else {
throw Object.assign(new Error(`Error loading ${fileForErrorMessage}`), {parent: error});
}
throw Object.assign(new Error(`Error loading ${fileForErrorMessage}: ${error.message}`), {parent: error});
}

if (config === MISSING_DEFAULT_EXPORT) {
14 changes: 6 additions & 8 deletions lib/test.js
Original file line number Diff line number Diff line change
@@ -69,22 +69,20 @@ class ExecutionContext extends assert.Assertions {
};

this.try = async (...attemptArgs) => {
if (test.experiments.tryAssertion !== true) {
throw new Error('t.try() is currently an experiment. Opt in by setting `nonSemVerExperiments.tryAssertion` to `true`.');
}

const {args, buildTitle, implementations, receivedImplementationArray} = parseTestArgs(attemptArgs);

if (implementations.length === 0) {
throw new TypeError('Expected an implementation.');
}

const attemptPromises = implementations.map(implementation => {
const attemptPromises = implementations.map((implementation, index) => {
let {title, isSet, isValid, isEmpty} = buildTitle(implementation);

if (!isSet || isEmpty) {
title = `${test.title} (attempt ${test.attemptCount + 1})`;
} else if (!isValid) {
title = `${test.title} ─ attempt ${test.attemptCount + 1 + index}`;
} else if (isValid) {
title = `${test.title}${title}`;
} else {
throw new TypeError('`t.try()` titles must be strings'); // Throw synchronously!
}

@@ -594,7 +592,7 @@ class Test {
if (this.metadata.callback) {
if (returnedObservable || returnedPromise) {
const asyncType = returnedObservable ? 'observables' : 'promises';
this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`, if you want to return a promise simply declare the test via \`test(...)\``));
this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`. Use \`test.cb(...)\` for legacy callback APIs. When using promises, observables or async functions, use \`test(...)\`.`));
return this.finishPromised();
}

30 changes: 17 additions & 13 deletions lib/worker/subprocess.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,15 @@ require('./ensure-forked'); // eslint-disable-line import/no-unassigned-import

const ipc = require('./ipc');

const supportsESM = async () => {
try {
await import('data:text/javascript,');
return true;
} catch {}

return false;
};

ipc.send({type: 'ready-for-options'});
ipc.options.then(async options => {
require('./options').set(options);
@@ -134,23 +143,18 @@ ipc.options.then(async options => {
return null;
}).filter(provider => provider !== null);

// Lazily determine support since this prints an experimental warning.
let supportsESM = async () => {
try {
await import('../esm-probe.mjs');
supportsESM = async () => true;
} catch {
supportsESM = async () => false;
}

return supportsESM();
};

let requireFn = require;
let isESMSupported;
const load = async ref => {
for (const extension of extensionsToLoadAsModules) {
if (ref.endsWith(`.${extension}`)) {
if (await supportsESM()) { // eslint-disable-line no-await-in-loop
if (typeof isESMSupported !== 'boolean') {
// Lazily determine support since this prints an experimental warning.
// eslint-disable-next-line no-await-in-loop
isESMSupported = await supportsESM();
}

if (isESMSupported) {
return import(pathToFileURL(ref));
}

1,370 changes: 247 additions & 1,123 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ava",
"version": "3.3.0",
"version": "3.4.0",
"description": "Testing can be a drag. AVA helps you get it done.",
"license": "MIT",
"repository": "avajs/ava",
@@ -95,8 +95,8 @@
"p-map": "^3.0.0",
"picomatch": "^2.2.1",
"pkg-conf": "^3.1.0",
"plur": "^3.1.1",
"pretty-ms": "^5.1.0",
"plur": "^4.0.0",
"pretty-ms": "^6.0.0",
"read-pkg": "^5.2.0",
"resolve-cwd": "^3.0.0",
"slash": "^3.0.0",
@@ -106,33 +106,32 @@
"supertap": "^1.0.0",
"temp-dir": "^2.0.0",
"trim-off-newlines": "^1.0.1",
"update-notifier": "^4.0.0",
"update-notifier": "^4.1.0",
"write-file-atomic": "^3.0.1",
"yargs": "^15.1.0"
},
"devDependencies": {
"@ava/babel": "^1.0.1",
"@types/node": "^10.17.13",
"@sinonjs/fake-timers": "^6.0.0",
"ansi-escapes": "^4.3.0",
"delay": "^4.3.0",
"esm": "^3.2.25",
"execa": "^4.0.0",
"get-stream": "^5.1.0",
"lolex": "^5.1.2",
"p-event": "^4.1.0",
"proxyquire": "^2.1.3",
"react": "^16.12.0",
"react-test-renderer": "^16.12.0",
"replace-string": "^3.0.0",
"sinon": "^8.1.0",
"sinon": "^8.1.1",
"source-map-fixtures": "^2.1.0",
"tap": "^14.10.6",
"temp-write": "^4.0.0",
"tempy": "^0.3.0",
"tempy": "^0.4.0",
"touch": "^3.1.0",
"ts-node": "^8.6.2",
"typescript": "^3.7.5",
"xo": "^0.25.3",
"xo": "^0.26.1",
"zen-observable": "^0.8.15"
},
"xo": {
33 changes: 19 additions & 14 deletions test/assert.js
Original file line number Diff line number Diff line change
@@ -937,13 +937,18 @@ test('.throws()', gather(t => {
}, null);
});

// Regression test for https://github.com/avajs/ava/issues/1676
fails(t, () => {
passes(t, () => {
assertions.throws(() => {
throw new Error('foo');
}, undefined);
});

passes(t, async () => {
await assertions.throwsAsync(() => {
return Promise.reject(new Error('foo'));
}, undefined);
});

failsWith(t, () => {
assertions.throws(() => {}, null, null);
}, {
@@ -1085,47 +1090,47 @@ test('.throws() fails if passed a bad expectation', t => {
assertions.throws(() => {}, true);
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /true/}]
});

failsWith(t, () => {
assertions.throws(() => {}, 'foo');
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /foo/}]
});

failsWith(t, () => {
assertions.throws(() => {}, /baz/);
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /baz/}]
});

failsWith(t, () => {
assertions.throws(() => {}, class Bar {});
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /Bar/}]
});

failsWith(t, () => {
assertions.throws(() => {}, {});
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /\{\}/}]
});

failsWith(t, () => {
assertions.throws(() => {}, []);
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /\[\]/}]
});

@@ -1177,47 +1182,47 @@ test('.throwsAsync() fails if passed a bad expectation', t => {
assertions.throwsAsync(() => {}, true);
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /true/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, 'foo');
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /foo/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, /baz/);
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /baz/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, class Bar {});
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /Bar/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, {});
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /\{\}/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, []);
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object, `null` or `undefined`',
values: [{label: 'Called with:', formatted: /\[\]/}]
});

23 changes: 17 additions & 6 deletions test/helper/ava-test.js
Original file line number Diff line number Diff line change
@@ -2,15 +2,25 @@ const Test = require('../../lib/test');
const ContextRef = require('../../lib/context-ref');

function withExperiments(experiments = {}) {
function ava(fn, contextRef) {
const uniqueTestTitles = new Set();
const registerUniqueTitle = title => {
if (uniqueTestTitles.has(title)) {
return false;
}

uniqueTestTitles.add(title);
return true;
};

function ava(fn, contextRef, title = 'test') {
return new Test({
contextRef: contextRef || new ContextRef(),
experiments,
failWithoutAssertions: true,
fn,
registerUniqueTitle: () => true,
registerUniqueTitle,
metadata: {type: 'test', callback: false},
title: 'test'
title
});
}

@@ -20,7 +30,7 @@ function withExperiments(experiments = {}) {
experiments,
failWithoutAssertions: true,
fn,
registerUniqueTitle: () => true,
registerUniqueTitle,
metadata: {type: 'test', callback: false, failing: true},
title: 'test.failing'
});
@@ -32,7 +42,7 @@ function withExperiments(experiments = {}) {
experiments,
failWithoutAssertions: true,
fn,
registerUniqueTitle: () => true,
registerUniqueTitle,
metadata: {type: 'test', callback: true},
title: 'test.cb'
});
@@ -44,7 +54,7 @@ function withExperiments(experiments = {}) {
experiments,
failWithoutAssertions: true,
fn,
registerUniqueTitle: () => true,
registerUniqueTitle,
metadata: {type: 'test', callback: true, failing: true},
title: 'test.cb.failing'
});
@@ -55,3 +65,4 @@ function withExperiments(experiments = {}) {

exports.ava = withExperiments();
exports.withExperiments = withExperiments;
exports.newAva = () => withExperiments();
4 changes: 2 additions & 2 deletions test/helper/fix-reporter-env.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';
const os = require('os');
const lolex = require('lolex');
const fakeTimers = require('@sinonjs/fake-timers');

const fixColors = () => {
// Force consistent and high-fidelity logs.
@@ -10,7 +10,7 @@ const fixColors = () => {

module.exports = () => {
// Fix timestamps.
const clock = lolex.install({
const clock = fakeTimers.install({
now: new Date(2014, 11, 19, 17, 19, 12, 200).getTime(),
toFake: [
'Date'
2 changes: 1 addition & 1 deletion test/helper/report.js
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ exports.sanitizers = {
posix: str => replaceString(str, '\\', '/'),
slow: str => str.replace(/(?<slow>slow.+?)\(\d+m?s\)/g, '$<slow> (000ms)'),
timeout: str => replaceString(str, 'Timeout._onTimeout', 'Timeout.setTimeout'),
traces: str => str.replace(/(\[...)?[^\s'[]+\s\((.+\.js:\d+:\d+)\)/g, '$1$2'), // eslint-disable-line prefer-named-capture-group
traces: str => str.replace(/(\[...)?[^\s'[]+\s\((.+\.js:\d+:\d+)\)/g, '$1$2'),
version: str => replaceString(str, `v${pkg.version}`, 'v1.0.0-beta.5.1')
};

3 changes: 1 addition & 2 deletions test/integration/config.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ const fs = require('fs');
const path = require('path');
const {test} = require('tap');
const execa = require('execa');
const figures = require('figures');
const tempy = require('tempy');
const {execCli} = require('../helper/cli');

@@ -13,7 +12,7 @@ test('formats errors from ava.config.js', t => {

const lines = stderr.split('\n');
t.is(lines[0], '');
t.is(lines[1], ' ' + figures.cross + ' Error loading ava.config.js');
t.match(lines[1], /Error loading ava\.config\.js:/);
t.is(lines[2], '');
t.match(lines[3], /ava\.config\.js/);
t.match(lines[4], /foo/);
2 changes: 1 addition & 1 deletion test/load-config.js
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ test('loads config from factory function', t => {

test('does not support require() inside config.js files', t => {
changeDir('require');
t.throws(loadConfig, /require\(\) is not available in ava\.config\.js\. Use a \.cjs file instead/);
t.throws(loadConfig, /Error loading ava\.config\.js: require is not defined/);
t.end();
});

2 changes: 1 addition & 1 deletion test/observable.js
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ require('../lib/chalk').set();
require('../lib/worker/options').set({});

const {test} = require('tap');
const Observable = require('zen-observable');
const Test = require('../lib/test');
const Observable = require('zen-observable'); // eslint-disable-line import/order

function ava(fn) {
return new Test({
87 changes: 71 additions & 16 deletions test/test-try-commit.js
Original file line number Diff line number Diff line change
@@ -5,11 +5,10 @@ require('../lib/worker/options').set({chalkOptions: {level: 0}});
const {test} = require('tap');
const delay = require('delay');
const ContextRef = require('../lib/context-ref');
const {withExperiments} = require('./helper/ava-test');

const ava = withExperiments({tryAssertion: true});
const {newAva} = require('./helper/ava-test');

test('try-commit works', async t => {
const ava = newAva();
const instance = ava(async a => {
const res = await a.try(b => b.pass());
t.true(res.passed);
@@ -23,6 +22,7 @@ test('try-commit works', async t => {
});

test('try-commit is bound', async t => {
const ava = newAva();
const result = await ava(async a => {
const {try: tryFn} = a;
const res = await tryFn(b => b.pass());
@@ -33,6 +33,7 @@ test('try-commit is bound', async t => {
});

test('try-commit discards failed attempt', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => b.fail());
await res.discard();
@@ -43,6 +44,7 @@ test('try-commit discards failed attempt', async t => {
});

test('try-commit can discard produced result', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => b.pass());
res.discard();
@@ -55,6 +57,7 @@ test('try-commit can discard produced result', async t => {
});

test('try-commit fails when not all assertions were committed/discarded', async t => {
const ava = newAva();
const result = await ava(async a => {
a.pass();
await a.try(b => b.pass());
@@ -70,6 +73,7 @@ test('try-commit works with values', async t => {
const testValue1 = 123;
const testValue2 = 123;

const ava = newAva();
const result = await ava(async a => {
const res = await a.try((b, val1, val2) => {
b.is(val1, val2);
@@ -82,6 +86,7 @@ test('try-commit works with values', async t => {
});

test('try-commit is properly counted', async t => {
const ava = newAva();
const instance = ava(async a => {
const res = await a.try(b => {
b.is(1, 1);
@@ -102,6 +107,7 @@ test('try-commit is properly counted', async t => {
});

test('try-commit is properly counted multiple', async t => {
const ava = newAva();
const instance = ava(async a => {
const [res1, res2, res3] = await Promise.all([
a.try(b => b.pass()),
@@ -124,6 +130,7 @@ test('try-commit is properly counted multiple', async t => {

test('try-commit goes as many levels', async t => {
t.plan(5);
const ava = newAva();
const instance = ava(async a => {
t.ok(a.try);
const res1 = await a.try(async b => {
@@ -144,6 +151,7 @@ test('try-commit goes as many levels', async t => {
});

test('try-commit fails when not committed', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => b.pass());
t.true(res.passed);
@@ -156,6 +164,7 @@ test('try-commit fails when not committed', async t => {
});

test('try-commit fails when no assertions inside try', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(() => {});
t.false(res.passed);
@@ -171,6 +180,7 @@ test('try-commit fails when no assertions inside try', async t => {
});

test('try-commit fails when no assertions inside multiple try', async t => {
const ava = newAva();
const result = await ava(async a => {
const [res1, res2] = await Promise.all([
a.try(b => b.pass()),
@@ -193,6 +203,7 @@ test('try-commit fails when no assertions inside multiple try', async t => {
});

test('test fails when try-commit committed to failed state', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => b.fail());
t.false(res.passed);
@@ -204,25 +215,27 @@ test('test fails when try-commit committed to failed state', async t => {

test('try-commit has proper titles, when going in depth and width', async t => {
t.plan(6);
const ava = newAva();
await ava(async a => {
t.is(a.title, 'test');

await Promise.all([
a.try(async b => {
t.is(b.title, 'test (attempt 1)');
t.is(b.title, 'test attempt 1');

await Promise.all([
b.try(c => t.is(c.title, 'test (attempt 1) (attempt 1)')),
b.try(c => t.is(c.title, 'test (attempt 1) (attempt 2)'))
b.try(c => t.is(c.title, 'test attempt 1attempt 1')),
b.try(c => t.is(c.title, 'test attempt 1attempt 2'))
]);
}),
a.try(b => t.is(b.title, 'test (attempt 2)')),
a.try(b => t.is(b.title, 'test (attempt 3)'))
a.try(b => t.is(b.title, 'test attempt 2')),
a.try(b => t.is(b.title, 'test attempt 3'))
]);
}).run();
});

test('try-commit does not fail when calling commit twice', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => b.pass());
res.commit();
@@ -234,6 +247,7 @@ test('try-commit does not fail when calling commit twice', async t => {
});

test('try-commit does not fail when calling discard twice', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => b.pass());
res.discard();
@@ -247,6 +261,7 @@ test('try-commit does not fail when calling discard twice', async t => {
});

test('try-commit allows planning inside the try', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => {
b.plan(3);
@@ -263,6 +278,7 @@ test('try-commit allows planning inside the try', async t => {
});

test('try-commit fails when plan is not reached inside the try', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => {
b.plan(3);
@@ -278,6 +294,7 @@ test('try-commit fails when plan is not reached inside the try', async t => {
});

test('plan within try-commit is not affected by assertions outside', async t => {
const ava = newAva();
const result = await ava(async a => {
a.is(1, 1);
a.is(2, 2);
@@ -297,6 +314,7 @@ test('plan within try-commit is not affected by assertions outside', async t =>
});

test('assertions within try-commit do not affect plan in the parent test', async t => {
const ava = newAva();
const result = await ava(async a => {
a.plan(2);

@@ -317,6 +335,7 @@ test('assertions within try-commit do not affect plan in the parent test', async
});

test('test expected to fail will pass with failing try-commit within the test', async t => {
const ava = newAva();
const result = await ava.failing(async a => {
const res = await a.try(b => b.fail());
t.false(res.passed);
@@ -332,6 +351,7 @@ test('test expected to fail will pass with failing try-commit within the test',
});

test('try-commit works with callback test', async t => {
const ava = newAva();
const result = await ava.cb(a => {
a
.try(b => b.pass())
@@ -345,6 +365,7 @@ test('try-commit works with callback test', async t => {
});

test('try-commit works with failing callback test', async t => {
const ava = newAva();
const result = await ava.cb.failing(a => {
a
.try(b => b.fail())
@@ -366,6 +387,7 @@ test('try-commit works with failing callback test', async t => {
});

test('try-commit does not allow to use .end() in attempt when parent is callback test', async t => {
const ava = newAva();
const result = await ava.cb(a => {
a
.try(b => {
@@ -386,6 +408,7 @@ test('try-commit does not allow to use .end() in attempt when parent is callback
});

test('try-commit does not allow to use .end() in attempt when parent is regular test', async t => {
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => {
b.pass();
@@ -404,12 +427,13 @@ test('try-commit does not allow to use .end() in attempt when parent is regular

test('try-commit accepts macros', async t => {
const macro = b => {
t.is(b.title, ' Title');
t.is(b.title, 'test ─ Title');
b.pass();
};

macro.title = providedTitle => `${providedTitle ? providedTitle : ''} Title`;
macro.title = (providedTitle = '') => `${providedTitle} Title`.trim();

const ava = newAva();
const result = await ava(async a => {
const res = await a.try(macro);
t.true(res.passed);
@@ -420,19 +444,44 @@ test('try-commit accepts macros', async t => {
});

test('try-commit accepts multiple macros', async t => {
const macros = [b => b.pass(), b => b.fail()];
const ava = newAva();
const result = await ava(async a => {
const [res1, res2] = await a.try(macros);
const [res1, res2] = await a.try([
b => {
t.is(b.title, 'test ─ attempt 1');
b.pass();
},
b => {
t.is(b.title, 'test ─ attempt 2');
b.fail();
}
]);
t.true(res1.passed);
res1.commit();
t.false(res2.passed);
res2.discard();

const [res3, res4] = await a.try([
b => {
t.is(b.title, 'test ─ attempt 3');
b.pass();
},
b => {
t.is(b.title, 'test ─ attempt 4');
b.fail();
}
]);
t.true(res3.passed);
res3.commit();
t.false(res4.passed);
res4.discard();
}).run();

t.true(result.passed);
});

test('try-commit returns results in the same shape as when implementations are passed', async t => {
const ava = newAva();
const result = await ava(async a => {
const [res1, res2, res3] = await Promise.all([
a.try(b => b.pass()),
@@ -457,6 +506,7 @@ test('try-commit returns results in the same shape as when implementations are p
});

test('try-commit abides timeout', async t => {
const ava = newAva();
const result1 = await ava(async a => {
a.timeout(10);
const result = await a.try(async b => {
@@ -471,6 +521,7 @@ test('try-commit abides timeout', async t => {
});

test('try-commit fails when it exceeds its own timeout', async t => {
const ava = newAva();
const result = await ava(async a => {
a.timeout(200);
const result = await a.try(async b => {
@@ -494,6 +545,7 @@ test('try-commit fails when it exceeds its own timeout', async t => {
});

test('try-commit refreshes the timeout on commit/discard', async t => {
const ava = newAva();
const result1 = await ava.cb(a => {
a.timeout(100);
a.plan(3);
@@ -507,6 +559,7 @@ test('try-commit refreshes the timeout on commit/discard', async t => {
});

test('assertions within try-commit do not refresh the timeout', async t => {
const ava = newAva();
const result = await ava(async a => {
a.timeout(15);
a.pass();
@@ -534,6 +587,7 @@ test('try-commit inherits the test context', async t => {
const context = new ContextRef();
const data = {foo: 'bar'};
context.set(data);
const ava = newAva();
const result = await ava(async a => {
const res = await a.try(b => {
b.pass();
@@ -549,6 +603,7 @@ test('assigning context in try-commit does not affect parent', async t => {
const context = new ContextRef();
const data = {foo: 'bar'};
context.set(data);
const ava = newAva();
const result = await ava(async a => {
t.strictDeepEqual(a.context, data);
const res = await a.try(b => {
@@ -563,26 +618,26 @@ test('assigning context in try-commit does not affect parent', async t => {
});

test('do not run assertions outside of an active attempt', async t => {
const ava = newAva();
const passing = await ava(async a => {
await a.try(() => {});
a.pass();
}).run();
}, undefined, 'passing').run();

t.false(passing.passed);
t.match(passing.error.message, /Assertion passed, but an attempt is pending. Use the attempts assertions instead/);

const pending = await ava(async a => {
await a.try(() => {});
await a.throwsAsync(Promise.reject(new Error('')));
}).run();

}, undefined, 'pending').run();
t.false(pending.passed);
t.match(pending.error.message, /Assertion started, but an attempt is pending. Use the attempts assertions instead/);

const failing = await ava(async a => {
await a.try(() => {});
a.fail();
}).run();
}, undefined, 'failing').run();

t.false(failing.passed);
t.match(failing.error.message, /Assertion failed, but an attempt is pending. Use the attempts assertions instead/);
2 changes: 1 addition & 1 deletion test/try-snapshot.js
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ const ContextRef = require('../lib/context-ref');

function setup(title, manager, fn) {
return new Test({
experiments: {tryAssertion: true},
experiments: {},
fn,
failWithoutAssertions: true,
metadata: {type: 'test', callback: false},
3 changes: 2 additions & 1 deletion test/ts-types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"target": "es2018",
"types": [],
"strict": true
}
}
4 changes: 2 additions & 2 deletions test/watcher.js
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@
const path = require('path');
const EventEmitter = require('events');
const {PassThrough} = require('stream');
const fakeTimers = require('@sinonjs/fake-timers');
const defaultIgnore = require('ignore-by-default').directories();
const lolex = require('lolex');
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const {test} = require('tap');
@@ -112,7 +112,7 @@ group('chokidar', (beforeEach, test, group) => {
clock.uninstall();
}

clock = lolex.install({
clock = fakeTimers.install({
toFake: [
'setImmediate',
'setTimeout',