diff --git a/README.md b/README.md
index a6baba0..9d0517c 100644
--- a/README.md
+++ b/README.md
@@ -86,6 +86,37 @@ it.each([10, 20, 30])('checking item %K', (x) => { ... })
// "checking item 3"
```
+### Title function
+
+You can form the test title yourself using a function. The function will get the item, the index, and all items and should return a string with the test title.
+
+```js
+function makeTestTitle(s, k, strings) {
+ return `test ${k + 1} for "${s}"`
+}
+it.each(['first', 'second'])(makeTestTitle, () => ...)
+// creates the tests
+// 'test 1 for "first"'
+// 'test 2 for "second"'
+```
+
+It is very useful for forming a test title based on a property of an object, like
+
+```js
+it.each([
+ { name: 'Joe', age: 30 },
+ { name: 'Mary', age: 20 },
+])(
+ (person) => `tests person ${person.name}`,
+ (person) => { ... }
+})
+// creates the tests
+// "tests person Joe"
+// "tests person Mary"
+```
+
+See [cypress/integration/title-function.js](./cypress/integration/ title-function.js) for more examples
+
## Examples
- Watch [Using cypress-each To Create Separate Tests](https://youtu.be/utPKRV_fL1E)
diff --git a/cypress/integration/each-k.js b/cypress/integration/each-k.js
index ff31017..be96c1e 100644
--- a/cypress/integration/each-k.js
+++ b/cypress/integration/each-k.js
@@ -3,18 +3,20 @@
import '../..'
-it('has test title', function () {
- expect(this, 'has test').to.have.property('test')
- // @ts-ignore
- expect(this.test.title).to.equal('has test title')
-})
+describe('Using %k and %K placeholders', () => {
+ it('has test title', function () {
+ expect(this, 'has test').to.have.property('test')
+ // @ts-ignore
+ expect(this.test.title).to.equal('has test title')
+ })
-it.each([1, 2, 3])('has 0-based index %k', function (x) {
- expect(x).to.be.oneOf([1, 2, 3])
- // needs test context to be passed correctly by it.each
- // expect(this.test.title).to.equal('has 0-based index 0')
-})
+ it.each([1, 2, 3])('has 0-based index %k', function (x) {
+ expect(x).to.be.oneOf([1, 2, 3])
+ // needs test context to be passed correctly by it.each
+ // expect(this.test.title).to.equal('has 0-based index 0')
+ })
-it.each([1, 2, 3])('has 1-based index %K', (x) => {
- expect(x).to.be.oneOf([1, 2, 3])
+ it.each([1, 2, 3])('has 1-based index %K', (x) => {
+ expect(x).to.be.oneOf([1, 2, 3])
+ })
})
diff --git a/cypress/integration/title-function.js b/cypress/integration/title-function.js
new file mode 100644
index 0000000..46e05c8
--- /dev/null
+++ b/cypress/integration/title-function.js
@@ -0,0 +1,42 @@
+// @ts-check
+///
+
+import '../../src'
+
+/**
+ * Forms the test title from the item and index
+ * @param {string} s Individual string
+ * @param {number} k Item index (0-based)
+ * @param {string[]} strings All strings
+ * @returns
+ */
+function makeTestTitle(s, k, strings) {
+ expect(s, 'first argument is the item').to.equal('foo')
+ expect(k, 'second argument is the index').to.equal(0)
+ expect(strings, 'all items').to.deep.equal(['foo'])
+
+ return `test ${k + 1} for "${s}"`
+}
+
+describe('Form test title using a function', () => {
+ it.each(['foo'])(makeTestTitle, function (s) {
+ expect(s, 'item value').to.equal('foo')
+ // @ts-ignore
+ expect(this.test.title, 'computed test title').to.equal('test 1 for "foo"')
+ })
+
+ it.each([
+ { name: 'Joe', age: 30 },
+ { name: 'Mary', age: 20 },
+ ])(
+ (person) => `tests person ${person.name}`,
+ function (user) {
+ expect(user).to.have.keys('name', 'age')
+ // @ts-ignore
+ expect(this.test.title, 'computed test title').to.be.oneOf([
+ 'tests person Joe',
+ 'tests person Mary',
+ ])
+ },
+ )
+})
diff --git a/src/index.d.ts b/src/index.d.ts
index 6ced071..05fca64 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -1,6 +1,9 @@
// types for it.each and describe.each
// any help improving them is welcome
// https://github.com/bahmutov/cypress-each
+
+type TestTitleFn = (item: T, index: number, items: T[]) => string
+
declare namespace Mocha {
type TestCallback = (this: Context, arg0: T, arg1: any, arg2: any) => void
@@ -8,13 +11,13 @@ declare namespace Mocha {
// definition for it.each
each(
values: T[],
- ): (titlePattern: string, fn: TestCallback) => void
+ ): (titlePattern: string | TestTitleFn, fn: TestCallback) => void
}
interface SuiteFunction {
// definition for describe.each
each(
values: T[],
- ): (titlePattern: string, fn: TestCallback) => void
+ ): (titlePattern: string | TestTitleFn, fn: TestCallback) => void
}
}
diff --git a/src/index.js b/src/index.js
index cac9fd0..0024eb5 100644
--- a/src/index.js
+++ b/src/index.js
@@ -11,20 +11,41 @@ function formatTitle(pattern, ...values) {
return format.apply(null, [pattern].concat(values.slice(0, count)))
}
+function makeTitle(titlePattern, value, k, values) {
+ if (typeof titlePattern === 'string') {
+ const testTitle = titlePattern.replace('%k', k).replace('%K', k + 1)
+ if (Array.isArray(value)) {
+ return formatTitle(testTitle, ...value)
+ } else {
+ return formatTitle(testTitle, value)
+ }
+ } else if (typeof titlePattern === 'function') {
+ return titlePattern(value, k, values)
+ }
+
+ throw new Error('titlePattern must be a string or function')
+}
+
if (!it.each) {
it.each = function (values) {
return function (titlePattern, testCallback) {
values.forEach(function (value, k) {
- const testTitle = titlePattern.replace('%k', k).replace('%K', k + 1)
+ // const testTitle = titlePattern.replace('%k', k).replace('%K', k + 1)
+ const title = makeTitle(titlePattern, value, k, values)
+ if (!title) {
+ throw new Error(
+ `Could not compute the test title ${k} for value ${value}`,
+ )
+ }
// define a test for each value
if (Array.isArray(value)) {
- const title = formatTitle(testTitle, ...value)
+ // const title = formatTitle(testTitle, ...value)
it(title, function itArrayCallback() {
return testCallback.apply(this, value)
})
} else {
- const title = formatTitle(testTitle, value)
+ // const title = formatTitle(testTitle, value)
it(title, function itCallback() {
return testCallback.call(this, value)
})
@@ -39,13 +60,20 @@ if (!describe.each) {
return function describeEach(titlePattern, testCallback) {
// define a test for each value
values.forEach((value, k) => {
- const testTitle = titlePattern.replace('%k', k).replace('%K', k + 1)
+ // const testTitle = titlePattern.replace('%k', k).replace('%K', k + 1)
+ const title = makeTitle(titlePattern, value, k, values)
+
+ if (!title) {
+ throw new Error(
+ `Could not compute the suite title ${k} for value ${value}`,
+ )
+ }
if (Array.isArray(value)) {
- const title = formatTitle(testTitle, ...value)
+ // const title = formatTitle(testTitle, ...value)
describe(title, testCallback.bind(null, ...value))
} else {
- const title = formatTitle(testTitle, value)
+ // const title = formatTitle(testTitle, value)
describe(title, testCallback.bind(null, value))
}
})