If/else condition in Cypress not working as expected - javascript

I have this code
Cypress.Commands.add('VerifyLoginState', () => {
if(cy.contains('Login')) {
cy.get('.form-control').eq(0).type('firstfield')
cy.get('.form-control').eq(1).type('secondfield')
cy.get('.btn-login').click()
cy.wait(2500)
cy.contains('Upcoming Appointments').should('be.visible')
}
else
{
cy.contains('Appointment summary').should('be.visible')
}
})
How should I write the code so that it can pass to the condition of else, when I am authenticated in the browser and the condition of if is not valid?
In other words, I want to check if an element is present on the page, and even if it is not present, the code should not give an error and move on

Cypress yields the result of cy functions, and does not return them. So your if/else will not work as it would in traditional JavaScript. Check out this article from Cypress about conditional testing.
Something like the following should help you out:
// Get the body of the DOM
cy.get('body').then(($body) => {
// Check if the body contains the `Login` element
if ($body.contains('Login').length) {
cy.get('.form-control').eq(0).type('firstfield')
cy.get('.form-control').eq(1).type('secondfield')
cy.get('.btn-login').click()
cy.wait(2500)
cy.contains('Upcoming Appointments').should('be.visible')
} else {
cy.contains('Appointment summary').should('be.visible')
}

Another option is to use within
ref: https://docs.cypress.io/api/commands/within#Syntax
cy.contains('Login')
.within(() => {
cy.get('.form-control').eq(0).type('firstfield')
cy.get('.form-control').eq(1).type('secondfield')
cy.get('.btn-login').click()
cy.wait(2500)
cy.contains('Upcoming Appointments').should('be.visible')
})

You may find it easier to test for either/or text using jQuery :contains and multiple selectors separated by a comma
cy.get('body')
.children(':contains(Login), :contains(Appointment summary)') // only one is on the page
.invoke('text')
.then(labelText => {
if (labelText.trim() === 'Login') {
cy.get('.form-control').eq(0).type('firstfield')
... etc
} else {
cy.contains('Appointment summary').should('be.visible')
}
})

Related

Cypress cy.get returns HTML object, but if I add .then(elem) to the cy.get function elem is an empty HTML object

Here is my test:
it('tests', () => {
cy.log(cy.get('body'))
cy.get('body').then((body) => {
cy.log(body)
})
})
And the output:
1 get body
2 log Object{5}
3 get body
4 log {}
So cy.get('body') returns an html element with 5 child elements, however when I add the .then(body) to the cy.get, body is empty. I want to be able to save the body as a variable and continue using the .find jQuery function on the body variable. Anyone know what may be happening here?
UPDATED
What I really want to do is add a conditional. So if an element is found then do something, otherwise do something else. And the above seems to be a blocker to this. So something like:
cy.get('body').then((body) => {
if (cy.wrap(body).find('.dashboard')) {
cy.log('found')
} else {
cy.log('not found')
}
})
But in this case I get this error:
cy.find() failed because it requires a DOM element.
The subject received was:
> {}
The previous command that ran was:
> cy.wrap()
All 2 subject validations failed on this subject.
If you want to use find() you can use it directly use it inside then() -
cy.get('body', {timeout: 6000}).then(($body) => {
if ($body.find('.dashboard').length > 0) {
cy.log('found')
} else {
cy.log('not found')
}
})

How .elements() works in nightwatch?

Considering I have multiple trs I want to pick one and edit value in it by clicking edit button bounded to it.
I'm doing something like this:
browser.elements('css selector', '#someId tr', elements => {
elements.value.forEach(val => {
console.log(val)
}
And I get something like this:
{
'abcd-1234-qwer-0987': 'some id'
},
{
'abcd-1234-qwer-0987': 'some other id'
}
What is exactly 'abcd-1234-qwer-0987', is this session id kind of stuff and does it change?
What is the best way to grab particular element? Because as I guess my approach is wrong: elements.value[1]['abcd-1234-qwer-0987']
When I run browser.elements and log the entries in the elements.value array, I get something like { ELEMENT: '5' }. So, to answer your questions:
What is exactly 'abcd-1234-qwer-0987', is this session id kind of stuff and does it change?
Probably some sort of session ID, but I'm not sure. Whatever it is, you don't need it for what you're doing.
What is the best way to grab particular element?
If I'm trying to select one out of several similar elements, my code ends up looking something like this:
browser.elements('css selector', '#someId', result => {
// index is defined somewhere else
browser.elementIdClick(result.value[index].ELEMENT, () => {
// whatever you want to do after you click the element.
});
});
If this doesn't work for you, can you share what OS and browser you are running on, as well as your nightwatch configuration file?
Its return the elements items are Web Element json objects. that a Web Element ID
https://nightwatchjs.org/api/commands/#elements
1.What is exactly 'abcd-1234-qwer-0987', is this session id kind of stuff and does it change?
yes it will change according to Session
2.what is the best way to grab particular element
refer the below
Using Nightwatch, how do you get the DOM element, and it's attributes from the ELEMENT entity?
browser.elements('css selector', '#someId tr', elements => {
console.log(elements);
elements.value.forEach(element =>{
let key = Object.keys(element)[0];
let ElementIDvalue = element[key];
console.log("key = ", key);
console.log("WebElementIDvalue = ", ElementIDvalue);
})
});
All of the documentation I've found around what you're trying to do says I should be able to use obj.ELEMENT but what they don't say is that it requires setting w3c to false in your nightwatch config.
Example with context:
test_settings: {
default: {
end_session_on_fail: false,
disable_error_log: false,
launch_url: 'http://localhost:3000',
desiredCapabilities : {
browserName : 'chrome',
'goog:chromeOptions' : {
// More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/
//
// w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78)
w3c: true,
args: [
'--auto-open-devtools-for-tabs',
//'--no-sandbox',
. . .
Here is an example of how I use it:
.elements('css selector', `${targetNodeSelector} #nodeCard`, nodeContents => {
console.log(nodeContents.value)
nodeContents.value.forEach(nodeContent => {
console.log(nodeContent.ELEMENT)
// browser.elementIdText(nodeContent.ELEMENT, function (result) {
// console.log('\n' + result.value)
// })
})
})
This will display the correct keys/values:
[
{ ELEMENT: '0.944080972569816-108' },
{ ELEMENT: '0.944080972569816-109' },
{ ELEMENT: '0.944080972569816-110' },

How to check if element exists using Cypress.io

How to check if element is present or not, so that certain steps can be performed if element is present. Else certain different steps can be performed if element is not present.
I tried something like below but it didn't work:
Cypress.Commands.add('deleteSometheingFunction', () => {
cy.get('body').then($body => {
if ($body.find(selectors.ruleCard).length) {
let count = 0;
cy.get(selectors.ruleCard)
.each(() => count++)
.then(() => {
while (count-- > 0) {
cy.get('body')
// ...
// ...
}
});
}
});
});
I am looking for a simple solution, which can be incorporated with simple javascript
if else block or then() section of the promise
Something similar to Webdriver protocol's below implementions:
driver.findElements(By.yourLocator).size() > 0
check for presenece of element in wait
Kindly advise. Thanks
I'll just add that if you decide to do if condition by checking the .length property of cy.find command, you need to respect the asynchronous nature of cypress.
Example:
Following condition evaluates as false despite appDrawerOpener button exists
if (cy.find("button[data-cy=appDrawerOpener]").length > 0) //evaluates as false
But this one evaluates as true because $body variable is already resolved as you're in .then() part of the promise:
cy.get("body").then($body => {
if ($body.find("button[data-cy=appDrawerOpener]").length > 0) {
//evaluates as true
}
});
Read more in Cypress documentation on conditional testing
it has been questioned before: Conditional statement in cypress
Thus you can basically try this:
cy.get('header').then(($a) => {
if ($a.text().includes('Account')) {
cy.contains('Account')
.click({force:true})
} else if ($a.text().includes('Sign')) {
cy.contains('Sign In')
.click({force:true})
} else {
cy.get('.navUser-item--account .navUser-action').click({force:true})
}
})
cypress all steps are async ,, so that you should make common function in commands file or page object file,,..
export function checkIfEleExists(ele){
return new Promise((resolve,reject)=>{
/// here if ele exists or not
cy.get('body').find( ele ).its('length').then(res=>{
if(res > 0){
//// do task that you want to perform
cy.get(ele).select('100').wait(2000);
resolve();
}else{
reject();
}
});
})
}
// here check if select[aria-label="rows per page"] exists
cy.checkIfEleExists('select[aria-label="rows per page"]')
.then(e=>{
//// now do what if that element is in ,,..
})
.catch(e=>{
////// if not exists...
})
I found a solution, hope it helps!
You can use this:
cy.window().then((win) => {
const identifiedElement = win.document.querySelector(element)
cy.log('Object value = ' + identifiedElement)
});
You can add this to your commands.js file in Cypress
Cypress.Commands.add('isElementExist', (element) => {
cy.window().then((win) => {
const identifiedElement = win.document.querySelector(element)
cy.log('Object value = ' + identifiedElement)
});
})
Cypress official document has offered a solution addressing the exact issue.
How to check Element existence
// click the button causing the new
// elements to appear
cy.get('button').click()
cy.get('body')
.then(($body) => {
// synchronously query from body
// to find which element was created
if ($body.find('input').length) {
// input was found, do something else here
return 'input'
}
// else assume it was textarea
return 'textarea'
})
.then((selector) => {
// selector is a string that represents
// the selector we could use to find it
cy.get(selector).type(`found the element by selector ${selector}`)
})
For me the following command is working for testing a VS code extension inside Code server:
Cypress.Commands.add('elementExists', (selector) => {
return cy.window().then($window => $window.document.querySelector(selector));
});
And I'm using it like this in my E2E test for a Code Server extension:
cy.visit("http://localhost:8080");
cy.wait(10000); // just an example here, better use iframe loaded & Promise.all
cy.elementExists("a[title='Yes, I trust the authors']").then((confirmBtn) => {
if(confirmBtn) {
cy.wrap(confirmBtn).click();
}
});
Just ensure that you're calling this check once everything is loaded.
If you're using Tyepscript, add the following to your global type definitions:
declare global {
namespace Cypress {
interface Chainable<Subject> {
/**
* Check if element exits
*
* #example cy.elementExists("#your-id").then($el => 'do something with the element');
*/
elementExists(element: string): Chainable<Subject>
}
}
}
Aside
VS Code server relies heavily on Iframes which can be hard to test. The following blog post will give you an idea - Testing iframes with Cypress.
The above code is needed to dismiss the "trust modal" if it's shown. Once the feature disable-workspace-trust is released it could be disabled as CLI option.
This command throws no error if element does not exist. If it does, it returns the actual element.
cypress/support/commands.js
elementExists(selector) {
cy.get('body').then(($body) => {
if ($body.find(selector).length) {
return cy.get(selector)
} else {
// Throws no error when element not found
assert.isOk('OK', 'Element does not exist.')
}
})
},
Usage:
cy.elementExists('#someSelectorId').then(($element) => {
// your code if element exists
})
In case somebody is looking for a way to use cy.contains to find an element and interact with it based on the result. See this post for more details about conditional testing.
Use case for me was that user is prompted with options, but when there are too many options, an extra click on a 'show more' button needs to be done before the 'desired option' could be clicked.
Command:
Cypress.Commands.add('clickElementWhenFound', (
content: string,
) => {
cy.contains(content)
// prevent previous line assertion from triggering
.should((_) => {})
.then(($element) => {
if (!($element || []).length) {
/** Do something when element was not found */
} else {
cy.contains(content).click();
}
});
});
Usage:
// Click button with 'Submit' text if it exists
cy.clickElementWhenFound('Submit');
Using async/await gives a clean syntax:
const $el = await cy.find("selector")
if ($el.length > 0) {
...
More info here: https://medium.com/#NicholasBoll/cypress-io-using-async-and-await-4034e9bab207
I had the same issue like button can appear in the webpage or not. I fixed it using the below code.
export function clickIfExist(element) {
cy.get('body').then((body) => {
cy.wait(5000).then(() => {
if (body.find(element).length > 0) {
cy.log('Element found, proceeding with test')
cy.get(element).click()
} else {
cy.log('Element not found, skipping test')
}
})
})
}

How to fix; "if-else" statement skips the "if" part and directly goes to the "else if" part?

I need to log in with 5 different roles and check if they have the right tabs to navigate depending on their roles.
But the problem is; it logs in with the 'driver' role and checks both "rides.title" and "chauffeurs.title". Since 'driver' role doesn't include "chauffeurs.title", the test fails.
It skips the "if" part and directly goes to "else if" part.
What I tried:
I tried to test if my loop is working well or not and apparently it's working well.
I deleted the whole if block and pasted the code below and saw that it logged in with every single role one-by-one and checked for "rides.title".
.visit('/planned')
.get('[data-qa="rides.title"')
.should('exist')
.get('[data-qa="logout"]')
.click()
Except the last role; since it doesn't have "rides.title", which is fine.
Here is the full code to review and get more info about what I did so far:
const roles = ["driver", "dispatcher", "provider", "reviewer", "admin"];
it("Navigation items for a single permission", () => {
cy.fixture("users.json").then(users => {
const user = users[Cypress.env("ENVIRONMENT")];
roles.forEach(role => {
cy.login(user[role], Cypress.env("DEFAULT_USER_PASSWORD")).then(
response => {
cy.setCookie("__bl_pp__", response.body.result.access_token);
if (user[role] === "driver") {
cy.visit("/planned")
.get('[data-qa="rides.title"')
.should("exist")
.get('[data-qa="logout"]')
.click();
} else if (["dispatcher", "provider", "reviewer"].includes(user[role])) {
cy.visit("/planned")
.get('[data-qa="rides.title"')
.should("exist")
.get('[data-qa="chauffeurs.title"')
.should("exist")
.get('[data-qa="logout"]')
.click();
} else {
cy.visit("/planned")
.get('[data-qa="chauffeurs.title"')
.should("exist")
.get('[data-qa="logout"]')
.click();
}
})
})
})
})
})
The expected scenario should be; the test should log in with 5 different roles and verify the available tabs, as mentioned in the code above.
Role Navigation Tabs
Driver Rides
Dispatcher Rides, Chauffeurs
Provider Rides, Chauffeurs
Reviewer Rides, Chauffeurs
Admin Chauffeurs
Thanks in advance!
Here is my users.json file:
{
"development":
{
"driver": "aa+driver#b.com",
"dispatcher": "bb+dispatcher#b.com",
"provider": "cc+provider#b.com",
"reviewer": "dd+reviewer#b.com",
"admin": "ee+admin#b.com"
}
}
You should not test
if (user[role] === "driver")
and
if (["dispatcher", "provider", "reviewer"].includes(user[role]))
because user[role] is the fixture object.
You should test
if (role === "driver")
and
if (["dispatcher", "provider", "reviewer"].includes(role))
Not sure if that solves all the problems, but it's an obvious first step.
The problem here is with the if conditional statement. This is not correct syntax for what you want to achieve:
if(user[role] === "dispatcher" || "provider" || "reviewer") {
...
}
It basically means "if the user role is 'dispatcher' or true or true", and can be shortened to "true". This is because any non-empty string is always 'truthy' in js.
You need to repeat the comparison for each of these "or" groups, like this:
if(user[role] === "dispatcher" || user[role] === "provider" || user[role] === "reviewer") {
...
}
Or preferably like this:
if(["dispatcher", "provider", "reviewer"].includes(user[role])) {
...
}
Thanks for your comments! I've fixed this by defining another const to store the value of the users' actual roles which I got from another page, then I used that as a condition.

How to get separate out characters from within a string

I have a string:
string = "abc_test_dashboard.json";
The value of string could vary like:
"tes.test.dashboard.json"
"jhgwefj-gfjh.widget.json"
The last "dashboard.json" and "widget.json" is static and could be either of them depending on a condition.
Basically I'm trying to identify if its "dashboard" or "widget" from the string.
I want to do stuff based on:
if ("dashboard.json") {//do some stuff}
else { // do something else
}
I also just realized that I may have multiple files with same name, and hence I may end up getting (1), (2) suffixes i.e: "abc_test_dashboard(1).json", "abc_test_dashboard(2).json". is there any way to test these kind of scenarios?
Thanks
You can do it with
if(string.endsWith('dashboard.json')) {
}
if(string.endsWith('widget.json')) {
}
Also you can use regex if you want (in case your target browsers do not support endsWith);
if (/widget\.json$/.test('widget.json')) {
}
Using regex you can even extract the initial portion of the file;
var widgetInfo = 'asd.widget.json'.match(/^(.*)widget\.json$/)
if (widgetInfo) {
console.log(widgetInfo[1]) // will print `asd.`
}
// similar code to check for `dashboard.json`
EDIT:
In the case you commented you can use the following regex; /^(.*)widget(\(.+\))?\.json$/. It will match strings in the forms of randomstring.widget.json and randomstring.widget(1).json, but not randomstring.widget().json
If you don't mind RegEx, you can use the String match() method as in the below:
function checkStrEnd(str) {
if (str.match(/dashboard\.json$/)) {
console.log('Do dashboard stuff');
} else if (str.match(/widget\.json$/)) {
console.log('Do widget stuff');
} else {
console.log('Do something else');
}
}
checkStrEnd('tes.test.dashboard.json'); // 'Do dashboard stuff'
checkStrEnd('jhgwefj-gfjh.widget.json'); // 'Do widget stuff'
checkStrEnd('random string'); // 'Do something else'
you can use includes to see if the string exists within the string
let arr = ["tes.test.dashboard.json", "jhgwefj-gfjh.widget.json"]
arr.forEach(item => {
if (item.includes('dashboard.json')) {
console.log('dasboard')
} else if (item.includes('widget.json')) {
console.log('widget')
}
})

Categories

Resources