I’m currently using Puppeteer for testing a React application. However, I’ve noticed that when I try to interact with controlled inputs by simply setting the DOM value, it doesn’t update the Virtual DOM or the state.
After days of research, I found a solution based on a Stack Overflow reply:
const puppeteer = require('puppeteer');
describe('<HomePage />', () => {
it('makes a new element visible', async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('http://localhost:3000/links');
await page.waitForSelector('input', { visible: true });
await page.addScriptTag({ url: 'https://code.jquery.com/jquery-3.6.0.min.js' });
const newElementVisible = await page.evaluate(() => {
const $ = window.$;
function triggerEvent(element, eventName) {
const event = new Event(eventName, { bubbles: true });
element.dispatchEvent(event);
}
const selectElement = $('[name="mySelect"]')[0];
selectElement.value = 'ShowNewElement';
triggerEvent(selectElement, 'change');
return $('#newElementIsVisible').length;
});
expect(newElementVisible).toBe(1);
setTimeout(() => { browser.close(); }, 3000);
});
});
My question is: How can the triggerEvent
function be replaced using a jQuery function?
Handling controlled React components with Puppeteer can be tricky since direct DOM manipulations do not always trigger React’s internal state updates. From my experience, using jQuery can help streamline this process effectively. You can simply integrate jQuery as you initially set up and then use it to set the value and trigger the necessary events. Here is an alternate method to consider, making sure the necessary script loads properly before running your jQuery commands:
await page.addScriptTag({ url: 'https://code.jquery.com/jquery-3.6.0.min.js' });
await page.evaluate(() => {
const $ = window.$;
const element = $('[name="mySelect"]');
element.val('ShowNewElement').trigger('input').trigger('change');
});
Adding .trigger('input')
along with .trigger('change')
ensures that the component updates correctly, especially if React is listening for multiple events. This should help ensure React processes the value change and updates its state and Virtual DOM accordingly.
In Puppeteer, to handle controlled React components with jQuery, it’s vital to trigger changes that React will detect. Your current triggerEvent
function simulates event dispatching, but with jQuery, this can be streamlined. Insert the jQuery script as you did, then set the value and trigger the event like so:
await page.evaluate(() => {
const $ = window.$;
const element = $('[name="mySelect"]');
element.val('ShowNewElement').trigger('change');
});
Using .val()
sets the value, and .trigger('change')
dispatches the change event, ensuring React updates its state and the Virtual DOM accordingly.
Setting the value won’t update React’s state directly. Use jQuery to handle this neatly. Load jQuery as you’ve done, then try: element.val('ShowNewElement').trigger('input').trigger('change');
. this forces react to process the value update properly.