### Example of Suite Callback Context Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md An example demonstrating the usage of `describe.each` with a closure variable for accessing data. ```javascript describe.each([{ id: 1 }])('suite', (person) => { // person is available here via closure it('test', () => { expect(person.id).to.equal(1) }) }) ``` -------------------------------- ### Installation Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/README.md Install the cypress-each plugin using npm. ```bash npm install --save-dev cypress-each ``` -------------------------------- ### Install cypress-each Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Install the cypress-each package using NPM or Yarn. ```bash # install using NPM $ npm i -D cypress-each # install using Yarn # yarn add -D cypress-each ``` -------------------------------- ### String Values Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Pass an array where each element becomes a test input. This example uses string values. ```javascript it.each(['red', 'green', 'blue'])('color %s', (color) => { cy.get(`[data-color="${color}"]`).should('be.visible') }) ``` -------------------------------- ### Simple Values Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Pass an array where each element becomes a test input. This example uses simple numbers. ```javascript it.each([1, 2, 3])('test %d', (value) => { expect(value).to.be.a('number') }) ``` -------------------------------- ### Positional Placeholders Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Example using positional placeholders (`%0`, `%1`, etc.) to reference specific array elements when unpacking. ```javascript const pairs = [ ['Alice', 'Bob'], ['Charlie', 'David'], ] it.each(pairs)('%0 paired with %1', (first, second) => { expect(first).to.be.a('string') expect(second).to.be.a('string') }) ``` -------------------------------- ### With Iteration Index Example Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/it-each.md Example demonstrating how to access the zero-based iteration index as the second parameter in the test callback. ```javascript const values = [10, 20, 30] it.each(values)('checking item %k', (value, k) => { expect(value).to.equal([10, 20, 30][k]) }) ``` -------------------------------- ### Three Values per Item Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Example of unpacking three values from an array into function arguments. ```javascript it.each([ [2, 3, 5], // 2 + 3 = 5 [10, 20, 30], // 10 + 20 = 30 [-5, 5, 0], // -5 + 5 = 0 ])('add %d + %d = %d', (a, b, expected) => { expect(a + b).to.equal(expected) }) ``` -------------------------------- ### Testing Product Catalog Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Tests product listing and filtering by category using `describe.each`. ```javascript const categories = ['electronics', 'books', 'clothing'] describe.each(categories)('product category: %s', (category) => { beforeEach(() => { cy.visit(`/products?category=${category}`) }) it('displays products', () => { cy.get('[data-testid="product-item"]') .should('have.length.greaterThan', 0) }) it('shows category name', () => { cy.get('h1').should('contain', category) }) it('allows adding to cart', () => { cy.get('[data-testid="product-item"]').first().within(() => { cy.get('[data-testid="add-to-cart"]').click() }) cy.get('[data-testid="cart-badge"]').should('contain', '1') }) }) ``` -------------------------------- ### getChunk Example with uneven distribution Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md An example demonstrating how getChunk distributes items into chunks when the array length is not perfectly divisible by the number of chunks. ```javascript items = [1,2,3,4,5,6,7,8,9] (length 9) totalChunks = 3 chunkSize = Math.ceil(9/3) = 3 Chunk 0: start=0*3=0, end=0+3=3 → [1,2,3] Chunk 1: start=1*3=3, end=3+3=6 → [4,5,6] Chunk 2: start=2*3=6, end=6+3=9 → [7,8,9] ``` -------------------------------- ### Testing Multiple Viewports Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Run the same tests in different screen sizes. ```javascript const viewports = [ { name: 'mobile', width: 375, height: 667 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'desktop', width: 1920, height: 1080 }, ] describe.each(viewports)('responsive design: %s', (viewport) => { beforeEach(() => { cy.viewport(viewport.width, viewport.height) cy.visit('/') }) it('header adapts to viewport', () => { cy.get('header').should('be.visible') cy.get('header nav').should('be.visible') }) it('layout is not horizontally scrollable', () => { cy.get('body').then(($body) => { const bodyWidth = $body[0].getBoundingClientRect().width expect(bodyWidth).to.be.lte(viewport.width) }) }) }) ``` -------------------------------- ### makeTitle Usage Example 2: String pattern with standard format specifiers Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Example of using makeTitle with a string pattern and standard format specifiers like %s. ```javascript // String pattern with standard format specifiers const title2 = makeTitle('checking %s', 'homepage', 0, ['homepage', 'about']) // Result: "checking homepage" ``` -------------------------------- ### makeTitle - Example Flow Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Illustrates how makeTitle processes a string pattern with special placeholders and format specifiers. ```javascript Input: 'item %K: %s' Value: 'apple' Index: 0 Values: ['apple', 'banana'] Step 1: Replace special placeholders 'item %K: %s' → 'item 1: %s' Step 2: Call formatTitle() formatTitle('item 1: %s', 'apple') Result: 'item 1: apple' ``` -------------------------------- ### makeTitle Usage Example 1: String pattern with format placeholders Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Example of using makeTitle with a string pattern and special placeholders like %K and %N. ```javascript const { makeTitle } = require('cypress-each') // String pattern with format placeholders const title1 = makeTitle('item %K of %N', 'apple', 0, ['apple', 'banana', 'cherry']) // Result: "item 1 of 3" ``` -------------------------------- ### Run specs in parallel Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of how to run specs in parallel using cypress-each. ```javascript // utils.js export const testTitle = (selector, k) => `testing ${k + 1} ...` export const testDataItem = (item) => { ... } // spec1.js import { data } from '...' import { testTitle, testDataItem } from './utils' it.each(data, 3, 0)(testTitle, testDataItem) // spec2.js import { data } from '...' import { testTitle, testDataItem } from './utils' it.each(data, 3, 1)(testTitle, testDataItem) // spec3.js import { data } from '...' import { testTitle, testDataItem } from './utils' it.each(data, 3, 2)(testTitle, testDataItem) ``` -------------------------------- ### Quick Start - Basic Usage Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/README.md Import the plugin and use `it.each` or `describe.each` for data-driven tests and suites. ```javascript import 'cypress-each' // Create a test for each item it.each(['home', 'about', 'contact'])('page %s loads', (page) => { cy.visit(`/${page}`) cy.get('body').should('be.visible') }) // Create a suite for each item describe.each(['chrome', 'firefox'])('browser %s', (browser) => { it('loads homepage', () => { cy.visit('/') cy.get('h1').should('exist') }) }) // Repeat a test N times it.each(5)('test %K of 5', (k) => { expect(k).to.be.within(0, 4) }) ``` -------------------------------- ### Mixed Types Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Pass an array where each element becomes a test input. This example uses mixed data types. ```javascript it.each([1, 'two', true])('mixed %K', (value) => { expect(value).to.exist }) ``` -------------------------------- ### Testing Permission Levels Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Verifies different user roles have correct permissions using `describe.each`. ```javascript const permissions = [ { role: 'admin', canCreate: true, canDelete: true, canExport: true }, { role: 'editor', canCreate: true, canDelete: false, canExport: true }, { role: 'viewer', canCreate: false, canDelete: false, canExport: false }, ] describe.each(permissions)('permissions for %s role', (perm) => { beforeEach(() => { cy.loginAs(perm.role) cy.visit('/items') }) it('handles create permission', () => { if (perm.canCreate) { cy.get('[data-testid="create-button"]').should('be.visible') } else { cy.get('[data-testid="create-button"]').should('not.exist') } }) it('handles delete permission', () => { cy.get('tr').first().within(() => { if (perm.canDelete) { cy.get('[data-testid="delete-button"]').should('be.visible') } else { cy.get('[data-testid="delete-button"]').should('not.exist') } }) }) it('handles export permission', () => { if (perm.canExport) { cy.get('[data-testid="export-button"]').should('be.visible') } else { cy.get('[data-testid="export-button"]').should('not.exist') } }) }) ``` -------------------------------- ### formatTitle Usage Example Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Demonstrates how to use the formatTitle function to format a string with placeholders. ```javascript const { formatTitle } = require('cypress-each') const title = formatTitle('test %s with %d items', 'data', 5) // Result: "test data with 5 items" ``` -------------------------------- ### Simple Array Iteration Example Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/it-each.md Example of using it.each with a simple array of strings to create separate tests for each element. ```javascript import 'cypress-each' const selectors = ['header', 'footer', '.new-todo'] it.each(selectors)('element %s is visible', (selector) => { cy.visit('/') cy.get(selector).should('be.visible') }) // Creates 3 tests: // - "element header is visible" // - "element footer is visible" // - "element .new-todo is visible" ``` -------------------------------- ### makeTitle Usage Example 3: String pattern with positional arguments Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Example of using makeTitle with a string pattern and positional arguments like %0, %1. ```javascript // String pattern with positional arguments const title3 = makeTitle('x=%0 y=%1', [10, 20], 0, [[10, 20]]) // Result: "x=10 y=20" ``` -------------------------------- ### Testing Login Scenarios Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Tests login with various user types and validates access levels using `describe.each`. ```typescript interface User { username: string password: string role: string hasAccess: boolean } const users: User[] = [ { username: 'admin@example.com', password: 'adminPass123', role: 'admin', hasAccess: true }, { username: 'user@example.com', password: 'userPass123', role: 'user', hasAccess: true }, { username: 'guest@example.com', password: 'wrongPass', role: 'guest', hasAccess: false }, ] describe.each(users)('login as %s', (user) => { it('attempts login', () => { cy.visit('/login') cy.get('#username').type(user.username) cy.get('#password').type(user.password) cy.get('[data-testid="login-button"]').click() }) it('validates access level', () => { if (user.hasAccess) { cy.url().should('include', '/dashboard') cy.contains('Welcome').should('be.visible') } else { cy.get('[data-error]').should('contain', 'Invalid credentials') } }) }) ``` -------------------------------- ### Pitfall: Using Dynamic Data - Solution Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Demonstrates the correct way to use dynamic data by importing it at the module level. ```javascript // ✅ CORRECT - data is loaded before tests are defined import items from './fixtures/items.json' it.each(items)('test', (item) => { ... }) ``` -------------------------------- ### Exported Utilities Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Examples of using exported utility functions like formatTitle, makeTitle, and getChunk. ```javascript const { formatTitle, makeTitle, getChunk } = require('cypress-each') // Format string with placeholders formatTitle('item %d', 42) // "item 42" // Generate test title with special placeholders makeTitle('test %K of %N', item, 0, items) // "test 1 of 5" // Split array into chunks getChunk([1,2,3,4,5,6], 3, 0) // [1, 2] getChunk([1,2,3,4,5,6], 3, 1) // [3, 4] getChunk([1,2,3,4,5,6], 3, 2) // [5, 6] ``` -------------------------------- ### Array of Arrays Example Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/it-each.md Example of using it.each with an array of arrays to pass multiple arguments to each test. ```javascript const testData = [ ['header', 'be.visible'], ['footer', 'exist'], ['.new-todo', 'not.be.visible'], ] it.each(testData)('element %s should %s', (selector, assertion) => { cy.visit('/') cy.get(selector).should(assertion) }) // Creates 3 tests with both arguments unpacked ``` -------------------------------- ### Example Usage of TestCaseObject3 Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/types.md Demonstrates how to use TestCaseObject3 with specific types and provides an example test case. ```typescript type AdditionCases = TestCaseObject3 const testCases: AdditionCases = { 'small numbers': [1, 2, 3], 'large numbers': [100, 200, 300], 'with negatives': [-5, 10, 5], } it.each(testCases)((a: number, b: number, expected: number) => { expect(a + b).to.equal(expected) }) ``` -------------------------------- ### Testing Multiple DOM Elements Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Verify visibility and accessibility of multiple page elements. ```javascript import 'cypress-each' const pageElements = [ { name: 'header', selector: 'header' }, { name: 'main content', selector: 'main' }, { name: 'footer', selector: 'footer' }, { name: 'navigation', selector: 'nav' }, ] describe.each(pageElements)('page element: %s', (element) => { beforeEach(() => { cy.visit('/') }) it('should exist in DOM', () => { cy.get(element.selector).should('exist') }) it('should be visible', () => { cy.get(element.selector).should('be.visible') }) it('should be accessible', () => { cy.get(element.selector).within(() => { cy.get('[role]').should('have.length.greaterThan', 0) }) }) }) ``` -------------------------------- ### Pitfall: Array Unpacking Mismatch - Solution Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Illustrates ensuring consistent array sizes for correct parameter unpacking. ```javascript // ✅ CORRECT - all arrays have same size it.each([ [1, 2, 'sum'], [3, 4, 'sum'], ])('test', (a, b, op) => { // All tests receive 3 parameters }) ``` -------------------------------- ### Testing Checkout with Variations Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Tests checkout with different shipping and payment methods using `it.each`. ```typescript const checkoutScenarios: { [key: string]: [string, string] // [shipping, payment] } = { 'standard shipping credit card': ['standard', 'credit'], 'express shipping paypal': ['express', 'paypal'], 'overnight debit card': ['overnight', 'debit'], } it.each(checkoutScenarios)( 'checkout: %s and %s', (shipping, payment) => { cy.loginAs('customer') cy.addItemsToCart() cy.visit('/checkout') cy.get(`[value="${shipping}"]`).click() cy.get(`[value="${payment}"]`).click() cy.get('[data-testid="place-order"]').click() cy.url().should('include', '/confirmation') cy.get('[data-testid="order-number"]').should('exist') } ) ``` -------------------------------- ### Objects as Values Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Using an object where keys are test titles and values are object test inputs. ```javascript it.each({ 'user Alice': { name: 'Alice', role: 'admin' }, 'user Bob': { name: 'Bob', role: 'user' }, })('user roles', (user) => { expect(user.name).to.be.a('string') expect(user.role).to.be.oneOf(['admin', 'user']) }) ``` -------------------------------- ### Testing CRUD Operations Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Data-driven CRUD testing for multiple resources. ```typescript interface Resource { name: string endpoint: string } const resources: Resource[] = [ { name: 'users', endpoint: '/api/users' }, { name: 'products', endpoint: '/api/products' }, { name: 'orders', endpoint: '/api/orders' }, ] describe.each(resources)('CRUD operations for %s', (resource) => { it('creates a new item', () => { cy.request('POST', `https://api.example.com${resource.endpoint}`, { name: 'test', }).then((response) => { expect(response.status).to.equal(201) expect(response.body).to.have.property('id') }) }) it('reads items', () => { cy.request('GET', `https://api.example.com${resource.endpoint}`) .then((response) => { expect(response.status).to.equal(200) expect(response.body).to.be.an('array') }) }) it('updates an item', () => { cy.request('PATCH', `https://api.example.com${resource.endpoint}/1`, { name: 'updated', }).then((response) => { expect(response.status).to.be.oneOf([200, 204]) }) }) }) ``` -------------------------------- ### Repeat Test N Times Example Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/it-each.md Example of using it.each with a number to repeat the same test multiple times. ```javascript it.each(5)('test %K of 5', function (k) { expect(k).to.be.within(0, 4) }) // Creates 5 tests: "test 1 of 5", "test 2 of 5", etc. ``` -------------------------------- ### makeTitle Usage Example 4: Function pattern Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Example of using makeTitle with a function pattern to generate a custom title. ```javascript // Function pattern const titleFn = (value, k, values) => `test ${k + 1}: ${value.name}` const title4 = makeTitle(titleFn, { name: 'Alice' }, 0, [{ name: 'Alice' }]) // Result: "test 1: Alice" ``` -------------------------------- ### With One-Based Index Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Repeating a test and using a one-based index for formatting. ```javascript it.each(3)('attempt %K', (k) => { // k = 0, 1, 2 // %K gives 1, 2, 3 expect(k + 1).to.be.within(1, 3) }) ``` -------------------------------- ### Testing Form Fields Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Validate multiple form fields. ```javascript const formFields = [ { label: 'First Name', selector: '#firstName', type: 'text' }, { label: 'Email', selector: '#email', type: 'email' }, { label: 'Phone', selector: '#phone', type: 'tel' }, { label: 'Message', selector: '#message', type: 'textarea' }, ] describe('form validation', () => { beforeEach(() => { cy.visit('/contact') }) describe.each(formFields)('%s field', (field) => { it('should be visible and enabled', () => { cy.get(field.selector) .should('be.visible') .should('not.be.disabled') }) it('should have correct input type', () => { cy.get(field.selector) .should('have.attr', 'type', field.type) }) it('should be required', () => { cy.get(field.selector) .should('have.attr', 'required') }) }) }) ``` -------------------------------- ### Runtime Dependency: format-util Validation Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Code snippet to validate the correct installation and functionality of the 'format-util' dependency. ```javascript const format = require('format-util') if (typeof format !== 'function') { throw new Error('Missing format-util function') } ``` -------------------------------- ### Testing Multiple Endpoints Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Test API endpoints with consistent assertions. ```javascript const endpoints = [ { method: 'GET', path: '/api/users', statusCode: 200 }, { method: 'GET', path: '/api/products', statusCode: 200 }, { method: 'GET', path: '/api/orders', statusCode: 200 }, { method: 'GET', path: '/api/invalid', statusCode: 404 }, ] it.each(endpoints)('%s %s returns %d', (method, path, statusCode) => { cy.request({ method, url: `https://api.example.com${path}`, failOnStatusCode: false, }).then((response) => { expect(response.status).to.equal(statusCode) }) }) ``` -------------------------------- ### Using Test Case Objects with Types Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Demonstrates how to use typed test case objects with cypress-each for better type safety. ```typescript import type { TestCaseObject3 } from 'cypress-each' interface MathTest { a: number b: number expected: number } const mathTests: TestCaseObject3 = { 'add positive': [1, 2, 3], 'add negative': [-1, -2, -3], 'add mixed': [5, -3, 2], } it.each(mathTests)( 'addition test', (a: number, b: number, expected: number) => { expect(a + b).to.equal(expected) } ) ``` -------------------------------- ### Simple Object Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Using an object where keys are test titles and values are single test inputs. ```javascript it.each({ 'positive': 5, 'negative': -5, 'zero': 0, })('absolute value test', (value) => { expect(Math.abs(value)).to.equal(Math.abs(value)) }) ``` -------------------------------- ### Static data - DOES NOT WORK Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of incorrect usage of cy.fixture with cypress-each. ```javascript 🚨 DOES NOT WORK let list before(() => { cy.fixture('list.json').then(data => list = data) }) it.each(list)(...) // Nope, the list will always be undefined ``` -------------------------------- ### Common Use Cases - Multiple Arguments Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/README.md Example of using `it.each` with multiple arguments. ```javascript it.each([ ['username', 'be.visible'], ['password', 'exist'], ])('field %s should %s', (field, assertion) => { cy.get(`[data-testid="${field}"]`).should(assertion) }) ``` -------------------------------- ### Testing with Calculated Data Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Generates test data on the fly using a function and then iterates through it. ```javascript function generateTestUsers(count) { return Array.from({ length: count }, (_, i) => ({ id: i + 1, name: `User ${i + 1}`, email: `user${i + 1}@example.com`, })) } const users = generateTestUsers(10) it.each(users)('user %d profile works', (user) => { cy.request(`/api/users/${user.id}`).then((response) => { expect(response.body).to.have.property('id', user.id) }) }) ``` -------------------------------- ### Importing Helper Functions Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Example of how to import and use exported helper functions. ```javascript const { formatTitle, makeTitle, getChunk } = require('cypress-each') ``` -------------------------------- ### Pitfall: Accessing Index in Objects - Solution Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Shows how to convert an object to an array using Object.entries to access the index. ```javascript // ✅ CORRECT - use array mode for index access it.each(Object.entries({ 'case 1': 10, 'case 2': 20, }))('test %s = %d', ([name, value], index) => { // index is available }) ``` -------------------------------- ### Testing User Flows Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Tests complete user journeys with data variation by iterating through different scenarios. ```javascript const scenarios = [ { name: 'complete purchase', actions: ['browse', 'add-to-cart', 'checkout'], }, { name: 'save for later', actions: ['browse', 'wishlist', 'logout'], }, { name: 'apply discount', actions: ['browse', 'add-to-cart', 'apply-coupon', 'checkout'], }, ] describe.each(scenarios)('user flow: %s', (scenario) => { it('completes the flow', () => { cy.visit('/') scenario.actions.forEach((action) => { cy.performAction(action) }) cy.get('[data-testid="success"]').should('be.visible') }) }) ``` -------------------------------- ### String Values Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Using an object where keys are test titles and values are string test inputs. ```javascript it.each({ 'red color': 'rgb(255, 0, 0)', 'green color': 'rgb(0, 255, 0)', 'blue color': 'rgb(0, 0, 255)', })('test color %s', (rgbValue) => { cy.get('[data-color]').should('have.css', 'background-color', rgbValue) }) ``` -------------------------------- ### Every 2nd Item Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Selecting and testing every second item from a list. ```javascript const items = [1, 2, 3, 4, 5, 6, 7, 8] const n = it.each(items, 2)('every 2nd item %K', (x) => { expect(x).to.be.oneOf([1, 3, 5, 7]) }) // n = 4 ``` -------------------------------- ### Common Use Cases - Testing Multiple Selectors Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/README.md Example of using `it.each` to test multiple selectors. ```javascript it.each(['header', 'footer', 'nav'])('element %s exists', (selector) => { cy.get(selector).should('exist') }) ``` -------------------------------- ### Testing with External Data Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Loads test data from a JSON file and iterates through it to test user profiles. ```json // test-data.json // { // "users": [ // {"id": 1, "name": "Alice", "email": "alice@example.com"}, // {"id": 2, "name": "Bob", "email": "bob@example.com"} // ] // } ``` ```javascript import data from './fixtures/test-data.json' describe('user profiles', () => { it.each(data.users)('displays profile for %s', (user) => { cy.visit(`/profile/${user.id}`) cy.get('h1').should('contain', user.name) cy.get('[data-testid="email"]').should('contain', user.email) }) }) ``` -------------------------------- ### Usage Examples of getChunk Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/api-reference/helper-functions.md Demonstrates how to use the getChunk function to split an array into a specified number of chunks. ```javascript const { getChunk } = require('cypress-each') const items = [ 1, 2, 3, 4, 5, 6, 7, 8 ] // Split into 4 chunks of 2 items each const chunk0 = getChunk(items, 4, 0) // [1, 2] const chunk1 = getChunk(items, 4, 1) // [3, 4] const chunk2 = getChunk(items, 4, 2) // [5, 6] const chunk3 = getChunk(items, 4, 3) // [7, 8] // Split into 3 chunks; first chunk gets extra item due to uneven division const items9 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] const chunk0_3 = getChunk(items9, 3, 0) // [1, 2, 3, 4] (Math.ceil(9/3) = 3, but starts at 0) const chunk1_3 = getChunk(items9, 3, 1) // [4, 5, 6] const chunk2_3 = getChunk(items9, 3, 2) // [7, 8, 9] ``` -------------------------------- ### Every 3rd Item Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Selecting and testing every third item from a list. ```javascript const items = [1, 2, 3, 4, 5, 6, 7, 8, 9] const n = it.each(items, 3)('every 3rd item %K', (x) => { expect(x).to.be.oneOf([1, 4, 7]) }) // n = 3 ``` -------------------------------- ### Static data - ✅ static JSON import Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of correctly importing JSON data for cypress-each. ```javascript // ✅ static JSON import import list from '../fixtures/list.json' it.each(list)(...) ``` -------------------------------- ### Invalid Data Example Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Illustrates a scenario where using dynamic data for `.each` will not work because the data is not available at test definition time. ```javascript // ❌ Won't work - list is undefined at test definition time before(() => { cy.fixture('list.json').then(data => { list = data }) }) it.each(list)('test', ...) ``` -------------------------------- ### Filtering by Multiple Criteria Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Illustrates filtering tests based on multiple criteria like duration and browser. ```javascript const tests = [ { name: 'quick', duration: 1, browser: 'chrome' }, { name: 'slow', duration: 5, browser: 'chrome' }, { name: 'flaky', duration: 2, browser: 'firefox' }, ] // Run only quick tests it.each(tests, (t) => t.duration <= 1)( 'quick test: %s', (test) => { // test } ) // Run chrome tests only it.each(tests, (t) => t.browser === 'chrome')( 'chrome test: %s', (test) => { // test } ) ``` -------------------------------- ### Sampling Tests Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Runs a subset of tests for quick feedback, either by sampling every Nth item or using random sampling. ```javascript const items = [...array of 1000 items...] // Smoke test - run every 10th item it.each(items, 10)('smoke test %K', (item) => { cy.validateItem(item) }) // Or use Cypress._.sampleSize for random sampling it.each(Cypress._.sampleSize(items, 50))( 'random sample test %K', (item) => { cy.validateItem(item) } ) ``` -------------------------------- ### Four or More Values Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Demonstrates that `it.each` supports unpacking four or more values from an array into function arguments. ```javascript it.each([ ['a', 1, true, { x: 10 }], ['b', 2, false, { x: 20 }], ])('complex test %s %d', (str, num, bool, obj) => { expect(str).to.be.a('string') expect(num).to.be.a('number') expect(bool).to.be.a('boolean') expect(obj).to.be.an('object') }) ``` -------------------------------- ### Typed Object with Two Values Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Using an object where values are arrays, which are then unpacked as multiple function parameters. This example uses TypeScript for type safety. ```typescript type TestCase2 = { [key: string]: [A, B] } const cases: TestCase2 = { 'one': [1, 'one'], 'ten': [10, 'ten'], 'hundred': [100, 'hundred'], } it.each(cases)('number to string', (num: number, str: string) => { expect(String(num)).to.equal(str) }) ``` -------------------------------- ### Aliases Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Demonstrates that describe.each and context.each are equivalent. ```javascript // describe.each and context.each are equivalent describe.each([...])('suite', (item) => { ... }) context.each([...])('suite', (item) => { ... }) // Same thing ``` -------------------------------- ### Complex Predicate Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Filters test cases using a complex predicate function that checks multiple conditions on the request object. Only GET requests with a status of 200 are included. ```javascript const requests = [ { method: 'GET', status: 200 }, { method: 'GET', status: 404 }, { method: 'POST', status: 201 }, { method: 'POST', status: 400 }, { method: 'DELETE', status: 204 }, ] const n = it.each( requests, (req) => req.method === 'GET' && req.status === 200 )('valid GET requests', (req) => { expect(req.method).to.equal('GET') expect(req.status).to.equal(200) }) // n = 1 ``` -------------------------------- ### Four Chunks, One Item Each Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Distributing items across four chunks, with each chunk containing one item. ```javascript const items = [1, 2, 3, 4] // spec-1.js it.each(items, 4, 0)('test %d', (x) => { expect(x).to.equal(1) }) // spec-2.js it.each(items, 4, 1)('test %d', (x) => { expect(x).to.equal(2) }) // spec-3.js it.each(items, 4, 2)('test %d', (x) => { expect(x).to.equal(3) }) // spec-4.js it.each(items, 4, 3)('test %d', (x) => { expect(x).to.equal(4) }) ``` -------------------------------- ### Two Chunks, Two Items Each Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Distributing items across two chunks, with each chunk containing two items. ```javascript const items = [1, 2, 3, 4] // spec-1.js const tests1 = it.each(items, 2, 0)('test %d', (x) => { expect(x).to.be.oneOf([1, 2]) }) // tests1 = 2 // spec-2.js const tests2 = it.each(items, 2, 1)('test %d', (x) => { expect(x).to.be.oneOf([3, 4]) }) // tests2 = 2 ``` -------------------------------- ### describe.each Closure Binding Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Key line demonstrating how suite callbacks are bound with values for closure variables. ```javascript describe(title, testCallback.bind(null, ...value)) ``` -------------------------------- ### Array of Arrays - TypeScript Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Shows how to use an array of tuples (arrays) as input with TypeScript, inferring the types for each parameter. ```typescript const pairs: [string, number][] = [['a', 1], ['b', 2]] it.each(pairs)('test', (s: string, n: number) => { ... }) // TypeScript infers parameters as string and number ``` -------------------------------- ### Advanced Parameters - Every Nth Item Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Create tests for every Nth item in a dataset, starting from the first item. ```javascript it.each(items, 2)('sample %K', (item) => { ... }) // Creates tests for items at indices 0, 2, 4, ... ``` -------------------------------- ### Import Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Import the cypress-each library to make `it.each` and `describe.each` available. ```javascript import 'cypress-each' // Now it.each and describe.each are available ``` -------------------------------- ### Testing Error Scenarios Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Tests error handling and recovery by simulating different error conditions and verifying error messages and recovery actions. ```typescript interface ErrorScenario { description: string action: () => void expectedError: string recovery: () => void } const scenarios: ErrorScenario[] = [ { description: 'network timeout', action: () => cy.intercept('/api/data', { delayMs: 10000 }), expectedError: 'Request timeout', recovery: () => cy.reload(), }, { description: 'server error', action: () => cy.intercept('/api/data', { statusCode: 500 }), expectedError: 'Server error', recovery: () => cy.get('[data-testid="retry"]').click(), }, ] describe.each(scenarios)('error handling: %s', (scenario) => { it('shows error message', () => { scenario.action() cy.visit('/data') cy.get('[data-testid="error"]').should('contain', scenario.expectedError) }) it('recovers from error', () => { scenario.recovery() cy.get('[data-testid="content"]').should('exist') }) }) ``` -------------------------------- ### Combining with Cypress Configuration Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/README.md Shows how to combine describe.each with Cypress configuration options. ```javascript describe('my tests', { retries: 2 }, () => { describe.each(items)('item %K', (item) => { // All tests inherit retries: 2 }) }) ``` -------------------------------- ### Types - tsconfig.json Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of configuring TypeScript to include cypress-each types. ```json { "compilerOptions": { "types": ["cypress", "cypress-each"] } } ``` -------------------------------- ### Test case object Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of using a test case object with cypress-each. ```typescript const testCases = { // key: the test label // value: list of inputs for each test case 'positive numbers': [1, 6, 7], // [a, b, expected result] 'negative numbers': [1, -6, -5], } it.each(testCases)((a, b, expectedResult) => { expect(add(a, b)).to.equal(expectedResult) }) ``` -------------------------------- ### it.each Array Unpacking Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Key line demonstrating how array values are unpacked for test callbacks. ```javascript if (Array.isArray(value)) { it(title, function itArrayCallback() { return testCallback.apply(this, value, k) }) } ``` -------------------------------- ### Basic Repetition Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Repeating the same test a specified number of times. ```javascript it.each(5)('test %K of 5', (k) => { expect(k).to.be.within(0, 4) }) ``` -------------------------------- ### Basic Syntax - Repeat N Times Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Create a specified number of tests, with an index provided to each test. ```javascript it.each(5)('test %K of 5', (k) => { ... }) // Creates 5 tests, k = 0,1,2,3,4 ``` -------------------------------- ### Types - Object input Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of using TypeScript definitions with object input for cypress-each. ```javascript it.each([ { name: 'Joe', age: 30 }, { name: 'Mary', age: 20 }, ])('has correct types', (user) => { // the type for the "user" should be // name: string, age: number expect(user).to.have.keys('name', 'age') expect(user.name).to.be.a('string') expect(user.age).to.be.a('number') }) ``` -------------------------------- ### test case types Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of using utility types for test case inputs. ```typescript // two arguments // each value is [number, string] const toString: TestCaseObject2 = { one: [1, '1'], ten: [10, '10'], } it.each(toString)((a, b) => { // a is a number // b is a string }) // three arguments const additions: TestCaseObject3 = { one: [1, 2, '3'], // a + b in string form ten: [10, 20, '30'], } it.each(additions)((a, b, s) => { expect(String(a + b)).to.equal(s) }) ``` -------------------------------- ### Suite Repetition Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Repeating a describe block a specified number of times. ```javascript describe.each(3)('attempt %K', (k) => { before(() => { expect(k).to.be.within(0, 2) }) it('works', () => { expect(true).to.be.true }) }) ``` -------------------------------- ### Common Patterns - Test Multiple Viewports Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Test the layout of a page across different screen sizes by iterating over a list of viewport configurations. ```javascript const viewports = [ { name: 'mobile', width: 375 }, { name: 'desktop', width: 1920 }, ] describe.each(viewports)('viewport %s', (vp) => { it('layout works', () => { cy.viewport(vp.width, 667) cy.visit('/') }) }) ``` -------------------------------- ### Types - Individual spec file Source: https://github.com/bahmutov/cypress-each/blob/main/README.md Example of referencing cypress-each types in an individual spec file. ```javascript /// ``` -------------------------------- ### Basic Syntax - Simple Array Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Create multiple tests from a simple array of values. ```javascript it.each([1, 2, 3])('test %d', (value) => { ... }) // Creates 3 tests with values 1, 2, 3 ``` -------------------------------- ### Basic Syntax - Object Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Create multiple tests from an object, using keys as test titles and values as test parameters. ```javascript it.each({ 'first': 10, 'second': 20, })('test', (value) => { ... }) // Creates 2 tests: "first" and "second" ``` -------------------------------- ### describe() Call Integration Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md How cypress-each integrates with Mocha's describe() function. ```javascript describe(title, testCallback.bind(null, value)) ``` -------------------------------- ### Execution Flow Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Diagram illustrating the execution flow of cypress-each. ```text Import cypress-each \ Extend it, describe, context with .each() \ Developer calls it.each(data)(title, fn) \ Plugin processes data \ Generate titles \ Call it(title, fn) for each item \ Mocha discovers tests \ Cypress runs tests normally ``` -------------------------------- ### Testing Input Validation Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Validates form inputs with multiple test cases using `it.each`. ```typescript const testCases: { [key: string]: [string, boolean] // [value, isValid] } = { 'valid email': ['user@example.com', true], 'missing @': ['userexample.com', false], 'missing domain': ['user@', false], 'special chars': ['user+tag@example.com', true], 'empty': ['', false], } it.each(testCases)('email validation: %s', (email, isValid) => { cy.visit('/signup') cy.get('#email').clear().type(email) if (isValid) { cy.get('#submit').should('not.be.disabled') } else { cy.get('#submit').should('be.disabled') cy.get('[data-error="email"]').should('be.visible') } }) ``` -------------------------------- ### Objects in Array Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/input-formats.md Pass an array where each element is an object, which becomes a single test input. ```javascript it.each([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ])('user %s', (user) => { expect(user.id).to.be.a('number') expect(user.name).to.be.a('string') }) ``` -------------------------------- ### Conditional Test Execution Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/patterns-and-examples.md Shows how to skip tests based on data properties using a filter function. ```javascript const features = [ { name: 'feature-a', enabled: true }, { name: 'feature-b', enabled: false }, { name: 'feature-c', enabled: true }, ] // Only test enabled features it.each(features, (f) => f.enabled)( 'feature %s works', (feature) => { cy.enableFeature(feature.name) cy.get(`[data-feature="${feature.name}"]`).should('be.visible') } ) ``` -------------------------------- ### Important Constraints - DO: Use describe.only to Focus Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Use `describe.only` to focus on a specific suite when using `it.each`. ```javascript describe.only('my tests', () => { it.each(items)('test', (item) => { ... }) }) ``` -------------------------------- ### Suite Declaration - Create Suites from Data Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/QUICK-REFERENCE.md Create multiple describe blocks based on an array of data, using the data in the describe block's title. ```javascript describe.each(['chrome', 'firefox'])('browser %s', (browser) => { it('loads page', () => { ... }) }) // Creates 2 describe blocks, each with tests ``` -------------------------------- ### describe.each Implementation Source: https://github.com/bahmutov/cypress-each/blob/main/_autodocs/implementation-details.md Core logic for the describe.each function. ```javascript describe.each = function (values) { // 1. Validate values (must be array or number) // 2. Return a function that accepts (titlePattern, suiteCallback) // 3. That function iterates and calls describe() for each item // 4. Returns count of created suites } ```