Cypress Wait for List of Rows in View to Appear Before Checking Number of Rows

Viewed 423

Using Angular I have created the following list view listing rows of replications.
I was required to write a Cypress test case that:

  1. checks the # of rows upon initial loading of the replications tab
  2. filters the replications and check the # of rows
  3. clear the filters and check the # of rows again

The problem is for #1, Cypress would always try to check the count before the list of replications is loaded. Click the link "The way my application works" below to see the animated .gif

Angular Component code:

  <section class="my-3 d-flex">
    <div class="my-auto">
      <ng-container *ngTemplateOutlet="rowCount"></ng-container>
    </div>
...
  </section>
  <section class="my-3">
    <app-list
      data-cy="replicateList"
      [rows]="replicates"
      [loadError]="loadError"
      [rowTemplate]="replicateRow"
      [noRowsTemplate]="noRows"
      [noFilteredRowsTemplate]="noFilteredRows"
      [selectedFn]="isSelectedReplicate"
      [filterFns]="filters"
      [sortFn]="sort"
      (rowsFiltered)="onRowsFiltered($event)"
    >
      <ng-template #replicateRow let-replicate="row" let-index="index" let-selected="selected">
        <app-replicate-list-entry [replicate]="replicate" [index]="index" [selected]="selected"></app-replicate-list-entry>
      </ng-template>

      <ng-template #noRows>
        <div class="d-flex justify-content-center">{{ 'replicate_list_no_rows' | translate }}</div>
      </ng-template>

      <ng-template #noFilteredRows>
        <div class="d-flex justify-content-center">{{ 'replicate_list_no_filtered_rows' | translate }}</div>
      </ng-template>
    </app-list>
  </section>

The way my application works

Cypress test to test the feature

From the Cypress test .gif, you can see that we are trying to count the # of rows when the spinner is still loading. Hence we always get the following null values:

Loading Replications // This should read "79 results" as per the animated .gif; then the Cypress test below would pick up the 79

the number for resAfterFilter is: null

the value of resultInNum at the beginning is: null

My Cypress test code:

pageSelected = 'replicateList'


  static countAfterFilter(pageSelected) {
      cy.get('[data-cy=' + pageSelected + ']', { timeout: 1000 }).invoke('text').then((text) => {
        cy.get('[data-cy=\'rowCount\']', { timeout: 10000 }).invoke('text').then((text) => {
          cy.log(text);
          var resultInStr = text;
          var resAfterFilter = (resultInStr.match(/\d+/));
          cy.log("the number for resAfterFilter is : " + resAfterFilter);
          this.filtersAllOffResultInNum = resAfterFilter;
          cy.log("the value of resultInNum at the begining is : " + this.filtersAllOffResultInNum)
        });
    });
  }

My unsuccessful attempts to get ensure the list contents are loaded before checking the # of rows in the listview:

cy.get('div.spinner-border.m-5', { timeout: 10000 }).should('not.be.visible', { timeout: 10000 }).then(() => {0

Failed because Cypress did not find the spinner.

cy.get('[data-cy=' + pageSelected + ']', { timeout: 10000 }).find('.listRow', { timeout: 10000}).should('have.length.gt', 0).then(() => {

Failed because the length of the list view contents will always be 0 - as list view is loading and we only see the spinner, as per the Cypress animated .gif link above.

3 Answers

You have the right approach to wait for the rows to appear, because .should(...) forces a retry until it's condition is met

cy.get('[data-cy=' + pageSelected + ']', { timeout: 10000 })
  .find('.listRow', { timeout: 10000})
  .should('have.length.gt', 0)

but sometimes it's difficult to know if the right things are being retried. Perhaps only the find() is being retried, but not the get().

Try combining the criteria

cy.get('[data-cy=' + pageSelected + ']' + ' .listRow', { timeout: 10000 })
  .should('have.length.gt', 0)

Note there's a space in front of the .listRow to indicate a descendent element.

Or, using a string template

cy.get(`[data-cy="${pageSelected}"] .listRow`, { timeout: 10000 })
  .should('have.length.gt', 0)

The static countAfterFilter() indicates you have a Page Object, but that pattern doesn't always work well with Cypress. Start by trying the above code inline in the test, if it works then move it to the Page Object.

I would check for the #noRows to not exist and then check the number of rows:

cy.get('[data-cy="replicateList"]').should('be.visible') //try locating the selector for this element as a pageRender validation
cy.get('#noRows`).should('not.exist') // //try locating the selector for this element as an assertion that the list is present
cy.get('[data-cy=' + pageSelected + ']', { timeout: 1000 
    }).invoke('text').then((text) => {
        cy.get('[data-cy=\'rowCount\']', { timeout: 10000 }).invoke('text').then((text) => {
          cy.log(text);
          var resultInStr = text;
          var resAfterFilter = (resultInStr.match(/\d+/));
          cy.log("the number for resAfterFilter is : " + resAfterFilter);
          this.filtersAllOffResultInNum = resAfterFilter;
          cy.log("the value of resultInNum at the begining is : " + this.filtersAllOffResultInNum)
     });
});

Based on @RosenMihaylov's suggestion, might search for whatever text corresponds to replicate_list_no_rows in the $translateProvider,

e.g if in English it's

$translateProvider.translations('en', {
  'replicate_list_no_rows': 'There are no rows',
  ...
});

use this

cy.contains('div', 'There are no rows').should('not.exist');

Then the counter text is just

cy.get('[data-cy="rowCount"]')
  .invoke('text')
  .should('eq', '46 results')

or if you're testing in various languages

cy.get('[data-cy="rowCount"]')
  .invoke('text')
  .should('contain', '46')    // drop the language dependent text
Related