diff --git a/docs/src/api/class-apirequestcontext.md b/docs/src/api/class-apirequestcontext.md index bdb305eeb7..9ceafa3978 100644 --- a/docs/src/api/class-apirequestcontext.md +++ b/docs/src/api/class-apirequestcontext.md @@ -371,6 +371,16 @@ await request.GetAsync("https://example.com/api/getText", new() { Params = param * since: v1.16 ### option: APIRequestContext.get.headers = %%-js-python-csharp-fetch-option-headers-%% * since: v1.16 +### option: APIRequestContext.get.data = %%-js-python-csharp-fetch-option-data-%% +* since: v1.26 +### option: APIRequestContext.get.form = %%-js-python-fetch-option-form-%% +* since: v1.26 +### option: APIRequestContext.get.form = %%-csharp-fetch-option-form-%% +* since: v1.26 +### option: APIRequestContext.get.multipart = %%-js-python-fetch-option-multipart-%% +* since: v1.26 +### option: APIRequestContext.get.multipart = %%-csharp-fetch-option-multipart-%% +* since: v1.26 ### option: APIRequestContext.get.timeout = %%-js-python-csharp-fetch-option-timeout-%% * since: v1.16 ### option: APIRequestContext.get.failOnStatusCode = %%-js-python-csharp-fetch-option-failonstatuscode-%% @@ -398,6 +408,16 @@ context cookies from the response. The method will automatically follow redirect * since: v1.16 ### option: APIRequestContext.head.headers = %%-js-python-csharp-fetch-option-headers-%% * since: v1.16 +### option: APIRequestContext.head.data = %%-js-python-csharp-fetch-option-data-%% +* since: v1.26 +### option: APIRequestContext.head.form = %%-js-python-fetch-option-form-%% +* since: v1.26 +### option: APIRequestContext.head.form = %%-csharp-fetch-option-form-%% +* since: v1.26 +### option: APIRequestContext.head.multipart = %%-js-python-fetch-option-multipart-%% +* since: v1.26 +### option: APIRequestContext.head.multipart = %%-csharp-fetch-option-multipart-%% +* since: v1.26 ### option: APIRequestContext.head.timeout = %%-js-python-csharp-fetch-option-timeout-%% * since: v1.16 ### option: APIRequestContext.head.failOnStatusCode = %%-js-python-csharp-fetch-option-failonstatuscode-%% diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index 5bd399eabb..8083eb5fbf 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -51,7 +51,6 @@ type NewContextOptions = Omit; -type RequestWithoutBodyOptions = Omit; export class APIRequest implements api.APIRequest { private _playwright: Playwright; @@ -107,14 +106,14 @@ export class APIRequestContext extends ChannelOwner { + async head(url: string, options?: RequestWithBodyOptions): Promise { return this.fetch(url, { ...options, method: 'HEAD', }); } - async get(url: string, options?: RequestWithoutBodyOptions): Promise { + async get(url: string, options?: RequestWithBodyOptions): Promise { return this.fetch(url, { ...options, method: 'GET', diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 959e33eca2..676de9042e 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -12885,11 +12885,25 @@ export interface APIRequestContext { * @param options */ get(url: string, options?: { + /** + * Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string and + * `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type` header will + * be set to `application/octet-stream` if not explicitly set. + */ + data?: string|Buffer|Serializable; + /** * Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes. */ failOnStatusCode?: boolean; + /** + * Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent as + * this request body. If this parameter is specified `content-type` header will be set to + * `application/x-www-form-urlencoded` unless explicitly provided. + */ + form?: { [key: string]: string|number|boolean; }; + /** * Allows to set HTTP headers. */ @@ -12906,6 +12920,29 @@ export interface APIRequestContext { */ maxRedirects?: number; + /** + * Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request + * body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly + * provided. File values can be passed either as [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) + * or as file-like object containing file name, mime-type and its content. + */ + multipart?: { [key: string]: string|number|boolean|ReadStream|{ + /** + * File name + */ + name: string; + + /** + * File type + */ + mimeType: string; + + /** + * File content + */ + buffer: Buffer; + }; }; + /** * Query parameters to be sent with the URL. */ @@ -12925,11 +12962,25 @@ export interface APIRequestContext { * @param options */ head(url: string, options?: { + /** + * Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string and + * `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type` header will + * be set to `application/octet-stream` if not explicitly set. + */ + data?: string|Buffer|Serializable; + /** * Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes. */ failOnStatusCode?: boolean; + /** + * Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent as + * this request body. If this parameter is specified `content-type` header will be set to + * `application/x-www-form-urlencoded` unless explicitly provided. + */ + form?: { [key: string]: string|number|boolean; }; + /** * Allows to set HTTP headers. */ @@ -12946,6 +12997,29 @@ export interface APIRequestContext { */ maxRedirects?: number; + /** + * Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request + * body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless explicitly + * provided. File values can be passed either as [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) + * or as file-like object containing file name, mime-type and its content. + */ + multipart?: { [key: string]: string|number|boolean|ReadStream|{ + /** + * File name + */ + name: string; + + /** + * File type + */ + mimeType: string; + + /** + * File content + */ + buffer: Buffer; + }; }; + /** * Query parameters to be sent with the URL. */ diff --git a/tests/library/browsercontext-fetch.spec.ts b/tests/library/browsercontext-fetch.spec.ts index 55d3a64a64..c10ccfc7f9 100644 --- a/tests/library/browsercontext-fetch.spec.ts +++ b/tests/library/browsercontext-fetch.spec.ts @@ -409,20 +409,83 @@ it('should return error with wrong credentials', async ({ context, server }) => expect(response2.status()).toBe(401); }); -for (const method of ['delete', 'get', 'head', 'patch', 'post', 'put']) { - it(`${method} should support post data`, async ({ context, server }) => { - const [request, response] = await Promise.all([ - server.waitForRequest('/simple.json'), - context.request[method](`${server.PREFIX}/simple.json`, { - data: 'My request' - }) - ]); - expect(request.method).toBe(method.toUpperCase()); - expect((await request.postBody).toString()).toBe('My request'); - expect(response.status()).toBe(200); - expect(request.url).toBe('/simple.json'); - }); -} +it('delete should support post data', async ({ context, server }) => { + const [request, response] = await Promise.all([ + server.waitForRequest('/simple.json'), + context.request.delete(`${server.PREFIX}/simple.json`, { + data: 'My request' + }) + ]); + expect(request.method).toBe('DELETE'); + expect((await request.postBody).toString()).toBe('My request'); + expect(response.status()).toBe(200); + expect(request.url).toBe('/simple.json'); +}); + +it('get should support post data', async ({ context, server }) => { + const [request, response] = await Promise.all([ + server.waitForRequest('/simple.json'), + context.request.get(`${server.PREFIX}/simple.json`, { + data: 'My request' + }) + ]); + expect(request.method).toBe('GET'); + expect((await request.postBody).toString()).toBe('My request'); + expect(response.status()).toBe(200); + expect(request.url).toBe('/simple.json'); +}); + +it('head should support post data', async ({ context, server }) => { + const [request, response] = await Promise.all([ + server.waitForRequest('/simple.json'), + context.request.head(`${server.PREFIX}/simple.json`, { + data: 'My request' + }) + ]); + expect(request.method).toBe('HEAD'); + expect((await request.postBody).toString()).toBe('My request'); + expect(response.status()).toBe(200); + expect(request.url).toBe('/simple.json'); +}); + +it('patch should support post data', async ({ context, server }) => { + const [request, response] = await Promise.all([ + server.waitForRequest('/simple.json'), + context.request.patch(`${server.PREFIX}/simple.json`, { + data: 'My request' + }) + ]); + expect(request.method).toBe('PATCH'); + expect((await request.postBody).toString()).toBe('My request'); + expect(response.status()).toBe(200); + expect(request.url).toBe('/simple.json'); +}); + +it('post should support post data', async ({ context, server }) => { + const [request, response] = await Promise.all([ + server.waitForRequest('/simple.json'), + context.request.post(`${server.PREFIX}/simple.json`, { + data: 'My request' + }) + ]); + expect(request.method).toBe('POST'); + expect((await request.postBody).toString()).toBe('My request'); + expect(response.status()).toBe(200); + expect(request.url).toBe('/simple.json'); +}); + +it('put should support post data', async ({ context, server }) => { + const [request, response] = await Promise.all([ + server.waitForRequest('/simple.json'), + context.request.put(`${server.PREFIX}/simple.json`, { + data: 'My request' + }) + ]); + expect(request.method).toBe('PUT'); + expect((await request.postBody).toString()).toBe('My request'); + expect(response.status()).toBe(200); + expect(request.url).toBe('/simple.json'); +}); it('should add default headers', async ({ context, server, page }) => { const [request] = await Promise.all([