73

In Cypress, I want to select a button from a group of buttons based on its text-content. How can I do it? Here is my approach:

export const getCustomerButton = () => getNavigationSidenav()
  .find('mat-expansion-panel-header')
  .each(($el, index, $list) => {
    const text = $el.find('.mat-content > mat-panel-title').text();
    if (text === 'Customer') {
      return $el;
    }
    return null;
  });

The problem I have now is that I have to filter out the nulls from the element array. Is there a less complicated way?

Joshua
  • 3,055
  • 3
  • 22
  • 37
Phil
  • 7,065
  • 8
  • 49
  • 91
  • Do you have any HTML you can show. I see you already got your answer, but a bit of what you were trying to select would help. – Maccurt Aug 19 '19 at 16:02

6 Answers6

88

This code will yield the DOM element with YOUR_BUTTON_CLASS which contains text 'Customer'. Is that what you're looking for?

cy.get('.YOUR_BUTTON_CLASS').contains('Customer');

Here the documentation for .contains cypress command.

ColdHands
  • 947
  • 8
  • 28
DurkoMatko
  • 5,238
  • 4
  • 26
  • 35
  • 8
    Note that chaining commands like this can cause flakiness because the `get` is not retried after it finds something, even if none of the elements it finds contain 'Customer' (see [the docs](https://docs.cypress.io/guides/core-concepts/retry-ability.html#Only-the-last-command-is-retried)) – JessefSpecialisterren Nov 06 '20 at 09:39
  • Just doing `cy.get('Customer')` would try to yield an element *Customer* instead which is **not** what we require. – uinstinct Dec 25 '20 at 06:48
  • If you want to get the count of elements, better to use this refer this method https://stackoverflow.com/a/67699386/7469302 – Prasad Kavinda Aug 07 '21 at 19:46
46

Or maybe an even slicker solution is to use this:

cy.contains('YOUR_BUTTON_CLASS', 'Customer');

This can be done since contains() can hold 2 arguments. And if it gets two arguments the first one is always the element and the second the text.

Mr. J.
  • 3,499
  • 1
  • 11
  • 25
  • contains method returns only first found element – TheTanadu Nov 24 '21 at 11:28
  • and even simpler is this: cy.contains('Customer') – JohnP2 Dec 23 '21 at 22:39
  • 1
    This should be the default answer, due to the way Cypress handles query retries: "we recommend using cy.contains(selector, text) which is retried automatically as a single command." https://docs.cypress.io/guides/core-concepts/retry-ability#Merging-queries – TotomInc Jul 01 '22 at 10:30
40

Another option that's not mentioned in the previous answers here.

Use testing-library/cypress-testing-library

After the installation, just import it in cypress' commands.js:

import '@testing-library/cypress/add-commands'

And in your tests

cy.findAllByText("Jackie Chan").click();
cy.findByText("Button Text").should("exist");
cy.findByText("Non-existing Button Text").should("not.exist");
cy.findByLabelText("Label text", { timeout: 7000 }).should("exist");
cy.get("form").within(() => {
  cy.findByText("Button Text").should("exist");
});
cy.get("form").then((subject) => {
  cy.findByText("Button Text", { container: subject }).should("exist");
});

This is pretty straightforward and easy to use. We use this in our production site along with react testing library. Highly recommend :)

Joshua
  • 3,055
  • 3
  • 22
  • 37
  • 2
    This is what I'm going to use - thanks for the tip! – Mike Harrison Feb 24 '21 at 18:30
  • 2
    I was just wondering before I install this plugin, will `cy.findByText('Button').click()` yield same results as the built-in command `cy.contains('Button').click()`. If no, what is the difference? – ebanster May 01 '21 at 12:24
  • 2
    I did a quick test and it looks like the returned buttons are slightly different, see this [screenshot](https://i.imgur.com/zTjdfmd.png) but they both work as they should – Joshua May 03 '21 at 02:04
  • Is there a way for `findAllByText()` to only target visible elements? – kano Aug 16 '22 at 12:06
  • Didn't test, but I think you can do a `.filter(':visible')`? – Joshua Aug 17 '22 at 00:16
26

The accepted answer "can" work. However: if the element is not visible on the first try, the element cannot be found in subsequent retries.

See: https://github.com/cypress-io/cypress/issues/3745

Cypress uses "Sizzle" as selector library - so this:

cy.get('button:contains("FooBar")')

would work in retries.

madflow
  • 7,718
  • 3
  • 39
  • 54
11

There are multiple ways to do that

Syntaxes:

cy.contains(content)
cy.contains(content, options)
cy.contains(selector, content)
cy.contains(selector, content, options)

Examples:

cy.contains('button', 'Customer')
cy.contains('.buttonClass', 'Customer')
cy.get('button:contains("Customer")')
cy.contains('Customer')
Varun Sukheja
  • 6,170
  • 5
  • 51
  • 93
0

The simplest way to do it would be :

cy.get('Button_Class').contains('Button_Text')

In your case, the solution would be :

cy.get('.mat-content > mat-panel-title').contains('Customer')

There is a documentation for this here.