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

Does iterating table rows via Codeceptjs and Puppeteer require a Helper

我正在尝试使用 Codeceptjs 中可用的方法来简单地迭代 table 行和 select 行,该行基于迭代当前行的特定单元格中存在的文本。

以下代码在我的页面对象之一中并且部分有效。

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。

如果我传入的参数的值刚好在table的第一行,就可以了。但是,无论我做什么,我的代码似乎都只出现在第一行,我不明白为什么?

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

因此,我用于识别给定行中的文本的两段代码(大部分)有效,单击 'that' 行中的复选框的代码也有效。

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

此外,我似乎无法让 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", "user@somewhere.com" 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

最后,对于与 table 的基本交互,在使用 codeceptjs 时,您是否基本上需要使用 helper 然后使用原生 Puppeteer 编写代码? (我在我的项目中使用了 puppeteer)

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

谁能帮我理解为什么您似乎无法使用 CodeceptJS 迭代 table 的行?我必须错过一些东西。这是我当前来自 PageObject class 的代码,我不明白第一次迭代成功发生但随后失败的情况。

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", "bob@infdig.com" 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

谢谢! 鲍勃

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

人偶助手 class

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;