Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer methods for typing never complete typing the full strings passed to them

Problem

Summary of problem: I'm writing several test suites (using Jest and Puppeteer) to automate tests of my AngularJS app's home page. Many of the tests I'd like to automate involve filling out forms with <textareas>'s wherein strings (of varying length) need to be typed. But the problem is that every Puppeteer method available for typing is extremely inconsistent, in that not all characters in the strings passed to these methods are ultimately typed, and sometimes these methods even break subsequent methods doing unrelated things.

Overview of testing environment

  • Puppeteer version: 1.19.0
  • Jest version: 24.8.0

What I've tried to far

Research: I've searched extensively for a solution on the Puppteeer's Github Issues page, as it seems like this problem is pretty widespread. So far I've tried the solutions provided in #1648, #2784, #1958 and #1223. Below are the code snippets I've tried, taken from those various answers. None of them have worked so far.

<!-- index.html -->
<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <div>
      <md-content>
        <form test-id="myForm">
          <md-input-container>
            <div>
              <textarea ng-model="myCtrl.user.name" test-id="userName"></textarea>
            </div>
          </md-input-container>
          <md-input-container>
            <input type="file" ng-model="myCtrl.user.photo" test-id="uploadPhoto">
          </md-input-container>
          <md-dialog-actions>
            <button type="submit" test-id="submitForm">Submit</button>
          </md-dialog-actions>
        </form>
      </md-content>
    </div>
    <!-- These divs appear after the form has been submitted -->
    <div>
      <p><!-- username goes here --></p>
    </div>
    <div>
      <img><!--  photo goes here --></img>
    </div>
  </body>
</html>
// index.spec.js
describe('fill out and submit form', () => {

  test('page has loaded', async() => {

    // note: I've tried both these methods for waiting for all the DOM
    // content to load before starting to fill out the form,
    // and neither make much of a difference in the typing behavior,
    // so I usually go with waiting for all network connections
    // to finish
    await page.goto('https://my-website.com', {waitUntil: 'networkidle0'});
    // await page.goto('https://my-website.com', {waitUntil: 'networkidle2'});

  });

  test('fill out form', async() => {

    let formSelector = 'form[test-id="myForm"]';
    // waits for form to appear
    await page.waitForSelector(formSelector, {visible: true, timeout: 3000});

    let longNameInputSelector = 'textarea[test-id="userName"]';

    // attempt 1: focus then page.keyboard.type
    // behavior: rarely finishes typing
    await page.focus(longNameInputSelector);
    await page.keyboard.type('Casey');

    // attempt 2: page.type
    // behavior: sometimes finishes typing
    await page.type(longNameInputSelector, 'Casey');

    // attempt 3: page.type then press 'Enter' 
    // behavior: this method tends to fix the typing but
    // breaks the photo uploading code below
    await page.type(longNameInputSelector, 'Casey');
    await page.keyboard.press('Enter');

    // attempt 4: page.type then press 'Tab'
    // behavior: sometimes finishes typing
    await page.type(longNameInputSelector, 'Casey');
    await page.keyboard.press('Tab');

    // attempt 5: wait for input selector to be visible and then type
    // behavior: rarely finishes typing
    await page.waitForSelector(longNameInputSelector, {visible: true, timeouts: 3000});
    await page.focus(longNameInputSelector);
    await page.keyboard.type('Casey');

    // attempt 6: convert input to Element Handle and then click
    // behavior: more reliable but fails occasionally
    let inputHandle = await page.$(longNameInputSelector);
    await inputHandle.click();
    await page.keyboard.type('Casey');

    // upload photo
    let inputPhotoSelector = 'input[type="file" test-id="uploadPhoto"]';
    const inputPhotoHandle = await page.$(inputPhotoSelector);
    const filePath = path.relative(process.cwd(), __dirname + '/me.png');
    await inputPhotoHandle.uploadFile(filePath);
  });

  test('submit form', async() => {

    // note: I've played a little with the way I'm submitting the form
    // to see if that could make any difference. So far it hasn't.
    // Here is what I've tried:

    // attempt 1: wait for the submit button to no longer be
    // disabled (i.e. wait for entire form to be filled out)
    // behavior: doesn't fix the problem. typing behavior still inconsistent
    let submitBtnSelector = 'button[test-id="submitForm"]:not([disabled])';
    await page.click(submitBtnSelector);

    // attempt 2: issue a fake click over the submit button to 
    // prevent autocomplete
    // behavior: doesn't fix the problem. typing still erratic
    await page.evaluate((sel) => {
      document.querySelector(sel).click();
    }, submitBtnSelector);

  });

});

Final thoughts/question:

Does anyone know of a reliable way of automating typing in Puppeteer so that my tests won't fail sporadically?

like image 745
ElsaInSpirit Avatar asked Nov 07 '22 14:11

ElsaInSpirit


1 Answers

I was trying to test an Angular app and the click and type related actions was not working. Finally it was solved with MouseEvent and KeyboardEvent API.

It work with both MouseEvent and KeyboardEvent. Here is example with MouseEvent.

function simulateEvent(eventx, elem) {
    const event = new MouseEvent(eventx, {
        view: window,
        bubbles: true,
        cancelable: true,
    });
    return elem.dispatchEvent(event);
};

function simulateType(selector, value) {
    const elem = document.querySelector(selector);
    elem.value = value;
    const events = ['click', 'focus', 'keydown', 'keypress', 'mousedown', 'compositionend', 'compositionstart', 'blur']
    for (let fakeEvent of events) {
        simulateEvent(fakeEvent, elem);
    }
};

It sets the value to the input field, and sends a lot of events as you can see.

Why? Some website watches for keydown, keypress etc. however, I have seen a lot of website did not even let me submit the form if I did not click on the input fields.

like image 117
Md. Abu Taher Avatar answered Nov 14 '22 23:11

Md. Abu Taher