Blog #27: 🛒💻 A Tale of Dynamic Pages & Adaptive Code - Helping a Friend Debug a Playwright Test

Published on May 30, 2025
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 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.

Sounds simple, right? But the page had a dynamic twist!


🐛 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.

💡 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 time
Playwright 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 }) => {

  // Login
  await page.getByPlaceholder('Enter Email').fill('[email protected]');
  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!

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!