I am testing a UI with Playwright and JavaScript. I have an input element that can sometimes be a dropdown, text, or date field. To handle it, I fill the input and then trigger JavaScript by pressing Tab:
await page.fill("#myID", inputText);
await page.keyboard.press('Tab'); // triggers JS formatting
The problem is that Playwright does not wait for the JavaScript to finish before continuing to the next element. How can I wait for the JS processing to complete before moving on in the script?
When I faced this, the easiest way was to use waitForFunction to check that the JavaScript processing completed. For example, if your JS updates the input value after formatting:
await page.fill("#myID", inputText);
await page.keyboard.press('Tab');
// wait for the JS to update the input value
await page.waitForFunction(
(selector, expected) => document.querySelector(selector).value === expected,
"#myID",
formattedValue // the value JS should produce
);
This ensures Playwright waits until the JS has finished modifying the element before moving on.
If your JS triggers DOM changes (like adding classes, enabling buttons, or updating text), I usually wait for that:
await page.fill("#myID", inputText);
await page.keyboard.press('Tab');
// wait for a class or attribute change triggered by JS
await page.locator("#myID").waitFor({ state: 'attached' });
You can also wait for ‘visible’ or ‘detached’ depending on the action. This works well when the JS affects the DOM rather than just values.
Sometimes the JS is complex, and there isn’t a reliable selector change. In those cases, I use a short waitForTimeout after triggering the JS:
await page.fill("#myID", inputText);
await page.keyboard.press('Tab');
// wait briefly for JS to finish
await page.waitForTimeout(100); // 100ms or adjust as needed
Not ideal for precise testing, but it works when the JS execution is quick and consistent. I combine this with assertions to make sure the value or DOM is correct afterward.