diff --git a/src/helper.ts b/src/helper.ts index e39e9b0c92..5b31ad862c 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -125,7 +125,7 @@ class Helper { }); if (timeout) { eventTimeout = setTimeout(() => { - rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); + rejectCallback(new TimeoutError(`Timeout exceeded while waiting for ${String(eventName)}`)); }, timeout); } function cleanup() { diff --git a/src/page.ts b/src/page.ts index a404f44620..06083c98d7 100644 --- a/src/page.ts +++ b/src/page.ts @@ -98,7 +98,6 @@ export class Page extends EventEmitter { readonly _state: PageState; private _pageBindings = new Map(); readonly _screenshotter: Screenshotter; - private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>(); readonly _frameManager: frames.FrameManager; constructor(delegate: PageDelegate, browserContext: BrowserContext) { @@ -138,30 +137,15 @@ export class Page extends EventEmitter { } async _onFileChooserOpened(handle: dom.ElementHandle) { - if (!this._fileChooserInterceptors.size) { + const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple); + if (!this.listenerCount(Events.Page.FileChooser)) { await handle.dispose(); return; } - const interceptors = Array.from(this._fileChooserInterceptors); - this._fileChooserInterceptors.clear(); - const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple); - const fileChooser = { element: handle, multiple }; - for (const interceptor of interceptors) - interceptor.call(null, fileChooser); + const fileChooser: FileChooser = { element: handle, multiple }; this.emit(Events.Page.FileChooser, fileChooser); } - async waitForFileChooser(options: types.TimeoutOptions = {}): Promise { - const { timeout = this._timeoutSettings.timeout() } = options; - let callback; - const promise = new Promise(x => callback = x); - this._fileChooserInterceptors.add(callback); - return helper.waitWithTimeout(promise, 'waiting for file chooser', timeout).catch(e => { - this._fileChooserInterceptors.delete(callback); - throw e; - }); - } - browser(): BrowserInterface { return this._browserContext.browser(); } diff --git a/src/webkit/NetworkManager.ts b/src/webkit/NetworkManager.ts index d4a981849d..7f4873e4ed 100644 --- a/src/webkit/NetworkManager.ts +++ b/src/webkit/NetworkManager.ts @@ -17,7 +17,7 @@ import { TargetSession } from './Connection'; import { Page } from '../page'; -import { assert, helper, RegisteredListener } from '../helper'; +import { helper, RegisteredListener } from '../helper'; import { Protocol } from './protocol'; import * as network from '../network'; import * as frames from '../frames'; diff --git a/test/input.spec.js b/test/input.spec.js index f25d6a286f..5cc7ba151c 100644 --- a/test/input.spec.js +++ b/test/input.spec.js @@ -40,17 +40,25 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME }); describe('Page.waitForFileChooser', function() { + it('should emit event', async({page, server}) => { + await page.setContent(``); + const [chooser] = await Promise.all([ + new Promise(f => page.once('filechooser', f)), + page.click('input'), + ]); + expect(chooser).toBeTruthy(); + }); it('should work when file input is attached to DOM', async({page, server}) => { await page.setContent(``); const [chooser] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.click('input'), ]); expect(chooser).toBeTruthy(); }); it('should work when file input is not attached to DOM', async({page, server}) => { const [chooser] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.evaluate(() => { const el = document.createElement('input'); el.type = 'file'; @@ -61,24 +69,24 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME }); it('should respect timeout', async({page, server}) => { let error = null; - await page.waitForFileChooser({timeout: 1}).catch(e => error = e); + await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(playwright.errors.TimeoutError); }); it('should respect default timeout when there is no custom timeout', async({page, server}) => { page.setDefaultTimeout(1); let error = null; - await page.waitForFileChooser().catch(e => error = e); + await page.waitForEvent('filechooser').catch(e => error = e); expect(error).toBeInstanceOf(playwright.errors.TimeoutError); }); it('should prioritize exact timeout over default timeout', async({page, server}) => { page.setDefaultTimeout(0); let error = null; - await page.waitForFileChooser({timeout: 1}).catch(e => error = e); + await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(playwright.errors.TimeoutError); }); it('should work with no timeout', async({page, server}) => { const [chooser] = await Promise.all([ - page.waitForFileChooser({timeout: 0}), + page.waitForEvent('filechooser', {timeout: 0}), page.evaluate(() => setTimeout(() => { const el = document.createElement('input'); el.type = 'file'; @@ -90,19 +98,16 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME it('should return the same file chooser when there are many watchdogs simultaneously', async({page, server}) => { await page.setContent(``); const [fileChooser1, fileChooser2] = await Promise.all([ - page.waitForFileChooser(), - page.waitForFileChooser(), + page.waitForEvent('filechooser'), + page.waitForEvent('filechooser'), page.$eval('input', input => input.click()), ]); expect(fileChooser1 === fileChooser2).toBe(true); }); - }); - - describe('Page.waitForFileChooser', function() { it('should accept single file', async({page, server}) => { await page.setContent(``); const [{ element }] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.click('input'), ]); await element.setInputFiles(FILE_TO_UPLOAD); @@ -111,7 +116,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME }); it('should be able to read selected file', async({page, server}) => { await page.setContent(``); - page.waitForFileChooser().then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)); + page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)); expect(await page.$eval('input', async picker => { picker.click(); await new Promise(x => picker.oninput = x); @@ -123,13 +128,13 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME }); it('should be able to reset selected files with empty file list', async({page, server}) => { await page.setContent(``); - page.waitForFileChooser().then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)); + page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)); expect(await page.$eval('input', async picker => { picker.click(); await new Promise(x => picker.oninput = x); return picker.files.length; })).toBe(1); - page.waitForFileChooser().then(({element}) => element.setInputFiles()); + page.waitForEvent('filechooser').then(({element}) => element.setInputFiles()); expect(await page.$eval('input', async picker => { picker.click(); await new Promise(x => picker.oninput = x); @@ -139,7 +144,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME it('should not accept multiple files for single-file input', async({page, server}) => { await page.setContent(``); const [{ element }] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.click('input'), ]); let error = null; @@ -154,7 +159,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME it('should work for single file pick', async({page, server}) => { await page.setContent(``); const [{ multiple }] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.click('input'), ]); expect(multiple).toBe(false); @@ -162,7 +167,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME it('should work for "multiple"', async({page, server}) => { await page.setContent(``); const [{ multiple }] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.click('input'), ]); expect(multiple).toBe(true); @@ -170,7 +175,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME it('should work for "webkitdirectory"', async({page, server}) => { await page.setContent(``); const [{ multiple }] = await Promise.all([ - page.waitForFileChooser(), + page.waitForEvent('filechooser'), page.click('input'), ]); expect(multiple).toBe(true);