37

I'm running a test called create admin. The test will first create admin, then check if the admin was created successfully.

In the script, I have a part of code where I want to wait for 3 seconds before continuing because whenever the submit button was clicked, the website will need to take 3s to refresh the admin table (list of user info) after navigation was done. For more information, this refresh is not a navigation and therefore, my 'waitForNavigation()' is not working.

Therefore, the process will be like: 'fill out the form' > 'click submit button' > 'wait for navigation' > 'reload user table (3s).

If I don't wait 3s for the table to refresh, the test will throw an error because the registered user will not be found in the table (I have other scripts to find the user).

This is how the navigation looks like when 'Save button' was clicked: enter image description here

After that, the table takes 3s to refresh and it looks like: enter image description here

This is how the 'create' function looks like: enter image description here

0

5 Answers 5

91

Playwright has this capability natively:

await page.waitForTimeout(3000);

Documentation for this is here: https://playwright.dev/docs/api/class-page#page-wait-for-timeout


Adding pauses in this manner is usually a bad idea.

If at all possible, your test should be waiting, instead, for an expected change of state on the page; there are very few situations when you would not want to do this.

Adding a waitForTimeout can be very tempting as it is so easy. But you are inviting trouble and flaky tests downstream, not to mention you are artificially increasing the duration of your test suite.

This explains these issues further: https://stackoverflow.com/a/74933906/1831456.

Sign up to request clarification or add additional context in comments.

Comments

7

You can wrap setTimeout as Promise and use it inside async functions:

const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

where ms - delay in millisceonds you wanna wait.

Usage in your code:

...
await page.click('button :text-is("Save")');
await delay(3000); // <-- here we wait 3s
return username;

4 Comments

This doesn't work in my case, using @playwright/test 1.39.0
Doesn't seem to work in my Playwright tests either, though I can't think why.
It wasn't working for me in Playwright as well (strangely it did work while debugging a test), but now on @playwright/test 1.40.1 and Node v18.16.0 it does work. I am not sure if the update fixed it or something else.
working flawlessly for me, thanks
6

While you can wait for exactly 3 seconds it is inherently either flaky or unnecessarily slow, or both. It's better to detect the changes in the page that tell you the user has been added, e.g. that little green popup.

You could do that with a normal "expect" that awaits for that element "toBeVisible()".

Comments

0

You should avoid hard-coded waits like waitForTimeout(3000), because they make tests brittle and slow. Instead, you should wait for a concrete signal that the table has updated, using Playwright's locator-based assertions or visibility/state checks.

Root-Cause Solution: Wait for a Reliable Change in the Table

Assuming that after submitting the form, the admin user appears as a new row in the table, here’s how you should structure your code:

Option 1: Wait for a specific row/text to appear in the table:

await page.locator('button[type="submit"]').click();

// Wait for the user row to appear (use unique identifier like email/username)
await expect(page.locator('table')).toContainText('[email protected]');

Option 2: Wait for the number of rows to increase (if table rows are stable)

Before submitting:

const rowsBefore = await page.locator('table tbody tr').count();

After submitting:

await page.locator('button[type="submit"]').click();

// Wait for one more row to appear
await expect
  .poll(async () => await page.locator('table tbody tr').count())
  .toBe(rowsBefore + 1);

Option 3: Wait for a table loader/spinner to disappear (if applicable)

If the table shows a loading spinner or “Loading…” message during the refresh:

await page.locator('button[type="submit"]').click();

// Wait for spinner to appear and disappear
await page.locator('.spinner').waitFor({ state: 'visible' });
await page.locator('.spinner').waitFor({ state: 'hidden' });

// OR wait until "Loading..." text disappears
await expect(page.locator('text=Loading...')).toHaveCount(0);

Summary:

Don’t rely on fixed timeouts. Instead, wait for meaningful UI changes — like the presence of user info, row count increase, or a loading indicator disappearing.

Comments

-2

Use setTimeout to do that. Here's an example

function  delayedCheck() {
  const processComplete = true; // set the right boolean here

  if ( processComplete ) {
    // Do something
  } else {
    setTimeout(delayedCheck, 3000); // try again in 3 seconds
  }
}

delayedCheck();

1 Comment

I don't see the point in this at all, or how it's useful. In its state as written it just always immediately runs the "do something" branch and never even sets a timeout. Nothing is awaited.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.