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')
}
})
})
}
Related
How can i disable the button when waiting for the Promis function sort to be aborted. For now i managed to put here delay it works good when i put like 50 numbers to sort but for bigger ammout of number it doesnt work how can i make it scalable that this functionality works for all dataset sizes:
let isActive = false
let controller = new AbortController()
startButton.addEventListener('click', async () => {
if (! isActive) {
isActive = true
startButton.textContent = 'Stop'
startButton.style.backgroundColor = 'red'
try {
const sort = SelectAlgorithm(data, algorithmType)
await sort(updateBars, controller.signal)
svg.selectAll('rect').style('fill', 'black')
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') {
console.log('Sorting function aborted')
} else {
throw err
}
}
} else {
startButton.disabled = true
isActive = false
startButton.textContent = 'Wait ...'
startButton.style.backgroundColor = 'grey'
startButton.style.cursor = 'not-allowed'
controller.abort()
await delay(500)
startButton.textContent = 'Start'
startButton.style.backgroundColor = 'green'
startButton.disabled = false
startButton.style.cursor = 'pointer'
}
isActive = false
startButton.textContent = 'Start'
startButton.style.backgroundColor = 'green'
controller = new AbortController()
})
You haven't learned how HTML forms work, from the looks of it. You don't attach "click" handlers to buttons if you can avoid it -- in the kind of problem you seem to be solving, at least, you're just making it harder for yourself with that "click" handler.
Instead, use a form and the "submit" event which will ensure your submit button, when disabled, won't even trigger, which is exactly what you want and is something you don't need to program yourself much.
Also don't muck about with changing colours of buttons in JavaScript -- just make sure your button can be targeted with CSS when in another state -- disabled, for instance -- and have a CSS rule targeting it so that when it's disabled its appearance may change automatically.
You should also have separate "Start" and "Stop" buttons because that's far more manageable than having to "mutate" the "Start" button into a "Stop" button -- rest assured CSS can help you hide your [disabled] "Start" button so that things appear as if the "Start" button turned into a "Stop" button, while in reality the former was simply hidden and the second was then accordingly shown.
<form>
<button>Start</button>
<button disabled name="stop" type="button">Stop</button>
</form>
form button:not([type]):disabled {
background-color: red;
}
form.addEventListener("submit", async ev => {
ev.submitter.disabled = true; /// The form won't submit again while its only submit button is disabled
ev.target.elements.stop.disabled = false;
try {
const sort = SelectAlgorithm(data, algorithmType);
await sort(updateBars, controller.signal);
svg.classList.add("sorted"); /// Much better than changing the `fill` with JS -- now you can rely on CSS doing the same thing more efficiently and the way it was designed to
} catch (err) {
if (err instanceof DOMException && err.name == 'AbortError') {
console.log('Sorting function aborted');
} else {
throw err;
}
} finally {
ev.submitter.disabled = false; /// When done
ev.target.elements.stop.disabled = true;
}
});
Also please stop habitually using the strict equality operator, === -- even if you know exactly what it is for and haven't just copied and pasted a snippet much like unfortunately thousands of them using === that must have sprung up from some viral blog post of a code-bro 10 years ago, the strict equality operator not only isn't needed above, it's also an eye-sore and makes reading the code harder (because it's specific) -- == will suffice fine in your and most other use cases of code that isn't some low level library that needs to distinguish null from undefined or accommodate for some other JavaScript idiosyncrasies.
I've seen your kind of code be employed by people over and over again, and it is not a sustainable way to do it. Normally you should always try to contain form elements like buttons inside form elements, you get a lot of useful (assistive, not the least) behaviour for free and don't need to reinvent the wheel. Buttons and other controls that are not part of form elements (or associated with one), are only ever needed in a very narrow subset of [exceptional] use cases.
Ok i manged to do this i dont know it is good way to do this but it works :
...
let sortingPromise: Promise<unknown>
startButton.addEventListener('click', async () => {
if (!isActive) {
...
try {
const sort = SelectAlgorithm(data, algorithmType)
sortingPromise = sort(updateBars, controller.signal)
svg.selectAll('rect').style('fill', 'black')
await sortingPromise
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') {
console.log('Sorting function aborted')
} else {
throw err
}
}
} else {
...
await sortingPromise.catch(() => {}) // wait for the function to abort
...
}
...
})
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')
}
})
I have a code that uses window.foo.abc as a condition to display something.
I want to test this functionality with cypress and I want to mock this value to be false and true.
How can I do that?
I've tried
before(function() {
Cypress.on('window:before:load', win => {
win.foo.abc = true;
});
and
cy.window().then(win => {
window.foo.abc = true;
});
with no success.
How can I mock this value?
thanks 🙏
This code is incorrect,
Cypress.on('window:before:load', win => {
window.foo.abc = true;
});
It should be
Cypress.on('window:before:load', win => {
win.foo.abc = true;
});
You don't have to use it in before(), but it should be at the top of the spec.
But I suspect it still won't work after correcting, most likely the app resets foo to a new object during loading, i.e during cy.visit()
You can use the 2nd block
cy.visit('...') // visit before changing
cy.window().then(win => {
win.foo.abc = true; // correct the syntax here as well
})
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')
}
})
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' },