11

In Cypress test, I often need to check if the text in an DOM element is equal some expected test. But since there might be some whitespaces around the text, I can't simply write:

cy.get('.cell')
  .should('have.text', 'Hello')

Instead, I have to write:

cy.get('.cell')
  .then($cell => $cell.text().trim())
  .should('eq', 'Hello')

I want to define a custom assertion operator like have.text.trimmed, allow me to use it like this:

cy.get('.cell')
  .should('have.text.trimmed', 'Hello');

But I can't find any document in official site about it. Would someone share some example?

2 Answers 2

18

Finally, I find the way to do it. Although Cypress doesn't provide such a feature, but since Cypress uses Chai, we can just define Chai methods.

Note: it's not possible to define have.text.trimmed, since assertion method text is a Chai method. rather than a chainable method, there is no way to provide a trimmed after it.

But there are still two options:

  1. Define a Chai method textTrimmed. It allows us using .should('have.textTrimmed', 'sometext'), which is preferred since we can define a custom assertion message and no tricky hacking on jQuery instances.

  2. Define a Chai chainable method trimmed. It allows using .should('have.trimmed.text', 'sometext'), which seems to work, but the assertion is determined by the Chai method text, which may be confusion. It is not recommended.

have.textTrimmed

This is in TypeScript:

chai.Assertion.addMethod('textTrimmed', function (expectedString: string) {
  const $element = this._obj;

  new chai.Assertion($element).to.be.exist;

  const actual = $element.text().trim();
  const expected = expectedString.trim();
  this.assert(
    actual === expected
    , ' Expected #{this} to have text #{exp} after trimmed, but the text was #{act} after trimmed'
    , 'expected #{this} not to have text #{exp} after trimmed'
    , expected
    , actual
  );
});

Put the code in cypress/support/index.js file to make sure running it before tests.

You may want to see the complete demo here: https://github.com/freewind-demos/typescript-cypress-add-custom-assertion-method-textTrimmed-demo/blob/master/cypress/support/textTrimmed.ts

File have.trimmed.text

chai.use((chai, utils) => {

  chai.Assertion.addChainableMethod("trimmed", () => {
  }, function () {
    const obj = utils.flag(this, 'object')

    const oldText = obj.text.bind(obj);
    obj.text = () => {
      return oldText().trim()
    }
  });
})

As I said, it's not recommended because of the tricky hack and unclear assertion message.

You can also see the complete demo here: https://github.com/freewind-demos/typescript-cypress-custom-operator-have-trimmed-text-demo/blob/master/cypress/support/index.ts

Sign up to request clarification or add additional context in comments.

Comments

6

Currently it is not possible out of the box for Cypress. The feature request is Provide a "Cypress" way to access textContent (and/or innerText) - .text() command (#630).

But you can work around it by adding custom commands to support/commands.js and use those commands in your testscript. You will end up with this in commands.js:

Cypress.Commands.add('haveText', function (text) {
  cy.get('.cell')
  .then($cell => $cell.text().trim())
  .should('eq', text)
})

In the test script you will end up with:

cy.haveText('Hello')

Instead of using the trim() in command.js you could also use contains(), this does a partial match so whitespaces are no problem (note that 'apple pie' also meets the requirements if you look for 'apple', if that is not a problem you can use contains(). Commands.js would look like this:

Cypress.Commands.add('haveText', function (text) {
  cy.get('.cell')
  .should('contains', text)
})

But what probably meets your requirements even more is using contains() in combination with a regular expression. You don't need any scripts in commands.js, but just in the test script you can use this:

cy.contains(/^\s*Hello\s*$/))

The \s* is to match any whitespace character zero or more times. The ^ is to start matching at the beginning of the text The $ is to end matching at the end of the text.

The regular expression can't be used within a should(), sadly enough.

5 Comments

Thanks. Using command is a possible way, but it's a little bit heavy for me. And using contains doesn't fully meet my requirement, e.g. a cell have content Hello world, which satisfies .should('contains', 'Hello'), but is not what I want
check, that was why I explained the fuzziness of the check indeed. But as far as I know you are not able to create custom assertions. I'll add another suggestion, to my answer.
Thanks for updating. I'm going to create an issue in cypress github
Updated my answer once again with an addition to the feature request. Didn't know that the feature request for Cypress already existed.
I find a way to do it, please see my answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.