Commit 0aeb8866 authored by Miguel Rincon's avatar Miguel Rincon

Merge branch '262060_01-group-filter-vue' into 'master'

Global Search - Group Filter

See merge request gitlab-org/gitlab!44833
parents e1ef201b 23a81cc1
...@@ -3,5 +3,5 @@ import initSearchApp from '~/search'; ...@@ -3,5 +3,5 @@ import initSearchApp from '~/search';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initSearchApp(); initSearchApp();
return new Search(); return new Search(); // Deprecated Dropdown (Projects)
}); });
...@@ -5,48 +5,22 @@ import { deprecatedCreateFlash as Flash } from '~/flash'; ...@@ -5,48 +5,22 @@ import { deprecatedCreateFlash as Flash } from '~/flash';
import Api from '~/api'; import Api from '~/api';
import { __ } from '~/locale'; import { __ } from '~/locale';
import Project from '~/pages/projects/project'; import Project from '~/pages/projects/project';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl, queryToObject } from '~/lib/utils/url_utility';
import refreshCounts from './refresh_counts'; import refreshCounts from './refresh_counts';
export default class Search { export default class Search {
constructor() { constructor() {
setHighlightClass(); setHighlightClass(); // Code Highlighting
const $groupDropdown = $('.js-search-group-dropdown');
const $projectDropdown = $('.js-search-project-dropdown'); const $projectDropdown = $('.js-search-project-dropdown');
this.searchInput = '.js-search-input'; this.searchInput = '.js-search-input';
this.searchClear = '.js-search-clear'; this.searchClear = '.js-search-clear';
this.groupId = $groupDropdown.data('groupId'); const query = queryToObject(window.location.search);
this.groupId = query?.group_id;
this.eventListeners(); this.eventListeners();
refreshCounts(); refreshCounts();
initDeprecatedJQueryDropdown($groupDropdown, {
selectable: true,
filterable: true,
filterRemote: true,
fieldName: 'group_id',
search: {
fields: ['full_name'],
},
data(term, callback) {
return Api.groups(term, {}, data => {
data.unshift({
full_name: __('Any'),
});
data.splice(1, 0, { type: 'divider' });
return callback(data);
});
},
id(obj) {
return obj.id;
},
text(obj) {
return obj.full_name;
},
clicked: () => Search.submitSearch(),
});
initDeprecatedJQueryDropdown($projectDropdown, { initDeprecatedJQueryDropdown($projectDropdown, {
selectable: true, selectable: true,
filterable: true, filterable: true,
......
<script>
import {
GlDropdown,
GlDropdownItem,
GlSearchBoxByType,
GlLoadingIcon,
GlIcon,
GlSkeletonLoader,
GlTooltipDirective,
} from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { isEmpty } from 'lodash';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import { ANY_GROUP, GROUP_QUERY_PARAM, PROJECT_QUERY_PARAM } from '../constants';
export default {
name: 'GroupFilter',
components: {
GlDropdown,
GlDropdownItem,
GlSearchBoxByType,
GlLoadingIcon,
GlIcon,
GlSkeletonLoader,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
initialGroup: {
type: Object,
required: false,
default: () => ({}),
},
},
data() {
return {
groupSearch: '',
};
},
computed: {
...mapState(['groups', 'fetchingGroups']),
selectedGroup: {
get() {
return isEmpty(this.initialGroup) ? ANY_GROUP : this.initialGroup;
},
set(group) {
visitUrl(setUrlParams({ [GROUP_QUERY_PARAM]: group.id, [PROJECT_QUERY_PARAM]: null }));
},
},
},
methods: {
...mapActions(['fetchGroups']),
isGroupSelected(group) {
return group.id === this.selectedGroup.id;
},
handleGroupChange(group) {
this.selectedGroup = group;
},
},
ANY_GROUP,
};
</script>
<template>
<gl-dropdown
ref="groupFilter"
class="gl-w-full"
menu-class="gl-w-full!"
toggle-class="gl-text-truncate gl-reset-line-height!"
:header-text="__('Filter results by group')"
@show="fetchGroups(groupSearch)"
>
<template #button-content>
<span class="dropdown-toggle-text gl-flex-grow-1 gl-text-truncate">
{{ selectedGroup.name }}
</span>
<gl-loading-icon v-if="fetchingGroups" inline class="mr-2" />
<gl-icon
v-if="!isGroupSelected($options.ANY_GROUP)"
v-gl-tooltip
name="clear"
:title="__('Clear')"
class="gl-text-gray-200! gl-hover-text-blue-800!"
@click.stop="handleGroupChange($options.ANY_GROUP)"
/>
<gl-icon name="chevron-down" />
</template>
<div class="gl-sticky gl-top-0 gl-z-index-1 gl-bg-white">
<gl-search-box-by-type
v-model="groupSearch"
class="m-2"
:debounce="500"
@input="fetchGroups"
/>
<gl-dropdown-item
class="gl-border-b-solid gl-border-b-gray-100 gl-border-b-1 gl-pb-2! gl-mb-2"
:is-check-item="true"
:is-checked="isGroupSelected($options.ANY_GROUP)"
@click="handleGroupChange($options.ANY_GROUP)"
>
{{ $options.ANY_GROUP.name }}
</gl-dropdown-item>
</div>
<div v-if="!fetchingGroups">
<gl-dropdown-item
v-for="group in groups"
:key="group.id"
:is-check-item="true"
:is-checked="isGroupSelected(group)"
@click="handleGroupChange(group)"
>
{{ group.full_name }}
</gl-dropdown-item>
</div>
<div v-if="fetchingGroups" class="mx-3 mt-2">
<gl-skeleton-loader :height="100">
<rect y="0" width="90%" height="20" rx="4" />
<rect y="40" width="70%" height="20" rx="4" />
<rect y="80" width="80%" height="20" rx="4" />
</gl-skeleton-loader>
</div>
</gl-dropdown>
</template>
import { __ } from '~/locale';
export const ANY_GROUP = Object.freeze({
id: null,
name: __('Any'),
});
export const GROUP_QUERY_PARAM = 'group_id';
export const PROJECT_QUERY_PARAM = 'project_id';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import GroupFilter from './components/group_filter.vue';
Vue.use(Translate);
export default store => {
let initialGroup;
const el = document.getElementById('js-search-group-dropdown');
const { initialGroupData } = el.dataset;
initialGroup = JSON.parse(initialGroupData);
initialGroup = convertObjectPropsToCamelCase(initialGroup, { deep: true });
return new Vue({
el,
store,
render(createElement) {
return createElement(GroupFilter, {
props: {
initialGroup,
},
});
},
});
};
import { queryToObject } from '~/lib/utils/url_utility'; import { queryToObject } from '~/lib/utils/url_utility';
import createStore from './store'; import createStore from './store';
import initDropdownFilters from './dropdown_filter'; import initDropdownFilters from './dropdown_filter';
import initGroupFilter from './group_filter';
export default () => { export default () => {
const store = createStore({ query: queryToObject(window.location.search) }); const store = createStore({ query: queryToObject(window.location.search) });
initDropdownFilters(store); initDropdownFilters(store);
initGroupFilter(store);
}; };
import Api from '~/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
export const fetchGroups = ({ commit }, search) => {
commit(types.REQUEST_GROUPS);
Api.groups(search)
.then(data => {
commit(types.RECEIVE_GROUPS_SUCCESS, data);
})
.catch(() => {
createFlash({ message: __('There was a problem fetching groups.') });
commit(types.RECEIVE_GROUPS_ERROR);
});
};
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
import createState from './state'; import createState from './state';
Vue.use(Vuex); Vue.use(Vuex);
export const getStoreConfig = ({ query }) => ({ export const getStoreConfig = ({ query }) => ({
actions,
mutations,
state: createState({ query }), state: createState({ query }),
}); });
......
export const REQUEST_GROUPS = 'REQUEST_GROUPS';
export const RECEIVE_GROUPS_SUCCESS = 'RECEIVE_GROUPS_SUCCESS';
export const RECEIVE_GROUPS_ERROR = 'RECEIVE_GROUPS_ERROR';
import * as types from './mutation_types';
export default {
[types.REQUEST_GROUPS](state) {
state.fetchingGroups = true;
},
[types.RECEIVE_GROUPS_SUCCESS](state, data) {
state.fetchingGroups = false;
state.groups = data;
},
[types.RECEIVE_GROUPS_ERROR](state) {
state.fetchingGroups = false;
state.groups = [];
},
};
const createState = ({ query }) => ({ const createState = ({ query }) => ({
query, query,
groups: [],
fetchingGroups: false,
}); });
export default createState; export default createState;
...@@ -270,7 +270,8 @@ input[type='checkbox']:hover { ...@@ -270,7 +270,8 @@ input[type='checkbox']:hover {
width: 100%; width: 100%;
} }
.dropdown-menu-toggle { .dropdown-menu-toggle,
.gl-new-dropdown {
@include media-breakpoint-up(lg) { @include media-breakpoint-up(lg) {
width: 240px; width: 240px;
} }
......
...@@ -2,21 +2,10 @@ ...@@ -2,21 +2,10 @@
= hidden_field_tag :group_id, params[:group_id] = hidden_field_tag :group_id, params[:group_id]
- if params[:project_id].present? - if params[:project_id].present?
= hidden_field_tag :project_id, params[:project_id] = hidden_field_tag :project_id, params[:project_id]
.dropdown.form-group.mb-lg-0.mx-lg-1{ data: { testid: "group-filter" } } .dropdown.form-group.mb-lg-0.mx-lg-1.gl-p-0{ data: { testid: "group-filter" } }
%label.d-block{ for: "dashboard_search_group" } %label.d-block{ for: "dashboard_search_group" }
= _("Group") = _("Group")
%button.dropdown-menu-toggle.gl-display-inline-flex.js-search-group-dropdown.gl-mt-0{ type: "button", id: "dashboard_search_group", data: { toggle: "dropdown", group_id: params[:group_id] } } %input#js-search-group-dropdown.dropdown-menu-toggle{ value: "Loading...", data: { "initial-group-data": @group.to_json } }
%span.dropdown-toggle-text.gl-flex-grow-1.str-truncated-100
= @group&.name || _("Any")
- if @group.present?
= link_to sprite_icon("clear"), url_for(safe_params.except(:project_id, :group_id)), class: 'search-clear js-search-clear has-tooltip', title: _('Clear')
= icon("chevron-down")
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-right
= dropdown_title(_("Filter results by group"))
= dropdown_filter(_("Search groups"))
= dropdown_content
= dropdown_loading
.dropdown.form-group.mb-lg-0.mx-lg-1{ data: { testid: "project-filter" } } .dropdown.form-group.mb-lg-0.mx-lg-1{ data: { testid: "project-filter" } }
%label.d-block{ for: "dashboard_search_project" } %label.d-block{ for: "dashboard_search_project" }
= _("Project") = _("Project")
......
...@@ -8,11 +8,11 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in ...@@ -8,11 +8,11 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in
let(:project) { create(:project, :repository, :wiki_repo, namespace: group) } let(:project) { create(:project, :repository, :wiki_repo, namespace: group) }
def choose_group(group) def choose_group(group)
find('.js-search-group-dropdown').click find('[data-testid="group-filter"]').click
wait_for_requests wait_for_requests
page.within '.js-search-form' do page.within '[data-testid="group-filter"]' do
click_link group.name click_button group.name
end end
end end
...@@ -25,6 +25,9 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in ...@@ -25,6 +25,9 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in
sign_in(user) sign_in(user)
visit(search_path) visit(search_path)
wait_for_requests
choose_group(group) choose_group(group)
end end
......
...@@ -23328,9 +23328,6 @@ msgstr "" ...@@ -23328,9 +23328,6 @@ msgstr ""
msgid "Search forks" msgid "Search forks"
msgstr "" msgstr ""
msgid "Search groups"
msgstr ""
msgid "Search merge requests" msgid "Search merge requests"
msgstr "" msgstr ""
......
...@@ -18,15 +18,15 @@ RSpec.describe 'User uses search filters', :js do ...@@ -18,15 +18,15 @@ RSpec.describe 'User uses search filters', :js do
it 'shows group projects' do it 'shows group projects' do
visit search_path visit search_path
find('.js-search-group-dropdown').click find('[data-testid="group-filter"]').click
wait_for_requests wait_for_requests
page.within('.search-page-form') do page.within('[data-testid="group-filter"]') do
click_link(group.name) click_on(group.name)
end end
expect(find('.js-search-group-dropdown')).to have_content(group.name) expect(find('[data-testid="group-filter"]')).to have_content(group.name)
page.within('[data-testid="project-filter"]') do page.within('[data-testid="project-filter"]') do
find('.js-search-project-dropdown').click find('.js-search-project-dropdown').click
...@@ -44,10 +44,11 @@ RSpec.describe 'User uses search filters', :js do ...@@ -44,10 +44,11 @@ RSpec.describe 'User uses search filters', :js do
describe 'clear filter button' do describe 'clear filter button' do
it 'removes Group and Project filters' do it 'removes Group and Project filters' do
link = find('[data-testid="group-filter"] .js-search-clear') find('[data-testid="group-filter"] [data-testid="clear-icon"]').click
params = CGI.parse(URI.parse(link[:href]).query)
wait_for_requests
expect(params).not_to include(:group_id, :project_id) expect(page).to have_current_path(search_path(search: "test"))
end end
end end
end end
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { MOCK_QUERY } from 'jest/search/mock_data';
import * as urlUtils from '~/lib/utils/url_utility'; import * as urlUtils from '~/lib/utils/url_utility';
import initStore from '~/search/store'; import initStore from '~/search/store';
import DropdownFilter from '~/search/dropdown_filter/components/dropdown_filter.vue'; import DropdownFilter from '~/search/dropdown_filter/components/dropdown_filter.vue';
import stateFilterData from '~/search/dropdown_filter/constants/state_filter_data'; import stateFilterData from '~/search/dropdown_filter/constants/state_filter_data';
import confidentialFilterData from '~/search/dropdown_filter/constants/confidential_filter_data'; import confidentialFilterData from '~/search/dropdown_filter/constants/confidential_filter_data';
import { MOCK_QUERY } from '../mock_data';
jest.mock('~/lib/utils/url_utility', () => ({ jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn(), visitUrl: jest.fn(),
......
export const MOCK_QUERY = {
scope: 'issues',
state: 'all',
confidential: null,
};
import Vuex from 'vuex';
import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui';
import * as urlUtils from '~/lib/utils/url_utility';
import GroupFilter from '~/search/group_filter/components/group_filter.vue';
import { GROUP_QUERY_PARAM, PROJECT_QUERY_PARAM, ANY_GROUP } from '~/search/group_filter/constants';
import { MOCK_GROUPS, MOCK_GROUP, MOCK_QUERY } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn(),
setUrlParams: jest.fn(),
}));
describe('Global Search Group Filter', () => {
let wrapper;
const actionSpies = {
fetchGroups: jest.fn(),
};
const defaultProps = {
initialGroup: null,
};
const createComponent = (initialState, props = {}, mountFn = shallowMount) => {
const store = new Vuex.Store({
state: {
query: MOCK_QUERY,
...initialState,
},
actions: actionSpies,
});
wrapper = mountFn(GroupFilter, {
localVue,
store,
propsData: {
...defaultProps,
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findGlDropdown = () => wrapper.find(GlDropdown);
const findGlDropdownSearch = () => findGlDropdown().find(GlSearchBoxByType);
const findDropdownText = () => findGlDropdown().find('.dropdown-toggle-text');
const findDropdownItems = () => findGlDropdown().findAll(GlDropdownItem);
const findDropdownItemsText = () => findDropdownItems().wrappers.map(w => w.text());
const findAnyDropdownItem = () => findDropdownItems().at(0);
const findFirstGroupDropdownItem = () => findDropdownItems().at(1);
const findLoader = () => wrapper.find(GlSkeletonLoader);
describe('template', () => {
beforeEach(() => {
createComponent();
});
it('renders GlDropdown', () => {
expect(findGlDropdown().exists()).toBe(true);
});
describe('findGlDropdownSearch', () => {
it('renders always', () => {
expect(findGlDropdownSearch().exists()).toBe(true);
});
it('has debounce prop', () => {
expect(findGlDropdownSearch().attributes('debounce')).toBe('500');
});
describe('onSearch', () => {
const groupSearch = 'test search';
beforeEach(() => {
findGlDropdownSearch().vm.$emit('input', groupSearch);
});
it('calls fetchGroups when input event is fired from GlSearchBoxByType', () => {
expect(actionSpies.fetchGroups).toHaveBeenCalledWith(expect.any(Object), groupSearch);
});
});
});
describe('findDropdownItems', () => {
describe('when fetchingGroups is false', () => {
beforeEach(() => {
createComponent({ groups: MOCK_GROUPS });
});
it('does not render loader', () => {
expect(findLoader().exists()).toBe(false);
});
it('renders an instance for each namespace', () => {
const groupsIncludingAny = ['Any'].concat(MOCK_GROUPS.map(n => n.full_name));
expect(findDropdownItemsText()).toStrictEqual(groupsIncludingAny);
});
});
describe('when fetchingGroups is true', () => {
beforeEach(() => {
createComponent({ fetchingGroups: true, groups: MOCK_GROUPS });
});
it('does render loader', () => {
expect(findLoader().exists()).toBe(true);
});
it('renders only Any in dropdown', () => {
expect(findDropdownItemsText()).toStrictEqual(['Any']);
});
});
});
describe('Dropdown Text', () => {
describe('when initialGroup is null', () => {
beforeEach(() => {
createComponent({}, {}, mount);
});
it('sets dropdown text to Any', () => {
expect(findDropdownText().text()).toBe(ANY_GROUP.name);
});
});
describe('initialGroup is set', () => {
beforeEach(() => {
createComponent({}, { initialGroup: MOCK_GROUP }, mount);
});
it('sets dropdown text to group name', () => {
expect(findDropdownText().text()).toBe(MOCK_GROUP.name);
});
});
});
});
describe('actions', () => {
beforeEach(() => {
createComponent({ groups: MOCK_GROUPS });
});
it('clicking "Any" dropdown item calls setUrlParams with group id null, project id null,and visitUrl', () => {
findAnyDropdownItem().vm.$emit('click');
expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
[GROUP_QUERY_PARAM]: ANY_GROUP.id,
[PROJECT_QUERY_PARAM]: null,
});
expect(urlUtils.visitUrl).toHaveBeenCalled();
});
it('clicking group dropdown item calls setUrlParams with group id, project id null, and visitUrl', () => {
findFirstGroupDropdownItem().vm.$emit('click');
expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
[GROUP_QUERY_PARAM]: MOCK_GROUPS[0].id,
[PROJECT_QUERY_PARAM]: null,
});
expect(urlUtils.visitUrl).toHaveBeenCalled();
});
});
});
export const MOCK_QUERY = {
scope: 'issues',
state: 'all',
confidential: null,
};
export const MOCK_GROUP = {
name: 'test group',
full_name: 'full name test group',
id: 'test_1',
};
export const MOCK_GROUPS = [
{
name: 'test group',
full_name: 'full name test group',
id: 'test_1',
},
{
name: 'test group 2',
full_name: 'full name test group 2',
id: 'test_2',
},
];
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/search/store/actions';
import * as types from '~/search/store/mutation_types';
import state from '~/search/store/state';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { MOCK_GROUPS } from '../mock_data';
jest.mock('~/flash');
describe('Global Search Store Actions', () => {
let mock;
const noCallback = () => {};
const flashCallback = () => {
expect(createFlash).toHaveBeenCalledTimes(1);
createFlash.mockClear();
};
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe.each`
action | axiosMock | type | mutationCalls | callback
${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
`(`axios calls`, ({ action, axiosMock, type, mutationCalls, callback }) => {
describe(action.name, () => {
describe(`on ${type}`, () => {
beforeEach(() => {
mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
});
it(`should dispatch the correct mutations`, () => {
return testAction(action, null, state, mutationCalls, []).then(() => callback());
});
});
});
});
});
import mutations from '~/search/store/mutations';
import createState from '~/search/store/state';
import * as types from '~/search/store/mutation_types';
import { MOCK_QUERY, MOCK_GROUPS } from '../mock_data';
describe('Global Search Store Mutations', () => {
let state;
beforeEach(() => {
state = createState({ query: MOCK_QUERY });
});
describe('REQUEST_GROUPS', () => {
it('sets fetchingGroups to true', () => {
mutations[types.REQUEST_GROUPS](state);
expect(state.fetchingGroups).toBe(true);
});
});
describe('RECEIVE_GROUPS_SUCCESS', () => {
it('sets fetchingGroups to false and sets groups', () => {
mutations[types.RECEIVE_GROUPS_SUCCESS](state, MOCK_GROUPS);
expect(state.fetchingGroups).toBe(false);
expect(state.groups).toBe(MOCK_GROUPS);
});
});
describe('RECEIVE_GROUPS_ERROR', () => {
it('sets fetchingGroups to false and clears groups', () => {
mutations[types.RECEIVE_GROUPS_ERROR](state);
expect(state.fetchingGroups).toBe(false);
expect(state.groups).toEqual([]);
});
});
});
...@@ -36,16 +36,6 @@ describe('Search', () => { ...@@ -36,16 +36,6 @@ describe('Search', () => {
new Search(); // eslint-disable-line no-new new Search(); // eslint-disable-line no-new
}); });
it('requests groups from backend when filtering', () => {
jest.spyOn(Api, 'groups').mockImplementation(term => {
expect(term).toBe(searchTerm);
});
const inputElement = fillDropdownInput('.js-search-group-dropdown');
$(inputElement).trigger('input');
});
it('requests projects from backend when filtering', () => { it('requests projects from backend when filtering', () => {
jest.spyOn(Api, 'projects').mockImplementation(term => { jest.spyOn(Api, 'projects').mockImplementation(term => {
expect(term).toBe(searchTerm); expect(term).toBe(searchTerm);
......
...@@ -8,7 +8,7 @@ RSpec.describe 'search/_filter' do ...@@ -8,7 +8,7 @@ RSpec.describe 'search/_filter' do
render render
expect(rendered).to have_selector('label[for="dashboard_search_group"]') expect(rendered).to have_selector('label[for="dashboard_search_group"]')
expect(rendered).to have_selector('button#dashboard_search_group') expect(rendered).to have_selector('input#js-search-group-dropdown')
expect(rendered).to have_selector('label[for="dashboard_search_project"]') expect(rendered).to have_selector('label[for="dashboard_search_project"]')
expect(rendered).to have_selector('button#dashboard_search_project') expect(rendered).to have_selector('button#dashboard_search_project')
......
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