Validating the state of the slide toggle using Cypress - javascript

I'm just new to Cypress and I want to validate if the slide toggle button is ON or OFF.
I have this piece of code that checks whether it is ON or OFF but I have no idea how to put it in an If-Else condition.
cy.get('#slide-toggle-1')
.find('input')
.filter('#slide-toggle-1')
.should('have.attr', 'aria-checked', 'true')
//What I want to do
If(<the code above is true>) {
cy.get('#dropdown').select('value1')
}
else {
cy.get('#button').click()
}
All comments and suggestions are well appreciated. Thank you.

You can use a jQuery OR selector
cy.get('#slide-toggle-1[aria-checked="true"], #button') // "," in the selector means OR
.eq(0) // if both present take the first
.then(toggleOrButton => {
if (toggleOrButton.attr('id') === 'slide-toggle-1') {
cy.get('#dropdown').select('value1')
} else {
cy.get('#button').click()
}
})
Note this only works for static HTML. If you've just clicked the toggle and it's animating, it would pick the button before the animation completes (but the same applies to other methods using .then() or await).

You can use then, but it gets a bit messier when you have more levels of nesting
cy.get('#slide-toggle-1')
.find('input')
.filter('#slide-toggle-1')
.then((btn)=>{
if (btn.ariaChecked === 'true') {
cy.get('#dropdown').select('value1')
}
else {
cy.get('#button').click()
}
})
You should be able to use await with cypress-promise
import promisify from 'cypress-promise'
const btn = await promisify(
cy.get('#slide-toggle-1')
.find('input')
.filter('#slide-toggle-1')
);
if(btn.ariaChecked === 'true')
if(btn[0].ariaChecked === 'true') // might need [0] as per #eric99's comment

I have already done this using the following code:
cy.get('#slide-toggle-1-input').then(($toggleBtn) => {
if($toggleBtn.attr('aria-checked') === 'true') {
//command here if the toggle button is turned on
}
else {
//command here if the toggle button is turned off
}
})
Also don't use dynamic elements such as my example above, I just use that for easier understanding. Instead, use a regular expression or RegEx for the locators like below.
//example above, don't do this
cy.get('#slide-toggle-1-input')
//USE THIS for dynamic locators
cy.get("[id^='slide-toggle-'][id$='-input']").first()
//the ^ search for properties starting from
//the $ search for properties ending from
Read this for further details on the cy.get() and dynamic locators: https://docs.cypress.io/api/commands/get#Selector
Hope it helps everyone, especially who's just starting on Cypress like me! :)

Related

PLAYWRIGHT testing - How to skip to the next action if a click on a button is not possible (is not present)?

as anticipated in the question, I'd like to know if is possible to attempt a click action on a button, but if it's not in the page then the test skip that action (without being stucked or throwing an error) and continue to the next one.
To be more specific, I have a modal where I can add rows to a table, but what I'd like to do is to add a new row only if a given button in the modal is not present, while if it's present the action would be to press first on that button and then proceeding to add a new row.
I ask you if it's possible because I'd like to avoid conditional statements during the test. Just let me know, thank you in advance :)
You can check if the element is present then do something if not then do nothing or something else.
const modal = page.locator('modal-selector');
if (await modal.isVisible()) {
// do thing
} else {
// do another thing
}
const locator = page.locator("some selector");
if (locator.count() > 0) {
// found locator, do something
} else {
// not found, do something else
}

How to remove class from body if condition is not met?

I'm working with an if statement to show an alert message depending on certain conditions.
Under the second condition New Article I want a specific css class to be added (show-banner-alert). I am able to get the css added but its now affecting the conditions following it.
I've tried adding a $("body").removeClass and adding a new class with different properties but I am unable to get rid of the first-class successfully.
Here is my code:
$(window).on("load", function(e) {
$('.preloader').fadeOut('slow');
if (document.cookie.includes("isDismissed=true")) {
document.body.classList.add("alert.container");
} else if ("#pageTitle" === ("New Article") !== "-1") {
document.body.classList.add("show-banner-alert");
} else if ("#pageTitle".indexOf("Test") !== "-1") {
$("body").removeClass("show-banner-alert").addClass("show-alert");
} else if ("#pageTitle".indexOf("New Home") !== "-1") {
document.body.classList.add("show-alert");
}
});
first :
none of the conditions are correct.
all the if conditions are going to give you a correct answer ( true ).
second :
try either using jquery or using vanilla js for dom manipulation. ( I'd recommend jquery).

Cypress - how to find by text content?

In Cypress, I want to select a button from a group of buttons based on its text-content. How can I do it? Here is my approach:
export const getCustomerButton = () => getNavigationSidenav()
.find('mat-expansion-panel-header')
.each(($el, index, $list) => {
const text = $el.find('.mat-content > mat-panel-title').text();
if (text === 'Customer') {
return $el;
}
return null;
});
The problem I have now is that I have to filter out the nulls from the element array. Is there a less complicated way?
This code will yield the DOM element with YOUR_BUTTON_CLASS which contains text 'Customer'. Is that what you're looking for?
cy.get('.YOUR_BUTTON_CLASS').contains('Customer');
Here the documentation for .contains cypress command.
Or maybe an even slicker solution is to use this:
cy.contains('YOUR_BUTTON_CLASS', 'Customer');
This can be done since contains() can hold 2 arguments. And if it gets two arguments the first one is always the element and the second the text.
Another option that's not mentioned in the previous answers here.
Use testing-library/cypress-testing-library
After the installation, just import it in cypress' commands.js:
import '#testing-library/cypress/add-commands'
And in your tests
cy.findAllByText("Jackie Chan").click();
cy.findByText("Button Text").should("exist");
cy.findByText("Non-existing Button Text").should("not.exist");
cy.findByLabelText("Label text", { timeout: 7000 }).should("exist");
cy.get("form").within(() => {
cy.findByText("Button Text").should("exist");
});
cy.get("form").then((subject) => {
cy.findByText("Button Text", { container: subject }).should("exist");
});
This is pretty straightforward and easy to use. We use this in our production site along with react testing library. Highly recommend :)
The accepted answer "can" work. However: if the element is not visible on the first try, the element cannot be found in subsequent retries.
See: https://github.com/cypress-io/cypress/issues/3745
Cypress uses "Sizzle" as selector library - so this:
cy.get('button:contains("FooBar")')
would work in retries.
There are multiple ways to do that
Syntaxes:
cy.contains(content)
cy.contains(content, options)
cy.contains(selector, content)
cy.contains(selector, content, options)
Examples:
cy.contains('button', 'Customer')
cy.contains('.buttonClass', 'Customer')
cy.get('button:contains("Customer")')
cy.contains('Customer')
The simplest way to do it would be :
cy.get('Button_Class').contains('Button_Text')
In your case, the solution would be :
cy.get('.mat-content > mat-panel-title').contains('Customer')
There is a documentation for this here.

Is there a way to update a Slate change after the current change completes?

Basically I want to make inlines that can be updated from the end if the cursor/selection is there. However it seems to be overriding changes like deleting from the end an inline.
I was attempting to do something like this (with guidance from slate-sticky-inline)
JSFiddle or what I think is the relevant code below
const onChange = change => {
//Not in a inline but at the start of some node
if (!change.value.focusInline && change.value.selection.focusOffset === 0) {
//Find the index of the node
const textNodeIndex = change.value.focusBlock.nodes.findIndex(
node => node.key === change.value.focusText.key
);
//check the node before it to see if its an inline
const upcomingNode = change.value.focusBlock.nodes.get(textNodeIndex - 1);
if (Inline.isInline(upcomingNode)) {
//put me at the end of the inline
return change.extendToEndOf(upcomingNode);
}
}
};
I was hoping someone could help me understand whats going wrong here and possibly explain what I am misunderstanding. I also created a barebones jsfiddle with my plugin. You'll notice if you attempt to erase the "p" at the end of "#help" it will prevent you from doing so.
You forgot to this.setState({value: change.value}) in onChange function.
Slate Editor shall be considered as a controlled component.

Cannot switch between classes with jQuery

I've got a problem with switching between element classes - probably sth stupid, but I couldn't find the answer.
In my system I display a list of items. Now I want to be able to promote items, so that they appear at the top of the list. I created some backend infrastructure which works ok and added things to my frontend: a star (a span with star bg) next to every item's title and a jQuery script which is supposed to:
listen to 'click' event - when I click on a star
get some data- attributes from the span
post them to my controller
the controller checks if I'm allowed to promote items and replies 'true' or 'false'
if 'true' then I switch between 'gold-star' and 'silver-star' classes of the item
For some reason the classes don't switch - only when I refresh the page I can see the effect. I tried debugging with Firebug - it gets to the toggle line, but then nothing happens.
Here's the code:
<span class="silver-star promote">
$(".promote").bind("click", function() {
var $this = $(this);
var itemId = $this.attr("data-id"),
isPromoted = true;
if ($this.hasClass("gold-star")) {
isPromoted = false;
}
$.post('/promoteitems', { itemId: itemId, isPromoted: isPromoted }, function(allowPromotion) {
if (allowPromotion == true) {
$this.toggleClass("silver-star").toggleClass("gold-star");
}
});
});
Thanks in advance!
When you are getting a response back it might not recognise it as a boolean simple test would be to check response as string
From your comment on the question:
...the allowPromotion value is 'True' (with a capital T)...
That tell us it's a string, not a boolean. You don't want to just do if (allowPromotion), because that will toggle the classes even if you get back "False".
Instead:
if (allowPromotion == "True") { // Note the quotes and capital T
// ...toggle the classes
}
Or if you want to allow for possibly getting back something with a lower-case T at some point in the future:
if (/^\s*true\s*$/i.test(allowPromotion)) {
// ...toggle the classes
}
That's over-engineering it a bit (it'll work with "True", "true", " True " [note the spaces], and even an actual boolean true)...

Categories