chrome(filechooser): align file chooser implementations (#88)

This commit is contained in:
Pavel Feldman 2019-11-26 14:29:21 -08:00 committed by GitHub
parent 1c40eb0b28
commit 64d3e83ddf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 119 additions and 108 deletions

View File

@ -270,10 +270,10 @@
* [elementHandle.press(key[, options])](#elementhandlepresskey-options) * [elementHandle.press(key[, options])](#elementhandlepresskey-options)
* [elementHandle.screenshot([options])](#elementhandlescreenshotoptions) * [elementHandle.screenshot([options])](#elementhandlescreenshotoptions)
* [elementHandle.select(...values)](#elementhandleselectvalues) * [elementHandle.select(...values)](#elementhandleselectvalues)
* [elementHandle.setInputFiles(...files)](#elementhandlesetinputfilesfiles)
* [elementHandle.toString()](#elementhandletostring) * [elementHandle.toString()](#elementhandletostring)
* [elementHandle.tripleclick([options])](#elementhandletripleclickoptions) * [elementHandle.tripleclick([options])](#elementhandletripleclickoptions)
* [elementHandle.type(text[, options])](#elementhandletypetext-options) * [elementHandle.type(text[, options])](#elementhandletypetext-options)
* [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
- [class: Request](#class-request) - [class: Request](#class-request)
* [request.failure()](#requestfailure) * [request.failure()](#requestfailure)
* [request.frame()](#requestframe) * [request.frame()](#requestframe)
@ -3541,6 +3541,15 @@ handle.select('red', 'green', 'blue');
handle.select({ value: 'blue' }, { index: 2 }, 'red'); handle.select({ value: 'blue' }, { index: 2 }, 'red');
``` ```
#### elementHandle.setInputFiles(...files)
- `...files` <...[string]|[Object]> Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- `name` <[string]> <[File]> name
- `type` <[string]> <[File]> type
- `data` <[string]> Base64-encoded data
- returns: <[Promise]>
This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
#### elementHandle.toString() #### elementHandle.toString()
- returns: <[string]> - returns: <[string]>
@ -3583,12 +3592,6 @@ await elementHandle.type('some text');
await elementHandle.press('Enter'); await elementHandle.press('Enter');
``` ```
#### elementHandle.uploadFile(...filePaths)
- `...filePaths` <...[string]> Sets the value of the file input to these paths. If some of the `filePaths` are relative paths, then they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- returns: <[Promise]>
This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
### class: Request ### class: Request
Whenever the page sends a request, such as for a network resource, the following events are emitted by playwright's page: Whenever the page sends a request, such as for a network resource, the following events are emitted by playwright's page:
@ -3883,6 +3886,7 @@ TimeoutError is emitted whenever certain operations are terminated due to timeou
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element" [Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error" [Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[ExecutionContext]: #class-executioncontext "ExecutionContext" [ExecutionContext]: #class-executioncontext "ExecutionContext"
[File]: #class-file "https://developer.mozilla.org/en-US/docs/Web/API/File"
[FileChooser]: #class-filechooser "FileChooser" [FileChooser]: #class-filechooser "FileChooser"
[Frame]: #class-frame "Frame" [Frame]: #class-frame "Frame"
[JSHandle]: #class-jshandle "JSHandle" [JSHandle]: #class-jshandle "JSHandle"

View File

@ -151,17 +151,21 @@ export class ExecutionContext implements types.EvaluationContext<JSHandle> {
} }
} }
async _adoptBackendNodeId(backendNodeId: Protocol.DOM.BackendNodeId) {
const {object} = await this._client.send('DOM.resolveNode', {
backendNodeId,
executionContextId: this._contextId,
});
return createJSHandle(this, object) as ElementHandle;
}
async _adoptElementHandle(elementHandle: ElementHandle): Promise<ElementHandle> { async _adoptElementHandle(elementHandle: ElementHandle): Promise<ElementHandle> {
assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context'); assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
assert(this._frame, 'Cannot adopt handle without a Frame'); assert(this._frame, 'Cannot adopt handle without a Frame');
const nodeInfo = await this._client.send('DOM.describeNode', { const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: elementHandle._remoteObject.objectId, objectId: elementHandle._remoteObject.objectId,
}); });
const {object} = await this._client.send('DOM.resolveNode', { return this._adoptBackendNodeId(nodeInfo.node.backendNodeId);
backendNodeId: nodeInfo.node.backendNodeId,
executionContextId: this._contextId,
});
return createJSHandle(this, object) as ElementHandle;
} }
_injected(): Promise<JSHandle> { _injected(): Promise<JSHandle> {

View File

@ -15,10 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
import * as path from 'path';
import * as types from '../types';
import { assert, debugError, helper } from '../helper'; import { assert, debugError, helper } from '../helper';
import { ClickOptions, Modifier, MultiClickOptions, PointerActionOptions, SelectOption, selectFunction, fillFunction } from '../input'; import Injected from '../injected/injected';
import * as input from '../input';
import * as types from '../types';
import { CDPSession } from './Connection'; import { CDPSession } from './Connection';
import { ExecutionContext } from './ExecutionContext'; import { ExecutionContext } from './ExecutionContext';
import { Frame } from './Frame'; import { Frame } from './Frame';
@ -26,7 +26,6 @@ import { FrameManager } from './FrameManager';
import { Page } from './Page'; import { Page } from './Page';
import { Protocol } from './protocol'; import { Protocol } from './protocol';
import { releaseObject, valueFromRemoteObject } from './protocolHelper'; import { releaseObject, valueFromRemoteObject } from './protocolHelper';
import Injected from '../injected/injected';
type SelectorRoot = Element | ShadowRoot | Document; type SelectorRoot = Element | ShadowRoot | Document;
@ -236,7 +235,7 @@ export class ElementHandle extends JSHandle {
return { point, scrollX, scrollY }; return { point, scrollX, scrollY };
} }
async _performPointerAction(action: (point: Point) => Promise<void>, options?: PointerActionOptions): Promise<void> { async _performPointerAction(action: (point: Point) => Promise<void>, options?: input.PointerActionOptions): Promise<void> {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
let point: Point; let point: Point;
if (options && options.relativePoint) { if (options && options.relativePoint) {
@ -259,7 +258,7 @@ export class ElementHandle extends JSHandle {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
point = await this._clickablePoint(); point = await this._clickablePoint();
} }
let restoreModifiers: Modifier[] | undefined; let restoreModifiers: input.Modifier[] | undefined;
if (options && options.modifiers) if (options && options.modifiers)
restoreModifiers = await this._page.keyboard._ensureModifiers(options.modifiers); restoreModifiers = await this._page.keyboard._ensureModifiers(options.modifiers);
await action(point); await action(point);
@ -289,23 +288,23 @@ export class ElementHandle extends JSHandle {
})); }));
} }
hover(options?: PointerActionOptions): Promise<void> { hover(options?: input.PointerActionOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.move(point.x, point.y), options); return this._performPointerAction(point => this._page.mouse.move(point.x, point.y), options);
} }
click(options?: ClickOptions): Promise<void> { click(options?: input.ClickOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.click(point.x, point.y, options), options); return this._performPointerAction(point => this._page.mouse.click(point.x, point.y, options), options);
} }
dblclick(options?: MultiClickOptions): Promise<void> { dblclick(options?: input.MultiClickOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.dblclick(point.x, point.y, options), options); return this._performPointerAction(point => this._page.mouse.dblclick(point.x, point.y, options), options);
} }
tripleclick(options?: MultiClickOptions): Promise<void> { tripleclick(options?: input.MultiClickOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.tripleclick(point.x, point.y, options), options); return this._performPointerAction(point => this._page.mouse.tripleclick(point.x, point.y, options), options);
} }
async select(...values: (string | ElementHandle | SelectOption)[]): Promise<string[]> { async select(...values: (string | ElementHandle | input.SelectOption)[]): Promise<string[]> {
const options = values.map(value => typeof value === 'object' ? value : { value }); const options = values.map(value => typeof value === 'object' ? value : { value });
for (const option of options) { for (const option of options) {
if (option instanceof ElementHandle) if (option instanceof ElementHandle)
@ -317,22 +316,22 @@ export class ElementHandle extends JSHandle {
if (option.index !== undefined) if (option.index !== undefined)
assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"'); assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"');
} }
return this.evaluate(selectFunction, ...options); return this.evaluate(input.selectFunction, ...options);
} }
async fill(value: string): Promise<void> { async fill(value: string): Promise<void> {
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"'); assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
const error = await this.evaluate(fillFunction); const error = await this.evaluate(input.fillFunction);
if (error) if (error)
throw new Error(error); throw new Error(error);
await this.focus(); await this.focus();
await this._page.keyboard.sendCharacters(value); await this._page.keyboard.sendCharacters(value);
} }
async uploadFile(...filePaths: string[]) { async setInputFiles(...files: (string|input.FilePayload)[]) {
const files = filePaths.map(filePath => path.resolve(filePath)); const multiple = await this.evaluate((element: HTMLInputElement) => !!element.multiple);
const objectId = this._remoteObject.objectId; assert(multiple || files.length <= 1, 'Non-multiple file input can only accept single file!');
await this._client.send('DOM.setFileInputFiles', { objectId, files }); await this.evaluate(input.setFileInputFunction, await input.loadFiles(files));
} }
async focus() { async focus() {

View File

@ -16,16 +16,15 @@
*/ */
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path';
import * as types from '../types';
import { assert, debugError, helper } from '../helper'; import { assert, debugError, helper } from '../helper';
import { ClickOptions, fillFunction, MultiClickOptions, selectFunction, SelectOption } from '../input';
import { JugglerSession } from './Connection';
import Injected from '../injected/injected'; import Injected from '../injected/injected';
import * as input from '../input';
type SelectorRoot = Element | ShadowRoot | Document; import * as types from '../types';
import { JugglerSession } from './Connection';
import { ExecutionContext } from './ExecutionContext'; import { ExecutionContext } from './ExecutionContext';
import { Frame } from './FrameManager'; import { Frame } from './FrameManager';
type SelectorRoot = Element | ShadowRoot | Document;
const readFileAsync = helper.promisify(fs.readFile); const readFileAsync = helper.promisify(fs.readFile);
export class JSHandle { export class JSHandle {
@ -294,47 +293,28 @@ export class ElementHandle extends JSHandle {
throw new Error(error); throw new Error(error);
} }
async click(options?: ClickOptions) { async click(options?: input.ClickOptions) {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint(); const {x, y} = await this._clickablePoint();
await this._frame._page.mouse.click(x, y, options); await this._frame._page.mouse.click(x, y, options);
} }
async dblclick(options?: MultiClickOptions): Promise<void> { async dblclick(options?: input.MultiClickOptions): Promise<void> {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint(); const {x, y} = await this._clickablePoint();
await this._frame._page.mouse.dblclick(x, y, options); await this._frame._page.mouse.dblclick(x, y, options);
} }
async tripleclick(options?: MultiClickOptions): Promise<void> { async tripleclick(options?: input.MultiClickOptions): Promise<void> {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint(); const {x, y} = await this._clickablePoint();
await this._frame._page.mouse.tripleclick(x, y, options); await this._frame._page.mouse.tripleclick(x, y, options);
} }
async uploadFile(...files: Array<string>) { 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!');
const blobs = await Promise.all(files.map(path => readFileAsync(path))); await this.evaluate(input.setFileInputFunction, await input.loadFiles(files));
const payloads: FilePayload[] = [];
for (let i = 0; i < files.length; ++i) {
payloads.push({
name: path.basename(files[i]),
mimeType: 'application/octet-stream',
data: blobs[i].toString('base64')
});
}
await this.evaluate(async (element: HTMLInputElement, payloads: FilePayload[]) => {
const files = await Promise.all(payloads.map(async (file: FilePayload) => {
const result = await fetch(`data:${file.mimeType};base64,${file.data}`);
return new File([await result.blob()], file.name);
}));
const dt = new DataTransfer();
for (const file of files)
dt.items.add(file);
element.files = dt.files;
element.dispatchEvent(new Event('input', { 'bubbles': true }));
}, payloads);
} }
async hover() { async hover() {
@ -357,7 +337,7 @@ export class ElementHandle extends JSHandle {
await this._frame._page.keyboard.press(key, options); await this._frame._page.keyboard.press(key, options);
} }
async select(...values: (string | ElementHandle | SelectOption)[]): Promise<string[]> { async select(...values: (string | ElementHandle | input.SelectOption)[]): Promise<string[]> {
const options = values.map(value => typeof value === 'object' ? value : { value }); const options = values.map(value => typeof value === 'object' ? value : { value });
for (const option of options) { for (const option of options) {
if (option instanceof ElementHandle) if (option instanceof ElementHandle)
@ -369,12 +349,12 @@ export class ElementHandle extends JSHandle {
if (option.index !== undefined) if (option.index !== undefined)
assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"'); assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"');
} }
return this.evaluate(selectFunction, ...options); return this.evaluate(input.selectFunction, ...options);
} }
async fill(value: string): Promise<void> { async fill(value: string): Promise<void> {
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"'); assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
const error = await this.evaluate(fillFunction); const error = await this.evaluate(input.fillFunction);
if (error) if (error)
throw new Error(error); throw new Error(error);
await this.focus(); await this.focus();

View File

@ -558,7 +558,7 @@ export class Page extends EventEmitter {
const interceptors = Array.from(this._fileChooserInterceptors); const interceptors = Array.from(this._fileChooserInterceptors);
this._fileChooserInterceptors.clear(); this._fileChooserInterceptors.clear();
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple); const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
const fileChooser = new FileChooser(this, this._session, handle, multiple); const fileChooser = new FileChooser(handle, multiple);
for (const interceptor of interceptors) for (const interceptor of interceptors)
interceptor.call(null, fileChooser); interceptor.call(null, fileChooser);
} }
@ -623,21 +623,12 @@ export type Viewport = {
hasTouch?: boolean; hasTouch?: boolean;
} }
type MediaFeature = {
name: string,
value: string
};
export class FileChooser { export class FileChooser {
private _page; Page;
private _client: JugglerSession;
private _element: ElementHandle; private _element: ElementHandle;
private _multiple: boolean; private _multiple: boolean;
private _handled = false; private _handled = false;
constructor(page: Page, client: JugglerSession, element: ElementHandle, multiple: boolean) { constructor(element: ElementHandle, multiple: boolean) {
this._page = page;
this._client = client;
this._element = element; this._element = element;
this._multiple = multiple; this._multiple = multiple;
} }
@ -649,7 +640,7 @@ export class FileChooser {
async accept(filePaths: string[]): Promise<any> { async accept(filePaths: string[]): Promise<any> {
assert(!this._handled, 'Cannot accept FileChooser which is already handled!'); assert(!this._handled, 'Cannot accept FileChooser which is already handled!');
this._handled = true; this._handled = true;
await this._element.uploadFile(...filePaths); await this._element.setInputFiles(...filePaths);
} }
async cancel(): Promise<any> { async cancel(): Promise<any> {

View File

@ -1,8 +1,11 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
import { assert } from './helper'; import * as fs from 'fs';
import * as path from 'path';
import { assert, helper } from './helper';
import * as keyboardLayout from './USKeyboardLayout'; import * as keyboardLayout from './USKeyboardLayout';
const readFileAsync = helper.promisify(fs.readFile);
export type Modifier = 'Alt' | 'Control' | 'Meta' | 'Shift'; export type Modifier = 'Alt' | 'Control' | 'Meta' | 'Shift';
export type Button = 'left' | 'right' | 'middle'; export type Button = 'left' | 'right' | 'middle';
@ -344,5 +347,38 @@ export const fillFunction = (element: HTMLElement) => {
return false; return false;
}; };
export const loadFiles = async (items: (string|FilePayload)[]): Promise<FilePayload[]> => {
return Promise.all(items.map(async item => {
if (typeof item === 'string') {
const file: FilePayload = {
name: path.basename(item),
type: 'application/octet-stream',
data: (await readFileAsync(item)).toString('base64')
};
return file;
} else {
return item as FilePayload;
}
}));
}
export const setFileInputFunction = async (element: HTMLInputElement, payloads: FilePayload[]) => {
const files = await Promise.all(payloads.map(async (file: FilePayload) => {
const result = await fetch(`data:${file.type};base64,${file.data}`);
return new File([await result.blob()], file.name);
}));
const dt = new DataTransfer();
for (const file of files)
dt.items.add(file);
element.files = dt.files;
element.dispatchEvent(new Event('input', { 'bubbles': true }));
};
export type FilePayload = {
name: string,
type: string,
data: string
};
export const mediaTypes = new Set(['screen', 'print']); export const mediaTypes = new Set(['screen', 'print']);
export const mediaColorSchemes = new Set(['dark', 'light', 'no-preference']); export const mediaColorSchemes = new Set(['dark', 'light', 'no-preference']);

View File

@ -17,7 +17,7 @@
import * as fs from 'fs'; import * as fs from 'fs';
import { assert, debugError, helper } from '../helper'; import { assert, debugError, helper } from '../helper';
import { ClickOptions, MultiClickOptions, selectFunction, SelectOption, fillFunction } from '../input'; import * as input from '../input';
import { TargetSession } from './Connection'; import { TargetSession } from './Connection';
import { ExecutionContext } from './ExecutionContext'; import { ExecutionContext } from './ExecutionContext';
import { FrameManager } from './FrameManager'; import { FrameManager } from './FrameManager';
@ -217,25 +217,25 @@ export class ElementHandle extends JSHandle {
await this._page.mouse.move(x, y); await this._page.mouse.move(x, y);
} }
async click(options?: ClickOptions): Promise<void> { async click(options?: input.ClickOptions): Promise<void> {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint(); const {x, y} = await this._clickablePoint();
await this._page.mouse.click(x, y, options); await this._page.mouse.click(x, y, options);
} }
async dblclick(options?: MultiClickOptions): Promise<void> { async dblclick(options?: input.MultiClickOptions): Promise<void> {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint(); const {x, y} = await this._clickablePoint();
await this._page.mouse.dblclick(x, y, options); await this._page.mouse.dblclick(x, y, options);
} }
async tripleclick(options?: MultiClickOptions): Promise<void> { async tripleclick(options?: input.MultiClickOptions): Promise<void> {
await this._scrollIntoViewIfNeeded(); await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint(); const {x, y} = await this._clickablePoint();
await this._page.mouse.tripleclick(x, y, options); await this._page.mouse.tripleclick(x, y, options);
} }
async select(...values: (string | ElementHandle | SelectOption)[]): Promise<string[]> { async select(...values: (string | ElementHandle | input.SelectOption)[]): Promise<string[]> {
const options = values.map(value => typeof value === 'object' ? value : { value }); const options = values.map(value => typeof value === 'object' ? value : { value });
for (const option of options) { for (const option of options) {
if (option instanceof ElementHandle) if (option instanceof ElementHandle)
@ -247,18 +247,24 @@ export class ElementHandle extends JSHandle {
if (option.index !== undefined) if (option.index !== undefined)
assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"'); assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"');
} }
return this.evaluate(selectFunction, ...options); return this.evaluate(input.selectFunction, ...options);
} }
async fill(value: string): Promise<void> { async fill(value: string): Promise<void> {
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"'); assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
const error = await this.evaluate(fillFunction); const error = await this.evaluate(input.fillFunction);
if (error) if (error)
throw new Error(error); throw new Error(error);
await this.focus(); await this.focus();
await this._page.keyboard.sendCharacters(value); await this._page.keyboard.sendCharacters(value);
} }
async setInputFiles(...files: (string|input.FilePayload)[]) {
const multiple = await this.evaluate((element: HTMLInputElement) => !!element.multiple);
assert(multiple || files.length <= 1, 'Non-multiple file input can only accept single file!');
await this.evaluate(input.setFileInputFunction, await input.loadFiles(files));
}
async focus() { async focus() {
await this.evaluate(element => element.focus()); await this.evaluate(element => element.focus());
} }

View File

@ -19,7 +19,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
const {it, fit, xit} = testRunner; const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe.skip(FFOX)('DefaultBrowserContext', function() { describe('DefaultBrowserContext', function() {
beforeEach(async state => { beforeEach(async state => {
state.browser = await playwright.launch(defaultBrowserOptions); state.browser = await playwright.launch(defaultBrowserOptions);
state.page = await state.browser.newPage(); state.page = await state.browser.newPage();
@ -34,7 +34,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await page.evaluate(() => { await page.evaluate(() => {
document.cookie = 'username=John Doe'; document.cookie = 'username=John Doe';
}); });
expect(await page.cookies()).toEqual([{ expect(await page.browserContext().cookies()).toEqual([{
name: 'username', name: 'username',
value: 'John Doe', value: 'John Doe',
domain: 'localhost', domain: 'localhost',
@ -47,14 +47,15 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
sameSite: 'None', sameSite: 'None',
}]); }]);
}); });
it.skip(WEBKIT)('page.setCookie() should work', async({page, server}) => { it.skip(WEBKIT)('context.setCookies() should work', async({page, server}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.browserContext().setCookies([{
url: server.EMPTY_PAGE,
name: 'username', name: 'username',
value: 'John Doe' value: 'John Doe'
}); }]);
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe'); expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
expect(await page.cookies()).toEqual([{ expect(await page.browserContext().cookies()).toEqual([{
name: 'username', name: 'username',
value: 'John Doe', value: 'John Doe',
domain: 'localhost', domain: 'localhost',
@ -67,30 +68,20 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
sameSite: 'None', sameSite: 'None',
}]); }]);
}); });
it.skip(WEBKIT)('page.deleteCookie() should work', async({page, server}) => { it.skip(WEBKIT)('context.clearCookies() should work', async({page, server}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.browserContext().setCookies([{
url: server.EMPTY_PAGE,
name: 'cookie1', name: 'cookie1',
value: '1' value: '1'
}, { }, {
url: server.EMPTY_PAGE,
name: 'cookie2', name: 'cookie2',
value: '2' value: '2'
});
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
await page.deleteCookie({name: 'cookie2'});
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
expect(await page.cookies()).toEqual([{
name: 'cookie1',
value: '1',
domain: 'localhost',
path: '/',
expires: -1,
size: 8,
httpOnly: false,
secure: false,
session: true,
sameSite: 'None',
}]); }]);
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
await page.browserContext().clearCookies();
expect(await page.evaluate('document.cookie')).toBe('');
}); });
}); });
}; };

View File

@ -27,7 +27,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
await page.goto(server.PREFIX + '/input/fileupload.html'); await page.goto(server.PREFIX + '/input/fileupload.html');
const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD); const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD);
const input = await page.$('input'); const input = await page.$('input');
await input.uploadFile(filePath); await input.setInputFiles(filePath);
expect(await page.evaluate(e => e.files[0].name, input)).toBe('file-to-upload.txt'); expect(await page.evaluate(e => e.files[0].name, input)).toBe('file-to-upload.txt');
expect(await page.evaluate(e => { expect(await page.evaluate(e => {
const reader = new FileReader(); const reader = new FileReader();

View File

@ -102,10 +102,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
expect(requests[1].url()).toContain('/one-style.css'); expect(requests[1].url()).toContain('/one-style.css');
expect(requests[1].headers().referer).toContain('/one-style.html'); expect(requests[1].headers().referer).toContain('/one-style.html');
}); });
it('should properly return navigation response when URL has cookies', async({page, server}) => { it('should properly return navigation response when URL has cookies', async({context, page, server}) => {
// Setup cookie. // Setup cookie.
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setCookie({ name: 'foo', value: 'bar'}); await context.setCookies([{ url: server.EMPTY_PAGE, name: 'foo', value: 'bar'}]);
// Setup request interception. // Setup request interception.
await page.interception.enable(); await page.interception.enable();