How can I verify which class name in the element? - cypress

I have element. And I need verify which class in this element and do something after that. How can I do it?
cy.get('element')
.should('have.class', 'Enabled')
.log('Enabled')
or
.log('Disabled')

Using a .then() function to yield the element found by cy.get(), and then using the JQuery function hasClass to determine if the yielded element has the Enabled class should work.
cy.get('element').then(($el) => {
cy.log($el.hasClass('Enabled') ? 'Enabled' : 'Disabled');
if ($el.hasClass('Enabled')) {
// some code
} else {
// some other code
}
})

Related

Cypress - How to use if statement with contains

so I have to use cy.contains to find the element I want, but all I can find online is how to use if() with cy.find or cy.get if there a way to do this with contains?
Example code:
if(cy.contains('div.name', 'Test 1').length > 0) {
//Type in name
cy.get('input.newName').click().type('Test 1');
cy.wait(2000);
//Click Add Name
cy.get('div.createNewName> a').click();
cy.wait(2000);
}
What I am trying to do there is:
if(Name doesnt exist){
Create it
}
I'm not sure if I have explained myself too well, if any more clarifications are needed feel free to ask
You can also do like this:
cy.get('body').then(($body) => {
if ($body.find('div.name:contains("Test 1")').length > 0) {
//Element Found
} else {
//Element not found
}
})
The general pattern for this would be as follows:
const element = Cypress.$('div.name:contains(Test 1)')
if (element.length > 0) {
...
Make sure the DOM is stable when you run this code, there is no retry built in as there is with cy.contains()
If the code inside if() is creating the name, then maybe the logic would be
const element = Cypress.$('div.name:contains(Test 1)')
if (element.length === 0) {
// not found so create it
...
You can also do it like this
cy.get('div.name').then($div => {
const found = $div.find(':contains("Test 1")')
if (found.length === 0) {
// create...
}
})

Cypress get attribute value and assign it to variable in function

I am trying to get attribute value and return it from a function.
Here is the code that is working and can be used in a normal test class (into the integration folder).
describe('Example shows how to get attribute value.', () => {
// 'it' is used to create test case. You can add a name of the test case. You can have multiple test cases in one JS class.
it('Get attribute value.', () => {
// Cypress is not able to work with new tabs. It is not possible to switch between tabs. Cypress can manipulate the DOM tree, so we can change the element attributes and open the hyperlink in the same browser tab.
// 'visit()' method is used for navigating to URL address.
cy.visit('https://demoqa.com/links')
cy.xpath('//*[#id="simpleLink"]').then(function (element) {
// 'prop()' method is used to get the attribute value.
const url = element.prop('href')
cy.visit(url)
})
// Assert URL.
cy.url().should('include', 'demoqa.com').should('eq', 'https://demoqa.com/');
})
If I use the code that way - everything is working as expected.
But if I want to re-use the code and create a function like this:
// Give a value of the variable to use it for next function.
functionName = 'addAttribute';
// Declare a Cypress child custom command.
Cypress.Commands.add(functionName, { prevSubject: 'element' }, (subject: any, attributeName: string, attributeValue: string) => {
// Create a try-catch statement. If the function fails - we will recieve the error message.
try {
// Create the function steps after this comment.
cy
.wrap(subject)
.invoke('attr', attributeName, attributeValue)
.should('have.attr', attributeName, attributeValue)
} catch (error) {
// Create the error log and show it to the UI. Show the function name, the class where the function is located and catched error.
let errorMessage = `----------ERROR! It seems that we have an error. Please review the "${functionName}" function from "${__filename.split(__dirname + "/").pop()}" . The error is: ${error}`;
cy.log(errorMessage);
console.log(errorMessage);
}
})
The result is 'object' and I am not sure how to process it.
Here is the rest of the code:
describe("'getAttribute' custom child command example.", () => {
it("example shows how to use 'getAttribute' custom child command.", () => {
cy.visit('https://demoqa.com/buttons');
let attributeValue = cy.element('xpath','(//*[contains(text(),"Click Me")])[3]').getAttribute('class');
cy.log(`The attribute values is: ${attributeValue}`)
});
});
You have to do as below to have a return value:
let attributeValue = '';
cy.element('xpath', '(//*[contains(text(),"Click Me")])[3]')
.getAttribute('class')
.then((attr) => {
attributeValue = attr;
});
cy.log('The attribute values is:' + attributeValue)

How to override 'data-testid' in the 'findByTestId function from Cypress Testing Library

Most of my existing codebase uses a 'id' only in few places 'data-testId' attribute present.
tried this code
import { configure } from '#testing-library/cypress';
configure({ testIdAttribute: ['data-testId','id'] });
But, still its not working.
Is there any way to use 'id' value in any of the testing-library functions.
My HTML code is something like:
<div class="some random class name" id="userprofile-open" role="button">SB</div>
I want click that element with this code:
cy.findByTestId("userprofile-open", { timeout: 120000 }).click();
I don't think you can configure testing-library with an array of ids, ref API configuration,
import { configure } from '#testing-library/cypress'
configure({ testIdAttribute: 'id' })
But even this fails. Instead you have to use the Cypress command to change the attribute name (only one name is allowed).
cy.configureCypressTestingLibrary({ testIdAttribute: 'id' })
To use either/or attribute name you can change the attribute name on the fly, wrapping it in a custom command (based on Custom Queries)
Cypress.Commands.add('findByTestIdOrId', (idToFind) => {
let result;
const { queryHelpers } = require('#testing-library/dom');
let queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'data-testId');
result = queryAllByTestId(Cypress.$('body')[0], idToFind)
if (result.length) return result;
queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'id');
result = queryAllByTestId(Cypress.$('body')[0], idToFind);
if (result.length) return result;
throw `Unable to find an element by: [data-test-id="${idToFind}"] or [id="${idToFind}"]`
})
cy.findByTestIdOrId('my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
Note this custom command works only for synchronous DOM.
If you need to have Cypress retry and search for either/or attribute, don't use testing-library in the custom command.
Instead use Cypress .should() to enable retry
Cypress.Commands.add('findByTestIdOrId', (selector, idToFind) => {
cy.get(selector)
.should('satisfy', $els => {
const attrs = [...$els].reduce((acc, el) => {
const id = el.id || el.getAttribute('data-test-id') // either/or attribute
if (id) {
acc.push(id)
}
return acc
}, [])
return attrs.some(attr => attr === idToFind); // retries when false
})
.first(); // may be more than one
})
cy.findByTestIdOrId('div', 'my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
The usual cypress way - which has an inherent check on the element visibility and existence as well as included retries for a period of time is using cy.get()
If you want to select element using property like data-id you need this sintax: cy.get('[propertyName="propertyValue"]')
If you want select an element by CSS selector you just pass CSS selector like this:
cy.get('#id')

cypress: How can manage the application flow, if the element xpath is not present

I have the following scenario:
if the element is present, i have to do one activity and if not present will do another activity.
cy.xpath("//div[text()= 'button').its('length').then(res=> {
if (res > 0) {
return 1;
}
else {
cy.log ("Element is not present")
}
}
)} '''
if element is present = Code is working fine,
if the element xpath is not present = it try to search the element xpath (//div[text()= 'button') and throwing the error as 'Timed out retrying: Expected to find element: undefined, but never found it.'
if element is not present, Is there any way, i can handle the code ,
When using xpath you can (sort of) make it conditional by wrapping the xpath selector with count().
cy.xpath("count(//div[text()= 'button'])") // ok with async content
.then(count => {
if (count) {
//return 1; // not useful, returns a "chainer"
// ...but you can perform the required test here, e.g
cy.xpath("//div[text()= 'button']").click()
} else {
cy.log('not found')
}
})
The shorter syntax using built-in jQuery might be
const exists = !!Cypress.$("div:contains('button')").length
if (exists) {
cy.xpath("//div[text()= 'button']").click()
} else {
cy.log('not found')
}
Note that this is a partial match to 'button', where-as the xpath syntax is an exact match.
Also note - using Cypress.$ by-passes retry-ability, so it should not be used where the text is asynchronous.
From docs
This is a great way to synchronously query for elements when debugging from Developer Tools.
The implication is that it's more for debugging after the page has loaded.
The best practice is to try to construct the test and the app's data in such a way that you know that the button is present.
You can do something like this. With Cypress.$, you can validate the presence of the element with the help of the length attribute and then do further actions.
cy.get('body').then($body => {
const ele = $body.find('selector');
if (Cypress.$(ele).length == 0) {
//Do Something element is not present
}
else if (Cypress.$(ele).length > 0) {
//Do Something when element is present
}
})

CKEditor 5 Find Model Element By Attibute

A custom html dom element exists inside my editor:
<content id="1">
<p>Paragraf is here and link text exists</p>
</content>
I want to find this element in the model using it's attributes. I can find it using a recursive method starting from root. But is there a simpler method like querySelectorAll?
I've not found an out-of-the-box helper method which does this, but if anyone's just looking for a function definition they can use, try this:
function findDescendant (modelElement, predicate) {
if (predicate(modelElement)) {
return modelElement
}
if (modelElement.getChildren) {
for (let child of modelElement.getChildren()) {
const found = findDescendantByName(child, name)
if (found) {
return found
}
}
}
}
...
...
const descendant = findDescendant(element, (element) => element.hasAttribute('foobar'))

Resources