How to have two different tests for conditional tables that render on a page based upon the prop passed by parent?

526 Views Asked by At

I have a cypress test that checks the table on a page, however there are two different tables that could render depending on the table type conditional.

If the type of the table is product one table will render, if the type is equipment a different table will render.

How can I write this cypress test to check IF that specific table is on the page, then run a certain set of checks and vis versa?

Test description:

The conditional is if the selectedIssueKind is Product or Equipment.

The next page will have tables with different column headers and info.

My thought was to have the test check the data-class and then do the tests for that data-class table. So:

if ('[data-cy=product-issue-tracker-table]') { 
  // product table tests
} else {
  // equipment table tests
}

However, if I click on an equipment issue I get an error that it cannot find Part ID, but that's because it's trying to check the wrong table (product).

So my if ('[data-cy=product-issue-tracker-table]') check, doesn't seem to work.

Cypress test

it.only('Issue Tracker table exists, column headers are correct and there is data', () => {
    if ('[data-cy=product-issue-tracker-table]') {
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Issue ID')
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Part ID')
      cy.get('[data-cy=issue-tracker-table] table').contains(
        'th',
        'Station Name'
      )
      cy.get('[data-cy=issue-tracker-table] table').contains(
        'th',
        'Description'
      )
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'SN')
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Timestamp')
      cy.contains('td', /\w/g).should('be.visible')
    } else {
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Issue ID')
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Part')
      cy.get('[data-cy=issue-tracker-table] table').contains(
        'th',
        'Station Name'
      )
      cy.get('[data-cy=issue-tracker-table] table').contains(
        'th',
        'Description'
      )
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Start Time')
      cy.get('[data-cy=issue-tracker-table] table').contains('th', 'End Time')
      cy.contains('td', /\w/g).should('be.visible')
    }
  })

HTML

<div data-cy="issue-tracker-table">
  <div class="vgt-wrap issue-tracker-table-style cursor-pointer mb-12" data-cy="product-issue-tracker-table" pagination="[object Object]">
    <!--v-if--><div class="vgt-inner-wrap"><!--v-if--><!--v-if--><!--v-if-->
    <div class="vgt-fixed-header"><!--v-if-->
    </div>
    <div class="vgt-responsive">
      <table id="vgt-table" class="vgt-table condensed">
        <colgroup>
          <col id="col-0">
          <col id="col-1">
          <col id="col-2">
          <col id="col-3">
          <col id="col-4">
          <col id="col-5">
          <col id="col-6">
            
       </colgroup>
        <!-- Table header -->
        <thead>
          <tr><!--v-if--><!--v-if-->
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-0" style="min-width: auto; width: auto;">
              <span>Issue ID</span>
              <!--v-if-->
            </th>
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-1" style="min-width: auto; width: auto;">
              <span>Part ID</span>
              <!--v-if-->
            </th>
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-2" style="min-width: auto; width: auto;">
              <span>Station Name</span><!--v-if-->
            </th>
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-3" style="min-width: auto; width: auto;">
              <span>Description</span><!--v-if-->
            </th>
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-4" style="min-width: auto; width: auto;">
              <span>SN</span>
              <!--v-if-->
            </th>
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-5" style="min-width: auto; width: auto;">
              <span>Timestamp</span>
              <!--v-if-->
            </th>
            <th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-6" style="min-width: auto; width: auto;">
              <span></span>
              <!--v-if-->
            </th>
              
            </tr><!--v-if-->
        </thead><!-- Table body starts here -->
        <tbody><!-- if group row header is at the top --><!--v-if--><!-- normal rows here. we loop over all rows -->
          <tr class="">
            <!--v-if--><!--v-if-->
            <td class="vgt-left-align">
              <span>1</span>
                
            </td>
            <td class="vgt-left-align">
              <span></span>
                
            </td>
            <td class="vgt-left-align">
              <span></span>
                
            </td>
            <td class="vgt-left-align">
              <span> Connect to DUT by SSH</span>
                
            </td>
            <td class="vgt-left-align">
              <span></span>
                
            </td>
            <td class="vgt-left-align">
              <span>Mar 28 2022 - 10:01 AM</span>
                
            </td>
            <td class="vgt-left-align"><!--v-if--><!--v-if-->
              <div data-cy="issue-tracker-view-data-button">
                <button class="primary-button">View Data</button>
                  
              </div><!--v-if-->
            </td>
              
            </tr><!-- if group row header is at the bottom --><!--v-if-->
        </tbody><!--v-if-->
      </table>
        
      </div><!--v-if--><!--v-if-->
    </div>
      
    </div><!--v-if--><!-- Issue Data Modals --><!--v-if--><!--v-if--><!-- Issue Data Modals --><!-- Add New Analysis section -->
  <div class="flex">
    <span class="tab font-normal text-card-orange bg-card-orange bg-opacity-20 py-1 px-4 mb-3 mr-9 rounded-full w-max">Analysis Records</span>
    <!--v-if--><!--v-if-->
    <div data-cy="add-new-analysis-button">
      <button class="primary-button">Add New Analysis</button>
        
      </div><!--v-if--></div>
        
 </div>
2

There are 2 best solutions below

9
On BEST ANSWER

There's a bit of repetition in the test that may be reduced by getting an array of table headers.

You may be testing more than headers, but this gives you the idea.

const headers = ['Issue ID', ...]  // add all the common headers

cy.get('[data-cy="issue-tracker-table"]')
  .then($table => {
    return $table.find('[data-cy=product-issue-tracker-table]').length > 0
  })
  .then(console.log)              // for checking isProduct value
  .then(isProduct => {
    if (isProduct) {
      return headers.concat(['SN', 'Timestamp'])
    } else {
      return headers.concat(['Start Time', 'End Time'])
    }
  })
  .then(console.log)              // for checking headers array
  .then(headers => {
    // One test here
    cy.get('[data-cy=issue-tracker-table] table thead th')
      .each(($th, index, $list)) => {
        // last header is empty, presume you don't want to test it
        if (index < $list.length -1 ) {       
          expect($th.text()).to.eq(headers[index])
        }
      })
  })

Breaking down the steps is useful as well, you can incrementally check each step by (temporarily) logging the result of .then().


I notice the <!--v-if--> which shows you are using a Vue app.

If you want to get really sophisticated you can have the app tell Cypress which kind of table is being displayed. This is an app-action and it reduces risks with asynchronous loading.

The exact code depends on your Vue component, but the pattern is

// in Vue component

if (window.Cypress) {
  window.tableType = tableType
}
// in test

cy.window()
  .then(win => {
    return win.tableType === 'Product'
  })
  .then(console.log)              // for checking isProduct value
  .then(isProduct => {
    ...

Testing all <td> has content and visibility

cy.get('[data-cy=issue-tracker-table] table tbody td')
  .each($td => {
    cy.wrap($td)
      .contains(/\w/)
      .should('be.visible')
  })

Alternative step: tableType instead of isProduct

cy.get('[data-cy="issue-tracker-table"]')
  .then($table => {
    if ($table.find('[data-cy=product-issue-tracker-table]').length > 0) {
      return 'Product'
    } else {
      return 'Equipment'
    }
  })
  .then(console.log)              // for checking tableType value
  .then(tableType => {
    if (tableType === 'Product') {
      return headers = headers.concat(['SN', 'Timestamp'])
    } else {
      return headers = headers.concat(['Start Time', 'End Time'])
    }
  })
  .then(console.log)              // for checking headers array
  .then(headers => {
    // One test here
    cy.get('[data-cy=issue-tracker-table] table thead th')
      .each(($th, index, $list) => {
        // test header here
        if (index < $list.length -1 ) {
          expect($th.text().trim()).to.eq(headers[index])
        }
      })
  })
1
On

So, this sort of indeterminate testing is not a great practice. The ideal would be to know for sure that you are testing one table or the other.

The conditional is if the selectedIssueKind is Product or Equipment. The next page will have tables with different column headers and info.

Based on that description, I'd think that we could easily split this into two (or more) tests.

// pseudo-code
describe('table tests', () => {
  beforeEach(() => {
    // shared setup
  })

  it('tests the product table', () => {
    // in the product test, we'll select the `selectedIssueKind` of product
    // unclear from the HTML/Cypress how you're doing that, so very vague line below
    cy.get('product').click();
    // code to test the product table
  });

  it('tests the equipment table', () => {
    // in the equipment test, we'll select the `selectedIssueKind` of equipment
    // same as above, very vague example
    cy.get('equipment').click();
    // code to test the equipment table
  });
});

Also, if every test in your spec needed a selectedIssueKind, you could move that logic into your beforeEach(), set an environment variable in your it(), and not repeat that in each test.

// psuedo-code

describe('test', () => {
  beforeEach(() => {
    // other setup
    // grab the cypress environment variable as your selector
    cy.get(Cypress.env('selectedIssueKind')).click();
  })

  it('product test', { env: selectedIssueKind: 'product' } }, () => {
    // code
  });

  it('equipment test', { env: selectedIssueKind: 'equipment' } }, () => {
    // code
  });
});