Skip to content

Commit

Permalink
Merge branch 'main' into feat-unordered-equal
Browse files Browse the repository at this point in the history
  • Loading branch information
tommy-mitchell committed Feb 27, 2024
2 parents f0b4b80 + 2e0c2b1 commit b18c69d
Show file tree
Hide file tree
Showing 133 changed files with 10,505 additions and 11,031 deletions.
24 changes: 12 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [^16.18, ^18.16, ^20.3]
node-version: [^18.18, ^20.8, ^21]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Enable symlinks
if: matrix.os == 'windows-latest'
run: |
git config core.symlinks true
git reset --hard
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
Expand All @@ -41,10 +41,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
ts-version: [~5.1]
ts-version: [~5.2, ~5.3]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: npm
Expand All @@ -59,8 +59,8 @@ jobs:
name: Test package-lock for unexpected modifications
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: npm
Expand All @@ -79,8 +79,8 @@ jobs:
name: Install dependencies without using a lockfile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
- run: npm install --no-package-lock --no-audit
Expand All @@ -91,8 +91,8 @@ jobs:
name: Lint source files
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: npm
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.tap
/coverage
/media/**/node_modules/
/node_modules/
Expand Down
16 changes: 7 additions & 9 deletions .taprc
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
browser: false
coverage: false
files:
- "test-tap/*.js"
- "test-tap/reporters/*.js"
- "test-tap/integration/*.js"
flow: false
jsx: false
disable-coverage: true
allow-empty-coverage: true
include:
- test-tap/*.js
- test-tap/reporters/*.js
- test-tap/integration/*.js
timeout: 300
ts: false
tsconfig: test-tap/tsconfig.json
4 changes: 4 additions & 0 deletions .xo-config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ module.exports = {
'media/**',
'test/config/fixtures/config-errors/test.js',
'test/line-numbers/fixtures/line-numbers.js',
'test/**/fixtures',
'test-tap/fixture/snapshots/test-sourcemaps/build/**',
'test-tap/fixture/report/edgecases/ast-syntax-error.cjs',
'test-tap/fixture/**/*.ts',
'test-types',
'examples/typescript-*/**/*.ts',
],
rules: {
Expand All @@ -25,6 +28,7 @@ module.exports = {
],
'import/newline-after-import': 'error',
'unicorn/require-post-message-target-origin': 'off',
'unicorn/prefer-event-target': 'off',
},
overrides: [
{
Expand Down
4 changes: 3 additions & 1 deletion ava.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const skipWatchMode = process.env.TEST_AVA_SKIP_WATCH_MODE ? ['!test/watch-mode/

export default { // eslint-disable-line import/no-anonymous-default-export
files: ['test/**', '!test/**/{fixtures,helpers}/**', ...skipWatchMode],
ignoredByWatcher: ['{coverage,docs,media,test-types,test-tap}/**'],
watchMode: {
ignoreChanges: ['{coverage,docs,media,test-types,test-tap}/**'],
},
environmentVariables: {
AVA_FAKE_SCM_ROOT: '.fake-root', // This is an internal test flag.
},
Expand Down
4 changes: 2 additions & 2 deletions docs/01-writing-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ AVA tries to run test files with their current working directory set to the dire

## Test isolation

Each test file is run in a new worker thread. This is new as of AVA 4, though you can fall back to AVA 3's behavior of running in separate processes.
By default each test file is run in a new worker thread. You can fall back running in separate processes.

AVA will set `process.env.NODE_ENV` to `test`, unless the `NODE_ENV` environment variable has been set. This is useful if the code you're testing has test defaults (for example when picking what database to connect to). It may cause your code or its dependencies to behave differently though. Note that `'NODE_ENV' in process.env` will always be `true`.

Expand Down Expand Up @@ -154,7 +154,7 @@ AVA lets you register hooks that are run before and after your tests. This allow

If a test is skipped with the `.skip` modifier, the respective `.beforeEach()`, `.afterEach()` and `.afterEach.always()` hooks are not run. Likewise, if all tests in a test file are skipped `.before()`, `.after()` and `.after.always()` hooks for the file are not run.

*You may not need to use `.afterEach.always()` hooks to clean up after a test.* You can use [`t.teardown()`](./02-execution-context.md#tteardownfn) to undo side-effects *within* a particular test.
*You may not need to use `.afterEach.always()` hooks to clean up after a test.* You can use [`t.teardown()`](./02-execution-context.md#tteardownfn) to undo side-effects *within* a particular test. Or use [`registerCompletionHandler()`](./08-common-pitfalls.md#timeouts-because-a-file-failed-to-exit) to run cleanup code after AVA has completed its work.

Like `test()` these methods take an optional title and an implementation function. The title is shown if your hook fails to execute. The implementation is called with an [execution object](./02-execution-context.md). You can use assertions in your hooks. You can also pass a [macro function](#reusing-test-logic-through-macros) and additional arguments.

Expand Down
48 changes: 23 additions & 25 deletions docs/03-assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ test('unicorns are truthy', t => {

If multiple assertion failures are encountered within a single test, AVA will only display the *first* one.

Assertions return a boolean indicating whether they passed. You can use this to return early from a test. Note that this does not apply to the "throws" and `snapshot()` assertions.
Assertions return `true` if they've passed and throw otherwise. Catching this error does not cause the test to pass. The error value is undocumented.

If you use TypeScript you can use some assertions as type guards.

Note that the "throws" assertions return the error that was thrown (provided the assertion passed).

## Assertion planning

Expand Down Expand Up @@ -95,47 +99,47 @@ test('custom assertion', t => {

### `.pass(message?)`

Passing assertion. Returns a boolean indicating whether the assertion passed.
Passing assertion.

### `.fail(message?)`

Failing assertion. Returns a boolean indicating whether the assertion passed.
Failing assertion.

### `.assert(actual, message?)`

Asserts that `actual` is truthy. Returns a boolean indicating whether the assertion passed.
Asserts that `actual` is truthy.

### `.truthy(actual, message?)`

Assert that `actual` is truthy. Returns a boolean indicating whether the assertion passed.
Assert that `actual` is truthy.

### `.falsy(actual, message?)`

Assert that `actual` is falsy. Returns a boolean indicating whether the assertion passed.
Assert that `actual` is falsy.

### `.true(actual, message?)`

Assert that `actual` is `true`. Returns a boolean indicating whether the assertion passed.
Assert that `actual` is `true`.

### `.false(actual, message?)`

Assert that `actual` is `false`. Returns a boolean indicating whether the assertion passed.
Assert that `actual` is `false`.

### `.is(actual, expected, message?)`

Assert that `actual` is the same as `expected`. This is based on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Returns a boolean indicating whether the assertion passed.
Assert that `actual` is the same as `expected`. This is based on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is).

### `.not(actual, expected, message?)`

Assert that `actual` is not the same as `expected`. This is based on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Returns a boolean indicating whether the assertion passed.
Assert that `actual` is not the same as `expected`. This is based on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is).

### `.deepEqual(actual, expected, message?)`

Assert that `actual` is deeply equal to `expected`. See [Concordance](https://github.com/concordancejs/concordance) for details.

### `.notDeepEqual(actual, expected, message?)`

Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqual()`. Returns a boolean indicating whether the assertion passed.
Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqual()`.

### `.unorderedEqual(actual, expected, message?)`

Expand Down Expand Up @@ -176,14 +180,12 @@ You can also use arrays, but note that any indices in `actual` that are not in `
t.like([1, 2, 3, 4], [1, , 3])
```

Finally, this returns a boolean indicating whether the assertion passed.

### `.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. If the assertion fails then `undefined` is returned.

Assert that an error is thrown. `fn` must be a function which should throw. By default, the thrown value *must* be an error. It is returned so you can run more assertions against it.
`expectation` can be an object with one or more of the following properties:

* `any`: a boolean, if `true` then the thrown value does not need to be an error. Defaults to `false`
* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expectation.is`
* `message`: the following types are valid:
Expand Down Expand Up @@ -215,10 +217,10 @@ test('throws', t => {

Assert that an error is thrown. `thrower` can be an async function which should throw, or a promise that should reject. This assertion must be awaited.

The thrown value *must* be an error. It is returned so you can run more assertions against it. If the assertion fails then `undefined` is returned.

By default, the thrown value *must* be an error. It is returned so you can run more assertions against it.
`expectation` can be an object with one or more of the following properties:

* `any`: a boolean, if `true` then the thrown value does not need to be an error. Defaults to `false`
* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expectation.is`
* `message`: the following types are valid:
Expand Down Expand Up @@ -251,7 +253,7 @@ test('rejects', async t => {

### `.notThrows(fn, message?)`

Assert that no error is thrown. `fn` must be a function which shouldn't throw. Does not return anything.
Assert that no error is thrown. `fn` must be a function which shouldn't throw.

### `.notThrowsAsync(nonThrower, message?)`

Expand All @@ -265,15 +267,13 @@ test('resolves', async t => {
});
```

Does not return anything.

### `.regex(contents, regex, message?)`

Assert that `contents` matches `regex`. Returns a boolean indicating whether the assertion passed.
Assert that `contents` matches `regex`.

### `.notRegex(contents, regex, message?)`

Assert that `contents` does not match `regex`. Returns a boolean indicating whether the assertion passed.
Assert that `contents` does not match `regex`.

### `.snapshot(expected, message?)`

Expand All @@ -285,7 +285,7 @@ Compares the `expected` value with a previously recorded snapshot. Snapshots are

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 implementation 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.
`.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. Calling `commit()` on a failed result will throw an error.

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.

Expand Down Expand Up @@ -324,5 +324,3 @@ test('flaky macro', async t => {
secondTry.commit();
});
```

Returns a boolean indicating whether the assertion passed.
2 changes: 1 addition & 1 deletion docs/07-test-timeouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/avajs/ava/tree/main/examples/timeouts?file=test.js&terminal=test&view=editor)

Timeouts in AVA behave differently than in other test frameworks. AVA resets a timer after each test, forcing tests to quit if no new test results were received within the specified timeout. This can be used to handle stalled tests.
Timeouts in AVA behave differently than in other test frameworks. AVA resets a timer after each test, forcing tests to quit if no new test results were received within the specified timeout. This can be used to handle stalled tests. This same mechanism is used to determine when a test file is preventing a clean exit.

The default timeout is 10 seconds.

Expand Down
40 changes: 38 additions & 2 deletions docs/08-common-pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ Note that the following is not a native error:
const error = Object.create(Error.prototype);
```

This can be surprising, since `error instanceof Error` returns `true`.
This can be surprising, since `error instanceof Error` returns `true`. You can set `any: true` in the expectations to handle these values:

```js
const error = Object.create(Error.prototype);
t.throws(() => { throw error }, {any: true});
```

## AVA in Docker

Expand Down Expand Up @@ -76,6 +81,37 @@ Error [ERR_WORKER_INVALID_EXEC_ARGV]: Initiated Worker with invalid execArgv fla

If possible don't specify the command line option when running AVA. Alternatively you could [disable worker threads in AVA](./06-configuration.md#options).

## Timeouts because a file failed to exit

You may get a "Timed out while running tests" error because AVA failed to exit when running a particular file.

AVA waits for Node.js to exit the worker thread or child process. If this takes too long, AVA counts it as a timeout.

It is best practice to make sure your code exits cleanly. We've also seen occurrences where an explicit `process.exit()` call inside a worker thread could not be observed in AVA's main process.

For these reasons we're not providing an option to disable this timeout behavior. However, it is possible to register a callback for when AVA has completed the test run without uncaught exceptions or unhandled rejections. From inside this callback you can do whatever you need to do, including calling `process.exit()`.

Create a `_force-exit.mjs` file:

```js
import process from 'node:process';
import { registerCompletionHandler } from 'ava';

registerCompletionHandler(() => {
process.exit();
});
```

Completion handlers are invoked in order of registration. Results are not awaited.

Load it for all test files through AVA's `require` option:

```js
export default {
require: ['./_force-exit.mjs'],
};
```

## Sharing variables between asynchronous tests

By default AVA executes tests concurrently. This can cause problems if your tests are asynchronous and share variables.
Expand Down Expand Up @@ -107,7 +143,7 @@ test('increment twice', async t => {
});
```

Concurrent tests allow for asynchronous tests to execute more quickly, but if they rely on shared state you this may lead to unexpected test failures. If the shared state cannot be avoided, you can execute your tests serially:
Concurrent tests allow for asynchronous tests to execute more quickly, but if they rely on shared state this may lead to unexpected test failures. If the shared state cannot be avoided, you can execute your tests serially:

```js
import test from 'ava';
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/endpoint-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Since tests run concurrently, it's best to create a fresh server instance at lea
Check out the example below:

```js
import http from 'http';
import http from 'node:http';
import test from 'ava';
import got from 'got';
import listen from 'test-listen';
Expand Down
3 changes: 0 additions & 3 deletions docs/recipes/es-modules.md

This file was deleted.

0 comments on commit b18c69d

Please sign in to comment.