mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Fix url encoding query parameters and added tests (#17120)
* fix(utils): query parameters encoding for Url * improvement(utils): added tests to Url Also, made trailing slash behaviour consistent * fix: updated nvmrc to the recommended node version
This commit is contained in:
committed by
GitHub
parent
d312bd172d
commit
0f9f0d8fc4
136
api/src/utils/url.test.ts
Normal file
136
api/src/utils/url.test.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { expect, test, describe } from 'vitest';
|
||||
import { Url } from './url';
|
||||
|
||||
describe('path handling', () => {
|
||||
test('parse and serialize an URL without path', () => {
|
||||
expect(new Url('https://example.com').toString()).toStrictEqual('https://example.com');
|
||||
});
|
||||
|
||||
test('parse and serialize an URL without path, removing trailing slash', () => {
|
||||
expect(new Url('https://example.com/').toString()).toStrictEqual('https://example.com');
|
||||
});
|
||||
|
||||
test('parse and serialize an URL with path component', () => {
|
||||
expect(new Url('https://example.com/path').toString()).toStrictEqual('https://example.com/path');
|
||||
});
|
||||
|
||||
test('parse and serialize an URL with path component, removing trailing slash', () => {
|
||||
expect(new Url('https://example.com/path/').toString()).toStrictEqual('https://example.com/path');
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative URL handling', () => {
|
||||
test('parse and serialize a relative path', () => {
|
||||
expect(new Url('/sample-path').toString()).toStrictEqual('/sample-path');
|
||||
});
|
||||
|
||||
test('parse and serialize a relative path preserving path and hash components', () => {
|
||||
expect(new Url('/sample-path?sample_var=sample_value#sample_hash').toString()).toStrictEqual(
|
||||
'/sample-path?sample_var=sample_value#sample_hash'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('empty scheme handling', () => {
|
||||
test('parse and serialize an URl without scheme', () => {
|
||||
expect(new Url('//example.com/sample-path').toString()).toStrictEqual('//example.com/sample-path');
|
||||
});
|
||||
|
||||
test('parse and serialize a relative path preserving path and hash components', () => {
|
||||
expect(new Url('//example.com:1234/sample-path?sample_var=sample_value#sample_hash').toString()).toStrictEqual(
|
||||
'//example.com:1234/sample-path?sample_var=sample_value#sample_hash'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('query parameter handling', () => {
|
||||
const SAMPLE_URL = 'https://example.com:1234/sample-path?sample_param=sample_value';
|
||||
|
||||
test('parse and serialize an url to the original input', () => {
|
||||
expect(new Url(SAMPLE_URL).toString()).toStrictEqual(SAMPLE_URL);
|
||||
});
|
||||
|
||||
test('parse a URL containing escaped query parameters', () => {
|
||||
expect(new Url('https://example.com:1234/sample-path?key+that+needs+encoding%21=sample%21').query).toStrictEqual({
|
||||
'key that needs encoding!': 'sample!',
|
||||
});
|
||||
});
|
||||
|
||||
test('serialize an url without query', () => {
|
||||
expect(new Url('https://example.com:1234/sample-path').toString()).toStrictEqual(
|
||||
'https://example.com:1234/sample-path'
|
||||
);
|
||||
});
|
||||
|
||||
test('merge existing query params', () => {
|
||||
expect(new Url(SAMPLE_URL).setQuery('new_query_pram', 'new_query_value').toString()).toStrictEqual(
|
||||
'https://example.com:1234/sample-path?sample_param=sample_value&new_query_pram=new_query_value'
|
||||
);
|
||||
});
|
||||
|
||||
test('replace existing query params instead of adding with the same key', () => {
|
||||
expect(new Url(SAMPLE_URL).setQuery('sample_param', 'new_value').toString()).toStrictEqual(
|
||||
'https://example.com:1234/sample-path?sample_param=new_value'
|
||||
);
|
||||
});
|
||||
|
||||
test('properly serialize query params and URL encode them when needed', () => {
|
||||
expect(
|
||||
new Url(SAMPLE_URL)
|
||||
.setQuery('ascii', ` ,;:!?'()/&+=$@`)
|
||||
.setQuery('encoding_optional', `*-.`)
|
||||
.setQuery('non_ascii', `çÁâÑ清ゆ`)
|
||||
.setQuery('key that needs encoding!', `sample`)
|
||||
.toString()
|
||||
).toStrictEqual(
|
||||
'https://example.com:1234/sample-path' +
|
||||
'?sample_param=sample_value' +
|
||||
'&ascii=+%2C%3B%3A%21%3F%27%28%29%2F%26%2B%3D%24%40' +
|
||||
'&encoding_optional=*-.' +
|
||||
'&non_ascii=%C3%A7%C3%81%C3%A2%C3%91%E6%B8%85%E3%82%86' +
|
||||
'&key+that+needs+encoding%21=sample'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAbsolute', () => {
|
||||
test('returns true for full URL', () => {
|
||||
expect(new Url('https://example.com/sample-path').isAbsolute()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
test('returns false when URL has no protocol', () => {
|
||||
expect(new Url('//example.com/sample-path').isAbsolute()).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('returns false for relative URL', () => {
|
||||
expect(new Url('/sample-path').isAbsolute()).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isProtocolRelative', () => {
|
||||
test('returns true when URL has no protocol', () => {
|
||||
expect(new Url('//example.com/sample-path').isProtocolRelative()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
test('returns false for full URL', () => {
|
||||
expect(new Url('https://example.com/sample-path').isProtocolRelative()).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('returns false when for relative URL', () => {
|
||||
expect(new Url('/sample-path').isProtocolRelative()).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isRootRelative', () => {
|
||||
test('returns true when for relative URL', () => {
|
||||
expect(new Url('/sample-path').isRootRelative()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
test('returns false for full URL', () => {
|
||||
expect(new Url('https://example.com/sample-path').isRootRelative()).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('returns false when URL has host but no protocol', () => {
|
||||
expect(new Url('//example.com/sample-path').isRootRelative()).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
@@ -64,13 +64,10 @@ export class Url {
|
||||
const port = this.port !== null ? `:${this.port}` : '';
|
||||
const origin = `${this.host !== null ? `${protocol}//` : ''}${host}${port}`;
|
||||
|
||||
const path = `/${this.path.join('/')}`;
|
||||
const query =
|
||||
Object.keys(this.query).length !== 0
|
||||
? `?${Object.entries(this.query)
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join('&')}`
|
||||
: '';
|
||||
const path = this.path.length ? `/${this.path.join('/')}` : '';
|
||||
|
||||
const query = Object.keys(this.query).length !== 0 ? `?${new URLSearchParams(this.query).toString()}` : '';
|
||||
|
||||
const hash = this.hash !== null ? `#${this.hash}` : '';
|
||||
|
||||
return `${!rootRelative ? origin : ''}${path}${query}${hash}`;
|
||||
|
||||
Reference in New Issue
Block a user