Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Add built-in table helper for retrieving header column index and interacting with rows #33111

Open
srdjanmutlak opened this issue Oct 15, 2024 · 0 comments

Comments

@srdjanmutlak
Copy link

🚀 Feature Request

I would like to propose adding a built-in helper class or Playwright command that allows users to retrieve the index of a table column by header text and interact with rows dynamically based on that index. This would simplify automating interactions with data tables, which is a common need in end-to-end testing.

Example

`
const { expect } = require('@playwright/test');

class WebTableHelper {
constructor(page) {
this.page = page;
}

// Function to get the index of the header based on its text content with customizable selector
async getIndex(headerText, headerSelector = 'tr:first-child td') {
  // The selector is now passed as a parameter with a default value
  const elements = await this.page.$$(headerSelector);
  
  // Log the number of header elements found
  console.log(`Found ${elements.length} header elements.`);

  // Log all the header texts for debugging
  for (let index = 0; index < elements.length; index++) {
    let elementText = await elements[index].innerText();
    
    // Normalize the header text by removing newlines and extra spaces
    elementText = elementText.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
    
    console.log(`Normalized Header ${index}: ${elementText}`);

    if (elementText.toLowerCase() === headerText.trim().toLowerCase()) {
      return index;
    }
  }

  throw new Error(`Header with text "${headerText}" not found`);
}

// Function to check the first row values under the given header
async checkTableValuesForTheNewFirstWebtable(headerText, expectedValueBelowTh, headerSelector, firstRowSelector = `tr:nth-child(2)`) {
  const indexLocOfOurValue = await this.getIndex(headerText, headerSelector);
  const firstRow = await this.page.$(firstRowSelector); // Adjust for first data row (after header)
  const cells = await firstRow.$$('td');
  const cell = cells[indexLocOfOurValue];
  const tdText = await cell.innerText();
  console.log(tdText);
  expect(tdText.trim()).toBe(expectedValueBelowTh);
}

// Function to check the last non-empty row values under the given header
async checkTableValuesForTheNewLastNonEmptyRow(headerText, expectedValueBelowTh, headerSelector = `tbody tr:first-child td`, trSelector = `tbody tr`) {
  const indexLocOfOurValue = await this.getIndex(headerText, headerSelector);
  const rows = await this.page.$$(trSelector);
  
  // Filter rows that contain text (non-empty)
  const nonEmptyRows = [];
  for (const row of rows) {
    const rowText = await row.innerText();
    if (rowText.trim().length > 0) {
      nonEmptyRows.push(row);
    }
  }

  // Get the last non-empty row
  const lastRow = nonEmptyRows[nonEmptyRows.length - 1];
  const cells = await lastRow.$$('td');
  const cell = cells[indexLocOfOurValue];
  const tdText = await cell.innerText();
  console.log(tdText);
  expect(tdText.trim()).toBe(expectedValueBelowTh);
}

}

module.exports = WebTableHelper;

**EXAMPLE OF USING IT IN TEST
// test.spec.js

const { test } = require('@playwright/test');
const WebTableHelper = require('../pageobjects/webTableHelper');

test.describe('Webtable Tests', () => {

test.beforeEach(async ({ page }) => {
await page.goto('https://cosmocode.io/automation-practice-webtable/');
});

test('Verify value in the first row of the Webtable', async ({ page }) => {
const helper = new WebTableHelper(page);
await helper.checkTableValuesForTheNewFirstWebtable
// first request has different selectors, these selectors work same as default ones
('currency', 'Afghani', tbody tr:first-child td, tbody tr:nth-child(2));

await helper.checkTableValuesForTheNewFirstWebtable('Country', 'Afghanistan');
await helper.checkTableValuesForTheNewFirstWebtable('Capital(s)', 'Kabul');
await helper.checkTableValuesForTheNewFirstWebtable('Primary Language(s)', 'Dari Persian; Pashto');

});
test('Verify value in the last non-empty row of the Webtable', async ({ page }) => {
const helper = new WebTableHelper(page);
await helper.checkTableValuesForTheNewLastNonEmptyRow(Capital(s), 'Harare');
});

});
Image
Image

`

Motivation

Automating interactions with dynamic tables is a common use case in test automation. Currently, developers need to write custom helper functions to find column indices by header text and interact with rows, which leads to redundant code. A built-in Playwright helper class would make this process more straightforward and reusable, reducing boilerplate code in many test suites.

Proposed Solution:
I have developed a helper class that could be used as a starting point for this feature. It supports:

Retrieving the index of a column based on its header text.
Custom selectors to target specific table structures.
Flexible row interaction by referencing the column index.
Text normalization: The class includes functionality for normalizing header text by removing extra spaces and newlines, which can cause issues in matching header text. This ensures that headers are properly matched, even if there are formatting differences.
( However, I am open to further optimizations, and Playwright could implement additional trimming or newline replacement strategies if there are more efficient or optimal approaches to text normalization. This would ensure the command is robust across various table formats and text inconsistencies.)

Proposed API:
While the class implementation is flexible, this feature could also be implemented as Playwright commands or simplified APIs for users. For example:

`
// Command to get the index of a header by its text
const index = await page.getHeaderIndex('Currency', {
selector: 'thead tr th' // Optional: custom header selector
});

// Interact with a row based on header column index
const cellText = await page.getRowCellText(index, { row: 2 });

`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants