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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 16:18:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 16:18:24 +0300
commit0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch)
tree4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /spec/frontend/vue_shared/components
parent744144d28e3e7fddc117924fef88de5d9674fe4c (diff)
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/components')
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap4
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap4
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap1
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap4
-rw-r--r--spec/frontend/vue_shared/components/commit_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js176
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js22
-rw-r--r--spec/frontend/vue_shared/components/header_ci_component_spec.js32
-rw-r--r--spec/frontend/vue_shared/components/issuable/issuable_header_warnings_spec.js74
-rw-r--r--spec/frontend/vue_shared/components/markdown/field_spec.js13
-rw-r--r--spec/frontend/vue_shared/components/registry/title_area_spec.js11
-rw-r--r--spec/frontend/vue_shared/components/runner_aws_deployments/runner_aws_deployments_modal_spec.js1
-rw-r--r--spec/frontend/vue_shared/components/settings/__snapshots__/settings_block_spec.js.snap23
-rw-r--r--spec/frontend/vue_shared/components/settings/settings_block_spec.js54
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js18
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js12
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_button_spec.js91
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js15
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js79
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js152
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_title_spec.js61
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js48
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js189
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js8
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js85
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/getters_spec.js59
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js115
-rw-r--r--spec/frontend/vue_shared/components/storage_counter/usage_graph_spec.js137
-rw-r--r--spec/frontend/vue_shared/components/user_popover/user_popover_spec.js17
32 files changed, 680 insertions, 847 deletions
diff --git a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
index bab928318ce..c7758b0faef 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
@@ -3,9 +3,13 @@
exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
<gl-dropdown-stub
category="primary"
+ clearalltext="Clear all"
headertext=""
hideheaderborder="true"
+ highlighteditemstitle="Selected"
+ highlighteditemstitleclass="gl-px-5"
right="true"
+ showhighlighteditemstitle="true"
size="medium"
text="Clone"
variant="info"
diff --git a/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap
index db174346729..7f655d67ae8 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap
@@ -2,7 +2,7 @@
exports[`Code Block with default props renders correctly 1`] = `
<pre
- class="code-block rounded"
+ class="code-block rounded code"
>
<code
class="d-block"
@@ -14,7 +14,7 @@ exports[`Code Block with default props renders correctly 1`] = `
exports[`Code Block with maxHeight set to "200px" renders correctly 1`] = `
<pre
- class="code-block rounded"
+ class="code-block rounded code"
style="max-height: 200px; overflow-y: auto;"
>
<code
diff --git a/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
index f4f9cc288f9..87eaabf4e98 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
@@ -9,7 +9,6 @@ exports[`MemoryGraph Render chart should draw container with chart 1`] = `
data="Nov 12 2019 19:17:33,2.87,Nov 12 2019 19:18:33,2.78,Nov 12 2019 19:19:33,2.78,Nov 12 2019 19:20:33,3.01"
height="25"
tooltiplabel="MB"
- variant="gray900"
/>
</div>
`;
diff --git a/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap
index c4f351eb58d..f2ff12b2acd 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap
@@ -3,9 +3,13 @@
exports[`SplitButton renders actionItems 1`] = `
<gl-dropdown-stub
category="primary"
+ clearalltext="Clear all"
headertext=""
hideheaderborder="true"
+ highlighteditemstitle="Selected"
+ highlighteditemstitleclass="gl-px-5"
menu-class=""
+ showhighlighteditemstitle="true"
size="medium"
split="true"
text="professor"
diff --git a/spec/frontend/vue_shared/components/commit_spec.js b/spec/frontend/vue_shared/components/commit_spec.js
index 6a31742141b..d91853e7b79 100644
--- a/spec/frontend/vue_shared/components/commit_spec.js
+++ b/spec/frontend/vue_shared/components/commit_spec.js
@@ -162,8 +162,6 @@ describe('Commit component', () => {
expect(refEl.attributes('href')).toBe(props.commitRef.ref_url);
- expect(refEl.attributes('title')).toBe(props.commitRef.name);
-
expect(findIcon('branch').exists()).toBe(true);
});
});
@@ -195,8 +193,6 @@ describe('Commit component', () => {
expect(refEl.attributes('href')).toBe(props.mergeRequestRef.path);
- expect(refEl.attributes('title')).toBe(props.mergeRequestRef.title);
-
expect(findIcon('git-merge').exists()).toBe(true);
});
});
diff --git a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js
new file mode 100644
index 00000000000..04f63b4bd45
--- /dev/null
+++ b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js
@@ -0,0 +1,176 @@
+import {
+ GlSprintf,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import DiffStatsDropdown, { i18n } from '~/vue_shared/components/diff_stats_dropdown.vue';
+
+jest.mock('fuzzaldrin-plus', () => ({
+ filter: jest.fn().mockReturnValue([]),
+}));
+
+const mockFiles = [
+ {
+ added: 0,
+ href: '#a5cc2925ca8258af241be7e5b0381edf30266302',
+ icon: 'file-modified',
+ iconColor: '',
+ name: '',
+ path: '.gitignore',
+ removed: 3,
+ title: '.gitignore',
+ },
+ {
+ added: 1,
+ href: '#fa288d1472d29beccb489a676f68739ad365fc47',
+ icon: 'file-modified',
+ iconColor: 'danger',
+ name: 'package-lock.json',
+ path: 'lock/file/path',
+ removed: 1,
+ },
+];
+
+describe('Diff Stats Dropdown', () => {
+ let wrapper;
+
+ const createComponent = ({ changed = 0, added = 0, deleted = 0, files = [] } = {}) => {
+ wrapper = shallowMountExtended(DiffStatsDropdown, {
+ propsData: {
+ changed,
+ added,
+ deleted,
+ files,
+ },
+ stubs: {
+ GlSprintf,
+ GlDropdown,
+ },
+ });
+ };
+
+ const findChanged = () => wrapper.findComponent(GlDropdown);
+ const findChangedFiles = () => findChanged().findAllComponents(GlDropdownItem);
+ const findNoFilesText = () => findChanged().findComponent(GlDropdownText);
+ const findCollapsed = () => wrapper.findByTestId('diff-stats-additions-deletions-expanded');
+ const findExpanded = () => wrapper.findByTestId('diff-stats-additions-deletions-collapsed');
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+
+ describe('file item', () => {
+ beforeEach(() => {
+ createComponent({ files: mockFiles });
+ });
+
+ it('when no file name provided ', () => {
+ expect(findChangedFiles().at(0).text()).toContain(i18n.noFileNameAvailable);
+ });
+
+ it('when all file data is available', () => {
+ const fileData = findChangedFiles().at(1);
+ const fileText = findChangedFiles().at(1).text();
+ expect(fileText).toContain(mockFiles[1].name);
+ expect(fileText).toContain(mockFiles[1].path);
+ expect(fileData.props()).toMatchObject({
+ iconName: mockFiles[1].icon,
+ iconColor: mockFiles[1].iconColor,
+ });
+ });
+
+ it('when no files changed', () => {
+ createComponent({ files: [] });
+ expect(findNoFilesText().text()).toContain(i18n.noFilesFound);
+ });
+ });
+
+ describe.each`
+ changed | added | deleted | expectedDropdownHeader | expectedAddedDeletedExpanded | expectedAddedDeletedCollapsed
+ ${0} | ${0} | ${0} | ${'0 changed files'} | ${'+0 -0'} | ${'with 0 additions and 0 deletions'}
+ ${2} | ${0} | ${2} | ${'2 changed files'} | ${'+0 -2'} | ${'with 0 additions and 2 deletions'}
+ ${2} | ${2} | ${0} | ${'2 changed files'} | ${'+2 -0'} | ${'with 2 additions and 0 deletions'}
+ ${2} | ${1} | ${1} | ${'2 changed files'} | ${'+1 -1'} | ${'with 1 addition and 1 deletion'}
+ ${1} | ${0} | ${1} | ${'1 changed file'} | ${'+0 -1'} | ${'with 0 additions and 1 deletion'}
+ ${1} | ${1} | ${0} | ${'1 changed file'} | ${'+1 -0'} | ${'with 1 addition and 0 deletions'}
+ ${4} | ${2} | ${2} | ${'4 changed files'} | ${'+2 -2'} | ${'with 2 additions and 2 deletions'}
+ `(
+ 'when there are $changed changed file(s), $added added and $deleted deleted file(s)',
+ ({
+ changed,
+ added,
+ deleted,
+ expectedDropdownHeader,
+ expectedAddedDeletedExpanded,
+ expectedAddedDeletedCollapsed,
+ }) => {
+ beforeAll(() => {
+ createComponent({ changed, added, deleted });
+ });
+
+ afterAll(() => {
+ wrapper.destroy();
+ });
+
+ it(`dropdown header should be '${expectedDropdownHeader}'`, () => {
+ expect(findChanged().props('text')).toBe(expectedDropdownHeader);
+ });
+
+ it(`added and deleted count in expanded section should be '${expectedAddedDeletedExpanded}'`, () => {
+ expect(findExpanded().text()).toBe(expectedAddedDeletedExpanded);
+ });
+
+ it(`added and deleted count in collapsed section should be '${expectedAddedDeletedCollapsed}'`, () => {
+ expect(findCollapsed().text()).toBe(expectedAddedDeletedCollapsed);
+ });
+ },
+ );
+
+ describe('fuzzy file search', () => {
+ beforeEach(() => {
+ createComponent({ files: mockFiles });
+ });
+
+ it('should call `fuzzaldrinPlus.filter` to search for files when the search query is NOT empty', async () => {
+ const searchStr = 'file name';
+ findSearchBox().vm.$emit('input', searchStr);
+ await nextTick();
+ expect(fuzzaldrinPlus.filter).toHaveBeenCalledWith(mockFiles, searchStr, { key: 'name' });
+ });
+
+ it('should NOT call `fuzzaldrinPlus.filter` to search for files when the search query is empty', async () => {
+ const searchStr = '';
+ findSearchBox().vm.$emit('input', searchStr);
+ await nextTick();
+ expect(fuzzaldrinPlus.filter).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('selecting file dropdown item', () => {
+ beforeEach(() => {
+ createComponent({ files: mockFiles });
+ });
+
+ it('updates the URL ', () => {
+ findChangedFiles().at(0).vm.$emit('click');
+ expect(window.location.hash).toBe(mockFiles[0].href);
+ findChangedFiles().at(1).vm.$emit('click');
+ expect(window.location.hash).toBe(mockFiles[1].href);
+ });
+ });
+
+ describe('on dropdown open', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('should set the search input focus', () => {
+ wrapper.vm.$refs.search.focusInput = jest.fn();
+ findChanged().vm.$emit('shown');
+
+ expect(wrapper.vm.$refs.search.focusInput).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
index 1b97011bf7f..d85b6e6d115 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
@@ -25,7 +25,7 @@ import {
const mockStorageKey = 'recent-tokens';
function setLocalStorageAvailability(isAvailable) {
- jest.spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').mockReturnValue(isAvailable);
+ jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(isAvailable);
}
describe('Filtered Search Utils', () => {
@@ -309,7 +309,7 @@ describe('urlQueryToFilter', () => {
{
[FILTERED_SEARCH_TERM]: [{ value: 'my' }, { value: 'terms' }],
},
- { filteredSearchTermKey: 'search', legacySpacesDecode: false },
+ { filteredSearchTermKey: 'search' },
],
[
'search=my terms&foo=bar&nop=xxx',
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
index 529844817d3..bfb593bf82d 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
@@ -11,7 +11,10 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { sortMilestonesByDueDate } from '~/milestones/milestone_utils';
-import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ DEFAULT_MILESTONES,
+ DEFAULT_MILESTONES_GRAPHQL,
+} from '~/vue_shared/components/filtered_search_bar/constants';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import { mockMilestoneToken, mockMilestones, mockRegularMilestone } from '../mock_data';
@@ -191,5 +194,22 @@ describe('MilestoneToken', () => {
expect(suggestions.at(index).text()).toBe(milestone.text);
});
});
+
+ describe('when getActiveMilestones is called and milestones is empty', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockMilestoneToken, defaultMilestones: DEFAULT_MILESTONES_GRAPHQL },
+ });
+ });
+
+ it('finds the correct value from the activeToken', () => {
+ DEFAULT_MILESTONES_GRAPHQL.forEach(({ value, title }) => {
+ const activeToken = wrapper.vm.getActiveMilestone([], value);
+
+ expect(activeToken.title).toEqual(title);
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/header_ci_component_spec.js b/spec/frontend/vue_shared/components/header_ci_component_spec.js
index b54d120b55b..42f4439df51 100644
--- a/spec/frontend/vue_shared/components/header_ci_component_spec.js
+++ b/spec/frontend/vue_shared/components/header_ci_component_spec.js
@@ -16,8 +16,6 @@ describe('Header CI Component', () => {
text: 'failed',
details_path: 'path',
},
- itemName: 'job',
- itemId: 123,
time: '2017-05-08T14:57:39.781Z',
user: {
web_url: 'path',
@@ -55,17 +53,13 @@ describe('Header CI Component', () => {
describe('render', () => {
beforeEach(() => {
- createComponent();
+ createComponent({ itemName: 'Pipeline' });
});
it('should render status badge', () => {
expect(findIconBadge().exists()).toBe(true);
});
- it('should render item name and id', () => {
- expect(findHeaderItemText().text()).toBe('job #123');
- });
-
it('should render timeago date', () => {
expect(findTimeAgo().exists()).toBe(true);
});
@@ -83,9 +77,29 @@ describe('Header CI Component', () => {
});
});
+ describe('with item id', () => {
+ beforeEach(() => {
+ createComponent({ itemName: 'Pipeline', itemId: '123' });
+ });
+
+ it('should render item name and id', () => {
+ expect(findHeaderItemText().text()).toBe('Pipeline #123');
+ });
+ });
+
+ describe('without item id', () => {
+ beforeEach(() => {
+ createComponent({ itemName: 'Job build_job' });
+ });
+
+ it('should render item name', () => {
+ expect(findHeaderItemText().text()).toBe('Job build_job');
+ });
+ });
+
describe('slot', () => {
it('should render header action buttons', () => {
- createComponent({}, { slots: { default: 'Test Actions' } });
+ createComponent({ itemName: 'Job build_job' }, { slots: { default: 'Test Actions' } });
expect(findActionButtons().exists()).toBe(true);
expect(findActionButtons().text()).toBe('Test Actions');
@@ -94,7 +108,7 @@ describe('Header CI Component', () => {
describe('shouldRenderTriggeredLabel', () => {
it('should render created keyword when the shouldRenderTriggeredLabel is false', () => {
- createComponent({ shouldRenderTriggeredLabel: false });
+ createComponent({ shouldRenderTriggeredLabel: false, itemName: 'Job build_job' });
expect(wrapper.text()).toContain('created');
expect(wrapper.text()).not.toContain('triggered');
diff --git a/spec/frontend/vue_shared/components/issuable/issuable_header_warnings_spec.js b/spec/frontend/vue_shared/components/issuable/issuable_header_warnings_spec.js
index 573501233b9..ad8331afcff 100644
--- a/spec/frontend/vue_shared/components/issuable/issuable_header_warnings_spec.js
+++ b/spec/frontend/vue_shared/components/issuable/issuable_header_warnings_spec.js
@@ -1,5 +1,7 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createStore as createMrStore } from '~/mr_notes/stores';
import createIssueStore from '~/notes/stores';
import IssuableHeaderWarnings from '~/vue_shared/components/issuable/issuable_header_warnings.vue';
@@ -12,52 +14,53 @@ localVue.use(Vuex);
describe('IssuableHeaderWarnings', () => {
let wrapper;
- let store;
- const findConfidentialIcon = () => wrapper.find('[data-testid="confidential"]');
- const findLockedIcon = () => wrapper.find('[data-testid="locked"]');
+ const findConfidentialIcon = () => wrapper.findByTestId('confidential');
+ const findLockedIcon = () => wrapper.findByTestId('locked');
+ const findHiddenIcon = () => wrapper.findByTestId('hidden');
const renderTestMessage = (renders) => (renders ? 'renders' : 'does not render');
- const setLock = (locked) => {
- store.getters.getNoteableData.discussion_locked = locked;
- };
-
- const setConfidential = (confidential) => {
- store.getters.getNoteableData.confidential = confidential;
- };
-
- const createComponent = () => {
- wrapper = shallowMount(IssuableHeaderWarnings, { store, localVue });
+ const createComponent = ({ store, provide }) => {
+ wrapper = shallowMountExtended(IssuableHeaderWarnings, {
+ store,
+ localVue,
+ provide,
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
- store = null;
});
describe.each`
issuableType
${ISSUABLE_TYPE_ISSUE} | ${ISSUABLE_TYPE_MR}
`(`when issuableType=$issuableType`, ({ issuableType }) => {
- beforeEach(() => {
- store = issuableType === ISSUABLE_TYPE_ISSUE ? createIssueStore() : createMrStore();
- createComponent();
- });
-
describe.each`
- lockStatus | confidentialStatus
- ${true} | ${true}
- ${true} | ${false}
- ${false} | ${true}
- ${false} | ${false}
+ lockStatus | confidentialStatus | hiddenStatus
+ ${true} | ${true} | ${false}
+ ${true} | ${false} | ${false}
+ ${false} | ${true} | ${false}
+ ${false} | ${false} | ${false}
+ ${true} | ${true} | ${true}
+ ${true} | ${false} | ${true}
+ ${false} | ${true} | ${true}
+ ${false} | ${false} | ${true}
`(
- `when locked=$lockStatus and confidential=$confidentialStatus`,
- ({ lockStatus, confidentialStatus }) => {
+ `when locked=$lockStatus, confidential=$confidentialStatus, and hidden=$hiddenStatus`,
+ ({ lockStatus, confidentialStatus, hiddenStatus }) => {
+ const store = issuableType === ISSUABLE_TYPE_ISSUE ? createIssueStore() : createMrStore();
+
beforeEach(() => {
- setLock(lockStatus);
- setConfidential(confidentialStatus);
+ store.getters.getNoteableData.confidential = confidentialStatus;
+ store.getters.getNoteableData.discussion_locked = lockStatus;
+
+ createComponent({ store, provide: { hidden: hiddenStatus } });
});
it(`${renderTestMessage(lockStatus)} the locked icon`, () => {
@@ -67,6 +70,19 @@ describe('IssuableHeaderWarnings', () => {
it(`${renderTestMessage(confidentialStatus)} the confidential icon`, () => {
expect(findConfidentialIcon().exists()).toBe(confidentialStatus);
});
+
+ it(`${renderTestMessage(confidentialStatus)} the hidden icon`, () => {
+ const hiddenIcon = findHiddenIcon();
+
+ expect(hiddenIcon.exists()).toBe(hiddenStatus);
+
+ if (hiddenStatus) {
+ expect(hiddenIcon.attributes('title')).toBe(
+ 'This issue is hidden because its author has been banned',
+ );
+ expect(getBinding(hiddenIcon.element, 'gl-tooltip')).not.toBeUndefined();
+ }
+ });
},
);
});
diff --git a/spec/frontend/vue_shared/components/markdown/field_spec.js b/spec/frontend/vue_shared/components/markdown/field_spec.js
index 442032840e1..76e1a1162ad 100644
--- a/spec/frontend/vue_shared/components/markdown/field_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/field_spec.js
@@ -32,7 +32,7 @@ describe('Markdown field component', () => {
axiosMock.restore();
});
- function createSubject() {
+ function createSubject(lines = []) {
// We actually mount a wrapper component so that we can force Vue to rerender classes in order to test a regression
// caused by mixing Vanilla JS and Vue.
subject = mount(
@@ -60,6 +60,7 @@ describe('Markdown field component', () => {
markdownPreviewPath,
isSubmitting: false,
textareaValue,
+ lines,
},
},
);
@@ -243,4 +244,14 @@ describe('Markdown field component', () => {
});
});
});
+
+ describe('suggestions', () => {
+ it('escapes new line characters', () => {
+ createSubject([{ rich_text: 'hello world\\n' }]);
+
+ expect(subject.find('[data-testid="markdownHeader"]').props('lineContent')).toBe(
+ 'hello world%br',
+ );
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/registry/title_area_spec.js b/spec/frontend/vue_shared/components/registry/title_area_spec.js
index fb0009ebb8d..75aa3bc7096 100644
--- a/spec/frontend/vue_shared/components/registry/title_area_spec.js
+++ b/spec/frontend/vue_shared/components/registry/title_area_spec.js
@@ -135,15 +135,16 @@ describe('title area', () => {
},
});
};
+
it('shows dynamic slots', async () => {
mountComponent();
// we manually add a new slot to simulate dynamic slots being evaluated after the initial mount
wrapper.vm.$slots[DYNAMIC_SLOT] = createDynamicSlot();
+ // updating the slots like we do on line 141 does not cause the updated lifecycle-hook to be triggered
+ wrapper.vm.$forceUpdate();
await wrapper.vm.$nextTick();
- expect(findDynamicSlot().exists()).toBe(false);
- await wrapper.vm.$nextTick();
expect(findDynamicSlot().exists()).toBe(true);
});
@@ -160,10 +161,8 @@ describe('title area', () => {
'metadata-foo': wrapper.vm.$slots['metadata-foo'],
};
- await wrapper.vm.$nextTick();
- expect(findDynamicSlot().exists()).toBe(false);
- expect(findMetadataSlot('metadata-foo').exists()).toBe(true);
-
+ // updating the slots like we do on line 159 does not cause the updated lifecycle-hook to be triggered
+ wrapper.vm.$forceUpdate();
await wrapper.vm.$nextTick();
expect(findSlotOrderElements().at(0).attributes('data-testid')).toBe(DYNAMIC_SLOT);
diff --git a/spec/frontend/vue_shared/components/runner_aws_deployments/runner_aws_deployments_modal_spec.js b/spec/frontend/vue_shared/components/runner_aws_deployments/runner_aws_deployments_modal_spec.js
index 69db3ec7132..ad692a38e65 100644
--- a/spec/frontend/vue_shared/components/runner_aws_deployments/runner_aws_deployments_modal_spec.js
+++ b/spec/frontend/vue_shared/components/runner_aws_deployments/runner_aws_deployments_modal_spec.js
@@ -21,6 +21,7 @@ describe('RunnerAwsDeploymentsModal', () => {
wrapper = shallowMount(RunnerAwsDeploymentsModal, {
propsData: {
modalId: 'runner-aws-deployments-modal',
+ imgSrc: '/assets/aws-cloud-formation.png',
},
});
};
diff --git a/spec/frontend/vue_shared/components/settings/__snapshots__/settings_block_spec.js.snap b/spec/frontend/vue_shared/components/settings/__snapshots__/settings_block_spec.js.snap
index ed085fb66dc..165caea2751 100644
--- a/spec/frontend/vue_shared/components/settings/__snapshots__/settings_block_spec.js.snap
+++ b/spec/frontend/vue_shared/components/settings/__snapshots__/settings_block_spec.js.snap
@@ -8,12 +8,25 @@ exports[`Settings Block renders the correct markup 1`] = `
class="settings-header"
>
<h4>
- <div
- data-testid="title-slot"
- />
+ <span
+ aria-controls="settings_content_3"
+ aria-expanded="false"
+ class="gl-cursor-pointer"
+ data-testid="section-title-button"
+ id="settings_label_2"
+ role="button"
+ tabindex="0"
+ >
+ <div
+ data-testid="title-slot"
+ />
+ </span>
</h4>
<gl-button-stub
+ aria-controls="settings_content_3"
+ aria-expanded="false"
+ aria-label="Expand settings section"
buttontextclasses=""
category="primary"
icon=""
@@ -33,7 +46,11 @@ exports[`Settings Block renders the correct markup 1`] = `
</div>
<div
+ aria-labelledby="settings_label_2"
class="settings-content"
+ id="settings_content_3"
+ role="region"
+ tabindex="-1"
>
<div
data-testid="default-slot"
diff --git a/spec/frontend/vue_shared/components/settings/settings_block_spec.js b/spec/frontend/vue_shared/components/settings/settings_block_spec.js
index be5a15631eb..528dfd89690 100644
--- a/spec/frontend/vue_shared/components/settings/settings_block_spec.js
+++ b/spec/frontend/vue_shared/components/settings/settings_block_spec.js
@@ -1,12 +1,12 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import component from '~/vue_shared/components/settings/settings_block.vue';
+import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
describe('Settings Block', () => {
let wrapper;
const mountComponent = (propsData) => {
- wrapper = shallowMount(component, {
+ wrapper = shallowMount(SettingsBlock, {
propsData,
slots: {
title: '<div data-testid="title-slot"></div>',
@@ -18,13 +18,25 @@ describe('Settings Block', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
const findTitleSlot = () => wrapper.find('[data-testid="title-slot"]');
const findDescriptionSlot = () => wrapper.find('[data-testid="description-slot"]');
- const findExpandButton = () => wrapper.find(GlButton);
+ const findExpandButton = () => wrapper.findComponent(GlButton);
+ const findSectionTitleButton = () => wrapper.find('[data-testid="section-title-button"]');
+
+ const expectExpandedState = ({ expanded = true } = {}) => {
+ const settingsExpandButton = findExpandButton();
+
+ expect(wrapper.classes('expanded')).toBe(expanded);
+ expect(settingsExpandButton.text()).toBe(
+ expanded ? SettingsBlock.i18n.collapseText : SettingsBlock.i18n.expandText,
+ );
+ expect(settingsExpandButton.attributes('aria-label')).toBe(
+ expanded ? SettingsBlock.i18n.collapseAriaLabel : SettingsBlock.i18n.expandAriaLabel,
+ );
+ };
it('renders the correct markup', () => {
mountComponent();
@@ -75,33 +87,41 @@ describe('Settings Block', () => {
it('is collapsed by default', () => {
mountComponent();
- expect(wrapper.classes('expanded')).toBe(false);
+ expectExpandedState({ expanded: false });
});
it('adds expanded class when the expand button is clicked', async () => {
mountComponent();
- expect(wrapper.classes('expanded')).toBe(false);
- expect(findExpandButton().text()).toBe('Expand');
-
await findExpandButton().vm.$emit('click');
- expect(wrapper.classes('expanded')).toBe(true);
- expect(findExpandButton().text()).toBe('Collapse');
+ expectExpandedState({ expanded: true });
});
- it('is expanded when `defaultExpanded` is true no matter what', async () => {
- mountComponent({ defaultExpanded: true });
+ it('adds expanded class when the section title is clicked', async () => {
+ mountComponent();
- expect(wrapper.classes('expanded')).toBe(true);
+ await findSectionTitleButton().trigger('click');
- await findExpandButton().vm.$emit('click');
+ expectExpandedState({ expanded: true });
+ });
- expect(wrapper.classes('expanded')).toBe(true);
+ describe('when `collapsible` is `false`', () => {
+ beforeEach(() => {
+ mountComponent({ collapsible: false });
+ });
- await findExpandButton().vm.$emit('click');
+ it('does not render clickable section title', () => {
+ expect(findSectionTitleButton().exists()).toBe(false);
+ });
+
+ it('contains expanded class', () => {
+ expect(wrapper.classes('expanded')).toBe(true);
+ });
- expect(wrapper.classes('expanded')).toBe(true);
+ it('does not render expand toggle button', () => {
+ expect(findExpandButton().exists()).toBe(false);
+ });
});
});
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
index a1942e59571..e39e8794fdd 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
@@ -124,7 +124,7 @@ describe('DropdownContentsLabelsView', () => {
});
it('returns false when provided `label` param is not one of the selected labels', () => {
- expect(wrapper.vm.isLabelSelected(mockLabels[2])).toBe(false);
+ expect(wrapper.vm.isLabelSelected(mockLabels[1])).toBe(false);
});
});
@@ -203,7 +203,7 @@ describe('DropdownContentsLabelsView', () => {
it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
wrapper.setData({
- currentHighlightItem: 1,
+ currentHighlightItem: 2,
});
wrapper.vm.handleKeyDown({
@@ -213,7 +213,7 @@ describe('DropdownContentsLabelsView', () => {
expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([
{
- ...mockLabels[1],
+ ...mockLabels[2],
set: true,
},
]);
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js
index c90e63313b2..960ea77cb6e 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_value_spec.js
@@ -6,7 +6,7 @@ import DropdownValue from '~/vue_shared/components/sidebar/labels_select_vue/dro
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
-import { mockConfig, mockRegularLabel, mockScopedLabel } from './mock_data';
+import { mockConfig, mockLabels, mockRegularLabel, mockScopedLabel } from './mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -14,6 +14,9 @@ localVue.use(Vuex);
describe('DropdownValue', () => {
let wrapper;
+ const findAllLabels = () => wrapper.findAllComponents(GlLabel);
+ const findLabel = (index) => findAllLabels().at(index).props('title');
+
const createComponent = (initialState = {}, slots = {}) => {
const store = new Vuex.Store(labelsSelectModule());
@@ -28,7 +31,6 @@ describe('DropdownValue', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
describe('methods', () => {
@@ -82,7 +84,17 @@ describe('DropdownValue', () => {
it('renders labels when `selectedLabels` is not empty', () => {
createComponent();
- expect(wrapper.findAll(GlLabel).length).toBe(2);
+ expect(findAllLabels()).toHaveLength(2);
+ });
+
+ it('orders scoped labels first', () => {
+ createComponent({ selectedLabels: mockLabels });
+
+ expect(findAllLabels()).toHaveLength(mockLabels.length);
+ expect(findLabel(0)).toBe('Foo::Bar');
+ expect(findLabel(1)).toBe('Boog');
+ expect(findLabel(2)).toBe('Bug');
+ expect(findLabel(3)).toBe('Foo Label');
});
});
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
index 730afcbecab..1faa3b0af1d 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
@@ -15,22 +15,22 @@ export const mockScopedLabel = {
};
export const mockLabels = [
- mockRegularLabel,
- mockScopedLabel,
{
- id: 28,
- title: 'Bug',
+ id: 29,
+ title: 'Boog',
description: 'Label for bugs',
color: '#FF0000',
textColor: '#FFFFFF',
},
{
- id: 29,
- title: 'Boog',
+ id: 28,
+ title: 'Bug',
description: 'Label for bugs',
color: '#FF0000',
textColor: '#FFFFFF',
},
+ mockRegularLabel,
+ mockScopedLabel,
];
export const mockCollapsedLabels = [
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_button_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_button_spec.js
deleted file mode 100644
index 0a42d389b67..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_button_spec.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { GlIcon, GlButton } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-
-import DropdownButton from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_button.vue';
-
-import labelSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
-
-import { mockConfig } from './mock_data';
-
-let store;
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-const createComponent = (initialState = mockConfig) => {
- store = new Vuex.Store(labelSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownButton, {
- localVue,
- store,
- });
-};
-
-describe('DropdownButton', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findDropdownButton = () => wrapper.find(GlButton);
- const findDropdownText = () => wrapper.find('.dropdown-toggle-text');
- const findDropdownIcon = () => wrapper.find(GlIcon);
-
- describe('methods', () => {
- describe('handleButtonClick', () => {
- it.each`
- variant | expectPropagationStopped
- ${'standalone'} | ${true}
- ${'embedded'} | ${false}
- `(
- 'toggles dropdown content and handles event propagation when `state.variant` is "$variant"',
- ({ variant, expectPropagationStopped }) => {
- const event = { stopPropagation: jest.fn() };
-
- wrapper = createComponent({ ...mockConfig, variant });
-
- findDropdownButton().vm.$emit('click', event);
-
- expect(store.state.showDropdownContents).toBe(true);
- expect(event.stopPropagation).toHaveBeenCalledTimes(expectPropagationStopped ? 1 : 0);
- },
- );
- });
- });
-
- describe('template', () => {
- it('renders component container element', () => {
- expect(wrapper.find(GlButton).element).toBe(wrapper.element);
- });
-
- it('renders default button text element', () => {
- const dropdownTextEl = findDropdownText();
-
- expect(dropdownTextEl.exists()).toBe(true);
- expect(dropdownTextEl.text()).toBe('Label');
- });
-
- it('renders provided button text element', () => {
- store.state.dropdownButtonText = 'Custom label';
- const dropdownTextEl = findDropdownText();
-
- return wrapper.vm.$nextTick().then(() => {
- expect(dropdownTextEl.text()).toBe('Custom label');
- });
- });
-
- it('renders chevron icon element', () => {
- const iconEl = findDropdownIcon();
-
- expect(iconEl.exists()).toBe(true);
- expect(iconEl.props('name')).toBe('chevron-down');
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
index 90bc1980ac3..843298a1406 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
@@ -7,7 +7,12 @@ import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
import createLabelMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql';
-import { mockSuggestedColors, createLabelSuccessfulResponse } from './mock_data';
+import projectLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql';
+import {
+ mockSuggestedColors,
+ createLabelSuccessfulResponse,
+ labelsQueryResponse,
+} from './mock_data';
jest.mock('~/flash');
@@ -44,6 +49,14 @@ describe('DropdownContentsCreateView', () => {
const createComponent = ({ mutationHandler = createLabelSuccessHandler } = {}) => {
const mockApollo = createMockApollo([[createLabelMutation, mutationHandler]]);
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: projectLabelsQuery,
+ data: labelsQueryResponse.data,
+ variables: {
+ fullPath: '',
+ searchTerm: '',
+ },
+ });
wrapper = shallowMount(DropdownContentsCreateView, {
localVue,
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
index 8bd944a3d54..537bbc8e71e 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
@@ -45,8 +45,6 @@ describe('DropdownContentsLabelsView', () => {
provide: {
projectPath: 'test',
iid: 1,
- allowLabelCreate: true,
- labelsManagePath: '/gitlab-org/my-project/-/labels',
variant: DropdownVariant.Sidebar,
...injected,
},
@@ -69,10 +67,7 @@ describe('DropdownContentsLabelsView', () => {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findLabelsList = () => wrapper.find('[data-testid="labels-list"]');
- const findDropdownWrapper = () => wrapper.find('[data-testid="dropdown-wrapper"]');
- const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
const findNoResultsMessage = () => wrapper.find('[data-testid="no-results"]');
- const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]');
describe('when loading labels', () => {
it('renders disabled search input field', async () => {
@@ -109,40 +104,6 @@ describe('DropdownContentsLabelsView', () => {
expect(findLabelsList().exists()).toBe(true);
expect(findLabels()).toHaveLength(2);
});
-
- it('changes highlighted label correctly on pressing down button', async () => {
- expect(findLabels().at(0).attributes('highlight')).toBeUndefined();
-
- await findDropdownWrapper().trigger('keydown.down');
- expect(findLabels().at(0).attributes('highlight')).toBe('true');
-
- await findDropdownWrapper().trigger('keydown.down');
- expect(findLabels().at(1).attributes('highlight')).toBe('true');
- expect(findLabels().at(0).attributes('highlight')).toBeUndefined();
- });
-
- it('changes highlighted label correctly on pressing up button', async () => {
- await findDropdownWrapper().trigger('keydown.down');
- await findDropdownWrapper().trigger('keydown.down');
- expect(findLabels().at(1).attributes('highlight')).toBe('true');
-
- await findDropdownWrapper().trigger('keydown.up');
- expect(findLabels().at(0).attributes('highlight')).toBe('true');
- });
-
- it('changes label selected state when Enter is pressed', async () => {
- expect(findLabels().at(0).attributes('islabelset')).toBeUndefined();
- await findDropdownWrapper().trigger('keydown.down');
- await findDropdownWrapper().trigger('keydown.enter');
-
- expect(findLabels().at(0).attributes('islabelset')).toBe('true');
- });
-
- it('emits `closeDropdown event` when Esc button is pressed', () => {
- findDropdownWrapper().trigger('keydown.esc');
-
- expect(wrapper.emitted('closeDropdown')).toEqual([[selectedLabels]]);
- });
});
it('when search returns 0 results', async () => {
@@ -170,44 +131,4 @@ describe('DropdownContentsLabelsView', () => {
await waitForPromises();
expect(createFlash).toHaveBeenCalled();
});
-
- it('does not render footer on standalone dropdown', () => {
- createComponent({ injected: { variant: DropdownVariant.Standalone } });
-
- expect(findDropdownFooter().exists()).toBe(false);
- });
-
- it('renders footer on sidebar dropdown', () => {
- createComponent();
-
- expect(findDropdownFooter().exists()).toBe(true);
- });
-
- it('renders footer on embedded dropdown', () => {
- createComponent({ injected: { variant: DropdownVariant.Embedded } });
-
- expect(findDropdownFooter().exists()).toBe(true);
- });
-
- it('does not render create label button if `allowLabelCreate` is false', () => {
- createComponent({ injected: { allowLabelCreate: false } });
-
- expect(findCreateLabelButton().exists()).toBe(false);
- });
-
- describe('when `allowLabelCreate` is true', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders create label button', () => {
- expect(findCreateLabelButton().exists()).toBe(true);
- });
-
- it('emits `toggleDropdownContentsCreateView` event on create label button click', () => {
- findCreateLabelButton().vm.$emit('click');
-
- expect(wrapper.emitted('toggleDropdownContentsCreateView')).toEqual([[]]);
- });
- });
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js
index 3c2fd0c5acc..a1b40a891ec 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_spec.js
@@ -1,77 +1,127 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
+import { GlDropdown } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
-
-import { mockConfig, mockLabels } from './mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-const createComponent = (initialState = mockConfig, defaultProps = {}) => {
- const store = new Vuex.Store(labelsSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownContents, {
- propsData: {
- ...defaultProps,
- labelsCreateTitle: 'test',
- selectedLabels: mockLabels,
- allowMultiselect: true,
- labelsListTitle: 'Assign labels',
- footerCreateLabelTitle: 'create',
- footerManageLabelTitle: 'manage',
- },
- localVue,
- store,
- });
-};
+import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
+import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue';
+
+import { mockLabels } from './mock_data';
describe('DropdownContent', () => {
let wrapper;
+ const createComponent = ({ props = {}, injected = {} } = {}) => {
+ wrapper = shallowMount(DropdownContents, {
+ propsData: {
+ labelsCreateTitle: 'test',
+ selectedLabels: mockLabels,
+ allowMultiselect: true,
+ labelsListTitle: 'Assign labels',
+ footerCreateLabelTitle: 'create',
+ footerManageLabelTitle: 'manage',
+ dropdownButtonText: 'Labels',
+ variant: 'sidebar',
+ ...props,
+ },
+ provide: {
+ allowLabelCreate: true,
+ labelsManagePath: 'foo/bar',
+ ...injected,
+ },
+ stubs: {
+ GlDropdown,
+ },
+ });
+ };
+
beforeEach(() => {
- wrapper = createComponent();
+ createComponent();
});
afterEach(() => {
wrapper.destroy();
});
- describe('computed', () => {
- describe('dropdownContentsView', () => {
- it('returns string "dropdown-contents-create-view" when `showDropdownContentsCreateView` prop is `true`', () => {
- wrapper.vm.$store.dispatch('toggleDropdownContentsCreateView');
+ const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]');
+ const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]');
+ const findGoBackButton = () => wrapper.find('[data-testid="go-back-button"]');
- expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-create-view');
- });
+ describe('Create view', () => {
+ beforeEach(() => {
+ wrapper.vm.toggleDropdownContentsCreateView();
+ });
- it('returns string "dropdown-contents-labels-view" when `showDropdownContentsCreateView` prop is `false`', () => {
- expect(wrapper.vm.dropdownContentsView).toBe('dropdown-contents-labels-view');
- });
+ it('renders create view when `showDropdownContentsCreateView` prop is `true`', () => {
+ expect(wrapper.findComponent(DropdownContentsCreateView).exists()).toBe(true);
+ });
+
+ it('does not render footer', () => {
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
+
+ it('does not render create label button', () => {
+ expect(findCreateLabelButton().exists()).toBe(false);
+ });
+
+ it('renders go back button', () => {
+ expect(findGoBackButton().exists()).toBe(true);
});
});
- describe('template', () => {
- it('renders component container element with class `labels-select-dropdown-contents` and no styles', () => {
- expect(wrapper.attributes('class')).toContain('labels-select-dropdown-contents');
- expect(wrapper.attributes('style')).toBeUndefined();
+ describe('Labels view', () => {
+ it('renders labels view when `showDropdownContentsCreateView` when `showDropdownContentsCreateView` prop is `false`', () => {
+ expect(wrapper.findComponent(DropdownContentsLabelsView).exists()).toBe(true);
});
- describe('when `renderOnTop` is true', () => {
- it.each`
- variant | expected
- ${DropdownVariant.Sidebar} | ${'bottom: 3rem'}
- ${DropdownVariant.Standalone} | ${'bottom: 2rem'}
- ${DropdownVariant.Embedded} | ${'bottom: 2rem'}
- `('renders upward for $variant variant', ({ variant, expected }) => {
- wrapper = createComponent({ ...mockConfig, variant }, { renderOnTop: true });
+ it('renders footer on sidebar dropdown', () => {
+ expect(findDropdownFooter().exists()).toBe(true);
+ });
+
+ it('does not render footer on standalone dropdown', () => {
+ createComponent({ props: { variant: DropdownVariant.Standalone } });
+
+ expect(findDropdownFooter().exists()).toBe(false);
+ });
- expect(wrapper.attributes('style')).toContain(expected);
+ it('renders footer on embedded dropdown', () => {
+ createComponent({ props: { variant: DropdownVariant.Embedded } });
+
+ expect(findDropdownFooter().exists()).toBe(true);
+ });
+
+ it('does not render go back button', () => {
+ expect(findGoBackButton().exists()).toBe(false);
+ });
+
+ it('does not render create label button if `allowLabelCreate` is false', () => {
+ createComponent({ injected: { allowLabelCreate: false } });
+
+ expect(findCreateLabelButton().exists()).toBe(false);
+ });
+
+ describe('when `allowLabelCreate` is true', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders create label button', () => {
+ expect(findCreateLabelButton().exists()).toBe(true);
});
+
+ it('triggers `toggleDropdownContent` method on create label button click', () => {
+ jest.spyOn(wrapper.vm, 'toggleDropdownContent').mockImplementation(() => {});
+ findCreateLabelButton().trigger('click');
+
+ expect(wrapper.vm.toggleDropdownContent).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element with classes `gl-w-full gl-mt-2` and no styles', () => {
+ expect(wrapper.attributes('class')).toContain('gl-w-full gl-mt-2');
+ expect(wrapper.attributes('style')).toBeUndefined();
});
});
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_title_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_title_spec.js
deleted file mode 100644
index d2401a1f725..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_title_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { GlButton, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-
-import DropdownTitle from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue';
-
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
-
-import { mockConfig } from './mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-const createComponent = (initialState = mockConfig) => {
- const store = new Vuex.Store(labelsSelectModule());
-
- store.dispatch('setInitialState', initialState);
-
- return shallowMount(DropdownTitle, {
- localVue,
- store,
- propsData: {
- labelsSelectInProgress: false,
- },
- });
-};
-
-describe('DropdownTitle', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- it('renders component container element with string "Labels"', () => {
- expect(wrapper.text()).toContain('Labels');
- });
-
- it('renders edit link', () => {
- const editBtnEl = wrapper.find(GlButton);
-
- expect(editBtnEl.exists()).toBe(true);
- expect(editBtnEl.text()).toBe('Edit');
- });
-
- it('renders loading icon element when `labelsSelectInProgress` prop is true', () => {
- wrapper.setProps({
- labelsSelectInProgress: true,
- });
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js
index b3ffee2d020..e7e78cd7a33 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_value_spec.js
@@ -9,8 +9,8 @@ describe('DropdownValue', () => {
let wrapper;
const findAllLabels = () => wrapper.findAllComponents(GlLabel);
- const findRegularLabel = () => findAllLabels().at(0);
- const findScopedLabel = () => findAllLabels().at(1);
+ const findRegularLabel = () => findAllLabels().at(1);
+ const findScopedLabel = () => findAllLabels().at(0);
const findWrapper = () => wrapper.find('[data-testid="value-wrapper"]');
const findEmptyPlaceholder = () => wrapper.find('[data-testid="empty-placeholder"]');
@@ -20,11 +20,13 @@ describe('DropdownValue', () => {
propsData: {
selectedLabels: [mockRegularLabel, mockScopedLabel],
allowLabelRemove: true,
- allowScopedLabels: true,
labelsFilterBasePath: '/gitlab-org/my-project/issues',
labelsFilterParam: 'label_name',
...props,
},
+ provide: {
+ allowScopedLabels: true,
+ },
});
};
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
index 23810339833..6e8841411a2 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
@@ -1,4 +1,3 @@
-import { GlIcon, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import LabelItem from '~/vue_shared/components/sidebar/labels_select_widget/label_item.vue';
@@ -6,16 +5,10 @@ import { mockRegularLabel } from './mock_data';
const mockLabel = { ...mockRegularLabel, set: true };
-const createComponent = ({
- label = mockLabel,
- isLabelSet = mockLabel.set,
- highlight = true,
-} = {}) =>
+const createComponent = ({ label = mockLabel } = {}) =>
shallowMount(LabelItem, {
propsData: {
label,
- isLabelSet,
- highlight,
},
});
@@ -31,45 +24,6 @@ describe('LabelItem', () => {
});
describe('template', () => {
- it('renders gl-link component', () => {
- expect(wrapper.find(GlLink).exists()).toBe(true);
- });
-
- it('renders component root with class `is-focused` when `highlight` prop is true', () => {
- const wrapperTemp = createComponent({
- highlight: true,
- });
-
- expect(wrapperTemp.classes()).toContain('is-focused');
-
- wrapperTemp.destroy();
- });
-
- it('renders visible gl-icon component when `isLabelSet` prop is true', () => {
- const wrapperTemp = createComponent({
- isLabelSet: true,
- });
-
- const iconEl = wrapperTemp.find(GlIcon);
-
- expect(iconEl.isVisible()).toBe(true);
- expect(iconEl.props('name')).toBe('mobile-issue-close');
-
- wrapperTemp.destroy();
- });
-
- it('renders visible span element as placeholder instead of gl-icon when `isLabelSet` prop is false', () => {
- const wrapperTemp = createComponent({
- isLabelSet: false,
- });
-
- const placeholderEl = wrapperTemp.find('[data-testid="no-icon"]');
-
- expect(placeholderEl.isVisible()).toBe(true);
-
- wrapperTemp.destroy();
- });
-
it('renders label color element', () => {
const colorEl = wrapper.find('[data-testid="label-color-box"]');
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
index e17dfd93efc..a18511fa21d 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
@@ -1,193 +1,74 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-
-import { isInViewport } from '~/lib/utils/common_utils';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
-import DropdownButton from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_button.vue';
+import { shallowMount } from '@vue/test-utils';
+import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
-import DropdownTitle from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue';
import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue';
import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue';
import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
-import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
-
import { mockConfig } from './mock_data';
-jest.mock('~/lib/utils/common_utils', () => ({
- isInViewport: jest.fn().mockReturnValue(true),
-}));
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
describe('LabelsSelectRoot', () => {
let wrapper;
- let store;
const createComponent = (config = mockConfig, slots = {}) => {
wrapper = shallowMount(LabelsSelectRoot, {
- localVue,
slots,
- store,
propsData: config,
stubs: {
- 'dropdown-contents': DropdownContents,
+ DropdownContents,
+ SidebarEditableItem,
},
provide: {
iid: '1',
projectPath: 'test',
+ canUpdate: true,
+ allowLabelEdit: true,
},
});
};
- beforeEach(() => {
- store = new Vuex.Store(labelsSelectModule());
- });
-
afterEach(() => {
wrapper.destroy();
});
- describe('methods', () => {
- describe('handleDropdownClose', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('emits `updateSelectedLabels` & `onDropdownClose` events on component when provided `labels` param is not empty', () => {
- wrapper.vm.handleDropdownClose([{ id: 1 }, { id: 2 }]);
-
- expect(wrapper.emitted().updateSelectedLabels).toBeTruthy();
- expect(wrapper.emitted().onDropdownClose).toBeTruthy();
- });
-
- it('emits only `onDropdownClose` event on component when provided `labels` param is empty', () => {
- wrapper.vm.handleDropdownClose([]);
-
- expect(wrapper.emitted().updateSelectedLabels).toBeFalsy();
- expect(wrapper.emitted().onDropdownClose).toBeTruthy();
- });
- });
-
- describe('handleCollapsedValueClick', () => {
- it('emits `toggleCollapse` event on component', () => {
- createComponent();
- wrapper.vm.handleCollapsedValueClick();
-
- expect(wrapper.emitted().toggleCollapse).toBeTruthy();
- });
- });
+ it('renders component with classes `labels-select-wrapper position-relative`', () => {
+ createComponent();
+ expect(wrapper.classes()).toEqual(['labels-select-wrapper', 'position-relative']);
});
- describe('template', () => {
- it('renders component with classes `labels-select-wrapper position-relative`', () => {
- createComponent();
- expect(wrapper.attributes('class')).toContain('labels-select-wrapper position-relative');
- });
-
- it.each`
- variant | cssClass
- ${'standalone'} | ${'is-standalone'}
- ${'embedded'} | ${'is-embedded'}
- `(
- 'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
- ({ variant, cssClass }) => {
- createComponent({
- ...mockConfig,
- variant,
- });
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.classes()).toContain(cssClass);
- });
- },
- );
-
- it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => {
- createComponent();
- await wrapper.vm.$nextTick;
- expect(wrapper.find(DropdownValueCollapsed).exists()).toBe(true);
- });
-
- it('renders `dropdown-title` component', async () => {
- createComponent();
- await wrapper.vm.$nextTick;
- expect(wrapper.find(DropdownTitle).exists()).toBe(true);
- });
-
- it('renders `dropdown-value` component', async () => {
- createComponent(mockConfig, {
- default: 'None',
+ it.each`
+ variant | cssClass
+ ${'standalone'} | ${'is-standalone'}
+ ${'embedded'} | ${'is-embedded'}
+ `(
+ 'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
+ ({ variant, cssClass }) => {
+ createComponent({
+ ...mockConfig,
+ variant,
});
- await wrapper.vm.$nextTick;
-
- const valueComp = wrapper.find(DropdownValue);
-
- expect(valueComp.exists()).toBe(true);
- expect(valueComp.text()).toBe('None');
- });
-
- it('renders `dropdown-button` component when `showDropdownButton` prop is `true`', async () => {
- createComponent();
- wrapper.vm.$store.dispatch('toggleDropdownButton');
- await wrapper.vm.$nextTick;
- expect(wrapper.find(DropdownButton).exists()).toBe(true);
- });
-
- it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', async () => {
- createComponent();
- wrapper.vm.$store.dispatch('toggleDropdownContents');
- await wrapper.vm.$nextTick;
- expect(wrapper.find(DropdownContents).exists()).toBe(true);
- });
- describe('sets content direction based on viewport', () => {
- describe.each(Object.values(DropdownVariant))(
- 'when labels variant is "%s"',
- ({ variant }) => {
- beforeEach(() => {
- createComponent({ ...mockConfig, variant });
- wrapper.vm.$store.dispatch('toggleDropdownContents');
- });
-
- it('set direction when out of viewport', () => {
- isInViewport.mockImplementation(() => false);
- wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(DropdownContents).props('renderOnTop')).toBe(true);
- });
- });
-
- it('does not set direction when inside of viewport', () => {
- isInViewport.mockImplementation(() => true);
- wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(DropdownContents).props('renderOnTop')).toBe(false);
- });
- });
- },
- );
- });
- });
+ return wrapper.vm.$nextTick(() => {
+ expect(wrapper.classes()).toContain(cssClass);
+ });
+ },
+ );
- it('calls toggleDropdownContents action when isEditing prop is changing to true', async () => {
+ it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => {
createComponent();
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
- await wrapper.setProps({ isEditing: true });
-
- expect(store.dispatch).toHaveBeenCalledWith('toggleDropdownContents');
+ await wrapper.vm.$nextTick;
+ expect(wrapper.find(DropdownValueCollapsed).exists()).toBe(true);
});
- it('does not call toggleDropdownContents action when isEditing prop is changing to false', async () => {
- createComponent();
+ it('renders `dropdown-value` component', async () => {
+ createComponent(mockConfig, {
+ default: 'None',
+ });
+ await wrapper.vm.$nextTick;
- jest.spyOn(store, 'dispatch').mockResolvedValue();
- await wrapper.setProps({ isEditing: false });
+ const valueComp = wrapper.find(DropdownValue);
- expect(store.dispatch).not.toHaveBeenCalled();
+ expect(valueComp.exists()).toBe(true);
+ expect(valueComp.text()).toBe('None');
});
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
index 5dd8fc1b8b2..fceaabec2d0 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
@@ -34,18 +34,12 @@ export const mockLabels = [
];
export const mockConfig = {
- allowLabelEdit: true,
- allowLabelCreate: true,
- allowScopedLabels: true,
allowMultiselect: true,
labelsListTitle: 'Assign labels',
labelsCreateTitle: 'Create label',
variant: 'sidebar',
- dropdownOnly: false,
selectedLabels: [mockRegularLabel, mockScopedLabel],
labelsSelectInProgress: false,
- labelsFetchPath: '/gitlab-org/my-project/-/labels.json',
- labelsManagePath: '/gitlab-org/my-project/-/labels',
labelsFilterBasePath: '/gitlab-org/my-project/issues',
labelsFilterParam: 'label_name',
footerCreateLabelTitle: 'create',
@@ -83,9 +77,7 @@ export const createLabelSuccessfulResponse = {
id: 'gid://gitlab/ProjectLabel/126',
color: '#dc143c',
description: null,
- descriptionHtml: '',
title: 'ewrwrwer',
- textColor: '#FFFFFF',
__typename: 'Label',
},
errors: [],
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js
deleted file mode 100644
index ee905410ffa..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/actions_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import testAction from 'helpers/vuex_action_helper';
-import * as actions from '~/vue_shared/components/sidebar/labels_select_widget/store/actions';
-import * as types from '~/vue_shared/components/sidebar/labels_select_widget/store/mutation_types';
-import defaultState from '~/vue_shared/components/sidebar/labels_select_widget/store/state';
-
-jest.mock('~/flash');
-
-describe('LabelsSelect Actions', () => {
- let state;
- const mockInitialState = {
- labels: [],
- selectedLabels: [],
- };
-
- beforeEach(() => {
- state = { ...defaultState() };
- });
-
- describe('setInitialState', () => {
- it('sets initial store state', (done) => {
- testAction(
- actions.setInitialState,
- mockInitialState,
- state,
- [{ type: types.SET_INITIAL_STATE, payload: mockInitialState }],
- [],
- done,
- );
- });
- });
-
- describe('toggleDropdownButton', () => {
- it('toggles dropdown button', (done) => {
- testAction(
- actions.toggleDropdownButton,
- {},
- state,
- [{ type: types.TOGGLE_DROPDOWN_BUTTON }],
- [],
- done,
- );
- });
- });
-
- describe('toggleDropdownContents', () => {
- it('toggles dropdown contents', (done) => {
- testAction(
- actions.toggleDropdownContents,
- {},
- state,
- [{ type: types.TOGGLE_DROPDOWN_CONTENTS }],
- [],
- done,
- );
- });
- });
-
- describe('toggleDropdownContentsCreateView', () => {
- it('toggles dropdown create view', (done) => {
- testAction(
- actions.toggleDropdownContentsCreateView,
- {},
- state,
- [{ type: types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW }],
- [],
- done,
- );
- });
- });
-
- describe('updateSelectedLabels', () => {
- it('updates `state.labels` based on provided `labels` param', (done) => {
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
-
- testAction(
- actions.updateSelectedLabels,
- labels,
- state,
- [{ type: types.UPDATE_SELECTED_LABELS, payload: { labels } }],
- [],
- done,
- );
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/getters_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/getters_spec.js
deleted file mode 100644
index 40eb0323146..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/getters_spec.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import * as getters from '~/vue_shared/components/sidebar/labels_select_widget/store/getters';
-
-describe('LabelsSelect Getters', () => {
- describe('dropdownButtonText', () => {
- it.each`
- labelType | dropdownButtonText | expected
- ${'default'} | ${''} | ${'Label'}
- ${'custom'} | ${'Custom label'} | ${'Custom label'}
- `(
- 'returns $labelType text when state.labels has no selected labels',
- ({ dropdownButtonText, expected }) => {
- const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
- const selectedLabels = [];
- const state = { labels, selectedLabels, dropdownButtonText };
-
- expect(getters.dropdownButtonText(state, {})).toBe(expected);
- },
- );
-
- it('returns label title when state.labels has only 1 label', () => {
- const labels = [{ id: 1, title: 'Foobar', set: true }];
-
- expect(getters.dropdownButtonText({ labels }, { isDropdownVariantSidebar: true })).toBe(
- 'Foobar',
- );
- });
-
- it('returns first label title and remaining labels count when state.labels has more than 1 label', () => {
- const labels = [
- { id: 1, title: 'Foo', set: true },
- { id: 2, title: 'Bar', set: true },
- ];
-
- expect(getters.dropdownButtonText({ labels }, { isDropdownVariantSidebar: true })).toBe(
- 'Foo +1 more',
- );
- });
- });
-
- describe('selectedLabelsList', () => {
- it('returns array of IDs of all labels within `state.selectedLabels`', () => {
- const selectedLabels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
-
- expect(getters.selectedLabelsList({ selectedLabels })).toEqual([1, 2, 3, 4]);
- });
- });
-
- describe('isDropdownVariantSidebar', () => {
- it('returns `true` when `state.variant` is "sidebar"', () => {
- expect(getters.isDropdownVariantSidebar({ variant: 'sidebar' })).toBe(true);
- });
- });
-
- describe('isDropdownVariantStandalone', () => {
- it('returns `true` when `state.variant` is "standalone"', () => {
- expect(getters.isDropdownVariantStandalone({ variant: 'standalone' })).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js
deleted file mode 100644
index 1f0e0eee420..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/store/mutations_spec.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import * as types from '~/vue_shared/components/sidebar/labels_select_widget/store/mutation_types';
-import mutations from '~/vue_shared/components/sidebar/labels_select_widget/store/mutations';
-
-describe('LabelsSelect Mutations', () => {
- describe(`${types.SET_INITIAL_STATE}`, () => {
- it('initializes provided props to store state', () => {
- const state = {};
- mutations[types.SET_INITIAL_STATE](state, {
- labels: 'foo',
- });
-
- expect(state.labels).toEqual('foo');
- });
- });
-
- describe(`${types.TOGGLE_DROPDOWN_BUTTON}`, () => {
- it('toggles value of `state.showDropdownButton`', () => {
- const state = {
- showDropdownButton: false,
- };
- mutations[types.TOGGLE_DROPDOWN_BUTTON](state);
-
- expect(state.showDropdownButton).toBe(true);
- });
- });
-
- describe(`${types.TOGGLE_DROPDOWN_CONTENTS}`, () => {
- it('toggles value of `state.showDropdownButton` when `state.dropdownOnly` is false', () => {
- const state = {
- dropdownOnly: false,
- showDropdownButton: false,
- variant: 'sidebar',
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
-
- expect(state.showDropdownButton).toBe(true);
- });
-
- it('toggles value of `state.showDropdownContents`', () => {
- const state = {
- showDropdownContents: false,
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
-
- expect(state.showDropdownContents).toBe(true);
- });
-
- it('sets value of `state.showDropdownContentsCreateView` to `false` when `showDropdownContents` is true', () => {
- const state = {
- showDropdownContents: false,
- showDropdownContentsCreateView: true,
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
-
- expect(state.showDropdownContentsCreateView).toBe(false);
- });
- });
-
- describe(`${types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW}`, () => {
- it('toggles value of `state.showDropdownContentsCreateView`', () => {
- const state = {
- showDropdownContentsCreateView: false,
- };
- mutations[types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state);
-
- expect(state.showDropdownContentsCreateView).toBe(true);
- });
- });
-
- describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
- let labels;
-
- beforeEach(() => {
- labels = [
- { id: 1, title: 'scoped::test', set: true },
- { id: 2, set: false, title: 'scoped::one' },
- { id: 3, title: '' },
- { id: 4, title: '' },
- ];
- });
-
- it('updates `state.labels` to include `touched` and `set` props based on provided `labels` param', () => {
- const updatedLabelIds = [2];
- const state = {
- labels,
- };
- mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: 2 }] });
-
- state.labels.forEach((label) => {
- if (updatedLabelIds.includes(label.id)) {
- expect(label.touched).toBe(true);
- expect(label.set).toBe(true);
- }
- });
- });
-
- describe('when label is scoped', () => {
- it('unsets the currently selected scoped label and sets the current label', () => {
- const state = {
- labels,
- };
- mutations[types.UPDATE_SELECTED_LABELS](state, {
- labels: [{ id: 2, title: 'scoped::one' }],
- });
-
- expect(state.labels).toEqual([
- { id: 1, title: 'scoped::test', set: false },
- { id: 2, set: true, title: 'scoped::one', touched: true },
- { id: 3, title: '' },
- { id: 4, title: '' },
- ]);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/storage_counter/usage_graph_spec.js b/spec/frontend/vue_shared/components/storage_counter/usage_graph_spec.js
new file mode 100644
index 00000000000..103eee4b9a8
--- /dev/null
+++ b/spec/frontend/vue_shared/components/storage_counter/usage_graph_spec.js
@@ -0,0 +1,137 @@
+import { shallowMount } from '@vue/test-utils';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import UsageGraph from '~/vue_shared/components/storage_counter/usage_graph.vue';
+
+let data;
+let wrapper;
+
+function mountComponent({ rootStorageStatistics, limit }) {
+ wrapper = shallowMount(UsageGraph, {
+ propsData: {
+ rootStorageStatistics,
+ limit,
+ },
+ });
+}
+function findStorageTypeUsagesSerialized() {
+ return wrapper
+ .findAll('[data-testid="storage-type-usage"]')
+ .wrappers.map((wp) => wp.element.style.flex);
+}
+
+describe('Storage Counter usage graph component', () => {
+ beforeEach(() => {
+ data = {
+ rootStorageStatistics: {
+ wikiSize: 5000,
+ repositorySize: 4000,
+ packagesSize: 3000,
+ lfsObjectsSize: 2000,
+ buildArtifactsSize: 500,
+ pipelineArtifactsSize: 500,
+ snippetsSize: 2000,
+ storageSize: 17000,
+ uploadsSize: 1000,
+ },
+ limit: 2000,
+ };
+ mountComponent(data);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the legend in order', () => {
+ const types = wrapper.findAll('[data-testid="storage-type-legend"]');
+
+ const {
+ buildArtifactsSize,
+ pipelineArtifactsSize,
+ lfsObjectsSize,
+ packagesSize,
+ repositorySize,
+ wikiSize,
+ snippetsSize,
+ uploadsSize,
+ } = data.rootStorageStatistics;
+
+ expect(types.at(0).text()).toMatchInterpolatedText(`Wikis ${numberToHumanSize(wikiSize)}`);
+ expect(types.at(1).text()).toMatchInterpolatedText(
+ `Repositories ${numberToHumanSize(repositorySize)}`,
+ );
+ expect(types.at(2).text()).toMatchInterpolatedText(
+ `Packages ${numberToHumanSize(packagesSize)}`,
+ );
+ expect(types.at(3).text()).toMatchInterpolatedText(
+ `LFS Objects ${numberToHumanSize(lfsObjectsSize)}`,
+ );
+ expect(types.at(4).text()).toMatchInterpolatedText(
+ `Snippets ${numberToHumanSize(snippetsSize)}`,
+ );
+ expect(types.at(5).text()).toMatchInterpolatedText(
+ `Artifacts ${numberToHumanSize(buildArtifactsSize + pipelineArtifactsSize)}`,
+ );
+ expect(types.at(6).text()).toMatchInterpolatedText(`Uploads ${numberToHumanSize(uploadsSize)}`);
+ });
+
+ describe('when storage type is not used', () => {
+ beforeEach(() => {
+ data.rootStorageStatistics.wikiSize = 0;
+ mountComponent(data);
+ });
+
+ it('filters the storage type', () => {
+ expect(wrapper.text()).not.toContain('Wikis');
+ });
+ });
+
+ describe('when there is no storage usage', () => {
+ beforeEach(() => {
+ data.rootStorageStatistics.storageSize = 0;
+ mountComponent(data);
+ });
+
+ it('it does not render', () => {
+ expect(wrapper.html()).toEqual('');
+ });
+ });
+
+ describe('when limit is 0', () => {
+ beforeEach(() => {
+ data.limit = 0;
+ mountComponent(data);
+ });
+
+ it('sets correct flex values', () => {
+ expect(findStorageTypeUsagesSerialized()).toStrictEqual([
+ '0.29411764705882354',
+ '0.23529411764705882',
+ '0.17647058823529413',
+ '0.11764705882352941',
+ '0.11764705882352941',
+ '0.058823529411764705',
+ '0.058823529411764705',
+ ]);
+ });
+ });
+
+ describe('when storage exceeds limit', () => {
+ beforeEach(() => {
+ data.limit = data.rootStorageStatistics.storageSize - 1;
+ mountComponent(data);
+ });
+
+ it('it does render correclty', () => {
+ expect(findStorageTypeUsagesSerialized()).toStrictEqual([
+ '0.29411764705882354',
+ '0.23529411764705882',
+ '0.17647058823529413',
+ '0.11764705882352941',
+ '0.11764705882352941',
+ '0.058823529411764705',
+ '0.058823529411764705',
+ ]);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
index 538e67ef354..926223e0670 100644
--- a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
@@ -94,7 +94,7 @@ describe('User Popover Component', () => {
const bio = 'My super interesting bio';
it('should show only bio if work information is not available', () => {
- const user = { ...DEFAULT_PROPS.user, bio, bioHtml: bio };
+ const user = { ...DEFAULT_PROPS.user, bio };
createWrapper({ user });
@@ -117,7 +117,6 @@ describe('User Popover Component', () => {
const user = {
...DEFAULT_PROPS.user,
bio,
- bioHtml: bio,
workInformation: 'Frontend Engineer at GitLab',
};
@@ -127,16 +126,15 @@ describe('User Popover Component', () => {
expect(findWorkInformation().text()).toBe('Frontend Engineer at GitLab');
});
- it('should not encode special characters in bio', () => {
+ it('should encode special characters in bio', () => {
const user = {
...DEFAULT_PROPS.user,
- bio: 'I like CSS',
- bioHtml: 'I like <b>CSS</b>',
+ bio: 'I like <b>CSS</b>',
};
createWrapper({ user });
- expect(findBio().html()).toContain('I like <b>CSS</b>');
+ expect(findBio().html()).toContain('I like &lt;b&gt;CSS&lt;/b&gt;');
});
it('shows icon for bio', () => {
@@ -250,6 +248,13 @@ describe('User Popover Component', () => {
const securityBotDocsLink = findSecurityBotDocsLink();
expect(securityBotDocsLink.exists()).toBe(true);
expect(securityBotDocsLink.attributes('href')).toBe(SECURITY_BOT_USER.websiteUrl);
+ expect(securityBotDocsLink.text()).toBe('Learn more about GitLab Security Bot');
+ });
+
+ it("doesn't escape user's name", () => {
+ createWrapper({ user: { ...SECURITY_BOT_USER, name: '%<>\';"' } });
+ const securityBotDocsLink = findSecurityBotDocsLink();
+ expect(securityBotDocsLink.text()).toBe('Learn more about %<>\';"');
});
});
});