最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use JestPuppeteer to wait until an element as been removed from the DOM - Stack Overflow

programmeradmin1浏览0评论

Summary of problem: I'm writing several test suites (using Jest and Puppeteer) to automate tests of my AngularJS app's home page. One of the tests I would like to automate is a user pressing a button on the page that deletes an element in the DOM. Unforuntealy, this element is used to display a large amount of data, so in order for the element to be deleted, the client first needs to make POST request to my server to delete the data from the db, and only then can the element be deleted from the DOM. All in all, this whole process takes about a second or two. What's more, this element I'm trying to delete was added dynamically to the DOM, so the only way I am able to access the element is by using an XPath which identifies the element by the text it contains, rather than a traditional CSS selector. Now here's my question: how can I employ Jest and Puppeteer's Api's to write some test code that WAITS for this element to no longer exist (i.e. leave the DOM).

Here is a overview of what my HTML looks like so you have an idea of what I'm working with:

<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <!-- Dynamically added div -->
    <div>My Data
      <table><!-- Displays tons of data --></table>
    </div>
    <form>
      <button type="submit">Delete</button>
    </form>
  </body>
</html>

Background: I'm using Jest (v24.8.0) as my testing framework. I'm using Puppeteer (v1.19.0) to spin up and control a headless Chromium browser.

What I've tried so far:

Currently, I have this code

  test('deleted elem no longer exists', async() => {
    elemXPath = '//div[contains(text(), "My Data")]';

    // this is a function to pause the 
    // execution of the test for a given amount of milliseconds
    // in order to wait for elem to be removed
    await delay(2000);

    // This fails because Puppeteer timeouts after 3000 
    // ms b/c elemXPath no longer exists
    const elemExists = await page.waitForXPath(elemXPath, {timeout: 3000}) ? true : false;
    expect(elemExists).toBe(false);
  }); 

I could do something like this:

  test('deleted elem no longer exists', async() => {
    elemXPath = '//div[contains(text(), "My Data")]';

    // wait for elem to be removed
    await delay(2000);

    try {
      var elemExists = await page.waitForXPath(elemXPath, {timeout: 3000}) ? true : false;
    } catch(err) {
      var elemExists = false
    }
    expect(elemExists).toBe(false);
  }); 

... but I want to be able to get rid of my await delay line and just have the test wait precisely until the element is gone. The problem with await delay is that it's unreliable as depending on how much data the element is displaying it may take more or less time to be deleted than what await delay specifies.

Conclusion: Have any of you Jest/Puppeteer hackers e across an issue like this before and know of any clever solutions?

Summary of problem: I'm writing several test suites (using Jest and Puppeteer) to automate tests of my AngularJS app's home page. One of the tests I would like to automate is a user pressing a button on the page that deletes an element in the DOM. Unforuntealy, this element is used to display a large amount of data, so in order for the element to be deleted, the client first needs to make POST request to my server to delete the data from the db, and only then can the element be deleted from the DOM. All in all, this whole process takes about a second or two. What's more, this element I'm trying to delete was added dynamically to the DOM, so the only way I am able to access the element is by using an XPath which identifies the element by the text it contains, rather than a traditional CSS selector. Now here's my question: how can I employ Jest and Puppeteer's Api's to write some test code that WAITS for this element to no longer exist (i.e. leave the DOM).

Here is a overview of what my HTML looks like so you have an idea of what I'm working with:

<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <!-- Dynamically added div -->
    <div>My Data
      <table><!-- Displays tons of data --></table>
    </div>
    <form>
      <button type="submit">Delete</button>
    </form>
  </body>
</html>

Background: I'm using Jest (v24.8.0) as my testing framework. I'm using Puppeteer (v1.19.0) to spin up and control a headless Chromium browser.

What I've tried so far:

Currently, I have this code

  test('deleted elem no longer exists', async() => {
    elemXPath = '//div[contains(text(), "My Data")]';

    // this is a function to pause the 
    // execution of the test for a given amount of milliseconds
    // in order to wait for elem to be removed
    await delay(2000);

    // This fails because Puppeteer timeouts after 3000 
    // ms b/c elemXPath no longer exists
    const elemExists = await page.waitForXPath(elemXPath, {timeout: 3000}) ? true : false;
    expect(elemExists).toBe(false);
  }); 

I could do something like this:

  test('deleted elem no longer exists', async() => {
    elemXPath = '//div[contains(text(), "My Data")]';

    // wait for elem to be removed
    await delay(2000);

    try {
      var elemExists = await page.waitForXPath(elemXPath, {timeout: 3000}) ? true : false;
    } catch(err) {
      var elemExists = false
    }
    expect(elemExists).toBe(false);
  }); 

... but I want to be able to get rid of my await delay line and just have the test wait precisely until the element is gone. The problem with await delay is that it's unreliable as depending on how much data the element is displaying it may take more or less time to be deleted than what await delay specifies.

Conclusion: Have any of you Jest/Puppeteer hackers e across an issue like this before and know of any clever solutions?

Share Improve this question edited Aug 26, 2019 at 18:01 ElsaInSpirit asked Aug 7, 2019 at 18:08 ElsaInSpiritElsaInSpirit 3416 silver badges17 bronze badges 1
  • github./GoogleChrome/puppeteer/issues/705 : await page.waitFor(() => !document.querySelector(".cool-thing")); – skyboyer Commented Aug 7, 2019 at 18:46
Add a ment  | 

2 Answers 2

Reset to default 7

You can use page.waitForXPath with option { hidden: true } or use page.waitForFunction for this by writing a function which tests if the element does not exist.

page.waitForXPath with hidden:true

await page.waitForXPath(elemXPath, { hidden: true });

Alternative: page.waitForFunction

Alternatively, you can use the following code to use a simple selector:

await page.waitForFunction(() => !document.querySelector('#selector-of-element'));

If you want to use a XPath expression, you can use this code:

const elemXPath = '//div[contains(text(), "My Data")]';
await page.waitForFunction(
  xpath => !document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
  {},
  elemXPath
);

This passes your selector to the function and uses document.evaluate function to run the XPath expression inside the browser context.

Or you can use:

await page.waitForSelector(selector, { hidden: true });
发布评论

评论列表(0)

  1. 暂无评论