diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 459f7a9ee..7a4a2316d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -59,10 +59,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Use Node.js 8.x + - name: Use Node.js 12.4 uses: actions/setup-node@v2 with: - node-version: 8.x + node-version: 12.x - name: Cache Node Modules id: cache-node-modules uses: actions/cache@v2 diff --git a/README.md b/README.md index c85fd329d..a0bb533d8 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Swagger Client Version | Release Date | OpenAPI Spec compatibility | Notes ### Runtime -- Node.js `>=` 10.x +- Node.js `>=` 12.4.x - `swagger-client` works in the latest versions of Chrome, Safari, Firefox, and Edge. ## Security contact diff --git a/docs/development/setting-up.md b/docs/development/setting-up.md index 0cc0cf41b..43f117689 100644 --- a/docs/development/setting-up.md +++ b/docs/development/setting-up.md @@ -12,15 +12,13 @@ Current Node.js: - NPM >=7.10.x Current Node.js Active LTS: -- Node.js 14.x +- Node.js >=14.x - NPM >=6.14.x Current Node.js Maintenance LTS: -- Node.js >=12.x +- Node.js >=12.4 - NPM >=6.12.x -> Note: our build artifacts should also work on older node versions such as node>=8. - ### Steps 1. `git clone https://github.com/swagger-api/swagger-js.git` diff --git a/docs/usage/installation.md b/docs/usage/installation.md index 85cd00a42..430bf78a6 100644 --- a/docs/usage/installation.md +++ b/docs/usage/installation.md @@ -4,8 +4,8 @@ ### NPM Registry -We publish two modules to npm: [swagger-client](https://www.npmjs.com/package/swagger-client). -`swagger-client` is meant for consumption by any JavaScript engine (node.js, web, etc...). +We publish single module to npm: [swagger-client](https://www.npmjs.com/package/swagger-client). +`swagger-client` is meant for consumption by any JavaScript engine (node.js, browser, etc...). The npm package contains transpiled and minified ES5 compatible code. ```shell script diff --git a/package-lock.json b/package-lock.json index ea7612623..3e2bb3009 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5202,7 +5202,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "at-least-node": { "version": "1.0.0", @@ -5987,6 +5988,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -6437,7 +6439,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "detect-newline": { "version": "3.1.0", @@ -7705,6 +7708,11 @@ "bser": "2.1.1" } }, + "fetch-blob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz", + "integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==" + }, "fetch-mock": { "version": "9.11.0", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz", @@ -7798,14 +7806,22 @@ "dev": true, "optional": true }, - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "form-data-encoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.0.1.tgz", + "integrity": "sha512-sCHYvVRBIe2iGx30O8g0H8c5O0j1AdnPPO9IWYLIq3nZqBOa6rO+0mipXk+gqTDj9fSQ1Dm/pItxF5QW+B5faQ==", + "requires": { + "nanoid": "3.1.23" + } + }, + "formdata-node": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-3.6.2.tgz", + "integrity": "sha512-G8Hjh1S4Ifsk5e+cTZyteYO9u5ODaif+LsWhzGuGtYQMSlmxN3Ug/NrXpX+ij9kK/EsZq+dM7fQH8fEwhMV3dQ==", "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "fetch-blob": "2.1.2", + "form-data-encoder": "1.0.1", + "node-domexception": "1.0.0" } }, "fp-ts": { @@ -8781,14 +8797,6 @@ "dev": true, "optional": true }, - "isomorphic-form-data": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz", - "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==", - "requires": { - "form-data": "^2.3.2" - } - }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -11054,12 +11062,14 @@ "mime-db": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "dev": true }, "mime-types": { "version": "2.1.26", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "dev": true, "requires": { "mime-db": "1.43.0" } @@ -11158,6 +11168,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -11208,6 +11223,11 @@ "propagate": "^2.0.0" } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", diff --git a/package.json b/package.json index bad65b300..7ec627016 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,9 @@ "name": "swagger-client", "version": "3.13.7", "description": "SwaggerJS - a collection of interfaces for OAI specs", + "browser": { + "./src/http/fold-formdata-to-request.node.js": "./src/http/fold-formdata-to-request.browser.js" + }, "main": "lib/commonjs.js", "module": "es/index.js", "jsnext:main": "es/index.js", @@ -41,7 +44,7 @@ "test:unit:coverage": "cross-env BABEL_ENV=commonjs jest --runInBand --config ./config/jest/jest.unit.coverage.config.js", "test:unit:watch": "cross-env BABEL_ENV=commonjs jest --runInBand --watch --config ./config/jest/jest.unit.config.js", "test:artifact": "run-s test:artifact:umd:browser test:artifact:es test:artifact:commonjs", - "test:artifact:umd:browser": "npm run build:umd:browser && cross-env BABEL_ENV=commonjs jest --config ./config/jest/jest.artifact-umd-browser.config.js", + "test:artifact:umd:browser": "npm run build:umd:browser && cross-env BABEL_ENV=browser jest --config ./config/jest/jest.artifact-umd-browser.config.js", "test:artifact:es": "npm run build:es && cross-env BABEL_ENV=commonjs jest --config ./config/jest/jest.artifact-es.config.js", "test:artifact:commonjs": "npm run build:commonjs && cross-env BABEL_ENV=commonjs jest --config ./config/jest/jest.artifact-commonjs.config.js", "deps:license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv", @@ -109,7 +112,8 @@ "cross-fetch": "^3.1.4", "deep-extend": "~0.6.0", "fast-json-patch": "^3.0.0-1", - "isomorphic-form-data": "~2.0.0", + "form-data-encoder": "^1.0.1", + "formdata-node": "^3.6.2", "js-yaml": "^3.14.0", "lodash": "^4.17.19", "qs": "^6.9.4", diff --git a/src/http/fold-formdata-to-request.browser.js b/src/http/fold-formdata-to-request.browser.js new file mode 100644 index 000000000..ad05f62d2 --- /dev/null +++ b/src/http/fold-formdata-to-request.browser.js @@ -0,0 +1,5 @@ +const foldFormDataToRequest = (formdata, request) => { + request.body = formdata; +}; + +export default foldFormDataToRequest; diff --git a/src/http/fold-formdata-to-request.node.js b/src/http/fold-formdata-to-request.node.js new file mode 100644 index 000000000..9eaa8c251 --- /dev/null +++ b/src/http/fold-formdata-to-request.node.js @@ -0,0 +1,29 @@ +import { Readable } from 'stream'; +import { Encoder } from 'form-data-encoder'; + +/** + * formdata-node works in node-fetch@2.x via form-data-encoder only. + * FormData instance is converted to Encoder instance which gets converted + * to Readable Stream. + * + * TODO(vladimir.gorej@gmail.com): this can be removed when migrated to node-fetch@3.x + */ +const foldFormDataToRequest = (formdata, request) => { + const encoder = new Encoder(formdata); + const readableStream = Readable.from(encoder); + + // get rid of previous headers + delete request.headers['content-type']; + delete request.headers['Content-Type']; + + // set computed headers + request.headers = { ...request.headers, ...encoder.headers }; + + // set FormData instance to request for debugging purposes + request.formdata = formdata; + + // assign readable stream as request body + request.body = readableStream; +}; + +export default foldFormDataToRequest; diff --git a/src/http.js b/src/http/index.js similarity index 95% rename from src/http.js rename to src/http/index.js index 6ec67fc38..5bc3654a7 100644 --- a/src/http.js +++ b/src/http/index.js @@ -4,9 +4,10 @@ import jsYaml from 'js-yaml'; import pick from 'lodash/pick'; import isFunction from 'lodash/isFunction'; import { Buffer } from 'buffer'; +import { FormData } from 'formdata-node'; -import FormData from './internal/form-data-monkey-patch'; -import { encodeDisallowedCharacters } from './execute/oas3/style-serializer'; +import { encodeDisallowedCharacters } from '../execute/oas3/style-serializer'; +import foldFormDataToRequest from './fold-formdata-to-request.node'; // For testing export const self = { @@ -50,9 +51,10 @@ export default async function http(url, request = {}) { } // for content-type=multipart\/form-data remove content-type from request before fetch - // so that correct one with `boundary` is set + // so that correct one with `boundary` is set when request body is different than boundary encoded string const contentType = request.headers['content-type'] || request.headers['Content-Type']; - if (/multipart\/form-data/i.test(contentType)) { + // TODO(vladimir.gorej@gmail.com): assertion of FormData instance can be removed when migrated to node-fetch@2.x + if (/multipart\/form-data/i.test(contentType) && request.body instanceof FormData) { delete request.headers['content-type']; delete request.headers['Content-Type']; } @@ -393,7 +395,8 @@ export function mergeInQueryOrForm(req = {}) { const contentType = req.headers['content-type'] || req.headers['Content-Type']; if (hasFile || /multipart\/form-data/i.test(contentType)) { - req.body = buildFormData(req.form); + const formdata = buildFormData(req.form); + foldFormDataToRequest(formdata, req); } else { req.body = encodeFormOrQuery(form); } diff --git a/src/internal/form-data-monkey-patch.js b/src/internal/form-data-monkey-patch.js deleted file mode 100644 index 84c296a0d..000000000 --- a/src/internal/form-data-monkey-patch.js +++ /dev/null @@ -1,62 +0,0 @@ -import isFunction from 'lodash/isFunction'; -import IsomorphicFormData from 'isomorphic-form-data'; - -// patches FormData type by mutating it. -// patch :: FormData -> PatchedFormData -export const patch = (FormData) => { - const createEntry = (field, value) => ({ name: field, value }); - /** We return original type if prototype already contains one of methods we're trying to patch. - * Reasoning: if one of the methods already exists, it would access data in other - * property than our `_entryList`. That could potentially create nasty - * hardly detectable bugs if `form-data` library implements only couple of - * methods that it misses, instead of implementing all of them. - * Current solution will fail the tests to let us know that form-data library - * already implements some of the methods that we try to monkey-patch, and our - * monkey-patch code should then compensate the library changes easily. - */ - if ( - isFunction(FormData.prototype.set) || - isFunction(FormData.prototype.get) || - isFunction(FormData.prototype.getAll) || - isFunction(FormData.prototype.has) - ) { - return FormData; - } - class PatchedFormData extends FormData { - constructor(form) { - super(form); - this.entryList = []; - } - - append(field, value, options) { - this.entryList.push(createEntry(field, value)); - return super.append(field, value, options); - } - - set(field, value) { - const newEntry = createEntry(field, value); - - this.entryList = this.entryList.filter((entry) => entry.name !== field); - - this.entryList.push(newEntry); - } - - get(field) { - const foundEntry = this.entryList.find((entry) => entry.name === field); - - return foundEntry === undefined ? null : foundEntry; - } - - getAll(field) { - return this.entryList.filter((entry) => entry.name === field).map((entry) => entry.value); - } - - has(field) { - return this.entryList.some((entry) => entry.name === field); - } - } - - return PatchedFormData; -}; - -export default patch(IsomorphicFormData); diff --git a/test/execute/main.js b/test/execute/main.js index 4fa224ce5..bdf28de22 100644 --- a/test/execute/main.js +++ b/test/execute/main.js @@ -1,4 +1,5 @@ -import FormData from '../../src/internal/form-data-monkey-patch'; +import { Readable } from 'stream'; + import { execute, buildRequest, self as stubs } from '../../src/execute'; import { normalizeSwagger } from '../../src/helpers'; @@ -701,14 +702,15 @@ describe('execute', () => { // Then expect(req.headers).toEqual({ - 'Content-Type': 'multipart/form-data', + 'Content-Length': 134, + 'Content-Type': expect.stringMatching(/^multipart\/form-data/), }); // Would like to do a more thourough test ( ie: ensure the value `foo` exists.. // but I don't feel like attacking the interals of the node pollyfill // for FormData, as it seems to be missing `.get()`) expect(req.url).toEqual('http://swagger.io/one'); - expect(req.body).toBeInstanceOf(FormData); + expect(req.body).toBeInstanceOf(Readable); }); test('should add Content-Type application/x-www-form-urlencoded when in: formData ', () => { @@ -1190,7 +1192,7 @@ describe('execute', () => { }); expect(fetchSpy.mock.calls.length).toEqual(1); - expect(fetchSpy.mock.calls[0][0].body).toBeInstanceOf(FormData); + expect(fetchSpy.mock.calls[0][0].body).toBeInstanceOf(Readable); }); describe('parameterBuilders', () => { diff --git a/test/http-multipart.js b/test/http/http-multipart.js similarity index 81% rename from test/http-multipart.js rename to test/http/http-multipart.js index d3d85b814..5a31d002e 100644 --- a/test/http-multipart.js +++ b/test/http/http-multipart.js @@ -1,9 +1,9 @@ import fetchMock from 'fetch-mock'; +import { Readable } from 'stream'; -import FormData from '../src/internal/form-data-monkey-patch'; -import { buildRequest } from '../src/execute'; -import sampleMultipartOpenApi2 from './data/sample-multipart-oas2'; -import sampleMultipartOpenApi3 from './data/sample-multipart-oas3'; +import { buildRequest } from '../../src/execute'; +import sampleMultipartOpenApi2 from '../data/sample-multipart-oas2'; +import sampleMultipartOpenApi3 from '../data/sample-multipart-oas3'; /** * fetch-mock uses node-fetch under the hood @@ -39,49 +39,48 @@ describe('buildRequest - openapi 2.0', () => { url: '/api/v1/land/content/ViewOfAuthOwner', credentials: 'same-origin', headers: { - 'Content-Type': 'multipart/form-data', + 'Content-Type': expect.stringMatching(/^multipart\/form-data/), }, }); }); - test('should build request body as FormData', () => { - const validateFormDataInstance = req.body instanceof FormData; - expect(validateFormDataInstance).toEqual(true); + test('should build request body as Readable Stream', () => { + expect(req.body).toBeInstanceOf(Readable); }); test('should return "collectionFormat: multi" as FormData entry list and entry item entries (in order)', () => { - const itemEntries = req.body.getAll('email[]'); + const itemEntries = req.formdata.getAll('email[]'); expect(itemEntries.length).toEqual(2); expect(itemEntries[0]).toEqual('person1'); expect(itemEntries[1]).toEqual('person2'); }); test('should return "collectionFormat: none" as single FormData entry in csv format', () => { - const itemEntriesNone = req.body.getAll('none[]'); + const itemEntriesNone = req.formdata.getAll('none[]'); expect(itemEntriesNone.length).toEqual(1); expect(itemEntriesNone[0]).toEqual('foo,bar'); }); test('should return "collectionFormat: csv" as single FormData entry in csv format', () => { - const itemEntriesCsv = req.body.getAll('csv[]'); + const itemEntriesCsv = req.formdata.getAll('csv[]'); expect(itemEntriesCsv.length).toEqual(1); expect(itemEntriesCsv[0]).toEqual('foo,bar'); }); test('should return "collectionFormat: tsv" as single FormData entry in tsv format', () => { - const itemEntriesTsv = req.body.getAll('tsv[]'); + const itemEntriesTsv = req.formdata.getAll('tsv[]'); expect(itemEntriesTsv.length).toEqual(1); expect(itemEntriesTsv[0]).toEqual('foo%09bar'); }); test('should return "collectionFormat: ssv" as single FormData entry in ssv format', () => { - const itemEntriesSsv = req.body.getAll('ssv[]'); + const itemEntriesSsv = req.formdata.getAll('ssv[]'); expect(itemEntriesSsv.length).toEqual(1); expect(itemEntriesSsv[0]).toEqual('foo%20bar'); }); test('should return "collectionFormat: pipes" as single FormData entry in pipes format', () => { - const itemEntriesPipes = req.body.getAll('pipes[]'); + const itemEntriesPipes = req.formdata.getAll('pipes[]'); expect(itemEntriesPipes.length).toEqual(1); expect(itemEntriesPipes[0]).toEqual('foo|bar'); }); @@ -132,10 +131,9 @@ describe('buildRequest - openapi 2.0', () => { expect(json.data.email.length).toEqual(2); expect(json.data.email[0]).toEqual('person1'); expect(json.data.email[1]).toEqual('person2'); - // duck typing that fetch received a FormData instance instead of plain object + // fetch received a FormData instance instead of plain object const lastOptions = fetchMock.lastOptions(); - expect(lastOptions.body.readable).toEqual(true); - // expect(lastOptions.body._streams).toBeDefined() + expect(lastOptions.body).toBeInstanceOf(Readable); }); }); }); @@ -159,18 +157,17 @@ describe('buildRequest - openapi 3.0', () => { url: '/api/v1/land/content/ViewOfAuthOwner', credentials: 'same-origin', headers: { - 'Content-Type': 'multipart/form-data', + 'Content-Type': expect.stringMatching(/^multipart\/form-data/), }, }); }); test('should build request body as FormData', () => { - const validateFormDataInstance = req.body instanceof FormData; - expect(validateFormDataInstance).toEqual(true); + expect(req.body).toBeInstanceOf(Readable); }); test('should return FormData entry list and item entries (in order)', () => { - const itemEntries = req.body.getAll('email[]'); + const itemEntries = req.formdata.getAll('email[]'); expect(itemEntries.length).toEqual(2); expect(itemEntries[0]).toEqual('person1'); expect(itemEntries[1]).toEqual('person2'); @@ -222,10 +219,9 @@ describe('buildRequest - openapi 3.0', () => { expect(json.data.email.length).toEqual(2); expect(json.data.email[0]).toEqual('person1'); expect(json.data.email[1]).toEqual('person2'); - // duck typing that fetch received a FormData instance instead of plain object + // fetch received a FormData instance instead of plain object const lastOptions = fetchMock.lastOptions(); - expect(lastOptions.body.readable).toEqual(true); - // expect(lastOptions.body._streams).toBeDefined() + expect(lastOptions.body).toBeInstanceOf(Readable); }); }); }); @@ -243,21 +239,21 @@ describe('buildRequest - openapi 3.0', () => { }, }); - test('should return FormData entry list and item entries (in order)', () => { + test('should return FormData entry list and item entries (in order)', async () => { expect(req).toMatchObject({ method: 'POST', url: '/api/v1/land/content/uploadImage', credentials: 'same-origin', headers: { - 'Content-Type': 'multipart/form-data', + 'Content-Type': expect.stringMatching(/^multipart\/form-data/), }, }); - const validateFormDataInstance = req.body instanceof FormData; - expect(validateFormDataInstance).toEqual(true); - const itemEntries = req.body.getAll('images[]'); + expect(req.body).toBeInstanceOf(Readable); + const itemEntries = req.formdata.getAll('images[]'); + expect(itemEntries.length).toEqual(2); - expect(itemEntries[0]).toEqual(file1); - expect(itemEntries[1]).toEqual(file2); + expect(await itemEntries[0].text()).toEqual(file1.toString()); + expect(await itemEntries[1].text()).toEqual(file2.toString()); }); }); @@ -314,14 +310,14 @@ describe('buildRequest - openapi 3.0', () => { url: 'http://petstore.swagger.io/v2/one', credentials: 'same-origin', headers: { - 'Content-Type': 'multipart/form-data', + 'Content-Type': expect.stringMatching(/^multipart\/form-data/), }, }); - expect(Array.from(req.body.entryList)).toEqual([ - { name: 'color[R]', value: '100' }, - { name: 'color[G]', value: '200' }, - { name: 'color[B]', value: '150' }, + expect(Array.from(req.formdata)).toEqual([ + ['color[R]', '100'], + ['color[G]', '200'], + ['color[B]', '150'], ]); }); }); diff --git a/test/http.js b/test/http/index.js similarity index 99% rename from test/http.js rename to test/http/index.js index bb10f545e..4c35af9e6 100644 --- a/test/http.js +++ b/test/http/index.js @@ -8,7 +8,7 @@ import http, { serializeRes, shouldDownloadAsText, isFile, -} from '../src/http'; +} from '../../src/http'; describe('http', () => { let xapp; @@ -240,7 +240,7 @@ describe('http', () => { }, }; mergeInQueryOrForm(req); - const fdArrayItem = req.body.getAll('testJson'); + const fdArrayItem = req.formdata.getAll('testJson'); expect(fdArrayItem.length).toEqual(1); expect(fdArrayItem[0]).toEqual('{"name": "John"}'); });