playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Feature] Adding waitFor Locator Helpers that do not fail after timeout

Open sustainjane98 opened this issue 2 years ago • 6 comments

I am currently testing a Web Application, which has a lot of optional processes in it (f.e. an Box just appears in a process in special cases, but takes some time)

For this I wanted to use the waitFor Locator Function, but it always ends the test with an error, like it should. But maybe it would be handy to support a flag on the existing waitFor, waitForSelector etc Functions to just keep the element as optional. So that I can do something like this:

test("Example for WaitFor", async ({page})=>{

 const exampleNotAlwaysAvailableElement = page.getByTestId("example-elem").waitFor({timeout: 5000,  noFail: true});

 if(exampleNotAlwaysAvailableElement) {
   // Do whatever needed to handle the case when this element somehow appears in time
   
}

// Continue here when not


})

sustainjane98 avatar Dec 08 '23 06:12 sustainjane98

@sustainjane98 You can try/catch to achieve the same:

test("Example for WaitFor", async ({page})=>{
  try {
    await page.getByTestId("example-elem").waitFor({ timeout: 5000 });
    // Do whatever needed to handle the case when this element somehow appears in time
  } catch {
    // Continue here when not
  }
})

dgozman avatar Dec 08 '23 19:12 dgozman

I tried this, but it seems like the catched error is still visible in trace viewer.

So it is more like something has an error but continue but I need more like an if it exists else cause. So it is no error when it doesn't exist.

Also a try catch without error handling is bad code in general.

sustainjane98 avatar Dec 08 '23 19:12 sustainjane98

I typically handle this situation by using a Promise.race() construction like the following:

await Promise.race([
   page.getByTestId("example-elem").waitFor({ timeout: 6000 }),
   page.waitForTimeout(5000)
]);

This way either the element you are waiting for resolves within 5s or the timeout gets resolved first and the test continues.

I do agree however that it would be useful to have this as an option of the waitFor function.

larsheylen avatar Dec 11 '23 07:12 larsheylen

chiming in regarding this: i have a few situations where either A, B or C could occur, and whether any of them do is outside of the test's jurisdiction. i do the Promise.race solution but the downside is that it puts a lot of garbage in the log (failures which don't affect the overall success of the test). i would really welcome a throw: false option or something like that in the .waitFor() method.

subpardaemon avatar Mar 06 '24 14:03 subpardaemon

You can wait for one of several locators. This can be useful in some situations. For my case which is to wait for either a locator or the closing of the popup, I have not yet found an adequate solution.

// Wait for a locator
assertThat(accountButton.or(emailInput).or(userAnotherAccountButton).first()).isVisible();

Arsou33 avatar Nov 25 '24 20:11 Arsou33


async isElementVisible(locator: Locator, timeout: number, interval: number = 1000): Promise<boolean> {
        while (timeout > 0) {
            if (await locator.isVisible()) {
                return true;
            }
            await this.page.waitForTimeout(interval);
            timeout -= interval;
        }
        return false;
}

Just implement this function it would skip if element not found and also step would not been mark as failed step in both trace viewer and allure report

giangtb7798 avatar Jun 24 '25 10:06 giangtb7798