diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /spec/frontend/vue_shared/components/filtered_search_bar/tokens | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) | |
download | gitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'spec/frontend/vue_shared/components/filtered_search_bar/tokens')
4 files changed, 462 insertions, 50 deletions
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js index 160febf9d06..72840ce381f 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js @@ -1,18 +1,42 @@ import { mount } from '@vue/test-utils'; -import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui'; +import { + GlFilteredSearchToken, + GlFilteredSearchTokenSegment, + GlFilteredSearchSuggestion, + GlDropdownDivider, +} from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import { deprecatedCreateFlash as createFlash } from '~/flash'; +import { + DEFAULT_LABEL_NONE, + DEFAULT_LABEL_ANY, +} from '~/vue_shared/components/filtered_search_bar/constants'; import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'; import { mockAuthorToken, mockAuthors } from '../mock_data'; jest.mock('~/flash'); - -const createComponent = ({ config = mockAuthorToken, value = { data: '' }, active = false } = {}) => - mount(AuthorToken, { +const defaultStubs = { + Portal: true, + GlFilteredSearchSuggestionList: { + template: '<div></div>', + methods: { + getValue: () => '=', + }, + }, +}; + +function createComponent(options = {}) { + const { + config = mockAuthorToken, + value = { data: '' }, + active = false, + stubs = defaultStubs, + } = options; + return mount(AuthorToken, { propsData: { config, value, @@ -22,18 +46,9 @@ const createComponent = ({ config = mockAuthorToken, value = { data: '' }, activ portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, }, - stubs: { - Portal: { - template: '<div><slot></slot></div>', - }, - GlFilteredSearchSuggestionList: { - template: '<div></div>', - methods: { - getValue: () => '=', - }, - }, - }, + stubs, }); +} describe('AuthorToken', () => { let mock; @@ -141,5 +156,57 @@ describe('AuthorToken', () => { expect(tokenSegments.at(2).text()).toBe(mockAuthors[0].name); // "Administrator" }); }); + + it('renders provided defaultAuthors as suggestions', async () => { + const defaultAuthors = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY]; + wrapper = createComponent({ + active: true, + config: { ...mockAuthorToken, defaultAuthors }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(defaultAuthors.length); + defaultAuthors.forEach((label, index) => { + expect(suggestions.at(index).text()).toBe(label.text); + }); + }); + + it('does not render divider when no defaultAuthors', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockAuthorToken, defaultAuthors: [] }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + expect(wrapper.contains(GlFilteredSearchSuggestion)).toBe(false); + expect(wrapper.contains(GlDropdownDivider)).toBe(false); + }); + + it('renders `DEFAULT_LABEL_ANY` as default suggestions', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockAuthorToken }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(1); + expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_ANY.text); + }); }); }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js new file mode 100644 index 00000000000..12b7fd58670 --- /dev/null +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js @@ -0,0 +1,207 @@ +import { mount } from '@vue/test-utils'; +import { + GlFilteredSearchToken, + GlFilteredSearchSuggestion, + GlFilteredSearchTokenSegment, + GlDropdownDivider, +} from '@gitlab/ui'; +import MockAdapter from 'axios-mock-adapter'; +import waitForPromises from 'helpers/wait_for_promises'; + +import axios from '~/lib/utils/axios_utils'; +import createFlash from '~/flash'; +import { + DEFAULT_LABEL_NONE, + DEFAULT_LABEL_ANY, +} from '~/vue_shared/components/filtered_search_bar/constants'; +import BranchToken from '~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue'; + +import { mockBranches, mockBranchToken } from '../mock_data'; + +jest.mock('~/flash'); +const defaultStubs = { + Portal: true, + GlFilteredSearchSuggestionList: { + template: '<div></div>', + methods: { + getValue: () => '=', + }, + }, +}; + +function createComponent(options = {}) { + const { + config = mockBranchToken, + value = { data: '' }, + active = false, + stubs = defaultStubs, + } = options; + return mount(BranchToken, { + propsData: { + config, + value, + active, + }, + provide: { + portalName: 'fake target', + alignSuggestions: function fakeAlignSuggestions() {}, + }, + stubs, + }); +} + +describe('BranchToken', () => { + let mock; + let wrapper; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + wrapper.destroy(); + }); + + describe('computed', () => { + beforeEach(async () => { + wrapper = createComponent({ value: { data: mockBranches[0].name } }); + + wrapper.setData({ + branches: mockBranches, + }); + + await wrapper.vm.$nextTick(); + }); + + describe('currentValue', () => { + it('returns lowercase string for `value.data`', () => { + expect(wrapper.vm.currentValue).toBe('master'); + }); + }); + + describe('activeBranch', () => { + it('returns object for currently present `value.data`', () => { + expect(wrapper.vm.activeBranch).toEqual(mockBranches[0]); + }); + }); + }); + + describe('methods', () => { + beforeEach(() => { + wrapper = createComponent(); + }); + + describe('fetchBranchBySearchTerm', () => { + it('calls `config.fetchBranches` with provided searchTerm param', () => { + jest.spyOn(wrapper.vm.config, 'fetchBranches'); + + wrapper.vm.fetchBranchBySearchTerm('foo'); + + expect(wrapper.vm.config.fetchBranches).toHaveBeenCalledWith('foo'); + }); + + it('sets response to `branches` when request is succesful', () => { + jest.spyOn(wrapper.vm.config, 'fetchBranches').mockResolvedValue({ data: mockBranches }); + + wrapper.vm.fetchBranchBySearchTerm('foo'); + + return waitForPromises().then(() => { + expect(wrapper.vm.branches).toEqual(mockBranches); + }); + }); + + it('calls `createFlash` with flash error message when request fails', () => { + jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({}); + + wrapper.vm.fetchBranchBySearchTerm('foo'); + + return waitForPromises().then(() => { + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem fetching branches.', + }); + }); + }); + + it('sets `loading` to false when request completes', () => { + jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({}); + + wrapper.vm.fetchBranchBySearchTerm('foo'); + + return waitForPromises().then(() => { + expect(wrapper.vm.loading).toBe(false); + }); + }); + }); + }); + + describe('template', () => { + const defaultBranches = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY]; + async function showSuggestions() { + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + } + + beforeEach(async () => { + wrapper = createComponent({ value: { data: mockBranches[0].name } }); + + wrapper.setData({ + branches: mockBranches, + }); + + await wrapper.vm.$nextTick(); + }); + + it('renders gl-filtered-search-token component', () => { + expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true); + }); + + it('renders token item when value is selected', () => { + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + + expect(tokenSegments).toHaveLength(3); + expect(tokenSegments.at(2).text()).toBe(mockBranches[0].name); + }); + + it('renders provided defaultBranches as suggestions', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockBranchToken, defaultBranches }, + stubs: { Portal: true }, + }); + await showSuggestions(); + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(defaultBranches.length); + defaultBranches.forEach((branch, index) => { + expect(suggestions.at(index).text()).toBe(branch.text); + }); + }); + + it('does not render divider when no defaultBranches', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockBranchToken, defaultBranches: [] }, + stubs: { Portal: true }, + }); + await showSuggestions(); + + expect(wrapper.contains(GlFilteredSearchSuggestion)).toBe(false); + expect(wrapper.contains(GlDropdownDivider)).toBe(false); + }); + + it('renders no suggestions as default', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockBranchToken }, + stubs: { Portal: true }, + }); + await showSuggestions(); + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(0); + }); + }); +}); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js index 0e60ee99327..3feb05bab35 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js @@ -1,5 +1,10 @@ import { mount } from '@vue/test-utils'; -import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui'; +import { + GlFilteredSearchToken, + GlFilteredSearchSuggestion, + GlFilteredSearchTokenSegment, + GlDropdownDivider, +} from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import { @@ -9,14 +14,34 @@ import { import axios from '~/lib/utils/axios_utils'; import { deprecatedCreateFlash as createFlash } from '~/flash'; +import { + DEFAULT_LABELS, + DEFAULT_LABEL_NONE, + DEFAULT_LABEL_ANY, +} from '~/vue_shared/components/filtered_search_bar/constants'; import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'; import { mockLabelToken } from '../mock_data'; jest.mock('~/flash'); - -const createComponent = ({ config = mockLabelToken, value = { data: '' }, active = false } = {}) => - mount(LabelToken, { +const defaultStubs = { + Portal: true, + GlFilteredSearchSuggestionList: { + template: '<div></div>', + methods: { + getValue: () => '=', + }, + }, +}; + +function createComponent(options = {}) { + const { + config = mockLabelToken, + value = { data: '' }, + active = false, + stubs = defaultStubs, + } = options; + return mount(LabelToken, { propsData: { config, value, @@ -26,18 +51,9 @@ const createComponent = ({ config = mockLabelToken, value = { data: '' }, active portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, }, - stubs: { - Portal: { - template: '<div><slot></slot></div>', - }, - GlFilteredSearchSuggestionList: { - template: '<div></div>', - methods: { - getValue: () => '=', - }, - }, - }, + stubs, }); +} describe('LabelToken', () => { let mock; @@ -45,7 +61,6 @@ describe('LabelToken', () => { beforeEach(() => { mock = new MockAdapter(axios); - wrapper = createComponent(); }); afterEach(() => { @@ -98,6 +113,10 @@ describe('LabelToken', () => { }); describe('methods', () => { + beforeEach(() => { + wrapper = createComponent(); + }); + describe('fetchLabelBySearchTerm', () => { it('calls `config.fetchLabels` with provided searchTerm param', () => { jest.spyOn(wrapper.vm.config, 'fetchLabels'); @@ -140,6 +159,8 @@ describe('LabelToken', () => { }); describe('template', () => { + const defaultLabels = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY]; + beforeEach(async () => { wrapper = createComponent({ value: { data: `"${mockRegularLabel.title}"` } }); @@ -166,5 +187,58 @@ describe('LabelToken', () => { .attributes('style'), ).toBe('background-color: rgb(186, 218, 85); color: rgb(255, 255, 255);'); }); + + it('renders provided defaultLabels as suggestions', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockLabelToken, defaultLabels }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(defaultLabels.length); + defaultLabels.forEach((label, index) => { + expect(suggestions.at(index).text()).toBe(label.text); + }); + }); + + it('does not render divider when no defaultLabels', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockLabelToken, defaultLabels: [] }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + expect(wrapper.contains(GlFilteredSearchSuggestion)).toBe(false); + expect(wrapper.contains(GlDropdownDivider)).toBe(false); + }); + + it('renders `DEFAULT_LABELS` as default suggestions', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockLabelToken }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(DEFAULT_LABELS.length); + DEFAULT_LABELS.forEach((label, index) => { + expect(suggestions.at(index).text()).toBe(label.text); + }); + }); }); }); 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 de893bf44c8..0ec814e3f15 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 @@ -1,10 +1,16 @@ import { mount } from '@vue/test-utils'; -import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui'; +import { + GlFilteredSearchToken, + GlFilteredSearchSuggestion, + GlFilteredSearchTokenSegment, + GlDropdownDivider, +} from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; +import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants'; import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; import { @@ -16,12 +22,24 @@ import { jest.mock('~/flash'); -const createComponent = ({ - config = mockMilestoneToken, - value = { data: '' }, - active = false, -} = {}) => - mount(MilestoneToken, { +const defaultStubs = { + Portal: true, + GlFilteredSearchSuggestionList: { + template: '<div></div>', + methods: { + getValue: () => '=', + }, + }, +}; + +function createComponent(options = {}) { + const { + config = mockMilestoneToken, + value = { data: '' }, + active = false, + stubs = defaultStubs, + } = options; + return mount(MilestoneToken, { propsData: { config, value, @@ -31,18 +49,9 @@ const createComponent = ({ portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, }, - stubs: { - Portal: { - template: '<div><slot></slot></div>', - }, - GlFilteredSearchSuggestionList: { - template: '<div></div>', - methods: { - getValue: () => '=', - }, - }, - }, + stubs, }); +} describe('MilestoneToken', () => { let mock; @@ -128,6 +137,8 @@ describe('MilestoneToken', () => { }); describe('template', () => { + const defaultMilestones = [{ text: 'foo', value: 'foo' }, { text: 'bar', value: 'baz' }]; + beforeEach(async () => { wrapper = createComponent({ value: { data: `"${mockRegularMilestone.title}"` } }); @@ -146,7 +157,60 @@ describe('MilestoneToken', () => { const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // Milestone, =, '%"4.0"' - expect(tokenSegments.at(2).text()).toBe(`%"${mockRegularMilestone.title}"`); // "4.0 RC1" + expect(tokenSegments.at(2).text()).toBe(`%${mockRegularMilestone.title}`); // "4.0 RC1" + }); + + it('renders provided defaultMilestones as suggestions', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockMilestoneToken, defaultMilestones }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(defaultMilestones.length); + defaultMilestones.forEach((milestone, index) => { + expect(suggestions.at(index).text()).toBe(milestone.text); + }); + }); + + it('does not render divider when no defaultMilestones', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockMilestoneToken, defaultMilestones: [] }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + expect(wrapper.contains(GlFilteredSearchSuggestion)).toBe(false); + expect(wrapper.contains(GlDropdownDivider)).toBe(false); + }); + + it('renders `DEFAULT_MILESTONES` as default suggestions', async () => { + wrapper = createComponent({ + active: true, + config: { ...mockMilestoneToken }, + stubs: { Portal: true }, + }); + const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const suggestionsSegment = tokenSegments.at(2); + suggestionsSegment.vm.$emit('activate'); + await wrapper.vm.$nextTick(); + + const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + + expect(suggestions).toHaveLength(DEFAULT_MILESTONES.length); + DEFAULT_MILESTONES.forEach((milestone, index) => { + expect(suggestions.at(index).text()).toBe(milestone.text); + }); }); }); }); |