r3plica
r3plica

Reputation: 13397

Cypress how to get a text from a div and store in a variable for later

I am new to Cypress and some of the things that I expect to work have really weird issues. For example, I am trying to get the value of a column in a table and use the value in a search input. I have done it like this:

it('should filter', () => {
    let id = 0;

    cy.get('[data-cy=data-table-row]').should('have.length', 25);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
        id = $div1.text();
        expect(id).not.to.eq(0);
    });

    //expect(id).not.to.eq(0);

    cy.get('[data-cy=table-filters-search]').find('input').type(id);

    cy.get('[data-cy=data-table-row]').should('have.length', 1);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
        expect(id).to.eq($div1.text());
    });
});

But when I run this, I get an error stating that [data-cy=data-table-row] has a length of 25 not 1. It turns out that the id variable I am using is not accessible outside the should method. I assume that's because it is a promise.

If I try to do this:

it('should filter', () => {
    let id = 0;

    cy.get('[data-cy=data-table-row]').should('have.length', 25);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
        id = $div1.text();
        expect(id).not.to.eq(0);

        cy.get('[data-cy=table-filters-search]').find('input').type(id);

        cy.get('[data-cy=data-table-row]').should('have.length', 1);

        cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
            expect(id).to.eq($div1.text());
        });
    });
});

The test goes mental and tries to get the [data-cy=table-filters-search] over and over and over again. I am not sure why.

Is there an easier way to grab the innerText of a div and store it to compare later?


As someone gave a response, I tried this:

it('should filter', () => {
    let variables = {};

    cy.get('[data-cy=data-table-row]').should('have.length', 25);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').then(($div1) => {
        variables.id = $div1.text();
        expect(variables.id).not.to.be.undefined;
    });

    console.log(variables);

    expect(variables.id).not.to.be.undefined;
    cy.get('[data-cy=table-filters-search]').find('input').type(variables.id);

    cy.get('[data-cy=data-table-row]').should('have.length', 1);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').then(($div1) => {
        expect(variables.id).to.eq($div1.text());
    });
});

But the test fails on the second expect(variables.id).not.to.be.undefined;

Upvotes: 2

Views: 1761

Answers (2)

Rosen Mihaylov
Rosen Mihaylov

Reputation: 1425

If you want to use it within a single case - Allias is the cypress way:

cy.wrap('value').as('variable') //setting the variable
cy.get('@variable') // with this you can call it at any time in the test after aliasing
   .then(variable =>{
      //here you can use it
   })

For a variable to use on multiple cases I recommend using an object as a Reference

let variables = {} //This would need to be declared on the scope you wish to use it in 
cy.get('element')
   .then($el => {
      variables.elText = $el.text() //Assigning the value
    })
    
cy.log(variables.elText) //You can call it later like this

Upvotes: 0

user14783414
user14783414

Reputation:

Closure problem

The problem with the first example is that the test runs in two phases. The first phase sets up the commands in the queue, and the second runs them.

During the first phase, .type(id) "captures" the current value of id (which is "0") and in the second phase that's the value that gets used.

You can fix it in a couple of ways, with an alias or moving the type(id) inside the callback, as per your second example.

This gets around the closure problem by deferring cy.get(...).find('input').type(id) until the id has actually changed.


Retry problem

The problem with the second example is that should() with an expect in the callback will retry until it succeeds or times out.

Something in the callback is continuously failing (or an error is thrown) causing a continuous retry. It should time out, not sure why that doesn't happen.

You can separate the parts of the callback into two sections, and use a .then() which does not attempt to retry.

cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
  .should(($div1) => {
    id = +$div1.text();         // Note $div1.text() is always a string
                                // so convert with "+" to compare numbers
    expect(id).not.to.eq(0)     // otherwise this always succeeds 
                                
  })                            
  .then(($div1) => {            // use a then which does not retry

    id = $div1.text();
    cy.get('[data-cy=table-filters-search]').find('input').type(id);

    cy.get('[data-cy=data-table-row]').should('have.length', 1);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
      .should(($div1) => {
        expect(id).to.eq($div1.text()) 
      });

  })

Or

cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
  .invoke('text')               // find it's text
  .should('not.eq', '0')        // passes on the id
  .then(id => {                 

    cy.get('[data-cy=table-filters-search]').find('input').type(id);

    cy.get('[data-cy=data-table-row]').should('have.length', 1);

    cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
      .should($div1 => expect(id).to.eq($div1.text()) );

  })

Upvotes: 1

Related Questions