Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contain.text and include.text should do substring matches but instead do full-string matches #1969

Closed
alexch opened this issue Jun 17, 2018 · 21 comments

Comments

9 participants
@alexch
Copy link

commented Jun 17, 2018

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
@brian-mann

This comment has been minimized.

Copy link
Member

commented Jun 17, 2018

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.

@brian-mann

This comment has been minimized.

Copy link
Member

commented Jun 17, 2018

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.

@alexch

This comment has been minimized.

Copy link
Author

commented Jun 17, 2018

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.

@Shailth

This comment has been minimized.

Copy link

commented Nov 30, 2018

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*/')
@jennifer-shehane

This comment has been minimized.

Copy link
Member

commented Dec 4, 2018

@Shailth

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

This comment has been minimized.

Copy link

commented Dec 21, 2018

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. 🙃

@c32hedge

This comment has been minimized.

Copy link

commented Jan 30, 2019

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...

@Bkucera

This comment has been minimized.

Copy link
Member

commented Jan 30, 2019

Hi everyone, I'm currently working on this.

@alexch

This comment has been minimized.

Copy link
Author

commented Jan 30, 2019

@Bkucera Bkucera referenced this issue Jan 30, 2019

Merged

support partial matching text,html,value #3259

3 of 3 tasks complete
@Bkucera

This comment has been minimized.

Copy link
Member

commented Jan 30, 2019

PR open here #3259

@alexch

This comment has been minimized.

Copy link
Author

commented Jan 31, 2019

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...

@Bkucera

This comment has been minimized.

Copy link
Member

commented Jan 31, 2019

@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

@alexch

This comment has been minimized.

Copy link
Author

commented Jan 31, 2019

@tristanwilson-caredove

This comment has been minimized.

Copy link

commented Feb 1, 2019

Awesome, awaiting this! Just ran into this today.

@RoraxHan

This comment has been minimized.

Copy link

commented Feb 28, 2019

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?

@bahmutov

This comment has been minimized.

Copy link
Collaborator

commented Feb 28, 2019

@RoraxHan

This comment has been minimized.

Copy link

commented Feb 28, 2019

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

@bahmutov

This comment has been minimized.

Copy link
Collaborator

commented Feb 28, 2019

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/)
@RoraxHan

This comment has been minimized.

Copy link

commented Feb 28, 2019

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

@flotwig flotwig closed this in #3259 Jul 9, 2019

@cypress-bot

This comment has been minimized.

Copy link

commented Jul 9, 2019

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.

@cypress-bot

This comment has been minimized.

Copy link

commented Jul 9, 2019

Released in 3.4.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.