feature(filechooser): move waitForFileChooser to common waitForEvent (#281)

This commit is contained in:
Dmitry Gozman 2019-12-17 17:34:32 -08:00 committed by GitHub
parent 533d058ea6
commit 0f8333ba89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 40 deletions

View File

@ -125,7 +125,7 @@ class Helper {
}); });
if (timeout) { if (timeout) {
eventTimeout = setTimeout(() => { eventTimeout = setTimeout(() => {
rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); rejectCallback(new TimeoutError(`Timeout exceeded while waiting for ${String(eventName)}`));
}, timeout); }, timeout);
} }
function cleanup() { function cleanup() {

View File

@ -98,7 +98,6 @@ export class Page extends EventEmitter {
readonly _state: PageState; readonly _state: PageState;
private _pageBindings = new Map<string, Function>(); private _pageBindings = new Map<string, Function>();
readonly _screenshotter: Screenshotter; readonly _screenshotter: Screenshotter;
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
readonly _frameManager: frames.FrameManager; readonly _frameManager: frames.FrameManager;
constructor(delegate: PageDelegate, browserContext: BrowserContext) { constructor(delegate: PageDelegate, browserContext: BrowserContext) {
@ -138,30 +137,15 @@ export class Page extends EventEmitter {
} }
async _onFileChooserOpened(handle: dom.ElementHandle) { 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(); await handle.dispose();
return; return;
} }
const interceptors = Array.from(this._fileChooserInterceptors); const fileChooser: FileChooser = { element: handle, multiple };
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);
this.emit(Events.Page.FileChooser, fileChooser); this.emit(Events.Page.FileChooser, fileChooser);
} }
async waitForFileChooser(options: types.TimeoutOptions = {}): Promise<FileChooser> {
const { timeout = this._timeoutSettings.timeout() } = options;
let callback;
const promise = new Promise<FileChooser>(x => callback = x);
this._fileChooserInterceptors.add(callback);
return helper.waitWithTimeout<FileChooser>(promise, 'waiting for file chooser', timeout).catch(e => {
this._fileChooserInterceptors.delete(callback);
throw e;
});
}
browser(): BrowserInterface { browser(): BrowserInterface {
return this._browserContext.browser(); return this._browserContext.browser();
} }

View File

@ -17,7 +17,7 @@
import { TargetSession } from './Connection'; import { TargetSession } from './Connection';
import { Page } from '../page'; import { Page } from '../page';
import { assert, helper, RegisteredListener } from '../helper'; import { helper, RegisteredListener } from '../helper';
import { Protocol } from './protocol'; import { Protocol } from './protocol';
import * as network from '../network'; import * as network from '../network';
import * as frames from '../frames'; import * as frames from '../frames';

View File

@ -40,17 +40,25 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
}); });
describe('Page.waitForFileChooser', function() { describe('Page.waitForFileChooser', function() {
it('should emit event', async({page, server}) => {
await page.setContent(`<input type=file>`);
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}) => { it('should work when file input is attached to DOM', async({page, server}) => {
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.click('input'), page.click('input'),
]); ]);
expect(chooser).toBeTruthy(); expect(chooser).toBeTruthy();
}); });
it('should work when file input is not attached to DOM', async({page, server}) => { it('should work when file input is not attached to DOM', async({page, server}) => {
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.evaluate(() => { page.evaluate(() => {
const el = document.createElement('input'); const el = document.createElement('input');
el.type = 'file'; el.type = 'file';
@ -61,24 +69,24 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
}); });
it('should respect timeout', async({page, server}) => { it('should respect timeout', async({page, server}) => {
let error = null; 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); expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
}); });
it('should respect default timeout when there is no custom timeout', async({page, server}) => { it('should respect default timeout when there is no custom timeout', async({page, server}) => {
page.setDefaultTimeout(1); page.setDefaultTimeout(1);
let error = null; let error = null;
await page.waitForFileChooser().catch(e => error = e); await page.waitForEvent('filechooser').catch(e => error = e);
expect(error).toBeInstanceOf(playwright.errors.TimeoutError); expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
}); });
it('should prioritize exact timeout over default timeout', async({page, server}) => { it('should prioritize exact timeout over default timeout', async({page, server}) => {
page.setDefaultTimeout(0); page.setDefaultTimeout(0);
let error = null; 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); expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
}); });
it('should work with no timeout', async({page, server}) => { it('should work with no timeout', async({page, server}) => {
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser({timeout: 0}), page.waitForEvent('filechooser', {timeout: 0}),
page.evaluate(() => setTimeout(() => { page.evaluate(() => setTimeout(() => {
const el = document.createElement('input'); const el = document.createElement('input');
el.type = 'file'; 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}) => { it('should return the same file chooser when there are many watchdogs simultaneously', async({page, server}) => {
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
const [fileChooser1, fileChooser2] = await Promise.all([ const [fileChooser1, fileChooser2] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.$eval('input', input => input.click()), page.$eval('input', input => input.click()),
]); ]);
expect(fileChooser1 === fileChooser2).toBe(true); expect(fileChooser1 === fileChooser2).toBe(true);
}); });
});
describe('Page.waitForFileChooser', function() {
it('should accept single file', async({page, server}) => { it('should accept single file', async({page, server}) => {
await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`); await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`);
const [{ element }] = await Promise.all([ const [{ element }] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.click('input'), page.click('input'),
]); ]);
await element.setInputFiles(FILE_TO_UPLOAD); 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}) => { it('should be able to read selected file', async({page, server}) => {
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
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 => { expect(await page.$eval('input', async picker => {
picker.click(); picker.click();
await new Promise(x => picker.oninput = x); 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}) => { it('should be able to reset selected files with empty file list', async({page, server}) => {
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
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 => { expect(await page.$eval('input', async picker => {
picker.click(); picker.click();
await new Promise(x => picker.oninput = x); await new Promise(x => picker.oninput = x);
return picker.files.length; return picker.files.length;
})).toBe(1); })).toBe(1);
page.waitForFileChooser().then(({element}) => element.setInputFiles()); page.waitForEvent('filechooser').then(({element}) => element.setInputFiles());
expect(await page.$eval('input', async picker => { expect(await page.$eval('input', async picker => {
picker.click(); picker.click();
await new Promise(x => picker.oninput = x); 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}) => { it('should not accept multiple files for single-file input', async({page, server}) => {
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
const [{ element }] = await Promise.all([ const [{ element }] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.click('input'), page.click('input'),
]); ]);
let error = null; 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}) => { it('should work for single file pick', async({page, server}) => {
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
const [{ multiple }] = await Promise.all([ const [{ multiple }] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.click('input'), page.click('input'),
]); ]);
expect(multiple).toBe(false); 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}) => { it('should work for "multiple"', async({page, server}) => {
await page.setContent(`<input multiple type=file>`); await page.setContent(`<input multiple type=file>`);
const [{ multiple }] = await Promise.all([ const [{ multiple }] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.click('input'), page.click('input'),
]); ]);
expect(multiple).toBe(true); 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}) => { it('should work for "webkitdirectory"', async({page, server}) => {
await page.setContent(`<input multiple webkitdirectory type=file>`); await page.setContent(`<input multiple webkitdirectory type=file>`);
const [{ multiple }] = await Promise.all([ const [{ multiple }] = await Promise.all([
page.waitForFileChooser(), page.waitForEvent('filechooser'),
page.click('input'), page.click('input'),
]); ]);
expect(multiple).toBe(true); expect(multiple).toBe(true);