0

1. Initial post

I know that Playwright has auto-waiting.

I have a test where sometimes click works, sometimes does not. Below is the code.



import { test, expect } from '@playwright/test';

test('test', async ({ page }) => {

 await page.goto('https://website.com');

  await page.getByRole('link', { name: 'Log in' }).click();
  await expect(page).toHaveURL('https://website.com/login/');

});

Error:

expect(received).toHaveURL(expected) Expected string: "https://website.com/login/" Received string: "https://website.com"

Here is a piece of HTML with a login button

<span data-v-154d6ddf="" class="absolute-top-right">
  <div class="row justify-end q-gutter-x-sm q-pt-lg q-px-sm">
    <div class="col-auto">
      <a tabindex="0" type="button" href="/login/potential" role="link" class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-white q-btn--actionable q-focusable q-hoverable q-btn--no-uppercase q-btn--wrap">
        <span class="q-focus-helper" tabindex="-1"></span>
        <span class="q-btn__wrapper col row q-anchor--skip">
          <span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
            <span>Log in</span>
          </span>
        </span>
      </a>
    </div>
    <div class="col-auto">
      <a tabindex="0" type="button" href="/signup" role="link" class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable q-btn--no-uppercase q-btn--wrap">
        <span class="q-focus-helper"></span>
        <span class="q-btn__wrapper col row q-anchor--skip">
          <span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
            <span>Sign up</span>
          </span>
        </span>
      </a>
    </div>
    <div class="col-auto">
      <label for="f_2334a082-8df9-41a7-9ca0-403e5ce7c237" class="q-field q-validation-component row no-wrap items-start q-select q-field--auto-height q-select--without-input q-select--without-chips q-field--outlined q-field--float q-field--labeled" style="min-width:120px;max-height:20px">
        <div class="q-field__inner relative-position col self-stretch column justify-center">
          <div tabindex="-1" class="q-field__control relative-position row no-wrap">
            <div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
              <div id="translate" class="q-field__native row items-center">
                <span>English</span>
                <div id="f_2334a082-8df9-41a7-9ca0-403e5ce7c237" tabindex="0" class="no-outline"></div>
              </div>
              <div class="q-field__label no-pointer-events absolute ellipsis">Translate</div>
            </div>
            <div class="q-field__append q-field__marginal row no-wrap items-center q-anchor--skip">
              <i aria-hidden="true" role="presentation" class="q-select__dropdown-icon fal fa-caret-down q-icon notranslate"></i>
            </div>
          </div>
        </div>
      </label>
    </div>
  </div>
</span>

2. Update

I tried this variation (see below), and it seems like it fails less frequently, but still does not work 100%.

I also tried .click({force: true}). Fails on 3rd run.

I am new to test automation. Just started learning Playwright.

I will appreciate any comments/suggestions. thank you.



import { test, expect } from '@playwright/test';

test('test', async ({ page }) => {

await page.goto('https://website.com');

const loginBtn = page.getByRole('link', { name: 'Log in' });
expect(await loginBtn.evaluate(node => node.isConnected)).toBe(true) ;

  await page.getByRole('link', { name: 'Log in' }).click();
  await expect(page).toHaveURL('https://website.com/login/');

});

3. Update

"You should wait after clicking the button before verifying the url." This ws suggestion from @jaky-ruby

I tried this code

test('test2', async ({ page }) => {
      await page.goto('https://website.com/');

      // Note that Promise.all prevents a race condition
      // between clicking and expecting navigation result.
      const [response] = await Promise.all([
        page.getByRole('link', { name: 'Log in' }).click(),
        page.waitForNavigation({ url: 'https://website.com/login' }),
      ]);
      await expect(page).toHaveURL('https://website/login');
    ...
    });

enter image description here

4. Update

I created similar test in Cypress. Here is the code

describe("Login", () => {
beforeEach(() => {
    cy.visit(baseUrl);
});

it.only("Login page should open", () => {
    //does not help: cy.wait(3000);
    cy.get('a[href*="login"]').click();
    //does not help: cy.get(':nth-child(1) > .q-btn > .q-btn__wrapper > .q-btn__content > span').click();
    //does not help: cy.get('#q-app > div > span > div > div:nth-child(1) > a > span.q-btn__wrapper.col.row.q-anchor--skip > span').click();
    cy.url().should("eq", authUrl.replace("env", env) + "/login/potential");

});

});

As you can see i tried different variations of locators. The interesting part is that Cypress showed this error. Not sure this is the same problem that occurs in Playwright test.

CypressError

Timed out retrying after 4050ms: cy.click() failed because this element: ...is being covered by another element:

enter image description here

enter image description here

enter image description here

I tried to find that element in the DOM but it is not there.

5. Update

Another thing that I do not understand is that when I select "click" step in Cypress and use picker to select my button it shows me selector cy.get('[data-layer="Content"]'). Again, when I try to search it in the DOM it is not there.

enter image description here

enter image description here

6. Update

I checked each step in Playwright using Trace Viewer. Interesting. The log shows that Playwright waited for element to be visible and stable. However, on the attached page both Login and Sign up buttons are not visible and still loading (turns out you can inspect it. It's not just a static screenshot). I think, possibly, Playwright's auto-wait is not good enough in this example. Possibly, element is not actually stable and still loading but Playwright performs click which does not work. Now question, "can I do some additional wait to unsure that element has loaded and stable? "

enter image description here

MZin
  • 105
  • 1
  • 9
  • 1
    Did you try this. https://playwright.dev/docs/api/class-page#page-wait-for-navigation – itronic1990 Nov 03 '22 at 12:36
  • I think, as @itronic1990 said, your problem is not for the button itself, the problem is the navigation that happens after you click the button. You should wait after clicking the button before verifying the url. Why sometimes works? Because simetimes the URL changes before the expect is executed and sometimes not. – Jaky Ruby Nov 03 '22 at 18:12
  • @JakyRuby thank you for reply. I tried your suggestion. That did not work. I provided details above. – MZin Nov 04 '22 at 09:49

2 Answers2

6

Okay, looks like I found ~~solution~~ a way to make my test less flaky.

Test still fails sometimes. I think, additional 2 waits which I added just give more time for element to load, so in most cases, probably, that is enough for button to reach stable state. That's why clicking works. Note, on the screenshot below that 'networkidle' is 1.4s

Here is the final code

import { test, expect } from '@playwright/test';

test('test', async ({ page }) => {

await page.goto('https://website.com');
  await page.waitForLoadState('networkidle'); // <-  until there are no network connections for at least 500 ms.
  await page.getByRole('link', { name: 'Log in' }).waitFor();  // <- wait for element
  await page.getByRole('link', { name: 'Log in' }).click();
  await expect(page).toHaveURL('https://website.com/login/');

});


enter image description here

Sharing here some interesting links I found along the way

MZin
  • 105
  • 1
  • 9
  • I had a similar issue where click wasn't working. The correlation seemed to be that a previous action moved the location of the link element in the page. That seems to have thrown `page.getByRole` off. Disabling the previous actions fixed it for me (i.e. test isolation). – Matt Kleinsmith Feb 16 '23 at 18:09
0

waitFor() waits for the element to be visible, which is already done by Playwright. So it doesn't make sense to add it before the click() method. I think the issue is related to rendering, at least it seems to be the problem with my project. It is a problem with hydration, there is a period of time afterward where the page appears to be fully loaded and interactive, but is not until the client-side JavaScript is executed and event handlers have been attached. At that time, the playwright makes click action, which is useless.

lovric
  • 1
  • 1
  • And how do you deal with it? I think what in the same boat: https://stackoverflow.com/questions/76959007/playwright-works-unpredictably – EgoPingvina Aug 23 '23 at 08:01