Blog #27: ๐๐ป A Tale of Dynamic Pages & Adaptive Code - Helping a Friend Debug a Playwright Test
The other day, a friend messaged me, a bit stuck on a Playwright test:
“I’m trying to add all 3 products to the cart, but only 2 are getting added, and then it crashes! ๐คฏ”
“I’m trying to add all 3 products to the cart, but only 2 are getting added, and then it crashes! ๐คฏ”
I couldn’t resist helping out and it turned into a fun little debugging adventure. Let me share how we cracked it!
๐ The Scenario
It was a practice website with exactly 3 products. My friend’s mission:
๐ Log in
๐ Add all 3 items to the cart
๐ Validate that everything worked smoothly.
๐ Add all 3 items to the cart
๐ Validate that everything worked smoothly.
๐ The Initial Problem
Here’s what he was using at first:
const addToCartButtons = page.locator('button:has-text("Add to Cart")');for (let i = 0; i < 3; i++) {await addToCartButtons.nth(i).click();}
But… it would only add 2 items and then fail with a timeout error!
Turns out, Playwright was trying to click a button that no longer existed after the page updated.
๐ What Was Happening?
When you click “Add to Cart” on a product, the page dynamically removes that product from the list. But the for loop was working with the original static list of buttons — which quickly became outdated.Turns out, Playwright was trying to click a button that no longer existed after the page updated.
๐ก The Fix: Fresh Searches with a while Loop
I explained to my friend:Dynamic pages require dynamic approaches! Instead of relying on a stale list, let’s search fresh for the button each time we want to click.
Here’s what we changed it to:
let added = 0;while (added < 3) {await page.locator('button:has-text("Add to Cart")').first().click({ force: true });added++;}
✅ Why the while Loop Worked
๐น Fresh search every timePlaywright looked at the page as it was in that moment, clicking the first visible “Add to Cart” button each time.
๐น No stale references
No more “clicking” on buttons that had been removed.
๐น Perfect for dynamic UIs
This makes it robust for dynamic sites where the UI updates after each interaction.
๐ The Win!
With this simple tweak, we got the script working perfectly:✅ All 3 products added!
✅ No timeouts!
✅ And a happy friend! ๐
๐ป Here’s the Full Working Script
const { test, expect } = require('@playwright/test');
test('Add 3 items to cart using while loop', async ({ page }) => { await page.goto("https://freelance-learn-automation.vercel.app/login");
// Login await page.getByPlaceholder('Enter Email').fill('example@example.com'); await page.getByPlaceholder('Enter Password').fill('example'); await page.getByRole('button', { name: 'Sign in' }).click();
// Wait for page to load await page.waitForLoadState('networkidle'); await expect(page).toHaveURL("https://freelance-learn-automation.vercel.app/");
// Wait for products to load await page.locator('button:has-text("Add to Cart")').first().waitFor({ state: 'visible' });
let added = 0;
while (added < 3) { await page.locator('button:has-text("Add to Cart")').first().click({ force: true }); added++; }
console.log(`Added ${added} items to the cart.`);
// Check that the number of items in the cart is 3 await page.locator('span.count').waitFor({ state: 'visible' }); const countText = await page.locator('span.count').textContent(); console.log("the count is " + countText); const count = parseInt(countText.trim(), 10); expect(count).toBe(3);
//Go to cart await page.locator('.cartBtn').click(); await expect(page).toHaveURL('https://freelance-learn-automation.vercel.app/cart'); await page.locator('button:has-text("Enroll Now")').click(); await page.locator('textarea#address').fill('Bingley'); await page.locator('input#phone').fill('0123456789'); await page.locator('button.action-btn').last().click(); //capture the text const text = await page.locator('h4.uniqueId').textContent(); console.log(text);
// Assert that the uniqueId is not empty expect(text).toBeTruthy();
//Click on cancel button await page.locator('button.action-btn.white-action-btn', { hasText: 'Cancel' }).click();
});
const { test, expect } = require('@playwright/test');
test('Add 3 items to cart using while loop', async ({ page }) => {
await page.goto("https://freelance-learn-automation.vercel.app/login");
// Login
await page.getByPlaceholder('Enter Email').fill('example@example.com');
await page.getByPlaceholder('Enter Password').fill('example');
await page.getByRole('button', { name: 'Sign in' }).click();
// Wait for page to load
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL("https://freelance-learn-automation.vercel.app/");
// Wait for products to load
await page.locator('button:has-text("Add to Cart")').first().waitFor({ state: 'visible' });
let added = 0;
while (added < 3) {
await page.locator('button:has-text("Add to Cart")').first().click({ force: true });
added++;
}
console.log(`Added ${added} items to the cart.`);
// Check that the number of items in the cart is 3
await page.locator('span.count').waitFor({ state: 'visible' });
const countText = await page.locator('span.count').textContent();
console.log("the count is " + countText);
const count = parseInt(countText.trim(), 10);
expect(count).toBe(3);
//Go to cart
await page.locator('.cartBtn').click();
await expect(page).toHaveURL('https://freelance-learn-automation.vercel.app/cart');
await page.locator('button:has-text("Enroll Now")').click();
await page.locator('textarea#address').fill('Bingley');
await page.locator('input#phone').fill('0123456789');
await page.locator('button.action-btn').last().click();
//capture the text
const text = await page.locator('h4.uniqueId').textContent();
console.log(text);
// Assert that the uniqueId is not empty
expect(text).toBeTruthy();
//Click on cancel button
await page.locator('button.action-btn.white-action-btn', { hasText: 'Cancel' }).click();
});
Final Takeaways
✅ Dynamic pages? Dynamic tests!
✅ Always think about how the DOM changes after each action.
✅ And don’t be afraid to swap out a for loop for a fresh approach like while!
✅ Always think about how the DOM changes after each action.
✅ And don’t be afraid to swap out a for loop for a fresh approach like while!
Have you faced similar challenges testing dynamic pages?
Drop a comment below and let’s chat about it — I’d love to hear your tips and tricks!
Drop a comment below and let’s chat about it — I’d love to hear your tips and tricks!
Comments
Post a Comment