diff --git a/packages/playwright-core/src/client/elementHandle.ts b/packages/playwright-core/src/client/elementHandle.ts index 1e6cdda53a..eabe3b4379 100644 --- a/packages/playwright-core/src/client/elementHandle.ts +++ b/packages/playwright-core/src/client/elementHandle.ts @@ -269,34 +269,25 @@ export async function convertInputFiles(files: string | FilePayload | string[] | if (context._connection.isRemote()) { let streams: channels.WritableStreamChannel[] | undefined; let localPaths: string[] | undefined; - for (const item of items as string[]) { - if ((await fs.promises.stat(item)).isDirectory()) { - const files = (await fs.promises.readdir(item, { withFileTypes: true, recursive: true })).filter(f => f.isFile()).map(f => path.join(item, f.name)); - const { writableStreams, dir } = await context._wrapApiCall(async () => context._channel.createTempDirectory({ - root: item, - items: await Promise.all(files.map(async f => { - const lastModifiedMs = (await fs.promises.stat(f)).mtimeMs; - return { - name: path.relative(item, f), - lastModifiedMs - }; - })), - }), true); - for (let i = 0; i < files.length; i++) { - const writable = WritableStream.from(writableStreams[i]); - await pipelineAsync(fs.createReadStream(files[i]), writable.stream()); - } - localPaths ??= []; - localPaths.push(dir); - } else { - const lastModifiedMs = (await fs.promises.stat(item)).mtimeMs; - const { writableStream } = await context._wrapApiCall(() => context._channel.createTempFile({ name: path.basename(item), lastModifiedMs }), true); - const writable = WritableStream.from(writableStream); - await pipelineAsync(fs.createReadStream(item), writable.stream()); - streams ??= []; - streams.push(writableStream); + await Promise.all((items as string[]).map(async item => { + const isDirectory = (await fs.promises.stat(item)).isDirectory(); + const files = isDirectory ? (await fs.promises.readdir(item, { withFileTypes: true, recursive: true })).filter(f => f.isFile()).map(f => path.join(item, f.name)) : [item]; + const { writableStreams, remoteDir } = await context._wrapApiCall(async () => context._channel.createTempFiles({ + rootDirName: isDirectory ? item : undefined, + items: await Promise.all(files.map(f => fileToTempFileParams(f))), + }), true); + for (let i = 0; i < files.length; i++) { + const writable = WritableStream.from(writableStreams[i]); + await pipelineAsync(fs.createReadStream(files[i]), writable.stream()); } - } + if (isDirectory) { + localPaths ??= []; + localPaths.push(remoteDir); + } else { + streams ??= []; + streams.push(...writableStreams); + } + })); return { streams, localPaths }; } return { localPaths: items.map(f => path.resolve(f as string)) as string[] }; @@ -308,6 +299,11 @@ export async function convertInputFiles(files: string | FilePayload | string[] | return { payloads }; } +async function fileToTempFileParams(file: string): Promise { + const lastModifiedMs = (await fs.promises.stat(file)).mtimeMs; + return { name: path.basename(file), lastModifiedMs }; +} + export function determineScreenshotType(options: { path?: string, type?: 'png' | 'jpeg' }): 'png' | 'jpeg' | undefined { if (options.path) { const mimeType = mime.getType(options.path); diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 35582ecce9..d80f331ae4 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -951,22 +951,15 @@ scheme.BrowserContextHarExportParams = tObject({ scheme.BrowserContextHarExportResult = tObject({ artifact: tChannel(['Artifact']), }); -scheme.BrowserContextCreateTempFileParams = tObject({ - name: tString, - lastModifiedMs: tOptional(tNumber), -}); -scheme.BrowserContextCreateTempFileResult = tObject({ - writableStream: tChannel(['WritableStream']), -}); -scheme.BrowserContextCreateTempDirectoryParams = tObject({ - root: tString, +scheme.BrowserContextCreateTempFilesParams = tObject({ + rootDirName: tOptional(tString), items: tArray(tObject({ name: tString, lastModifiedMs: tOptional(tNumber), })), }); -scheme.BrowserContextCreateTempDirectoryResult = tObject({ - dir: tString, +scheme.BrowserContextCreateTempFilesResult = tObject({ + remoteDir: tString, writableStreams: tArray(tChannel(['WritableStream'])), }); scheme.BrowserContextUpdateSubscriptionParams = tObject({ diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 360b524562..458cfefa1d 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -178,22 +178,13 @@ export class BrowserContextDispatcher extends Dispatcher { + async createTempFiles(params: channels.BrowserContextCreateTempFilesParams): Promise { const dir = this._context._browser.options.artifactsDir; const tmpDir = path.join(dir, 'upload-' + createGuid()); - await fs.promises.mkdir(tmpDir); - this._context._tempDirs.push(tmpDir); - const file = fs.createWriteStream(path.join(tmpDir, params.name)); - return { writableStream: new WritableStreamDispatcher(this, file, params.lastModifiedMs) }; - } - - async createTempDirectory(params: channels.BrowserContextCreateTempDirectoryParams): Promise { - const dir = this._context._browser.options.artifactsDir; - const tmpDir = path.join(dir, 'upload-' + createGuid()); - const tempDirWithRootName = path.join(tmpDir, path.basename(params.root)); + const tempDirWithRootName = path.join(tmpDir, params.rootDirName ? path.basename(params.rootDirName) : ''); await fs.promises.mkdir(tempDirWithRootName, { recursive: true }); return { - dir: tempDirWithRootName, + remoteDir: tempDirWithRootName, writableStreams: await Promise.all(params.items.map(async item => { await fs.promises.mkdir(path.dirname(path.join(tempDirWithRootName, item.name)), { recursive: true }); const file = fs.createWriteStream(path.join(tempDirWithRootName, item.name)); diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 60f6d055bd..ecec62b35b 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -1458,8 +1458,7 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: CallMetadata): Promise; harStart(params: BrowserContextHarStartParams, metadata?: CallMetadata): Promise; harExport(params: BrowserContextHarExportParams, metadata?: CallMetadata): Promise; - createTempFile(params: BrowserContextCreateTempFileParams, metadata?: CallMetadata): Promise; - createTempDirectory(params: BrowserContextCreateTempDirectoryParams, metadata?: CallMetadata): Promise; + createTempFiles(params: BrowserContextCreateTempFilesParams, metadata?: CallMetadata): Promise; updateSubscription(params: BrowserContextUpdateSubscriptionParams, metadata?: CallMetadata): Promise; clockInstallFakeTimers(params: BrowserContextClockInstallFakeTimersParams, metadata?: CallMetadata): Promise; clockRunAllTimers(params?: BrowserContextClockRunAllTimersParams, metadata?: CallMetadata): Promise; @@ -1738,28 +1737,18 @@ export type BrowserContextHarExportOptions = { export type BrowserContextHarExportResult = { artifact: ArtifactChannel, }; -export type BrowserContextCreateTempFileParams = { - name: string, - lastModifiedMs?: number, -}; -export type BrowserContextCreateTempFileOptions = { - lastModifiedMs?: number, -}; -export type BrowserContextCreateTempFileResult = { - writableStream: WritableStreamChannel, -}; -export type BrowserContextCreateTempDirectoryParams = { - root: string, +export type BrowserContextCreateTempFilesParams = { + rootDirName?: string, items: { name: string, lastModifiedMs?: number, }[], }; -export type BrowserContextCreateTempDirectoryOptions = { - +export type BrowserContextCreateTempFilesOptions = { + rootDirName?: string, }; -export type BrowserContextCreateTempDirectoryResult = { - dir: string, +export type BrowserContextCreateTempFilesResult = { + remoteDir: string, writableStreams: WritableStreamChannel[], }; export type BrowserContextUpdateSubscriptionParams = { diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 8d2b9216f8..d36a7bad22 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1184,16 +1184,9 @@ BrowserContext: returns: artifact: Artifact - createTempFile: + createTempFiles: parameters: - name: string - lastModifiedMs: number? - returns: - writableStream: WritableStream - - createTempDirectory: - parameters: - root: string + rootDirName: string? items: type: array items: @@ -1202,7 +1195,7 @@ BrowserContext: name: string lastModifiedMs: number? returns: - dir: string + remoteDir: string writableStreams: type: array items: WritableStream