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:
Diffstat (limited to 'spec/frontend/lib/utils')
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js69
-rw-r--r--spec/frontend/lib/utils/datetime_utility_spec.js4
-rw-r--r--spec/frontend/lib/utils/dom_utils_spec.js29
-rw-r--r--spec/frontend/lib/utils/file_upload_spec.js7
-rw-r--r--spec/frontend/lib/utils/mock_data.js42
-rw-r--r--spec/frontend/lib/utils/navigation_utility_spec.js5
-rw-r--r--spec/frontend/lib/utils/resize_observer_spec.js4
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js33
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js53
-rw-r--r--spec/frontend/lib/utils/users_cache_spec.js25
10 files changed, 211 insertions, 60 deletions
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index 763a9bd30fe..8e499844406 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -283,6 +283,75 @@ describe('common_utils', () => {
});
});
+ 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('');
+ });
+ });
+ });
+
describe('normalizedHeaders', () => {
it('should upperCase all the header keys to keep them consistent', () => {
const apiHeaders = {
diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js
index 7a64b654baa..8d989350173 100644
--- a/spec/frontend/lib/utils/datetime_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime_utility_spec.js
@@ -308,7 +308,9 @@ describe('datefix', () => {
});
describe('parsePikadayDate', () => {
- // removed because of https://gitlab.com/gitlab-org/gitlab-foss/issues/39834
+ it('should return a UTC date', () => {
+ expect(datetimeUtility.parsePikadayDate('2020-01-29')).toEqual(new Date(2020, 0, 29));
+ });
});
describe('pikadayToString', () => {
diff --git a/spec/frontend/lib/utils/dom_utils_spec.js b/spec/frontend/lib/utils/dom_utils_spec.js
index 2f240f25d2a..88dac449527 100644
--- a/spec/frontend/lib/utils/dom_utils_spec.js
+++ b/spec/frontend/lib/utils/dom_utils_spec.js
@@ -1,3 +1,4 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import {
addClassIfElementExists,
canScrollUp,
@@ -6,6 +7,7 @@ import {
isElementVisible,
isElementHidden,
getParents,
+ getParentByTagName,
setAttributes,
} from '~/lib/utils/dom_utils';
@@ -23,10 +25,14 @@ describe('DOM Utils', () => {
let parentElement;
beforeEach(() => {
- setFixtures(fixture);
+ setHTMLFixture(fixture);
parentElement = document.querySelector('.parent');
});
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
it('adds class if element exists', () => {
const childElement = parentElement.querySelector('.child');
@@ -126,10 +132,14 @@ describe('DOM Utils', () => {
let element;
beforeEach(() => {
- setFixtures('<div data-foo-bar data-baz data-qux="">');
+ setHTMLFixture('<div data-foo-bar data-baz data-qux="">');
element = document.querySelector('[data-foo-bar]');
});
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
it('throws if not given an element', () => {
expect(() => parseBooleanDataAttributes(null, ['baz'])).toThrow();
});
@@ -210,6 +220,21 @@ describe('DOM Utils', () => {
});
});
+ describe('getParentByTagName', () => {
+ const el = document.createElement('div');
+ el.innerHTML = '<p><span><strong><mark>hello world';
+
+ it.each`
+ tagName | parent
+ ${'strong'} | ${el.querySelector('strong')}
+ ${'span'} | ${el.querySelector('span')}
+ ${'p'} | ${el.querySelector('p')}
+ ${'pre'} | ${undefined}
+ `('gets a parent by tag name', ({ tagName, parent }) => {
+ expect(getParentByTagName(el.querySelector('mark'), tagName)).toBe(parent);
+ });
+ });
+
describe('setAttributes', () => {
it('sets multiple attribues on element', () => {
const div = document.createElement('div');
diff --git a/spec/frontend/lib/utils/file_upload_spec.js b/spec/frontend/lib/utils/file_upload_spec.js
index ff11107ea60..f63af2fe0a4 100644
--- a/spec/frontend/lib/utils/file_upload_spec.js
+++ b/spec/frontend/lib/utils/file_upload_spec.js
@@ -1,8 +1,9 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import fileUpload, { getFilename, validateImageName } from '~/lib/utils/file_upload';
describe('File upload', () => {
beforeEach(() => {
- setFixtures(`
+ setHTMLFixture(`
<form>
<button class="js-button" type="button">Click me!</button>
<input type="text" class="js-input" />
@@ -11,6 +12,10 @@ describe('File upload', () => {
`);
});
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
describe('when there is a matching button and input', () => {
beforeEach(() => {
fileUpload('.js-button', '.js-input');
diff --git a/spec/frontend/lib/utils/mock_data.js b/spec/frontend/lib/utils/mock_data.js
index df1f79529e7..49a2af8b307 100644
--- a/spec/frontend/lib/utils/mock_data.js
+++ b/spec/frontend/lib/utils/mock_data.js
@@ -3,3 +3,45 @@ export const faviconDataUrl =
export const overlayDataUrl =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC';
+
+const absoluteUrls = [
+ 'http://example.org',
+ 'http://example.org:8080',
+ 'https://example.org',
+ 'https://example.org:8080',
+ 'https://192.168.1.1',
+];
+
+const rootRelativeUrls = ['/relative/link'];
+
+const relativeUrls = ['./relative/link', '../relative/link'];
+
+const urlsWithoutHost = ['http://', 'https://', 'https:https:https:'];
+
+/* eslint-disable no-script-url */
+const nonHttpUrls = [
+ 'javascript:',
+ 'javascript:alert("XSS")',
+ 'jav\tascript:alert("XSS");',
+ ' &#14; javascript:alert("XSS");',
+ 'ftp://192.168.1.1',
+ 'file:///',
+ 'file:///etc/hosts',
+];
+/* eslint-enable no-script-url */
+
+// javascript:alert('XSS')
+const encodedJavaScriptUrls = [
+ '&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041',
+ '&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;',
+ '&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29',
+ '\\u006A\\u0061\\u0076\\u0061\\u0073\\u0063\\u0072\\u0069\\u0070\\u0074\\u003A\\u0061\\u006C\\u0065\\u0072\\u0074\\u0028\\u0027\\u0058\\u0053\\u0053\\u0027\\u0029',
+];
+
+export const safeUrls = [...absoluteUrls, ...rootRelativeUrls];
+export const unsafeUrls = [
+ ...relativeUrls,
+ ...urlsWithoutHost,
+ ...nonHttpUrls,
+ ...encodedJavaScriptUrls,
+];
diff --git a/spec/frontend/lib/utils/navigation_utility_spec.js b/spec/frontend/lib/utils/navigation_utility_spec.js
index 6a880a0f354..632a8904578 100644
--- a/spec/frontend/lib/utils/navigation_utility_spec.js
+++ b/spec/frontend/lib/utils/navigation_utility_spec.js
@@ -1,3 +1,4 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import findAndFollowLink from '~/lib/utils/navigation_utility';
import * as navigationUtils from '~/lib/utils/navigation_utility';
import { visitUrl } from '~/lib/utils/url_utility';
@@ -8,11 +9,13 @@ describe('findAndFollowLink', () => {
it('visits a link when the selector exists', () => {
const href = '/some/path';
- setFixtures(`<a class="my-shortcut" href="${href}">link</a>`);
+ setHTMLFixture(`<a class="my-shortcut" href="${href}">link</a>`);
findAndFollowLink('.my-shortcut');
expect(visitUrl).toHaveBeenCalledWith(href);
+
+ resetHTMLFixture();
});
it('does not throw an exception when the selector does not exist', () => {
diff --git a/spec/frontend/lib/utils/resize_observer_spec.js b/spec/frontend/lib/utils/resize_observer_spec.js
index 6560562f204..c88ba73ebc6 100644
--- a/spec/frontend/lib/utils/resize_observer_spec.js
+++ b/spec/frontend/lib/utils/resize_observer_spec.js
@@ -1,3 +1,4 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { contentTop } from '~/lib/utils/common_utils';
import { scrollToTargetOnResize } from '~/lib/utils/resize_observer';
@@ -19,7 +20,7 @@ describe('ResizeObserver Utility', () => {
jest.spyOn(document.documentElement, 'scrollTo');
- setFixtures(`<div id="content-body"><div id="note_1234">note to scroll to</div></div>`);
+ setHTMLFixture(`<div id="content-body"><div id="note_1234">note to scroll to</div></div>`);
const target = document.querySelector('#note_1234');
@@ -28,6 +29,7 @@ describe('ResizeObserver Utility', () => {
afterEach(() => {
contentTop.mockReset();
+ resetHTMLFixture();
});
describe('Observer behavior', () => {
diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js
index 103305f0797..d1bca3c73b6 100644
--- a/spec/frontend/lib/utils/text_markdown_spec.js
+++ b/spec/frontend/lib/utils/text_markdown_spec.js
@@ -1,5 +1,10 @@
import $ from 'jquery';
-import { insertMarkdownText, keypressNoteText } from '~/lib/utils/text_markdown';
+import {
+ insertMarkdownText,
+ keypressNoteText,
+ compositionStartNoteText,
+ compositionEndNoteText,
+} from '~/lib/utils/text_markdown';
import '~/lib/utils/jquery_at_who';
describe('init markdown', () => {
@@ -9,6 +14,9 @@ describe('init markdown', () => {
textArea = document.createElement('textarea');
document.querySelector('body').appendChild(textArea);
textArea.focus();
+
+ // needed for the underlying insertText to work
+ document.execCommand = jest.fn(() => false);
});
afterAll(() => {
@@ -172,7 +180,9 @@ describe('init markdown', () => {
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' });
beforeEach(() => {
- gon.features = { markdownContinueLists: true };
+ textArea.addEventListener('keydown', keypressNoteText);
+ textArea.addEventListener('compositionstart', compositionStartNoteText);
+ textArea.addEventListener('compositionend', compositionEndNoteText);
});
it.each`
@@ -203,7 +213,6 @@ describe('init markdown', () => {
textArea.value = text;
textArea.setSelectionRange(text.length, text.length);
- textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(enterEvent);
expect(textArea.value).toEqual(expected);
@@ -231,7 +240,6 @@ describe('init markdown', () => {
textArea.value = text;
textArea.setSelectionRange(text.length, text.length);
- textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(enterEvent);
expect(textArea.value.substr(0, textArea.selectionStart)).toEqual(expected);
@@ -251,7 +259,6 @@ describe('init markdown', () => {
textArea.value = text;
textArea.setSelectionRange(text.length, text.length);
- textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(enterEvent);
expect(textArea.value).toEqual(expected);
@@ -267,23 +274,25 @@ describe('init markdown', () => {
textArea.value = text;
textArea.setSelectionRange(add_at, add_at);
- textArea.addEventListener('keydown', keypressNoteText);
textArea.dispatchEvent(enterEvent);
expect(textArea.value).toEqual(expected);
},
);
- it('does nothing if feature flag disabled', () => {
- gon.features = { markdownContinueLists: false };
-
- const text = '- item';
- const expected = '- item';
+ it('does not duplicate a line item for IME characters', () => {
+ const text = '- 日本語';
+ const expected = '- 日本語\n- ';
+ textArea.dispatchEvent(new CompositionEvent('compositionstart'));
textArea.value = text;
+
+ // Press enter to end composition
+ textArea.dispatchEvent(enterEvent);
+ textArea.dispatchEvent(new CompositionEvent('compositionend'));
textArea.setSelectionRange(text.length, text.length);
- textArea.addEventListener('keydown', keypressNoteText);
+ // Press enter to make new line
textArea.dispatchEvent(enterEvent);
expect(textArea.value).toEqual(expected);
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 7608cff4c9e..81cf4bd293b 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -1,6 +1,7 @@
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
import * as urlUtils from '~/lib/utils/url_utility';
+import { safeUrls, unsafeUrls } from './mock_data';
const shas = {
valid: [
@@ -575,48 +576,6 @@ describe('URL utility', () => {
});
describe('isSafeUrl', () => {
- const absoluteUrls = [
- 'http://example.org',
- 'http://example.org:8080',
- 'https://example.org',
- 'https://example.org:8080',
- 'https://192.168.1.1',
- ];
-
- const rootRelativeUrls = ['/relative/link'];
-
- const relativeUrls = ['./relative/link', '../relative/link'];
-
- const urlsWithoutHost = ['http://', 'https://', 'https:https:https:'];
-
- /* eslint-disable no-script-url */
- const nonHttpUrls = [
- 'javascript:',
- 'javascript:alert("XSS")',
- 'jav\tascript:alert("XSS");',
- ' &#14; javascript:alert("XSS");',
- 'ftp://192.168.1.1',
- 'file:///',
- 'file:///etc/hosts',
- ];
- /* eslint-enable no-script-url */
-
- // javascript:alert('XSS')
- const encodedJavaScriptUrls = [
- '&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041',
- '&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;',
- '&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29',
- '\\u006A\\u0061\\u0076\\u0061\\u0073\\u0063\\u0072\\u0069\\u0070\\u0074\\u003A\\u0061\\u006C\\u0065\\u0072\\u0074\\u0028\\u0027\\u0058\\u0053\\u0053\\u0027\\u0029',
- ];
-
- const safeUrls = [...absoluteUrls, ...rootRelativeUrls];
- const unsafeUrls = [
- ...relativeUrls,
- ...urlsWithoutHost,
- ...nonHttpUrls,
- ...encodedJavaScriptUrls,
- ];
-
describe('with URL constructor support', () => {
it.each(safeUrls)('returns true for %s', (url) => {
expect(urlUtils.isSafeURL(url)).toBe(true);
@@ -628,6 +587,16 @@ describe('URL utility', () => {
});
});
+ describe('sanitizeUrl', () => {
+ it.each(safeUrls)('returns the url for %s', (url) => {
+ expect(urlUtils.sanitizeUrl(url)).toBe(url);
+ });
+
+ it.each(unsafeUrls)('returns `about:blank` for %s', (url) => {
+ expect(urlUtils.sanitizeUrl(url)).toBe('about:blank');
+ });
+ });
+
describe('getNormalizedURL', () => {
it.each`
url | base | result
diff --git a/spec/frontend/lib/utils/users_cache_spec.js b/spec/frontend/lib/utils/users_cache_spec.js
index 30bdddd8e73..d35ba20f570 100644
--- a/spec/frontend/lib/utils/users_cache_spec.js
+++ b/spec/frontend/lib/utils/users_cache_spec.js
@@ -228,4 +228,29 @@ describe('UsersCache', () => {
expect(userStatus).toBe(dummyUserStatus);
});
});
+
+ describe('updateById', () => {
+ describe('when the user is not cached', () => {
+ it('does nothing and returns undefined', () => {
+ expect(UsersCache.updateById(dummyUserId, { name: 'root' })).toBe(undefined);
+ expect(UsersCache.internalStorage).toStrictEqual({});
+ });
+ });
+
+ describe('when the user is cached', () => {
+ const updatedName = 'has two farms';
+ beforeEach(() => {
+ UsersCache.internalStorage[dummyUserId] = dummyUser;
+ });
+
+ it('updates the user only with the new data', async () => {
+ UsersCache.updateById(dummyUserId, { name: updatedName });
+
+ expect(await UsersCache.retrieveById(dummyUserId)).toStrictEqual({
+ username: dummyUser.username,
+ name: updatedName,
+ });
+ });
+ });
+ });
});