debian-mirror-gitlab/spec/frontend/lib/utils/common_utils_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1105 lines
36 KiB
JavaScript
Raw Normal View History

2020-10-24 23:57:45 +05:30
import * as commonUtils from '~/lib/utils/common_utils';
2023-03-04 22:38:38 +05:30
import setWindowLocation from 'helpers/set_window_location_helper';
2019-03-02 22:35:43 +05:30
2018-03-17 18:26:18 +05:30
describe('common_utils', () => {
2021-09-30 23:02:18 +05:30
describe('getPagePath', () => {
const { getPagePath } = commonUtils;
let originalBody;
beforeEach(() => {
originalBody = document.body;
document.body = document.createElement('body');
});
afterEach(() => {
document.body = originalBody;
});
it('returns an empty path if none is defined', () => {
expect(getPagePath()).toBe('');
expect(getPagePath(0)).toBe('');
});
describe('returns a path', () => {
const mockSection = 'my_section';
const mockSubSection = 'my_sub_section';
const mockPage = 'my_page';
it('returns a page', () => {
document.body.dataset.page = mockPage;
expect(getPagePath()).toBe(mockPage);
expect(getPagePath(0)).toBe(mockPage);
});
it('returns a section and page', () => {
document.body.dataset.page = `${mockSection}:${mockPage}`;
expect(getPagePath()).toBe(mockSection);
expect(getPagePath(0)).toBe(mockSection);
expect(getPagePath(1)).toBe(mockPage);
});
it('returns a section and subsection', () => {
document.body.dataset.page = `${mockSection}:${mockSubSection}:${mockPage}`;
expect(getPagePath()).toBe(mockSection);
expect(getPagePath(0)).toBe(mockSection);
expect(getPagePath(1)).toBe(mockSubSection);
expect(getPagePath(2)).toBe(mockPage);
});
});
});
2018-03-17 18:26:18 +05:30
describe('handleLocationHash', () => {
beforeEach(() => {
2020-04-08 14:13:33 +05:30
jest.spyOn(window.document, 'getElementById');
2018-03-17 18:26:18 +05:30
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
afterEach(() => {
window.history.pushState({}, null, '');
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
function expectGetElementIdToHaveBeenCalledWith(elementId) {
expect(window.document.getElementById).toHaveBeenCalledWith(elementId);
}
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it('decodes hash parameter', () => {
window.history.pushState({}, null, '#random-hash');
commonUtils.handleLocationHash();
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expectGetElementIdToHaveBeenCalledWith('random-hash');
expectGetElementIdToHaveBeenCalledWith('user-content-random-hash');
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it('decodes cyrillic hash parameter', () => {
window.history.pushState({}, null, '#definição');
commonUtils.handleLocationHash();
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expectGetElementIdToHaveBeenCalledWith('definição');
expectGetElementIdToHaveBeenCalledWith('user-content-definição');
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it('decodes encoded cyrillic hash parameter', () => {
window.history.pushState({}, null, '#defini%C3%A7%C3%A3o');
commonUtils.handleLocationHash();
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expectGetElementIdToHaveBeenCalledWith('definição');
expectGetElementIdToHaveBeenCalledWith('user-content-definição');
2017-08-17 22:00:37 +05:30
});
2022-08-13 15:12:31 +05:30
it(`does not scroll when ${commonUtils.NO_SCROLL_TO_HASH_CLASS} is set on target`, () => {
jest.spyOn(window, 'scrollBy');
document.body.innerHTML += `
<div id="parent">
<a href="#test">Link</a>
<div style="height: 2000px;"></div>
<div id="test" style="height: 2000px;" class="${commonUtils.NO_SCROLL_TO_HASH_CLASS}"></div>
</div>
`;
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
jest.runOnlyPendingTimers();
try {
expect(window.scrollBy).not.toHaveBeenCalled();
} finally {
document.getElementById('parent').remove();
}
});
2018-03-17 18:26:18 +05:30
it('scrolls element into view', () => {
document.body.innerHTML += `
<div id="parent">
<div style="height: 2000px;"></div>
<div id="test" style="height: 2000px;"></div>
</div>
`;
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expectGetElementIdToHaveBeenCalledWith('test');
2018-12-13 13:39:08 +05:30
2018-03-17 18:26:18 +05:30
expect(window.scrollY).toBe(document.getElementById('test').offsetTop);
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
document.getElementById('parent').remove();
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it('scrolls user content element into view', () => {
document.body.innerHTML += `
<div id="parent">
<div style="height: 2000px;"></div>
<div id="user-content-test" style="height: 2000px;"></div>
</div>
`;
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
2018-12-13 13:39:08 +05:30
2018-03-17 18:26:18 +05:30
expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop);
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
document.getElementById('parent').remove();
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it('scrolls to element with offset from navbar', () => {
2020-04-08 14:13:33 +05:30
jest.spyOn(window, 'scrollBy');
2018-03-17 18:26:18 +05:30
document.body.innerHTML += `
<div id="parent">
<div class="navbar-gitlab" style="position: fixed; top: 0; height: 50px;"></div>
<div style="height: 2000px; margin-top: 50px;"></div>
<div id="user-content-test" style="height: 2000px;"></div>
</div>
`;
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
2020-04-08 14:13:33 +05:30
jest.advanceTimersByTime(1);
2018-03-17 18:26:18 +05:30
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
2018-12-13 13:39:08 +05:30
2018-03-17 18:26:18 +05:30
expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop - 50);
expect(window.scrollBy).toHaveBeenCalledWith(0, -50);
document.getElementById('parent').remove();
});
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
describe('historyPushState', () => {
afterEach(() => {
window.history.replaceState({}, null, null);
2017-08-17 22:00:37 +05:30
});
2018-03-17 18:26:18 +05:30
it('should call pushState with the correct path', () => {
2020-04-08 14:13:33 +05:30
jest.spyOn(window.history, 'pushState').mockImplementation(() => {});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
commonUtils.historyPushState('newpath?page=2');
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expect(window.history.pushState).toHaveBeenCalled();
2020-04-08 14:13:33 +05:30
expect(window.history.pushState.mock.calls[0][2]).toContain('newpath?page=2');
2018-03-17 18:26:18 +05:30
});
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
describe('buildUrlWithCurrentLocation', () => {
it('should build an url with current location and given parameters', () => {
expect(commonUtils.buildUrlWithCurrentLocation()).toEqual(window.location.pathname);
2018-12-13 13:39:08 +05:30
expect(commonUtils.buildUrlWithCurrentLocation('?page=2')).toEqual(
`${window.location.pathname}?page=2`,
);
2018-03-17 18:26:18 +05:30
});
});
2017-09-10 17:25:29 +05:30
2020-06-23 00:09:42 +05:30
describe('scrollToElement*', () => {
let elem;
2021-03-08 18:12:59 +05:30
const windowHeight = 550;
2020-06-23 00:09:42 +05:30
const elemTop = 100;
2021-03-08 18:12:59 +05:30
const id = 'scroll_test';
2020-06-23 00:09:42 +05:30
beforeEach(() => {
elem = document.createElement('div');
2021-03-08 18:12:59 +05:30
elem.id = id;
document.body.appendChild(elem);
2020-06-23 00:09:42 +05:30
window.innerHeight = windowHeight;
2021-02-22 17:27:13 +05:30
window.mrTabs = { currentAction: 'show' };
2021-03-08 18:12:59 +05:30
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
jest.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({ top: elemTop });
2020-06-23 00:09:42 +05:30
});
afterEach(() => {
2021-03-08 18:12:59 +05:30
window.scrollTo.mockRestore();
Element.prototype.getBoundingClientRect.mockRestore();
elem.remove();
2020-06-23 00:09:42 +05:30
});
2021-03-08 18:12:59 +05:30
describe('scrollToElement with HTMLElement', () => {
2020-06-23 00:09:42 +05:30
it('scrolls to element', () => {
commonUtils.scrollToElement(elem);
2021-03-08 18:12:59 +05:30
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elemTop,
});
2020-06-23 00:09:42 +05:30
});
it('scrolls to element with offset', () => {
const offset = 50;
commonUtils.scrollToElement(elem, { offset });
2021-03-08 18:12:59 +05:30
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elemTop + offset,
});
});
});
describe('scrollToElement with Selector', () => {
it('scrolls to element', () => {
commonUtils.scrollToElement(`#${id}`);
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elemTop,
});
});
it('scrolls to element with offset', () => {
const offset = 50;
commonUtils.scrollToElement(`#${id}`, { offset });
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elemTop + offset,
});
2020-06-23 00:09:42 +05:30
});
});
describe('scrollToElementWithContext', () => {
2021-03-08 18:12:59 +05:30
// This is what the implementation of scrollToElementWithContext
// scrolls to, in case we change tha implementation
// it needs to be adjusted
const elementTopWithContext = elemTop - windowHeight * 0.1;
it('with HTMLElement scrolls with context', () => {
commonUtils.scrollToElementWithContext(elem);
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elementTopWithContext,
});
});
it('with Selector scrolls with context', () => {
commonUtils.scrollToElementWithContext(`#${id}`);
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elementTopWithContext,
});
2020-06-23 00:09:42 +05:30
});
2021-12-11 22:18:48 +05:30
it('passes through behaviour', () => {
commonUtils.scrollToElementWithContext(`#${id}`, { behavior: 'smooth' });
expect(window.scrollTo).toHaveBeenCalledWith({
behavior: 'smooth',
top: elementTopWithContext,
});
});
2020-06-23 00:09:42 +05:30
});
});
2019-03-02 22:35:43 +05:30
describe('debounceByAnimationFrame', () => {
2022-06-21 17:19:12 +05:30
it('debounces a function to allow a maximum of one call per animation frame', () => {
2020-04-08 14:13:33 +05:30
const spy = jest.fn();
2019-03-02 22:35:43 +05:30
const debouncedSpy = commonUtils.debounceByAnimationFrame(spy);
2022-06-21 17:19:12 +05:30
2022-08-27 11:52:29 +05:30
debouncedSpy();
debouncedSpy();
jest.runOnlyPendingTimers();
expect(spy).toHaveBeenCalledTimes(1);
2019-03-02 22:35:43 +05:30
});
2022-07-16 23:28:13 +05:30
});
describe('insertText', () => {
let textArea;
beforeAll(() => {
textArea = document.createElement('textarea');
document.querySelector('body').appendChild(textArea);
textArea.value = 'two';
textArea.setSelectionRange(0, 0);
textArea.focus();
});
afterAll(() => {
textArea.parentNode.removeChild(textArea);
});
describe('using execCommand', () => {
beforeAll(() => {
document.execCommand = jest.fn(() => true);
});
it('inserts the text', () => {
commonUtils.insertText(textArea, 'one');
expect(document.execCommand).toHaveBeenCalledWith('insertText', false, 'one');
});
it('removes selected text', () => {
textArea.setSelectionRange(0, textArea.value.length);
commonUtils.insertText(textArea, '');
expect(document.execCommand).toHaveBeenCalledWith('delete');
});
});
describe('using fallback', () => {
beforeEach(() => {
document.execCommand = jest.fn(() => false);
jest.spyOn(textArea, 'dispatchEvent');
textArea.value = 'two';
textArea.setSelectionRange(0, 0);
});
it('inserts the text', () => {
commonUtils.insertText(textArea, 'one');
expect(textArea.value).toBe('onetwo');
expect(textArea.dispatchEvent).toHaveBeenCalled();
});
it('replaces the selection', () => {
textArea.setSelectionRange(0, textArea.value.length);
commonUtils.insertText(textArea, 'one');
expect(textArea.value).toBe('one');
expect(textArea.selectionStart).toBe(textArea.value.length);
});
it('removes selected text', () => {
textArea.setSelectionRange(0, textArea.value.length);
commonUtils.insertText(textArea, '');
expect(textArea.value).toBe('');
});
});
2019-03-02 22:35:43 +05:30
});
2018-03-17 18:26:18 +05:30
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' },
};
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
const normalized = commonUtils.normalizeHeaders(apiHeaders);
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
const WORKHORSE = 'X-SOMETHING-WORKHORSE';
const NGINX = 'X-SOMETHING-NGINX';
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expect(normalized[WORKHORSE].workhorse).toBe('ok');
expect(normalized[NGINX].nginx).toBe('ok');
});
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
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);
});
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
describe('isMetaClick', () => {
it('should identify meta click on Windows/Linux', () => {
const e = {
metaKey: false,
ctrlKey: true,
which: 1,
};
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expect(commonUtils.isMetaClick(e)).toBe(true);
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it('should identify meta click on macOS', () => {
const e = {
metaKey: true,
ctrlKey: false,
which: 1,
};
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
expect(commonUtils.isMetaClick(e)).toBe(true);
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
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);
});
});
2019-02-15 15:39:39 +05:30
describe('parseBoolean', () => {
2020-04-22 19:07:51 +05:30
it.each`
input | expected
${'true'} | ${true}
${'false'} | ${false}
${'something'} | ${false}
${null} | ${false}
${true} | ${true}
${false} | ${false}
`('returns $expected for $input', ({ input, expected }) => {
expect(commonUtils.parseBoolean(input)).toBe(expected);
2019-02-15 15:39:39 +05:30
});
});
2018-03-17 18:26:18 +05:30
describe('backOff', () => {
beforeEach(() => {
2022-04-04 11:22:00 +05:30
jest.spyOn(window, 'setTimeout');
2018-03-17 18:26:18 +05:30
});
2022-06-21 17:19:12 +05:30
it('solves the promise from the callback', () => {
2018-03-17 18:26:18 +05:30
const expectedResponseValue = 'Success!';
2022-06-21 17:19:12 +05:30
return commonUtils
2018-12-13 13:39:08 +05:30
.backOff((next, stop) =>
2021-03-08 18:12:59 +05:30
new Promise((resolve) => {
2018-12-13 13:39:08 +05:30
resolve(expectedResponseValue);
2022-06-21 17:19:12 +05:30
}).then((resp) => {
stop(resp);
}),
2018-12-13 13:39:08 +05:30
)
2021-03-08 18:12:59 +05:30
.then((respBackoff) => {
2018-12-13 13:39:08 +05:30
expect(respBackoff).toBe(expectedResponseValue);
2022-06-21 17:19:12 +05:30
});
2018-03-17 18:26:18 +05:30
});
2017-08-17 22:00:37 +05:30
2022-10-11 01:57:18 +05:30
it('catches the rejected promise from the callback', () => {
2018-03-17 18:26:18 +05:30
const errorMessage = 'Mistakes were made!';
2022-06-21 17:19:12 +05:30
return commonUtils
2018-12-13 13:39:08 +05:30
.backOff((next, stop) => {
new Promise((resolve, reject) => {
reject(new Error(errorMessage));
})
2021-03-08 18:12:59 +05:30
.then((resp) => {
2018-12-13 13:39:08 +05:30
stop(resp);
})
2021-03-08 18:12:59 +05:30
.catch((err) => stop(err));
2018-12-13 13:39:08 +05:30
})
2021-03-08 18:12:59 +05:30
.catch((errBackoffResp) => {
2018-12-13 13:39:08 +05:30
expect(errBackoffResp instanceof Error).toBe(true);
expect(errBackoffResp.message).toBe(errorMessage);
});
2018-03-17 18:26:18 +05:30
});
2017-08-17 22:00:37 +05:30
2022-06-21 17:19:12 +05:30
it('solves the promise correctly after retrying a third time', () => {
2018-03-17 18:26:18 +05:30
let numberOfCalls = 1;
const expectedResponseValue = 'Success!';
2022-06-21 17:19:12 +05:30
return commonUtils
2018-12-13 13:39:08 +05:30
.backOff((next, stop) =>
2022-06-21 17:19:12 +05:30
Promise.resolve(expectedResponseValue).then((resp) => {
if (numberOfCalls < 3) {
numberOfCalls += 1;
next();
jest.runOnlyPendingTimers();
} else {
stop(resp);
}
}),
2018-12-13 13:39:08 +05:30
)
2021-03-08 18:12:59 +05:30
.then((respBackoff) => {
2020-04-08 14:13:33 +05:30
const timeouts = window.setTimeout.mock.calls.map(([, timeout]) => timeout);
2018-12-13 13:39:08 +05:30
expect(timeouts).toEqual([2000, 4000]);
expect(respBackoff).toBe(expectedResponseValue);
2022-06-21 17:19:12 +05:30
});
2018-12-13 13:39:08 +05:30
});
2022-06-21 17:19:12 +05:30
it('rejects the backOff promise after timing out', () => {
return commonUtils
2022-04-04 11:22:00 +05:30
.backOff((next) => {
next();
jest.runOnlyPendingTimers();
}, 64000)
2021-03-08 18:12:59 +05:30
.catch((errBackoffResp) => {
2020-04-08 14:13:33 +05:30
const timeouts = window.setTimeout.mock.calls.map(([, timeout]) => timeout);
2018-12-13 13:39:08 +05:30
2019-02-15 15:39:39 +05:30
expect(timeouts).toEqual([2000, 4000, 8000, 16000, 32000, 32000]);
expect(errBackoffResp instanceof Error).toBe(true);
expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
});
2018-03-17 18:26:18 +05:30
});
});
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
describe('spriteIcon', () => {
let beforeGon;
beforeEach(() => {
window.gon = window.gon || {};
2020-05-24 23:13:21 +05:30
beforeGon = { ...window.gon };
2018-03-17 18:26:18 +05:30
window.gon.sprite_icons = 'icons.svg';
2017-08-17 22:00:37 +05:30
});
2018-03-17 18:26:18 +05:30
afterEach(() => {
window.gon = beforeGon;
});
it('should return the svg for a linked icon', () => {
2018-12-13 13:39:08 +05:30
expect(commonUtils.spriteIcon('test')).toEqual(
'<svg ><use xlink:href="icons.svg#test" /></svg>',
);
2018-03-17 18:26:18 +05:30
});
it('should set svg className when passed', () => {
2021-02-22 17:27:13 +05:30
expect(commonUtils.spriteIcon('test', 'first-icon-class second-icon-class')).toEqual(
'<svg class="first-icon-class second-icon-class"><use xlink:href="icons.svg#test" /></svg>',
2018-12-13 13:39:08 +05:30
);
2018-03-17 18:26:18 +05:30
});
});
2017-08-17 22:00:37 +05:30
2020-04-08 14:13:33 +05:30
describe('convertObjectProps*', () => {
2021-03-08 18:12:59 +05:30
const mockConversionFunction = (prop) => `${prop}_converted`;
const isEmptyObject = (obj) =>
2020-04-08 14:13:33 +05:30
typeof obj === 'object' && obj !== null && Object.keys(obj).length === 0;
const mockObjects = {
convertObjectProps: {
obj: {
id: 1,
group_name: 'GitLab.org',
absolute_web_url: 'https://gitlab.com/gitlab-org/',
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
objNested: {
project_name: 'GitLab CE',
group_name: 'GitLab.org',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
},
convertObjectPropsToCamelCase: {
obj: {
id: 1,
group_name: 'GitLab.org',
absolute_web_url: 'https://gitlab.com/gitlab-org/',
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
objNested: {
project_name: 'GitLab CE',
group_name: 'GitLab.org',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
},
convertObjectPropsToSnakeCase: {
obj: {
id: 1,
groupName: 'GitLab.org',
absoluteWebUrl: 'https://gitlab.com/gitlab-org/',
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
objNested: {
projectName: 'GitLab CE',
groupName: 'GitLab.org',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontendFramework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
},
};
2018-03-17 18:26:18 +05:30
2020-04-08 14:13:33 +05:30
describe('convertObjectProps', () => {
it('returns an empty object if `conversionFunction` parameter is not a function', () => {
const result = commonUtils.convertObjectProps(null, mockObjects.convertObjectProps.obj);
2018-03-17 18:26:18 +05:30
2022-08-27 11:52:29 +05:30
expect(isEmptyObject(result)).toBe(true);
2017-08-17 22:00:37 +05:30
});
});
2018-03-17 18:26:18 +05:30
2020-04-08 14:13:33 +05:30
describe.each`
functionName | mockObj | mockObjNested
${'convertObjectProps'} | ${mockObjects.convertObjectProps.obj} | ${mockObjects.convertObjectProps.objNested}
${'convertObjectPropsToCamelCase'} | ${mockObjects.convertObjectPropsToCamelCase.obj} | ${mockObjects.convertObjectPropsToCamelCase.objNested}
${'convertObjectPropsToSnakeCase'} | ${mockObjects.convertObjectPropsToSnakeCase.obj} | ${mockObjects.convertObjectPropsToSnakeCase.objNested}
`('$functionName', ({ functionName, mockObj, mockObjNested }) => {
const testFunction =
functionName === 'convertObjectProps'
? (obj, options = {}) =>
commonUtils.convertObjectProps(mockConversionFunction, obj, options)
: commonUtils[functionName];
it('returns an empty object if `obj` parameter is null, undefined or an empty object', () => {
2022-08-27 11:52:29 +05:30
expect(isEmptyObject(testFunction(null))).toBe(true);
expect(isEmptyObject(testFunction())).toBe(true);
expect(isEmptyObject(testFunction({}))).toBe(true);
2018-11-08 19:23:39 +05:30
});
2020-04-08 14:13:33 +05:30
it('converts object properties', () => {
const expected = {
convertObjectProps: {
id_converted: 1,
group_name_converted: 'GitLab.org',
absolute_web_url_converted: 'https://gitlab.com/gitlab-org/',
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToCamelCase: {
id: 1,
groupName: 'GitLab.org',
absoluteWebUrl: 'https://gitlab.com/gitlab-org/',
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToSnakeCase: {
id: 1,
group_name: 'GitLab.org',
absolute_web_url: 'https://gitlab.com/gitlab-org/',
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
2020-01-01 13:55:28 +05:30
};
2020-04-08 14:13:33 +05:30
expect(testFunction(mockObj)).toEqual(expected[functionName]);
2020-01-01 13:55:28 +05:30
});
2020-04-08 14:13:33 +05:30
it('does not deep-convert by default', () => {
const expected = {
convertObjectProps: {
project_name_converted: 'GitLab CE',
group_name_converted: 'GitLab.org',
license_type_converted: 'MIT',
tech_stack_converted: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToCamelCase: {
projectName: 'GitLab CE',
groupName: 'GitLab.org',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToSnakeCase: {
project_name: 'GitLab CE',
group_name: 'GitLab.org',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontendFramework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
};
2018-11-08 19:23:39 +05:30
2020-04-08 14:13:33 +05:30
expect(testFunction(mockObjNested)).toEqual(expected[functionName]);
});
2018-11-08 19:23:39 +05:30
2020-04-08 14:13:33 +05:30
describe('with options', () => {
describe('when options.deep is true', () => {
const expected = {
convertObjectProps: {
project_name_converted: 'GitLab CE',
group_name_converted: 'GitLab.org',
license_type_converted: 'MIT',
tech_stack_converted: {
backend_converted: 'Ruby',
frontend_framework_converted: 'Vue',
database_converted: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToCamelCase: {
projectName: 'GitLab CE',
groupName: 'GitLab.org',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontendFramework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToSnakeCase: {
project_name: 'GitLab CE',
group_name: 'GitLab.org',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2019-03-02 22:35:43 +05:30
},
};
2018-11-08 19:23:39 +05:30
2020-04-08 14:13:33 +05:30
it('converts nested objects', () => {
expect(testFunction(mockObjNested, { deep: true })).toEqual(expected[functionName]);
2019-03-02 22:35:43 +05:30
});
2020-04-08 14:13:33 +05:30
it('converts array of nested objects', () => {
expect(testFunction([mockObjNested], { deep: true })).toEqual([expected[functionName]]);
});
2018-11-08 19:23:39 +05:30
2020-04-08 14:13:33 +05:30
it('converts array with child arrays', () => {
expect(testFunction([[mockObjNested]], { deep: true })).toEqual([
[expected[functionName]],
]);
});
2019-03-02 22:35:43 +05:30
});
2020-04-08 14:13:33 +05:30
describe('when options.dropKeys is provided', () => {
it('discards properties mentioned in `dropKeys` array', () => {
const expected = {
convertObjectProps: {
project_name_converted: 'GitLab CE',
license_type_converted: 'MIT',
tech_stack_converted: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2019-03-02 22:35:43 +05:30
},
2020-04-08 14:13:33 +05:30
convertObjectPropsToCamelCase: {
projectName: 'GitLab CE',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2019-03-02 22:35:43 +05:30
},
2020-04-08 14:13:33 +05:30
convertObjectPropsToSnakeCase: {
project_name: 'GitLab CE',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontendFramework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
};
const dropKeys = {
convertObjectProps: ['group_name'],
convertObjectPropsToCamelCase: ['group_name'],
convertObjectPropsToSnakeCase: ['groupName'],
};
expect(
testFunction(mockObjNested, {
dropKeys: dropKeys[functionName],
}),
).toEqual(expected[functionName]);
2019-03-02 22:35:43 +05:30
});
2020-04-08 14:13:33 +05:30
it('discards properties mentioned in `dropKeys` array when `deep` is true', () => {
const expected = {
convertObjectProps: {
project_name_converted: 'GitLab CE',
license_type_converted: 'MIT',
tech_stack_converted: {
backend_converted: 'Ruby',
frontend_framework_converted: 'Vue',
},
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToCamelCase: {
projectName: 'GitLab CE',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontendFramework: 'Vue',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToSnakeCase: {
project_name: 'GitLab CE',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontend_framework: 'Vue',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
};
const dropKeys = {
convertObjectProps: ['group_name', 'database'],
convertObjectPropsToCamelCase: ['group_name', 'database'],
convertObjectPropsToSnakeCase: ['groupName', 'database'],
};
expect(
testFunction(mockObjNested, {
dropKeys: dropKeys[functionName],
deep: true,
}),
).toEqual(expected[functionName]);
2019-03-02 22:35:43 +05:30
});
});
2020-04-08 14:13:33 +05:30
describe('when options.ignoreKeyNames is provided', () => {
it('leaves properties mentioned in `ignoreKeyNames` array intact', () => {
const expected = {
convertObjectProps: {
project_name_converted: 'GitLab CE',
group_name: 'GitLab.org',
license_type_converted: 'MIT',
tech_stack_converted: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToCamelCase: {
projectName: 'GitLab CE',
group_name: 'GitLab.org',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToSnakeCase: {
project_name: 'GitLab CE',
groupName: 'GitLab.org',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontendFramework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
};
const ignoreKeyNames = {
convertObjectProps: ['group_name'],
convertObjectPropsToCamelCase: ['group_name'],
convertObjectPropsToSnakeCase: ['groupName'],
};
expect(
testFunction(mockObjNested, {
ignoreKeyNames: ignoreKeyNames[functionName],
}),
).toEqual(expected[functionName]);
2019-03-02 22:35:43 +05:30
});
2020-04-08 14:13:33 +05:30
it('leaves properties mentioned in `ignoreKeyNames` array intact when `deep` is true', () => {
const expected = {
convertObjectProps: {
project_name_converted: 'GitLab CE',
group_name: 'GitLab.org',
license_type_converted: 'MIT',
tech_stack_converted: {
backend_converted: 'Ruby',
frontend_framework: 'Vue',
database_converted: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones_converted: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToCamelCase: {
projectName: 'GitLab CE',
group_name: 'GitLab.org',
licenseType: 'MIT',
techStack: {
backend: 'Ruby',
frontend_framework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
convertObjectPropsToSnakeCase: {
project_name: 'GitLab CE',
groupName: 'GitLab.org',
license_type: 'MIT',
tech_stack: {
backend: 'Ruby',
frontendFramework: 'Vue',
database: 'PostgreSQL',
},
2020-06-23 00:09:42 +05:30
milestones: ['12.3', '12.4'],
2020-04-08 14:13:33 +05:30
},
};
const ignoreKeyNames = {
convertObjectProps: ['group_name', 'frontend_framework'],
convertObjectPropsToCamelCase: ['group_name', 'frontend_framework'],
convertObjectPropsToSnakeCase: ['groupName', 'frontendFramework'],
};
expect(
testFunction(mockObjNested, {
deep: true,
ignoreKeyNames: ignoreKeyNames[functionName],
}),
).toEqual(expected[functionName]);
2019-03-02 22:35:43 +05:30
});
});
2018-11-08 19:23:39 +05:30
});
});
2017-08-17 22:00:37 +05:30
});
2018-11-18 11:00:15 +05:30
describe('roundOffFloat', () => {
it('Rounds off decimal places of a float number with provided precision', () => {
2019-09-04 21:01:54 +05:30
expect(commonUtils.roundOffFloat(3.141592, 3)).toBeCloseTo(3.142);
2018-11-18 11:00:15 +05:30
});
it('Rounds off a float number to a whole number when provided precision is zero', () => {
2019-09-04 21:01:54 +05:30
expect(commonUtils.roundOffFloat(3.141592, 0)).toBeCloseTo(3);
expect(commonUtils.roundOffFloat(3.5, 0)).toBeCloseTo(4);
2018-11-18 11:00:15 +05:30
});
it('Rounds off float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => {
2019-09-04 21:01:54 +05:30
expect(commonUtils.roundOffFloat(34567.14159, -1)).toBeCloseTo(34570);
expect(commonUtils.roundOffFloat(34567.14159, -2)).toBeCloseTo(34600);
expect(commonUtils.roundOffFloat(34567.14159, -3)).toBeCloseTo(35000);
expect(commonUtils.roundOffFloat(34567.14159, -4)).toBeCloseTo(30000);
expect(commonUtils.roundOffFloat(34567.14159, -5)).toBeCloseTo(0);
2021-01-29 00:20:46 +05:30
});
});
describe('roundDownFloat', () => {
it('Rounds down decimal places of a float number with provided precision', () => {
expect(commonUtils.roundDownFloat(3.141592, 3)).toBe(3.141);
});
it('Rounds down a float number to a whole number when provided precision is zero', () => {
expect(commonUtils.roundDownFloat(3.141592, 0)).toBe(3);
expect(commonUtils.roundDownFloat(3.9, 0)).toBe(3);
});
it('Rounds down float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => {
expect(commonUtils.roundDownFloat(34567.14159, -1)).toBeCloseTo(34560);
expect(commonUtils.roundDownFloat(34567.14159, -2)).toBeCloseTo(34500);
expect(commonUtils.roundDownFloat(34567.14159, -3)).toBeCloseTo(34000);
expect(commonUtils.roundDownFloat(34567.14159, -4)).toBeCloseTo(30000);
expect(commonUtils.roundDownFloat(34567.14159, -5)).toBeCloseTo(0);
2018-11-18 11:00:15 +05:30
});
});
2019-03-02 22:35:43 +05:30
2021-04-29 21:17:54 +05:30
describe('roundToNearestHalf', () => {
it('Rounds decimals ot the nearest half', () => {
expect(commonUtils.roundToNearestHalf(3.141592)).toBe(3);
expect(commonUtils.roundToNearestHalf(3.41592)).toBe(3.5);
expect(commonUtils.roundToNearestHalf(1.27)).toBe(1.5);
expect(commonUtils.roundToNearestHalf(1.23)).toBe(1);
expect(commonUtils.roundToNearestHalf(1.778)).toBe(2);
});
});
2019-07-31 22:56:46 +05:30
describe('isScopedLabel', () => {
it('returns true when `::` is present in title', () => {
expect(commonUtils.isScopedLabel({ title: 'foo::bar' })).toBe(true);
});
it('returns false when `::` is not present', () => {
expect(commonUtils.isScopedLabel({ title: 'foobar' })).toBe(false);
});
});
2019-12-21 20:55:43 +05:30
2021-12-11 22:18:48 +05:30
describe('scopedLabelKey', () => {
it.each`
label | expectedLabelKey
${undefined} | ${''}
${''} | ${''}
${'title'} | ${'title'}
${'scoped::value'} | ${'scoped'}
${'scoped::label::value'} | ${'scoped::label'}
${'scoped::label-some::value'} | ${'scoped::label-some'}
${'scoped::label::some::value'} | ${'scoped::label::some'}
`('returns "$expectedLabelKey" when label is "$label"', ({ label, expectedLabelKey }) => {
expect(commonUtils.scopedLabelKey({ title: label })).toBe(expectedLabelKey);
});
});
2019-12-21 20:55:43 +05:30
describe('getDashPath', () => {
it('returns the path following /-/', () => {
expect(commonUtils.getDashPath('/some/-/url-with-dashes-/')).toEqual('url-with-dashes-/');
});
it('returns null when no path follows /-/', () => {
expect(commonUtils.getDashPath('/some/url')).toEqual(null);
});
});
2021-03-11 19:13:27 +05:30
describe('convertArrayToCamelCase', () => {
it('returns a new array with snake_case string elements converted camelCase', () => {
const result = commonUtils.convertArrayToCamelCase(['hello', 'hello_world']);
expect(result).toEqual(['hello', 'helloWorld']);
});
});
2022-01-26 12:08:38 +05:30
describe('convertArrayOfObjectsToCamelCase', () => {
it('returns a new array with snake_case object property names converted camelCase', () => {
const result = commonUtils.convertArrayOfObjectsToCamelCase([
{ hello: '' },
{ hello_world: '' },
]);
expect(result).toEqual([{ hello: '' }, { helloWorld: '' }]);
});
});
2023-03-04 22:38:38 +05:30
describe('useNewFonts', () => {
let beforeGon;
const beforeLocation = window.location.href;
beforeEach(() => {
window.gon = window.gon || {};
beforeGon = { ...window.gon };
});
describe.each`
featureFlag | queryParameter | fontEnabled
${false} | ${false} | ${false}
${true} | ${false} | ${true}
${false} | ${true} | ${true}
`('new font', ({ featureFlag, queryParameter, fontEnabled }) => {
it(`will ${fontEnabled ? '' : 'NOT '}be applied when feature flag is ${
featureFlag ? '' : 'NOT '
}set and query parameter is ${queryParameter ? '' : 'NOT '}present`, () => {
const search = queryParameter ? `?new_fonts` : '';
setWindowLocation(search);
window.gon = { features: { newFonts: featureFlag } };
expect(commonUtils.useNewFonts()).toBe(fontEnabled);
});
});
afterEach(() => {
window.gon = beforeGon;
setWindowLocation(beforeLocation);
});
});
2018-03-17 18:26:18 +05:30
});