diff --git a/packages/playwright-core/src/server/chromium/crNetworkManager.ts b/packages/playwright-core/src/server/chromium/crNetworkManager.ts index 98727231f2..b78dd4084e 100644 --- a/packages/playwright-core/src/server/chromium/crNetworkManager.ts +++ b/packages/playwright-core/src/server/chromium/crNetworkManager.ts @@ -583,8 +583,9 @@ class InterceptableRequest { } = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request; const type = (requestWillBeSentEvent.type || '').toLowerCase(); let postDataBuffer = null; - if (postDataEntries && postDataEntries.length && postDataEntries[0].bytes) - postDataBuffer = Buffer.from(postDataEntries[0].bytes, 'base64'); + const entries = postDataEntries?.filter(entry => entry.bytes); + if (entries && entries.length) + postDataBuffer = Buffer.concat(entries.map(entry => Buffer.from(entry.bytes!, 'base64'))); this.request = new network.Request(context, frame, serviceWorker, redirectedFrom?.request || null, documentId, url, type, method, postDataBuffer, headersObjectToArray(headers)); } diff --git a/tests/page/page-network-request.spec.ts b/tests/page/page-network-request.spec.ts index d3679ed14a..903fd4cc1d 100644 --- a/tests/page/page-network-request.spec.ts +++ b/tests/page/page-network-request.spec.ts @@ -317,6 +317,30 @@ it('should get |undefined| with postDataJSON() when there is no post data', asyn expect(response.request().postDataJSON()).toBe(null); }); +it('should return multipart/form-data', async ({ page, server, browserName }) => { + it.fixme(browserName === 'webkit', 'File content is missing in WebKit'); + + await page.goto(server.EMPTY_PAGE); + server.setRoute('/post', (req, res) => res.end()); + await page.route('**/*', route => route.continue()); + const requestPromise = page.waitForRequest('**/post'); + await page.evaluate(async () => { + const body = new FormData(); + body.set('name1', 'value1'); + body.set('file', new File(['file-value'], 'foo.txt')); + body.set('name2', 'value2'); + body.append('name2', 'another-value2'); + await fetch('/post', { method: 'POST', body }); + }); + const request = await requestPromise; + const contentType = await request.headerValue('Content-Type'); + const re = /^multipart\/form-data; boundary=(.*)$/; + expect(contentType).toMatch(re); + const b = contentType.match(re)[1]!; + const expected = `--${b}\r\nContent-Disposition: form-data; name=\"name1\"\r\n\r\nvalue1\r\n--${b}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"foo.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nfile-value\r\n--${b}\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nvalue2\r\n--${b}\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nanother-value2\r\n--${b}--\r\n`; + expect(request.postDataBuffer().toString('utf8')).toEqual(expected); +}); + it('should return event source', async ({ page, server }) => { const SSE_MESSAGE = { foo: 'bar' }; // 1. Setup server-sent events on server that immediately sends a message to the client.