Cypress: contain.text and include.text should do substring matches but instead do full-string matches

Created on 17 Jun 2018  Â·  22Comments  Â·  Source: cypress-io/cypress

Current behavior:

    // cy.get('div#preview').should('have.text', 'Hello');     // this is a full match, but we want a partial match, to ignore newlines etc.
    // cy.get('div#preview').should('contain.text', 'Hello');  // bug: this does the same as have.text
    // cy.get('div#preview').should('include.text', 'Hello');  // bug: this also does the same as have.text
    cy.get('div#preview').then((el)=> {
      assert.include(el.text(), 'Hello');  // this works but it isn't pretty
    });

the log for all three of the above says:

ASSERT: expected <div#preview> to have text Hello, but the text was Hello\n

which makes me think that there might be some aliasing going on since I'd think the error messages for "include" and "contain" would say "include" and "contain", not "have

Desired behavior:

  • have.text should test whether the element's full text is equal to the given string
  • contain.text and include.text should test whether the element's text contains the given string but is not necessarily equal to it

This follows the Chai docs here: http://www.chaijs.com/api/bdd/#method_include
"When the target is a string, .include asserts that the given string val is a substring of the target."

Steps to reproduce:

Versions

  • Cypress 3.0.1
  • MacOS High Sierra
  • Google Chrome / Chromium as installed by npm
topic enhancement

Most helpful comment

Ok, here is example - either explicit or using regular expression

// validates text using OR
cy.get('.assertions-p').should(($el) => {
  const text = $el.text()

    const hasThisText = text.includes('unknown')
  const hasThatText = text.includes('even more text')

    expect(hasThisText || hasThatText,
    'element has either this or that string').to.be.true
})
// same check is better expressed using a regular expression and
// https://on.cypress.io/contains
cy.contains('.assertions-p', /unknown|even more text/)

All 22 comments

The reason this isn't working is because the subject / target is a DOM element, not a string.

I'm not sure this is actually a bug, since we vendor in chai (it does what it does), but we did write our own chai assertions for DOM elements here: https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cypress/chai_jquery.coffee

I would start diving into what contain.text and include.text actually trigger - whether its chai methods or the chai jquery stuff we wrote to understand it better.

All of this works though you just have to drill into and change the subject to be a string:

cy.wrap('foobar').should('contain', 'foo') // passes
cy.wrap('foobar').should('include', 'foo') // passes

cy.get('button').invoke('text').should('contain', 'foo') // passes
cy.get('button').invoke('text').should('include', 'foo') // passes

If you'd like to do the legwork to see what assertion is being applied to a DOM element that would be great.

This could also be differences between chai v3 vs v4.

If you npm install chai v4 you could manually try this..

// npm install chai@4
const expectv4 = require('chai').expect

cy.get('button').should(($btn) => {
  expectv4($btn).to.contain.text('foo')
  expectv4($btn).to.include.text('foo')
})

See if that matches a difference.

Thanks for the quick reply. I don't think I'll have time to get to this today, but I'll let you know what I find.

One of my DOM elements reads like :

<span class="LotCard__LotCardInputGroup___2F7Jd" data-automation-id="LotCardInputGroup">
    <input data-automation-id="quantity-box" type="text" inputmode="numeric" placeholder="Quantity" value="0">
    <span>EA</span>
    <button data-automation-id="lotOkButton" class=" LotCard__Green___3LxiY" type="button">Ok</button>
</span>

How do i write a cypress text code to test if there is a class whose name has "Green" in it?

Tried this:

.get('[data-automation-id=lotOkButton]')
      // .should('have.class', 'LotCard__Green___3LxiY')
      .should('match','/*Green*/')

@Shailth

cy.get('[data-automation-id=lotOkButton]')
  .should('have.attr', 'class')
  .and('match', /*Green*/)

This could also be differences between chai v3 vs v4.

If you npm install chai v4 you could manually try this..

// npm install chai@4
const expectv4 = require('chai').expect

cy.get('button').should(($btn) => {
  expectv4($btn).to.contain.text('foo')
  expectv4($btn).to.include.text('foo')
})

I tried this after having the same issue. I got:
"CypressError: Timed out retrying: Invalid Chai property: text. Did you mean "that"?

I could have done something wrong...new to all these tools, but I had to do this instead:
expectv4($btn.text()).to.contain('foo');

but in that case just doing expect($btn.text()).to.contain('foo'); worked as well.

I'd like to second that it would be really nice if contain.text and include.text did substring matching. In my case I was doing:

cy.get('div#mySpecialKey')
  .should('have.class', 'value')
  .and('contain.text', 'substring');

Which reads much cleaner than:

cy.get('div#mySpecialKey')
  .should('have.class', 'value')
  .and(($div) => {
    expect($div.text()).to.contain('substring');
  });

Unrelated side note: I find your lack of semicolons disturbing. :upside_down_face:

Any thoughts on this? I see it's still in "awaiting response"--the OP hasn't been around in a while but I tried what Brian last suggested as described above...

Hi everyone, I'm currently working on this.

"the OP hasn't been around in a while" -- sorry, I'm still here, just
lurking. Thanks for all the activity on my behalf! :-)

--
Alex Chaffee - [email protected]
http://burlingtoncodeacademy.com
http://alexchaffee.com
http://codelikethis.com
http://twitter.com/alexch

PR open here #3259

On a related note, these assertions behave inconsistently during multiple matches too. I wrote it up in these two slides: http://codelikethis.com/lessons/javascript/cypress#anchor/multiple_matches

I wonder how much @Bkucera 's patch #3259 will fix or change multiple-element string-match behavior...

@alexch the assertions today need to be re-worked for sure. those PR changes won't fix anything but allow matching a partial value using .includes or contains, as mentioned in the PR description.

we are thinking about adding should(all.have.text) and friends, which would be similar to your .each workaround

fixing the inconsistencies you mentioned are breaking changes, however

Nice! I think you all are doing great work and I love using and teaching

about Cypress.

Alex Chaffee - [email protected]
http://burlingtoncodeacademy.com
http://alexchaffee.com
http://codelikethis.com
http://twitter.com/alexch

Awesome, awaiting this! Just ran into this today.

Can I do 'OR' operation for contain?

cy.wrap('foobar').should('contain', 'foo')

This will check whether foobar contains 'foo'
but I want to check whether footbar contains 'foo' or 'bar'
How can I make it?

at this point, use should(cb) form of the assertion

http://on.cypress.io/should#Function

On Thu, Feb 28, 2019 at 2:12 PM Shine(kyl) notifications@github.com wrote:

Can I do 'OR' operation for contain?

cy.wrap('foobar').should('contain', 'foo')

This will check whether foobar contains 'foo'
but I want to check whether footbar contains 'foo' or 'bar'
How can I make it?

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/cypress-io/cypress/issues/1969#issuecomment-468399339,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACHApsn_g5pafLe1q9ObGldqDq5oAtu-ks5vSCo3gaJpZM4Uq8Ib
.

--
Dr. Gleb Bahmutov, PhD

Schedule video chat / phone call / meeting with me via
https://calendly.com/bahmutov
gleb.[email protected] @bahmutov https://twitter.com/@bahmutov
https://glebbahmutov.com/ https://glebbahmutov.com/blog
https://github.com/bahmutov

@bahmutov Sorry I've checked cypress today.
Can you write simple example for this?

Ok, here is example - either explicit or using regular expression

// validates text using OR
cy.get('.assertions-p').should(($el) => {
  const text = $el.text()

    const hasThisText = text.includes('unknown')
  const hasThatText = text.includes('even more text')

    expect(hasThisText || hasThatText,
    'element has either this or that string').to.be.true
})
// same check is better expressed using a regular expression and
// https://on.cypress.io/contains
cy.contains('.assertions-p', /unknown|even more text/)

@bahmutov Thanks alot. I've done this by regex.
but your code is helpful

The code for this is done in cypress-io/cypress#3259, but has yet to be released.
We'll update this issue and reference the changelog when it's released.

Released in 3.4.0.

Here is what worked for me:

cy.wrap($body)
        .find(`div[class*='desiredClassName']`)
        .eq(0)
        .click();

Note: I am passing an already prepared element into body, making it so I do not need to cy.get before .find.

Was this page helpful?
0 / 5 - 0 ratings