summaryrefslogtreecommitdiff
path: root/spec/frontend/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/lib')
-rw-r--r--spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js2
-rw-r--r--spec/frontend/lib/utils/apollo_startup_js_link_spec.js2
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js9
-rw-r--r--spec/frontend/lib/utils/confirm_via_gl_modal/confirm_modal_spec.js18
-rw-r--r--spec/frontend/lib/utils/number_utility_spec.js16
-rw-r--r--spec/frontend/lib/utils/table_utility_spec.js31
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js74
-rw-r--r--spec/frontend/lib/utils/vuex_module_mappers_spec.js10
-rw-r--r--spec/frontend/lib/utils/yaml_spec.js105
9 files changed, 255 insertions, 12 deletions
diff --git a/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js b/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js
index 7b604724977..971ba8b583c 100644
--- a/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js
+++ b/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js
@@ -1,4 +1,4 @@
-import { ApolloLink, Observable } from 'apollo-link';
+import { ApolloLink, Observable } from '@apollo/client/core';
import waitForPromises from 'helpers/wait_for_promises';
import { getSuppressNetworkErrorsDuringNavigationLink } from '~/lib/apollo/suppress_network_errors_during_navigation_link';
import { isNavigatingAway } from '~/lib/utils/is_navigating_away';
diff --git a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
index c0e5b06651f..e58bc063004 100644
--- a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
+++ b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
@@ -1,4 +1,4 @@
-import { ApolloLink, Observable } from 'apollo-link';
+import { ApolloLink, Observable } from '@apollo/client/core';
import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link';
describe('StartupJSLink', () => {
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index 3e2ba918d9b..3fea08d5512 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -394,8 +394,7 @@ describe('common_utils', () => {
describe('backOff', () => {
beforeEach(() => {
- // shortcut our timeouts otherwise these tests will take a long time to finish
- jest.spyOn(window, 'setTimeout').mockImplementation((cb) => setImmediate(cb, 0));
+ jest.spyOn(window, 'setTimeout');
});
it('solves the promise from the callback', (done) => {
@@ -446,6 +445,7 @@ describe('common_utils', () => {
if (numberOfCalls < 3) {
numberOfCalls += 1;
next();
+ jest.runOnlyPendingTimers();
} else {
stop(resp);
}
@@ -464,7 +464,10 @@ describe('common_utils', () => {
it('rejects the backOff promise after timing out', (done) => {
commonUtils
- .backOff((next) => next(), 64000)
+ .backOff((next) => {
+ next();
+ jest.runOnlyPendingTimers();
+ }, 64000)
.catch((errBackoffResp) => {
const timeouts = window.setTimeout.mock.calls.map(([, timeout]) => timeout);
diff --git a/spec/frontend/lib/utils/confirm_via_gl_modal/confirm_modal_spec.js b/spec/frontend/lib/utils/confirm_via_gl_modal/confirm_modal_spec.js
index d19f9352bbc..e06d1384610 100644
--- a/spec/frontend/lib/utils/confirm_via_gl_modal/confirm_modal_spec.js
+++ b/spec/frontend/lib/utils/confirm_via_gl_modal/confirm_modal_spec.js
@@ -6,11 +6,13 @@ describe('Confirm Modal', () => {
let wrapper;
let modal;
- const createComponent = ({ primaryText, primaryVariant } = {}) => {
+ const createComponent = ({ primaryText, primaryVariant, title, hideCancel = false } = {}) => {
wrapper = mount(ConfirmModal, {
propsData: {
primaryText,
primaryVariant,
+ hideCancel,
+ title,
},
});
};
@@ -55,5 +57,19 @@ describe('Confirm Modal', () => {
expect(customProps.text).toBe('OK');
expect(customProps.attributes.variant).toBe('confirm');
});
+
+ it('should hide the cancel button if `hideCancel` is set', () => {
+ createComponent({ hideCancel: true });
+ const props = findGlModal().props();
+
+ expect(props.actionCancel).toBeNull();
+ });
+
+ it('should set the modal title when the `title` prop is set', () => {
+ const title = 'Modal title';
+ createComponent({ title });
+
+ expect(findGlModal().props().title).toBe(title);
+ });
});
});
diff --git a/spec/frontend/lib/utils/number_utility_spec.js b/spec/frontend/lib/utils/number_utility_spec.js
index e743678ea90..dc4aa0ea5ed 100644
--- a/spec/frontend/lib/utils/number_utility_spec.js
+++ b/spec/frontend/lib/utils/number_utility_spec.js
@@ -4,6 +4,7 @@ import {
bytesToMiB,
bytesToGiB,
numberToHumanSize,
+ numberToMetricPrefix,
sum,
isOdd,
median,
@@ -99,6 +100,21 @@ describe('Number Utils', () => {
});
});
+ describe('numberToMetricPrefix', () => {
+ it.each`
+ number | expected
+ ${123} | ${'123'}
+ ${1234} | ${'1.2k'}
+ ${12345} | ${'12.3k'}
+ ${123456} | ${'123.5k'}
+ ${1234567} | ${'1.2m'}
+ ${12345678} | ${'12.3m'}
+ ${123456789} | ${'123.5m'}
+ `('returns $expected given $number', ({ number, expected }) => {
+ expect(numberToMetricPrefix(number)).toBe(expected);
+ });
+ });
+
describe('sum', () => {
it('should add up two values', () => {
expect(sum(1, 2)).toEqual(3);
diff --git a/spec/frontend/lib/utils/table_utility_spec.js b/spec/frontend/lib/utils/table_utility_spec.js
index 75b9252aa40..0ceccbe4c74 100644
--- a/spec/frontend/lib/utils/table_utility_spec.js
+++ b/spec/frontend/lib/utils/table_utility_spec.js
@@ -8,4 +8,35 @@ describe('table_utility', () => {
expect(tableUtils.thWidthClass(width)).toBe(`gl-w-${width}p ${DEFAULT_TH_CLASSES}`);
});
});
+
+ describe('sortObjectToString', () => {
+ it('returns the expected sorting string ending in "DESC" when sortDesc is true', () => {
+ expect(tableUtils.sortObjectToString({ sortBy: 'mergedAt', sortDesc: true })).toBe(
+ 'MERGED_AT_DESC',
+ );
+ });
+
+ it('returns the expected sorting string ending in "ASC" when sortDesc is false', () => {
+ expect(tableUtils.sortObjectToString({ sortBy: 'mergedAt', sortDesc: false })).toBe(
+ 'MERGED_AT_ASC',
+ );
+ });
+ });
+
+ describe('sortStringToObject', () => {
+ it.each`
+ sortBy | sortDesc | sortString
+ ${'mergedAt'} | ${true} | ${'MERGED_AT_DESC'}
+ ${'mergedAt'} | ${false} | ${'MERGED_AT_ASC'}
+ ${'severity'} | ${true} | ${'SEVERITY_DESC'}
+ ${'severity'} | ${false} | ${'SEVERITY_ASC'}
+ ${null} | ${null} | ${'SEVERITY'}
+ ${null} | ${null} | ${''}
+ `(
+ 'returns the expected sort object when the sort string is "$sortString"',
+ ({ sortBy, sortDesc, sortString }) => {
+ expect(tableUtils.sortStringToObject(sortString)).toStrictEqual({ sortBy, sortDesc });
+ },
+ );
+ });
});
diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js
index ab81ec47b64..dded32cc890 100644
--- a/spec/frontend/lib/utils/text_markdown_spec.js
+++ b/spec/frontend/lib/utils/text_markdown_spec.js
@@ -165,6 +165,80 @@ describe('init markdown', () => {
// cursor placement should be between tags
expect(textArea.selectionStart).toBe(start.length + tag.length);
});
+
+ describe('Continuing markdown lists', () => {
+ const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' });
+
+ beforeEach(() => {
+ gon.features = { markdownContinueLists: true };
+ });
+
+ it.each`
+ text | expected
+ ${'- item'} | ${'- item\n- '}
+ ${'- [ ] item'} | ${'- [ ] item\n- [ ] '}
+ ${'- [x] item'} | ${'- [x] item\n- [x] '}
+ ${'- item\n - second'} | ${'- item\n - second\n - '}
+ ${'1. item'} | ${'1. item\n1. '}
+ ${'1. [ ] item'} | ${'1. [ ] item\n1. [ ] '}
+ ${'1. [x] item'} | ${'1. [x] item\n1. [x] '}
+ ${'108. item'} | ${'108. item\n108. '}
+ ${'108. item\n - second'} | ${'108. item\n - second\n - '}
+ ${'108. item\n 1. second'} | ${'108. item\n 1. second\n 1. '}
+ `('adds correct list continuation characters', ({ text, expected }) => {
+ textArea.value = text;
+ textArea.setSelectionRange(text.length, text.length);
+
+ textArea.addEventListener('keydown', keypressNoteText);
+ textArea.dispatchEvent(enterEvent);
+
+ expect(textArea.value).toEqual(expected);
+ expect(textArea.selectionStart).toBe(expected.length);
+ });
+
+ // test that when pressing Enter on an empty list item, the empty
+ // list item text is selected, so that when the Enter propagates,
+ // it's removed
+ it.each`
+ text | expected
+ ${'- item\n- '} | ${'- item\n'}
+ ${'- [ ] item\n- [ ] '} | ${'- [ ] item\n'}
+ ${'- [x] item\n- [x] '} | ${'- [x] item\n'}
+ ${'- item\n - second\n - '} | ${'- item\n - second\n'}
+ ${'1. item\n1. '} | ${'1. item\n'}
+ ${'1. [ ] item\n1. [ ] '} | ${'1. [ ] item\n'}
+ ${'1. [x] item\n1. [x] '} | ${'1. [x] item\n'}
+ ${'108. item\n108. '} | ${'108. item\n'}
+ ${'108. item\n - second\n - '} | ${'108. item\n - second\n'}
+ ${'108. item\n 1. second\n 1. '} | ${'108. item\n 1. second\n'}
+ `('adds correct list continuation characters', ({ text, expected }) => {
+ 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);
+ expect(textArea.selectionStart).toBe(expected.length);
+ expect(textArea.selectionEnd).toBe(text.length);
+ });
+
+ it('does nothing if feature flag disabled', () => {
+ gon.features = { markdownContinueLists: false };
+
+ const text = '- item';
+ const expected = '- item';
+
+ textArea.value = text;
+ textArea.setSelectionRange(text.length, text.length);
+
+ textArea.addEventListener('keydown', keypressNoteText);
+ textArea.dispatchEvent(enterEvent);
+
+ expect(textArea.value).toEqual(expected);
+ expect(textArea.selectionStart).toBe(expected.length);
+ });
+ });
});
describe('with selection', () => {
diff --git a/spec/frontend/lib/utils/vuex_module_mappers_spec.js b/spec/frontend/lib/utils/vuex_module_mappers_spec.js
index d7e51e4daca..1821a15f677 100644
--- a/spec/frontend/lib/utils/vuex_module_mappers_spec.js
+++ b/spec/frontend/lib/utils/vuex_module_mappers_spec.js
@@ -1,4 +1,4 @@
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import {
@@ -10,13 +10,12 @@ import {
const TEST_MODULE_NAME = 'testModuleName';
-const localVue = createLocalVue();
-localVue.use(Vuex);
+Vue.use(Vuex);
// setup test component and store ----------------------------------------------
//
// These are used to indirectly test `vuex_module_mappers`.
-const TestComponent = Vue.extend({
+const TestComponent = {
props: {
vuexModule: {
type: String,
@@ -47,7 +46,7 @@ const TestComponent = Vue.extend({
<pre data-testid="state">{{ stateJson }}</pre>
<pre data-testid="getters">{{ gettersJson }}</pre>
</div>`,
-});
+};
const createTestStore = () => {
return new Vuex.Store({
@@ -94,7 +93,6 @@ describe('~/lib/utils/vuex_module_mappers', () => {
vuexModule: TEST_MODULE_NAME,
},
store,
- localVue,
});
});
diff --git a/spec/frontend/lib/utils/yaml_spec.js b/spec/frontend/lib/utils/yaml_spec.js
new file mode 100644
index 00000000000..d1ce00130e2
--- /dev/null
+++ b/spec/frontend/lib/utils/yaml_spec.js
@@ -0,0 +1,105 @@
+import { Document, parseDocument } from 'yaml';
+import { merge } from '~/lib/utils/yaml';
+
+// Mock data for Comments on pairs
+const COMMENTS_ON_PAIRS_SOURCE = `foo:
+ # barbaz
+ bar: baz
+
+ # bazboo
+ baz: boo
+`;
+
+const COMMENTS_ON_PAIRS_TARGET = `foo:
+ # abcdef
+ abc: def
+ # boobaz
+ boo: baz
+`;
+
+const COMMENTS_ON_PAIRS_EXPECTED = `foo:
+ # abcdef
+ abc: def
+ # boobaz
+ boo: baz
+ # barbaz
+ bar: baz
+
+ # bazboo
+ baz: boo
+`;
+
+// Mock data for Comments on seqs
+const COMMENTS_ON_SEQS_SOURCE = `foo:
+ # barbaz
+ - barbaz
+ # bazboo
+ - baz: boo
+`;
+
+const COMMENTS_ON_SEQS_TARGET = `foo:
+ # abcdef
+ - abcdef
+
+ # boobaz
+ - boobaz
+`;
+
+const COMMENTS_ON_SEQS_EXPECTED = `foo:
+ # abcdef
+ - abcdef
+
+ # boobaz
+ - boobaz
+ # barbaz
+ - barbaz
+ # bazboo
+ - baz: boo
+`;
+
+describe('Yaml utility functions', () => {
+ describe('merge', () => {
+ const getAsNode = (yamlStr) => {
+ return parseDocument(yamlStr).contents;
+ };
+
+ describe('Merge two Nodes', () => {
+ it.each`
+ scenario | source | target | options | expected
+ ${'merge a map'} | ${getAsNode('foo:\n bar: baz\n')} | ${'foo:\n abc: def\n'} | ${undefined} | ${'foo:\n abc: def\n bar: baz\n'}
+ ${'merge a seq'} | ${getAsNode('foo:\n - bar\n')} | ${'foo:\n - abc\n'} | ${undefined} | ${'foo:\n - bar\n'}
+ ${'merge-append seqs'} | ${getAsNode('foo:\n - bar\n')} | ${'foo:\n - abc\n'} | ${{ onSequence: 'append' }} | ${'foo:\n - abc\n - bar\n'}
+ ${'merge-replace a seq'} | ${getAsNode('foo:\n - bar\n')} | ${'foo:\n - abc\n'} | ${{ onSequence: 'replace' }} | ${'foo:\n - bar\n'}
+ ${'override existing paths'} | ${getAsNode('foo:\n bar: baz\n')} | ${'foo:\n bar: boo\n'} | ${undefined} | ${'foo:\n bar: baz\n'}
+ ${'deep maps'} | ${getAsNode('foo:\n bar:\n abc: def\n')} | ${'foo:\n bar:\n baz: boo\n jkl: mno\n'} | ${undefined} | ${'foo:\n bar:\n baz: boo\n abc: def\n jkl: mno\n'}
+ ${'append maps inside seqs'} | ${getAsNode('foo:\n - abc: def\n')} | ${'foo:\n - bar: baz\n'} | ${{ onSequence: 'append' }} | ${'foo:\n - bar: baz\n - abc: def\n'}
+ ${'inexistent paths create new nodes'} | ${getAsNode('foo:\n bar: baz\n')} | ${'abc: def\n'} | ${undefined} | ${'abc: def\nfoo:\n bar: baz\n'}
+ ${'document as source'} | ${parseDocument('foo:\n bar: baz\n')} | ${'foo:\n abc: def\n'} | ${undefined} | ${'foo:\n abc: def\n bar: baz\n'}
+ ${'object as source'} | ${{ foo: { bar: 'baz' } }} | ${'foo:\n abc: def\n'} | ${undefined} | ${'foo:\n abc: def\n bar: baz\n'}
+ ${'comments on pairs'} | ${parseDocument(COMMENTS_ON_PAIRS_SOURCE)} | ${COMMENTS_ON_PAIRS_TARGET} | ${undefined} | ${COMMENTS_ON_PAIRS_EXPECTED}
+ ${'comments on seqs'} | ${parseDocument(COMMENTS_ON_SEQS_SOURCE)} | ${COMMENTS_ON_SEQS_TARGET} | ${{ onSequence: 'append' }} | ${COMMENTS_ON_SEQS_EXPECTED}
+ `('$scenario', ({ source, target, expected, options }) => {
+ const targetDoc = parseDocument(target);
+ merge(targetDoc, source, options);
+ const expectedDoc = parseDocument(expected);
+ expect(targetDoc.toString()).toEqual(expectedDoc.toString());
+ });
+
+ it('type conflict will throw an Error', () => {
+ const sourceDoc = parseDocument('foo:\n bar:\n - baz\n');
+ const targetDoc = parseDocument('foo:\n bar: def\n');
+ expect(() => merge(targetDoc, sourceDoc)).toThrow(
+ 'Type conflict at "foo.bar": Destination node is of type Scalar, the node' +
+ ' to be merged is of type YAMLSeq',
+ );
+ });
+
+ it('merging a collection into an empty doc', () => {
+ const targetDoc = new Document();
+ merge(targetDoc, { foo: { bar: 'baz' } });
+ const expected = parseDocument('foo:\n bar: baz\n');
+ expect(targetDoc.toString()).toEqual(expected.toString());
+ });
+ });
+ });
+});