import { appendQueryParameters, parseQueryString } from '../location'; import { loadFixtures } from './helpers'; describe('location utility', () => { describe('when appendQueryParameters is called', () => { it('should append query parameters to url string', () => { expect(appendQueryParameters('api.flow.io', { limit: 25, offset: 0, })).toBe('api.flow.io?limit=25&offset=0'); }); it('should encode query parameters', () => { expect(appendQueryParameters('api.flow.io', { name: 'john doe', })).toBe(`api.flow.io?name=${encodeURIComponent('john doe')}`); }); it('should honor existing queries', () => { expect(appendQueryParameters('api.flow.io?limit=25', { offset: 0, sort: 'name', })).toBe('api.flow.io?limit=25&offset=0&sort=name'); }); it('should honor existing hashes', () => { expect(appendQueryParameters('api.flow.io#hash', { limit: 25, })).toBe('api.flow.io?limit=25#hash'); }); it('should map nested objects into query parameters', () => { expect(appendQueryParameters('api.flow.io', { user: { firstName: 'John', lastName: 'Doe', }, })).toBe('api.flow.io?user[firstName]=John&user[lastName]=Doe'); }); it('should map nested arrays into query parameters', () => { expect(appendQueryParameters('api.flow.io', { streets: [ '1600 Amphitheatre Parkway', '2nd Floor', ], // eslint-disable-next-line max-len })).toBe(`api.flow.io?streets[0]=${encodeURIComponent('1600 Amphitheatre Parkway')}&streets[1]=${encodeURIComponent('2nd Floor')}`); }); it('should map nested object with arrays into query parameters', () => { expect(appendQueryParameters('api.flow.io', { address: { streets: [ '1600 Amphitheatre Parkway', '2nd Floor', ], }, // eslint-disable-next-line max-len })).toBe(`api.flow.io?address[streets][0]=${encodeURIComponent('1600 Amphitheatre Parkway')}&address[streets][1]=${encodeURIComponent('2nd Floor')}`); }); }); describe('when parseQueryString is called', () => { it('should not return query params', () => { expect(parseQueryString()).toEqual({}); }); it('should strip leading question mark', () => { const params = parseQueryString('?foo=bar'); const withQuestionMark = Object.entries(params) .find( ([key, value]) => String(value).includes('?') || key.includes('?'), ); expect(params).toEqual({ foo: 'bar' }); expect(!!withQuestionMark).toBe(false); }); it('should return simple params', () => { const params = parseQueryString('?key=1&experience=canada&utm_campaign=testing'); expect(params).toEqual({ key: '1', experience: 'canada', utm_campaign: 'testing', }); }); it('should return simple params (without question mark)', () => { const params = parseQueryString('key=1&experience=canada&utm_campaign=testing'); expect(params).toEqual({ key: '1', experience: 'canada', utm_campaign: 'testing', }); }); it('should decode complex keys and values', () => { const params = parseQueryString(`?key=1&${encodeURIComponent('with&and')}=${encodeURIComponent('with space')}`); expect(params).toEqual({ key: '1', 'with&and': 'with space', }); }); it('should decode nested objects/arrays', () => { const params = parseQueryString('?test[0]=aa&test[1]=bb&test[2]=cc&barp[sea]=blue&barp[water]=34&barp[earth][elements][0]=oil&barp[earth][elements][1]=fire'); expect(params).toEqual({ test: ['aa', 'bb', 'cc'], barp: { sea: 'blue', water: '34', earth: { elements: ['oil', 'fire'], }, }, }); }); it('should sanitize deny list keys', () => { const params = parseQueryString('__proto__=1&experience=canada&prototype=testing&constructor=test'); expect(params).toEqual({ __PROTO__: '1', PROTOTYPE: 'testing', CONSTRUCTOR: 'test', experience: 'canada', }); }); it('should sanitize deny list keys in nested objects/arrays', () => { const params = parseQueryString('?test[0]=aa&test[1]=bb&test[2]=cc&barp[constructor]=test&barp[sea]=blue&barp[water]=34&barp[earth][elements][0]=oil&barp[earth][elements][1]=fire'); expect(params).toEqual({ test: ['aa', 'bb', 'cc'], barp: { CONSTRUCTOR: 'test', sea: 'blue', water: '34', earth: { elements: ['oil', 'fire'], }, }, }); }); it('should handle all fixtures correctly', () => { const fixtures = loadFixtures(); fixtures.forEach((fixture) => { expect(parseQueryString(fixture.query)) .toEqual(fixture.json); }); }); }); });