Permalink
Browse files

Pass an undefined title argument to macro-title functions

Previously, AVA would pass the empty string. By passing an undefined value, the title function can use argument defaults which is more natural.

This is a breaking change for users who were concatenating `title`, under the assumption it was an empty string.
  • Loading branch information...
novemberborn committed Oct 28, 2018
1 parent 37390e6 commit aa35f154e7cd3685f705dd07419f55af7b7440e5
Showing with 13 additions and 13 deletions.
  1. +1 −1 docs/recipes/typescript.md
  2. +2 −2 index.d.ts
  3. +2 −2 index.js.flow
  4. +5 −5 lib/runner.js
  5. +2 −2 readme.md
  6. +1 −1 test/runner.js
@@ -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);
View
@@ -390,13 +390,13 @@ export interface Macro<Context = {}> {
* 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<any>) => string;
title?: (providedTitle: string | undefined, ...args: Array<any>) => string;
}
/** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */
export interface CbMacro<Context = {}> {
(t: CbExecutionContext<Context>, ...args: Array<any>): ImplementationResult;
title?: (providedTitle: string, ...args: Array<any>) => string;
title?: (providedTitle: string | undefined, ...args: Array<any>) => string;
}
export interface TestInterface<Context = {}> {
View
@@ -403,13 +403,13 @@ export interface Macro<Context = {}> {
* 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<any>) => string;
title?: (providedTitle: string | void, ...args: Array<any>) => string;
}
/** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */
export interface CbMacro<Context = {}> {
(t: CbExecutionContext<Context>, ...args: Array<any>): ImplementationResult;
title?: (providedTitle: string, ...args: Array<any>) => string;
title?: (providedTitle: string | void, ...args: Array<any>) => string;
}
export interface TestInterface<Context = {}> {
View
@@ -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) {
View
@@ -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:
View
@@ -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 => {

0 comments on commit aa35f15

Please sign in to comment.