Skip to content

Commit e04e7d6

Browse files
committed
Refactor TypeScript definition
Remove the need for a build step. Update the `throws` and `notThrows` assertions, adding `Observable` and allowing the returned error to be typed. Remove the inline documentation, to be added later. Simplify typing of `t.context`. Reassigning the `test` method along with a type cast is now sufficient. Update to TypeScript 2.7.1.
1 parent 0b654c4 commit e04e7d6

File tree

6 files changed

+257
-388
lines changed

6 files changed

+257
-388
lines changed

docs/recipes/typescript.md

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,7 @@ Translations: [Español](https://github.com/avajs/ava-docs/blob/master/es_ES/doc
44

55
AVA comes bundled with a TypeScript definition file. This allows developers to leverage TypeScript for writing tests.
66

7-
8-
## Setup
9-
10-
First install [TypeScript](https://github.com/Microsoft/TypeScript) (if you already have it installed, make sure you use version 2.1 or greater).
11-
12-
```
13-
$ npm install --save-dev typescript
14-
```
15-
16-
Create a [`tsconfig.json`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) file. This file specifies the compiler options required to compile the project or the test file.
17-
18-
```json
19-
{
20-
"compilerOptions": {
21-
"module": "commonjs",
22-
"target": "es2015",
23-
"sourceMap": true
24-
}
25-
}
26-
```
7+
This guide assumes you've already set up TypeScript for your project. Note that AVA's definition has been tested with version 2.7.1.
278

289
Add a `test` script in the `package.json` file. It will compile the project first and then run AVA.
2910

@@ -35,8 +16,9 @@ Add a `test` script in the `package.json` file. It will compile the project firs
3516
}
3617
```
3718

19+
Make sure that AVA runs your built TypeScript files.
3820

39-
## Add tests
21+
## Writing tests
4022

4123
Create a `test.ts` file.
4224

@@ -50,57 +32,82 @@ test(async (t) => {
5032
});
5133
```
5234

53-
## Working with [macros](https://github.com/avajs/ava#test-macros)
35+
## Using [macros](https://github.com/avajs/ava#test-macros)
5436

55-
In order to be able to assign the `title` property to a macro:
37+
In order to be able to assign the `title` property to a macro you need to type the function:
5638

5739
```ts
58-
import test, { AssertContext, Macro } from 'ava';
40+
import test, {Macro} from 'ava';
5941

60-
const macro: Macro<AssertContext> = (t, input, expected) => {
42+
const macro: Macro = (t, input: string, expected: number) => {
6143
t.is(eval(input), expected);
62-
}
63-
64-
macro.title = (providedTitle, input, expected) => `${providedTitle} ${input} = ${expected}`.trim();
44+
};
45+
macro.title = (providedTitle: string, input: string, expected: number) => `${providedTitle} ${input} = ${expected}`.trim();
6546

6647
test(macro, '2 + 2', 4);
6748
test(macro, '2 * 3', 6);
6849
test('providedTitle', macro, '3 * 3', 9);
6950
```
7051

71-
## Working with [`context`](https://github.com/avajs/ava#test-context)
72-
73-
By default, the type of `t.context` will be [`any`](https://www.typescriptlang.org/docs/handbook/basic-types.html#any). AVA exposes an interface `RegisterContextual<T>` which you can use to apply your own type to `t.context`. This can help you catch errors at compile-time:
52+
You'll need a different type if you're expecting your macro to be used with a callback test:
7453

7554
```ts
76-
import * as ava from 'ava';
55+
import test, {CbMacro} from 'ava';
7756

78-
function contextualize<T>(getContext: () => T): ava.RegisterContextual<T> {
79-
ava.test.beforeEach(t => {
80-
Object.assign(t.context, getContext());
81-
});
57+
const macro: CbMacro = t => {
58+
t.pass();
59+
setTimeout(t.end, 100);
60+
};
8261

83-
return ava.test;
84-
}
62+
test.cb(macro);
63+
```
8564

86-
const test = contextualize(() => ({ foo: 'bar' }));
65+
## Typing [`t.context`](https://github.com/avajs/ava#test-context)
66+
67+
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:
68+
69+
```ts
70+
import anyTest, {TestInterface} from 'ava';
71+
72+
const test: TestInterface<{foo: string}> = anyTest;
73+
74+
test.beforeEach(t => {
75+
t.context = {foo: 'bar'};
76+
});
8777

8878
test.beforeEach(t => {
8979
t.context.foo = 123; // error: Type '123' is not assignable to type 'string'
9080
});
9181

92-
test.serial.failing.cb('very long chains are properly typed', t => {
93-
t.context.fooo = 'a value'; // error: Property 'fooo' does not exist on type '{ foo: string }'
82+
test.serial.cb.failing('very long chains are properly typed', t => {
83+
t.context.fooo = 'a value'; // error: Property 'fooo' does not exist on type ''
9484
});
9585

9686
test('an actual test', t => {
9787
t.deepEqual(t.context.foo.map(c => c), ['b', 'a', 'r']); // error: Property 'map' does not exist on type 'string'
9888
});
9989
```
10090

91+
You can also type the context when creating macros:
10192

102-
## Execute the tests
93+
```ts
94+
import anyTest, {Macro, TestInterface} from 'ava';
10395

96+
interface Context {
97+
foo: string
98+
}
99+
100+
const test: TestInterface<Context> = anyTest;
101+
102+
const macro: Macro<Context> = (t, expected: string) => {
103+
t.is(t.context.foo, expected);
104+
};
105+
106+
test.beforeEach(t => {
107+
t.context = {foo: 'bar'};
108+
});
109+
110+
test('foo is bar', macro, 'bar');
104111
```
105-
$ npm test
106-
```
112+
113+
Note that, despite the type cast above, when executing `t.context` is an empty object unless it's assigned.

index.d.ts

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
export interface ObservableLike {
2+
subscribe(observer: (value: any) => void): void;
3+
}
4+
5+
export type ThrowsErrorValidator = (new (...args: Array<any>) => any) | RegExp | string | ((error: any) => boolean);
6+
7+
export interface SnapshotOptions {
8+
id?: string;
9+
}
10+
11+
export interface Assertions {
12+
deepEqual<ValueType = any>(actual: ValueType, expected: ValueType, message?: string): void;
13+
fail(message?: string): void;
14+
false(actual: any, message?: string): void;
15+
falsy(actual: any, message?: string): void;
16+
ifError(error: any, message?: string): void;
17+
is<ValueType = any>(actual: ValueType, expected: ValueType, message?: string): void;
18+
not<ValueType = any>(actual: ValueType, expected: ValueType, message?: string): void;
19+
notDeepEqual<ValueType = any>(actual: ValueType, expected: ValueType, message?: string): void;
20+
notRegex(string: string, regex: RegExp, message?: string): void;
21+
notThrows(value: () => never, message?: string): void;
22+
notThrows(value: () => ObservableLike, message?: string): Promise<void>;
23+
notThrows(value: () => PromiseLike<any>, message?: string): Promise<void>;
24+
notThrows(value: () => any, message?: string): void;
25+
notThrows(value: ObservableLike, message?: string): Promise<void>;
26+
notThrows(value: PromiseLike<any>, message?: string): Promise<void>;
27+
pass(message?: string): void;
28+
regex(string: string, regex: RegExp, message?: string): void;
29+
snapshot(expected: any, message?: string): void;
30+
snapshot(expected: any, options: SnapshotOptions, message?: string): void;
31+
throws(value: () => never, error?: ThrowsErrorValidator, message?: string): any;
32+
throws(value: () => ObservableLike, error?: ThrowsErrorValidator, message?: string): Promise<any>;
33+
throws(value: () => PromiseLike<any>, error?: ThrowsErrorValidator, message?: string): Promise<any>;
34+
throws(value: () => any, error?: ThrowsErrorValidator, message?: string): any;
35+
throws(value: ObservableLike, error?: ThrowsErrorValidator, message?: string): Promise<any>;
36+
throws(value: PromiseLike<any>, error?: ThrowsErrorValidator, message?: string): Promise<any>;
37+
true(actual: any, message?: string): void;
38+
truthy(actual: any, message?: string): void;
39+
}
40+
41+
export interface ExecutionContext<Context = {}> extends Assertions {
42+
context: Context;
43+
skip: Assertions;
44+
title: string;
45+
log(...values: Array<any>): void;
46+
plan(count: number): void;
47+
}
48+
49+
export interface CbExecutionContext<Context = {}> extends ExecutionContext<Context> {
50+
end(): void;
51+
}
52+
53+
export type ImplementationResult = PromiseLike<void> | ObservableLike | Iterator<any> | void;
54+
export type Implementation<Context = {}> = (t: ExecutionContext<Context>) => ImplementationResult;
55+
export type CbImplementation<Context = {}> = (t: CbExecutionContext<Context>) => ImplementationResult;
56+
57+
export interface Macro<Context = {}> {
58+
(t: ExecutionContext<Context>, ...args: Array<any>): ImplementationResult;
59+
title?: (providedTitle: string, ...args: Array<any>) => string;
60+
}
61+
62+
export interface CbMacro<Context = {}> {
63+
(t: CbExecutionContext<Context>, ...args: Array<any>): ImplementationResult;
64+
title?: (providedTitle: string, ...args: Array<any>) => string;
65+
}
66+
67+
export interface TestInterface<Context = {}> {
68+
(title: string, implementation: Implementation<Context>): void;
69+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
70+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
71+
72+
after: AfterInterface<null>;
73+
afterEach: AfterInterface<Context>;
74+
before: BeforeInterface<null>;
75+
beforeEach: BeforeInterface<Context>;
76+
cb: CbInterface<Context>;
77+
failing: FailingInterface<Context>;
78+
only: OnlyInterface<Context>;
79+
serial: SerialInterface<Context>;
80+
skip: SkipInterface<Context>;
81+
todo: TodoDeclaration;
82+
}
83+
84+
export interface AfterInterface<Context = {}> {
85+
(title: string, implementation: Implementation<Context>): void;
86+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
87+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
88+
89+
always: AlwaysInterface<Context>;
90+
cb: HookCbInterface<Context>;
91+
skip: SkipInterface<Context>;
92+
}
93+
94+
export interface AlwaysInterface<Context = {}> {
95+
(title: string, implementation: Implementation<Context>): void;
96+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
97+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
98+
99+
cb: HookCbInterface<Context>;
100+
skip: SkipInterface<Context>;
101+
}
102+
103+
export interface BeforeInterface<Context = {}> {
104+
(title: string, implementation: Implementation<Context>): void;
105+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
106+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
107+
108+
cb: HookCbInterface<Context>;
109+
skip: SkipInterface<Context>;
110+
}
111+
112+
export interface CbInterface<Context = {}> {
113+
(title: string, implementation: CbImplementation<Context>): void;
114+
(title: string, macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
115+
(macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
116+
117+
failing: CbFailingInterface<Context>;
118+
only: CbOnlyInterface<Context>;
119+
skip: CbSkipInterface<Context>;
120+
}
121+
122+
export interface CbFailingInterface<Context = {}> {
123+
(title: string, implementation: CbImplementation<Context>): void;
124+
(title: string, macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
125+
(macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
126+
127+
only: CbOnlyInterface<Context>;
128+
skip: CbSkipInterface<Context>;
129+
}
130+
131+
export interface CbOnlyInterface<Context = {}> {
132+
(title: string, implementation: CbImplementation<Context>): void;
133+
(title: string, macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
134+
(macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
135+
}
136+
137+
export interface CbSkipInterface<Context = {}> {
138+
(title: string, implementation: CbImplementation<Context>): void;
139+
(title: string, macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
140+
(macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
141+
}
142+
143+
export interface FailingInterface<Context = {}> {
144+
(title: string, implementation: Implementation<Context>): void;
145+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
146+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
147+
148+
only: OnlyInterface<Context>;
149+
skip: SkipInterface<Context>;
150+
}
151+
152+
export interface HookCbInterface<Context = {}> {
153+
(title: string, implementation: CbImplementation<Context>): void;
154+
(title: string, macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
155+
(macro: CbMacro<Context> | CbMacro<Context>[], ...args: Array<any>): void;
156+
157+
skip: CbSkipInterface<Context>;
158+
}
159+
160+
export interface OnlyInterface<Context = {}> {
161+
(title: string, implementation: Implementation<Context>): void;
162+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
163+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
164+
}
165+
166+
export interface SerialInterface<Context = {}> {
167+
(title: string, implementation: Implementation<Context>): void;
168+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
169+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
170+
171+
cb: CbInterface<Context>;
172+
failing: FailingInterface<Context>;
173+
only: OnlyInterface<Context>;
174+
skip: SkipInterface<Context>;
175+
todo: TodoDeclaration;
176+
}
177+
178+
export interface SkipInterface<Context = {}> {
179+
(title: string, implementation: Implementation<Context>): void;
180+
(title: string, macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
181+
(macro: Macro<Context> | Macro<Context>[], ...args: Array<any>): void;
182+
}
183+
184+
export type TodoDeclaration = (title: string) => void;
185+
186+
declare const test: TestInterface;
187+
export default test;
188+
189+
export {test};
190+
export const after: AfterInterface<null>;
191+
export const afterEach: AfterInterface;
192+
export const before: BeforeInterface<null>;
193+
export const beforeEach: BeforeInterface;
194+
export const cb: CbInterface;
195+
export const failing: FailingInterface;
196+
export const only: OnlyInterface;
197+
export const serial: SerialInterface;
198+
export const skip: SkipInterface;
199+
export const todo: TodoDeclaration;

package-lock.json

Lines changed: 3 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)