diff --git a/frontend/src/helpers/__tests__/querySyncHelper.spec.js b/frontend/src/helpers/__tests__/querySyncHelper.spec.js new file mode 100644 index 0000000000..b351725e87 --- /dev/null +++ b/frontend/src/helpers/__tests__/querySyncHelper.spec.js @@ -0,0 +1,73 @@ +import { getQueryAsString } from '@/helpers/querySyncHelper' + +describe('getQueryAsString', () => { + it('should return an empty string for an empty query object', () => { + const result = getQueryAsString({}) + expect(result).toBe('') + }) + + it('should convert a simple query object to a query string', () => { + const queryObj = { + responsible: '3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2', + category: 'c829d2d0-7e81-4b6e-8f68-9b4d2c58a109', + } + const result = getQueryAsString(queryObj) + expect(result).toBe( + '?responsible=3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2&category=c829d2d0-7e81-4b6e-8f68-9b4d2c58a109' + ) + }) + + it('should handle array values in the query object', () => { + const queryObj = { + responsible: [ + '3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2', + '65cdde81-32bc-4ff3-9f59-9e23c1a6ec6a', + ], + category: 'c829d2d0-7e81-4b6e-8f68-9b4d2c58a109', + progressLabel: ['f4d40dd5-049b-4ae0-9a04-18c34f35b4a5'], + } + const result = getQueryAsString(queryObj) + expect(result).toBe( + '?responsible=3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2&responsible=65cdde81-32bc-4ff3-9f59-9e23c1a6ec6a&category=c829d2d0-7e81-4b6e-8f68-9b4d2c58a109&progressLabel=f4d40dd5-049b-4ae0-9a04-18c34f35b4a5' + ) + }) + + it('should properly encode special characters in keys and values', () => { + const queryObj = { + 'responsible@1': '3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2', + 'category&2': 'c829d2d0-7e81-4b6e-8f68-9b4d2c58a109', + progressLabel$3: 'f4d40dd5-049b-4ae0-9a04-18c34f35b4a5', + } + const result = getQueryAsString(queryObj) + expect(result).toBe( + '?responsible%401=3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2&category%262=c829d2d0-7e81-4b6e-8f68-9b4d2c58a109&progressLabel%243=f4d40dd5-049b-4ae0-9a04-18c34f35b4a5' + ) + }) + + it('should handle a mix of string and array values', () => { + const queryObj = { + responsible: [ + '3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2', + '65cdde81-32bc-4ff3-9f59-9e23c1a6ec6a', + ], + category: 'c829d2d0-7e81-4b6e-8f68-9b4d2c58a109', + progressLabel: ['f4d40dd5-049b-4ae0-9a04-18c34f35b4a5'], + } + const result = getQueryAsString(queryObj) + expect(result).toBe( + '?responsible=3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2&responsible=65cdde81-32bc-4ff3-9f59-9e23c1a6ec6a&category=c829d2d0-7e81-4b6e-8f68-9b4d2c58a109&progressLabel=f4d40dd5-049b-4ae0-9a04-18c34f35b4a5' + ) + }) + + it('should ignore key-value pairs with null values', () => { + const queryObj = { + responsible: '3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2', + category: null, + progressLabel: 'f4d40dd5-049b-4ae0-9a04-18c34f35b4a5', + } + const result = getQueryAsString(queryObj) + expect(result).toBe( + '?responsible=3bde8a8a-4dd5-45aa-8b21-f3f9811b5ea2&progressLabel=f4d40dd5-049b-4ae0-9a04-18c34f35b4a5' + ) + }) +}) diff --git a/frontend/src/helpers/querySyncHelper.js b/frontend/src/helpers/querySyncHelper.js index 8a12a80090..dc641c91cc 100644 --- a/frontend/src/helpers/querySyncHelper.js +++ b/frontend/src/helpers/querySyncHelper.js @@ -1,20 +1,25 @@ /** - * - * @param queryObj{{[p: string]: string[] | string}} - * @return {string} + * Converts a query object to a query string. + * @param {Record} queryObj - The query object. + * @returns {string} The query string. */ export function getQueryAsString(queryObj) { - const params = Object.entries(queryObj).map(([key, vals]) => { - if (typeof vals === 'string') { - return `&${key}=${vals}` - } else { - let q = '' - for (const val of vals) { - q = `${q}&${key}=${val}` - } - return q - } - }) - if (params.length === 0) return '' - return '?' + params.join('').slice(1) + const queryParams = [] + + for (const [key, values] of Object.entries(queryObj)) { + if (values === null || values === undefined) continue + const normalizedValues = Array.isArray(values) ? values : [values] + + const paramStrings = normalizedValues.map( + (value) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}` + ) + + queryParams.push(...paramStrings) + } + + if (queryParams.length === 0) { + return '' + } + + return '?' + queryParams.join('&') }