2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-17 19:21:29 +03:00
Files
axios/tests/unit/helpers/buildURL.test.js
T
Liuwei1125 847d89b436 fix: support URL object as config.url input (#10866)
* fix(buildURL): support URL object as input

Fixes #6546

When passing a URL object (e.g., new URL(...)) to axios methods
like axios.get(url, { params: {...} }), the buildURL function
would crash with 'url.indexOf is not a function' because it assumed
url was always a string.

This fix converts URL objects to strings before processing.

* fix: support URL object as config.url input

- Move URL object coercion from buildURL to Axios._request so that all
  downstream consumers (buildFullPath, combineURLs, adapters) see a string.
  This fixes the crash when using URL object with baseURL (combineURLs
  calls .replace() on the URL object).
- Remove the broken buildURL unit test that tested buildURL(url, null)
  without actually exercising URL coercion (buildURL early-returns when
  !params).
- Add e2e tests via the HTTP adapter for URL object with params and
  URL object without params (no crash).

* feat: note + example added under axios(url[, config]) documenting URL support

* feat: url widened to string | URL

* feat: oercion now uses config.url instanceof url

* chore: add additional test cases to improve coverage

* fix: apply suggestions from cubic review

---------

Co-authored-by: liuwei53 <liuwei53@baidu.com>
Co-authored-by: Jason Saayman <jasonsaayman@gmail.com>
2026-05-09 19:09:13 +02:00

208 lines
5.1 KiB
JavaScript

import { describe, it, expect, vi } from 'vitest';
import buildURL, { encode } from '../../../lib/helpers/buildURL.js';
describe('helpers::buildURL', () => {
it('should support null params', () => {
expect(buildURL('/foo')).toEqual('/foo');
});
it('should support params', () => {
expect(
buildURL('/foo', {
foo: 'bar',
isUndefined: undefined,
isNull: null,
})
).toEqual('/foo?foo=bar');
});
it('should support sending raw params to custom serializer func', () => {
const serializer = vi.fn().mockReturnValue('foo=bar');
const params = { foo: 'bar' };
const options = {
serialize: serializer,
};
expect(
buildURL(
'/foo',
{
foo: 'bar',
},
options
)
).toEqual('/foo?foo=bar');
expect(serializer).toHaveBeenCalledTimes(1);
expect(serializer).toHaveBeenCalledWith(params, options);
});
it('should support object params', () => {
expect(
buildURL('/foo', {
foo: {
bar: 'baz',
},
})
).toEqual('/foo?foo%5Bbar%5D=baz');
});
it('should support date params', () => {
const date = new Date();
expect(
buildURL('/foo', {
date,
})
).toEqual('/foo?date=' + date.toISOString());
});
it('should support array params with encode', () => {
expect(
buildURL('/foo', {
foo: ['bar', 'baz'],
})
).toEqual('/foo?foo%5B%5D=bar&foo%5B%5D=baz');
});
it('should support special char params', () => {
expect(
buildURL('/foo', {
foo: ':$, ',
})
).toEqual('/foo?foo=:$,+');
});
it('should support existing params', () => {
expect(
buildURL('/foo?foo=bar', {
bar: 'baz',
})
).toEqual('/foo?foo=bar&bar=baz');
});
it('should support "length" parameter', () => {
expect(
buildURL('/foo', {
query: 'bar',
start: 0,
length: 5,
})
).toEqual('/foo?query=bar&start=0&length=5');
});
it('should correct discard url hash mark', () => {
expect(
buildURL('/foo?foo=bar#hash', {
query: 'baz',
})
).toEqual('/foo?foo=bar&query=baz');
});
it('should support URLSearchParams', () => {
expect(buildURL('/foo', new URLSearchParams('bar=baz'))).toEqual('/foo?bar=baz');
});
it('should support URL object without params', () => {
const url = new URL('http://example.com/foo?a=1');
expect(buildURL(url)).toEqual('http://example.com/foo?a=1');
});
it('should support URL object with params', () => {
const url = new URL('http://example.com/foo?a=1');
expect(buildURL(url, { b: 2 })).toEqual('http://example.com/foo?a=1&b=2');
});
it('should support URL-like object with params', () => {
const url = {
toString() {
return 'http://example.com/foo?a=1';
},
};
expect(buildURL(url, { b: 2 })).toEqual('http://example.com/foo?a=1&b=2');
});
it('should not require global URL to be a constructor', () => {
const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'URL');
const url = {
toString() {
return 'http://example.com/foo?a=1';
},
};
Object.defineProperty(globalThis, 'URL', {
configurable: true,
writable: true,
value: {},
});
try {
expect(buildURL(url, { b: 2 })).toEqual('http://example.com/foo?a=1&b=2');
} finally {
if (descriptor) {
Object.defineProperty(globalThis, 'URL', descriptor);
} else {
delete globalThis.URL;
}
}
});
it('should support custom serialize function', () => {
const params = {
x: 1,
};
const options = {
serialize: (thisParams, thisOptions) => {
expect(thisParams).toEqual(params);
expect(thisOptions).toEqual(options);
return 'rendered';
},
};
expect(buildURL('/foo', params, options)).toEqual('/foo?rendered');
const customSerializer = (thisParams) => {
expect(thisParams).toEqual(params);
return 'rendered';
};
expect(buildURL('/foo', params, customSerializer)).toEqual('/foo?rendered');
});
});
describe('helpers::encode', () => {
it('should be exported as a named export', () => {
expect(typeof encode).toBe('function');
});
it('should leave plain ASCII unchanged', () => {
expect(encode('foo')).toEqual('foo');
});
it('should preserve `:` rather than percent-encoding it', () => {
expect(encode(':')).toEqual(':');
});
it('should preserve `$` rather than percent-encoding it', () => {
expect(encode('$')).toEqual('$');
});
it('should preserve `,` rather than percent-encoding it', () => {
expect(encode(',')).toEqual(',');
});
it('should encode space as `+` (form-style) rather than `%20`', () => {
expect(encode(' ')).toEqual('+');
});
it('should still percent-encode characters outside the preserved set', () => {
expect(encode('a/b')).toEqual('a%2Fb');
expect(encode('a&b')).toEqual('a%26b');
expect(encode('a=b')).toEqual('a%3Db');
});
it('should apply all substitutions together', () => {
expect(encode('a:b$c,d e')).toEqual('a:b$c,d+e');
});
});