Open fr0 opened 5 years ago
There's a related feature request here for making cy.contains()
strip newlines: #92
Recently I added a .text()
command to https://github.com/Lakitna/cypress-commands .
.text()
will trim whitespace and provides some whitespace options.
.text() command proposal: https://github.com/cypress-io/cypress/issues/630
It is nice to have the text()
trim the string but is it possible to chain assertions with text()
?
What I did was to add a custom command like:
Cypress.Commands.add(
'shouldHaveTrimmedText',
{
prevSubject: true,
},
(subject, equalTo) => {
if (isNaN(equalTo)) {
expect(subject.text()).to.eq(equalTo);
} else {
expect(parseInt(subject.text())).to.eq(equalTo);
}
return subject;
},
);
And now I can do:
cy
.get('.some-class')
.shouldHaveTrimmedText('whatever');
One way to go about it is:
cy.get('...').should($el => expect($el.text().trim()).to.equal('...'));
It is nice to have the
text()
trim the string but is it possible to chain assertions withtext()
?
Yes, .text()
as it exists in cypress-commands
awaits all its upcoming assertions via the same API as the default Cypress commands. This way it has the same behaviour as commands you're familiar with like .get()
.
cy.get('...')
.text()
.should('equal', 'foo');
I also encountered the same issue so I use:
cy.get('.message').should('contain.text', message)
the same issue as above - cypres 4.x
The problem with contains is it matches any substring.
Given:
<div> Tag1, Tag2 </div>
cy.get('div').should('have.text', 'Tag1, Tag2');
should pass (trims whitespace)
cy.get('div').should('have.text', 'Tag1');
should fail because of the extra text
Given that the cypress matchers are Chai, I'm not sure if we'd have to extend Chai or cypress to implement this.
This seems to work:
cy.get("div").invoke("text").then((text) => text.trim()).should("equal", "Tag1");
I'm not sure how to turn that into a custom command though.
@loren138 Have you seen these posts? https://github.com/cypress-io/cypress/issues/3887#issuecomment-482277997 https://github.com/cypress-io/cypress/issues/3887#issuecomment-533880136
We try to keep the number of external packages as low as possible in our project so we can add our own custom commands but are not likely to use third party command packages.
This seems to work as a slight modification on https://github.com/cypress-io/cypress/issues/3887#issuecomment-522962482 but I don't think it's as flexible since it doesn't have the jQuery helper, but I can't figure out how to use invoke
in the custom command.
Cypress.Commands.add(
"shouldHaveTrimmedText",
{ prevSubject: true },
(subject, equalTo) => {
expect(subject.text().trim()).to.eq(equalTo);
return subject;
},
);
It shouldn't be difficult to copy .text()
from cypress-commands
. Why reinvent the wheel?
any update?
please fix this!
Please FIX
I've been using this as a workaround for the last while and it seems to be working well.
I took bits and pieces from comments above and overwrote .should
and .and
so that when the chainer have.text
is used you compare the element's trimmed inner text instead. For all other chainers, the commands behave like the normally do.
Just put this into your commands.js
file and adjust your have.text
assertions as needed to remove the extra whitespace.
function overwriteShould(originalFn, subject, chainer, method, value) {
if (chainer === 'have.text') {
return originalFn(subject.prop('innerText').trim(), 'equal', method, value)
.then(() => subject);
}
const args = Cypress._.reject(arguments, { name: 'originalFn' });
return originalFn(...args);
}
Cypress.Commands.overwrite('should', overwriteShould);
Cypress.Commands.overwrite('and', overwriteShould);
The problem with contains is it matches any substring.
Given:
<div> Tag1, Tag2 </div>
cy.get('div').should('have.text', 'Tag1, Tag2');
should pass (trims whitespace)cy.get('div').should('have.text', 'Tag1');
should fail because of the extra textGiven that the cypress matchers are Chai, I'm not sure if we'd have to extend Chai or cypress to implement this.
This seems to work:
cy.get("div").invoke("text").then((text) => text.trim()).should("equal", "Tag1");
I'm not sure how to turn that into a custom command though.
this worked for me
any update?
I've been using this as a workaround for the last while and it seems to be working well.
I took bits and pieces from comments above and overwrote
.should
and.and
so that when the chainerhave.text
is used you compare the element's trimmed inner text instead. For all other chainers, the commands behave like the normally do.Just put this into your
commands.js
file and adjust yourhave.text
assertions as needed to remove the extra whitespace.function overwriteShould(originalFn, subject, chainer, method, value) { if (chainer === 'have.text') { return originalFn(subject.prop('innerText').trim(), 'equal', method, value) .then(() => subject); } const args = Cypress._.reject(arguments, { name: 'originalFn' }); return originalFn(...args); } Cypress.Commands.overwrite('should', overwriteShould); Cypress.Commands.overwrite('and', overwriteShould);
@todd-m-kemp I used your method (thanks for that!), but I found that subject.prop("innerText")
kept returning text in CAPITALS, so I opted for using the jQuery.text()
method: subject.text().trim()
.
I've been using this as a workaround for the last while and it seems to be working well.
I took bits and pieces from comments above and overwrote
.should
and.and
so that when the chainerhave.text
is used you compare the element's trimmed inner text instead. For all other chainers, the commands behave like the normally do.Just put this into your
commands.js
file and adjust yourhave.text
assertions as needed to remove the extra whitespace.function overwriteShould(originalFn, subject, chainer, method, value) { if (chainer === 'have.text') { return originalFn(subject.prop('innerText').trim(), 'equal', method, value) .then(() => subject); } const args = Cypress._.reject(arguments, { name: 'originalFn' }); return originalFn(...args); } Cypress.Commands.overwrite('should', overwriteShould); Cypress.Commands.overwrite('and', overwriteShould);
this works for me but has some typescript issues which I ignore with @ts-ignore. Anyone know how to add the proper types to this?
And when will there be an official solution to this? I encountered that issue testing the headline of a site!
Having this in one line in my IDE
<h1 data-cy="headline">Welcome to mySite</h1>
and test it with the following cypress command
it('headline should display `Welcome to mySite`', () => {
cy.mount(index)
cy.get('[data-cy=headline]').should('have.text', 'Welcome to mySite')
})
its all working so far...
But my editor tends to break that into 3 loc like with eslint and autosave enabled:
<h1 data-cy="headline">
Welcome to mySite
</h1>
The test will than fail with the following message:
expected '<h1>' to have text 'Welcome to mySite', but the text was '\n Welcome to mySite\n '
any updates?
May be this will help someone, instead of overwriting should or creating new query, I tested below and its working
cy.visit("https://ecommerce-playground.lambdatest.io/"); cy.getSomeElement("#widget-navbar-217834 ul.horizontal") .find("li:contains(' Blog')") .invoke("text") .should("to.include", "Blog");
using to.include will ignore the spaces and compares the containing text
.should("to.include", "Blog"); using to.include will ignore the spaces and compares the containing text
Wouldn't this also match "aBlogger" ?
I ran into a problem where cypress is not waiting for the element to update when using this command:
cy.wrap(subject).invoke("text").then((text) => text.trim()).should("equal", text)
So it is better to use the function parameter overload of should, so that cypress waits for the element to have the expected text.
cy.wrap(subject).should(expect(element.text().trim()).to.equal(text))
Full command:
Cypress.Commands.add(
'shouldHaveTrimmedText',
{prevSubject: true},
(subject: JQuery<HTMLElement>, text: string) => {
cy.wrap(subject)
.should(element => expect(element.text().trim()).to.equal(text));
}
);
Another approach - I didn't want to override functions in all cases, so I defined a trimMatch
function that I can use in other functions as needed.
const trimMatch = (exp : string) => (act : any) => expect(act.text().trim()).to.equal(exp);
// check disabled button
const checkButtonDisabled = (id : string, label : string, icon : string) => {
return cy.get(`button#${id}`).should('exist').and('be.visible').and('be.disabled').within(() => {
cy.get('span' ).should(trimMatch(label));
cy.get('mat-icon' ).should('have.text', icon );
});
}
If your HTML happens to have whitespace in the tag, e.g.,
<div> foo </div>
, that text will not impact how the user sees the page, unless it is a<pre>
element. I don't think that the'have.text'
assertion should fail just because it found' foo '
when it was expecting'foo'
.If you're not willing to change this (since it is technically a breaking change), maybe there should be a
have.text.trimmed
?Current behavior:
should('have.text', ...)
fails if the HTML contains whitespace, even if that whitespace doesn't impact the rendered content.Desired behavior:
should('have.text', ...)
should ignore leading and trailing whitespace for elements that won't render it. Elements likepre
(and elements with certainwhite-space
css values) should still consider leading and trailing whitespace.Versions
3.2.0