Commit bd88ae75 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 1a12d008 afec9e8d
/* eslint-disable @gitlab/require-i18n-strings */ /* eslint-disable @gitlab/require-i18n-strings */
import { __ } from '~/locale'; import { __ } from '~/locale';
export const ANY_AUTHOR = 'Any';
const DEFAULT_LABEL_NO_LABEL = { value: 'No label', text: __('No label') }; const DEFAULT_LABEL_NO_LABEL = { value: 'No label', text: __('No label') };
export const DEFAULT_LABEL_NONE = { value: 'None', text: __('None') }; export const DEFAULT_LABEL_NONE = { value: 'None', text: __('None') };
export const DEFAULT_LABEL_ANY = { value: 'Any', text: __('Any') }; export const DEFAULT_LABEL_ANY = { value: 'Any', text: __('Any') };
......
...@@ -11,10 +11,9 @@ import { debounce } from 'lodash'; ...@@ -11,10 +11,9 @@ import { debounce } from 'lodash';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { ANY_AUTHOR, DEBOUNCE_DELAY } from '../constants'; import { DEFAULT_LABEL_ANY, DEBOUNCE_DELAY } from '../constants';
export default { export default {
anyAuthor: ANY_AUTHOR,
components: { components: {
GlFilteredSearchToken, GlFilteredSearchToken,
GlAvatar, GlAvatar,
...@@ -35,6 +34,7 @@ export default { ...@@ -35,6 +34,7 @@ export default {
data() { data() {
return { return {
authors: this.config.initialAuthors || [], authors: this.config.initialAuthors || [],
defaultAuthors: this.config.defaultAuthors || [DEFAULT_LABEL_ANY],
loading: true, loading: true,
}; };
}, },
...@@ -99,10 +99,14 @@ export default { ...@@ -99,10 +99,14 @@ export default {
<span>{{ activeAuthor ? activeAuthor.name : inputValue }}</span> <span>{{ activeAuthor ? activeAuthor.name : inputValue }}</span>
</template> </template>
<template #suggestions> <template #suggestions>
<gl-filtered-search-suggestion :value="$options.anyAuthor"> <gl-filtered-search-suggestion
{{ __('Any') }} v-for="author in defaultAuthors"
:key="author.value"
:value="author.value"
>
{{ author.text }}
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-deprecated-dropdown-divider /> <gl-deprecated-dropdown-divider v-if="defaultAuthors.length" />
<gl-loading-icon v-if="loading" /> <gl-loading-icon v-if="loading" />
<template v-else> <template v-else>
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
......
...@@ -112,7 +112,7 @@ export default { ...@@ -112,7 +112,7 @@ export default {
> >
{{ label.text }} {{ label.text }}
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-dropdown-divider /> <gl-dropdown-divider v-if="defaultLabels.length" />
<gl-loading-icon v-if="loading" /> <gl-loading-icon v-if="loading" />
<template v-else> <template v-else>
<gl-filtered-search-suggestion v-for="label in labels" :key="label.id" :value="label.title"> <gl-filtered-search-suggestion v-for="label in labels" :key="label.id" :value="label.title">
......
...@@ -95,7 +95,7 @@ export default { ...@@ -95,7 +95,7 @@ export default {
> >
{{ milestone.text }} {{ milestone.text }}
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-dropdown-divider /> <gl-dropdown-divider v-if="defaultMilestones.length" />
<gl-loading-icon v-if="loading" /> <gl-loading-icon v-if="loading" />
<template v-else> <template v-else>
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
......
- page_description brand_title unless page_description - page_description brand_title unless page_description
-# Needs a redirect on the client side since it's using an anchor to distuingish -# Needs a redirect on the client side since it's using an anchor to distinguish
-# between sign in and registration. We need to inline the JS to not render -# between sign in and registration. We need to inline the JS to not render
-# anything from this page beforehand. -# anything from this page beforehand.
-# Part of an experiment to build a new sign up flow. Will be removed again with -# Part of an experiment to build a new sign up flow. Will be removed again with
......
---
title: Update lodash to 4.17.20
merge_request: 41036
author: Takuya Noguchi
type: security
...@@ -154,7 +154,12 @@ export default { ...@@ -154,7 +154,12 @@ export default {
<label for="iteration-title">{{ __('Title') }}</label> <label for="iteration-title">{{ __('Title') }}</label>
</div> </div>
<div class="col-sm-10"> <div class="col-sm-10">
<gl-form-input id="iteration-title" v-model="title" autocomplete="off" /> <gl-form-input
id="iteration-title"
v-model="title"
autocomplete="off"
data-qa-selector="iteration_title_field"
/>
</div> </div>
</div> </div>
...@@ -181,6 +186,7 @@ export default { ...@@ -181,6 +186,7 @@ export default {
dir="auto" dir="auto"
data-supports-quick-actions="false" data-supports-quick-actions="false"
:aria-label="__('Description')" :aria-label="__('Description')"
data-qa-selector="iteration_description_field"
> >
</textarea> </textarea>
</template> </template>
...@@ -201,6 +207,7 @@ export default { ...@@ -201,6 +207,7 @@ export default {
class="datepicker form-control" class="datepicker form-control"
:placeholder="__('Select start date')" :placeholder="__('Select start date')"
autocomplete="off" autocomplete="off"
data-qa-selector="iteration_start_date_field"
@change="updateStartDate" @change="updateStartDate"
/> />
<a class="inline float-right gl-mt-2 js-clear-start-date" href="#">{{ <a class="inline float-right gl-mt-2 js-clear-start-date" href="#">{{
...@@ -219,6 +226,7 @@ export default { ...@@ -219,6 +226,7 @@ export default {
class="datepicker form-control" class="datepicker form-control"
:placeholder="__('Select due date')" :placeholder="__('Select due date')"
autocomplete="off" autocomplete="off"
data-qa-selector="iteration_due_date_field"
@change="updateDueDate" @change="updateDueDate"
/> />
<a class="inline float-right gl-mt-2 js-clear-due-date" href="#">{{ <a class="inline float-right gl-mt-2 js-clear-due-date" href="#">{{
...@@ -230,12 +238,18 @@ export default { ...@@ -230,12 +238,18 @@ export default {
</gl-form> </gl-form>
<div class="form-actions d-flex"> <div class="form-actions d-flex">
<gl-button :loading="loading" data-testid="save-iteration" variant="success" @click="save">{{ <gl-button
isEditing ? __('Update iteration') : __('Create iteration') :loading="loading"
}}</gl-button> data-testid="save-iteration"
<gl-button class="ml-auto" data-testid="cancel-iteration" @click="cancel">{{ variant="success"
__('Cancel') data-qa-selector="save_iteration_button"
}}</gl-button> @click="save"
>
{{ isEditing ? __('Update iteration') : __('Create iteration') }}
</gl-button>
<gl-button class="ml-auto" data-testid="cancel-iteration" @click="cancel">
{{ __('Cancel') }}
</gl-button>
</div> </div>
</div> </div>
</template> </template>
...@@ -193,12 +193,17 @@ export default { ...@@ -193,12 +193,17 @@ export default {
:show-empty="true" :show-empty="true"
fixed fixed
stacked="sm" stacked="sm"
data-qa-selector="iteration_issues_container"
> >
<template #cell(title)="{ item: { iid, title, webUrl } }"> <template #cell(title)="{ item: { iid, title, webUrl } }">
<div class="gl-text-truncate"> <div class="gl-text-truncate">
<gl-link class="gl-text-gray-900 gl-font-weight-bold" :href="webUrl">{{ <gl-link
title class="gl-text-gray-900 gl-font-weight-bold"
}}</gl-link> :href="webUrl"
data-qa-selector="iteration_issue_link"
:data-qa-issue-title="title"
>{{ title }}</gl-link
>
<!-- TODO: add references.relative (project name) --> <!-- TODO: add references.relative (project name) -->
<!-- Depends on https://gitlab.com/gitlab-org/gitlab/-/issues/222763 --> <!-- Depends on https://gitlab.com/gitlab-org/gitlab/-/issues/222763 -->
<div class="gl-text-secondary">#{{ iid }}</div> <div class="gl-text-secondary">#{{ iid }}</div>
......
...@@ -166,7 +166,13 @@ export default { ...@@ -166,7 +166,13 @@ export default {
</gl-tab> </gl-tab>
<template v-if="canAdmin" #tabs-end> <template v-if="canAdmin" #tabs-end>
<li class="gl-ml-auto gl-display-flex gl-align-items-center"> <li class="gl-ml-auto gl-display-flex gl-align-items-center">
<gl-button variant="success" :href="newIterationPath">{{ __('New iteration') }}</gl-button> <gl-button
variant="success"
data-qa-selector="new_iteration_button"
:href="newIterationPath"
>
{{ __('New iteration') }}
</gl-button>
</li> </li>
</template> </template>
</gl-tabs> </gl-tabs>
......
...@@ -9,7 +9,7 @@ import { updateHistory, setUrlParams } from '~/lib/utils/url_utility'; ...@@ -9,7 +9,7 @@ import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'; import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import { ANY_AUTHOR } from '~/vue_shared/components/filtered_search_bar/constants'; import { DEFAULT_LABEL_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import RequirementsTabs from './requirements_tabs.vue'; import RequirementsTabs from './requirements_tabs.vue';
import RequirementsLoading from './requirements_loading.vue'; import RequirementsLoading from './requirements_loading.vue';
...@@ -472,7 +472,7 @@ export default { ...@@ -472,7 +472,7 @@ export default {
filters.forEach(filter => { filters.forEach(filter => {
if (typeof filter === 'string') { if (typeof filter === 'string') {
textSearch = filter; textSearch = filter;
} else if (filter.value.data !== ANY_AUTHOR) { } else if (filter.value.data !== DEFAULT_LABEL_ANY.value) {
authors.push(filter.value.data); authors.push(filter.value.data);
} }
}); });
......
- if group_sidebar_link?(:iterations) - if group_sidebar_link?(:iterations)
= nav_link(path: 'iterations#index') do = nav_link(path: 'iterations#index') do
= link_to group_iterations_path(@group) do = link_to group_iterations_path(@group), data: { qa_selector: 'group_iterations_link' } do
%span %span
= _('Iterations') = _('Iterations')
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'epics swimlanes', :js do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, group: group) }
let_it_be(:board) { create(:board, project: project) }
let_it_be(:label) { create(:label, project: project, name: 'Label 1') }
let_it_be(:list) { create(:list, board: board, label: label, position: 0) }
let_it_be(:issue1) { create(:issue, project: project, labels: [label]) }
let_it_be(:issue2) { create(:issue, project: project) }
let_it_be(:issue3) { create(:issue, project: project) }
let_it_be(:epic1) { create(:epic, group: group) }
let_it_be(:epic2) { create(:epic, group: group) }
let_it_be(:epic_issue1) { create(:epic_issue, epic: epic1, issue: issue1) }
let_it_be(:epic_issue2) { create(:epic_issue, epic: epic2, issue: issue2) }
context 'switch to swimlanes view' do
context 'feature flag on' do
before do
stub_licensed_features(epics: true)
sign_in(user)
visit_board_page
page.within('.board-swimlanes-toggle-wrapper') do
page.find('.dropdown-toggle').click
page.find('.dropdown-item', text: 'Epic').click
end
end
it 'displays epics swimlanes when selecting Epic in Group by dropdown' do
expect(page).to have_css('.board-swimlanes')
epic_lanes = page.all(:css, '.board-epic-lane')
expect(epic_lanes.length).to eq(2)
end
it 'displays issue not assigned to epic in unassigned issues lane' do
page.within('.board-lane-unassigned-issues') do
expect(page.find('span[data-testid="issues-lane-issue-count"]')).to have_content('1')
end
end
end
end
def visit_board_page
visit project_boards_path(project)
wait_for_requests
end
end
...@@ -106,7 +106,7 @@ ...@@ -106,7 +106,7 @@
"jszip": "^3.1.3", "jszip": "^3.1.3",
"jszip-utils": "^0.0.2", "jszip-utils": "^0.0.2",
"katex": "^0.10.0", "katex": "^0.10.0",
"lodash": "^4.17.15", "lodash": "^4.17.20",
"marked": "^0.3.12", "marked": "^0.3.12",
"mermaid": "^8.5.2", "mermaid": "^8.5.2",
"mersenne-twister": "1.1.0", "mersenne-twister": "1.1.0",
......
...@@ -38,6 +38,12 @@ module QA ...@@ -38,6 +38,12 @@ module QA
autoload :Members, 'qa/ee/page/group/members' autoload :Members, 'qa/ee/page/group/members'
autoload :ContributionAnalytics, 'qa/ee/page/group/contribution_analytics' autoload :ContributionAnalytics, 'qa/ee/page/group/contribution_analytics'
module Iteration
autoload :Index, 'qa/ee/page/group/iteration/index'
autoload :New, 'qa/ee/page/group/iteration/new'
autoload :Show, 'qa/ee/page/group/iteration/show'
end
module Settings module Settings
autoload :SamlSSO, 'qa/ee/page/group/settings/saml_sso' autoload :SamlSSO, 'qa/ee/page/group/settings/saml_sso'
autoload :LDAPSync, 'qa/ee/page/group/settings/ldap_sync' autoload :LDAPSync, 'qa/ee/page/group/settings/ldap_sync'
...@@ -188,7 +194,8 @@ module QA ...@@ -188,7 +194,8 @@ module QA
module Resource module Resource
autoload :License, 'qa/ee/resource/license' autoload :License, 'qa/ee/resource/license'
autoload :Epic, 'qa/ee/resource/epic' autoload :Epic, 'qa/ee/resource/epic'
autoload :GroupLabel, 'qa/ee/resource/group_label.rb' autoload :GroupLabel, 'qa/ee/resource/group_label'
autoload :GroupIteration, 'qa/ee/resource/group_iteration'
module Board module Board
autoload :BaseBoard, 'qa/ee/resource/board/base_board' autoload :BaseBoard, 'qa/ee/resource/board/base_board'
......
# frozen_string_literal: true
module QA
module EE
module Page
module Group
module Iteration
class Index < QA::Page::Base
view 'ee/app/assets/javascripts/iterations/components/iterations.vue' do
element :new_iteration_button
end
def click_new_iteration_button
click_element(:new_iteration_button, EE::Page::Group::Iteration::New)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Group
module Iteration
class New < QA::Page::Base
view 'ee/app/assets/javascripts/iterations/components/iteration_form.vue' do
element :iteration_description_field
element :iteration_due_date_field
element :iteration_start_date_field
element :iteration_title_field, required: true
element :save_iteration_button
end
def click_create_iteration_button
click_element(:save_iteration_button, EE::Page::Group::Iteration::Show)
end
def fill_description(description)
fill_element(:iteration_description_field, description)
end
def fill_due_date(due_date)
fill_element(:iteration_due_date_field, due_date)
end
def fill_start_date(start_date)
fill_element(:iteration_start_date_field, start_date)
end
def fill_title(title)
fill_element(:iteration_title_field, title)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Group
module Iteration
class Show < QA::Page::Base
view 'ee/app/assets/javascripts/iterations/components/iteration_report_tabs.vue' do
element :iteration_issues_container, required: true
element :iteration_issue_link
end
def has_issue?(issue)
within_element(:iteration_issues_container) do
has_element?(:iteration_issue_link, issue_title: issue.title)
end
end
end
end
end
end
end
end
...@@ -13,6 +13,24 @@ module QA ...@@ -13,6 +13,24 @@ module QA
base.class_eval do base.class_eval do
prepend QA::Page::Group::SubMenus::Common prepend QA::Page::Group::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_group.html.haml' do
element :group_issue_boards_link
element :group_issues_item
element :group_sidebar
element :group_sidebar_submenu
element :group_settings_item
end
view 'app/views/layouts/nav/sidebar/_wiki_link.html.haml' do
element :wiki_link
end
view 'ee/app/views/groups/ee/_administration_nav.html.haml' do
element :group_administration_link
element :group_sidebar_submenu_content
element :group_saml_sso_link
end
view 'ee/app/views/groups/ee/_settings_nav.html.haml' do view 'ee/app/views/groups/ee/_settings_nav.html.haml' do
element :ldap_synchronization_link element :ldap_synchronization_link
element :audit_events_settings_link element :audit_events_settings_link
...@@ -32,22 +50,8 @@ module QA ...@@ -32,22 +50,8 @@ module QA
element :group_insights_link element :group_insights_link
end end
view 'app/views/layouts/nav/sidebar/_group.html.haml' do view 'ee/app/views/layouts/nav/sidebar/_group_iterations_link.html.haml' do
element :group_issue_boards_link element :group_iterations_link
element :group_issues_item
element :group_sidebar
element :group_sidebar_submenu
element :group_settings_item
end
view 'ee/app/views/groups/ee/_administration_nav.html.haml' do
element :group_administration_link
element :group_sidebar_submenu_content
element :group_saml_sso_link
end
view 'app/views/layouts/nav/sidebar/_wiki_link.html.haml' do
element :wiki_link
end end
end end
end end
...@@ -68,6 +72,14 @@ module QA ...@@ -68,6 +72,14 @@ module QA
end end
end end
def go_to_group_iterations
hover_element(:group_issues_item) do
within_submenu(:group_issues_sidebar_submenu) do
click_element(:group_iterations_link)
end
end
end
def go_to_saml_sso_group_settings def go_to_saml_sso_group_settings
hover_element(:group_administration_link) do hover_element(:group_administration_link) do
within_submenu(:group_sidebar_submenu_content) do within_submenu(:group_sidebar_submenu_content) do
......
# frozen_string_literal: true
module QA
module EE
module Resource
class GroupIteration < QA::Resource::Base
include Support::Dates
attr_accessor :title
attribute :group do
QA::Resource::Group.fabricate_via_api!
end
attribute :id
attribute :start_date
attribute :due_date
attribute :description
attribute :title
def initialize
@start_date = current_date_yyyy_mm_dd
@due_date = next_month_yyyy_mm_dd
@title = "Iteration-#{SecureRandom.hex(8)}"
@description = "This is a test iteration."
end
def fabricate!
group.visit!
QA::Page::Group::Menu.perform(&:go_to_group_iterations)
QA::EE::Page::Group::Iteration::Index.perform(&:click_new_iteration_button)
QA::EE::Page::Group::Iteration::New.perform do |new|
new.fill_title(@title)
new.fill_description(@description)
new.fill_start_date(@start_date)
new.fill_due_date(@due_date)
new.click_create_iteration_button
end
end
end
end
end
end
# frozen_string_literal: true
module QA
RSpec.describe 'Plan' do
describe 'Group Iterations' do
include Support::Dates
let(:title) { "Group iteration created via GUI #{SecureRandom.hex(8)}" }
let(:start_date) { current_date_yyyy_mm_dd }
let(:due_date) { next_month_yyyy_mm_dd }
let(:description) { "This is a group test iteration." }
before do
Flow::Login.sign_in
end
it 'creates a group iteration', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/957' do
EE::Resource::GroupIteration.fabricate_via_browser_ui! do |iteration|
iteration.title = title
iteration.description = description
iteration.due_date = due_date
iteration.start_date = start_date
end
EE::Page::Group::Iteration::Show.perform do |iteration|
aggregate_failures "iteration created successfully" do
expect(iteration).to have_content(title)
expect(iteration).to have_content(description)
expect(iteration).to have_content(format_date(start_date))
expect(iteration).to have_content(format_date(due_date))
end
end
end
end
end
end
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui'; import {
GlFilteredSearchToken,
GlFilteredSearchTokenSegment,
GlFilteredSearchSuggestion,
GlNewDropdownDivider as GlDropdownDivider,
} from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as createFlash } from '~/flash'; 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 AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import { mockAuthorToken, mockAuthors } from '../mock_data'; import { mockAuthorToken, mockAuthors } from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
const defaultStubs = {
const createComponent = ({ config = mockAuthorToken, value = { data: '' }, active = false } = {}) => Portal: true,
mount(AuthorToken, { GlFilteredSearchSuggestionList: {
template: '<div></div>',
methods: {
getValue: () => '=',
},
},
};
function createComponent(options = {}) {
const {
config = mockAuthorToken,
value = { data: '' },
active = false,
stubs = defaultStubs,
} = options;
return mount(AuthorToken, {
propsData: { propsData: {
config, config,
value, value,
...@@ -22,16 +46,9 @@ const createComponent = ({ config = mockAuthorToken, value = { data: '' }, activ ...@@ -22,16 +46,9 @@ const createComponent = ({ config = mockAuthorToken, value = { data: '' }, activ
portalName: 'fake target', portalName: 'fake target',
alignSuggestions: function fakeAlignSuggestions() {}, alignSuggestions: function fakeAlignSuggestions() {},
}, },
stubs: { stubs,
Portal: true,
GlFilteredSearchSuggestionList: {
template: '<div></div>',
methods: {
getValue: () => '=',
},
},
},
}); });
}
describe('AuthorToken', () => { describe('AuthorToken', () => {
let mock; let mock;
...@@ -139,5 +156,57 @@ describe('AuthorToken', () => { ...@@ -139,5 +156,57 @@ describe('AuthorToken', () => {
expect(tokenSegments.at(2).text()).toBe(mockAuthors[0].name); // "Administrator" 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);
});
}); });
}); });
...@@ -3,6 +3,7 @@ import { ...@@ -3,6 +3,7 @@ import {
GlFilteredSearchToken, GlFilteredSearchToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlFilteredSearchTokenSegment, GlFilteredSearchTokenSegment,
GlNewDropdownDivider as GlDropdownDivider,
} from '@gitlab/ui'; } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
...@@ -33,13 +34,14 @@ const defaultStubs = { ...@@ -33,13 +34,14 @@ const defaultStubs = {
}, },
}; };
const createComponent = ({ function createComponent(options = {}) {
const {
config = mockLabelToken, config = mockLabelToken,
value = { data: '' }, value = { data: '' },
active = false, active = false,
stubs = defaultStubs, stubs = defaultStubs,
} = {}) => } = options;
mount(LabelToken, { return mount(LabelToken, {
propsData: { propsData: {
config, config,
value, value,
...@@ -51,6 +53,7 @@ const createComponent = ({ ...@@ -51,6 +53,7 @@ const createComponent = ({
}, },
stubs, stubs,
}); });
}
describe('LabelToken', () => { describe('LabelToken', () => {
let mock; let mock;
...@@ -204,6 +207,21 @@ describe('LabelToken', () => { ...@@ -204,6 +207,21 @@ describe('LabelToken', () => {
}); });
}); });
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 () => { it('renders `DEFAULT_LABELS` as default suggestions', async () => {
wrapper = createComponent({ wrapper = createComponent({
active: true, active: true,
......
...@@ -3,6 +3,7 @@ import { ...@@ -3,6 +3,7 @@ import {
GlFilteredSearchToken, GlFilteredSearchToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlFilteredSearchTokenSegment, GlFilteredSearchTokenSegment,
GlNewDropdownDivider as GlDropdownDivider,
} from '@gitlab/ui'; } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
...@@ -31,13 +32,14 @@ const defaultStubs = { ...@@ -31,13 +32,14 @@ const defaultStubs = {
}, },
}; };
const createComponent = ({ function createComponent(options = {}) {
const {
config = mockMilestoneToken, config = mockMilestoneToken,
value = { data: '' }, value = { data: '' },
active = false, active = false,
stubs = defaultStubs, stubs = defaultStubs,
} = {}) => } = options;
mount(MilestoneToken, { return mount(MilestoneToken, {
propsData: { propsData: {
config, config,
value, value,
...@@ -49,6 +51,7 @@ const createComponent = ({ ...@@ -49,6 +51,7 @@ const createComponent = ({
}, },
stubs, stubs,
}); });
}
describe('MilestoneToken', () => { describe('MilestoneToken', () => {
let mock; let mock;
...@@ -176,6 +179,21 @@ describe('MilestoneToken', () => { ...@@ -176,6 +179,21 @@ describe('MilestoneToken', () => {
}); });
}); });
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 () => { it('renders `DEFAULT_MILESTONES` as default suggestions', async () => {
wrapper = createComponent({ wrapper = createComponent({
active: true, active: true,
......
...@@ -7744,10 +7744,10 @@ lodash.values@^4.3.0: ...@@ -7744,10 +7744,10 @@ lodash.values@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c= integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@~4.17.10: lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@~4.17.10:
version "4.17.15" version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
log-symbols@^2.1.0, log-symbols@^2.2.0: log-symbols@^2.1.0, log-symbols@^2.2.0:
version "2.2.0" version "2.2.0"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment