Repository: microsoft/playwright
Pull request #35374: Revert "chore: temporarily disable floating promise warning messages (#34957)
Author: login: agg23
Created: 2025-03-26T17:15:07Z | Merged: 2025-03-31T14:16:01Z

This reverts commit 67d6f7f603e19427b91028788a88bf8f24a6ba57.

https://github.com/microsoft/playwright/pull/35374/commits/1e680cd4d0b897048a7bbad054d25fbc1bc72c5d contains minor changes to improve consistency issues that seem to have been created since the feature was originally written.


Diff:
diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts
index c8326ece31a6e..8516ad2fb2f3d 100644
--- a/packages/playwright/src/reporters/base.ts
+++ b/packages/playwright/src/reporters/base.ts
@@ -269,8 +269,7 @@ export class TerminalReporter implements ReporterV2 {
     if (full && summary.failuresToPrint.length && !this._omitFailures)
       this._printFailures(summary.failuresToPrint);
     this._printSlowTests();
-    // TODO: 1.52: Make warning display prettier
-    // this._printWarnings();
+    this._printWarnings();
     this._printSummary(summaryMessage);
   }
 
diff --git a/packages/playwright/src/worker/workerMain.ts b/packages/playwright/src/worker/workerMain.ts
index 54f5bf6968ceb..e7110517ec49e 100644
--- a/packages/playwright/src/worker/workerMain.ts
+++ b/packages/playwright/src/worker/workerMain.ts
@@ -324,17 +324,6 @@ export class WorkerMain extends ProcessRunner {
 
     testInfo._allowSkips = true;
 
-    // Create warning if any of the async calls were not awaited in various stages.
-    const checkForFloatingPromises = (functionDescription: string) => {
-      if (process.env.PW_DISABLE_FLOATING_PROMISES_WARNING)
-        return;
-      if (!testInfo._floatingPromiseScope.hasFloatingPromises())
-        return;
-      // TODO: 1.52: Actually build annotations
-      // testInfo.annotations.push({ type: 'warning', description: `Some async calls were not awaited by the end of ${functionDescription}. This can cause flakiness.` });
-      testInfo._floatingPromiseScope.clear();
-    };
-
     await (async () => {
       await testInfo._runWithTimeout({ type: 'test' }, async () => {
         // Ideally, "trace" would be an config-level option belonging to the
@@ -374,8 +363,6 @@ export class WorkerMain extends ProcessRunner {
         testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, 'test', { type: 'test' });
       });
 
-      checkForFloatingPromises('beforeAll/beforeEach hooks');
-
       if (testFunctionParams === null) {
         // Fixture setup failed or was skipped, we should not run the test now.
         return;
@@ -385,7 +372,6 @@ export class WorkerMain extends ProcessRunner {
         // Now run the test itself.
         const fn = test.fn; // Extract a variable to get a better stack trace ("myTest" vs "TestCase.myTest [as fn]").
         await fn(testFunctionParams, testInfo);
-        checkForFloatingPromises('the test');
       });
     })().catch(() => {});  // Ignore the top-level error, it is already inside TestInfo.errors.
 
@@ -443,7 +429,11 @@ export class WorkerMain extends ProcessRunner {
         throw firstAfterHooksError;
     }).catch(() => {});  // Ignore the top-level error, it is already inside TestInfo.errors.
 
-    checkForFloatingPromises('afterAll/afterEach hooks');
+    // Create warning if any of the async calls were not awaited in various stages.
+    if (!process.env.PW_DISABLE_FLOATING_PROMISES_WARNING && testInfo._floatingPromiseScope.hasFloatingPromises()) {
+      testInfo.annotations.push({ type: 'warning', description: `Some async calls were not awaited by the end of the test. This can cause flakiness.` });
+      testInfo._floatingPromiseScope.clear();
+    }
 
     if (testInfo._isFailure())
       this._isStopped = true;
diff --git a/tests/playwright-test/warnings.spec.ts b/tests/playwright-test/warnings.spec.ts
index 2734c9593e5c1..a19910bd1066d 100644
--- a/tests/playwright-test/warnings.spec.ts
+++ b/tests/playwright-test/warnings.spec.ts
@@ -14,274 +14,257 @@
  * limitations under the License.
  */
 
-// import { JSONReport } from 'packages/playwright-test/reporter';
-// import { test, expect } from './playwright-test-fixtures';
+import { test, expect } from './playwright-test-fixtures';
 
-// const warningSnippet = 'Some async calls were not awaited';
+const warningSnippet = 'Some async calls were not awaited by the end of the test';
 
-// test.describe.configure({ mode: 'parallel' });
+test.describe.configure({ mode: 'parallel' });
 
-// const getWarnings = (report: JSONReport) => report.suites.flatMap(s => s.specs).flatMap(s => s.tests).flatMap(t => t.annotations).filter(a => a.type === 'warning');
+test.describe('await', () => {
+  test('should not care about non-API promises', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test } from '@playwright/test';
+        test('test', () => {
+          new Promise(() => {});
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).not.toContain(warningSnippet);
+  });
 
-// test.describe('await', () => {
-//   test('should not care about non-API promises', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test } from '@playwright/test';
-//         test('test', () => {
-//           new Promise(() => {});
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(0);
-//   });
+  test('should warn about missing await on expects when failing', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('custom test name', async ({ page }) => {
+          expect(page.locator('div')).toHaveText('A', { timeout: 100 });
+          // Timeout to make sure the expect actually gets processed
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(1);
+    expect(stdout).toContain(warningSnippet);
+    expect(stdout).toContain('the test');
+    expect(stdout).toContain('custom test name');
+  });
 
-//   test('should warn about missing await on expects when failing', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('custom test name', async ({ page }) => {
-//           expect(page.locator('div')).toHaveText('A', { timeout: 100 });
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(1);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(1);
-//     expect(warnings[0].description).toContain(warningSnippet);
-//     expect(warnings[0].description).toContain('the test');
-//   });
+  test('should warn about missing await on expects when passing', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          expect(page.locator('div')).toHaveText('A');
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).toContain(warningSnippet);
+  });
 
-//   test('should warn about missing await on expects when passing', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           expect(page.locator('div')).toHaveText('A');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(1);
-//     expect(warnings[0].description).toContain(warningSnippet);
-//   });
+  test('should not warn when not missing await on expects when failing', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await expect(page.locator('div')).toHaveText('A', { timeout: 100 });
+        });
+      `
+    });
+    expect(exitCode).toBe(1);
+    expect(stdout).not.toContain(warningSnippet);
+  });
 
-//   test('should not warn when not missing await on expects when failing', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await expect(page.locator('div')).toHaveText('A', { timeout: 100 });
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(1);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(0);
-//   });
+  test('should not warn when not missing await on expects when passing', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          await expect(page.locator('div')).toHaveText('A');
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).not.toContain(warningSnippet);
+  });
 
-//   test('should not warn when not missing await on expects when passing', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           await expect(page.locator('div')).toHaveText('A');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(0);
-//   });
+  test('should not warn when using then on expects when passing', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          expect(page.locator('div')).toHaveText('A').then(() => {});
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).not.toContain(warningSnippet);
+  });
 
-//   test('should not warn when using then on expects when passing', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           expect(page.locator('div')).toHaveText('A').then(() => {});
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(0);
-//   });
+  test('should warn about missing await on reject', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          expect(Promise.reject(new Error('foo'))).rejects.toThrow('foo');
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).toContain(warningSnippet);
+  });
 
-//   test('should warn about missing await on reject', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           expect(Promise.reject(new Error('foo'))).rejects.toThrow('foo');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(1);
-//     expect(warnings[0].description).toContain(warningSnippet);
-//   });
+  test('should warn about missing await on reject.not', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          expect(Promise.reject(new Error('foo'))).rejects.not.toThrow('foo');
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(1);
+    expect(stdout).toContain(warningSnippet);
+  });
 
-//   test('should warn about missing await on reject.not', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           expect(Promise.reject(new Error('foo'))).rejects.not.toThrow('foo');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(1);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(1);
-//     expect(warnings[0].description).toContain(warningSnippet);
-//   });
+  test('should warn about missing await on test.step', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          test.step('step', () => {});
+          await expect(page.locator('div')).toHaveText('A');
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).toContain(warningSnippet);
+  });
 
-//   test('should warn about missing await on test.step', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           test.step('step', () => {});
-//           await expect(page.locator('div')).toHaveText('A');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(1);
-//     expect(warnings[0].description).toContain(warningSnippet);
-//   });
+  test('should not warn when not missing await on test.step', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          await test.step('step', () => {});
+          await expect(page.locator('div')).toHaveText('A');
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).not.toContain(warningSnippet);
+  });
 
-//   test('should not warn when not missing await on test.step', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           await test.step('step', () => {});
-//           await expect(page.locator('div')).toHaveText('A');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(0);
-//   });
+  test('should warn about missing await on test.step.skip', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          test.step.skip('step', () => {});
+          await expect(page.locator('div')).toHaveText('A');
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).toContain(warningSnippet);
+  });
 
-//   test('should warn about missing await on test.step.skip', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           test.step.skip('step', () => {});
-//           await expect(page.locator('div')).toHaveText('A');
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(1);
-//     expect(warnings[0].description).toContain(warningSnippet);
-//   });
+  test('traced promise should be instanceof Promise', async ({ runInlineTest }) => {
+    const { exitCode } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test('test', async ({ page }) => {
+          await page.setContent('<div>A</div>');
+          const expectPromise = expect(page.locator('div')).toHaveText('A');
+          expect(expectPromise instanceof Promise).toBeTruthy();
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+  });
 
-//   test('traced promise should be instanceof Promise', async ({ runInlineTest }) => {
-//     const { exitCode } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test('test', async ({ page }) => {
-//           await page.setContent('<div>A</div>');
-//           const expectPromise = expect(page.locator('div')).toHaveText('A');
-//           expect(expectPromise instanceof Promise).toBeTruthy();
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//   });
+  test('should warn about missing await in before hooks', async ({ runInlineTest }) => {
+    const group = ['beforeAll', 'beforeEach'];
+    for (const hook of group) {
+      await test.step(hook, async () => {
+        const { exitCode, stdout } = await runInlineTest({
+          'a.test.ts': `
+            import { test, expect } from '@playwright/test';
+            let page;
+            test.${hook}(async ({ browser }) => {
+              page = await browser.newPage();
+              await page.setContent('<div>A</div>');
+              expect(page.locator('div')).toHaveText('A');
+              await new Promise(f => setTimeout(f, 1000));
+            });
+            test('test ${hook}', async () => {
+              await expect(page.locator('div')).toBeVisible();
+            });
+          `
+        });
 
-//   test('should warn about missing await in before hooks', async ({ runInlineTest }) => {
-//     const group = ['beforeAll', 'beforeEach'];
-//     for (const hook of group) {
-//       await test.step(hook, async () => {
-//         const { exitCode, report } = await runInlineTest({
-//           'a.test.ts': `
-//             import { test, expect } from '@playwright/test';
-//             let page;
-//             test.${hook}(async ({ browser }) => {
-//               page = await browser.newPage();
-//               await page.setContent('<div>A</div>');
-//               expect(page.locator('div')).toHaveText('A');
-//             });
-//             test('test ${hook}', async () => {
-//               await expect(page.locator('div')).toBeVisible();
-//             });
-//           `
-//         });
+        expect(exitCode).toBe(0);
+        expect(stdout).toContain(warningSnippet);
+      });
+    }
+  });
 
-//         expect(exitCode).toBe(0);
-//         const warnings = getWarnings(report);
-//         expect(warnings.length).toEqual(1);
-//         expect(warnings[0].description).toContain(warningSnippet);
-//         expect(warnings[0].description).toContain(`${group[0]}/${group[1]} hooks`);
-//       });
-//     }
-//   });
+  test.describe('should warn about missing await in after hooks', () => {
+    const group = ['afterAll', 'afterEach'];
+    for (const hook of group) {
+      test(hook, async ({ runInlineTest }) => {
+        const { exitCode, stdout } = await runInlineTest({
+          'a.test.ts': `
+            import { test, expect } from '@playwright/test';
+            let page;
+            test('test ${hook}', async ({ browser }) => {
+              await expect(Promise.resolve()).resolves.toBe(undefined);
+            });
+            test.${hook}(async () => {
+              expect(Promise.resolve()).resolves.toBe(undefined);
+              await new Promise(f => setTimeout(f, 1000));
+            });
+          `
+        });
 
-//   test.describe('should warn about missing await in after hooks', () => {
-//     const group = ['afterAll', 'afterEach'];
-//     for (const hook of group) {
-//       test(hook, async ({ runInlineTest }) => {
-//         const { exitCode, report } = await runInlineTest({
-//           'a.test.ts': `
-//             import { test, expect } from '@playwright/test';
-//             let page;
-//             test('test ${hook}', async ({ browser }) => {
-//               await expect(Promise.resolve()).resolves.toBe(undefined);
-//             });
-//             test.${hook}(async () => {
-//               expect(Promise.resolve()).resolves.toBe(undefined);
-//             });
-//           `
-//         });
+        expect(exitCode).toBe(0);
+        expect(stdout).toContain(warningSnippet);
+      });
+    }
+  });
 
-//         expect(exitCode).toBe(0);
-//         const warnings = getWarnings(report);
-//         expect(warnings.length).toEqual(1);
-//         expect(warnings[0].description).toContain(warningSnippet);
-//         expect(warnings[0].description).toContain(`${group[0]}/${group[1]} hooks`);
-//       });
-//     }
-//   });
-
-//   test('should warn about missing await across hooks and test', async ({ runInlineTest }) => {
-//     const { exitCode, report } = await runInlineTest({
-//       'a.test.ts': `
-//         import { test, expect } from '@playwright/test';
-//         test.beforeAll(async () => {
-//           expect(Promise.resolve()).resolves.toBe(undefined);
-//         });
-//         test('test', async () => {
-//           expect(Promise.resolve()).resolves.toBe(undefined);
-//         });
-//         test.afterEach(async () => {
-//           expect(Promise.resolve()).resolves.toBe(undefined);
-//         });
-//       `
-//     });
-//     expect(exitCode).toBe(0);
-//     const warnings = getWarnings(report);
-//     expect(warnings.length).toEqual(3);
-//     expect(warnings[0].description).toContain(`${warningSnippet} by the end of beforeAll/beforeEach hooks.`);
-//     expect(warnings[1].description).toContain(`${warningSnippet} by the end of the test.`);
-//     expect(warnings[2].description).toContain(`${warningSnippet} by the end of afterAll/afterEach hooks.`);
-//   });
-// });
+  test('should warn about missing await across hooks and test', async ({ runInlineTest }) => {
+    const { exitCode, stdout } = await runInlineTest({
+      'a.test.ts': `
+        import { test, expect } from '@playwright/test';
+        test.beforeAll(async () => {
+          expect(Promise.resolve()).resolves.toBe(undefined);
+          await new Promise(f => setTimeout(f, 1000));
+        });
+        test('test', async () => {
+          expect(Promise.resolve()).resolves.toBe(undefined);
+          await new Promise(f => setTimeout(f, 1000));
+        });
+        test.afterEach(async () => {
+          expect(Promise.resolve()).resolves.toBe(undefined);
+          await new Promise(f => setTimeout(f, 1000));
+        });
+      `
+    });
+    expect(exitCode).toBe(0);
+    expect(stdout).toContain(warningSnippet);
+  });
+});


Reviews:
user: login: dgozman
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T15:04:03Z
user: login: dgozman
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-31T14:11:40Z

Review comments:
diff_hunk: @@ -23,9 +23,6 @@ export class FloatingPromiseScope {
    * **NOTE:** Returning from an async function wraps the result in a promise, regardless of whether the return value is a promise. This will automatically mark the promise as awaited. Avoid this.
    */
   wrapPromiseAPIResult<T>(promise: Promise<T>): Promise<T> {
-    if (process.env.PW_DISABLE_FLOATING_PROMISES_WARNING)
path: packages/playwright/src/worker/floatingPromiseScope.ts
user: login: dgozman
body: I'd leave the opt-out flag for one more release.
created_at: 2025-03-27T14:58:28Z
author_association: COLLABORATOR
diff_hunk: @@ -374,7 +363,7 @@ export class WorkerMain extends ProcessRunner {
         testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, 'test', { type: 'test' });
       });
 
-      checkForFloatingPromises('beforeAll/beforeEach hooks');
+      testInfo._checkForFloatingPromises('beforeAll/beforeEach hooks');
path: packages/playwright/src/worker/workerMain.ts
user: login: dgozman
body: To be consistent with "Before Hooks" step in tracing/reports.

```suggestion
      testInfo._checkForFloatingPromises('Before Hooks');
```
created_at: 2025-03-27T14:59:38Z
author_association: COLLABORATOR
diff_hunk: @@ -384,8 +373,13 @@ export class WorkerMain extends ProcessRunner {
       await testInfo._runWithTimeout({ type: 'test' }, async () => {
         // Now run the test itself.
         const fn = test.fn; // Extract a variable to get a better stack trace ("myTest" vs "TestCase.myTest [as fn]").
-        await fn(testFunctionParams, testInfo);
-        checkForFloatingPromises('the test');
+        try {
+          await fn(testFunctionParams, testInfo);
+        } catch (e) {
path: packages/playwright/src/worker/workerMain.ts
user: login: dgozman
body: There is no need for the catch block that just re-throws.
created_at: 2025-03-27T15:00:00Z
author_association: COLLABORATOR
diff_hunk: @@ -443,7 +437,7 @@ export class WorkerMain extends ProcessRunner {
         throw firstAfterHooksError;
     }).catch(() => {});  // Ignore the top-level error, it is already inside TestInfo.errors.
 
-    checkForFloatingPromises('afterAll/afterEach hooks');
+    testInfo._checkForFloatingPromises('afterAll/afterEach hooks');
path: packages/playwright/src/worker/workerMain.ts
user: login: dgozman
body: ```suggestion
    testInfo._checkForFloatingPromises('After Hooks');
```
created_at: 2025-03-27T15:00:27Z
author_association: COLLABORATOR
diff_hunk: @@ -384,8 +373,13 @@ export class WorkerMain extends ProcessRunner {
       await testInfo._runWithTimeout({ type: 'test' }, async () => {
         // Now run the test itself.
         const fn = test.fn; // Extract a variable to get a better stack trace ("myTest" vs "TestCase.myTest [as fn]").
-        await fn(testFunctionParams, testInfo);
-        checkForFloatingPromises('the test');
+        try {
+          await fn(testFunctionParams, testInfo);
+        } catch (e) {
+          throw e;
+        } finally {
+          testInfo._checkForFloatingPromises('the test');
path: packages/playwright/src/worker/workerMain.ts
user: login: dgozman
body: Perhaps only do this on success? A failing test already has some errors to worry about, wdyt? Note I do not feel strongly about this.
created_at: 2025-03-27T15:02:47Z
author_association: COLLABORATOR
diff_hunk: @@ -346,6 +346,16 @@ export class TestInfoImpl implements TestInfo {
       serialized.stack = `${(error as Error).name}: ${(error as Error).message}\n${stringifyStackFrames(step.boxedStack).join('\n')}`;
     this.errors.push(serialized);
     this._tracing.appendForError(serialized);
+    // We don't know if this occurred in test or hooks
+    this._checkForFloatingPromises('the test');
path: packages/playwright/src/worker/testInfo.ts
user: login: dgozman
body: Why specifically here? Sounds like a random place in the middle of a test. We should do it at the end to avoid false positives.
created_at: 2025-03-27T15:03:58Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-26T18:01:59Z
body: ### [Test results](https://mspwblobreport.z1.web.core.windows.net/run-14089437914-1-5af22ed72e6e9eb799b71d558641813222244e95/index.html#?q=s%3Afailed%20s%3Aflaky) for "tests 1"
**15 failed**
:x: [playwright-test] › tests/warnings.spec.ts:52:7 › await › should warn about missing await on expects when passing @macos-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:93:7 › await › should not warn when using then on expects when passing @macos-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:178:7 › await › traced promise should be instanceof Promise @macos-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:52:7 › await › should warn about missing await on expects when passing @ubuntu-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:93:7 › await › should not warn when using then on expects when passing @ubuntu-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:178:7 › await › traced promise should be instanceof Promise @ubuntu-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:52:7 › await › should warn about missing await on expects when passing @ubuntu-latest-node20-1
:x: [playwright-test] › tests/warnings.spec.ts:93:7 › await › should not warn when using then on expects when passing @ubuntu-latest-node20-1
:x: [playwright-test] › tests/warnings.spec.ts:178:7 › await › traced promise should be instanceof Promise @ubuntu-latest-node20-1
:x: [playwright-test] › tests/warnings.spec.ts:52:7 › await › should warn about missing await on expects when passing @ubuntu-latest-node22-1
:x: [playwright-test] › tests/warnings.spec.ts:93:7 › await › should not warn when using then on expects when passing @ubuntu-latest-node22-1
:x: [playwright-test] › tests/warnings.spec.ts:178:7 › await › traced promise should be instanceof Promise @ubuntu-latest-node22-1
:x: [playwright-test] › tests/warnings.spec.ts:52:7 › await › should warn about missing await on expects when passing @windows-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:93:7 › await › should not warn when using then on expects when passing @windows-latest-node18-1
:x: [playwright-test] › tests/warnings.spec.ts:178:7 › await › traced promise should be instanceof Promise @windows-latest-node18-1

<details>
<summary><b>3 flaky</b></summary>
:warning: [firefox-page] › tests/page/page-evaluate.spec.ts:424:3 › should throw for too deep reference chain @firefox-ubuntu-22.04-node18 <br/>
:warning: [chromium-library] › tests/library/inspector/cli-codegen-1.spec.ts:23:7 › cli codegen › should click @ubuntu-22.04-chromium-tip-of-tree <br/>
:warning: [webkit-page] › tests/page/page-screenshot.spec.ts:345:5 › page screenshot › should work while navigating @webkit-ubuntu-22.04-node18 <br/>

</details>

**38947 passed, 805 skipped**
:heavy_check_mark::heavy_check_mark::heavy_check_mark:


Merge [workflow run](https://github.com/microsoft/playwright/actions/runs/14090271082).
author_association: CONTRIBUTOR
user: login: agg23
created_at: 2025-03-27T12:42:14Z
body: https://github.com/microsoft/playwright/pull/35374/commits/1e680cd4d0b897048a7bbad054d25fbc1bc72c5d contains minor changes to improve consistency issues that seem to have been created since the feature was originally written.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-27T13:30:11Z
body: ### [Test results](https://mspwblobreport.z1.web.core.windows.net/run-14106829552-1-f5a7465a4152c9ed6a21f7c1bcc13cc3a20fa11b/index.html#?q=s%3Afailed%20s%3Aflaky) for "tests 1"
**1 failed**
:x: [playwright-test] › tests/runner.spec.ts:118:5 › should ignore subprocess creation error because of SIGINT @macos-latest-node18-1

<details>
<summary><b>6 flaky</b></summary>
:warning: [firefox-library] › tests/library/inspector/cli-codegen-3.spec.ts:222:7 › cli codegen › should generate frame locators (4) @firefox-ubuntu-22.04-node18 <br/>
:warning: [firefox-page] › tests/page/page-evaluate.spec.ts:424:3 › should throw for too deep reference chain @firefox-ubuntu-22.04-node18 <br/>
:warning: [webkit-library] › tests/library/browsercontext-pages.spec.ts:82:3 › should click the button with offset with page scale @webkit-ubuntu-22.04-node18 <br/>
:warning: [webkit-page] › tests/page/page-event-popup.spec.ts:28:3 › should work with window features @webkit-ubuntu-22.04-node18 <br/>
:warning: [webkit-page] › tests/page/page-screenshot.spec.ts:345:5 › page screenshot › should work while navigating @webkit-ubuntu-22.04-node18 <br/>
:warning: [playwright-test] › tests/ui-mode-test-watch.spec.ts:145:5 › should watch all @windows-latest-node18-1 <br/>

</details>

**38964 passed, 805 skipped**
:heavy_check_mark::heavy_check_mark::heavy_check_mark:


Merge [workflow run](https://github.com/microsoft/playwright/actions/runs/14107778833).
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-28T18:18:56Z
body: ### [Test results](https://mspwblobreport.z1.web.core.windows.net/run-14134574789-1-fbf519c5a548f99910368bfd8d5080c3cf58d419/index.html#?q=s%3Afailed%20s%3Aflaky) for "tests 1"
<details>
<summary><b>2 flaky</b></summary>
:warning: [firefox-page] › tests/page/page-evaluate.spec.ts:424:3 › should throw for too deep reference chain @firefox-ubuntu-22.04-node18 <br/>
:warning: [webkit-library] › tests/library/trace-viewer.spec.ts:781:1 › should handle src=blob @webkit-ubuntu-22.04-node18 <br/>

</details>

**39017 passed, 805 skipped**
:heavy_check_mark::heavy_check_mark::heavy_check_mark:


Merge [workflow run](https://github.com/microsoft/playwright/actions/runs/14135330071).
author_association: CONTRIBUTOR