Skip to content

Breaking changes to the test() interface #2785

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 10, 2021
17 changes: 11 additions & 6 deletions .xo-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@
},
"overrides": [
{
"files": "index.d.ts",
"files": [
"index.d.ts",
"types/*.d.ts"
],
"rules": {
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/method-signature-style": "off",
"@typescript-eslint/prefer-readonly-parameter-types": "off",
"@typescript-eslint/prefer-function-type": "off",
"@typescript-eslint/unified-signatures": "off"
"import/extensions": "off"
}
},
{
Expand Down Expand Up @@ -71,6 +70,12 @@
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off"
}
},
{
"files": "test/macros/fixtures/macros.js",
"rules": {
"ava/no-identical-title": "off"
}
}
]
}
36 changes: 26 additions & 10 deletions docs/01-writing-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ console.log('Test file currently being run:', test.meta.file);

Additional arguments passed to the test declaration will be passed to the test implementation. This is useful for creating reusable test macros.

You can use plain functions:

```js
function macro(t, input, expected) {
t.is(eval(input), expected);
Expand All @@ -303,7 +305,7 @@ test('2 + 2 = 4', macro, '2 + 2', 4);
test('2 * 3 = 6', macro, '2 * 3', 6);
```

You can build the test title programmatically by attaching a `title` function to the macro:
With AVA 3 you can build the test title programmatically by attaching a `title` function to the macro:

```js
function macro(t, input, expected) {
Expand All @@ -319,21 +321,35 @@ test('providedTitle', macro, '3 * 3', 9);

The `providedTitle` argument defaults to `undefined` if the user does not supply a string title. This means you can use a parameter assignment to set the default value. The example above uses the empty string as the default.

You can also pass arrays of macro functions:
However with AVA 4 the preferred approach is to use the `test.macro()` helper:

```js
const safeEval = require('safe-eval');
import test from 'ava';

function evalMacro(t, input, expected) {
const macro = test.macro((t, input, expected) => {
t.is(eval(input), expected);
}
});

function safeEvalMacro(t, input, expected) {
t.is(safeEval(input), expected);
}
test('title', macro, '3 * 3', 9);
```

Or with a title function:

test([evalMacro, safeEvalMacro], '2 + 2', 4);
test([evalMacro, safeEvalMacro], '2 * 3', 6);
```js
import test from 'ava';

const macro = test.macro({
exec(t, input, expected) {
t.is(eval(input), expected);
},
title(providedTitle = '', input, expected) {
return `${providedTitle} ${input} = ${expected}`.trim();
}
});

test(macro, '2 + 2', 4);
test(macro, '2 * 3', 6);
test('providedTitle', macro, '3 * 3', 9);
```

We encourage you to use macros instead of building your own test generators ([here is an example](https://github.com/avajs/ava-codemods/blob/47073b5b58aa6f3fb24f98757be5d3f56218d160/test/ok-to-truthy.js#L7-L9) of code that should be replaced with a macro). Macros are designed to perform static analysis of your code, which can lead to better performance, IDE integration, and linter rules.
10 changes: 3 additions & 7 deletions docs/03-assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ 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.
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 `null` is returned.

`expectation` can be an object with one or more of the following properties:

Expand Down Expand Up @@ -257,13 +257,11 @@ test('throws', t => {
});
```

Does not return anything.

### `.throwsAsync(thrower, expectation?, message?)`

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.
The thrown value *must* be an error. It is returned so you can run more assertions against it. If the assertion fails then `null` is returned.

`expectation` can be an object with one or more of the following properties:

Expand Down Expand Up @@ -294,8 +292,6 @@ test('rejects', async t => {
});
```

Does not return anything.

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

Assert that no error is thrown. `fn` must be a function which shouldn't throw. Does not return anything.
Expand Down Expand Up @@ -330,7 +326,7 @@ AVA 3 supports an `options` object that lets you select a specific snapshot, fo

In AVA 3, you cannot update snapshots while using `t.snapshot.skip()`.

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

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

Expand Down
44 changes: 40 additions & 4 deletions docs/recipes/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ const hasLength = (t: ExecutionContext, input: string, expected: number) => {
test('bar has length 3', hasLength, 'bar', 3);
```

In order to be able to assign the `title` property to a macro you need to type the function:
### AVA 3

With AVA 3, in order to be able to assign the `title` property to a macro you need to type the function:

```ts
import test, {Macro} from 'ava';
Expand Down Expand Up @@ -149,12 +151,46 @@ const macro: CbMacro<[]> = t => {
test.cb(macro);
```

### AVA 4

With AVA 4 you can use the `test.macro()` helper to create macros:

```ts
import test from 'ava';

const macro = test.macro((t, input: string, expected: number) => {
t.is(eval(input), expected);
});

test('title', macro, '3 * 3', 9);
```

Or with a title function:

```ts
import test from 'ava';

const macro = test.macro({
exec(t, input: string, expected: number) {
t.is(eval(input), expected);
},
title(providedTitle = '', input, expected) {
return `${providedTitle} ${input} = ${expected}`.trim();
}
});

test(macro, '2 + 2', 4);
test(macro, '2 * 3', 6);
test('providedTitle', macro, '3 * 3', 9);
```

## Typing [`t.context`](../01-writing-tests.md#test-context)

By default, the type of `t.context` will be the empty object (`{}`). AVA exposes an interface `TestInterface<Context>` which you can use to apply your own type to `t.context`. This can help you catch errors at compile-time:
By default, the type of `t.context` will be the empty object (`{}`). AVA exposes an interface `TestInterface<Context>` (in AVA 4 this is `TestFn<Context>`) which you can use to apply your own type to `t.context`. This can help you catch errors at compile-time:

```ts
import anyTest, {TestInterface} from 'ava';
import anyTest, {TestInterface} from 'ava'; // AVA 3
// import anyTest, {TestFn as TestInterface} from 'ava'; // AVA 4, usage is the same

const test = anyTest as TestInterface<{foo: string}>;

Expand All @@ -178,7 +214,7 @@ test('an actual test', t => {
You can also type the context when creating macros:

```ts
import anyTest, {Macro, TestInterface} from 'ava';
import anyTest, {Macro, TestInterface} from 'ava'; // AVA 3

interface Context {
foo: string
Expand Down
Loading