diff --git a/docs/src/api/class-download.md b/docs/src/api/class-download.md index a6407bcc1a..701354b377 100644 --- a/docs/src/api/class-download.md +++ b/docs/src/api/class-download.md @@ -8,9 +8,13 @@ browser context is closed. Download event is emitted once the download starts. Download path becomes available once download completes: ```js +// Note that Promise.all prevents a race condition +// between clicking and waiting for the download. const [ download ] = await Promise.all([ - page.waitForEvent('download'), // wait for download to start - page.click('a') + // It is important to call waitForEvent before click to set up waiting. + page.waitForEvent('download'), + // Triggers the download. + page.locator('text=Download file').click(), ]); // wait for download to complete const path = await download.path(); diff --git a/docs/src/api/class-filechooser.md b/docs/src/api/class-filechooser.md index a3afa27354..85e19e4d6b 100644 --- a/docs/src/api/class-filechooser.md +++ b/docs/src/api/class-filechooser.md @@ -3,9 +3,13 @@ [FileChooser] objects are dispatched by the page in the [`event: Page.fileChooser`] event. ```js +// Note that Promise.all prevents a race condition +// between clicking and waiting for the file chooser. const [fileChooser] = await Promise.all([ + // It is important to call waitForEvent before click to set up waiting. page.waitForEvent('filechooser'), - page.click('upload') + // Opens the file chooser. + page.locator('text=Upload').click(), ]); await fileChooser.setFiles('myfile.pdf'); ``` diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 68a39f1327..8840dbd85c 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -366,8 +366,12 @@ popup with `window.open('http://example.com')`, this event will fire when the ne done and its response has started loading in the popup. ```js +// Note that Promise.all prevents a race condition +// between evaluating and waiting for the popup. const [popup] = await Promise.all([ + // It is important to call waitForEvent first. page.waitForEvent('popup'), + // Opens the popup. page.evaluate(() => window.open('https://example.com')), ]); console.log(await popup.evaluate('location.href')); @@ -3122,9 +3126,13 @@ Waits for event to fire and passes its value into the predicate function. Return value. Will throw an error if the page is closed before the event is fired. Returns the event data value. ```js +// Note that Promise.all prevents a race condition +// between clicking and waiting for the event. const [frame, _] = await Promise.all([ + // It is important to call waitForEvent before click to set up waiting. page.waitForEvent('framenavigated'), - page.click('button') + // Triggers the navigation. + page.locator('button').click(), ]); ``` @@ -3331,8 +3339,10 @@ await page.WaitForLoadStateAsync(); // The promise resolves after 'load' event. ```js const [popup] = await Promise.all([ + // It is important to call waitForEvent before click to set up waiting. page.waitForEvent('popup'), - page.click('button'), // Click triggers a popup. + // Click triggers a popup. + page.locator('button').click(), ]) await popup.waitForLoadState('domcontentloaded'); // The promise resolves after 'domcontentloaded' event. console.log(await popup.title()); // Popup is ready to use. @@ -3394,9 +3404,13 @@ cause the page to navigate. e.g. The click target has an `onclick` handler that Consider this example: ```js +// Note that Promise.all prevents a race condition +// between clicking and waiting for the navigation. const [response] = await Promise.all([ - page.waitForNavigation(), // The promise resolves after navigation has finished - page.click('a.delayed-navigation'), // Clicking the link will indirectly cause a navigation + // It is important to call waitForNavigation before click to set up waiting. + page.waitForNavigation(), + // Clicking the link will indirectly cause a navigation. + page.locator('a.delayed-navigation').click(), ]); ``` diff --git a/docs/src/downloads.md b/docs/src/downloads.md index c1d8b19de5..88e4224a42 100644 --- a/docs/src/downloads.md +++ b/docs/src/downloads.md @@ -24,7 +24,7 @@ const [ download ] = await Promise.all([ // Start waiting for the download page.waitForEvent('download'), // Perform the action that initiates download - page.click('button#delayed-download') + page.locator('button#delayed-download').click(), ]); // Wait for the download process to complete const path = await download.path(); diff --git a/docs/src/events.md b/docs/src/events.md index 53c75715c2..c2ac8961c4 100644 --- a/docs/src/events.md +++ b/docs/src/events.md @@ -62,6 +62,7 @@ Wait for popup window: // Note that Promise.all prevents a race condition // between clicking and waiting for the popup. const [popup] = await Promise.all([ + // It is important to call waitForEvent first. page.waitForEvent('popup'), // This action triggers the popup page.evaluate('window.open()') diff --git a/docs/src/input.md b/docs/src/input.md index 4edff73b49..51fb43368e 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -721,9 +721,12 @@ If you don't have input element in hand (it is created dynamically), you can han or use a corresponding waiting method upon your action: ```js +// Note that Promise.all prevents a race condition +// between clicking and waiting for the file chooser. const [fileChooser] = await Promise.all([ + // It is important to call waitForEvent before click to set up waiting. page.waitForEvent('filechooser'), - page.click('upload') + page.locator('upload').click(), ]); await fileChooser.setFiles('myfile.pdf'); ``` diff --git a/docs/src/navigations.md b/docs/src/navigations.md index 04ed499dd4..d05932867a 100644 --- a/docs/src/navigations.md +++ b/docs/src/navigations.md @@ -313,8 +313,11 @@ recommended to explicitly call [`method: Page.waitForNavigation`]. For example: // Note that Promise.all prevents a race condition // between clicking and waiting for a navigation. await Promise.all([ - page.waitForNavigation(), // Waits for the next navigation - page.click('div.delayed-navigation'), // Triggers a navigation after a timeout + // Waits for the next navigation. + // It is important to call waitForNavigation before click to set up waiting. + page.waitForNavigation(), + // Triggers a navigation after a timeout. + page.locator('div.delayed-navigation').clikc(), ]); ``` @@ -363,8 +366,10 @@ Clicking an element could trigger multiple navigations. In these cases, it is re // Note that Promise.all prevents a race condition // between clicking and waiting for a navigation. await Promise.all([ + // It is important to call waitForNavigation before click to set up waiting. page.waitForNavigation({ url: '**/login' }), - page.click('a'), // Triggers a navigation with a script redirect + // Triggers a navigation with a script redirect. + page.locator('text=Click me').click(), ]); ``` @@ -411,9 +416,13 @@ When popup is opened, explicitly calling [`method: Page.waitForLoadState`] ensur state. ```js +// Note that Promise.all prevents a race condition +// between clicking and waiting for the popup. const [ popup ] = await Promise.all([ + // It is important to call waitForEvent before click to set up waiting. page.waitForEvent('popup'), - page.click('a[target="_blank"]'), // Opens popup + // Opens popup. + page.locator('a[target="_blank"]').click(), ]); await popup.waitForLoadState('load'); ``` diff --git a/docs/src/pages.md b/docs/src/pages.md index 34974ee447..414ea409c0 100644 --- a/docs/src/pages.md +++ b/docs/src/pages.md @@ -241,10 +241,13 @@ If the page opens a pop-up (e.g. pages opened by `target="_blank"` links), you c This event is emitted in addition to the `browserContext.on('page')` event, but only for popups relevant to this page. ```js -// Get popup after a specific action (e.g., click) +// Note that Promise.all prevents a race condition +// between clicking and waiting for the popup. const [popup] = await Promise.all([ + // It is important to call waitForEvent before click to set up waiting. page.waitForEvent('popup'), - page.click('#open') + // Opens popup. + page.locator('#open').click(), ]); await popup.waitForLoadState(); console.log(await popup.title()); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 86fb5d9e0b..f83af432a7 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -955,8 +955,12 @@ export interface Page { * done and its response has started loading in the popup. * * ```js + * // Note that Promise.all prevents a race condition + * // between evaluating and waiting for the popup. * const [popup] = await Promise.all([ + * // It is important to call waitForEvent first. * page.waitForEvent('popup'), + * // Opens the popup. * page.evaluate(() => window.open('https://example.com')), * ]); * console.log(await popup.evaluate('location.href')); @@ -1224,8 +1228,12 @@ export interface Page { * done and its response has started loading in the popup. * * ```js + * // Note that Promise.all prevents a race condition + * // between evaluating and waiting for the popup. * const [popup] = await Promise.all([ + * // It is important to call waitForEvent first. * page.waitForEvent('popup'), + * // Opens the popup. * page.evaluate(() => window.open('https://example.com')), * ]); * console.log(await popup.evaluate('location.href')); @@ -3578,8 +3586,12 @@ export interface Page { * done and its response has started loading in the popup. * * ```js + * // Note that Promise.all prevents a race condition + * // between evaluating and waiting for the popup. * const [popup] = await Promise.all([ + * // It is important to call waitForEvent first. * page.waitForEvent('popup'), + * // Opens the popup. * page.evaluate(() => window.open('https://example.com')), * ]); * console.log(await popup.evaluate('location.href')); @@ -3646,8 +3658,10 @@ export interface Page { * * ```js * const [popup] = await Promise.all([ + * // It is important to call waitForEvent before click to set up waiting. * page.waitForEvent('popup'), - * page.click('button'), // Click triggers a popup. + * // Click triggers a popup. + * page.locator('button').click(), * ]) * await popup.waitForLoadState('domcontentloaded'); // The promise resolves after 'domcontentloaded' event. * console.log(await popup.title()); // Popup is ready to use. @@ -3683,9 +3697,13 @@ export interface Page { * Consider this example: * * ```js + * // Note that Promise.all prevents a race condition + * // between clicking and waiting for the navigation. * const [response] = await Promise.all([ - * page.waitForNavigation(), // The promise resolves after navigation has finished - * page.click('a.delayed-navigation'), // Clicking the link will indirectly cause a navigation + * // It is important to call waitForNavigation before click to set up waiting. + * page.waitForNavigation(), + * // Clicking the link will indirectly cause a navigation. + * page.locator('a.delayed-navigation').click(), * ]); * ``` * @@ -13257,9 +13275,13 @@ export interface Dialog { * Download event is emitted once the download starts. Download path becomes available once download completes: * * ```js + * // Note that Promise.all prevents a race condition + * // between clicking and waiting for the download. * const [ download ] = await Promise.all([ - * page.waitForEvent('download'), // wait for download to start - * page.click('a') + * // It is important to call waitForEvent before click to set up waiting. + * page.waitForEvent('download'), + * // Triggers the download. + * page.locator('text=Download file').click(), * ]); * // wait for download to complete * const path = await download.path(); @@ -13529,9 +13551,13 @@ export interface Electron { * [page.on('filechooser')](https://playwright.dev/docs/api/class-page#page-event-file-chooser) event. * * ```js + * // Note that Promise.all prevents a race condition + * // between clicking and waiting for the file chooser. * const [fileChooser] = await Promise.all([ + * // It is important to call waitForEvent before click to set up waiting. * page.waitForEvent('filechooser'), - * page.click('upload') + * // Opens the file chooser. + * page.locator('text=Upload').click(), * ]); * await fileChooser.setFiles('myfile.pdf'); * ```