开发者问题收集

通过 Codeceptjs 和 Puppeteer 迭代表格行是否需要 Helper

2020-09-12
1482

我正在尝试使用 Codeceptjs 中可用的方法来简单地迭代表格的行并根据正在迭代的当前行的特定单元格中存在的文本选择一行。

以下代码位于我的一个页面对象中,并且部分有效。

async selectSiteById(siteId) {
    I.waitForElement('table');
    for (let i = 1; i < 5; i++) {
      let val = await I.grabTextFrom(`tbody tr:nth-child(${i}) td:nth-child(2)`);
      if (val === siteId) {
        I.say('Val' + val + ' -- Site Id ' + siteId)
        within(`tbody tr:nth-child(${i + 1}) td:nth-child(1)`, () => {
          I.click('input');
        });
      }
      break;
    }
  },

grabTextFrom 取回我想要的精确值并将其存储到 val。

如果我传入的参数的值恰好位于表格的第一行,那么这种方法有效。但是,无论我做什么,我的代码似乎都只能命中第一行,我不明白为什么?

同样,如果第一行具有传入的参数的值,则我的 within 方法将触发并检查第一列中的输入框,这正是我想要它做的事情。

因此,我用于识别给定行中我想要的文本的两段代码(大部分情况下)有效,并且用于单击“该”行中的复选框的代码也有效。

如果有人能帮助我理解为什么我不能让它循环遍历表格的所有行,我将不胜感激。

此外,我似乎无法让 codeceptjs 将 tbody 中的 tr 总数作为简单数组拉回,因此我可以将其用作循环的长度,因此那里的任何指针都会很棒。

为此,我尝试过 - let rowCount = await I.grabNumberOfVisibleElements('tbody tr'); 但似乎不起作用。

我确实尝试将我的分页向上移动一级,因为我最初认为它在错误的位置。当我这样做时,运行我的测试导致以下错误。

Sandbox to play around in -- Table check ALL Object: login I am on page "/login" I fill field "#username", " [email protected] " I fill field "#password", ***** I click "Sign in" I grab cookie I click "li[id="resources.admin.name"]" I click "Sites" I wait for element "table" I grab text from "tbody tr:nth-child(1) td:nth-child(2)" √ OK in 7254ms

tableFragment: selectSiteById
  I grab text from "tbody tr:nth-child(2) td:nth-child(2)"
  I grab text from "tbody tr:nth-child(3) td:nth-child(2)"   × "after all" hook: codeceptjs.afterSuite for "Check box of specific

table row" in 4672ms TypeError: Cannot read property '$$' of null (node:24032) UnhandledPromiseRejectionWarning: Cannot read property '$$' of null (node:24032) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode ). (rejection id: 7) (node:24032) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

-- FAILURES:

  1. Sandbox to play around in "after all" hook: codeceptjs.afterSuite for "Check box of specific table row": Cannot read property '$$' of null

Run with --verbose flag to see NodeJS stacktrace

最后,对于与表格的基本交互,当使用 codeceptjs 时,您是否基本上需要使用助手,然后使用本机 Puppeteer 编写代码?(我在我的项目中使用 puppeteer)

<<<<<<<>>>>> 更新

有人能帮我理解为什么你似乎无法使用 CodeceptJS 迭代表格的行吗?我一定错过了什么。这是我当前来自 PageObject 类的代码,我不明白为什么第一次迭代成功,但随后失败了。

async test() {
    const totalRows = await I.grabAttributeFrom('tbody tr');
    I.say('Total Rows: ' + totalRows.length);
    for (let i = 1; i < totalRows.length; i++) {
      I.say('Current row is: ' + i);
      let str = await I.grabTextFrom(
        `tbody tr:nth-child(${i}) td:nth-child(2)`,
      );
      I.say('String value from table is: ' + str);
      if (str === 'IDR') {
        I.say('Match found in row: ' + i);
        within(`tbody tr:nth-child(1) td:nth-child(1) span span`, () => {
          I.click('input');
        });
        break;
      }
    }
    // I.say('Hello');
    //   let scores = [10, 15, 20, 30];
    //   for (let score of scores) {
    //     score += 3;
    //     I.say(score);
    //   }
  },

输出

Sandbox to play around in -- Table check ALL Object: login I am on page "/login" I fill field "#username", " [email protected] " I fill field "#password", ***** I click "Sign in" I grab cookie I click "li[id="resources.admin.name"]" I click "Sites" I grab attribute from "tbody tr" I wait 5 √ OK in 12226ms

Total Rows: 4 Current row is: 1 sitesPage: test I grab text from "tbody tr:nth-child(1) td:nth-child(2)" String value from table is: IDH Current row is: 2 I grab text from "tbody tr:nth-child(2) td:nth-child(2)" × "after all" hook: codeceptjs.afterSuite for "More goofing around" in 4682ms TypeError: Cannot read property '$$' of null (node:7180) UnhandledPromiseRejectionWarning: Cannot read property '$$' of null (node:7180) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode ). (rejection id: 7) (node:7180) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

-- FAILURES:

  1. Sandbox to play around in "after all" hook: codeceptjs.afterSuite for "More goofing around": Cannot read property '$$' of null

Run with --verbose flag to see NodeJS stacktrace

谢谢! Bob

2个回答

假设您有一个数据表,其中第一行是标题,两列是值:

Header1 Header2
value1 value2
value1 value2
value1 value2

您可以使用以下代码从该表中获取数据:

When(
  'You have to fetch data from a table',
  (Table: { rows: { [x: string]: { cells: any } } }) => {
    for (const records in Table.rows) {
      if (parseFloat(records) < 1) {
        continue; // skip headers of a table
      }

      // go by row cells
      const cells = Table.rows[records].cells;

      // take values
      const Header1= cells[0].value;
      const Header2= cells[1].value;
      genericPage.setData(Header1, Header2);
    }
  }
);
setData: (Header1: string,Header2: string) => 
  {
    const selector=Locator  //create locator using Header1
    I.fillField({ xpath: selector }, Header2);
  },
Nikita
2022-08-10

我最终选择了辅助路线,并构建了一些通用函数来处理表格。我想分享一下,以防万一这对任何人都有帮助。尝试使用内置的 codeceptjs 方法执行此操作是不可靠的。

Puppeteer Helper 类

const { Helper } = codeceptjs;

class Table extends Helper {
  // before/after hooks
  /**
   * @protected
   */
  _before() {
    // remove if not used
  }

  /**
   * @protected
   */
  _after() {
    // remove if not used
  }

  // add custom methods here
  // If you need to access other helpers
  // use: this.helpers['helperName']

  /**
   * Get the total rows displayed in a table contained within
   * the table body (tbody tr)
   */
  async getRowCount() { 
    //const browser = this.helpers['Puppeteer'].browser;
    const page = this.helpers['Puppeteer'].page;

    page.waitForSelector('tbody');
    const tableRows = 'tbody tr';
    let rowCount = await page.$$eval(tableRows, rows => rows.length);
    return rowCount;    
  }

  /**
   * When a table is present on the page, will check the box
   * in column 1 of the header row to select all items listed on 
   * the current table page (could be more than one page full)
   */
  async selectAll() {
    const page = this.helpers['Puppeteer'].page;
    page.waitForSelector('thead tr th:nth-child(1)');
    page.click('thead tr th:nth-child(1)');
  }

  /**
   * Checks the box in column 1 for the row containing the value
   * passed in (val), where that value exists in column (col)
   * @param {string} val The value you are looking for 
   * @param {number} col Which column the value will be in
   */
  async selectRow(val, col) {
    const page = this.helpers['Puppeteer'].page;

    page.waitForSelector('tbody');
    const tableRows = 'tbody tr';
    let rowCount = await page.$$eval(tableRows, rows => rows.length);

    for (let i = 0; i < rowCount; i++) {
      const str = await page.$eval(
        `${tableRows}:nth-child(${i + 1}) td:nth-child(${col})`,
        (e) => e.innerText
      )
      if (str === val) {
        await page.waitForSelector(`${tableRows}:nth-child(${i + 1}) td:nth-child(1)`);
        await page.click(`${tableRows}:nth-child(${i + 1}) td:nth-child(1)`);
        break;
      }
    }
  }

  /**
   * Will iterate through all rows displayed in the table and check the box
   * in column 1 for each row where the value in colum (col) matches.
   * @param {string} val The value passed in to look for
   * @param {number} col The column to find the value in
   */
  async selectAllRows(val, col) {
    const page = this.helpers['Puppeteer'].page;

    page.waitForSelector('tbody');
    const tableRows = 'tbody tr';
    let rowCount = await page.$$eval(tableRows, rows => rows.length);

    for (let i = 0; i < rowCount; i++) {
      const str = await page.$eval(
        `${tableRows}:nth-child(${i + 1}) td:nth-child(${col})`,
        (e) => e.innerText
      )
      if (str.includes(val)) {
        await page.waitForSelector(`${tableRows}:nth-child(${i + 1}) td:nth-child(1)`);
        await page.click(`${tableRows}:nth-child(${i + 1}) td:nth-child(1)`);
        continue;
      }
    }
  }

  /**
   * Locates the row containing the value passed in, in the
   * specified column (col)
   * @param {string} val Value passed in to look for in each row
   * @param {number} col The column to look for the value in
   */
  async editRow(val, col) {
    const page = this.helpers['Puppeteer'].page;

    page.waitForSelector('tbody');
    const tableRows = 'tbody tr';
    let rowCount = await page.$$eval(tableRows, rows => rows.length);

    for (let i = 0; i < rowCount; i++) {
      const str = await page.$eval(
        `${tableRows}:nth-child(${i + 1}) td:nth-child(${col})`,
        (e) => e.innerText
      )
      if (str === val) {
        await page.waitForSelector(`${tableRows}:nth-child(${i + 1}) td:nth-child(1)`);
        await page.click(`${tableRows}:nth-child(${i + 1}) td:nth-child(${col})`);
        break;
      }
    }
  }
}

module.exports = Table;
bboursaw73
2020-09-16