chore: remove from client check if browser is co-located with server (#28071)

Reference https://github.com/microsoft/playwright/issues/27792
This commit is contained in:
Yury Semikhatsky 2023-11-10 15:24:31 -08:00 committed by GitHub
parent 1aee48f2d0
commit fae5dd898a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 149 additions and 223 deletions

View File

@ -151,10 +151,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
this.tracing._tracesDir = browserOptions.tracesDir;
}
_isLocalBrowserOnServer(): boolean {
return this._initializer.isLocalBrowserOnServer;
}
private _onPage(page: Page): void {
this._pages.add(page);
this.emit(Events.BrowserContext.Page, page);

View File

@ -24,14 +24,13 @@ import fs from 'fs';
import { mime } from '../utilsBundle';
import path from 'path';
import { assert, isString } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { fileUploadSizeLimit, mkdirIfNeeded } from '../utils/fileUtils';
import type * as api from '../../types/types';
import type * as structs from '../../types/structs';
import type { BrowserContext } from './browserContext';
import { WritableStream } from './writableStream';
import { pipeline } from 'stream';
import { promisify } from 'util';
import { debugLogger } from '../common/debugLogger';
const pipelineAsync = promisify(pipeline);
@ -151,13 +150,7 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
if (!frame)
throw new Error('Cannot set input files to detached element');
const converted = await convertInputFiles(files, frame.page().context());
if (converted.files) {
debugLogger.log('api', 'setting input buffers');
await this._elementChannel.setInputFiles({ files: converted.files, ...options });
} else {
debugLogger.log('api', 'setting input file paths');
await this._elementChannel.setInputFilePaths({ ...converted, ...options });
}
await this._elementChannel.setInputFiles({ ...converted, ...options });
}
async focus(): Promise<void> {
@ -257,36 +250,13 @@ export function convertSelectOptionValues(values: string | api.ElementHandle | S
return { options: values as SelectOption[] };
}
type SetInputFilesFiles = channels.ElementHandleSetInputFilesParams['files'];
type InputFilesList = {
files?: SetInputFilesFiles;
localPaths?: string[];
streams?: channels.WritableStreamChannel[];
};
const filePayloadSizeLimit = 50 * 1024 * 1024;
type SetInputFilesFiles = Pick<channels.ElementHandleSetInputFilesParams, 'payloads' | 'localPaths' | 'streams'>;
function filePayloadExceedsSizeLimit(payloads: FilePayload[]) {
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= filePayloadSizeLimit;
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= fileUploadSizeLimit;
}
async function filesExceedSizeLimit(files: string[]) {
const sizes = await Promise.all(files.map(async file => (await fs.promises.stat(file)).size));
return sizes.reduce((total, size) => total + size, 0) >= filePayloadSizeLimit;
}
async function readFilesIntoBuffers(items: string[]): Promise<SetInputFilesFiles> {
const filePayloads: SetInputFilesFiles = await Promise.all((items as string[]).map(async item => {
return {
name: path.basename(item),
buffer: await fs.promises.readFile(item),
lastModifiedMs: (await fs.promises.stat(item)).mtimeMs,
};
}));
return filePayloads;
}
export async function convertInputFiles(files: string | FilePayload | string[] | FilePayload[], context: BrowserContext): Promise<InputFilesList> {
export async function convertInputFiles(files: string | FilePayload | string[] | FilePayload[], context: BrowserContext): Promise<SetInputFilesFiles> {
const items: (string | FilePayload)[] = Array.isArray(files) ? files.slice() : [files];
if (items.some(item => typeof item === 'string')) {
@ -294,35 +264,22 @@ export async function convertInputFiles(files: string | FilePayload | string[] |
throw new Error('File paths cannot be mixed with buffers');
if (context._connection.isRemote()) {
if (context._isLocalBrowserOnServer()) {
const streams: channels.WritableStreamChannel[] = await Promise.all((items as string[]).map(async item => {
const lastModifiedMs = (await fs.promises.stat(item)).mtimeMs;
const { writableStream: stream } = await context._channel.createTempFile({ name: path.basename(item), lastModifiedMs });
const writable = WritableStream.from(stream);
await pipelineAsync(fs.createReadStream(item), writable.stream());
return stream;
}));
return { streams };
}
if (await filesExceedSizeLimit(items as string[]))
throw new Error('Cannot transfer files larger than 50Mb to a browser not co-located with the server');
return { files: await readFilesIntoBuffers(items as string[]) };
const streams: channels.WritableStreamChannel[] = await Promise.all((items as string[]).map(async item => {
const lastModifiedMs = (await fs.promises.stat(item)).mtimeMs;
const { writableStream: stream } = await context._channel.createTempFile({ name: path.basename(item), lastModifiedMs });
const writable = WritableStream.from(stream);
await pipelineAsync(fs.createReadStream(item), writable.stream());
return stream;
}));
return { streams };
}
if (context._isLocalBrowserOnServer())
return { localPaths: items.map(f => path.resolve(f as string)) as string[] };
if (await filesExceedSizeLimit(items as string[]))
throw new Error('Cannot transfer files larger than 50Mb to a browser not co-located with the server');
return { files: await readFilesIntoBuffers(items as string[]) };
return { localPaths: items.map(f => path.resolve(f as string)) as string[] };
}
const payloads = items as FilePayload[];
if (filePayloadExceedsSizeLimit(payloads)) {
let error = 'Cannot set buffer larger than 50Mb';
if (context._isLocalBrowserOnServer())
error += ', please write it to a file and pass its path instead.';
throw new Error(error);
}
return { files: payloads };
if (filePayloadExceedsSizeLimit(payloads))
throw new Error('Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.');
return { payloads };
}
export function determineScreenshotType(options: { path?: string, type?: 'png' | 'jpeg' }): 'png' | 'jpeg' | undefined {

View File

@ -35,7 +35,6 @@ import { kLifecycleEvents } from './types';
import { urlMatches } from '../utils/network';
import type * as api from '../../types/types';
import type * as structs from '../../types/structs';
import { debugLogger } from '../common/debugLogger';
export type WaitForNavigationOptions = {
timeout?: number,
@ -401,13 +400,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options: channels.FrameSetInputFilesOptions = {}): Promise<void> {
const converted = await convertInputFiles(files, this.page().context());
if (converted.files) {
debugLogger.log('api', 'setting input buffers');
await this._channel.setInputFiles({ selector, files: converted.files, ...options });
} else {
debugLogger.log('api', 'setting input file paths');
await this._channel.setInputFilePaths({ selector, ...converted, ...options });
}
await this._channel.setInputFiles({ selector, ...converted, ...options });
}
async type(selector: string, text: string, options: channels.FrameTypeOptions = {}) {

View File

@ -44,7 +44,6 @@ export const slowMoActions = new Set([
'Frame.press',
'Frame.selectOption',
'Frame.setInputFiles',
'Frame.setInputFilePaths',
'Frame.tap',
'Frame.type',
'Frame.uncheck',
@ -60,7 +59,6 @@ export const slowMoActions = new Set([
'ElementHandle.selectOption',
'ElementHandle.selectText',
'ElementHandle.setInputFiles',
'ElementHandle.setInputFilePaths',
'ElementHandle.tap',
'ElementHandle.type',
'ElementHandle.uncheck'
@ -121,7 +119,6 @@ export const commandsWithTracingSnapshots = new Set([
'Frame.selectOption',
'Frame.setContent',
'Frame.setInputFiles',
'Frame.setInputFilePaths',
'Frame.tap',
'Frame.textContent',
'Frame.type',
@ -158,7 +155,6 @@ export const commandsWithTracingSnapshots = new Set([
'ElementHandle.selectOption',
'ElementHandle.selectText',
'ElementHandle.setInputFiles',
'ElementHandle.setInputFilePaths',
'ElementHandle.tap',
'ElementHandle.textContent',
'ElementHandle.type',
@ -177,7 +173,6 @@ export const pausesBeforeInputActions = new Set([
'Frame.press',
'Frame.selectOption',
'Frame.setInputFiles',
'Frame.setInputFilePaths',
'Frame.tap',
'Frame.type',
'Frame.uncheck',
@ -189,7 +184,6 @@ export const pausesBeforeInputActions = new Set([
'ElementHandle.press',
'ElementHandle.selectOption',
'ElementHandle.setInputFiles',
'ElementHandle.setInputFilePaths',
'ElementHandle.tap',
'ElementHandle.type',
'ElementHandle.uncheck'

View File

@ -762,7 +762,6 @@ scheme.ElectronApplicationWaitForEventInfoResult = tType('EventTargetWaitForEven
scheme.AndroidDeviceWaitForEventInfoResult = tType('EventTargetWaitForEventInfoResult');
scheme.BrowserContextInitializer = tObject({
isChromium: tBoolean,
isLocalBrowserOnServer: tBoolean,
requestContext: tChannel(['APIRequestContext']),
tracing: tChannel(['Tracing']),
});
@ -1557,25 +1556,17 @@ scheme.FrameSetContentResult = tOptional(tObject({}));
scheme.FrameSetInputFilesParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
files: tArray(tObject({
payloads: tOptional(tArray(tObject({
name: tString,
mimeType: tOptional(tString),
buffer: tBinary,
lastModifiedMs: tOptional(tNumber),
})),
timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean),
});
scheme.FrameSetInputFilesResult = tOptional(tObject({}));
scheme.FrameSetInputFilePathsParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
}))),
localPaths: tOptional(tArray(tString)),
streams: tOptional(tArray(tChannel(['WritableStream']))),
timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean),
});
scheme.FrameSetInputFilePathsResult = tOptional(tObject({}));
scheme.FrameSetInputFilesResult = tOptional(tObject({}));
scheme.FrameTapParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
@ -1931,23 +1922,17 @@ scheme.ElementHandleSelectTextParams = tObject({
});
scheme.ElementHandleSelectTextResult = tOptional(tObject({}));
scheme.ElementHandleSetInputFilesParams = tObject({
files: tArray(tObject({
payloads: tOptional(tArray(tObject({
name: tString,
mimeType: tOptional(tString),
buffer: tBinary,
lastModifiedMs: tOptional(tNumber),
})),
timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean),
});
scheme.ElementHandleSetInputFilesResult = tOptional(tObject({}));
scheme.ElementHandleSetInputFilePathsParams = tObject({
}))),
localPaths: tOptional(tArray(tString)),
streams: tOptional(tArray(tChannel(['WritableStream']))),
timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean),
});
scheme.ElementHandleSetInputFilePathsResult = tOptional(tObject({}));
scheme.ElementHandleSetInputFilesResult = tOptional(tObject({}));
scheme.ElementHandleTapParams = tObject({
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),

View File

@ -23,9 +23,6 @@ import { JSHandleDispatcher, serializeResult, parseArgument } from './jsHandleDi
import type { JSHandleDispatcherParentScope } from './jsHandleDispatcher';
import { FrameDispatcher } from './frameDispatcher';
import type { CallMetadata } from '../instrumentation';
import type { WritableStreamDispatcher } from './writableStreamDispatcher';
import { assert } from '../../utils';
import path from 'path';
import { BrowserContextDispatcher } from './browserContextDispatcher';
import { PageDispatcher, WorkerDispatcher } from './pageDispatcher';
@ -151,19 +148,7 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
}
async setInputFiles(params: channels.ElementHandleSetInputFilesParams, metadata: CallMetadata): Promise<void> {
return await this._elementHandle.setInputFiles(metadata, { files: params.files }, params);
}
async setInputFilePaths(params: channels.ElementHandleSetInputFilePathsParams, metadata: CallMetadata): Promise<void> {
let { localPaths } = params;
if (!localPaths) {
if (!params.streams)
throw new Error('Neither localPaths nor streams is specified');
localPaths = params.streams.map(c => (c as WritableStreamDispatcher).path());
}
for (const p of localPaths)
assert(path.isAbsolute(p) && path.resolve(p) === p, 'Paths provided to localPaths must be absolute and fully resolved.');
return await this._elementHandle.setInputFiles(metadata, { localPaths }, params);
return await this._elementHandle.setInputFiles(metadata, params);
}
async focus(params: channels.ElementHandleFocusParams, metadata: CallMetadata): Promise<void> {

View File

@ -23,9 +23,6 @@ import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { ResponseDispatcher } from './networkDispatchers';
import { RequestDispatcher } from './networkDispatchers';
import type { CallMetadata } from '../instrumentation';
import type { WritableStreamDispatcher } from './writableStreamDispatcher';
import { assert } from '../../utils';
import path from 'path';
import type { BrowserContextDispatcher } from './browserContextDispatcher';
import type { PageDispatcher } from './pageDispatcher';
@ -218,19 +215,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Br
}
async setInputFiles(params: channels.FrameSetInputFilesParams, metadata: CallMetadata): Promise<channels.FrameSetInputFilesResult> {
return await this._frame.setInputFiles(metadata, params.selector, { files: params.files }, params);
}
async setInputFilePaths(params: channels.FrameSetInputFilePathsParams, metadata: CallMetadata): Promise<void> {
let { localPaths } = params;
if (!localPaths) {
if (!params.streams)
throw new Error('Neither localPaths nor streams is specified');
localPaths = params.streams.map(c => (c as WritableStreamDispatcher).path());
}
for (const p of localPaths)
assert(path.isAbsolute(p) && path.resolve(p) === p, 'Paths provided to localPaths must be absolute and fully resolved.');
return await this._frame.setInputFiles(metadata, params.selector, { localPaths }, params);
return await this._frame.setInputFiles(metadata, params.selector, params);
}
async type(params: channels.FrameTypeParams, metadata: CallMetadata): Promise<void> {

View File

@ -14,9 +14,8 @@
* limitations under the License.
*/
import { mime } from '../utilsBundle';
import * as injectedScriptSource from '../generated/injectedScriptSource';
import type * as channels from '@protocol/channels';
import * as injectedScriptSource from '../generated/injectedScriptSource';
import { isSessionClosedError } from './protocolError';
import type { ScreenshotOptions } from './screenshotter';
import type * as frames from './frames';
@ -29,9 +28,13 @@ import { ProgressController } from './progress';
import type * as types from './types';
import type { TimeoutOptions } from '../common/types';
import { isUnderTest } from '../utils';
import { prepareFilesForUpload } from './fileUploadUtils';
export type InputFilesItems = {
filePayloads?: types.FilePayload[],
localPaths?: string[]
};
type SetInputFilesFiles = channels.ElementHandleSetInputFilesParams['files'];
export type InputFilesItems = { files?: SetInputFilesFiles, localPaths?: string[] };
type ActionName = 'click' | 'hover' | 'dblclick' | 'tap' | 'move and up' | 'move and down';
export class NonRecoverableDOMError extends Error {
@ -579,29 +582,18 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}, this._page._timeoutSettings.timeout(options));
}
async setInputFiles(metadata: CallMetadata, items: InputFilesItems, options: types.NavigatingActionWaitOptions) {
async setInputFiles(metadata: CallMetadata, params: channels.ElementHandleSetInputFilesParams) {
const inputFileItems = await prepareFilesForUpload(this._frame, params);
const controller = new ProgressController(metadata, this);
return controller.run(async progress => {
const result = await this._setInputFiles(progress, items, options);
const result = await this._setInputFiles(progress, inputFileItems, params);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options));
}, this._page._timeoutSettings.timeout(params));
}
async _setInputFiles(progress: Progress, items: InputFilesItems, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
const { files, localPaths } = items;
let filePayloads: types.FilePayload[] | undefined;
if (files) {
filePayloads = [];
for (const payload of files) {
filePayloads.push({
name: payload.name,
mimeType: payload.mimeType || mime.getType(payload.name) || 'application/octet-stream',
buffer: payload.buffer.toString('base64'),
lastModifiedMs: payload.lastModifiedMs
});
}
}
const multiple = files && files.length > 1 || localPaths && localPaths.length > 1;
const { filePayloads, localPaths } = items;
const multiple = filePayloads && filePayloads.length > 1 || localPaths && localPaths.length > 1;
const result = await this.evaluateHandleInUtility(([injected, node, multiple]): Element | undefined => {
const element = injected.retarget(node, 'follow-label');
if (!element)

View File

@ -0,0 +1,77 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type * as channels from '@protocol/channels';
import fs from 'fs';
import path from 'path';
import { assert, fileUploadSizeLimit } from '../utils';
import { mime } from '../utilsBundle';
import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher';
import type { InputFilesItems } from './dom';
import type { Frame } from './frames';
import type * as types from './types';
async function filesExceedUploadLimit(files: string[]) {
const sizes = await Promise.all(files.map(async file => (await fs.promises.stat(file)).size));
return sizes.reduce((total, size) => total + size, 0) >= fileUploadSizeLimit;
}
export async function prepareFilesForUpload(frame: Frame, params: channels.ElementHandleSetInputFilesParams): Promise<InputFilesItems> {
const { payloads, streams } = params;
let { localPaths } = params;
if ([payloads, localPaths, streams].filter(Boolean).length !== 1)
throw new Error('Exactly one of payloads, localPaths and streams must be provided');
if (streams)
localPaths = streams.map(c => (c as WritableStreamDispatcher).path());
if (localPaths) {
for (const p of localPaths)
assert(path.isAbsolute(p) && path.resolve(p) === p, 'Paths provided to localPaths must be absolute and fully resolved.');
}
let fileBuffers: {
name: string,
mimeType?: string,
buffer: Buffer,
lastModifiedMs?: number,
}[] | undefined = payloads;
if (!frame._page._browserContext._browser._isCollocatedWithServer) {
// If the browser is on a different machine read files into buffers.
if (localPaths) {
if (await filesExceedUploadLimit(localPaths))
throw new Error('Cannot transfer files larger than 50Mb to a browser not co-located with the server');
fileBuffers = await Promise.all(localPaths.map(async item => {
return {
name: path.basename(item),
buffer: await fs.promises.readFile(item),
lastModifiedMs: (await fs.promises.stat(item)).mtimeMs,
};
}));
localPaths = undefined;
}
}
const filePayloads: types.FilePayload[] | undefined = fileBuffers?.map(payload => ({
name: payload.name,
mimeType: payload.mimeType || mime.getType(payload.name) || 'application/octet-stream',
buffer: payload.buffer.toString('base64'),
lastModifiedMs: payload.lastModifiedMs
}));
return { localPaths, filePayloads };
}

View File

@ -38,10 +38,10 @@ import type { InjectedScript, ElementStateWithoutStable, FrameExpectParams, Inje
import { isSessionClosedError } from './protocolError';
import { type ParsedSelector, isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
import type { ScreenshotOptions } from './screenshotter';
import type { InputFilesItems } from './dom';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import { FrameSelectors } from './frameSelectors';
import { TimeoutError } from './errors';
import { prepareFilesForUpload } from './fileUploadUtils';
type ContextData = {
contextPromise: ManualPromise<dom.FrameExecutionContext | { destroyedReason: string }>;
@ -1319,11 +1319,12 @@ export class Frame extends SdkObject {
}, this._page._timeoutSettings.timeout(options));
}
async setInputFiles(metadata: CallMetadata, selector: string, items: InputFilesItems, options: types.NavigatingActionWaitOptions = {}): Promise<channels.FrameSetInputFilesResult> {
async setInputFiles(metadata: CallMetadata, selector: string, params: channels.FrameSetInputFilesParams): Promise<channels.FrameSetInputFilesResult> {
const inputFileItems = await prepareFilesForUpload(this, params);
const controller = new ProgressController(metadata, this);
return controller.run(async progress => {
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._setInputFiles(progress, items, options)));
}, this._page._timeoutSettings.timeout(options));
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params.strict, handle => handle._setInputFiles(progress, inputFileItems, params)));
}, this._page._timeoutSettings.timeout(params));
}
async type(metadata: CallMetadata, selector: string, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) {

View File

@ -17,6 +17,8 @@
import fs from 'fs';
import path from 'path';
export const fileUploadSizeLimit = 50 * 1024 * 1024;
export const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
export async function mkdirIfNeeded(filePath: string) {

View File

@ -1407,7 +1407,6 @@ export interface EventTargetEvents {
// ----------- BrowserContext -----------
export type BrowserContextInitializer = {
isChromium: boolean,
isLocalBrowserOnServer: boolean,
requestContext: APIRequestContextChannel,
tracing: TracingChannel,
};
@ -2305,7 +2304,6 @@ export interface FrameChannel extends FrameEventTarget, Channel {
selectOption(params: FrameSelectOptionParams, metadata?: CallMetadata): Promise<FrameSelectOptionResult>;
setContent(params: FrameSetContentParams, metadata?: CallMetadata): Promise<FrameSetContentResult>;
setInputFiles(params: FrameSetInputFilesParams, metadata?: CallMetadata): Promise<FrameSetInputFilesResult>;
setInputFilePaths(params: FrameSetInputFilePathsParams, metadata?: CallMetadata): Promise<FrameSetInputFilePathsResult>;
tap(params: FrameTapParams, metadata?: CallMetadata): Promise<FrameTapResult>;
textContent(params: FrameTextContentParams, metadata?: CallMetadata): Promise<FrameTextContentResult>;
title(params?: FrameTitleParams, metadata?: CallMetadata): Promise<FrameTitleResult>;
@ -2792,37 +2790,29 @@ export type FrameSetContentResult = void;
export type FrameSetInputFilesParams = {
selector: string,
strict?: boolean,
files: {
payloads?: {
name: string,
mimeType?: string,
buffer: Binary,
lastModifiedMs?: number,
}[],
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type FrameSetInputFilesOptions = {
strict?: boolean,
payloads?: {
name: string,
mimeType?: string,
buffer: Binary,
}[],
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type FrameSetInputFilesResult = void;
export type FrameSetInputFilePathsParams = {
selector: string,
strict?: boolean,
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type FrameSetInputFilePathsOptions = {
strict?: boolean,
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type FrameSetInputFilePathsResult = void;
export type FrameTapParams = {
selector: string,
strict?: boolean,
@ -3115,7 +3105,6 @@ export interface ElementHandleChannel extends ElementHandleEventTarget, JSHandle
selectOption(params: ElementHandleSelectOptionParams, metadata?: CallMetadata): Promise<ElementHandleSelectOptionResult>;
selectText(params: ElementHandleSelectTextParams, metadata?: CallMetadata): Promise<ElementHandleSelectTextResult>;
setInputFiles(params: ElementHandleSetInputFilesParams, metadata?: CallMetadata): Promise<ElementHandleSetInputFilesResult>;
setInputFilePaths(params: ElementHandleSetInputFilePathsParams, metadata?: CallMetadata): Promise<ElementHandleSetInputFilePathsResult>;
tap(params: ElementHandleTapParams, metadata?: CallMetadata): Promise<ElementHandleTapResult>;
textContent(params?: ElementHandleTextContentParams, metadata?: CallMetadata): Promise<ElementHandleTextContentResult>;
type(params: ElementHandleTypeParams, metadata?: CallMetadata): Promise<ElementHandleTypeResult>;
@ -3423,33 +3412,28 @@ export type ElementHandleSelectTextOptions = {
};
export type ElementHandleSelectTextResult = void;
export type ElementHandleSetInputFilesParams = {
files: {
payloads?: {
name: string,
mimeType?: string,
buffer: Binary,
lastModifiedMs?: number,
}[],
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type ElementHandleSetInputFilesOptions = {
payloads?: {
name: string,
mimeType?: string,
buffer: Binary,
}[],
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type ElementHandleSetInputFilesResult = void;
export type ElementHandleSetInputFilePathsParams = {
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type ElementHandleSetInputFilePathsOptions = {
localPaths?: string[],
streams?: WritableStreamChannel[],
timeout?: number,
noWaitAfter?: boolean,
};
export type ElementHandleSetInputFilePathsResult = void;
export type ElementHandleTapParams = {
force?: boolean,
noWaitAfter?: boolean,

View File

@ -1012,7 +1012,6 @@ BrowserContext:
initializer:
isChromium: boolean
isLocalBrowserOnServer: boolean
requestContext: APIRequestContext
tracing: Tracing
@ -2111,28 +2110,15 @@ Frame:
parameters:
selector: string
strict: boolean?
files:
type: array
# Only one of payloads, localPaths and streams should be present.
payloads:
type: array?
items:
type: object
properties:
name: string
mimeType: string?
buffer: binary
lastModifiedMs: number?
timeout: number?
noWaitAfter: boolean?
flags:
slowMo: true
snapshot: true
pausesBeforeInput: true
# This method should be used if one of the files is large (>50Mb).
setInputFilePaths:
parameters:
selector: string
strict: boolean?
# Only one of localPaths and streams should be present.
localPaths:
type: array?
items: string
@ -2680,26 +2666,15 @@ ElementHandle:
setInputFiles:
parameters:
files:
type: array
# Only one of payloads, localPaths and streams should be present.
payloads:
type: array?
items:
type: object
properties:
name: string
mimeType: string?
buffer: binary
lastModifiedMs: number?
timeout: number?
noWaitAfter: boolean?
flags:
slowMo: true
snapshot: true
pausesBeforeInput: true
# This method should be used if one of the files is large (>50Mb).
setInputFilePaths:
parameters:
# Only one of localPaths and streams should be present.
localPaths:
type: array?
items: string