diff --git a/index.d.ts b/index.d.ts index 26de25c576..38a05c1249 100644 --- a/index.d.ts +++ b/index.d.ts @@ -21,8 +21,7 @@ type AxiosHeaderTester = (matcher?: AxiosHeaderMatcher) => boolean; export class AxiosHeaders { constructor( - headers?: RawAxiosHeaders | AxiosHeaders, - defaultHeaders?: RawAxiosHeaders | AxiosHeaders + headers?: RawAxiosHeaders | AxiosHeaders ); set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; @@ -39,12 +38,16 @@ export class AxiosHeaders { normalize(format: boolean): AxiosHeaders; + concat(...targets: Array): AxiosHeaders; + toJSON(asStrings?: boolean): RawAxiosHeaders; static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; static accessor(header: string | string[]): AxiosHeaders; + static concat(...targets: Array): AxiosHeaders; + setContentType: AxiosHeaderSetter; getContentType: AxiosHeaderGetter; hasContentType: AxiosHeaderTester; diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 9e56fdad6b..b561645d2f 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -203,7 +203,7 @@ export default function httpAdapter(config) { data: convertedData, status: 200, statusText: 'OK', - headers: {}, + headers: new AxiosHeaders(), config }); } @@ -588,4 +588,4 @@ export default function httpAdapter(config) { }); } -export const __setProxy = setProxy; \ No newline at end of file +export const __setProxy = setProxy; diff --git a/lib/axios.js b/lib/axios.js index 312ec95c17..ed9d662444 100644 --- a/lib/axios.js +++ b/lib/axios.js @@ -75,5 +75,6 @@ axios.AxiosHeaders = AxiosHeaders; axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing); axios.default = axios; + // this module should only have a default export export default axios diff --git a/lib/core/Axios.js b/lib/core/Axios.js index 31f8b531dc..ff602ba52c 100644 --- a/lib/core/Axios.js +++ b/lib/core/Axios.js @@ -47,7 +47,7 @@ class Axios { config = mergeConfig(this.defaults, config); - const {transitional, paramsSerializer} = config; + const {transitional, paramsSerializer, headers} = config; if (transitional !== undefined) { validator.assertOptions(transitional, { @@ -67,20 +67,22 @@ class Axios { // Set config.method config.method = (config.method || this.defaults.method || 'get').toLowerCase(); + let contextHeaders; + // Flatten headers - const defaultHeaders = config.headers && utils.merge( - config.headers.common, - config.headers[config.method] + contextHeaders = headers && utils.merge( + headers.common, + headers[config.method] ); - defaultHeaders && utils.forEach( + contextHeaders && utils.forEach( ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], - function cleanHeaderConfig(method) { - delete config.headers[method]; + (method) => { + delete headers[method]; } ); - config.headers = new AxiosHeaders(config.headers, defaultHeaders); + config.headers = AxiosHeaders.concat(contextHeaders, headers); // filter out skipped interceptors const requestInterceptorChain = []; diff --git a/lib/core/AxiosHeaders.js b/lib/core/AxiosHeaders.js index 68e098a0f7..b2f1c2fe76 100644 --- a/lib/core/AxiosHeaders.js +++ b/lib/core/AxiosHeaders.js @@ -4,7 +4,6 @@ import utils from '../utils.js'; import parseHeaders from '../helpers/parseHeaders.js'; const $internals = Symbol('internals'); -const $defaults = Symbol('defaults'); function normalizeHeader(header) { return header && String(header).trim().toLowerCase(); @@ -30,6 +29,10 @@ function parseTokens(str) { return tokens; } +function isValidHeaderName(str) { + return /^[-_a-zA-Z]+$/.test(str.trim()); +} + function matchHeaderValue(context, value, header, filter) { if (utils.isFunction(filter)) { return filter.call(this, value, header); @@ -66,27 +69,12 @@ function buildAccessors(obj, header) { }); } -function findKey(obj, key) { - key = key.toLowerCase(); - const keys = Object.keys(obj); - let i = keys.length; - let _key; - while (i-- > 0) { - _key = keys[i]; - if (key === _key.toLowerCase()) { - return _key; - } +class AxiosHeaders { + constructor(headers) { + headers && this.set(headers); } - return null; -} - -function AxiosHeaders(headers, defaults) { - headers && this.set(headers); - this[$defaults] = defaults || null; -} -Object.assign(AxiosHeaders.prototype, { - set: function(header, valueOrRewrite, rewrite) { + set(header, valueOrRewrite, rewrite) { const self = this; function setHeader(_value, _header, _rewrite) { @@ -96,69 +84,70 @@ Object.assign(AxiosHeaders.prototype, { throw new Error('header name must be a non-empty string'); } - const key = findKey(self, lHeader); + const key = utils.findKey(self, lHeader); - if (key && _rewrite !== true && (self[key] === false || _rewrite === false)) { - return; + if(!key || self[key] === undefined || _rewrite === true || (_rewrite === undefined && self[key] !== false)) { + self[key || _header] = normalizeValue(_value); } - - self[key || _header] = normalizeValue(_value); } - if (utils.isPlainObject(header)) { - utils.forEach(header, (_value, _header) => { - setHeader(_value, _header, valueOrRewrite); - }); + const setHeaders = (headers, _rewrite) => + utils.forEach(headers, (_value, _header) => setHeader(_value, _header, _rewrite)); + + if (utils.isPlainObject(header) || header instanceof this.constructor) { + setHeaders(header, valueOrRewrite) + } else if(utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) { + setHeaders(parseHeaders(header), valueOrRewrite); } else { - setHeader(valueOrRewrite, header, rewrite); + header != null && setHeader(valueOrRewrite, header, rewrite); } return this; - }, + } - get: function(header, parser) { + get(header, parser) { header = normalizeHeader(header); - if (!header) return undefined; + if (header) { + const key = utils.findKey(this, header); - const key = findKey(this, header); + if (key) { + const value = this[key]; - if (key) { - const value = this[key]; + if (!parser) { + return value; + } - if (!parser) { - return value; - } + if (parser === true) { + return parseTokens(value); + } - if (parser === true) { - return parseTokens(value); - } + if (utils.isFunction(parser)) { + return parser.call(this, value, key); + } - if (utils.isFunction(parser)) { - return parser.call(this, value, key); - } + if (utils.isRegExp(parser)) { + return parser.exec(value); + } - if (utils.isRegExp(parser)) { - return parser.exec(value); + throw new TypeError('parser must be boolean|regexp|function'); } - - throw new TypeError('parser must be boolean|regexp|function'); } - }, + } - has: function(header, matcher) { + has(header, matcher) { header = normalizeHeader(header); if (header) { - const key = findKey(this, header); + const key = utils.findKey(this, header); return !!(key && (!matcher || matchHeaderValue(this, this[key], key, matcher))); } return false; - }, + } - delete: function(header, matcher) { + delete(header, matcher) { const self = this; let deleted = false; @@ -166,7 +155,7 @@ Object.assign(AxiosHeaders.prototype, { _header = normalizeHeader(_header); if (_header) { - const key = findKey(self, _header); + const key = utils.findKey(self, _header); if (key && (!matcher || matchHeaderValue(self, self[key], key, matcher))) { delete self[key]; @@ -183,18 +172,18 @@ Object.assign(AxiosHeaders.prototype, { } return deleted; - }, + } - clear: function() { + clear() { return Object.keys(this).forEach(this.delete.bind(this)); - }, + } - normalize: function(format) { + normalize(format) { const self = this; const headers = {}; utils.forEach(this, (value, header) => { - const key = findKey(headers, header); + const key = utils.findKey(headers, header); if (key) { self[key] = normalizeValue(value); @@ -214,30 +203,47 @@ Object.assign(AxiosHeaders.prototype, { }); return this; - }, + } + + concat(...targets) { + return this.constructor.concat(this, ...targets); + } - toJSON: function(asStrings) { + toJSON(asStrings) { const obj = Object.create(null); - utils.forEach(Object.assign({}, this[$defaults] || null, this), - (value, header) => { - if (value == null || value === false) return; - obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value; - }); + utils.forEach(this, (value, header) => { + value != null && value !== false && (obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value); + }); return obj; } -}); -Object.assign(AxiosHeaders, { - from: function(thing) { - if (utils.isString(thing)) { - return new this(parseHeaders(thing)); - } + [Symbol.iterator]() { + return Object.entries(this.toJSON())[Symbol.iterator](); + } + + toString() { + return Object.entries(this.toJSON()).map(([header, value]) => header + ': ' + value).join('\n'); + } + + get [Symbol.toStringTag]() { + return 'AxiosHeaders'; + } + + static from(thing) { return thing instanceof this ? thing : new this(thing); - }, + } - accessor: function(header) { + static concat(first, ...targets) { + const computed = new this(first); + + targets.forEach((target) => computed.set(target)); + + return computed; + } + + static accessor(header) { const internals = this[$internals] = (this[$internals] = { accessors: {} }); @@ -258,7 +264,7 @@ Object.assign(AxiosHeaders, { return this; } -}); +} AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent']); diff --git a/lib/core/dispatchRequest.js b/lib/core/dispatchRequest.js index 46ced28e33..dfe4c41d66 100644 --- a/lib/core/dispatchRequest.js +++ b/lib/core/dispatchRequest.js @@ -41,6 +41,10 @@ export default function dispatchRequest(config) { config.transformRequest ); + if (['post', 'put', 'patch'].indexOf(config.method) !== -1) { + config.headers.setContentType('application/x-www-form-urlencoded', false); + } + const adapter = config.adapter || defaults.adapter; return adapter(config).then(function onAdapterResolution(response) { diff --git a/lib/core/mergeConfig.js b/lib/core/mergeConfig.js index 328e631824..2aee6b895c 100644 --- a/lib/core/mergeConfig.js +++ b/lib/core/mergeConfig.js @@ -1,6 +1,9 @@ 'use strict'; import utils from '../utils.js'; +import AxiosHeaders from "./AxiosHeaders.js"; + +const headersToObject = (thing) => thing instanceof AxiosHeaders ? thing.toJSON() : thing; /** * Config-specific merge-function which creates a new config-object @@ -16,9 +19,9 @@ export default function mergeConfig(config1, config2) { config2 = config2 || {}; const config = {}; - function getMergedValue(target, source) { + function getMergedValue(target, source, caseless) { if (utils.isPlainObject(target) && utils.isPlainObject(source)) { - return utils.merge(target, source); + return utils.merge.call({caseless}, target, source); } else if (utils.isPlainObject(source)) { return utils.merge({}, source); } else if (utils.isArray(source)) { @@ -28,72 +31,73 @@ export default function mergeConfig(config1, config2) { } // eslint-disable-next-line consistent-return - function mergeDeepProperties(prop) { - if (!utils.isUndefined(config2[prop])) { - return getMergedValue(config1[prop], config2[prop]); - } else if (!utils.isUndefined(config1[prop])) { - return getMergedValue(undefined, config1[prop]); + function mergeDeepProperties(a, b, caseless) { + if (!utils.isUndefined(b)) { + return getMergedValue(a, b, caseless); + } else if (!utils.isUndefined(a)) { + return getMergedValue(undefined, a, caseless); } } // eslint-disable-next-line consistent-return - function valueFromConfig2(prop) { - if (!utils.isUndefined(config2[prop])) { - return getMergedValue(undefined, config2[prop]); + function valueFromConfig2(a, b) { + if (!utils.isUndefined(b)) { + return getMergedValue(undefined, b); } } // eslint-disable-next-line consistent-return - function defaultToConfig2(prop) { - if (!utils.isUndefined(config2[prop])) { - return getMergedValue(undefined, config2[prop]); - } else if (!utils.isUndefined(config1[prop])) { - return getMergedValue(undefined, config1[prop]); + function defaultToConfig2(a, b) { + if (!utils.isUndefined(b)) { + return getMergedValue(undefined, b); + } else if (!utils.isUndefined(a)) { + return getMergedValue(undefined, a); } } // eslint-disable-next-line consistent-return - function mergeDirectKeys(prop) { + function mergeDirectKeys(a, b, prop) { if (prop in config2) { - return getMergedValue(config1[prop], config2[prop]); + return getMergedValue(a, b); } else if (prop in config1) { - return getMergedValue(undefined, config1[prop]); + return getMergedValue(undefined, a); } } const mergeMap = { - 'url': valueFromConfig2, - 'method': valueFromConfig2, - 'data': valueFromConfig2, - 'baseURL': defaultToConfig2, - 'transformRequest': defaultToConfig2, - 'transformResponse': defaultToConfig2, - 'paramsSerializer': defaultToConfig2, - 'timeout': defaultToConfig2, - 'timeoutMessage': defaultToConfig2, - 'withCredentials': defaultToConfig2, - 'adapter': defaultToConfig2, - 'responseType': defaultToConfig2, - 'xsrfCookieName': defaultToConfig2, - 'xsrfHeaderName': defaultToConfig2, - 'onUploadProgress': defaultToConfig2, - 'onDownloadProgress': defaultToConfig2, - 'decompress': defaultToConfig2, - 'maxContentLength': defaultToConfig2, - 'maxBodyLength': defaultToConfig2, - 'beforeRedirect': defaultToConfig2, - 'transport': defaultToConfig2, - 'httpAgent': defaultToConfig2, - 'httpsAgent': defaultToConfig2, - 'cancelToken': defaultToConfig2, - 'socketPath': defaultToConfig2, - 'responseEncoding': defaultToConfig2, - 'validateStatus': mergeDirectKeys + url: valueFromConfig2, + method: valueFromConfig2, + data: valueFromConfig2, + baseURL: defaultToConfig2, + transformRequest: defaultToConfig2, + transformResponse: defaultToConfig2, + paramsSerializer: defaultToConfig2, + timeout: defaultToConfig2, + timeoutMessage: defaultToConfig2, + withCredentials: defaultToConfig2, + adapter: defaultToConfig2, + responseType: defaultToConfig2, + xsrfCookieName: defaultToConfig2, + xsrfHeaderName: defaultToConfig2, + onUploadProgress: defaultToConfig2, + onDownloadProgress: defaultToConfig2, + decompress: defaultToConfig2, + maxContentLength: defaultToConfig2, + maxBodyLength: defaultToConfig2, + beforeRedirect: defaultToConfig2, + transport: defaultToConfig2, + httpAgent: defaultToConfig2, + httpsAgent: defaultToConfig2, + cancelToken: defaultToConfig2, + socketPath: defaultToConfig2, + responseEncoding: defaultToConfig2, + validateStatus: mergeDirectKeys, + headers: (a, b) => mergeDeepProperties(headersToObject(a), headersToObject(b), true) }; utils.forEach(Object.keys(config1).concat(Object.keys(config2)), function computeConfigValue(prop) { const merge = mergeMap[prop] || mergeDeepProperties; - const configValue = merge(prop); + const configValue = merge(config1[prop], config2[prop], prop); (utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue); }); diff --git a/lib/defaults/index.js b/lib/defaults/index.js index b4b9db22bd..b68ab0eae6 100644 --- a/lib/defaults/index.js +++ b/lib/defaults/index.js @@ -10,7 +10,7 @@ import formDataToJSON from '../helpers/formDataToJSON.js'; import adapters from '../adapters/index.js'; const DEFAULT_CONTENT_TYPE = { - 'Content-Type': 'application/x-www-form-urlencoded' + 'Content-Type': undefined }; /** diff --git a/lib/utils.js b/lib/utils.js index e075f9e2ab..849d0444b1 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -228,7 +228,7 @@ const trim = (str) => str.trim ? * @param {Function} fn The callback to invoke for each item * * @param {Boolean} [allOwnKeys = false] - * @returns {void} + * @returns {any} */ function forEach(obj, fn, {allOwnKeys = false} = {}) { // Don't bother if no value provided @@ -263,6 +263,24 @@ function forEach(obj, fn, {allOwnKeys = false} = {}) { } } +function findKey(obj, key) { + key = key.toLowerCase(); + const keys = Object.keys(obj); + let i = keys.length; + let _key; + while (i-- > 0) { + _key = keys[i]; + if (key === _key.toLowerCase()) { + return _key; + } + } + return null; +} + +const _global = typeof self === "undefined" ? typeof global === "undefined" ? this : global : self; + +const isContextDefined = (context) => !isUndefined(context) && context !== _global; + /** * Accepts varargs expecting each argument to be an object, then * immutably merges the properties of each object and returns result. @@ -282,16 +300,18 @@ function forEach(obj, fn, {allOwnKeys = false} = {}) { * @returns {Object} Result of all merge properties */ function merge(/* obj1, obj2, obj3, ... */) { + const {caseless} = isContextDefined(this) && this || {}; const result = {}; const assignValue = (val, key) => { - if (isPlainObject(result[key]) && isPlainObject(val)) { - result[key] = merge(result[key], val); + const targetKey = caseless && findKey(result, key) || key; + if (isPlainObject(result[targetKey]) && isPlainObject(val)) { + result[targetKey] = merge(result[targetKey], val); } else if (isPlainObject(val)) { - result[key] = merge({}, val); + result[targetKey] = merge({}, val); } else if (isArray(val)) { - result[key] = val.slice(); + result[targetKey] = val.slice(); } else { - result[key] = val; + result[targetKey] = val; } } @@ -527,6 +547,11 @@ const reduceDescriptors = (obj, reducer) => { const freezeMethods = (obj) => { reduceDescriptors(obj, (descriptor, name) => { + // skip restricted props in strict mode + if (isFunction(obj) && ['arguments', 'caller', 'callee'].indexOf(name) !== -1) { + return false; + } + const value = obj[name]; if (!isFunction(value)) return; @@ -540,7 +565,7 @@ const freezeMethods = (obj) => { if (!descriptor.set) { descriptor.set = () => { - throw Error('Can not read-only method \'' + name + '\''); + throw Error('Can not rewrite read-only method \'' + name + '\''); }; } }); @@ -609,5 +634,8 @@ export default { toObjectSet, toCamelCase, noop, - toFiniteNumber + toFiniteNumber, + findKey, + global: _global, + isContextDefined }; diff --git a/test/module/test.js b/test/module/test.js index 42c256b69b..166e649ce6 100644 --- a/test/module/test.js +++ b/test/module/test.js @@ -15,6 +15,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); const exec = util.promisify(cp.exec); const {Axios} = axiosFactory; + const ignoreList = ['default']; const instance = axiosFactory.create({}); @@ -133,5 +134,4 @@ describe('module', function () { await exec(`npm test --prefix ${pkgPath}`, {}); }); }); - }); diff --git a/test/specs/core/mergeConfig.spec.js b/test/specs/core/mergeConfig.spec.js index ffe666330e..2669b50923 100644 --- a/test/specs/core/mergeConfig.spec.js +++ b/test/specs/core/mergeConfig.spec.js @@ -1,5 +1,6 @@ import defaults from '../../../lib/defaults'; import mergeConfig from '../../../lib/core/mergeConfig'; +import {AxiosHeaders} from "../../../index.js"; describe('core::mergeConfig', function() { it('should accept undefined for second argument', function() { @@ -100,6 +101,27 @@ describe('core::mergeConfig', function() { expect(merged.nestedConfig.propertyOnRequestConfig).toEqual(true); }); + describe('headers', ()=> { + it('should allow merging with AxiosHeaders instances', () => { + const merged = mergeConfig({ + headers: new AxiosHeaders({ + x: 1, + y: 2 + }) + }, { + headers: new AxiosHeaders({ + X: 1, + Y: 2 + }) + }); + expect(merged.headers).toEqual({ + x: '1', + y: '2' + }); + }); + }); + + describe('valueFromConfig2Keys', function() { const config1 = {url: '/foo', method: 'post', data: {a: 3}}; diff --git a/test/specs/headers.spec.js b/test/specs/headers.spec.js index 92516c343d..765fd3c564 100644 --- a/test/specs/headers.spec.js +++ b/test/specs/headers.spec.js @@ -1,3 +1,5 @@ +const {AxiosHeaders} = axios; + function testHeaderValue(headers, key, val) { let found = false; @@ -106,12 +108,35 @@ describe('headers', function () { }); }); - it('should preserve content-type if data is false', function (done) { + it('should preserve content-type if data is false', async function () { axios.post('/foo', false); - getAjaxRequest().then(function (request) { + await getAjaxRequest().then(function (request) { testHeaderValue(request.requestHeaders, 'Content-Type', 'application/x-www-form-urlencoded'); - done(); }); }); + + it('should allow an AxiosHeaders instance to be used as the value of the headers option', async ()=> { + const instance = axios.create({ + headers: new AxiosHeaders({ + xFoo: 'foo', + xBar: 'bar' + }) + }); + + instance.get('/foo', { + headers: { + XFOO: 'foo2', + xBaz: 'baz' + } + }); + + await getAjaxRequest().then(function (request) { + expect(request.requestHeaders.xFoo).toEqual('foo2'); + expect(request.requestHeaders.xBar).toEqual('bar'); + expect(request.requestHeaders.xBaz).toEqual('baz'); + expect(request.requestHeaders.XFOO).toEqual(undefined); + }); + + }); }); diff --git a/test/specs/options.spec.js b/test/specs/options.spec.js index f2e589b4da..9879fdc885 100644 --- a/test/specs/options.spec.js +++ b/test/specs/options.spec.js @@ -1,3 +1,5 @@ +import AxiosHeaders from "../../lib/core/AxiosHeaders.js"; + describe('options', function () { beforeEach(function () { jasmine.Ajax.install(); diff --git a/test/specs/utils/merge.spec.js b/test/specs/utils/merge.spec.js index 6a820c6efe..75c6312dbf 100644 --- a/test/specs/utils/merge.spec.js +++ b/test/specs/utils/merge.spec.js @@ -83,4 +83,14 @@ describe('utils::merge', function () { expect(d).toEqual({a: [1, 2, 3]}); expect(d.a).not.toBe(a); }); + + it('should support caseless option', ()=> { + const a = {x: 1}; + const b = {X: 2}; + const merged = merge.call({caseless: true}, a, b); + + expect(merged).toEqual({ + x: 2 + }); + }); }); diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js index c92b0394bb..fba276d141 100644 --- a/test/unit/adapters/http.js +++ b/test/unit/adapters/http.js @@ -1393,6 +1393,7 @@ describe('supports http with nodejs', function () { it('should omit a user-agent if one is explicitly disclaimed', function (done) { server = http.createServer(function (req, res) { + console.log(req.headers); assert.equal("user-agent" in req.headers, false); assert.equal("User-Agent" in req.headers, false); res.end(); diff --git a/test/unit/core/AxiosHeaders.js b/test/unit/core/AxiosHeaders.js index aa30091982..758a7d4cbb 100644 --- a/test/unit/core/AxiosHeaders.js +++ b/test/unit/core/AxiosHeaders.js @@ -33,7 +33,16 @@ describe('AxiosHeaders', function () { assert.strictEqual(headers.get('foo'), 'value1'); assert.strictEqual(headers.get('bar'), 'value2'); - }) + }); + + it('should support adding multiple headers from raw headers string', function(){ + const headers = new AxiosHeaders(); + + headers.set(`foo:value1\nbar:value2`); + + assert.strictEqual(headers.get('foo'), 'value1'); + assert.strictEqual(headers.get('bar'), 'value2'); + }); it('should not rewrite header the header if the value is false', function(){ const headers = new AxiosHeaders(); @@ -338,4 +347,49 @@ describe('AxiosHeaders', function () { }); }); }); + + describe('AxiosHeaders.concat', function () { + it('should concatenate plain headers into an AxiosHeader instance', function () { + const a = {a: 1}; + const b = {b: 2}; + const c = {c: 3}; + const headers = AxiosHeaders.concat(a, b, c); + + assert.deepStrictEqual({...headers.toJSON()}, { + a: '1', + b: '2', + c: '3' + }); + }); + + it('should concatenate raw headers into an AxiosHeader instance', function () { + const a = 'a:1\nb:2'; + const b = 'c:3\nx:4'; + const headers = AxiosHeaders.concat(a, b); + + assert.deepStrictEqual({...headers.toJSON()}, { + a: '1', + b: '2', + c: '3', + x: '4' + }); + }); + + it('should concatenate Axios headers into a new AxiosHeader instance', function () { + const a = new AxiosHeaders({x: 1}); + const b = new AxiosHeaders({y: 2}); + const headers = AxiosHeaders.concat(a, b); + + assert.deepStrictEqual({...headers.toJSON()}, { + x: '1', + y: '2' + }); + }); + }); + + describe('toString', function () { + it('should serialize AxiosHeader instance to a raw headers string', function () { + assert.deepStrictEqual(new AxiosHeaders({x:1, y:2}).toString(), 'x: 1\ny: 2'); + }); + }); });