Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js89
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js57
-rw-r--r--app/assets/javascripts/pages/sessions/new/oauth_remember_me.js6
-rw-r--r--app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js61
-rw-r--r--spec/javascripts/lib/utils/url_utility_spec.js82
6 files changed, 127 insertions, 172 deletions
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index ccf1d924ef2..afdca012127 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -4,6 +4,14 @@ import { getLocationHash } from './url_utility';
import { convertToCamelCase } from './text_utility';
import { isObject } from './type_utility';
+/**
+ * Simply returns `window.location`. This function exists to provide a means to spy
+ * `window.location` in unit tests.
+ *
+ * @returns {Location | string | any} The browser's `window.location`
+ */
+export const windowLocation = () => window.location;
+
export const getPagePath = (index = 0) => {
const page = $('body').attr('data-page') || '';
@@ -180,87 +188,6 @@ export const urlParamsToObject = (path = '') =>
return data;
}, {});
-/**
- * Apply the query param and value to the given url by returning a new url string that includes
- * the param/value pair. If the given url already includes the query param, the query param value
- * will be updated in the new url string. Otherwise, the query param and value will by added in
- * the new url string.
- *
- * @param url {string} - url to which the query param will be applied
- * @param param {string} - name of the query param to set
- * @param value {string|number} - value to give the query param
- * @returns {string} A copy of the original url with the new or updated query param
- */
-export const setUrlParam = (url, param, value) => {
- const [rootAndQuery, fragment] = url.split('#');
- const [root, query] = rootAndQuery.split('?');
- const encodedParam = encodeURIComponent(param);
- const encodedPair = `${encodedParam}=${encodeURIComponent(value)}`;
-
- let paramExists = false;
- const paramArray =
- (query ? query.split('&') : [])
- .map(paramPair => {
- const [foundParam] = paramPair.split('=');
- if (foundParam === encodedParam) {
- paramExists = true;
- return encodedPair;
- }
- return paramPair;
- });
-
- if (paramExists === false) {
- paramArray.push(encodedPair);
- }
-
- const writableFragment = fragment ? `#${fragment}` : '';
- return `${root}?${paramArray.join('&')}${writableFragment}`;
-};
-
-/**
- * Remove the query param from the given url by returning a new url string that no longer includes
- * the param/value pair.
- *
- * @param url {string} - url from which the query param will be removed
- * @param param {string} - the name of the query param to remove
- * @returns {string} A copy of the original url but without the query param
- */
-export const removeUrlParam = (url, param) => {
- const [rootAndQuery, fragment] = url.split('#');
- const [root, query] = rootAndQuery.split('?');
-
- if (query === undefined) {
- return url;
- }
-
- const encodedParam = encodeURIComponent(param);
- const updatedQuery = query
- .split('&')
- .filter(paramPair => {
- const [foundParam] = paramPair.split('=');
- return foundParam !== encodedParam;
- })
- .join('&');
-
- const writableQuery = updatedQuery.length > 0 ? `?${updatedQuery}` : '';
- const writableFragment = fragment ? `#${fragment}` : '';
- return `${root}${writableQuery}${writableFragment}`;
-};
-
-/**
- * Apply the fragment to the given url by returning a new url string that includes
- * the fragment. If the given url already contains a fragment, the original fragment
- * will be removed.
- *
- * @param url {string} - url to which the fragment will be applied
- * @param fragment {string} - fragment to append
- */
-export const setUrlFragment = (url, fragment) => {
- const [rootUrl] = url.split('#');
- const encodedFragment = encodeURIComponent(fragment.replace(/^#/, ''));
- return `${rootUrl}#${encodedFragment}`;
-};
-
export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
// Identify following special clicks
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 9850f7ce782..61f53a632b8 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,3 +1,5 @@
+import { windowLocation } from './common_utils';
+
// Returns an array containing the value(s) of the
// of the key passed as an argument
export function getParameterValues(sParam) {
@@ -42,22 +44,35 @@ export function mergeUrlParams(params, url) {
return `${urlparts[1]}?${query}${urlparts[3]}`;
}
-export function removeParamQueryString(url, param) {
- const decodedUrl = decodeURIComponent(url);
- const urlVariables = decodedUrl.split('&');
-
- return urlVariables.filter(variable => variable.indexOf(param) === -1).join('&');
-}
-
-export function removeParams(params, source = window.location.href) {
- const url = document.createElement('a');
- url.href = source;
+/**
+ * Removes specified query params from the url by returning a new url string that no longer
+ * includes the param/value pair. If no url is provided, `window.location.href` is used as
+ * the default value.
+ *
+ * @param {string[]} params - the query param names to remove
+ * @param {string} [url=windowLocation().href] - url from which the query param will be removed
+ * @returns {string} A copy of the original url but without the query param
+ */
+export function removeParams(params, url = windowLocation().href) {
+ const [rootAndQuery, fragment] = url.split('#');
+ const [root, query] = rootAndQuery.split('?');
+
+ if (query === undefined) {
+ return url;
+ }
- params.forEach(param => {
- url.search = removeParamQueryString(url.search, param);
- });
+ const encodedParams = params.map(param => encodeURIComponent(param));
+ const updatedQuery = query
+ .split('&')
+ .filter(paramPair => {
+ const [foundParam] = paramPair.split('=');
+ return encodedParams.indexOf(foundParam) < 0;
+ })
+ .join('&');
- return url.href;
+ const writableQuery = updatedQuery.length > 0 ? `?${updatedQuery}` : '';
+ const writableFragment = fragment ? `#${fragment}` : '';
+ return `${root}${writableQuery}${writableFragment}`;
}
export function getLocationHash(url = window.location.href) {
@@ -66,6 +81,20 @@ export function getLocationHash(url = window.location.href) {
return hashIndex === -1 ? null : url.substring(hashIndex + 1);
}
+/**
+ * Apply the fragment to the given url by returning a new url string that includes
+ * the fragment. If the given url already contains a fragment, the original fragment
+ * will be removed.
+ *
+ * @param {string} url - url to which the fragment will be applied
+ * @param {string} fragment - fragment to append
+ */
+export const setUrlFragment = (url, fragment) => {
+ const [rootUrl] = url.split('#');
+ const encodedFragment = encodeURIComponent(fragment.replace(/^#/, ''));
+ return `${rootUrl}#${encodedFragment}`;
+};
+
export function visitUrl(url, external = false) {
if (external) {
// Simulate `target="blank" rel="noopener noreferrer"`
diff --git a/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js b/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js
index 0c6ccd6e495..191221a48cd 100644
--- a/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js
+++ b/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import { setUrlParam, removeUrlParam } from '~/lib/utils/common_utils';
+import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
/**
* OAuth-based login buttons have a separate "remember me" checkbox.
@@ -25,9 +25,9 @@ export default class OAuthRememberMe {
const href = $(element).attr('href');
if (rememberMe) {
- $(element).attr('href', setUrlParam(href, 'remember_me', 1));
+ $(element).attr('href', mergeUrlParams({ remember_me: 1 }, href));
} else {
- $(element).attr('href', removeUrlParam(href, 'remember_me'));
+ $(element).attr('href', removeParams(['remember_me'], href));
}
});
}
diff --git a/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js b/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js
index 82ac59224df..71b7ca8ec31 100644
--- a/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js
+++ b/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js
@@ -1,4 +1,4 @@
-import { setUrlFragment, setUrlParam } from '../../../lib/utils/common_utils';
+import { mergeUrlParams, setUrlFragment } from '~/lib/utils/url_utility';
/**
* Ensure the given URL fragment is preserved by appending it to sign-in/sign-up form actions and
@@ -22,7 +22,7 @@ export default function preserveUrlFragment(fragment) {
// query param will be available in the omniauth callback upon successful authentication
const anchors = document.querySelectorAll('#signin-container a.oauth-login');
Array.prototype.forEach.call(anchors, (anchor) => {
- const newHref = setUrlParam(anchor.getAttribute('href'), 'redirect_fragment', normalFragment);
+ const newHref = mergeUrlParams({ redirect_fragment: normalFragment }, anchor.getAttribute('href'));
anchor.setAttribute('href', newHref);
});
}
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 3a25be766cb..f320f232687 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -65,67 +65,6 @@ describe('common_utils', () => {
});
});
- describe('setUrlParam', () => {
- it('should append param when url has no other params', () => {
- const url = commonUtils.setUrlParam('/feature/home', 'newParam', 'yes');
- expect(url).toBe('/feature/home?newParam=yes');
- });
-
- it('should append param when url has other params', () => {
- const url = commonUtils.setUrlParam('/feature/home?showAll=true', 'newParam', 'yes');
- expect(url).toBe('/feature/home?showAll=true&newParam=yes');
- });
-
- it('should replace param when url contains the param', () => {
- const url = commonUtils.setUrlParam('/feature/home?showAll=true&limit=5', 'limit', '100');
- expect(url).toBe('/feature/home?showAll=true&limit=100');
- });
-
- it('should update param and preserve fragment', () => {
- const url = commonUtils.setUrlParam('/home?q=no&limit=5&showAll=true#H1', 'limit', '100');
- expect(url).toBe('/home?q=no&limit=100&showAll=true#H1');
- });
- });
-
- describe('removeUrlParam', () => {
- it('should remove param when url has no other params', () => {
- const url = commonUtils.removeUrlParam('/feature/home?size=5', 'size');
- expect(url).toBe('/feature/home');
- });
-
- it('should remove param when url has other params', () => {
- const url = commonUtils.removeUrlParam('/feature/home?q=1&size=5&f=html', 'size');
- expect(url).toBe('/feature/home?q=1&f=html');
- });
-
- it('should remove param and preserve fragment', () => {
- const url = commonUtils.removeUrlParam('/feature/home?size=5#H2', 'size');
- expect(url).toBe('/feature/home#H2');
- });
-
- it('should not modify url if param does not exist', () => {
- const url = commonUtils.removeUrlParam('/feature/home?q=1&size=5&f=html', 'locale');
- expect(url).toBe('/feature/home?q=1&size=5&f=html');
- });
- });
-
- describe('setUrlFragment', () => {
- it('should set fragment when url has no fragment', () => {
- const url = commonUtils.setUrlFragment('/home/feature', 'usage');
- expect(url).toBe('/home/feature#usage');
- });
-
- it('should set fragment when url has existing fragment', () => {
- const url = commonUtils.setUrlFragment('/home/feature#overview', 'usage');
- expect(url).toBe('/home/feature#usage');
- });
-
- it('should set fragment when given fragment includes #', () => {
- const url = commonUtils.setUrlFragment('/home/feature#overview', '#install');
- expect(url).toBe('/home/feature#install');
- });
- });
-
describe('handleLocationHash', () => {
beforeEach(() => {
spyOn(window.document, 'getElementById').and.callThrough();
diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js
index e4df8441793..fe787baf08e 100644
--- a/spec/javascripts/lib/utils/url_utility_spec.js
+++ b/spec/javascripts/lib/utils/url_utility_spec.js
@@ -1,4 +1,4 @@
-import { webIDEUrl, mergeUrlParams } from '~/lib/utils/url_utility';
+import UrlUtility, * as urlUtils from '~/lib/utils/url_utility';
describe('URL utility', () => {
describe('webIDEUrl', () => {
@@ -8,7 +8,7 @@ describe('URL utility', () => {
describe('without relative_url_root', () => {
it('returns IDE path with route', () => {
- expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe(
+ expect(urlUtils.webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe(
'/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1',
);
});
@@ -20,7 +20,7 @@ describe('URL utility', () => {
});
it('returns IDE path with route', () => {
- expect(webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe(
+ expect(urlUtils.webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe(
'/gitlab/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1',
);
});
@@ -29,23 +29,83 @@ describe('URL utility', () => {
describe('mergeUrlParams', () => {
it('adds w', () => {
- expect(mergeUrlParams({ w: 1 }, '#frag')).toBe('?w=1#frag');
- expect(mergeUrlParams({ w: 1 }, '/path#frag')).toBe('/path?w=1#frag');
- expect(mergeUrlParams({ w: 1 }, 'https://host/path')).toBe('https://host/path?w=1');
- expect(mergeUrlParams({ w: 1 }, 'https://host/path#frag')).toBe('https://host/path?w=1#frag');
- expect(mergeUrlParams({ w: 1 }, 'https://h/p?k1=v1#frag')).toBe('https://h/p?k1=v1&w=1#frag');
+ expect(urlUtils.mergeUrlParams({ w: 1 }, '#frag')).toBe('?w=1#frag');
+ expect(urlUtils.mergeUrlParams({ w: 1 }, '/path#frag')).toBe('/path?w=1#frag');
+ expect(urlUtils.mergeUrlParams({ w: 1 }, 'https://host/path')).toBe('https://host/path?w=1');
+ expect(urlUtils.mergeUrlParams({ w: 1 }, 'https://host/path#frag')).toBe('https://host/path?w=1#frag');
+ expect(urlUtils.mergeUrlParams({ w: 1 }, 'https://h/p?k1=v1#frag')).toBe('https://h/p?k1=v1&w=1#frag');
});
it('updates w', () => {
- expect(mergeUrlParams({ w: 1 }, '?k1=v1&w=0#frag')).toBe('?k1=v1&w=1#frag');
+ expect(urlUtils.mergeUrlParams({ w: 1 }, '?k1=v1&w=0#frag')).toBe('?k1=v1&w=1#frag');
});
it('adds multiple params', () => {
- expect(mergeUrlParams({ a: 1, b: 2, c: 3 }, '#frag')).toBe('?a=1&b=2&c=3#frag');
+ expect(urlUtils.mergeUrlParams({ a: 1, b: 2, c: 3 }, '#frag')).toBe('?a=1&b=2&c=3#frag');
});
it('adds and updates encoded params', () => {
- expect(mergeUrlParams({ a: '&', q: '?' }, '?a=%23#frag')).toBe('?a=%26&q=%3F#frag');
+ expect(urlUtils.mergeUrlParams({ a: '&', q: '?' }, '?a=%23#frag')).toBe('?a=%26&q=%3F#frag');
+ });
+ });
+
+ describe('removeParams', () => {
+ describe('when url is passed', () => {
+ it('removes query param with encoded ampersand', () => {
+ const url = urlUtils.removeParams(['filter'], '/mail?filter=n%3Djoe%26l%3Dhome');
+
+ expect(url).toBe('/mail');
+ });
+
+ it('should remove param when url has no other params', () => {
+ const url = urlUtils.removeParams(['size'], '/feature/home?size=5');
+ expect(url).toBe('/feature/home');
+ });
+
+ it('should remove param when url has other params', () => {
+ const url = urlUtils.removeParams(['size'], '/feature/home?q=1&size=5&f=html');
+ expect(url).toBe('/feature/home?q=1&f=html');
+ });
+
+ it('should remove param and preserve fragment', () => {
+ const url = urlUtils.removeParams(['size'], '/feature/home?size=5#H2');
+ expect(url).toBe('/feature/home#H2');
+ });
+
+ it('should remove multiple params', () => {
+ const url = urlUtils.removeParams(['z', 'a'], '/home?z=11111&l=en_US&a=true#H2');
+ expect(url).toBe('/home?l=en_US#H2');
+ });
+ });
+
+ describe('when no url is passed', () => {
+ it('should remove params from window.location.href', () => {
+ spyOnDependency(UrlUtility, 'windowLocation').and.callFake(() => {
+ const anchor = document.createElement('a');
+ anchor.href = 'https://mysite.com/?zip=11111&locale=en_US&ads=false#privacy';
+ return anchor;
+ });
+
+ const url = urlUtils.removeParams(['locale']);
+ expect(url).toBe('https://mysite.com/?zip=11111&ads=false#privacy');
+ });
+ });
+ });
+
+ describe('setUrlFragment', () => {
+ it('should set fragment when url has no fragment', () => {
+ const url = urlUtils.setUrlFragment('/home/feature', 'usage');
+ expect(url).toBe('/home/feature#usage');
+ });
+
+ it('should set fragment when url has existing fragment', () => {
+ const url = urlUtils.setUrlFragment('/home/feature#overview', 'usage');
+ expect(url).toBe('/home/feature#usage');
+ });
+
+ it('should set fragment when given fragment includes #', () => {
+ const url = urlUtils.setUrlFragment('/home/feature#overview', '#install');
+ expect(url).toBe('/home/feature#install');
});
});
});