mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(webkit): implement file chooser interception (frontend) (#98)
This commit is contained in:
parent
494347e009
commit
cf9c4d153a
@ -9,7 +9,7 @@
|
|||||||
"playwright": {
|
"playwright": {
|
||||||
"chromium_revision": "719491",
|
"chromium_revision": "719491",
|
||||||
"firefox_revision": "1004",
|
"firefox_revision": "1004",
|
||||||
"webkit_revision": "1001"
|
"webkit_revision": "1002"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"unit": "node test/test.js",
|
"unit": "node test/test.js",
|
||||||
|
@ -183,7 +183,9 @@ export class ElementHandle extends js.JSHandle<ElementHandle> {
|
|||||||
async setInputFiles(...files: (string|input.FilePayload)[]) {
|
async setInputFiles(...files: (string|input.FilePayload)[]) {
|
||||||
const multiple = await this.evaluate((element: HTMLInputElement) => !!element.multiple);
|
const multiple = await this.evaluate((element: HTMLInputElement) => !!element.multiple);
|
||||||
assert(multiple || files.length <= 1, 'Non-multiple file input can only accept single file!');
|
assert(multiple || files.length <= 1, 'Non-multiple file input can only accept single file!');
|
||||||
await this.evaluate(input.setFileInputFunction, await input.loadFiles(files));
|
const filePayloads = await input.loadFiles(files);
|
||||||
|
const objectId = this._remoteObject.objectId;
|
||||||
|
await this._client.send('DOM.setInputFiles', { objectId, files: filePayloads });
|
||||||
}
|
}
|
||||||
|
|
||||||
async focus() {
|
async focus() {
|
||||||
|
@ -60,6 +60,7 @@ export class Page extends EventEmitter {
|
|||||||
private _disconnectPromise: Promise<Error> | undefined;
|
private _disconnectPromise: Promise<Error> | undefined;
|
||||||
private _sessionListeners: RegisteredListener[] = [];
|
private _sessionListeners: RegisteredListener[] = [];
|
||||||
private _emulatedMediaType: string | undefined;
|
private _emulatedMediaType: string | undefined;
|
||||||
|
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||||
|
|
||||||
static async create(session: TargetSession, target: Target, defaultViewport: Viewport | null, screenshotTaskQueue: TaskQueue): Promise<Page> {
|
static async create(session: TargetSession, target: Target, defaultViewport: Viewport | null, screenshotTaskQueue: TaskQueue): Promise<Page> {
|
||||||
const page = new Page(session, target, screenshotTaskQueue);
|
const page = new Page(session, target, screenshotTaskQueue);
|
||||||
@ -97,6 +98,7 @@ export class Page extends EventEmitter {
|
|||||||
this._frameManager.initialize(),
|
this._frameManager.initialize(),
|
||||||
this._session.send('Console.enable'),
|
this._session.send('Console.enable'),
|
||||||
this._session.send('Dialog.enable'),
|
this._session.send('Dialog.enable'),
|
||||||
|
this._session.send('Page.setInterceptFileChooserDialog', { enabled: true }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +110,8 @@ export class Page extends EventEmitter {
|
|||||||
helper.addEventListener(this._session, 'Page.loadEventFired', event => this.emit(Events.Page.Load)),
|
helper.addEventListener(this._session, 'Page.loadEventFired', event => this.emit(Events.Page.Load)),
|
||||||
helper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)),
|
helper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)),
|
||||||
helper.addEventListener(this._session, 'Page.domContentEventFired', event => this.emit(Events.Page.DOMContentLoaded)),
|
helper.addEventListener(this._session, 'Page.domContentEventFired', event => this.emit(Events.Page.DOMContentLoaded)),
|
||||||
helper.addEventListener(this._session, 'Dialog.javascriptDialogOpening', event => this._onDialog(event))
|
helper.addEventListener(this._session, 'Dialog.javascriptDialogOpening', event => this._onDialog(event)),
|
||||||
|
helper.addEventListener(this._session, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,6 +441,32 @@ export class Page extends EventEmitter {
|
|||||||
return this._closed;
|
return this._closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async waitForFileChooser(options: { timeout?: number; } = {}): 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onFileChooserOpened(event: {frameId: Protocol.Network.FrameId, element: Protocol.Runtime.RemoteObject}) {
|
||||||
|
if (!this._fileChooserInterceptors.size)
|
||||||
|
return;
|
||||||
|
const context = await this._frameManager.frame(event.frameId)._utilityContext();
|
||||||
|
const handle = createJSHandle(context, event.element) as ElementHandle;
|
||||||
|
const interceptors = Array.from(this._fileChooserInterceptors);
|
||||||
|
this._fileChooserInterceptors.clear();
|
||||||
|
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
|
||||||
|
const fileChooser = new FileChooser(handle, multiple);
|
||||||
|
for (const interceptor of interceptors)
|
||||||
|
interceptor.call(null, fileChooser);
|
||||||
|
}
|
||||||
|
|
||||||
get mouse(): input.Mouse {
|
get mouse(): input.Mouse {
|
||||||
return this._mouse;
|
return this._mouse;
|
||||||
}
|
}
|
||||||
@ -557,7 +586,28 @@ export class ConsoleMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaFeature = {
|
export class FileChooser {
|
||||||
name: string,
|
private _element: ElementHandle;
|
||||||
value: string
|
private _multiple: boolean;
|
||||||
|
private _handled = false;
|
||||||
|
|
||||||
|
constructor(element: ElementHandle, multiple: boolean) {
|
||||||
|
this._element = element;
|
||||||
|
this._multiple = multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMultiple(): boolean {
|
||||||
|
return this._multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
async accept(filePaths: string[]): Promise<any> {
|
||||||
|
assert(!this._handled, 'Cannot accept FileChooser which is already handled!');
|
||||||
|
this._handled = true;
|
||||||
|
await this._element.setInputFiles(...filePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancel(): Promise<any> {
|
||||||
|
assert(!this._handled, 'Cannot cancel FileChooser which is already handled!');
|
||||||
|
this._handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip(WEBKIT)('Page.waitForFileChooser', function() {
|
describe('Page.waitForFileChooser', function() {
|
||||||
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([
|
||||||
@ -97,7 +97,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip(WEBKIT)('FileChooser.accept', function() {
|
describe('FileChooser.accept', 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 [chooser] = await Promise.all([
|
const [chooser] = await Promise.all([
|
||||||
@ -161,7 +161,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip(WEBKIT)('FileChooser.cancel', function() {
|
describe('FileChooser.cancel', function() {
|
||||||
it('should cancel dialog', async({page, server}) => {
|
it('should cancel dialog', async({page, server}) => {
|
||||||
// Consider file chooser canceled if we can summon another one.
|
// Consider file chooser canceled if we can summon another one.
|
||||||
// There's no reliable way in WebPlatform to see that FileChooser was
|
// There's no reliable way in WebPlatform to see that FileChooser was
|
||||||
@ -191,7 +191,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip(WEBKIT)('FileChooser.isMultiple', () => {
|
describe('FileChooser.isMultiple', () => {
|
||||||
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 [chooser] = await Promise.all([
|
const [chooser] = await Promise.all([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user