diff --git a/.xo-config.json b/.xo-config.json index 0eb05e8e1..1a7cd8568 100644 --- a/.xo-config.json +++ b/.xo-config.json @@ -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" } }, { diff --git a/docs/recipes/typescript.md b/docs/recipes/typescript.md index d005e5075..84cc1db50 100644 --- a/docs/recipes/typescript.md +++ b/docs/recipes/typescript.md @@ -186,10 +186,11 @@ 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` 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` (in AVA 4 this is `TestFn`) 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}>; @@ -213,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 diff --git a/index.d.ts b/index.d.ts index 608d043c6..24b722040 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,638 +1,12 @@ -export interface Subscribable { - subscribe(observer: { - error(error: any): void; - complete(): void; - }): void; -} +import type {TestFn} from './types/test-fn'; -export type ErrorConstructor = new (...args: any[]) => Error; - -/** Specify one or more expectations the thrown error must satisfy. */ -export type ThrowsExpectation = { - /** The thrown error must have a code that equals the given string or number. */ - code?: string | number; - - /** The thrown error must be an instance of this constructor. */ - instanceOf?: ErrorConstructor; - - /** The thrown error must be strictly equal to this value. */ - is?: Error; - - /** The thrown error must have a message that equals the given string, or matches the regular expression. */ - message?: string | RegExp; - - /** The thrown error must have a name that equals the given string. */ - name?: string; -}; - -export type CommitDiscardOptions = { - /** - * Whether the logs should be included in those of the parent test. - */ - retainLogs?: boolean; -}; - -export interface Assertions { - /** - * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean - * indicating whether the assertion passed. Comes with power-assert. - */ - assert: AssertAssertion; - - /** - * Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to - * `expected`, returning a boolean indicating whether the assertion passed. - */ - deepEqual: DeepEqualAssertion; - - /** - * Assert that `value` is like `selector`, returning a boolean indicating whether the assertion passed. - */ - like: LikeAssertion; - - /** Fail the test, always returning `false`. */ - fail: FailAssertion; - - /** - * Assert that `actual` is strictly false, returning a boolean indicating whether the assertion passed. - */ - false: FalseAssertion; - - /** - * Assert that `actual` is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy), returning a boolean - * indicating whether the assertion passed. - */ - falsy: FalsyAssertion; - - /** - * Assert that `actual` is [the same - * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, - * returning a boolean indicating whether the assertion passed. - */ - is: IsAssertion; - - /** - * Assert that `actual` is not [the same - * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, - * returning a boolean indicating whether the assertion passed. - */ - not: NotAssertion; - - /** - * Assert that `actual` is not [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to - * `expected`, returning a boolean indicating whether the assertion passed. - */ - notDeepEqual: NotDeepEqualAssertion; - - /** - * Assert that `string` does not match the regular expression, returning a boolean indicating whether the assertion - * passed. - */ - notRegex: NotRegexAssertion; - - /** Assert that the function does not throw. */ - notThrows: NotThrowsAssertion; - - /** Assert that the async function does not throw, or that the promise does not reject. Must be awaited. */ - notThrowsAsync: NotThrowsAsyncAssertion; - - /** Count a passing assertion, always returning `true`. */ - pass: PassAssertion; - - /** - * Assert that `string` matches the regular expression, returning a boolean indicating whether the assertion passed. - */ - regex: RegexAssertion; - - /** - * Assert that `expected` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to a - * previously recorded [snapshot](https://github.com/concordancejs/concordance#serialization-details), or if - * necessary record a new snapshot. - */ - snapshot: SnapshotAssertion; - - /** - * Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value. - */ - throws: ThrowsAssertion; - - /** - * Assert that the async function throws [an error](https://www.npmjs.com/package/is-error), or the promise rejects - * with one. If so, returns a promise for the error value, which must be awaited. - */ - throwsAsync: ThrowsAsyncAssertion; - - /** - * Assert that `actual` is strictly true, returning a boolean indicating whether the assertion passed. - */ - true: TrueAssertion; - - /** - * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean - * indicating whether the assertion passed. - */ - truthy: TruthyAssertion; -} - -export interface AssertAssertion { - /** - * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean - * indicating whether the assertion passed. Comes with power-assert. - */ - (actual: any, message?: string): boolean; - - /** Skip this assertion. */ - skip(actual: any, message?: string): void; -} - -export interface DeepEqualAssertion { - /** - * Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to - * `expected`, returning a boolean indicating whether the assertion passed. - */ - (actual: Actual, expected: Expected, message?: string): actual is Expected; - - /** Skip this assertion. */ - skip(actual: any, expected: any, message?: string): void; -} - -export interface LikeAssertion { - /** - * Assert that `value` is like `selector`, returning a boolean indicating whether the assertion passed. - */ - >(value: any, selector: Expected, message?: string): value is Expected; - - /** Skip this assertion. */ - skip(value: any, selector: any, message?: string): void; -} - -export interface FailAssertion { - /** Fail the test, always returning `false`. */ - (message?: string): boolean; - - /** Skip this assertion. */ - skip(message?: string): void; -} - -export interface FalseAssertion { - /** - * Assert that `actual` is strictly false, returning a boolean indicating whether the assertion passed. - */ - (actual: any, message?: string): actual is false; - - /** Skip this assertion. */ - skip(actual: any, message?: string): void; -} - -export interface FalsyAssertion { - /** - * Assert that `actual` is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy), returning a boolean - * indicating whether the assertion passed. - */ - (actual: any, message?: string): boolean; - - /** Skip this assertion. */ - skip(actual: any, message?: string): void; -} - -export interface IsAssertion { - /** - * Assert that `actual` is [the same - * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, - * returning a boolean indicating whether the assertion passed. - */ - (actual: Actual, expected: Expected, message?: string): actual is Expected; - - /** Skip this assertion. */ - skip(actual: any, expected: any, message?: string): void; -} - -export interface NotAssertion { - /** - * Assert that `actual` is not [the same - * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, - * returning a boolean indicating whether the assertion passed. - */ - (actual: Actual, expected: Expected, message?: string): boolean; - - /** Skip this assertion. */ - skip(actual: any, expected: any, message?: string): void; -} - -export interface NotDeepEqualAssertion { - /** - * Assert that `actual` is not [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to - * `expected`, returning a boolean indicating whether the assertion passed. - */ - (actual: Actual, expected: Expected, message?: string): boolean; - - /** Skip this assertion. */ - skip(actual: any, expected: any, message?: string): void; -} - -export interface NotRegexAssertion { - /** - * Assert that `string` does not match the regular expression, returning a boolean indicating whether the assertion - * passed. - */ - (string: string, regex: RegExp, message?: string): boolean; - - /** Skip this assertion. */ - skip(string: string, regex: RegExp, message?: string): void; -} - -export interface NotThrowsAssertion { - /** Assert that the function does not throw. */ - (fn: () => any, message?: string): void; - - /** Skip this assertion. */ - skip(fn: () => any, message?: string): void; -} - -export interface NotThrowsAsyncAssertion { - /** Assert that the async function does not throw. You must await the result. */ - (fn: () => PromiseLike, message?: string): Promise; - - /** Assert that the promise does not reject. You must await the result. */ - (promise: PromiseLike, message?: string): Promise; - - /** Skip this assertion. */ - skip(nonThrower: any, message?: string): void; -} - -export interface PassAssertion { - /** Count a passing assertion, always returning `true`. */ - (message?: string): boolean; - - /** Skip this assertion. */ - skip(message?: string): void; -} - -export interface RegexAssertion { - /** - * Assert that `string` matches the regular expression, returning a boolean indicating whether the assertion passed. - */ - (string: string, regex: RegExp, message?: string): boolean; - - /** Skip this assertion. */ - skip(string: string, regex: RegExp, message?: string): void; -} - -export interface SnapshotAssertion { - /** - * Assert that `expected` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to a - * previously recorded [snapshot](https://github.com/concordancejs/concordance#serialization-details), or if - * necessary record a new snapshot. - */ - (expected: any, message?: string): void; - - /** Skip this assertion. */ - skip(expected: any, message?: string): void; -} - -export interface ThrowsAssertion { - /** - * 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. - */ - (fn: () => any, expectations?: ThrowsExpectation | null, message?: string): ThrownError; - - /** Skip this assertion. */ - skip(fn: () => any, expectations?: any, message?: string): void; -} - -export interface ThrowsAsyncAssertion { - /** - * Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error - * value. You must await the result. - */ - (fn: () => PromiseLike, expectations?: null, message?: string): Promise; - - /** - * Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error - * value. You must await the result. The error must satisfy all expectations. - */ - (fn: () => PromiseLike, expectations: ThrowsExpectation, message?: string): Promise; - - /** - * Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the - * rejection reason. You must await the result. - */ - (promise: PromiseLike, expectations?: null, message?: string): Promise; - - /** - * Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the - * rejection reason. You must await the result. The error must satisfy all expectations. - */ - (promise: PromiseLike, expectations: ThrowsExpectation, message?: string): Promise; - - /** Skip this assertion. */ - skip(thrower: any, expectations?: any, message?: string): void; -} - -export interface TrueAssertion { - /** - * Assert that `actual` is strictly true, returning a boolean indicating whether the assertion passed. - */ - (actual: any, message?: string): actual is true; - - /** Skip this assertion. */ - skip(actual: any, message?: string): void; -} - -export interface TruthyAssertion { - /** - * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean - * indicating whether the assertion passed. - */ - (actual: any, message?: string): boolean; - - /** Skip this assertion. */ - skip(actual: any, message?: string): void; -} - -/** The `t` value passed to test & hook implementations. */ -export interface ExecutionContext extends Assertions { - /** Test context, shared with hooks. */ - context: Context; - - /** Title of the test or hook. */ - readonly title: string; - - /** Whether the test has passed. Only accurate in afterEach hooks. */ - readonly passed: boolean; - - log: LogFn; - plan: PlanFn; - teardown: TeardownFn; - timeout: TimeoutFn; - try: TryFn; -} - -export interface LogFn { - /** Log one or more values. */ - (...values: any[]): void; - - /** Skip logging. */ - skip(...values: any[]): void; -} - -export interface PlanFn { - /** - * Plan how many assertion there are in the test. The test will fail if the actual assertion count doesn't match the - * number of planned assertions. See [assertion planning](https://github.com/avajs/ava#assertion-planning). - */ - (count: number): void; - - /** Don't plan assertions. */ - skip(count: number): void; -} - -export interface TimeoutFn { - /** - * Set a timeout for the test, in milliseconds. The test will fail if the timeout is exceeded. - * The timeout is reset each time an assertion is made. - */ - (ms: number, message?: string): void; -} - -export interface TeardownFn { - /** Declare a function to be run after the test has ended. */ - (fn: () => void): void; -} - -export type ImplementationFn = - ((t: ExecutionContext, ...args: Args) => PromiseLike) | - ((t: ExecutionContext, ...args: Args) => Subscribable) | - ((t: ExecutionContext, ...args: Args) => void); - -export type TitleFn = (providedTitle: string | undefined, ...args: Args) => string; - -/** A reusable test or hook implementation. */ -export type Macro = { - /** The function that is executed when the macro is used. */ - readonly exec: ImplementationFn; - - /** Generates a test title when this macro is used. */ - readonly title?: TitleFn; -}; - -/** A test or hook implementation. */ -export type Implementation = ImplementationFn | Macro; - -export interface TryFn { - /** - * Attempt to run some assertions. The result must be explicitly committed or discarded or else - * the test will fail. The title may help distinguish attempts from one another. - */ - (title: string, fn: Implementation, ...args: Args): Promise; - - /** - * Attempt to run some assertions. The result must be explicitly committed or discarded or else - * the test will fail. - */ - (fn: Implementation, ...args: Args): Promise; -} - -export interface AssertionError extends Error {} - -export interface TryResult { - /** - * Title of the attempt, helping you tell attempts aparts. - */ - title: string; - - /** - * Indicates whether all assertions passed, or at least one failed. - */ - passed: boolean; - - /** - * Errors raised for each failed assertion. - */ - errors: AssertionError[]; - - /** - * Logs created during the attempt using `t.log()`. Contains formatted values. - */ - logs: string[]; - - /** - * Commit the attempt. Counts as one assertion for the plan count. If the - * attempt failed, calling this will also cause your test to fail. - */ - commit(options?: CommitDiscardOptions): void; - - /** - * Discard the attempt. - */ - discard(options?: CommitDiscardOptions): void; -} - -export interface TestInterface { - /** Declare a concurrent test. Additional arguments are passed along. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** - * Declare a concurrent test that uses a macro. The macro is responsible for generating a unique test title. - * Additional arguments are passed along. - */ - (macro: Macro, ...args: Args): void; - - /** Declare a hook that is run once, after all tests have passed. */ - after: AfterInterface; - - /** Declare a hook that is run after each passing test. */ - afterEach: AfterInterface; - - /** Declare a hook that is run once, before all tests. */ - before: BeforeInterface; - - /** Declare a hook that is run before each test. */ - beforeEach: BeforeInterface; - - /** Declare a test that is expected to fail. */ - failing: FailingInterface; - - /** Declare tests and hooks that are run serially. */ - serial: SerialInterface; - - only: OnlyInterface; - skip: SkipInterface; - todo: TodoDeclaration; - macro: MacroDeclaration; - meta: MetaInterface; -} - -export interface AfterInterface { - /** Declare a hook that is run once, after all tests have passed. Additional arguments are passed along. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** Declare a hook that is run once, after all tests have passed. Additional arguments are passed along. */ - (implementation: Implementation, ...args: Args): void; - - /** Declare a hook that is run once, after all tests are done. */ - always: AlwaysInterface; - - skip: HookSkipInterface; -} - -export interface AlwaysInterface { - /** Declare a hook that is run once, after all tests are done. Additional arguments are passed along. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** Declare a hook that is run once, after all tests are done. Additional arguments are passed along. */ - (implementation: Implementation, ...args: Args): void; - - skip: HookSkipInterface; -} - -export interface BeforeInterface { - /** Declare a hook that is run once, before all tests. Additional arguments are passed along. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** Declare a hook that is run once, before all tests. Additional arguments are passed along. */ - (implementation: Implementation, ...args: Args): void; - - skip: HookSkipInterface; -} - -export interface FailingInterface { - /** Declare a concurrent test. Additional arguments are passed along. The test is expected to fail. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** - * Declare a concurrent test that uses a macro. Additional arguments are passed along. - * The macro is responsible for generating a unique test title. The test is expected to fail. - */ - (macro: Macro, ...args: Args): void; - - only: OnlyInterface; - skip: SkipInterface; -} - -export interface HookSkipInterface { - /** Skip this hook. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** Skip this hook. */ - (implementation: Implementation, ...args: Args): void; -} - -export interface OnlyInterface { - /** Declare a test. Additional arguments are passed along. Only this test and others declared with `.only()` are run. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** - * Declare a test that uses a macro. The macro is responsible for generating a unique test title. - * Only this test and others declared with `.only()` are run. - */ - (macro: Macro, ...args: Args): void; -} - -export interface SerialInterface { - /** Declare a serial test. Additional arguments are passed along. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** - * Declare a serial test that uses a macro. The macro is responsible for generating a unique test title. - */ - (macro: Macro, ...args: Args): void; - - /** Declare a serial hook that is run once, after all tests have passed. */ - after: AfterInterface; - - /** Declare a serial hook that is run after each passing test. */ - afterEach: AfterInterface; - - /** Declare a serial hook that is run once, before all tests. */ - before: BeforeInterface; - - /** Declare a serial hook that is run before each test. */ - beforeEach: BeforeInterface; - - /** Declare a serial test that is expected to fail. */ - failing: FailingInterface; - - only: OnlyInterface; - skip: SkipInterface; - todo: TodoDeclaration; -} - -export interface SkipInterface { - /** Skip this test. */ - (title: string, implementation: Implementation, ...args: Args): void; - - /** Skip this test. */ - (macro: Macro, ...args: Args): void; -} - -export interface TodoDeclaration { - /** Declare a test that should be implemented later. */ - (title: string): void; -} - -export type MacroDeclarationOptions = { - exec: ImplementationFn; - title: TitleFn; -}; - -export interface MacroDeclaration { - /** Declare a reusable test implementation. */ - (exec: ImplementationFn): Macro; - (declaration: MacroDeclarationOptions): Macro; -} - -export interface MetaInterface { - /** Path to the test file being executed. */ - file: string; - - /** Directory where snapshots are stored. */ - snapshotDirectory: string; -} +export * from './types/assertions'; +export * from './types/try-fn'; +export * from './types/test-fn'; +export * from './types/subscribable'; /** Call to declare a test, or chain to declare hooks or test modifiers */ -declare const test: TestInterface; +declare const test: TestFn; /** Call to declare a test, or chain to declare hooks or test modifiers */ export default test; diff --git a/package.json b/package.json index 2b22505d6..446735df7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "files": [ "entrypoints", "lib", - "*.d.ts" + "types", + "index.d.ts", + "plugin.d.ts" ], "keywords": [ "🦄", diff --git a/test-d/context.ts b/test-d/context.ts index e7b89746c..1e1327433 100644 --- a/test-d/context.ts +++ b/test-d/context.ts @@ -1,12 +1,12 @@ import {expectError, expectType} from 'tsd'; -import anyTest, {ExecutionContext, TestInterface} from '..'; +import anyTest, {ExecutionContext, TestFn} from '..'; interface Context { foo: string; } -const test = anyTest as TestInterface; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion +const test = anyTest as TestFn; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion const macro = test.macro((t, expected: number) => { expectType(t.context.foo); @@ -27,6 +27,6 @@ interface Covariant extends Context { bar: number; } -const test2 = anyTest as TestInterface; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion +const test2 = anyTest as TestFn; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion const hook = (t: ExecutionContext) => {}; test2.beforeEach(hook); diff --git a/test-d/implementation-result.ts b/test-d/implementation-result.ts index 1fdf14478..b3117613e 100644 --- a/test-d/implementation-result.ts +++ b/test-d/implementation-result.ts @@ -16,7 +16,7 @@ test('return a subscribable', t => { }; }); -test('return anything else', t => { +test.after('return anything else', t => { return { foo: 'bar', subscribe() {}, diff --git a/types/assertions.d.ts b/types/assertions.d.ts new file mode 100644 index 000000000..df40dd26b --- /dev/null +++ b/types/assertions.d.ts @@ -0,0 +1,338 @@ +export type ErrorConstructor = new (...args: any[]) => Error; + +/** Specify one or more expectations the thrown error must satisfy. */ +export type ThrowsExpectation = { + /** The thrown error must have a code that equals the given string or number. */ + code?: string | number; + + /** The thrown error must be an instance of this constructor. */ + instanceOf?: ErrorConstructor; + + /** The thrown error must be strictly equal to this value. */ + is?: Error; + + /** The thrown error must have a message that equals the given string, or matches the regular expression. */ + message?: string | RegExp; + + /** The thrown error must have a name that equals the given string. */ + name?: string; +}; + +export interface Assertions { + /** + * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean + * indicating whether the assertion passed. Comes with power-assert. + */ + assert: AssertAssertion; + + /** + * Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to + * `expected`, returning a boolean indicating whether the assertion passed. + */ + deepEqual: DeepEqualAssertion; + + /** + * Assert that `value` is like `selector`, returning a boolean indicating whether the assertion passed. + */ + like: LikeAssertion; + + /** Fail the test, always returning `false`. */ + fail: FailAssertion; + + /** + * Assert that `actual` is strictly false, returning a boolean indicating whether the assertion passed. + */ + false: FalseAssertion; + + /** + * Assert that `actual` is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy), returning a boolean + * indicating whether the assertion passed. + */ + falsy: FalsyAssertion; + + /** + * Assert that `actual` is [the same + * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, + * returning a boolean indicating whether the assertion passed. + */ + is: IsAssertion; + + /** + * Assert that `actual` is not [the same + * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, + * returning a boolean indicating whether the assertion passed. + */ + not: NotAssertion; + + /** + * Assert that `actual` is not [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to + * `expected`, returning a boolean indicating whether the assertion passed. + */ + notDeepEqual: NotDeepEqualAssertion; + + /** + * Assert that `string` does not match the regular expression, returning a boolean indicating whether the assertion + * passed. + */ + notRegex: NotRegexAssertion; + + /** Assert that the function does not throw. */ + notThrows: NotThrowsAssertion; + + /** Assert that the async function does not throw, or that the promise does not reject. Must be awaited. */ + notThrowsAsync: NotThrowsAsyncAssertion; + + /** Count a passing assertion, always returning `true`. */ + pass: PassAssertion; + + /** + * Assert that `string` matches the regular expression, returning a boolean indicating whether the assertion passed. + */ + regex: RegexAssertion; + + /** + * Assert that `expected` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to a + * previously recorded [snapshot](https://github.com/concordancejs/concordance#serialization-details), or if + * necessary record a new snapshot. + */ + snapshot: SnapshotAssertion; + + /** + * Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value. + */ + throws: ThrowsAssertion; + + /** + * Assert that the async function throws [an error](https://www.npmjs.com/package/is-error), or the promise rejects + * with one. If so, returns a promise for the error value, which must be awaited. + */ + throwsAsync: ThrowsAsyncAssertion; + + /** + * Assert that `actual` is strictly true, returning a boolean indicating whether the assertion passed. + */ + true: TrueAssertion; + + /** + * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean + * indicating whether the assertion passed. + */ + truthy: TruthyAssertion; +} + +export interface AssertAssertion { + /** + * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean + * indicating whether the assertion passed. Comes with power-assert. + */ + (actual: any, message?: string): boolean; + + /** Skip this assertion. */ + skip(actual: any, message?: string): void; +} + +export interface DeepEqualAssertion { + /** + * Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to + * `expected`, returning a boolean indicating whether the assertion passed. + */ + (actual: Actual, expected: Expected, message?: string): actual is Expected; + + /** Skip this assertion. */ + skip(actual: any, expected: any, message?: string): void; +} + +export interface LikeAssertion { + /** + * Assert that `value` is like `selector`, returning a boolean indicating whether the assertion passed. + */ + >(value: any, selector: Expected, message?: string): value is Expected; + + /** Skip this assertion. */ + skip(value: any, selector: any, message?: string): void; +} + +export interface FailAssertion { + /** Fail the test, always returning `false`. */ + (message?: string): boolean; + + /** Skip this assertion. */ + skip(message?: string): void; +} + +export interface FalseAssertion { + /** + * Assert that `actual` is strictly false, returning a boolean indicating whether the assertion passed. + */ + (actual: any, message?: string): actual is false; + + /** Skip this assertion. */ + skip(actual: any, message?: string): void; +} + +export interface FalsyAssertion { + /** + * Assert that `actual` is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy), returning a boolean + * indicating whether the assertion passed. + */ + (actual: any, message?: string): boolean; + + /** Skip this assertion. */ + skip(actual: any, message?: string): void; +} + +export interface IsAssertion { + /** + * Assert that `actual` is [the same + * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, + * returning a boolean indicating whether the assertion passed. + */ + (actual: Actual, expected: Expected, message?: string): actual is Expected; + + /** Skip this assertion. */ + skip(actual: any, expected: any, message?: string): void; +} + +export interface NotAssertion { + /** + * Assert that `actual` is not [the same + * value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) as `expected`, + * returning a boolean indicating whether the assertion passed. + */ + (actual: Actual, expected: Expected, message?: string): boolean; + + /** Skip this assertion. */ + skip(actual: any, expected: any, message?: string): void; +} + +export interface NotDeepEqualAssertion { + /** + * Assert that `actual` is not [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to + * `expected`, returning a boolean indicating whether the assertion passed. + */ + (actual: Actual, expected: Expected, message?: string): boolean; + + /** Skip this assertion. */ + skip(actual: any, expected: any, message?: string): void; +} + +export interface NotRegexAssertion { + /** + * Assert that `string` does not match the regular expression, returning a boolean indicating whether the assertion + * passed. + */ + (string: string, regex: RegExp, message?: string): boolean; + + /** Skip this assertion. */ + skip(string: string, regex: RegExp, message?: string): void; +} + +export interface NotThrowsAssertion { + /** Assert that the function does not throw. */ + (fn: () => any, message?: string): void; + + /** Skip this assertion. */ + skip(fn: () => any, message?: string): void; +} + +export interface NotThrowsAsyncAssertion { + /** Assert that the async function does not throw. You must await the result. */ + (fn: () => PromiseLike, message?: string): Promise; + + /** Assert that the promise does not reject. You must await the result. */ + (promise: PromiseLike, message?: string): Promise; // eslint-disable-line @typescript-eslint/unified-signatures + + /** Skip this assertion. */ + skip(nonThrower: any, message?: string): void; +} + +export interface PassAssertion { + /** Count a passing assertion, always returning `true`. */ + (message?: string): boolean; + + /** Skip this assertion. */ + skip(message?: string): void; +} + +export interface RegexAssertion { + /** + * Assert that `string` matches the regular expression, returning a boolean indicating whether the assertion passed. + */ + (string: string, regex: RegExp, message?: string): boolean; + + /** Skip this assertion. */ + skip(string: string, regex: RegExp, message?: string): void; +} + +export interface SnapshotAssertion { + /** + * Assert that `expected` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to a + * previously recorded [snapshot](https://github.com/concordancejs/concordance#serialization-details), or if + * necessary record a new snapshot. + */ + (expected: any, message?: string): void; + + /** Skip this assertion. */ + skip(expected: any, message?: string): void; +} + +export interface ThrowsAssertion { + /** + * 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. + */ + (fn: () => any, expectations?: ThrowsExpectation | null, message?: string): ThrownError; + + /** Skip this assertion. */ + skip(fn: () => any, expectations?: any, message?: string): void; +} + +export interface ThrowsAsyncAssertion { + /** + * Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error + * value. You must await the result. + */ + (fn: () => PromiseLike, expectations?: null, message?: string): Promise; + + /** + * Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error + * value. You must await the result. The error must satisfy all expectations. + */ + (fn: () => PromiseLike, expectations: ThrowsExpectation, message?: string): Promise; + + /** + * Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the + * rejection reason. You must await the result. + */ + (promise: PromiseLike, expectations?: null, message?: string): Promise; // eslint-disable-line @typescript-eslint/unified-signatures + + /** + * Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the + * rejection reason. You must await the result. The error must satisfy all expectations. + */ + (promise: PromiseLike, expectations: ThrowsExpectation, message?: string): Promise; // eslint-disable-line @typescript-eslint/unified-signatures + + /** Skip this assertion. */ + skip(thrower: any, expectations?: any, message?: string): void; +} + +export interface TrueAssertion { + /** + * Assert that `actual` is strictly true, returning a boolean indicating whether the assertion passed. + */ + (actual: any, message?: string): actual is true; + + /** Skip this assertion. */ + skip(actual: any, message?: string): void; +} + +export interface TruthyAssertion { + /** + * Assert that `actual` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), returning a boolean + * indicating whether the assertion passed. + */ + (actual: any, message?: string): boolean; + + /** Skip this assertion. */ + skip(actual: any, message?: string): void; +} diff --git a/types/subscribable.ts b/types/subscribable.ts new file mode 100644 index 000000000..3a3399bca --- /dev/null +++ b/types/subscribable.ts @@ -0,0 +1,6 @@ +export interface Subscribable { + subscribe(observer: { + error(error: any): void; + complete(): void; + }): void; +} diff --git a/types/test-fn.d.ts b/types/test-fn.d.ts new file mode 100644 index 000000000..a876666df --- /dev/null +++ b/types/test-fn.d.ts @@ -0,0 +1,232 @@ +import type {Assertions} from './assertions'; +import type {Subscribable} from './subscribable'; +import type {TryFn} from './try-fn'; + +/** The `t` value passed to test & hook implementations. */ +export interface ExecutionContext extends Assertions { + /** Test context, shared with hooks. */ + context: Context; + + /** Title of the test or hook. */ + readonly title: string; + + /** Whether the test has passed. Only accurate in afterEach hooks. */ + readonly passed: boolean; + + readonly log: LogFn; + readonly plan: PlanFn; + readonly teardown: TeardownFn; + readonly timeout: TimeoutFn; + readonly try: TryFn; +} + +export interface LogFn { + /** Log one or more values. */ + (...values: any[]): void; + + /** Skip logging. */ + skip(...values: any[]): void; +} + +export interface PlanFn { + /** + * Plan how many assertion there are in the test. The test will fail if the actual assertion count doesn't match the + * number of planned assertions. See [assertion planning](https://github.com/avajs/ava#assertion-planning). + */ + (count: number): void; + + /** Don't plan assertions. */ + skip(count: number): void; +} + +/** + * Set a timeout for the test, in milliseconds. The test will fail if the timeout is exceeded. + * The timeout is reset each time an assertion is made. + */ +export type TimeoutFn = (ms: number, message?: string) => void; + +/** Declare a function to be run after the test has ended. */ +export type TeardownFn = (fn: () => void) => void; + +export type ImplementationFn = + ((t: ExecutionContext, ...args: Args) => PromiseLike) | + ((t: ExecutionContext, ...args: Args) => Subscribable) | + ((t: ExecutionContext, ...args: Args) => void); + +export type TitleFn = (providedTitle: string | undefined, ...args: Args) => string; + +/** A reusable test or hook implementation. */ +export type Macro = { + /** The function that is executed when the macro is used. */ + readonly exec: ImplementationFn; + + /** Generates a test title when this macro is used. */ + readonly title?: TitleFn; +}; + +/** A test or hook implementation. */ +export type Implementation = ImplementationFn | Macro; + +export interface TestFn { + after: AfterFn; + afterEach: AfterFn; + before: BeforeFn; + beforeEach: BeforeFn; + failing: FailingFn; + macro: MacroFn; + meta: Meta; + only: OnlyFn; + serial: SerialFn; + skip: SkipFn; + todo: TodoFn; + + /** Declare a concurrent test. Additional arguments are passed to the implementation or macro. */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a concurrent test that uses a macro. Additional arguments are passed to the macro. + * The macro is responsible for generating a unique test title. + */ + (macro: Macro, ...args: Args): void; +} + +export interface AfterFn { + always: AlwaysInterface; + skip: HookSkipFn; + + /** + * Declare a hook that is run once, after all tests have passed. + * Additional arguments are passed to the implementation or macro. + */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a hook that is run once, after all tests have passed. + * Additional arguments are passed to the implementation or macro. + */ + (implementation: Implementation, ...args: Args): void; + +} + +export interface AlwaysInterface { + skip: HookSkipFn; + + /** + * Declare a hook that is run once, after all tests are done. + * Additional arguments are passed to the implementation or macro. + */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a hook that is run once, after all tests are done. + * Additional arguments are passed to the implementation or macro. + */ + (implementation: Implementation, ...args: Args): void; +} + +export interface BeforeFn { + skip: HookSkipFn; + + /** + * Declare a hook that is run once, before all tests. + * Additional arguments are passed to the implementation or macro. + */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a hook that is run once, before all tests. + * Additional arguments are passed to the implementation or macro. + */ + (implementation: Implementation, ...args: Args): void; +} + +export interface FailingFn { + only: OnlyFn; + skip: SkipFn; + + /** + * Declare a concurrent test that is expected to fail. + * Additional arguments are passed to the implementation or macro. + */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a concurrent test, using a macro, that is expected to fail. + * Additional arguments are passed to the macro. The macro is responsible for generating a unique test title. + */ + (macro: Macro, ...args: Args): void; +} + +export interface HookSkipFn { + /** Skip this hook. */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** Skip this hook. */ + (implementation: Implementation, ...args: Args): void; +} + +export interface OnlyFn { + /** + * Declare a test. Only this test and others declared with `.only()` are run. + * Additional arguments are passed to the implementation or macro. + */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a test that uses a macro. Only this test and others declared with `.only()` are run. + * Additional arguments are passed to the macro. The macro is responsible for generating a unique test title. + */ + (macro: Macro, ...args: Args): void; +} + +export interface SerialFn { + after: AfterFn; + afterEach: AfterFn; + before: BeforeFn; + beforeEach: BeforeFn; + failing: FailingFn; + only: OnlyFn; + skip: SkipFn; + todo: TodoFn; + + /** Declare a serial test. Additional arguments are passed to the implementation or macro. */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** + * Declare a serial test that uses a macro. The macro is responsible for generating a unique test title. + */ + (macro: Macro, ...args: Args): void; +} + +export interface SkipFn { + /** Skip this test. */ + (title: string, implementation: Implementation, ...args: Args): void; + + /** Skip this test. */ + (macro: Macro, ...args: Args): void; +} + +/** Declare a test that should be implemented later. */ +export type TodoFn = (title: string) => void; + +export type MacroDeclarationOptions = { + /** The function that is executed when the macro is used. */ + exec: ImplementationFn; + + /** The function responsible for generating a unique title when the macro is used. */ + title: TitleFn; +}; + +export interface MacroFn { + /** Declare a reusable test implementation. */ + (/** The function that is executed when the macro is used. */ exec: ImplementationFn): Macro; + (declaration: MacroDeclarationOptions): Macro; // eslint-disable-line @typescript-eslint/unified-signatures +} + +export interface Meta { + /** Path to the test file being executed. */ + file: string; + + /** Directory where snapshots are stored. */ + snapshotDirectory: string; +} diff --git a/types/try-fn.d.ts b/types/try-fn.d.ts new file mode 100644 index 000000000..41a1664d5 --- /dev/null +++ b/types/try-fn.d.ts @@ -0,0 +1,58 @@ +import type {Implementation} from './test-fn'; + +export type CommitDiscardOptions = { + /** + * Whether the logs should be included in those of the parent test. + */ + retainLogs?: boolean; +}; + +export interface AssertionError extends Error {} + +export interface TryResult { + /** + * Title of the attempt, helping you tell attempts aparts. + */ + title: string; + + /** + * Indicates whether all assertions passed, or at least one failed. + */ + passed: boolean; + + /** + * Errors raised for each failed assertion. + */ + errors: AssertionError[]; + + /** + * Logs created during the attempt using `t.log()`. Contains formatted values. + */ + logs: string[]; + + /** + * Commit the attempt. Counts as one assertion for the plan count. If the + * attempt failed, calling this will also cause your test to fail. + */ + commit(options?: CommitDiscardOptions): void; + + /** + * Discard the attempt. + */ + discard(options?: CommitDiscardOptions): void; +} + +export interface TryFn { + /** + * Attempt to run some assertions. The result must be explicitly committed or discarded or else + * the test will fail. The title may help distinguish attempts from one another. + */ + (title: string, fn: Implementation, ...args: Args): Promise; + + /** + * Attempt to run some assertions. The result must be explicitly committed or discarded or else + * the test will fail. + */ + (fn: Implementation, ...args: Args): Promise; +} +