Javascript 相当于cypress contains()方法的木偶演员

Javascript 相当于cypress contains()方法的木偶演员,javascript,puppeteer,cypress,Javascript,Puppeteer,Cypress,HTML如下所示: const htmlStr = ` <div> <div> <h1>title1</h1> <div> <a>click me<a> </div> </div> <div> <h1>title2</h1> <div>

HTML如下所示:

const htmlStr = `
  <div>

    <div>
      <h1>title1</h1>
      <div>
        <a>click me<a>
      </div>
    </div>

    <div>
      <h1>title2</h1>
      <div>
        <a>click me<a>
      </div>
    </div>


    <div>
      <h1>title3</h1>
      <div>
        <a>click me<a>
      </div>
    </div>

  </div>
`
对于这个例子,有很多方法可以做到这一点。但这个想法是找到最近的谁包含文本“title1”。从这里开始,我想要一些如下:

cy.contains('div', 'title1').within(() => {
  cy.get('a').click()
})
const element = await page.elementContains('div', 'title1') // <- narrow down
await element.click('a')

如果我理解正确,这些是XPath和选择器等价物,它们碰巧具有类似的DOM结构:

"严格使用",; const puppeter=需要“puppeter”; 异步函数主{ 试一试{ const browser=wait puppeter.launch; const[page]=等待浏览器.pages; 等待页面。转到'https://example.org/'; const[elemByXPath]=wait page.$x'//div[h1[contains.,Example Domain]]//a'; const elemBySelector=wait page.evaluateHandle =>[…document.queryselectoral'div'] 发现 div=>[…div.queryselectoral'h1'] .someh1=>h1.innerText。包括“示例域” .querySelector'a' ; console.logelemByXPath.toString; console.logelemBySelector.toString; 等待浏览器关闭; }犯错误{ console.error; } };
您可以使用prototype轻松地向页面添加额外功能。并使用

page.evaluate和page.evaluateHandle之间的唯一区别是page.evaluateHandle在页面对象JSHandle中返回

创建elementContains函数 木偶演员模块导出如下类。您可以根据需要扩展它们的功能

// extract the Page class
const { Page } = require("puppeteer/lib/Page");
通常,您创建的页面将在原型中变成这个页面。page.evaluateHandle将变为this.evaluateHandle

创建.get函数 现在我们有了很棒的elementContains,是时候获得get函数了

/**
 * Replicate the .get function
 * gets an element from the executionContext
 * @param {String} selector
 * @returns {Promise} 
 */
const { JSHandle } = require("puppeteer/lib/JSHandle");
JSHandle.prototype.get = function get(selector) {
  // get the context and evaluate inside
  return this._context.evaluateHandle(
    (element, selector) => {
      return element.querySelector(selector);
    },
    // pass the JSHandle which is itself
    this,
    selector
  );
};
享受新功能带来的乐趣 结果:

我添加了一个小答案。检查它是否完美;我想知道为什么这不是公认的答案,有人能告诉我,让我敞开心扉吗?似乎elementContains不会像预期的那样工作。它使用querySelectorAll'div'查找所有节点。在本例中,根节点将与根节点匹配,因为rootDiv.innerText.includes'title1'为true@阿布·塔赫里博士你的问题提到了最近的,但最近的是什么?我测试了这个函数,它对所有给定的输入都有效。如果它不起作用,请随时回复错误日志。如果我没有清楚地表达自己,请向@Md.Abu Taher道歉。例如,const elem2=await page.element包含'div','title2'和const content2=await elem2.$eval'h1',e=>e.innerText。现在content2应该是title2,但实际上是title1。这似乎是一只很小的虫子。也许应该换一两行。
/**
 * @name elementContains
 * @param {String} selector specific selector globally search and match
 * @param {String} text filter the elements with the specified text
 * @returns {Promise} elementHandle
 */
Page.prototype.elementContains = function elementContains(...args) {
  return this.evaluateHandle((selector, text) => {
    // get all selectors for this specific selector
    const elements = [...document.querySelectorAll(selector)];
    // find element by text
    const results = elements.filter(element => element.innerText.includes(text));
    // get the last element because that's how querySelectorAll serializes the result
    return results[results.length-1]; 
  }, ...args);
};
/**
 * Replicate the .get function
 * gets an element from the executionContext
 * @param {String} selector
 * @returns {Promise} 
 */
const { JSHandle } = require("puppeteer/lib/JSHandle");
JSHandle.prototype.get = function get(selector) {
  // get the context and evaluate inside
  return this._context.evaluateHandle(
    (element, selector) => {
      return element.querySelector(selector);
    },
    // pass the JSHandle which is itself
    this,
    selector
  );
};
(async () => {
  const browser = await puppeteer.launch({
    headless: false
  });

  const page = await browser.newPage();
  await page.setContent(html); // your specified html text

  // get the element
  const elem = await page.elementContains('div', 'title1')

  // use it like any other normal element, click it, eval it, remove it etc.
  const content = await elem.$eval('h1', e=>e.innerText);
  console.log(content) // prints "title1"

  // OR use the built in click function
  const btn = await page.$('a', elem); // <-- pass the handle here
  await btn.click();

  // OR use our .get function to get another element
  const targetBtn = await elem.get('a');
  targetBtn.click(); // click it
})();