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
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:In this modified script:
.each()
loop iterates through each.burger-menu
element.cy.wrap(menu)
wraps the current menu element, allowing you to work within its context..burger-menu-button
is clicked to open the menu..burger-menu-button
, the removal button.burger-menu-remove-btn
is clicked..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.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.
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 precedingclick()
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
Refs: jQuery has-selector