diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 9ed649e02a..a74996bbe2 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -151,10 +151,6 @@ export class BrowserContext extends ChannelOwner 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); diff --git a/packages/playwright-core/src/client/elementHandle.ts b/packages/playwright-core/src/client/elementHandle.ts index 794e88134f..0a31ff96ff 100644 --- a/packages/playwright-core/src/client/elementHandle.ts +++ b/packages/playwright-core/src/client/elementHandle.ts @@ -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 extends JSHandle 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 { @@ -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; 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 { - 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 { +export async function convertInputFiles(files: string | FilePayload | string[] | FilePayload[], context: BrowserContext): Promise { 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 { diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts index 2114ee9f1f..18d36c732c 100644 --- a/packages/playwright-core/src/client/frame.ts +++ b/packages/playwright-core/src/client/frame.ts @@ -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 implements api.Fr async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options: channels.FrameSetInputFilesOptions = {}): Promise { 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 = {}) { diff --git a/packages/playwright-core/src/protocol/debug.ts b/packages/playwright-core/src/protocol/debug.ts index 5699f40b59..4f44f5941e 100644 --- a/packages/playwright-core/src/protocol/debug.ts +++ b/packages/playwright-core/src/protocol/debug.ts @@ -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' diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index eb1fd7dc24..de0d84cf1f 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -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), diff --git a/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts b/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts index 45ada314d6..d4d0280ec4 100644 --- a/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/elementHandlerDispatcher.ts @@ -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 { - return await this._elementHandle.setInputFiles(metadata, { files: params.files }, params); - } - - async setInputFilePaths(params: channels.ElementHandleSetInputFilePathsParams, metadata: CallMetadata): Promise { - 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 { diff --git a/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts b/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts index 39c46e27e3..c8fd0c9e24 100644 --- a/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts @@ -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 { - return await this._frame.setInputFiles(metadata, params.selector, { files: params.files }, params); - } - - async setInputFilePaths(params: channels.FrameSetInputFilePathsParams, metadata: CallMetadata): Promise { - 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 { diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index a49024b046..ded96eec5f 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -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 extends js.JSHandle { }, 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) diff --git a/packages/playwright-core/src/server/fileUploadUtils.ts b/packages/playwright-core/src/server/fileUploadUtils.ts new file mode 100644 index 0000000000..89fc0cf7e1 --- /dev/null +++ b/packages/playwright-core/src/server/fileUploadUtils.ts @@ -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 { + 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 }; +} \ No newline at end of file diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 488a30fbab..f46484c579 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -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; @@ -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 { + async setInputFiles(metadata: CallMetadata, selector: string, params: channels.FrameSetInputFilesParams): Promise { + 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 = {}) { diff --git a/packages/playwright-core/src/utils/fileUtils.ts b/packages/playwright-core/src/utils/fileUtils.ts index 1a9f552f58..287899d509 100644 --- a/packages/playwright-core/src/utils/fileUtils.ts +++ b/packages/playwright-core/src/utils/fileUtils.ts @@ -17,6 +17,8 @@ import fs from 'fs'; import path from 'path'; +export const fileUploadSizeLimit = 50 * 1024 * 1024; + export const existsAsync = (path: string): Promise => new Promise(resolve => fs.stat(path, err => resolve(!err))); export async function mkdirIfNeeded(filePath: string) { diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 6fd4bd651a..497fed66d0 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -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; setContent(params: FrameSetContentParams, metadata?: CallMetadata): Promise; setInputFiles(params: FrameSetInputFilesParams, metadata?: CallMetadata): Promise; - setInputFilePaths(params: FrameSetInputFilePathsParams, metadata?: CallMetadata): Promise; tap(params: FrameTapParams, metadata?: CallMetadata): Promise; textContent(params: FrameTextContentParams, metadata?: CallMetadata): Promise; title(params?: FrameTitleParams, metadata?: CallMetadata): Promise; @@ -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; selectText(params: ElementHandleSelectTextParams, metadata?: CallMetadata): Promise; setInputFiles(params: ElementHandleSetInputFilesParams, metadata?: CallMetadata): Promise; - setInputFilePaths(params: ElementHandleSetInputFilePathsParams, metadata?: CallMetadata): Promise; tap(params: ElementHandleTapParams, metadata?: CallMetadata): Promise; textContent(params?: ElementHandleTextContentParams, metadata?: CallMetadata): Promise; type(params: ElementHandleTypeParams, metadata?: CallMetadata): Promise; @@ -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, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 56f6652717..75b3f19d40 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -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