import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
import MockAdapter from 'axios-mock-adapter';
import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data';
import breakpointInstance from '~/breakpoints';
const PIXEL_TOLERANCE = 0.2;
/**
* Loads a data URL as the src of an
* {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image|Image}
* and resolves to that Image once loaded.
*
* @param url
* @returns {Promise}
*/
const urlToImage = url =>
new Promise(resolve => {
const img = new Image();
img.onload = function() {
resolve(img);
};
img.src = url;
});
describe('common_utils', () => {
describe('parseUrl', () => {
it('returns an anchor tag with url', () => {
expect(commonUtils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url');
});
it('url is escaped', () => {
// IE11 will return a relative pathname while other browsers will return a full pathname.
// parseUrl uses an anchor element for parsing an url. With relative urls, the anchor
// element will create an absolute url relative to the current execution context.
// The JavaScript test suite is executed at '/' which will lead to an absolute url
// starting with '/'.
expect(commonUtils.parseUrl('" test="asf"').pathname).toContain('/%22%20test=%22asf%22');
});
});
describe('parseUrlPathname', () => {
it('returns an absolute url when given an absolute url', () => {
expect(commonUtils.parseUrlPathname('/some/absolute/url')).toEqual('/some/absolute/url');
});
it('returns an absolute url when given a relative url', () => {
expect(commonUtils.parseUrlPathname('some/relative/url')).toEqual('/some/relative/url');
});
});
describe('urlParamsToArray', () => {
it('returns empty array for empty querystring', () => {
expect(commonUtils.urlParamsToArray('')).toEqual([]);
});
it('should decode params', () => {
expect(commonUtils.urlParamsToArray('?label_name%5B%5D=test')[0]).toBe('label_name[]=test');
});
it('should remove the question mark from the search params', () => {
const paramsArray = commonUtils.urlParamsToArray('?test=thing');
expect(paramsArray[0][0]).not.toBe('?');
});
});
describe('urlParamsToObject', () => {
it('parses path for label with trailing +', () => {
expect(commonUtils.urlParamsToObject('label_name[]=label%2B', {})).toEqual({
label_name: ['label+'],
});
});
it('parses path for milestone with trailing +', () => {
expect(commonUtils.urlParamsToObject('milestone_title=A%2B', {})).toEqual({
milestone_title: 'A+',
});
});
it('parses path for search terms with spaces', () => {
expect(commonUtils.urlParamsToObject('search=two+words', {})).toEqual({
search: 'two words',
});
});
});
describe('handleLocationHash', () => {
beforeEach(() => {
spyOn(window.document, 'getElementById').and.callThrough();
});
afterEach(() => {
window.history.pushState({}, null, '');
});
function expectGetElementIdToHaveBeenCalledWith(elementId) {
expect(window.document.getElementById).toHaveBeenCalledWith(elementId);
}
it('decodes hash parameter', () => {
window.history.pushState({}, null, '#random-hash');
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('random-hash');
expectGetElementIdToHaveBeenCalledWith('user-content-random-hash');
});
it('decodes cyrillic hash parameter', () => {
window.history.pushState({}, null, '#definição');
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('definição');
expectGetElementIdToHaveBeenCalledWith('user-content-definição');
});
it('decodes encoded cyrillic hash parameter', () => {
window.history.pushState({}, null, '#defini%C3%A7%C3%A3o');
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('definição');
expectGetElementIdToHaveBeenCalledWith('user-content-definição');
});
it('scrolls element into view', () => {
document.body.innerHTML += `
`;
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('test');
expect(window.scrollY).toBe(document.getElementById('test').offsetTop);
document.getElementById('parent').remove();
});
it('scrolls user content element into view', () => {
document.body.innerHTML += `
`;
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop);
document.getElementById('parent').remove();
});
it('scrolls to element with offset from navbar', () => {
spyOn(window, 'scrollBy').and.callThrough();
document.body.innerHTML += `
`;
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop - 50);
expect(window.scrollBy).toHaveBeenCalledWith(0, -50);
document.getElementById('parent').remove();
});
});
describe('historyPushState', () => {
afterEach(() => {
window.history.replaceState({}, null, null);
});
it('should call pushState with the correct path', () => {
spyOn(window.history, 'pushState');
commonUtils.historyPushState('newpath?page=2');
expect(window.history.pushState).toHaveBeenCalled();
expect(window.history.pushState.calls.allArgs()[0][2]).toContain('newpath?page=2');
});
});
describe('parseQueryStringIntoObject', () => {
it('should return object with query parameters', () => {
expect(commonUtils.parseQueryStringIntoObject('scope=all&page=2')).toEqual({
scope: 'all',
page: '2',
});
expect(commonUtils.parseQueryStringIntoObject('scope=all')).toEqual({ scope: 'all' });
expect(commonUtils.parseQueryStringIntoObject()).toEqual({});
});
});
describe('objectToQueryString', () => {
it('returns empty string when `param` is undefined, null or empty string', () => {
expect(commonUtils.objectToQueryString()).toBe('');
expect(commonUtils.objectToQueryString('')).toBe('');
});
it('returns query string with values of `params`', () => {
const singleQueryParams = { foo: true };
const multipleQueryParams = { foo: true, bar: true };
expect(commonUtils.objectToQueryString(singleQueryParams)).toBe('foo=true');
expect(commonUtils.objectToQueryString(multipleQueryParams)).toBe('foo=true&bar=true');
});
});
describe('buildUrlWithCurrentLocation', () => {
it('should build an url with current location and given parameters', () => {
expect(commonUtils.buildUrlWithCurrentLocation()).toEqual(window.location.pathname);
expect(commonUtils.buildUrlWithCurrentLocation('?page=2')).toEqual(
`${window.location.pathname}?page=2`,
);
});
});
describe('debounceByAnimationFrame', () => {
it('debounces a function to allow a maximum of one call per animation frame', done => {
const spy = jasmine.createSpy('spy');
const debouncedSpy = commonUtils.debounceByAnimationFrame(spy);
window.requestAnimationFrame(() => {
debouncedSpy();
debouncedSpy();
window.requestAnimationFrame(() => {
expect(spy).toHaveBeenCalledTimes(1);
done();
});
});
});
});
describe('getParameterByName', () => {
beforeEach(() => {
window.history.pushState({}, null, '?scope=all&p=2');
});
afterEach(() => {
window.history.replaceState({}, null, null);
});
it('should return valid parameter', () => {
const value = commonUtils.getParameterByName('scope');
expect(commonUtils.getParameterByName('p')).toEqual('2');
expect(value).toBe('all');
});
it('should return invalid parameter', () => {
const value = commonUtils.getParameterByName('fakeParameter');
expect(value).toBe(null);
});
it('should return valid paramentes if URL is provided', () => {
let value = commonUtils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar');
expect(value).toBe('bar');
value = commonUtils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu');
expect(value).toBe('canchu');
});
});
describe('normalizedHeaders', () => {
it('should upperCase all the header keys to keep them consistent', () => {
const apiHeaders = {
'X-Something-Workhorse': { workhorse: 'ok' },
'x-something-nginx': { nginx: 'ok' },
};
const normalized = commonUtils.normalizeHeaders(apiHeaders);
const WORKHORSE = 'X-SOMETHING-WORKHORSE';
const NGINX = 'X-SOMETHING-NGINX';
expect(normalized[WORKHORSE].workhorse).toBe('ok');
expect(normalized[NGINX].nginx).toBe('ok');
});
});
describe('normalizeCRLFHeaders', () => {
beforeEach(function() {
this.CLRFHeaders =
'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE';
spyOn(String.prototype, 'split').and.callThrough();
this.normalizeCRLFHeaders = commonUtils.normalizeCRLFHeaders(this.CLRFHeaders);
});
it('should split by newline', function() {
expect(String.prototype.split).toHaveBeenCalledWith('\n');
});
it('should split by colon+space for each header', function() {
expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(
3,
);
});
it('should return a normalized headers object', function() {
expect(this.normalizeCRLFHeaders).toEqual({
'A-HEADER': 'a-value',
'ANOTHER-HEADER': 'ANOTHER-VALUE',
'LAST-HEADER': 'last-VALUE',
});
});
});
describe('parseIntPagination', () => {
it('should parse to integers all string values and return pagination object', () => {
const pagination = {
'X-PER-PAGE': 10,
'X-PAGE': 2,
'X-TOTAL': 30,
'X-TOTAL-PAGES': 3,
'X-NEXT-PAGE': 3,
'X-PREV-PAGE': 1,
};
const expectedPagination = {
perPage: 10,
page: 2,
total: 30,
totalPages: 3,
nextPage: 3,
previousPage: 1,
};
expect(commonUtils.parseIntPagination(pagination)).toEqual(expectedPagination);
});
});
describe('isMetaClick', () => {
it('should identify meta click on Windows/Linux', () => {
const e = {
metaKey: false,
ctrlKey: true,
which: 1,
};
expect(commonUtils.isMetaClick(e)).toBe(true);
});
it('should identify meta click on macOS', () => {
const e = {
metaKey: true,
ctrlKey: false,
which: 1,
};
expect(commonUtils.isMetaClick(e)).toBe(true);
});
it('should identify as meta click on middle-click or Mouse-wheel click', () => {
const e = {
metaKey: false,
ctrlKey: false,
which: 2,
};
expect(commonUtils.isMetaClick(e)).toBe(true);
});
});
describe('contentTop', () => {
it('does not add height for fileTitle or compareVersionsHeader if screen is too small', () => {
spyOn(breakpointInstance, 'isDesktop').and.returnValue(false);
setFixtures(`
blah blah blah
more blah blah blah
`);
expect(commonUtils.contentTop()).toBe(0);
});
it('adds height for fileTitle and compareVersionsHeader screen is large enough', () => {
spyOn(breakpointInstance, 'isDesktop').and.returnValue(true);
setFixtures(`