1

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'd like to automate is clicking on an md-tab-item. Sounds easy, right? Unforuntealy, this simple test has become a huge issue, and the only solution I have been able to come up with to fix it (adding a pause function to my test) makes my test extremely slow. I should also note that my md-tab-item's are added dynamically, so the only way to select them is via an XPath selector that selects the target tab based on the text it contains. This makes clicking on the tab slightly harder as I first have to use Puppeteer's page.$x method to convert the tab into a clickable ElementHandle (the code below illustrates what I mean).

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.

Disclaimer: In order to provide this community with a minimal, reproducible example I chose not to copy and paste my original code by instead wrote up a simpler example. So if you find a typo, I can assure you that is not the source of my problem because I have checked my original code very carefully for typos. I'm sorry in advance for any typos in the following code. I was vigilant but I am human so I make mistakes.

Broken code:

<!-- index.html -->
<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <md-content>
      <md-toolbar>
        <div class='md-toolbar-tools'>
          <h2>My Toolbar</h2>
        </div>
      </md-toolbar>
      <md-progress-linear></md-progress-linear>
      <md-tabs>
        <md-tabs-canvas>
          <md-pagination-wrapper>
            <md-tab-item>Tab 1</md-tab-item>
            <md-tab-item>Tab 2</md-tab-item>
            <md-tab-item>Tab 3</md-tab-item>
          </md-pagination-wrapper>
        </md-tabs-canvas>
      </md-tabs>
      <div><!-- Displays tab content based on which tab is clicked --></div>
    </md-content>
  </body>
</html>
// index.spec.js
test('click on second tab', async() => {

  // check if tab exists: this ALWAYS passes
  tabXPath = '//md-tab-item[contains(text(), "Tab 2")]|//md-tab-item[contains(text(), "tab 2")]';
  const tabExists = await page.waitForXPath(tabXPath, {timeout: 3000}) ? true : false;
  expect(tabExists).toBe(true);

  // use tab's XPath to create a clickable ElementHandle
  let clickableTab = await page.$x(tabXPath);

  // bug: tab cannot be clicked unless I wait - increases test time by 1s :(
  await page.waitFor(1000);

  // click on this ElementHandle
  await clickableTab[0].click();

}); 

Questions:

  1. Do any of you Jest/Puppeteer experts have any idea why I have to wait before being able to click on my tab? If so, how can I improve my code so I no longer have to rely on page.waitFor to get the test to pass?
  2. Is there perhaps a better way of clicking on an element specified by a XPath selector that I could try?
ElsaInSpirit
  • 341
  • 6
  • 16
  • I came across this thread researching my answer in [Problem filling in an input field without an id or identifying attribute. (Puppeteer)](https://stackoverflow.com/questions/68229213/problem-filling-in-an-input-field-without-an-id-or-identifying-attribute-puppe/68230043#68230043) which is also using xpath on an AngularJS app. Ultimately, `.click()` was failing but using `.evaluate(el => el.click())` as described [here](https://stackoverflow.com/questions/51857070/66537619#66537619) worked. I'm unsure if this also applies here since there's no app to run/test, but seems worth leaving a note. – ggorlen Jul 02 '21 at 20:39

1 Answers1

0
  1. with Puppeteer, you need to wait as otherwise other parts of the code will execute before the elements of the webpage that you need have loaded. However, instead of doing waitFor, try waitForSelector(css selector here), or waitForNavigation - then you only have to wait for a specific bit to load.

  2. That is pretty much the only way Puppeteer supports doing it according to their docs, this might help.

Toby
  • 86
  • 8