feat(library): accept FormData in fetch (#32602)
Closes https://github.com/microsoft/playwright/issues/26520 by accepting `FormData`, which became stable in Node.js in v21.
This commit is contained in:
parent
cd4dabef8b
commit
48c7fb6b06
|
|
@ -159,7 +159,10 @@ context cookies from the response. The method will automatically follow redirect
|
|||
### option: APIRequestContext.delete.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.17
|
||||
|
||||
### option: APIRequestContext.delete.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.delete.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.17
|
||||
|
||||
### option: APIRequestContext.delete.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.17
|
||||
|
||||
### option: APIRequestContext.delete.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
@ -332,7 +335,10 @@ If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/
|
|||
### option: APIRequestContext.fetch.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.fetch.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.fetch.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.fetch.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.fetch.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
@ -442,7 +448,10 @@ await request.GetAsync("https://example.com/api/getText", new() { Params = query
|
|||
### option: APIRequestContext.get.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.26
|
||||
|
||||
### option: APIRequestContext.get.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.get.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.26
|
||||
|
||||
### option: APIRequestContext.get.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.26
|
||||
|
||||
### option: APIRequestContext.get.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
@ -504,7 +513,10 @@ context cookies from the response. The method will automatically follow redirect
|
|||
### option: APIRequestContext.head.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.26
|
||||
|
||||
### option: APIRequestContext.head.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.head.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.26
|
||||
|
||||
### option: APIRequestContext.head.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.26
|
||||
|
||||
### option: APIRequestContext.head.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
@ -566,7 +578,10 @@ context cookies from the response. The method will automatically follow redirect
|
|||
### option: APIRequestContext.patch.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.patch.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.patch.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.patch.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.patch.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
@ -749,7 +764,10 @@ await request.PostAsync("https://example.com/api/uploadScript", new() { Multipar
|
|||
### option: APIRequestContext.post.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.post.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.post.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.post.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.post.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
@ -811,7 +829,10 @@ context cookies from the response. The method will automatically follow redirect
|
|||
### option: APIRequestContext.put.data = %%-js-python-csharp-fetch-option-data-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.put.form = %%-js-python-fetch-option-form-%%
|
||||
### option: APIRequestContext.put.form = %%-python-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.put.form = %%-js-fetch-option-form-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.put.form = %%-csharp-fetch-option-form-%%
|
||||
|
|
|
|||
|
|
@ -405,8 +405,16 @@ Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to d
|
|||
Whether to throw on response codes other than 2xx and 3xx. By default response object is returned
|
||||
for all status codes.
|
||||
|
||||
## js-python-fetch-option-form
|
||||
* langs: js, python
|
||||
## js-fetch-option-form
|
||||
* langs: js
|
||||
- `form` <[Object]<[string], [string]|[float]|[boolean]>|[FormData]>
|
||||
|
||||
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.
|
||||
|
||||
## python-fetch-option-form
|
||||
* langs: python
|
||||
- `form` <[Object]<[string], [string]|[float]|[boolean]>>
|
||||
|
||||
Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent as
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ export type FetchOptions = {
|
|||
method?: string,
|
||||
headers?: Headers,
|
||||
data?: string | Buffer | Serializable,
|
||||
form?: { [key: string]: string|number|boolean; };
|
||||
multipart?: { [key: string]: string|number|boolean|fs.ReadStream|FilePayload; };
|
||||
form?: { [key: string]: string|number|boolean; } | FormData;
|
||||
multipart?: { [key: string]: string|number|boolean|fs.ReadStream|FilePayload; } | FormData;
|
||||
timeout?: number,
|
||||
failOnStatusCode?: boolean,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
|
|
@ -202,7 +202,16 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
throw new Error(`Unexpected 'data' type`);
|
||||
}
|
||||
} else if (options.form) {
|
||||
formData = objectToArray(options.form);
|
||||
if (globalThis.FormData && options.form instanceof FormData) {
|
||||
formData = [];
|
||||
for (const [name, value] of options.form.entries()) {
|
||||
if (typeof value !== 'string')
|
||||
throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`);
|
||||
formData.push({ name, value });
|
||||
}
|
||||
} else {
|
||||
formData = objectToArray(options.form);
|
||||
}
|
||||
} else if (options.multipart) {
|
||||
multipartData = [];
|
||||
if (globalThis.FormData && options.multipart instanceof FormData) {
|
||||
|
|
|
|||
14
packages/playwright-core/types/types.d.ts
vendored
14
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -16559,7 +16559,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
@ -16689,7 +16689,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
@ -16807,7 +16807,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
@ -16893,7 +16893,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
@ -16979,7 +16979,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
@ -17107,7 +17107,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
@ -17193,7 +17193,7 @@ export interface APIRequestContext {
|
|||
* 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; };
|
||||
form?: { [key: string]: string|number|boolean; }|FormData;
|
||||
|
||||
/**
|
||||
* Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by
|
||||
|
|
|
|||
|
|
@ -960,6 +960,22 @@ it('should support application/x-www-form-urlencoded', async function({ context,
|
|||
expect(params.get('file')).toBe('f.js');
|
||||
});
|
||||
|
||||
it('should support application/x-www-form-urlencoded with param lists', async function({ context, page, server }) {
|
||||
const form = new FormData();
|
||||
form.append('foo', '1');
|
||||
form.append('foo', '2');
|
||||
const [req] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
context.request.post(server.EMPTY_PAGE, { form })
|
||||
]);
|
||||
expect(req.method).toBe('POST');
|
||||
expect(req.headers['content-type']).toBe('application/x-www-form-urlencoded');
|
||||
const body = (await req.postBody).toString('utf8');
|
||||
const params = new URLSearchParams(body);
|
||||
expect(req.headers['content-length']).toBe(String(params.toString().length));
|
||||
expect(params.getAll('foo')).toEqual(['1', '2']);
|
||||
});
|
||||
|
||||
it('should encode to application/json by default', async function({ context, page, server }) {
|
||||
const data = {
|
||||
firstName: 'John',
|
||||
|
|
|
|||
Loading…
Reference in a new issue