How do you check the equality of the inner text of a element using cypress? - cypress

I have a div that has another div inside of it and I want to check the equality of the inner text of the div. I have figured out how to do it using the invoke('text') function, but i am wondering if that is the best way. So my question is: how do you check the equality of the inner text of a element using cypress?
it('the channel name should contain be Anakin Skywaler', () => {
//This works but can we be more specific with our selector
cy.get("[data-test-id='Skywalker,Anakin']").should('contain', 'Skywalker,Anakin');
})
it('the channel name should equal Skywalker,Anakin', () => {
cy.get("[data-test-id='Skywalker,Anakin']").find('.channel-name').invoke('text').then((text) => {
expect(text.trim()).equal('Skywalker,Anakin')
});
});
Please ignore the Star War Reference!

I think you can simplify this.
Assuming you have HTML that looks like this:
<div data-test-id="Skywalker,Anakin">
<div class=".channel-name">Skywalker,Anakin</div>
</div>
You can write your assert like this:
cy.get('[data-test-id="Skywalker,Anakin"]').should(
"have.text",
"Skywalker,Anakin"
);
This passed for me and if I modified the HTML to Skywalker,Anakin 1 it failed as you would expect. Cypress uses the have.text to look at what is rendered out so it will not worry about any markup and just see what the result is.
This did not work for trimming. you would need to add a callback to do the trimming.
cy.get('[data-test-id="Skywalker,Anakin"]').should(($div) => {
expect($div.text().trim()).equal("Skywalker,Anakin");
});

You can check if a string is contained somewhere inside the div:
cy.get("[data-test-id='Skywalker,Anakin']").contains('Skywalker,Anakin');
Or, if you need to make sure the div contains only the specified text and nothing else, you can tag on this extra assertion:
cy.get("[data-test-id='Skywalker,Anakin']").contains('Skywalker,Anakin').should((elem) => {
expect(elem.text()).to.equal('Skywalker,Anakin');
});
Explanation:
// Get the data
cy.get("[data-test-id='Skywalker,Anakin']")
// Get the child or children of the previous command that
// contain the text - this command fails if no child
// element is found containing the given text
.contains('Skywalker,Anakin');
// These two lines are explained above
cy.get("[data-test-id='Skywalker,Anakin']")
.contains('Skywalker,Anakin')
// Like a .then(), except the contents are retried until
// all contained assertions are successful or until the
// command times out
.should((elem) => {
// Expect the element's text to exactly equal the
// string, not just contain it
expect(elem.text()).to.equal('Skywalker,Anakin');
});

I think currently this is the best option, because it does not check for contains. I was hoping for a shorter piece of code to do this.
it('the channel name should equal Skywalker,Anakin', () => {
cy.get("[data-test-id='Skywalker,Anakin']").find('.channel-name').invoke('text').then((text) => {
expect(text.trim()).equal('Skywalker,Anakin')
});
});

Following is how you can check exact or partial match for a string in an element:
//matches exact text of result string
cy.get("[data-test-id='Skywalker,Anakin']").should('have.text', 'Skywalker,Anakin');
//matches partial text of result string
cy.get("[data-test-id='Skywalker,Anakin']")
.text()
.then(value => {
cy.log("Text value is :", value);
expect(value).to.include('Anakin');
});
where text() is defined in command.js file as following:
Cypress.Commands.add("text", { prevSubject: true }, (subject, options) => {
return subject.text();
});

Can't believe I didn't see one of the magical cypress .should matches. Also I use typescript cypress which gives great lookups/intellisense.
using should. However, these are exact text matches and may have a lot of spaces
cy.get("[data-test-id='Skywalker,Anakin']")
.should("have.text", "Skywalker,Anakin")
.should("have.attr", "data-test-id","Skywalker,Anakin'")
adding a new command would be better such as shouldHaveTrimmedText
I got it from https://github.com/cypress-io/cypress/issues/3887#issuecomment-522962482
but below is the setup to get it working also typescript with type
lookup
commands.ts
Cypress.Commands.add(
'shouldHaveTrimmedText',
{
prevSubject: true,
},
(subject, equalTo) => {
if (isNaN(equalTo)) {
expect(subject.text().trim().replace(/ +/g, ' ')).to.eq(equalTo);
} else {
expect(parseInt(subject.text())).to.eq(equalTo);
}
return subject;
},
);
cypress/support/index.d.ts
Cypress.Commands.add(
'shouldHaveTrimmedText',
{
prevSubject: true,
},
(subject, equalTo) => {
if (isNaN(equalTo)) {
// removes double spaces and ending spaces, does not remove special characters such as tabs or \n
expect(subject.text().trim().replace(/ +/g, ' ')).to.eq(equalTo);
} else {
expect(parseInt(subject.text())).to.eq(equalTo);
}
return subject;
},
);
tsconfig
{
"types": ["cypress","./cypress/support"]
// or "typeRoots": ... but not both
}
cy.get("[data-test-id='Skywalker,Anakin']")
.shouldHaveTrimmedText("Skywalker,Anakin")

Simple exact matching
cy.get('[data-test-id=Skywalker,Anakin]')
.invoke('text')
.should('equal', 'Skywalker,Anakin')

Related

Cypress - How to make an assertion on element depending on page we are land in?

In my test i have four links. Each of them redirect to the different page. After redirection I have page with breadcrubms. In my test I would like to assert that after redirection the breadcrumbs contain specific text which are defined in fixtures. How can I made it?
You could add a custom Cypress command to validate your breadcrumbs, and drive the behavior based on the url yielded by cy.url().
Cypress.Commands.add('validateBreadcrumbs', () => {
cy.url().then((url) => {
if (url === 'foo') {
// some code
} else if (url === 'bar') {
// some other code
} else {
// some other code
}
});
});
// in a test
it('test', () => {
// code to hit the re-direct
cy.validateBreadcrumbs();
});
That being said, it may just be simpler and more direct to not have an abstracted function, and just validate the text as you normally would after the re-direction.

How to check if an element has 'any' value Cypress?

I am writing some basic cypress tests and I just want to make sure that a table we have has all of the correct columns and that any data is rendering.
I know .contains() checks the element selection AND allows text matching, but there could be all sorts of data in this table, I just want to make sure something renders from the backend.
Any idea how to check if there is any (non-specific) value?
Cheers!
describe('Data Source Table renders properly', () => {
beforeEach(() => {
cy.viewport(1920, 1080)
cy.visit('http://localhost:3000/source')
// Assert we are on the correct page /source
cy.url().should('include', '/source')
})
it('Data Source header exists', () => {
cy.contains('Data Source')
})
it('Data Source table exists, column headers are correct and there is data', () => {
cy.get('table').should('exist')
cy.wait(2000)
cy.get('table').contains('th', 'Ip')
cy.get('table').contains('th', 'Station')
cy.get('table').contains('th', 'Table')
cy.get('table').contains('th', 'Status')
})
})
You are right, .contains() allows text matching. It also allows regex matching which you can apply for your situation.
// this will check all td's will have any text
// here is regex example regexr.com/6jmi6
cy.contains('td', /\w/g).should('be.visible')
This is more concise, IMO less prone to error
cy.get('table thead th')
.each($th => {
expect($th.text()).not.to.be.empty
})
You can remove cy.get('table').should('exist') and cy.wait(2000)

Cypress: Get all elements containing a given string / regex

I'm trying to test a pagination bar with cypress.
I want to assert the number of buttons containing a number only in this bar, and ignore the other buttons (previous page, next page...)
The buttons are looking like this:
<button class="...">33</button>
I first tried this test:
cy.get('.pagination')
.find('button')
.contains(/\d+/)
.should('have.length.gte', 2)
But this gave me a warning about the fact that contains will only return one element, making the "length" test useless.
I also tried many combinations based on filter, the ":contains" jquery keyword, but none worked:
.filter(`:contains('/\d+\')`)
// >> finds nothing
.filter((elt) => { return elt.contains(rx) })
// >> throws 'elt.contains is not a function'
.filter((elt) => { return rx.test(elt.text()) })
// >> throws 'elt.text is not a function'
.filter(() => { return rx.test(Cypress.$(this).text()) })
// filter everything and return nothing, even the buttons containing the text '1'
.filter() with a callback has parameters (index, elt) => {} which means you can use it like this
cy.get('.pagination')
.find('button')
.filter((index, elt) => { return elt.innerText.match(/\d+/) })
.should('have.length.gte', 2)
nextAll() might work in this situation:
cy
.get('.pagination')
.find('button')
.contains(/\d+/)
.nextAll()
.should('have.length.gte', 2);
Another solution might be to distinguish the pagination buttons by something else, like a class, or some html attribute that is unique to them.
You can use an loop through the elements and match the element text and then increment a count variable and then later validate it, something like:
var count =0
cy.get('.pagination').find('button').each(($ele) => {
if(/\d+/.test($ele.text()){
count++
}
})
expect(count).to.be.greaterThan(2)
You can do other things as well like:
Assertions
cy.get('.pagination').find('button').each(($ele) => {
if(/\d+/.test($ele.text()){
expect(+$ele.text().trim()).to.be.a('number')
}
})
Perform Click
cy.get('.pagination').find('button').each(($ele) => {
if(/\d+/.test($ele.text()){
cy.wrap($ele).click()
}
})
Validate Inner text
cy.get('.pagination').find('button').each(($ele) => {
if(/\d+/.test($ele.text()){
cy.wrap($ele).should('have.text', 'sometext')
}
})
nextAll() fails if there's element wrapping the buttons, but you can count the wrappers.
cy.get('.pagination')
.find('button') // presume this is 'Prev' button
.parent()
.nextAll(':not(:contains(Next))')
.should('have.length.gte', 2)
or .nextUntil()
cy.get('.pagination')
.find('button') // presume this is 'Prev' button
.parent()
.nextUntil(':contains(Next)')
.should('have.length.gte', 2)
or .children()
cy.get('.pagination')
.children(':not(:contains(Prev)):not(:contains(Next))')
.should('have.length.gte', 2)
Overall, .filter() is better as it does not assume the HTML structure.

Can I save a text element using Cypress, and then use its text as an input?

I'd like to get an element on my page, save its text, and then later type that same text into a text input element as part of a single spec. type() only accepts strings. Is there an accepted workaround, or is this a case of not knowing the best practice?
cy.get(".navbar-text").then(($user) => {
const user = $user.text;
cy.get(".historySearch").type($user.text);
//.type(user) and .type($user.text) both cause errors on the previous line
});
});
jQuery for text extract is a function, use it with Cypress invoke command
cy.get('.navbar-text.)
.invoke('text')
.then(user => {
cy.get(".historySearch").type(user);
});
or directly like this
cy.get(".navbar-text")
.then($user => {
const user = $user.text()
cy.get(".historySearch").type(user)
});
You can also use aliases .as() to save the inner text value and use it later.
cy.get('.navbar-text').invoke('text').as('navbarText')
cy.get('#navbarText').then((text) => {
cy.get('.historySearch').type(text);
})

How to use values from DOM in cypress test?

if I have a page containing:
<span data-testid="credit-balance">
10
</span>
In Cypress, how do I extract the value to a variable to use in tests?
Something along the lines of:
const creditBalance = cy.get('[data-testid="credit-balance"]').value();
Assigning return values with const, var, and let is considered an anti pattern while using Cypress.
However, when you find yourself wanting to do so, is best practice to accomplish this with closures.
it("uses closures to reference dom element", () => {
cy.get("[data-testid=credit-balance]").then(($span) => {
// $span is the object that the previous command yielded
const creditBalance = $span.text();
cy.log(creditBalance);
})
});
Another way to do this would be to use Aliases if you want to store and compare values or share values between tests using hooks.
it("aliasing the value from dom element", () => {
cy.get("[data-testid=credit-balance]").as("creditBalance")
cy.get("#creditBalance").should("contain", 10)
});
How you approach this really depends on the objective of your test. I recommend checking out more examples from the documentation: try Variables and Aliases , Best Practices, and FAQ
If you would like to retrieve the value and perform any assertions with it, a fast, efficient method would also be the use .invoke
it('Getting the value and performing an assertion', () =>{
cy.get('selector').invoke('val').should('eq',10)
})
Doc
The Cypress documentation has an example how to Compare text values of two elements
// will keep text from title element
let titleText
cy.get('.company-details')
.find('.title')
.then(($title) => {
// save text from the first element
titleText = $title.text(); //original uses normalizeText($title.text())
})
cy.get('.company-details')
.find('.identifier')
.should(($identifier) => {
// we can massage text before comparing
const idText = $identifier.text(); //original uses normalizeText($identifier.text())
// text from the title element should already be set
expect(idText, 'ID').to.equal(titleText)
})
If you have to get the value rather than text, use this. It worked for me.
<span data-testid="credit-balance" value='100'></span>
Like above
cy.get('[data-testid="credit-balance"]')
.invoke("val")
.then(($amount) => {
// $span is the object that the previous command yielded
cy.log($amount);
});

Resources