microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
65.66k stars 3.57k forks source link

[BUG] Unable to click() element due to stale closures while rendering table #23324

Closed uncvrd closed 1 year ago

uncvrd commented 1 year ago

Hi all - I've run in to a problem that has stumped me for the past day. I think I've narrowed down the scenario and need some help figuring out if I'm on the right track here. (Video and code reproductions below to replicate passing and failing scenarios)

I'm using @tanstack/react-table to render a table in my NextJS application. There is an action button above the table that will display a div when this button is clicked. I'd like to run a playwright test to click on the action button and confirm that the div is displayed, however, when I do, my test time's out with an error of:

locator.click: Target closed
=========================== logs ===========================
waiting for getByTestId('create-address-button')
  locator resolved to <button class="ml-auto hidden h-8 lg:flex" data-testid="…>Create Address</button>
attempting click action
  waiting for element to be visible, enabled and stable
  element is visible, enabled and stable
  scrolling into view if needed
  done scrolling
  performing click action
============================================================

The crazy thing is that if I comment out the @tanstack/react-table code, the playwright test successfully targets the action button and my test passes.

My hypothesis is that playwright is attempting to click on the button while the table is rendering/re-rendering/initializing causing the useState setter I'm using in the onClick to go "stale"? I don't want to have to worry about the render state of the table since these actions are different and it's not like I have to wait for the table to finish to click on the button above.

If I remove the setShowDiv from the onClick, the playwright test successfully runs up until the check to see if the div exists (since now we removed the line of code that should set it's visibility) I can also get around this by adding a hard-coded timeout but don't want to do that either.

// index.tsx

const [showDiv, setShowDiv] = useState(false);
...
...
<AddressDataTableToolbar
    // comment out this line
    table={table}
    onClick={(show) => {
        setShowDiv(show); // <-- this goes stale possibly?
    }}
/>

Is there something fundamental I'm missing or is this a scenario that Playwright should be able to handle?

System info

Source code

Local reproduction

Steps

I just want to click the button and not have to wait for the table to render because these two functionalities are separate

Here's a video showing the initial failed state (the first 0:25 seconds are just waiting for it to fail)

https://github.com/microsoft/playwright/assets/25097099/c2618e35-5c98-4f39-b22e-0be68a82f032

Here's a video showing me removing the table code and running the test to successful completion where the button is clicked and the div is displayed.

https://github.com/microsoft/playwright/assets/25097099/e95c50e9-92d5-4bf4-849c-f9fbac488e7c

Hopefully I've identified the reason for the failing test, but please let me know if the title does not accurately represent the reason why this test fails

[Describe expected behavior]

I should not have to wait for the table to render to click on the button

[Describe actual behavior]

The test fails due to locator.click: Target closed

Thanks a lot for the help :)

aslushnikov commented 1 year ago

Thanks, I can repro. A note on the repro: do not forget to run npm run build && npm run start in a separate terminal to actually start local server.

Assigning to @dgozman

uncvrd commented 1 year ago

Excellent, glad you were able to reproduce this!

Apologies on missing the run step, I updated my original post with npm run dev before running Playwright UI because that should work too instead of building.

Let me know what other information I can help provide

dgozman commented 1 year ago

@uncvrd Your page enters the infinite loop somewhere. When I go to localhost:3000 in Chrome or Safari (no Playwright involved) and click that button, everything freezes. I tried to record JavaScript profile, but wasn't able to, unfortunately. I'd recommend clicking on the "pause execution" button in Developer Tools a few times - perhaps you'll spot what exactly is looping forever.

Sorry for not being of much help, but this seems out of of scope of Playwright. Thank you for filing!

uncvrd commented 1 year ago

@dgozman Hey! I am a bit confused by this. One of your colleagues was able to replicate this and also I have video demos replicating the issue without any freezing. This is an extremely minimal demo presenting the problem on a fresh NextJS project so I'm not sure how this is happening for you?

I am happy to walk through this with you to demo as well. Please let me know where I can help

dgozman commented 1 year ago

@uncvrd Well, it does freeze on your first video. That's why you see performing click action in the logs, and nothing else after that - because page is running JavaScript forever. You can see that page takes 100% CPU in any kind of process performance monitor, for example in Chrome built-in Task Manger (open via "three dots menu" > More Tools > Task Manager).

uncvrd commented 1 year ago

@dgozman Oh I think I understand what you were saying now, thanks for clarifying. You're right, it looks like there's something unrelated to Playwright causing this. Appreciate your time 🙏

uncvrd commented 1 year ago

For anyone curious, I think it has to do with this: https://github.com/TanStack/table/issues/4614

Basically Tanstack Table needs a memoized version of your data else it will keep re-rendering which was causing issues for Playwright. Hope that helps!