playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Bug]: codegen is not able to keep track of tabs and popups opened and will refer non-existing variables

Open kurtcodebain opened this issue 7 months ago • 4 comments

Version

1.52.0

Steps to reproduce

  1. Create bug.html with this HTML:
<!DOCTYPE html>
<html>
<head>
  <title>Tab and Popup</title>
</head>
<body>
  <button onclick="openTabAndPopup()">Open Tab and Popup</button>
  <script>
    function openTabAndPopup() {
      const newTab = window.open('', '_blank');
      if (newTab) {
        newTab.document.write(`
          <html>
            <head><title>New Tab</title></head>
            <body>
              <h1>New Tab Content</h1>
              <script>
                window.onload = function() {
                  const popup = window.open('', '', 'width=300,height=200');
                  if (popup) {
                    popup.document.write('<p>This is a popup from the new tab.</p>');
                    popup.document.close();
                    window.close(); // Close the tab
                  } else {
                    alert('Popup was blocked.');
                  }
                };
              <\/script>
            </body>
          </html>
        `);
        newTab.document.close();
      } else {
        alert('Tab was blocked.');
      }
    }
  </script>
</body>
</html>
  1. Run codegen playwright codegen ~/<path>/bug.html
  2. Click button to open popup
  3. You should now see the following recorded steps:
using Microsoft.Playwright;
using System;
using System.Threading.Tasks;

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
    Headless = false,
});
var context = await browser.NewContextAsync();

var page = await context.NewPageAsync();
await page.GotoAsync("file:///Users/<user>/Desktop/bug.html");
var page2 = await page.RunAndWaitForPopupAsync(async () =>
{
    await page.GetByRole(AriaRole.Button, new() { Name = "Open Tab and Popup" }).ClickAsync();
});
await page1.CloseAsync();

Expected behavior

Playwright to be able to keep track of tabs and popups opened and closed referring real variable names.

Actual behavior

Playwright refers a non-existing variable page1, specifically the last line:

await page1.CloseAsync();

page1 never existed.

Additional context

No response

Environment

- Operating System: [macOS Sequoia 15.5]
- CPU: [arm64]
- Browser: [All]
- .NET Version (TFM): [net8.0]
- Other info:

kurtcodebain avatar Jun 04 '25 12:06 kurtcodebain

I can also reproduce with Codegen with Node.js in Library mode.

mxschmitt avatar Jun 11 '25 09:06 mxschmitt

Thanks for the report, Mikkel! I can repro with this test:

test('should generate correct variables when opening and closing popup', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36274' } },  async ({ openRecorder }) => {
    const { recorder } = await openRecorder();

    await recorder.setContentAndWait(`
      <button onclick="openTabAndPopup()">Open Tab and Popup</button>
      <script>
        function openTabAndPopup() {
          const newTab = window.open('', '_blank');
          newTab.document.write(\`
            <script>
              window.onload = function() {
                const popup = window.open('', '', 'width=300,height=200');
                popup.document.write('<p>This is a popup from the new tab.</p>');
                popup.document.close();
                window.close();
              };
            <\\/script>
          \`);
          newTab.document.close();
        }
      </script>
    `);

    await recorder.hoverOverElement('button');
    await recorder.trustedClick();
    const sources = await recorder.waitForOutput('JavaScript', 'Open Tab and Popup');
    expect(sources.get('JavaScript')!.text).not.toContain(`page1.close()`);
  });

Skn0tt avatar Jun 11 '25 17:06 Skn0tt

Thanks for the report, Mikkel! I can repro with this test:

test('should generate correct variables when opening and closing popup', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36274' } }, async ({ openRecorder }) => { const { recorder } = await openRecorder(); });

Hi @Skn0tt, I'm curious about how the openRecord() method was. Could you share this function?

minhphong306 avatar Jun 12 '25 01:06 minhphong306

openRecord() is a method in our own test harness, not a Playwright feature.

The bug here is in how we treat popups. There's two popups from the same button press, but we only take the first into account: https://github.com/microsoft/playwright/blob/64dd5ca5693775b2d1ee5c6e0aeb3e4dc8f03d64/packages/playwright-core/src/server/recorder/recorderCollection.ts#L113-L117 Since the second popup opens on the first popup, I don't see a trivial fix for this.

I'll mark it as P3 for now, let's collect feedback on whether this is a common bug. If it is, that might warrant some bigger changes in codegen to fix it.

Skn0tt avatar Jun 12 '25 08:06 Skn0tt