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.
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);
});
});
Does anyone know of a reliable way of automating typing in Puppeteer so that my tests won't fail sporadically?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With