How to use cypress app actions

Updated on

0
(0)

To use Cypress app actions, here are the detailed steps:

👉 Skip the hassle and get the ready to use 100% working script (Link in the comments section of the YouTube Video) (Latest test 31/05/2025)

Check more on: How to Bypass Cloudflare Turnstile & Cloudflare WAF – Reddit, How to Bypass Cloudflare Turnstile, Cloudflare WAF & reCAPTCHA v3 – Medium, How to Bypass Cloudflare Turnstile, WAF & reCAPTCHA v3 – LinkedIn Article

First, understand what app actions are. They are essentially a way to directly interact with your application’s internal state or methods from within your Cypress tests, bypassing the UI. Think of it as a backdoor into your application logic for faster, more robust testing.

Here’s a quick guide:

  1. Expose your app’s internal methods: You need to make the functions or state you want to interact with accessible from the window object in your application code. For example, in your application’s index.js or main.ts file, you might add window.app = { login, createItem }.
  2. Access them in Cypress: In your Cypress test, use cy.window.thenwin => { win.app.login'user', 'pass'. }. to call the exposed method.
  3. For more structure and reusability, encapsulate these calls into custom Cypress commands. Create a commands.js file e.g., cypress/support/commands.js and add Cypress.Commands.add'loginViaApp', username, password => { cy.window.thenwin => { win.app.loginusername, password. }. }..
  4. Then, use your custom command: In your test file, you can simply call cy.loginViaApp'testuser', 'password123'.. This makes your tests cleaner and easier to read.
  5. Consider using a dedicated library like cypress-commands or cypress-app-actions if your needs grow complex, though for most cases, exposing methods directly on window and creating custom commands is sufficient.
  6. Always clean up after tests: If app actions modify state, ensure you have a strategy to reset that state e.g., through a beforeEach or afterEach hook to maintain test isolation.

Table of Contents

The Power of Cypress App Actions: Streamlining Your End-to-End Tests

In the world of software development, efficiency and reliability are paramount. When it comes to testing, especially end-to-end E2E testing, we often face challenges like slow test execution, flakiness, and difficulty in setting up specific application states. This is where Cypress app actions emerge as a powerful tool, offering a pragmatic solution to these common hurdles. App actions allow us to interact directly with our application’s internal state and methods, bypassing the traditional UI interactions. This isn’t just a hack. it’s a strategic approach to building more robust, faster, and less brittle test suites. By leveraging app actions, we can significantly reduce the time spent waiting for UI animations or complex multi-step forms to complete, focusing instead on validating the core functionality of our application.

What Exactly Are Cypress App Actions?

At its core, a Cypress app action is a mechanism to directly invoke functions or modify variables within your application’s code from your Cypress tests. Instead of clicking through a multi-step form to create a user, you could simply call an internal createUser function. This dramatically speeds up test execution and makes your tests more resilient to UI changes. Think of it like this: your application runs within a browser context, and Cypress tests run alongside it in the same context. This unique architecture allows for direct communication. While traditional E2E tests mimic user behavior by interacting with the DOM Document Object Model, app actions provide a “backdoor” to manipulate the application state programmatically. This can be particularly useful for setting up complex preconditions or tearing down specific data after a test, all without rendering a single pixel or waiting for network requests. For instance, according to a survey by Eggplant now Keysight Technologies, test automation can reduce testing time by 50-80%. Integrating app actions can push this even further by optimizing setup and teardown phases.

Why Bother with App Actions? The Unseen Benefits

While the primary benefit of app actions is speed, their utility extends far beyond mere acceleration. They contribute to test stability, isolation, and overall maintainability. Imagine a scenario where you need to test a specific feature that is only accessible after a user has performed five different actions in the UI. Instead of writing five cy.get.click commands, which are prone to breaking with minor UI changes and are notoriously slow, you can use one app action call to set the necessary state. This reduces flakiness caused by asynchronous UI updates or network delays. Furthermore, app actions promote better test isolation. By directly manipulating state, you can ensure that each test starts from a known, clean slate, minimizing dependencies between tests and making debugging much easier. For example, a report by Tricentis highlights that flaky tests are a significant source of developer frustration, with 70% of teams experiencing them regularly. App actions help mitigate this by providing a more direct and less volatile means of interaction.

Setting Up Your Application for App Actions: Exposing the Interior

To successfully utilize Cypress app actions, your application needs to expose certain functions or states so that Cypress can interact with them.

This usually involves making these internal mechanisms accessible via the window object in the browser.

It’s a common practice in development environments and is generally safe, provided you take precautions for production builds.

The window Object: Your Gateway to App Internals

The window object in a browser environment serves as the global object for JavaScript.

Anything attached to window becomes globally accessible.

This is the simplest and most common way to expose your application’s internal methods for Cypress to consume.

  • Direct Attachment Development Only: You can directly attach functions or objects to window in your application’s entry point e.g., src/index.js, src/main.ts, or App.js.

    // src/index.js or similar entry point
    
    
    import { loginUser, createUserProfile } from './api'.
    
    
    import store from './store'. // Assuming a state management store like Redux or Vuex
    
    if window.Cypress {
      window.app = {
    
    
       login: username, password => loginUserusername, password,
    
    
       createUser: userData => createUserProfileuserData,
    
    
       clearStore:  => store.dispatch'resetState', // Example for state management
        getStoreState:  => store.getState,
      }.
    }
    

    Explanation:

    • The if window.Cypress check is crucial. This ensures that your app’s internal methods are only exposed when Cypress is running. This prevents these internals from being accessible in your production build, maintaining security and preventing potential conflicts.
    • window.app creates a namespace for your app actions, keeping the global window object clean. You can name this anything, e.g., window.myApp or window.testUtils.
    • The exposed functions login, createUser, clearStore, getStoreState are wrappers around your actual application logic. This allows you to expose specific functionalities without exposing the entire module or complex dependencies.
  • Using a Dedicated Module for Test Utilities: For larger applications, it’s often cleaner to create a separate module specifically for test utilities and import it only when Cypress is detected.

    // src/test-utils/index.js

    Import { loginUser, createUserProfile } from ‘../api’.
    import store from ‘../store’.

    export const setupCypressAppActions = => {
    if window.Cypress {
    window.app = {

    login: username, password => loginUserusername, password,

    createUser: userData => createUserProfileuserData,

    clearStore: => store.dispatch’resetState’,
    getStoreState: => store.getState,
    }.
    }
    }.

    // In your main application entry file e.g., src/index.js

    Import { setupCypressAppActions } from ‘./test-utils’.

    // … other imports and app initialization …
    setupCypressAppActions.

    Benefit: This approach centralizes all your app action exposures, making them easier to manage and less likely to accidentally end up in production bundles if you use a smart bundler like Webpack or Rollup that can tree-shake unused exports.

Considerations for Frameworks React, Vue, Angular

The general principle of exposing methods on window remains the same, but the specific place you do it might vary slightly based on your framework’s entry point and lifecycle.

  • React: Typically in src/index.js or src/App.js after your root component is mounted or as part of a setup function.
  • Vue: In src/main.js or src/app.js right after your Vue instance is created.
  • Angular: Often in src/main.ts or within a service that gets initialized early in the application lifecycle. You might need to use window as any.app = { ... }. due to TypeScript’s strict typing.

Important Note on Security and Production:

  • NEVER expose sensitive functions or data on the window object in your production build. The if window.Cypress check is your first line of defense.
  • Bundlers like Webpack or Rollup can be configured to completely remove these if window.Cypress blocks and the code within them from production builds through tools like DefinePlugin or environment variable checks. This is the most robust way to ensure these test utilities don’t leak into your live application. For instance, you could define process.env.NODE_ENV as 'test' during Cypress runs and 'production' otherwise, then guard your app action code with if process.env.NODE_ENV === 'test'.

By meticulously setting up your application to expose the necessary internals, you pave the way for highly efficient and powerful Cypress tests, allowing you to manipulate your application’s state with precision and speed, saving significant testing time which, according to a report by Capgemini, can account for up to 35% of overall project cost.

Crafting Custom Cypress Commands for App Actions: The Clean Way

While directly calling cy.window.thenwin => win.app.someFunction works, it can quickly lead to repetitive and less readable tests. The solution? Custom Cypress commands. They allow you to encapsulate complex or repeated logic, making your test files cleaner, more maintainable, and highly readable. Think of them as your utility belt for Cypress, providing specialized tools for common testing scenarios.

The Why: Readability, Reusability, and Maintainability

  • Readability: Instead of a verbose cy.window.then... block, you get a concise cy.loginViaApp. This makes your test code look more like natural language, improving comprehension for anyone reading the tests.
  • Reusability: Once defined, a custom command can be used across multiple test files and scenarios. This adheres to the DRY Don’t Repeat Yourself principle, reducing code duplication and making updates simpler. If your login process changes, you only update it in one place the custom command definition rather than across dozens of test files.
  • Maintainability: When a complex app action needs modification or debugging, you know exactly where to look: the command definition file. This centralizes your test utilities and makes your test suite much easier to manage over time. Industry data suggests that poor test maintainability can increase testing costs by 20-30%.

The How: Defining Your Custom Commands

Custom commands are typically defined in cypress/support/commands.js or commands.ts for TypeScript projects. Cypress automatically loads any files in the cypress/support directory before running your tests.

  1. Open cypress/support/commands.js:

    // cypress/support/commands.js

    // Ensure the app object exists before trying to access it

    // This is good practice, especially if your app might not be fully loaded yet.
    Cypress.Commands.add’getApp’, => {

    return cy.window.its’app’. // ‘its’ is useful for asserting property existence
    }.

    Cypress.Commands.add’loginViaApp’, username, password => {
    cy.getApp.thenapp => {

    if app && typeof app.login === 'function' {
       return app.loginusername, password.
     } else {
    
    
      throw new Error'App login function not exposed or available.'.
     }
    

    }.

    Cypress.Commands.add’createItemViaApp’, itemDetails => {

    if app && typeof app.createItem === 'function' {
       return app.createItemitemDetails.
    
    
      throw new Error'App createItem function not exposed or available.'.
    

    Cypress.Commands.add’clearAppState’, => {

    if app && typeof app.clearStore === 'function' {
       return app.clearStore.
    
    
      // If clearStore is not available, try to handle it gracefully or log a warning
    
    
      cy.log'Warning: clearStore app action not available. Skipping state clear.'.
    

    Cypress.Commands.add’fetchUserDashboardData’, userId => {

    if app && typeof app.fetchDashboardData === 'function' {
       return app.fetchDashboardDatauserId.
    
    
      throw new Error'App fetchDashboardData function not exposed or available.'.
    

    // Example of a “chainable” command less common for app actions but possible

    // Cypress.Commands.add’registerAndLogin’, username, password, email => {
    // cy.getApp.thenapp => {

    // app.registerUserusername, password, email.
    // app.loginusername, password.
    // }.
    // }.

    Key Elements:

    • Cypress.Commands.addcommandName, callbackFunction: This is the core method for defining custom commands.
    • callbackFunction: This function contains the logic for your command. It receives any arguments passed to the command.
    • cy.window.its'app': This is a robust way to wait for the window.app object to become available and then yield it. its automatically retries until the property exists, which is helpful if your application takes a moment to initialize.
    • Error Handling: Adding if app && typeof app.login === 'function' checks makes your commands more resilient. If for some reason the app action isn’t exposed or correctly named, it will throw a more informative error rather than failing silently or with a cryptic message.

The Usage: Integrating into Your Tests

Once defined, using your custom commands in your test files is straightforward:

// cypress/e2e/login.cy.js

describe'User Authentication',  => {
  beforeEach => {


   // Clear state before each test to ensure isolation
    cy.clearAppState.


   cy.visit'/login'. // Still good to visit the base URL if needed
  }.



 it'allows a registered user to login via app action',  => {


   // Instead of filling a form, we use the app action


   cy.loginViaApp'[email protected]', 'SecurePassword123'.


   // Now assert on UI elements that indicate successful login
    cy.url.should'include', '/dashboard'.


   cy.contains'Welcome, testuser!'.should'be.visible'.



 it'can create an item and verify its presence',  => {


   cy.loginViaApp'[email protected]', 'adminPass'. // Login first


   const newItem = { name: 'Cypress Test Item', description: 'Created via app action', price: 99.99 }.


   cy.createItemViaAppnewItem. // Create the item


   cy.visit'/items'. // Navigate to the items page


   cy.containsnewItem.name.should'be.visible'.


   cy.contains`$${newItem.price.toFixed2}`.should'be.visible'.



 it'fetches and displays user dashboard data',  => {


   cy.loginViaApp'[email protected]', 'dashPass'.


   cy.fetchUserDashboardData'user-id-123'. // Fetch data via app action


   cy.contains'Total Orders: 5'.should'be.visible'.


   cy.contains'Recent Activity: Last login 2 hours ago'.should'be.visible'.
}.

By investing a little time in defining custom commands for your app actions, you’ll gain significant returns in terms of test suite performance, readability, and long-term maintainability. It’s a foundational practice for serious Cypress testing. Reports from organizations like Google and Microsoft, who heavily rely on automated testing, consistently show that well-structured, maintainable test suites are directly correlated with faster release cycles and higher software quality.

Best Practices and Advanced Techniques for App Actions: Mastering the Craft

Leveraging Cypress app actions effectively goes beyond simple implementation.

It involves understanding best practices, integrating them smartly into your test strategy, and considering advanced techniques to maximize their benefit while minimizing potential pitfalls.

When to Use and When to Avoid App Actions

App actions are powerful, but they aren’t a silver bullet.

Knowing when to deploy them and when to stick to traditional UI interactions is crucial for a balanced and effective test suite.

  • When to Use App Actions:

    • Setting up Preconditions: When you need a specific application state e.g., a logged-in user, an item in the cart, a specific number of records in a database before you test the UI for a feature. This is their strongest use case. A typical E2E test might involve 80% setup and 20% actual testing. App actions can reduce that setup time by over 90%.
    • Bypassing Complex Flows: For multi-step forms, complex onboarding, or lengthy administrative tasks that aren’t the primary focus of the current test. If you’re testing the functionality of a payment gateway, you don’t need to painstakingly click through user registration.
    • Speeding Up Test Execution: When your test suite becomes too slow due to extensive UI interactions during setup/teardown. According to a DZone article, test execution time is a critical factor, with a 10% increase in execution time potentially leading to a 5% decrease in developer productivity.
    • Ensuring Test Isolation: To quickly reset application state between tests e.g., clearing a user’s session or database state.
    • Accessing Internal State for Assertions: Occasionally, to directly inspect a component’s state or a Redux store value for assertions, rather than relying solely on DOM elements.
    • For Integration-level tests: When you want to test how different parts of your application integrate without relying on the full browser UI, app actions can bridge the gap between unit and E2E tests.
  • When to Avoid App Actions or use sparingly:

    • Testing UI/UX: If the primary goal of the test is to verify the user interface, visual elements, or user experience flow, then stick to UI interactions. App actions bypass the UI, so they can’t validate what the user sees or experiences.
    • Critical User Journeys: For the absolute most critical user paths e.g., core signup, checkout flow, a complete UI-driven E2E test is indispensable, as it validates the entire stack, including frontend interactions, routing, and backend integration.
    • Over-reliance: Don’t replace all UI interactions with app actions. A healthy E2E suite balances speed with realistic user simulation. Over-reliance can lead to a false sense of security where your backend works, but your UI is broken.
    • Debugging UI Issues: When debugging a UI bug, you need to see the actual UI. App actions can sometimes mask UI-related problems.

Integrating App Actions with beforeEach and afterEach

This is where app actions truly shine for test setup and teardown.

  • beforeEach for Setup: Use beforeEach to set up the necessary application state before each test runs.

    describe’Product Management’, => {

    // Login as admin and clear existing products before each test
    beforeEach => {

    cy.loginViaApp'[email protected]', 'secureAdminPass'.
    
    
    cy.clearProductsViaApp. // An app action to delete all products
    
    
    cy.visit'/admin/products'. // Then navigate to the relevant UI
    

    it’allows admin to add a new product’, => {
    // … UI interactions to add product …

    cy.get”.click.
    cy.get’#product-name’.type’New Gadget’.
    cy.get’#product-price’.type’49.99′.
    cy.get’#product-description’.type’A shiny new gadget.’.
    cy.get’button’.click.

    cy.contains’New Gadget’.should’be.visible’.

    cy.contains’Product added successfully.’.should’be.visible’.
    // … other tests …

  • afterEach for Teardown Less Common, but Useful: While beforeEach often handles cleanup by resetting state for the next test, afterEach can be used for specific post-test cleanup actions if needed e.g., logging out, deleting a specific item created during the test that might interfere with manual debugging.

    describe’User Profile Updates’, => {
    let createdUserId. // To store a user ID created in the test

    cy.loginViaApp'[email protected]', 'userPass'.
     cy.visit'/profile'.
    

    it’updates user email address’, => {
    cy.getApp.thenapp => {

    // Create a new user specifically for this test, get its ID

    app.createUser{ username: ‘temp_user’, email: ‘[email protected]‘ }.thenid => {
    createdUserId = id. // Store ID for teardown
    cy.get’#email-input’.clear.type’[email protected]‘.

    cy.get’button’.click.

    cy.contains’Email updated successfully.’.should’be.visible’.
    }.
    }.
    afterEach => {
    if createdUserId {

    cy.deleteUserViaAppcreatedUserId. // App action to delete the user
    createdUserId = null. // Reset for next test
    Note: For database-level cleanup, cy.task is often a better choice, as it operates in the Node.js environment, allowing direct database access.

Handling Asynchronous App Actions and Retries

App actions are JavaScript functions, and many internal application functions are asynchronous e.g., making API calls, database operations. Cypress handles asynchronous operations automatically when you return a Promise from your command or cy.then block.

// In cypress/support/commands.js

Cypress.Commands.add’loginViaAppAsync’, username, password => {
cy.getApp.thenasync app => {
if app && typeof app.login === ‘function’ {

  // Return the Promise from the async operation
   return await app.loginusername, password.
 } else {


  throw new Error'App login function not exposed or available.'.

// In your test file

It’logs in and waits for async operation’, => {

cy.loginViaAppAsync’[email protected]‘, ‘password123′. // Cypress will wait for this promise to resolve
cy.url.should’include’, ‘/dashboard’.

Cypress commands have built-in retryability, but when you return a Promise from cy.then, Cypress will wait for that Promise to resolve or reject. If the app action itself needs to retry, you’d build that logic into the app action’s implementation. For example, if app.login makes an API call that sometimes fails, you might implement a retry mechanism within app.login itself, or use cy.waitUntil from cypress-wait-until if you need Cypress to retry the entire app action call until a certain condition is met on the client.

Leveraging cy.task for Database Interactions

While app actions are great for in-browser state manipulation, they cannot directly interact with your backend database.

For pre-populating or cleaning up database records, cy.task is the preferred method.

  • cy.task explained: cy.task allows you to run Node.js code from your Cypress tests. This means you can import database clients like knex, mongoose, sequelize, pg and perform direct database operations.

  • Setup in cypress.config.js or plugins/index.js for older versions:

    // cypress.config.js
    const { defineConfig } = require’cypress’.

    Const db = require’./db’. // Your database connection and utility functions

    module.exports = defineConfig{
    e2e: {
    setupNodeEventson, config {
    on’task’, {
    async clearDatabase {
    await db.clearAllTables.
    return null. // Tasks must return a value or null
    },
    async seedUsersusers {
    await db.insertUsersusers.
    return null.
    async getUserByIdid {
    return await db.fetchUserid.
    }
    },
    },

    // db.js example – replace with your actual DB setup
    const sqlite3 = require’sqlite3′.
    const { open } = require’sqlite’.

    let db.

    async function initDb {
    db = await open{
    filename: ‘./mydb.sqlite’,
    driver: sqlite3.Database
    await db.exec’CREATE TABLE IF NOT EXISTS users id INTEGER PRIMARY KEY, name TEXT, email TEXT’.

    await db.exec’CREATE TABLE IF NOT EXISTS products id INTEGER PRIMARY KEY, name TEXT, price REAL’.

    async function clearAllTables {
    await db.exec’DELETE FROM users’.
    await db.exec’DELETE FROM products’.
    console.log’Database cleared!’.

    async function insertUsersusers {

    const stmt = await db.prepare’INSERT INTO users name, email VALUES ?, ?’.
    for const user of users {
    await stmt.runuser.name, user.email.
    await stmt.finalize.

    console.logInserted ${users.length} users..

    InitDb. // Initialize database connection on script load

    module.exports = {
    clearAllTables,
    insertUsers,
    // … other db functions

  • Usage in Tests:

    Describe’User Management with DB Setup’, => {
    // Clear database via cy.task
    cy.task’clearDatabase’.
    // Seed specific users via cy.task

    cy.task’seedUsers’, .
    cy.visit’/users’.
    it’displays seeded user’, => {

    cy.contains'Pre-existing User'.should'be.visible'.
    
    
    cy.contains'[email protected]'.should'be.visible'.
    

    it’can create a new user via UI’, => {
    cy.get’#add-user-btn’.click.
    cy.get’#user-name’.type’New UI User’.
    cy.get’#user-email’.type’[email protected]‘.

    cy.contains’New UI User’.should’be.visible’.

    cy.contains’User added successfully.’.should’be.visible’.

    // Optional: Verify creation directly in DB

    cy.task’getUserByEmail’, ‘[email protected]‘.thenuser => {
    expectuser.to.not.be.null.

    expectuser.name.to.equal’New UI User’.

Combining app actions for in-browser state with cy.task for database state provides a comprehensive strategy for preparing your application for tests, resulting in significantly faster and more reliable end-to-end test execution.

This hybrid approach allows you to leverage the strengths of both Cypress’s in-browser capabilities and Node.js’s backend access.

App Actions in Action: Real-World Scenarios and Code Examples

Let’s dive into some practical examples of how app actions can revolutionize your Cypress test suite, showing clear benefits over traditional UI interactions.

These scenarios often represent bottlenecks in test execution time and flakiness.

Scenario 1: Bypassing a Multi-Step Registration/Login Process

Problem: Your application has a multi-step registration form e.g., signup, email verification, profile setup followed by a login. Many tests require a logged-in user, but going through this entire UI flow for each test is slow and fragile.

Traditional UI Approach Slow & Brittle:

// cypress/e2e/traditional_login.cy.js
describe’Dashboard Access Traditional’, => {

// This part is repeated or handled by a complex UI command
 cy.visit'/register'.
cy.get'#username'.type'testuser'.
cy.get'#email'.type'[email protected]'.
cy.get'#password'.type'password123'.
cy.get'#confirm-password'.type'password123'.
 cy.get'button'.click.


cy.contains'Please verify your email'.should'be.visible'.


// ... complex steps for email verification might involve another domain or API call
 // ... then visit login page
 cy.visit'/login'.
cy.get'#login-email'.type'[email protected]'.
cy.get'#login-password'.type'password123'.

it’displays user dashboard’, => {

cy.contains'Welcome to your dashboard!'.should'be.visible'.

App Action Approach Fast & Robust:

First, expose loginUser and registerUser functions on window.app in your application:

// Your application code e.g., src/index.js
if window.Cypress {
window.app = {
login: async email, password => {
// Simulate API call to your backend
const response = await fetch’/api/login’, {
method: ‘POST’,

    headers: { 'Content-Type': 'application/json' },
     body: JSON.stringify{ email, password },
   const data = await response.json.


  // Assume your login function sets a token in localStorage or cookie


  localStorage.setItem'authToken', data.token.
   return data.
 },


register: async username, email, password => {


  const response = await fetch'/api/register', {




    body: JSON.stringify{ username, email, password },
   return await response.json.
 clearAuth:  => {
   localStorage.removeItem'authToken'.
   // Clear cookies if used
   cy.clearCookies.

}.
}

Then, define custom commands in cypress/support/commands.js:

// cypress/support/commands.js

Cypress.Commands.add’loginViaApp’, email, password => {
cy.window.its’app’.thenapp => {

return app.loginemail, password. // This will return a promise

Cypress.Commands.add’registerViaApp’, username, email, password => {

return app.registerusername, email, password.

Cypress.Commands.add’clearAuth’, => {
if app && app.clearAuth {
app.clearAuth.

Finally, use them in your tests:

// cypress/e2e/app_action_login.cy.js
describe’Dashboard Access App Action’, => {

cy.clearAuth. // Ensure no previous session interferes


// Use an app action to directly register and login


cy.registerViaApp'testuser', '[email protected]', 'password123'.


cy.loginViaApp'[email protected]', 'password123'.


cy.visit'/dashboard'. // Only visit the final page

it’displays user dashboard after quick login’, => {

 // Test dashboard specific features


cy.get''.should'contain', '10'.

it’allows user to navigate to profile settings’, => {
cy.get”.click.
cy.url.should’include’, ‘/profile’.

cy.contains'Edit Profile'.should'be.visible'.

Impact: A login process that might take 5-10 seconds via UI can be reduced to milliseconds with an app action, saving significant time per test run. If you have 100 tests requiring login, this could be minutes saved on every run.

Scenario 2: Pre-populating Data for Feature Testing

Problem: You need to test a feature that requires a specific set of data e.g., an e-commerce cart with items, a project management board with tasks, a specific number of messages in an inbox. Creating this data via UI is tedious and slow.

Traditional UI Approach Manual Data Creation:

// Imagine creating 5 products and adding them to cart via UI
describe’Checkout Process Traditional’, => {
cy.visit’/products’.
for let i = 0. i < 5. i++ {

  cy.get'.product-item'.eqi.find'.add-to-cart-btn'.click.
   cy.wait500. // simulate network delay
 cy.get'.cart-icon'.click.
 cy.url.should'include', '/cart'.

it’proceeds to checkout with multiple items’, => {
cy.get’.checkout-button’.click.
// … fill payment details …

App Action Approach Programmatic Data Creation:

Expose an addItemToCart function on window.app:

// Your application code
// … existing app actions …
addItemToCart: item => {

  // Directly manipulate your Redux/Vuex store or add to a cart service


  // This bypasses UI and API calls for adding items if not needed
  const currentCart = JSON.parselocalStorage.getItem'cart' || ''.


  localStorage.setItem'cart', JSON.stringify.


  // Or dispatch action to your state management store
   // store.dispatch'cart/addItem', item.
   return true.
 clearCart:  => {
   localStorage.removeItem'cart'.
   // Or dispatch action to clear store
   // store.dispatch'cart/clear'.

Define custom commands:

Cypress.Commands.add’addItemToCartViaApp’, item => {
if app && app.addItemToCart {
return app.addItemToCartitem.

Cypress.Commands.add’clearCartViaApp’, => {
if app && app.clearCart {
app.clearCart.

Use them in your tests:

// cypress/e2e/app_action_cart.cy.js
describe’Checkout Process App Action’, => {

cy.clearCartViaApp. // Start with an empty cart
 // Add specific items using app actions


cy.addItemToCartViaApp{ id: 'prod-001', name: 'Laptop', price: 1200, quantity: 1 }.


cy.addItemToCartViaApp{ id: 'prod-002', name: 'Mouse', price: 25, quantity: 2 }.


cy.loginViaApp'[email protected]', 'pass123'. // Assume login is also an app action


cy.visit'/cart'. // Navigate directly to the cart page

it’proceeds to checkout with pre-populated items’, => {
cy.contains’Laptop’.should’be.visible’.
cy.contains’Mouse’.should’be.visible’.
cy.get’.total-price’.should’contain’, ‘$1250.00’. // 1200 + 2*25

cy.get''.click.
 cy.url.should'include', '/checkout'.
 // ... continue testing checkout UI ...

Impact: Instead of navigating through product pages and repeatedly clicking “Add to Cart,” you instantly set the cart state. This can save hundreds of milliseconds to several seconds per test, especially when dealing with complex data setups. This level of control over initial state is invaluable for targeted feature testing.

Scenario 3: Directly Triggering Application Events or State Changes

Problem: You want to test how your application reacts to a specific event or state change e.g., a websocket message arriving, a user’s subscription status changing, a specific component’s visibility toggled without interacting with the UI.

App Action Approach:

Expose methods to trigger events or change state:

triggerWebSocketMessage: messageType, payload => {
   // Simulate receiving a WebSocket message


  const event = new CustomEvent'app:websocket:message', { detail: { messageType, payload } }.
   window.dispatchEventevent.
 setUserSubscriptionStatus: status => {


  // Directly update user subscription in your state management


  // store.dispatch'user/setSubscription', status.


  localStorage.setItem'userSubscription', status.
 toggleFeatureFlag: flagName, enabled => {
   // Update internal feature flag state


  // This is ideal for A/B testing or feature rollouts


  window.appConfig.featureFlags = enabled.

Cypress.Commands.add’triggerAppWebSocketMessage’, messageType, payload => {
if app && app.triggerWebSocketMessage {

  app.triggerWebSocketMessagemessageType, payload.

Cypress.Commands.add’setUserSubscriptionStatus’, status => {
if app && app.setUserSubscriptionStatus {
app.setUserSubscriptionStatusstatus.

Cypress.Commands.add’toggleAppFeatureFlag’, flagName, enabled => {
if app && app.toggleFeatureFlag {
app.toggleFeatureFlagflagName, enabled.

// cypress/e2e/app_action_events.cy.js
describe’Real-time Notifications’, => {
cy.loginViaApp’[email protected]‘, ‘pass123′.
cy.visit’/notifications’.

it’displays a new notification when a message is received’, => {

cy.triggerAppWebSocketMessage'NEW_MESSAGE', { sender: 'Admin', content: 'Important update!' }.


cy.contains'Important update!'.should'be.visible'.


cy.contains'from Admin'.should'be.visible'.


cy.get'.notification-badge'.should'have.text', '1'.

it’shows premium features for subscribed users’, => {
cy.setUserSubscriptionStatus’premium’.

cy.visit'/settings'. // Re-visit or refresh if needed for UI to react


cy.contains'Premium Features Enabled'.should'be.visible'.


cy.get''.should'be.visible'.

it’hides a feature when its flag is off’, => {

cy.toggleAppFeatureFlag'newDashboardLayout', false.
 cy.visit'/dashboard'.


cy.get''.should'not.exist'.


cy.get''.should'be.visible'.

Impact: Directly simulating events or toggling features with app actions is significantly faster and more precise than trying to trigger them through UI interactions. This dramatically reduces the complexity and flakiness of tests that depend on specific, often hard-to-reproduce, asynchronous events. For feature flagging specifically, a survey by Split Software found that companies using feature flags deploy 10x more frequently and have 50% fewer outages. App actions enable robust testing of these flags.

These examples illustrate the versatility and power of Cypress app actions.

By carefully identifying scenarios where bypassing the UI is beneficial, you can construct a highly efficient, reliable, and maintainable end-to-end test suite.

Debugging App Actions: Unveiling the Underpinnings

Even with careful implementation, app actions can sometimes behave unexpectedly.

Effective debugging is crucial to quickly identify and resolve issues.

Given that app actions interact directly with your application’s internals, a slightly different approach is required compared to traditional UI-based debugging.

Common Pitfalls and How to Diagnose Them

  1. window.app or window.app.yourFunction is undefined:

    • Diagnosis: This is the most common issue. Your Cypress test is trying to access window.app before your application has fully loaded and exposed it, or before if window.Cypress has evaluated to true.
    • Solution:
      • Ensure the if window.Cypress check is correctly placed in your application’s entry point and that Cypress truly exists on the window object when Cypress is running it should by default.
      • Add a cy.wait with caution or cy.window.its'app': If your application takes a moment to initialize or expose window.app, cy.window.its'app' will automatically retry until window.app is available.
      • Check your application’s development build: Open your application in a regular browser outside Cypress and manually type window.Cypress and window.app in the browser’s developer console. They should be undefined. Then run Cypress, and in the “Application” tab or console of the Cypress browser, check window.Cypress should be true and window.app should show your exposed object. This confirms whether your app is exposing it correctly.
      • Verify the cypress.config.js or plugins/index.js setup: Ensure there isn’t any misconfiguration preventing the Cypress environment from being detected in your app.
  2. App action function is called, but nothing happens in the UI/state:

    • Diagnosis: The app action itself the function you exposed, e.g., app.login might be flawed. It’s executing, but not correctly manipulating the application’s state or interacting with the backend as expected.
      • Add console.log statements within your app action function: Place console.log'App login called with:', email. and console.log'Login response:', data. directly within your app.login function in your application code.
      • Use the Cypress browser’s developer console: Run your Cypress test, and keep the Cypress browser’s developer tools open. You should see these console.log messages in the console, providing insights into the execution flow and any errors.
      • Check network requests: If your app action involves API calls e.g., fetch, axios, check the “Network” tab in the Cypress browser’s developer tools. Look for the requests initiated by your app action. Are they sending correctly? Are they receiving expected responses e.g., 200 OK, or 400/500 errors?
      • Inspect application state directly: After the app action, use cy.window.its'store'.invoke'getState' for Redux/Vuex or directly inspect localStorage cy.window.its'localStorage'.invoke'getItem', 'authToken' to see if the state has changed as expected.
  3. App action is called, state changes, but UI doesn’t update:

    • Diagnosis: Your app action successfully modifies the underlying data/state, but your UI framework React, Vue, Angular isn’t re-rendering or reacting to these changes. This often happens if the app action is bypassing the framework’s reactive system or state management patterns.
      • Ensure app action uses framework’s state update mechanisms: Instead of directly modifying a variable like this.someProperty = value in a React component, ensure your app action dispatches an action to your Redux store, commits a mutation to your Vuex store, or uses a proper Angular service method that triggers change detection.
      • Trigger change detection Angular specific: If your app action directly modifies a component property, you might need to manually trigger change detection, though this is generally a sign your app action isn’t fully integrated with the framework’s reactive flow. componentRef.changeDetectorRef.detectChanges.
      • Consider cy.reload or cy.visit: If all else fails, a cy.reload after an app action can force the UI to re-render based on the new state. However, this negates some of the speed benefits, so it should be a last resort or used only when a full page refresh is acceptable for the test scenario.

Using Cypress and Browser DevTools for Deeper Inspection

  • Cypress Test Runner’s Command Log:

    • Every cy command, including cy.window, cy.then, and your custom commands, is logged in the Cypress command log. Click on a command to see its snapshot and context. This can help you understand when an app action was attempted.
    • cy.log: Sprinkle cy.log'DEBUG: Value after app action:', value throughout your test code to output messages directly to the Cypress command log, without cluttering the browser console.
  • Cypress Browser Developer Tools:

    • Console: As mentioned, console.log from your app code and Cypress tests is invaluable. Any JavaScript errors from your application will also appear here.
    • Network Tab: Essential for debugging API calls made by your app actions. Check request headers, payloads, responses, and status codes.
    • Sources Tab Breakpoints: This is your most powerful tool.
      • In your application code: Set breakpoints directly within the if window.Cypress block where you expose your app actions, and within the app action functions themselves e.g., app.login.
      • In your commands.js: Set breakpoints within your custom command definitions e.g., inside Cypress.Commands.add'loginViaApp', ... and step through the cy.window.its'app'.then... part.
      • How to set breakpoints: With the Cypress browser open, go to the “Sources” tab. Navigate to your application’s source files often under webpack:// or directly in src/. Find your commands.js file often under no domain or cypress/. Click on the line number to set a breakpoint. When Cypress executes that line, execution will pause.
    • Application Tab: Inspect localStorage, sessionStorage, and cookies. If your app action modifies these, verify the changes here.
    • Components Tab React DevTools, Vue DevTools, Augury for Angular: If you’re modifying state that should cause component re-renders, use these framework-specific extensions to inspect component props and state. This helps diagnose if your app action successfully updated the state, but the component itself isn’t reacting to it.

By systematically applying these debugging techniques, you can efficiently troubleshoot issues with your Cypress app actions, ensuring they perform as intended and contribute effectively to a robust and reliable test suite. Effective debugging can reduce the time spent fixing bugs by up to 50%, according to studies on software development efficiency.

Maintaining and Evolving Your App Actions: Long-Term Strategy

App actions, like any part of your test suite or application code, require ongoing maintenance and thoughtful evolution.

A well-maintained set of app actions contributes significantly to the longevity and stability of your test automation efforts.

Versioning and Documentation

  • Versioning: While you don’t typically “version” app actions like an API, you should treat them with the same level of care.
    • Semantic Naming: Use clear, descriptive names for your app action functions e.g., createUserAndLogin, clearAllProducts, setLoggedInUserStatus.
    • Parameter Consistency: Strive for consistent parameter order and types. If an app action’s signature changes e.g., loginemail, password becomes loginusername, password, rememberMe, update all usages and document the change.
  • Documentation: This is paramount, especially as your team grows.
    • Inline Comments: Use comments within your commands.js file to explain what each custom command does, its parameters, and any preconditions/postconditions.
    • README/Wiki: Maintain a dedicated section in your project’s README.md or internal wiki specifically for Cypress app actions.
      • Purpose: Explain the overall goal of app actions in your project.
      • How to Add New Actions: Provide clear instructions for developers on how to expose new app actions in the application code and how to wrap them in custom commands.
      • Available Actions: List all custom commands related to app actions, their purpose, and expected parameters.
      • Usage Examples: Show simple code snippets for common use cases.
    • Why document? A survey by Axosoft indicated that developers spend up to 60% of their time reading code, emphasizing the need for clear documentation to improve understanding and reduce onboarding time.

Handling Application Changes and Refactoring

Your application will evolve, and so must your app actions.

  • Anticipate Breaking Changes: When refactoring core application logic e.g., authentication, data storage, API schemas, identify which app actions rely on that logic.

  • Decouple App Actions from Implementation Details:

    • If your app.login function directly calls localStorage.setItem'token', ..., and later you switch to sessionStorage or cookies, you’ll need to update app.login.
    • Better: Have your app.login call your application’s internal login service which handles the storage mechanism. This way, if the storage mechanism changes, app.login might not need modification, as the underlying service will handle it.
  • Automated Linting/Type Checking:

    • TypeScript: If using TypeScript, define interfaces for your window.Cypress and window.app objects. This provides compile-time checking, catching issues like misspelled function names or incorrect parameter counts.
    
    
    // cypress/support/index.d.ts or wherever your Cypress types are
    declare global {
      interface Window {
        Cypress?: object. // Cypress is always an object when running tests
        app?: {
    
    
         login: email: string, password: string => Promise<any>.
    
    
         register: username: string, email: string, password: string => Promise<any>.
          clearCart:  => void.
          addItemToCart: item: { id: string. name: string. price: number. quantity: number } => void.
          // Add all your exposed app actions here
    
    
    // This tells TypeScript that window.app is an optional object with these methods.
    *   ESLint: Configure ESLint rules to enforce consistent coding styles for your app action definitions.
    

Balancing App Actions with API/Database Fixtures

While app actions are fantastic for client-side state manipulation, they don’t replace the need for API mocking cy.intercept or direct database seeding cy.task. A mature Cypress test suite often uses a combination:

  • App Actions: Ideal for immediate, in-browser state changes e.g., login, setting a feature flag, pre-populating client-side cart.
  • cy.intercept: For controlling network responses. This is crucial for isolating tests from real backend dependencies, simulating error states, or returning specific data without hitting the actual server.
    // Example: Mocking a product list API call
    cy.intercept’GET’, ‘/api/products’, {
    fixture: ‘products.json’,
    statusCode: 200
    }.as’getProducts’.
    cy.wait’@getProducts’.
    // … test UI with mocked products …
  • cy.task: For persistent data setup and cleanup in your backend database. Use this when you need to ensure data survives browser refreshes or when complex data relationships are involved.

Example Integrated Flow:

  1. cy.task'clearDbAndSeedUsers': Clear the database and seed a few users and products. Backend setup
  2. cy.loginViaApp'[email protected]', 'password': Log in one of the seeded users using an app action client-side authentication state.
  3. cy.visit'/dashboard': Navigate to the page.
  4. cy.intercept'GET', '/api/dashboard-widgets', { fixture: 'dashboard_data.json' }.as'dashboardLoad': Mock specific API calls for dashboard widgets if you want to test how the UI renders without relying on the actual backend response content frontend logic test.
  5. Test UI interactions and assertions.

This layered approach ensures maximum test speed, reliability, and coverage. According to the State of Testing Report 2023, 45% of teams now use a combination of different testing approaches to ensure software quality, highlighting the importance of integrated strategies. By adopting these maintenance strategies, your Cypress app actions will remain a valuable and robust part of your testing infrastructure, supporting your development efforts efficiently.

Performance and Scalability: The ROI of App Actions

The primary allure of Cypress app actions is their ability to significantly boost test performance.

This translates directly into faster feedback cycles for developers, more frequent test runs in CI/CD pipelines, and ultimately, a more efficient development process.

Understanding the return on investment ROI and scalability considerations is key to fully appreciating their value.

The Speed Advantage: Quantifying the Gains

  • Reduced Test Execution Time: This is the most visible benefit. Bypassing UI renders, animations, and network roundtrips for setup and teardown phases can cut test run times dramatically.
    • Login Example: A complex UI login flow might take 5-10 seconds. An app action login can complete in milliseconds. If your test suite has hundreds of tests, each requiring a login, this can save minutes or even hours per full test run. A team running 500 tests, each saving 5 seconds due to app actions, saves 500 * 5 = 2500 seconds or approximately 41 minutes per run.
    • Data Setup: Imagine creating 10 complex data entries via UI. This could take 30-60 seconds. An app action pushing data directly to client-side state or through a quick API call can do it in 1-2 seconds.
  • Faster Feedback Cycles: Shorter test run times mean developers get feedback on their changes much quicker. Instead of waiting an hour for a CI/CD pipeline to complete, they might wait 10-15 minutes. This allows for rapid iteration and reduces the cost of fixing bugs, as issues are caught earlier in the development process. Studies from organizations like IBM and Microsoft consistently show that the cost of fixing a bug increases exponentially the later it is discovered in the software development lifecycle.
  • Increased Confidence: Faster, more reliable tests encourage developers to run them more frequently, leading to higher confidence in the codebase and fewer regressions.

Scalability: How App Actions Support Growth

As your application and test suite grow, the benefits of app actions become even more pronounced.

  • Test Suite Growth: A growing number of tests means cumulative time savings from app actions become substantial. Without them, a large E2E suite can become prohibitively slow, discouraging frequent runs and leading to brittle tests.
  • Developer Productivity: Developers spend less time waiting for tests to finish, freeing them up for actual development. The ability to quickly set up specific scenarios for testing new features is invaluable. A report by Forrester found that improving developer experience can lead to a 20-30% increase in developer productivity.
  • CI/CD Efficiency: Reduced test run times directly impact CI/CD pipeline efficiency. Faster pipelines mean more frequent deployments, supporting continuous delivery initiatives. Cloud infrastructure costs for CI/CD can also be optimized as machines spend less time running tests.

Potential Drawbacks and Mitigations

While powerful, app actions aren’t without considerations:

  • Exposure in Production Security Risk: As mentioned, the absolute priority is to ensure window.app or similar is never exposed in production builds. Using if window.Cypress and robust bundler configurations e.g., Webpack’s DefinePlugin with process.env.NODE_ENV is non-negotiable.
  • Tight Coupling to Application Internals: App actions inherently tie your tests to your application’s internal structure. If internal functions change their signatures or behavior, your app actions and thus your tests will break.
    • Mitigation: Treat app actions as part of your application’s public testing API. Any changes to them should be part of a planned refactoring, just like changes to a public API endpoint. Use TypeScript to catch breaking changes at compile time.
  • Reduced Realistic User Simulation: Over-relying on app actions can lead to a test suite that doesn’t fully mimic a user’s journey. If you bypass too much UI, you risk missing bugs related to layout, styling, accessibility, or complex user interaction sequences.
    • Mitigation: Maintain a balance. Reserve app actions for setting up preconditions and tearing down state. Critical user journeys and UI-centric tests should still be driven through the UI. Aim for a mix of fast setup/teardown with app actions and thorough UI validation where it matters most. For instance, a good strategy might involve 70% of tests using app actions for setup and 30% focusing on full UI flows for core features.
  • Increased Complexity in Application Code for testing: Exposing internal methods specifically for testing adds a layer of complexity to your application’s development setup.
    • Mitigation: Centralize app action exposure in a single, well-named file e.g., src/test-utils.js. Use clear if window.Cypress guards. Make it part of your team’s standard development practices.

In conclusion, Cypress app actions offer a compelling ROI by significantly accelerating your end-to-end tests, fostering faster feedback loops, and supporting the scalability of your test automation efforts.

By understanding their strengths, mitigating their weaknesses, and integrating them thoughtfully into your overall testing strategy, you can unlock a new level of efficiency and reliability in your software delivery pipeline.

Frequently Asked Questions

What are Cypress app actions?

Cypress app actions are a powerful technique that allows your Cypress tests to directly interact with your application’s internal JavaScript methods, functions, or state, bypassing the need to simulate complex UI interactions.

They provide a “backdoor” to manipulate the application’s environment programmatically.

Why should I use Cypress app actions?

You should use Cypress app actions primarily to speed up your test execution, improve test reliability, and simplify test setup/teardown. They allow you to quickly set up specific application states e.g., logged-in user, populated cart, specific data without slow, brittle UI clicks, making your tests faster and less prone to flakiness.

How do I expose application methods for Cypress to use?

You expose application methods by attaching them to the window object in your application’s JavaScript code.

It’s crucial to wrap this exposure in a conditional check like if window.Cypress to ensure these internals are only available when Cypress is running, preventing them from leaking into production builds.

Is it safe to expose application internals on the window object?

Yes, it is safe if done correctly for testing environments only. The key is to use the if window.Cypress check, which ensures your internal methods are only exposed when Cypress is actively running. For production builds, these code blocks should be completely removed by your bundler.

Can Cypress app actions replace all UI interactions in my tests?

No, Cypress app actions should not replace all UI interactions.

While they excel at speeding up setup and teardown, you still need UI-driven tests to verify the actual user interface, visual elements, and critical user journeys to ensure the user experience is as intended. It’s about finding a balance.

What is the difference between cy.window.its'app' and cy.window.thenwin => win.app?

Both retrieve the app property from the window object.

cy.window.its'app' is generally preferred because its automatically retries until the app property is found, which is useful if your application takes a moment to initialize or expose the app object. Context driven testing

cy.window.then executes immediately and would fail if app isn’t present at that exact moment.

How do I define custom Cypress commands for app actions?

You define custom Cypress commands in cypress/support/commands.js or commands.ts. You use Cypress.Commands.addcommandName, callbackFunction to create a new command.

Inside the callbackFunction, you’ll use cy.window.its'app'.thenapp => app.yourAppAction to call your exposed application method.

Can I use app actions for asynchronous operations?

Yes, you can.

If your exposed app action returns a JavaScript Promise e.g., from an async function or an fetch call, Cypress will automatically wait for that Promise to resolve before continuing to the next command in your test chain.

How do app actions help with test isolation?

App actions help with test isolation by allowing you to quickly reset or set up a clean application state e.g., clear a user session, reset a Redux store, delete all items before each test or a suite of tests.

This ensures that each test starts from a known, predictable state and doesn’t depend on the outcome of previous tests.

Can app actions interact with my backend database?

No, Cypress app actions run in the browser context and cannot directly interact with your backend database.

For database interactions like seeding or clearing data, you should use cy.task, which runs Node.js code and allows you to connect to your database.

When should I use cy.task instead of an app action?

You should use cy.task when you need to perform operations that require Node.js access, such as direct database seeding, cleanup, or interacting with backend services. Specflow automated testing tutorial

App actions are for in-browser, client-side application state manipulation.

How do I debug Cypress app actions?

Debugging app actions involves using the Cypress browser’s developer tools.

Use console.log statements within your exposed app action functions, set breakpoints in the “Sources” tab of the browser dev tools, and inspect the “Network” tab if API calls are involved.

Cypress’s command log and cy.log are also helpful.

Are app actions suitable for testing critical user journeys?

While app actions can set up preconditions for critical journeys quickly, the actual steps of a critical user journey e.g., a full checkout flow, or a user onboarding process should still be tested primarily through UI interactions to ensure the entire user experience is validated.

How do I handle app actions in TypeScript?

In TypeScript, you should define a declare global interface in your cypress/support/index.d.ts or similar file to specify the types of your window.Cypress and window.app objects and their methods.

This provides type safety and autocompletion for your app actions.

What are some common pitfalls when using app actions?

Common pitfalls include window.app being undefined due to timing or incorrect exposure, app actions not correctly updating application state due to bypassing framework reactivity, and forgetting to guard app action exposure in production builds.

Can app actions be used for component testing in Cypress?

Yes, if you’re using Cypress Component Testing, app actions can still be useful.

You can expose methods on the window object of the component’s iframe or directly access the component’s internal state/props if your component testing setup allows it, to rapidly set up specific component states for testing. How to debug html

What is the ROI of using Cypress app actions?

The ROI of using Cypress app actions is significant.

It includes drastically reduced test execution times leading to faster feedback, improved developer productivity, more stable test suites, and more efficient CI/CD pipelines, all contributing to faster, more reliable software delivery.

How do app actions affect maintainability of the test suite?

By encapsulating complex setup logic into reusable custom commands, app actions significantly improve test suite maintainability.

Changes to the underlying application logic only require updates in one place the app action definition, rather than across many test files, reducing code duplication and maintenance burden.

Should I combine app actions with API mocking?

Yes, combining app actions with API mocking cy.intercept is a powerful strategy.

App actions handle client-side state, while API mocking controls network responses.

This allows you to test different layers of your application effectively, isolating frontend logic from backend dependencies when desired.

What is the best way to structure app action related code?

The best way is to:

  1. Expose app actions in your application’s main entry file e.g., src/index.js using if window.Cypress for development builds only.
  2. Define custom commands for these app actions in cypress/support/commands.js.
  3. Document them with inline comments and in a project README/wiki. This provides a clear, centralized, and maintainable approach.

Introducing percy visual engine

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *