diff --git a/docs/recipes/typescript.md b/docs/recipes/typescript.md index 29a1a283f..b6b20e83e 100644 --- a/docs/recipes/typescript.md +++ b/docs/recipes/typescript.md @@ -66,7 +66,7 @@ import test, {Macro} from 'ava'; const macro: Macro = (t, input: string, expected: number) => { t.is(eval(input), expected); }; -macro.title = (providedTitle: string, input: string, expected: number) => `${providedTitle} ${input} = ${expected}`.trim(); +macro.title = (providedTitle = '', input: string, expected: number) => `${providedTitle} ${input} = ${expected}`.trim(); test(macro, '2 + 2', 4); test(macro, '2 * 3', 6); diff --git a/index.d.ts b/index.d.ts index 97f2567a5..f6032f1e2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -390,13 +390,13 @@ export interface Macro { * Implement this function to generate a test (or hook) title whenever this macro is used. `providedTitle` contains * the title provided when the test or hook was declared. Also receives the remaining test arguments. */ - title?: (providedTitle: string, ...args: Array) => string; + title?: (providedTitle: string | undefined, ...args: Array) => string; } /** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */ export interface CbMacro { (t: CbExecutionContext, ...args: Array): ImplementationResult; - title?: (providedTitle: string, ...args: Array) => string; + title?: (providedTitle: string | undefined, ...args: Array) => string; } export interface TestInterface { diff --git a/index.js.flow b/index.js.flow index 479e5d022..22f0eaadf 100644 --- a/index.js.flow +++ b/index.js.flow @@ -403,13 +403,13 @@ export interface Macro { * Implement this function to generate a test (or hook) title whenever this macro is used. `providedTitle` contains * the title provided when the test or hook was declared. Also receives the remaining test arguments. */ - title?: (providedTitle: string, ...args: Array) => string; + title?: (providedTitle: string | void, ...args: Array) => string; } /** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */ export interface CbMacro { (t: CbExecutionContext, ...args: Array): ImplementationResult; - title?: (providedTitle: string, ...args: Array) => string; + title?: (providedTitle: string | void, ...args: Array) => string; } export interface TestInterface { diff --git a/lib/runner.js b/lib/runner.js index 67486b04f..73bbc1b50 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -55,7 +55,7 @@ class Runner extends Emittery { const specifiedTitle = typeof args[0] === 'string' ? args.shift() : - ''; + undefined; const implementations = Array.isArray(args[0]) ? args.shift() : args.splice(0, 1); @@ -65,7 +65,7 @@ class Runner extends Emittery { throw new TypeError('`todo` tests are not allowed to have an implementation. Use `test.skip()` for tests with an implementation.'); } - if (specifiedTitle === '') { + if (specifiedTitle === undefined || specifiedTitle === '') { throw new TypeError('`todo` tests require a title'); } @@ -97,14 +97,14 @@ class Runner extends Emittery { for (const implementation of implementations) { let title = implementation.title ? - implementation.title.apply(implementation, [specifiedTitle].concat(args)) : + implementation.title(specifiedTitle, ...args) : specifiedTitle; - if (typeof title !== 'string') { + if (title !== undefined && typeof title !== 'string') { throw new TypeError('Test & hook titles must be strings'); } - if (title === '') { + if (title === undefined || title === '') { if (metadata.type === 'test') { throw new TypeError('Tests must have a title'); } else if (metadata.always) { diff --git a/readme.md b/readme.md index d7eaa48e3..84ec6dc01 100644 --- a/readme.md +++ b/readme.md @@ -691,14 +691,14 @@ function macro(t, input, expected) { t.is(eval(input), expected); } -macro.title = (providedTitle, input, expected) => `${providedTitle} ${input} = ${expected}`.trim(); +macro.title = (providedTitle = '', input, expected) => `${providedTitle} ${input} = ${expected}`.trim(); test(macro, '2 + 2', 4); test(macro, '2 * 3', 6); test('providedTitle', macro, '3 * 3', 9); ``` -The `providedTitle` argument defaults to an empty string if the user does not supply a string title. This allows for easy concatenation without having to worry about `null` / `undefined`. It is worth remembering that the empty string is considered a falsy value, so you can still use `if(providedTitle) {...}`. +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: diff --git a/test/runner.js b/test/runner.js index 6ec4e4b3a..1fbed8e8d 100644 --- a/test/runner.js +++ b/test/runner.js @@ -611,7 +611,7 @@ test('macros: Customize test names attaching a `title` function', t => { avaT.pass(); } - macroFn.title = (title, firstArg) => (title || 'default') + firstArg; + macroFn.title = (title = 'default', firstArg) => title + firstArg; return promiseEnd(new Runner(), runner => { runner.on('stateChange', evt => {