skip to Main Content

I have a layout with many blocks. Each block has a hamberger menu button can trigger the menu open. The class is burger-menu-button. In each menu, there is a button, text is ‘remove’ and class is ‘burger-menu-remove-btn’. When click the remove button, it will trigger a modal with class ‘modal-confirm’.

I’m writing a script to trigger all blocks remove. I use .each to run each one and trigger the click, but when my second burger menu open, it will block the first one modal open. So I can only get the last one removed.

My script is:

cy.get("body").then((body) => {
    cy.get(".burger-menu").each((menu) => {
        cy.get(menu).within((menuIn) => {
            cy.get("burger-menu-button")
                .click()
                .then(() => {
                    const menuItemRemove = menuIn.find(".burger-menu-remove-btn")
                    if (menuItemRemove) {
                        menuItemRemove.trigger("click")
                        const modalConfirmBtn = body.find("button:contains('confirm')")
                        if (modalConfirmBtn) {
                            modalConfirmBtn.trigger("click")
                        }
                    }
                })
        })
    })
})

Since for my hamber menu and modal, when I click outside of the element, it will hide. With my script running, it’s like it cannot run one by one, they’re running at the same time, so some clicks will affect the modal and burger menu open and I can only remove the last one.

How can I ensure it run one by one so once one is removed, it runs another. The click between won’t affect each others.

2

Answers


  1. To ensure that the actions run in the desired sequence, you can use Cypress’s .each() loop combined with .then(). Here’s how you can modify your script to achieve that:

    cy.get(".burger-menu").each((menu) => {
      cy.wrap(menu).within((menuIn) => {
        cy.get(".burger-menu-button")
          .click()
          .then(() => {
            cy.get(menuIn).find(".burger-menu-remove-btn").click()
          })
          .then(() => {
            cy.get("body").find("button:contains('confirm')").click()
          })
      })
    })
    

    In this modified script:

    1. The .each() loop iterates through each .burger-menu element.
    2. cy.wrap(menu) wraps the current menu element, allowing you to work within its context.
    3. The .burger-menu-button is clicked to open the menu.
    4. After clicking the .burger-menu-button, the removal button .burger-menu-remove-btn is clicked.
    5. The .then() block ensures that the modal confirm button is clicked after the removal button action completes.

    By chaining the .then() blocks, you ensure that each action completes before moving on to the next one, allowing for sequential execution of the actions within each .burger-menu element.

    Login or Signup to reply.
  2. When trying to apply a loop in a test you need to "throttle" the test code when you trigger actions (like click open a menu or click to dismiss a modal).

    This is so that one iteration of the loop will not over-run the next one -from your description this is happening to you.

    Throttling

    Here I have added a couple of commands that will throttle the code, i.e wait until a condition on the page has been met.

    They are only examples, since I don’t know the full application.

    cy.get('.burger-menu').each((menu) => {
    
      cy.wrap(menu).within((menuIn) => {
        cy.get('burger-menu-button').click()
    
        // 1st waiting point - menu items should be visible
        cy.get('.burger-menu-item').should('be.visible')
    
        cy.get('.burger-menu-remove-btn').click()
    
        cy.contains('button', 'confirm').click()
    
        // 2nd waiting point
        cy.get('.modal').should('not.be.visible')
      })
    })
    

    Conditionals

    I notice you have a couple if jQuery + if() which make me think that not all burger-menus have the button.

    Please note, if() statements can put a spanner in the works when they follow an action.

    For example menuItemRemove = menuIn.find(".burger-menu-remove-btn") in your code, if the preceding click() causes the page to change, you have no insurance the the test will find the .burger-menu-remove-btn on first try.

    Using a command cy.get('.burger-menu-remove-btn') gives you insurance, because it retries – but you must know that the menu has the button, else the test fails.

    To do that, you would select only menus that have the button, for example

    cy.get('.burger-menu:has(.burger-menu-remove-btn)').each((menu) => {
      // only process the menus with a remove button
    

    Refs: jQuery has-selector

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search