Lesson 2
Handling Asynchronous Operations and Waiting Strategies
Introduction to Handling Asynchronous Operations and Waiting Strategies

Welcome to the next phase of our course, where we delve into handling asynchronous operations and waiting strategies using Playwright. As web applications become more interactive and dynamic, managing asynchronous events is crucial for ensuring that your automated tests are both reliable and effective. This lesson will guide you through essential techniques for handling asynchronous operations, building on your existing Playwright skills.

Key Concepts and Techniques

Until now, we assumed that the operations that we perform (click, text filling, etc) are completed immediately. But in real world, it's usually not the case. You might say, well, we do await page.click(selector), so we wait for it to complete. But, in fact, when you click a button using Playwright and use await to wait for that action, the promise resolves immediately after the click action is dispatched, not when the onClick method completes. What does this mean?

Let's consider an example when we click on the Load More button in the app that we have investigated in this course. You may have noticed that it does not load everything immediately. It is a beautiful simulation of how things happen in real-world apps. Usually, to receive the data about books, it sends some request to a database, and it takes time to fetch the data from it. What happens in our code when we click it programmatically, is the program proceeds to the next line until the load more button finishes its job. So if we expect the number of visible books to be increased, it may not be the case if the app doesn't update data immediately.

In this lesson, you will learn how to manage asynchronous operations using Playwright's waiting strategies. This will allow your tests to synchronize actions with the web page's state, ensuring that elements are ready for interaction. Let's look at an example:

TypeScript
1import { test, expect } from '@playwright/test'; 2 3test('async operations and waiting strategies', async ({ page }) => { 4 await page.goto('http://localhost:3000'); 5 6 // Click the 'Load more' button 7 await page.click('#load-more-btn'); 8 9 // Wait for the text 'Load More' to appear on the page 10 await page.waitForSelector('text=Load More'); 11 12 // Validate the number of books loaded is 8 13 const booksCnt = await page.locator('.book-title').count(); 14 expect(booksCnt).toBe(8); 15});

In this test, we perform the following actions:

  1. Page Navigation: The test begins by navigating to the desired URL using await page.goto('http://localhost:3000'). This ensures we are starting with a fresh state on the target page.

  2. Using Selectors and Asynchronous Interactions: We interact with the "Load more" button using await page.click('#load-more-btn') and then wait for the text 'Load More' to reappear on the page with await page.waitForSelector('text=Load More'). After ensuring the text has appeared, we validate the number of elements loaded using await page.locator('.book-title').count() and then assert the count with expect(booksCnt).toBe(8). This ensures that the asynchronous loading of content is successfully completed.

Thus we are now familiar with one of the waiting strategies that helps us handle asynchronous operations. Another useful case is the network load state management: utilizing await page.waitForLoadState('networkidle') after the goto command can be essential in scenarios where you need to ensure all network requests are completed after page navigation or other actions. It helps prevent actions from being executed before the page is fully interactive.

TypeScript
1import { test, expect } from '@playwright/test'; 2 3test('network load state management', async ({ page }) => { 4 await page.goto('http://localhost:3000'); 5 6 // Ensure all network requests are completed before proceeding 7 await page.waitForLoadState('networkidle'); 8 9 // Validate page is fully loaded by checking a key element 10 const isLoaded = await page.isVisible('h1'); 11 expect(isLoaded).toBe(true); 12});
  • Navigation: The goto command navigates the browser to the specified URL and waits for the page to load.
  • Network Idle State: After the initial page load, waitForLoadState('networkidle') pauses the script until there are no active network requests for at least 500 milliseconds. This ensures that all resources, such as images, scripts, and data, have been fully loaded.
Using waitForSelector Options

When using waitForSelector, Playwright provides options such as timeout and state to give you more control over waiting for elements on the page. Here's how you can use them:

Timeout

The timeout option allows you to specify how long to wait for the selector to appear before throwing an error. The default timeout is 30 seconds, but you can adjust this to suit your test's specific needs:

TypeScript
1await page.waitForSelector('text=Load More', { timeout: 10000 }); // waits up to 10 seconds

State

The state option allows you to define the element's state for which you are waiting. Common states include:

  • 'attached': Ensures the element is present in the DOM.
  • 'detached': Ensures the element is not present in the DOM.
  • 'visible': Ensures the element is visible.
  • 'hidden': Ensures the element is either detached from the DOM or hidden.
TypeScript
1await page.waitForSelector('text=Load More', { state: 'visible' }); // waits for the element to be visible

Using these options effectively can help tailor your waiting strategies to the specific requirements of your test scenarios, making them more robust and reliable.

Why It Matters

Understanding how to handle asynchronous operations is vital as web applications become increasingly dynamic. By mastering waiting strategies, you ensure that your automated tests are synchronized with the web page's behavior, leading to more reliable and accurate results. This skill will not only improve the robustness of your test scripts but also enhance your ability to tackle complex web interactions with confidence.

Feeling ready to tackle some practice tasks? Let's get started on honing these skills further with interactive exercises!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.