Commit 8b17d718 authored by Dylan Griffith's avatar Dylan Griffith

Revert "Merge branch '262066-global-search-scope-tabs-vue-component' into 'master'"

This reverts merge request !52018
parent dd6ec7c3
import axios from '~/lib/utils/axios_utils';
function showCount(el, count) {
el.textContent = count;
el.classList.remove('hidden');
}
function refreshCount(el) {
const { url } = el.dataset;
return axios
.get(url)
.then(({ data }) => showCount(el, data.count))
.catch((e) => {
// eslint-disable-next-line no-console
console.error(`Failed to fetch search count from '${url}'.`, e);
});
}
export default function refreshCounts() {
const elements = Array.from(document.querySelectorAll('.js-search-count'));
return Promise.all(elements.map(refreshCount));
}
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result'; import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
import Project from '~/pages/projects/project'; import Project from '~/pages/projects/project';
import refreshCounts from '~/pages/search/show/refresh_counts';
import { queryToObject } from '~/lib/utils/url_utility'; import { queryToObject } from '~/lib/utils/url_utility';
import createStore from './store'; import createStore from './store';
import { initTopbar } from './topbar'; import { initTopbar } from './topbar';
...@@ -19,5 +20,6 @@ export const initSearchApp = () => { ...@@ -19,5 +20,6 @@ export const initSearchApp = () => {
initSearchSort(store); initSearchSort(store);
setHighlightClass(query.search); // Code Highlighting setHighlightClass(query.search); // Code Highlighting
refreshCounts(); // Other Scope Tab Counts
Project.initRefSwitcher(); // Code Search Branch Picker Project.initRefSwitcher(); // Code Search Branch Picker
}; };
import axios from '~/lib/utils/axios_utils';
import Api from '~/api'; import Api from '~/api';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import * as types from './mutation_types'; import * as types from './mutation_types';
/* private */
const getCount = ({ params, state, activeCount }) => {
const globalSearchCountsPath = '/search/count';
const url = Api.buildUrl(globalSearchCountsPath);
// count is known for active tab, so return it and skip the Api call
if (params.scope === state.query?.scope) {
return { scope: params.scope, count: activeCount };
}
return axios
.get(url, { params })
.then(({ data }) => {
return { scope: params.scope, count: data.count };
})
.catch((e) => {
throw e;
});
};
export const fetchGroups = ({ commit }, search) => { export const fetchGroups = ({ commit }, search) => {
commit(types.REQUEST_GROUPS); commit(types.REQUEST_GROUPS);
Api.groups(search) Api.groups(search)
...@@ -59,21 +38,6 @@ export const fetchProjects = ({ commit, state }, search) => { ...@@ -59,21 +38,6 @@ export const fetchProjects = ({ commit, state }, search) => {
} }
}; };
export const fetchSearchCounts = ({ commit, state }, { scopeTabs, activeCount }) => {
commit(types.REQUEST_SEARCH_COUNTS, { scopeTabs, activeCount });
const promises = scopeTabs.map((scope) =>
getCount({ params: { ...state.query, scope }, state, activeCount }),
);
Promise.all(promises)
.then((data) => {
commit(types.RECEIVE_SEARCH_COUNTS_SUCCESS, data);
})
.catch(() => {
createFlash({ message: __('There was an error fetching the Search Counts') });
});
};
export const setQuery = ({ commit }, { key, value }) => { export const setQuery = ({ commit }, { key, value }) => {
commit(types.SET_QUERY, { key, value }); commit(types.SET_QUERY, { key, value });
}; };
...@@ -82,22 +46,6 @@ export const applyQuery = ({ state }) => { ...@@ -82,22 +46,6 @@ export const applyQuery = ({ state }) => {
visitUrl(setUrlParams({ ...state.query, page: null })); visitUrl(setUrlParams({ ...state.query, page: null }));
}; };
export const resetQuery = ({ state }, snippets = false) => { export const resetQuery = ({ state }) => {
let defaultQuery = { visitUrl(setUrlParams({ ...state.query, page: null, state: null, confidential: null }));
page: null,
state: null,
confidential: null,
nav_source: null,
};
if (snippets) {
defaultQuery = {
snippets: true,
group_id: null,
project_id: null,
...defaultQuery,
};
}
visitUrl(setUrlParams({ ...state.query, ...defaultQuery }));
}; };
...@@ -6,7 +6,4 @@ export const REQUEST_PROJECTS = 'REQUEST_PROJECTS'; ...@@ -6,7 +6,4 @@ export const REQUEST_PROJECTS = 'REQUEST_PROJECTS';
export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS'; export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR'; export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
export const REQUEST_SEARCH_COUNTS = 'REQUEST_SEARCH_COUNTS';
export const RECEIVE_SEARCH_COUNTS_SUCCESS = 'RECEIVE_SEARCH_COUNTS_SUCCESS';
export const SET_QUERY = 'SET_QUERY'; export const SET_QUERY = 'SET_QUERY';
import { ALL_SCOPE_TABS } from '~/search/topbar/constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
...@@ -24,16 +23,6 @@ export default { ...@@ -24,16 +23,6 @@ export default {
state.fetchingProjects = false; state.fetchingProjects = false;
state.projects = []; state.projects = [];
}, },
[types.REQUEST_SEARCH_COUNTS](state, { scopeTabs, activeCount }) {
state.inflatedScopeTabs = scopeTabs.map((tab) => {
return { ...ALL_SCOPE_TABS[tab], count: tab === state.query?.scope ? activeCount : '' };
});
},
[types.RECEIVE_SEARCH_COUNTS_SUCCESS](state, data) {
state.inflatedScopeTabs = data.map((tab) => {
return { ...ALL_SCOPE_TABS[tab.scope], count: tab.count };
});
},
[types.SET_QUERY](state, { key, value }) { [types.SET_QUERY](state, { key, value }) {
state.query[key] = value; state.query[key] = value;
}, },
......
...@@ -4,6 +4,5 @@ const createState = ({ query }) => ({ ...@@ -4,6 +4,5 @@ const createState = ({ query }) => ({
fetchingGroups: false, fetchingGroups: false,
projects: [], projects: [],
fetchingProjects: false, fetchingProjects: false,
inflatedScopeTabs: [],
}); });
export default createState; export default createState;
...@@ -3,7 +3,6 @@ import { mapState, mapActions } from 'vuex'; ...@@ -3,7 +3,6 @@ import { mapState, mapActions } from 'vuex';
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui'; import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
import GroupFilter from './group_filter.vue'; import GroupFilter from './group_filter.vue';
import ProjectFilter from './project_filter.vue'; import ProjectFilter from './project_filter.vue';
import ScopeTabs from './scope_tabs.vue';
export default { export default {
name: 'GlobalSearchTopbar', name: 'GlobalSearchTopbar',
...@@ -13,7 +12,6 @@ export default { ...@@ -13,7 +12,6 @@ export default {
GroupFilter, GroupFilter,
ProjectFilter, ProjectFilter,
GlButton, GlButton,
ScopeTabs,
}, },
props: { props: {
groupInitialData: { groupInitialData: {
...@@ -26,16 +24,6 @@ export default { ...@@ -26,16 +24,6 @@ export default {
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
scopeTabs: {
type: Array,
required: false,
default: () => [],
},
count: {
type: String,
required: false,
default: '',
},
}, },
computed: { computed: {
...mapState(['query']), ...mapState(['query']),
...@@ -50,9 +38,6 @@ export default { ...@@ -50,9 +38,6 @@ export default {
showFilters() { showFilters() {
return !this.query.snippets || this.query.snippets === 'false'; return !this.query.snippets || this.query.snippets === 'false';
}, },
showScopeTabs() {
return this.query.search;
},
}, },
methods: { methods: {
...mapActions(['applyQuery', 'setQuery']), ...mapActions(['applyQuery', 'setQuery']),
...@@ -61,31 +46,28 @@ export default { ...@@ -61,31 +46,28 @@ export default {
</script> </script>
<template> <template>
<section> <gl-form class="search-page-form" @submit.prevent="applyQuery">
<gl-form class="search-page-form" @submit.prevent="applyQuery"> <section class="gl-lg-display-flex gl-align-items-flex-end">
<section class="gl-lg-display-flex gl-align-items-flex-end"> <div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
<div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2"> <label>{{ __('What are you searching for?') }}</label>
<label>{{ __('What are you searching for?') }}</label> <gl-search-box-by-type
<gl-search-box-by-type id="dashboard_search"
id="dashboard_search" v-model="search"
v-model="search" name="search"
name="search" :placeholder="__(`Search for projects, issues, etc.`)"
:placeholder="__(`Search for projects, issues, etc.`)" />
/> </div>
</div> <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
<div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2"> <label class="gl-display-block">{{ __('Group') }}</label>
<label class="gl-display-block">{{ __('Group') }}</label> <group-filter :initial-data="groupInitialData" />
<group-filter :initial-data="groupInitialData" /> </div>
</div> <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
<div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2"> <label class="gl-display-block">{{ __('Project') }}</label>
<label class="gl-display-block">{{ __('Project') }}</label> <project-filter :initial-data="projectInitialData" />
<project-filter :initial-data="projectInitialData" /> </div>
</div> <gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
<gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{ __('Search')
__('Search') }}</gl-button>
}}</gl-button> </section>
</section> </gl-form>
</gl-form>
<scope-tabs v-if="showScopeTabs" :scope-tabs="scopeTabs" :count="count" />
</section>
</template> </template>
<script>
import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
export default {
name: 'ScopeTabs',
components: {
GlTabs,
GlTab,
GlBadge,
},
props: {
scopeTabs: {
type: Array,
required: true,
},
count: {
type: String,
required: false,
default: '',
},
},
computed: {
...mapState(['query', 'inflatedScopeTabs']),
},
created() {
this.fetchSearchCounts({ scopeTabs: this.scopeTabs, activeCount: this.count });
},
methods: {
...mapActions(['fetchSearchCounts', 'setQuery', 'resetQuery']),
handleTabChange(scope) {
this.setQuery({ key: 'scope', value: scope });
this.resetQuery(scope === 'snippet_titles');
},
isTabActive(scope) {
return scope === this.query.scope;
},
},
};
</script>
<template>
<div>
<gl-tabs
content-class="gl-p-0"
nav-class="search-filter search-nav-tabs gl-display-flex gl-overflow-x-auto"
>
<gl-tab
v-for="tab in inflatedScopeTabs"
:key="tab.scope"
class="gl-display-flex"
:active="isTabActive(tab.scope)"
:data-testid="`tab-${tab.scope}`"
:title-link-attributes="{ 'data-qa-selector': tab.qaSelector }"
title-link-class="gl-white-space-nowrap"
@click="handleTabChange(tab.scope)"
>
<template #title>
<span data-testid="tab-title"> {{ tab.title }} </span>
<gl-badge
v-show="tab.count"
:data-scope="tab.scope"
:data-testid="`badge-${tab.scope}`"
:variant="isTabActive(tab.scope) ? 'neutral' : 'muted'"
size="sm"
>
{{ tab.count }}
</gl-badge>
</template>
</gl-tab>
</gl-tabs>
</div>
</template>
...@@ -19,17 +19,3 @@ export const PROJECT_DATA = { ...@@ -19,17 +19,3 @@ export const PROJECT_DATA = {
selectedDisplayValue: 'name_with_namespace', selectedDisplayValue: 'name_with_namespace',
itemsDisplayValue: 'name_with_namespace', itemsDisplayValue: 'name_with_namespace',
}; };
export const ALL_SCOPE_TABS = {
blobs: { scope: 'blobs', title: __('Code'), qaSelector: 'code_tab' },
issues: { scope: 'issues', title: __('Issues') },
merge_requests: { scope: 'merge_requests', title: __('Merge requests') },
milestones: { scope: 'milestones', title: __('Milestones') },
notes: { scope: 'notes', title: __('Comments') },
wiki_blobs: { scope: 'wiki_blobs', title: __('Wiki') },
commits: { scope: 'commits', title: __('Commits') },
epics: { scope: 'epics', title: __('Epics') },
users: { scope: 'users', title: __('Users') },
snippet_titles: { scope: 'snippet_titles', title: __('Titles and Descriptions') },
projects: { scope: 'projects', title: __('Projects'), qaSelector: 'projects_tab' },
};
...@@ -11,12 +11,10 @@ export const initTopbar = (store) => { ...@@ -11,12 +11,10 @@ export const initTopbar = (store) => {
return false; return false;
} }
let { groupInitialData, projectInitialData, scopeTabs } = el.dataset; let { groupInitialData, projectInitialData } = el.dataset;
const { count } = el.dataset;
groupInitialData = JSON.parse(groupInitialData); groupInitialData = JSON.parse(groupInitialData);
projectInitialData = JSON.parse(projectInitialData); projectInitialData = JSON.parse(projectInitialData);
scopeTabs = JSON.parse(scopeTabs);
return new Vue({ return new Vue({
el, el,
...@@ -26,8 +24,6 @@ export const initTopbar = (store) => { ...@@ -26,8 +24,6 @@ export const initTopbar = (store) => {
props: { props: {
groupInitialData, groupInitialData,
projectInitialData, projectInitialData,
scopeTabs,
count,
}, },
}); });
}, },
......
...@@ -2,7 +2,6 @@ $search-dropdown-max-height: 400px; ...@@ -2,7 +2,6 @@ $search-dropdown-max-height: 400px;
$search-avatar-size: 16px; $search-avatar-size: 16px;
$search-sidebar-min-width: 240px; $search-sidebar-min-width: 240px;
$search-sidebar-max-width: 300px; $search-sidebar-max-width: 300px;
$search-topbar-min-height: 111px;
.search-results { .search-results {
.search-result-row { .search-result-row {
...@@ -20,12 +19,6 @@ $search-topbar-min-height: 111px; ...@@ -20,12 +19,6 @@ $search-topbar-min-height: 111px;
} }
} }
.search-topbar {
@include media-breakpoint-up(md) {
min-height: $search-topbar-min-height;
}
}
.search-sidebar { .search-sidebar {
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
min-width: $search-sidebar-min-width; min-width: $search-sidebar-min-width;
...@@ -33,11 +26,6 @@ $search-topbar-min-height: 111px; ...@@ -33,11 +26,6 @@ $search-topbar-min-height: 111px;
} }
} }
.search-nav-tabs {
overflow-y: hidden;
flex-wrap: nowrap;
}
.search form:hover, .search form:hover,
.file-finder-input:hover, .file-finder-input:hover,
.issuable-search-form:hover, .issuable-search-form:hover,
......
...@@ -511,8 +511,7 @@ module ProjectsHelper ...@@ -511,8 +511,7 @@ module ProjectsHelper
commits: :download_code, commits: :download_code,
merge_requests: :read_merge_request, merge_requests: :read_merge_request,
notes: [:read_merge_request, :download_code, :read_issue, :read_snippet], notes: [:read_merge_request, :download_code, :read_issue, :read_snippet],
members: :read_project_member, members: :read_project_member
wiki_blobs: :read_wiki
) )
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module SearchHelper module SearchHelper
PROJECT_SEARCH_TABS = %i{blobs issues merge_requests milestones notes wiki_blobs commits}.freeze SEARCH_GENERIC_PARAMS = [
BASIC_SEARCH_TABS = %i{projects issues merge_requests milestones}.freeze :search,
:scope,
:project_id,
:group_id,
:repository_ref,
:snippets,
:sort,
:force_search_results
].freeze
def search_autocomplete_opts(term) def search_autocomplete_opts(term)
return unless current_user return unless current_user
...@@ -284,19 +292,27 @@ module SearchHelper ...@@ -284,19 +292,27 @@ module SearchHelper
Sanitize.clean(str) Sanitize.clean(str)
end end
def search_nav_tabs def search_filter_link(scope, label, data: {}, search: {})
return [:snippet_titles] if !@project && @show_snippets search_params = params
.merge(search)
.merge({ scope: scope })
.permit(SEARCH_GENERIC_PARAMS)
tabs = if @scope == scope
if @project li_class = 'active'
PROJECT_SEARCH_TABS.select { |tab| project_search_tabs?(tab) } count = @search_results.formatted_count(scope)
else else
BASIC_SEARCH_TABS.dup badge_class = 'js-search-count hidden'
end badge_data = { url: search_count_path(search_params) }
end
tabs << :users if show_user_search_tab?
tabs content_tag :li, class: li_class, data: data do
link_to search_path(search_params) do
concat label
concat ' '
concat content_tag(:span, count, class: ['badge badge-pill', badge_class], data: badge_data)
end
end
end end
def search_filter_input_options(type, placeholder = _('Search or filter results...')) def search_filter_input_options(type, placeholder = _('Search or filter results...'))
......
- users = capture_haml do
- if show_user_search_tab?
= search_filter_link 'users', _("Users")
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= sprite_icon('chevron-lg-left', size: 12)
.fade-right= sprite_icon('chevron-lg-right', size: 12)
%ul.nav-links.search-filter.scrolling-tabs.nav.nav-tabs
- if @project
- if project_search_tabs?(:blobs)
= search_filter_link 'blobs', _("Code"), data: { qa_selector: 'code_tab' }
- if project_search_tabs?(:issues)
= search_filter_link 'issues', _("Issues")
- if project_search_tabs?(:merge_requests)
= search_filter_link 'merge_requests', _("Merge requests")
- if project_search_tabs?(:milestones)
= search_filter_link 'milestones', _("Milestones")
- if project_search_tabs?(:notes)
= search_filter_link 'notes', _("Comments")
- if project_search_tabs?(:wiki)
= search_filter_link 'wiki_blobs', _("Wiki")
- if project_search_tabs?(:commits)
= search_filter_link 'commits', _("Commits")
= users
- elsif @show_snippets
= search_filter_link 'snippet_titles', _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }
- else
= search_filter_link 'projects', _("Projects"), data: { qa_selector: 'projects_tab' }
= search_filter_link 'issues', _("Issues")
= search_filter_link 'merge_requests', _("Merge requests")
= search_filter_link 'milestones', _("Milestones")
= render_if_exists 'search/epics_filter_link'
= render_if_exists 'search/category_elasticsearch'
= users
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' } = render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
.gl-mt-3 .gl-mt-3
#js-search-topbar.search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json, "scope-tabs": search_nav_tabs.to_json, count: @search_results&.formatted_count(@scope) } } #js-search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json } }
- if @search_term - if @search_term
= render 'search/category'
= render 'search/results' = render 'search/results'
---
title: Change search tab to Vue component
merge_request: 52018
author:
type: changed
...@@ -3,8 +3,8 @@ module EE ...@@ -3,8 +3,8 @@ module EE
module SearchHelper module SearchHelper
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
SWITCH_TO_BASIC_SEARCHABLE_TABS = %w[projects issues merge_requests milestones users epics].freeze
PLACEHOLDER = '_PLACEHOLDER_' PLACEHOLDER = '_PLACEHOLDER_'
ADVANCED_SEARCH_TABS = %i{notes blobs commits wiki_blobs}.freeze
override :search_filter_input_options override :search_filter_input_options
def search_filter_input_options(type, placeholder = _('Search or filter results...')) def search_filter_input_options(type, placeholder = _('Search or filter results...'))
...@@ -130,20 +130,6 @@ module EE ...@@ -130,20 +130,6 @@ module EE
options + super options + super
end end
override :search_nav_tabs
def search_nav_tabs
return super if @project || @show_snippets
tabs = []
tabs << :epics if search_service.show_epics?
tabs.push(*ADVANCED_SEARCH_TABS) if search_service.use_elasticsearch?
super_tabs = super
users_index = super_tabs.index(:users) || -1
super_tabs.insert(users_index, *tabs)
end
private private
def recent_epics_autocomplete(term) def recent_epics_autocomplete(term)
......
- if search_service.use_elasticsearch?
= search_filter_link 'notes', _("Comments")
= search_filter_link 'blobs', _("Code"), data: { qa_selector: 'code_tab' }
= search_filter_link 'commits', _("Commits")
= search_filter_link 'wiki_blobs', _("Wiki")
- if search_service.show_epics?
= search_filter_link 'epics', _("Epics")
...@@ -77,7 +77,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -77,7 +77,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
end end
end end
describe 'I search through the issues and I see pagination', :js do describe 'I search through the issues and I see pagination' do
before do before do
create_list(:issue, 21, project: project, title: 'initial') create_list(:issue, 21, project: project, title: 'initial')
...@@ -94,7 +94,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -94,7 +94,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
end end
end end
describe 'I search through the notes and I see pagination', :js do describe 'I search through the notes and I see pagination' do
before do before do
issue = create(:issue, project: project, title: 'initial') issue = create(:issue, project: project, title: 'initial')
create_list(:note, 21, noteable: issue, project: project, note: 'foo') create_list(:note, 21, noteable: issue, project: project, note: 'foo')
...@@ -112,7 +112,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -112,7 +112,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
end end
end end
describe 'I search through the blobs', :js do describe 'I search through the blobs' do
let(:project_2) { create(:project, :repository, :wiki_repo) } let(:project_2) { create(:project, :repository, :wiki_repo) }
before do before do
...@@ -156,7 +156,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -156,7 +156,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
end end
end end
describe 'I search through the wiki blobs', :js do describe 'I search through the wiki blobs' do
before do before do
project.wiki.create_page('test.md', '# term') project.wiki.create_page('test.md', '# term')
project.wiki.index_wiki_blobs project.wiki.index_wiki_blobs
...@@ -175,10 +175,9 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -175,10 +175,9 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
end end
end end
describe 'I search through the commits', :js do describe 'I search through the commits' do
before do before do
project.repository.index_commits_and_blobs project.repository.index_commits_and_blobs
ensure_elasticsearch_index! ensure_elasticsearch_index!
end end
...@@ -188,7 +187,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -188,7 +187,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
submit_search('add') submit_search('add')
select_search_scope('Commits') select_search_scope('Commits')
expect(page).to have_selector('.commit-row-message') expect(page).to have_selector('.commit-row-description')
expect(page).to have_selector('.project-namespace') expect(page).to have_selector('.project-namespace')
end end
...@@ -198,7 +197,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -198,7 +197,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
submit_search('add') submit_search('add')
select_search_scope('Commits') select_search_scope('Commits')
expected_message = "Merge branch 'tree_helper_spec' into 'master'" expected_message = "Add directory structure for tree_helper spec"
expect(page).not_to have_content(expected_message) expect(page).not_to have_content(expected_message)
...@@ -232,7 +231,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do ...@@ -232,7 +231,7 @@ RSpec.describe 'Global elastic search', :elastic, :sidekiq_inline do
end end
end end
RSpec.describe 'Global elastic search redactions', :elastic, :js do RSpec.describe 'Global elastic search redactions', :elastic do
context 'when block_anonymous_global_searches is disabled' do context 'when block_anonymous_global_searches is disabled' do
before do before do
stub_feature_flags(block_anonymous_global_searches: false) stub_feature_flags(block_anonymous_global_searches: false)
......
...@@ -83,7 +83,6 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in ...@@ -83,7 +83,6 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in
describe 'commit search' do describe 'commit search' do
before do before do
project.repository.index_commits_and_blobs project.repository.index_commits_and_blobs
ensure_elasticsearch_index! ensure_elasticsearch_index!
end end
...@@ -96,7 +95,7 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in ...@@ -96,7 +95,7 @@ RSpec.describe 'Group elastic search', :js, :elastic, :sidekiq_might_not_need_in
end end
end end
RSpec.describe 'Group elastic search redactions', :elastic, :js do RSpec.describe 'Group elastic search redactions', :elastic do
it_behaves_like 'a redacted search results page' do it_behaves_like 'a redacted search results page' do
let(:search_path) { group_path(public_group) } let(:search_path) { group_path(public_group) }
end end
......
...@@ -10,7 +10,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -10,7 +10,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
end end
describe 'searching', :sidekiq_inline do describe 'searching' do
before do before do
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
...@@ -18,7 +18,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -18,7 +18,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do
visit project_path(project) visit project_path(project)
end end
it 'finds issues' do it 'finds issues', :sidekiq_inline do
create(:issue, project: project, title: 'Test searching for an issue') create(:issue, project: project, title: 'Test searching for an issue')
ensure_elasticsearch_index! ensure_elasticsearch_index!
...@@ -28,7 +28,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -28,7 +28,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do
expect(page).to have_selector('.results', text: 'Test searching for an issue') expect(page).to have_selector('.results', text: 'Test searching for an issue')
end end
it 'finds merge requests' do it 'finds merge requests', :sidekiq_inline do
create(:merge_request, source_project: project, target_project: project, title: 'Test searching for an MR') create(:merge_request, source_project: project, target_project: project, title: 'Test searching for an MR')
ensure_elasticsearch_index! ensure_elasticsearch_index!
...@@ -38,7 +38,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -38,7 +38,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do
expect(page).to have_selector('.results', text: 'Test searching for an MR') expect(page).to have_selector('.results', text: 'Test searching for an MR')
end end
it 'finds milestones' do it 'finds milestones', :sidekiq_inline do
create(:milestone, project: project, title: 'Test searching for a milestone') create(:milestone, project: project, title: 'Test searching for a milestone')
ensure_elasticsearch_index! ensure_elasticsearch_index!
...@@ -48,10 +48,9 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -48,10 +48,9 @@ RSpec.describe 'Project elastic search', :js, :elastic do
expect(page).to have_selector('.results', text: 'Test searching for a milestone') expect(page).to have_selector('.results', text: 'Test searching for a milestone')
end end
it 'finds wiki pages' do it 'finds wiki pages', :sidekiq_inline do
project.wiki.create_page('test.md', 'Test searching for a wiki page') project.wiki.create_page('test.md', 'Test searching for a wiki page')
project.wiki.index_wiki_blobs project.wiki.index_wiki_blobs
ensure_elasticsearch_index!
submit_search('Test') submit_search('Test')
select_search_scope('Wiki') select_search_scope('Wiki')
...@@ -59,7 +58,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -59,7 +58,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do
expect(page).to have_selector('.results', text: 'Test searching for a wiki page') expect(page).to have_selector('.results', text: 'Test searching for a wiki page')
end end
it 'finds notes' do it 'finds notes', :sidekiq_inline do
create(:note, project: project, note: 'Test searching for a comment') create(:note, project: project, note: 'Test searching for a comment')
ensure_elasticsearch_index! ensure_elasticsearch_index!
...@@ -69,9 +68,8 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -69,9 +68,8 @@ RSpec.describe 'Project elastic search', :js, :elastic do
expect(page).to have_selector('.results', text: 'Test searching for a comment') expect(page).to have_selector('.results', text: 'Test searching for a comment')
end end
it 'finds commits' do it 'finds commits', :sidekiq_inline do
project.repository.index_commits_and_blobs project.repository.index_commits_and_blobs
ensure_elasticsearch_index!
submit_search('initial') submit_search('initial')
select_search_scope('Commits') select_search_scope('Commits')
...@@ -79,9 +77,8 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -79,9 +77,8 @@ RSpec.describe 'Project elastic search', :js, :elastic do
expect(page).to have_selector('.results', text: 'Initial commit') expect(page).to have_selector('.results', text: 'Initial commit')
end end
it 'finds blobs' do it 'finds blobs', :sidekiq_inline do
project.repository.index_commits_and_blobs project.repository.index_commits_and_blobs
ensure_elasticsearch_index!
submit_search('def') submit_search('def')
select_search_scope('Code') select_search_scope('Code')
...@@ -129,7 +126,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do ...@@ -129,7 +126,7 @@ RSpec.describe 'Project elastic search', :js, :elastic do
end end
end end
RSpec.describe 'Project elastic search redactions', :elastic, :js do RSpec.describe 'Project elastic search redactions', :elastic do
it_behaves_like 'a redacted search results page' do it_behaves_like 'a redacted search results page' do
let(:search_path) { project_path(public_restricted_project) } let(:search_path) { project_path(public_restricted_project) }
end end
......
...@@ -295,67 +295,4 @@ RSpec.describe SearchHelper do ...@@ -295,67 +295,4 @@ RSpec.describe SearchHelper do
end end
end end
end end
describe '#search_nav_tabs' do
let(:current_user) { nil }
subject { search_nav_tabs }
context 'when @show_snippets is present' do
before do
@show_snippets = 1
end
it { is_expected.to eq([:snippet_titles]) }
end
context 'when @project is present' do
before do
@project = 1
allow(self).to receive(:project_search_tabs?).with(anything).and_return(true)
end
it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits, :users]) }
end
context 'when @show_snippets and @project are not present' do
context 'when user has access to read users' do
before do
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
end
context 'when elasticsearch is enabled' do
before do
allow(self.search_service).to receive(:use_elasticsearch?).and_return(true)
end
it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones, :notes, :blobs, :commits, :wiki_blobs, :users]) }
context 'when show_epics? is true' do
before do
allow(self.search_service).to receive(:show_epics?).and_return(true)
end
it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones, :epics, :notes, :blobs, :commits, :wiki_blobs, :users]) }
end
end
context 'when elasticsearch is disabled' do
before do
allow(self.search_service).to receive(:use_elasticsearch?).and_return(false)
end
it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones, :users]) }
context 'when show_epics? is true' do
before do
allow(self.search_service).to receive(:show_epics?).and_return(true)
end
it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones, :epics, :users]) }
end
end
end
end
end
end end
...@@ -29563,9 +29563,6 @@ msgstr "" ...@@ -29563,9 +29563,6 @@ msgstr ""
msgid "There was an error fetching the Node's Groups" msgid "There was an error fetching the Node's Groups"
msgstr "" msgstr ""
msgid "There was an error fetching the Search Counts"
msgstr ""
msgid "There was an error fetching the deploy freezes." msgid "There was an error fetching the deploy freezes."
msgstr "" msgstr ""
......
...@@ -4,7 +4,7 @@ module QA ...@@ -4,7 +4,7 @@ module QA
module Page module Page
module Search module Search
class Results < QA::Page::Base class Results < QA::Page::Base
view 'app/assets/javascripts/search/topbar/constants.js' do view 'app/views/search/_category.html.haml' do
element :code_tab element :code_tab
element :projects_tab element :projects_tab
end end
......
...@@ -28,7 +28,7 @@ RSpec.describe 'Global search' do ...@@ -28,7 +28,7 @@ RSpec.describe 'Global search' do
create_list(:issue, 2, project: project, title: 'initial') create_list(:issue, 2, project: project, title: 'initial')
end end
it "has a pagination", :js do it "has a pagination" do
submit_search('initial') submit_search('initial')
select_search_scope('Issues') select_search_scope('Issues')
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'User searches for code', :js do RSpec.describe 'User searches for code' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) } let(:project) { create(:project, :repository, namespace: user.namespace) }
...@@ -16,7 +16,6 @@ RSpec.describe 'User searches for code', :js do ...@@ -16,7 +16,6 @@ RSpec.describe 'User searches for code', :js do
visit(project_path(project)) visit(project_path(project))
submit_search('application.js') submit_search('application.js')
select_search_scope('Code') select_search_scope('Code')
expect(page).to have_selector('.results', text: 'application.js') expect(page).to have_selector('.results', text: 'application.js')
...@@ -25,7 +24,7 @@ RSpec.describe 'User searches for code', :js do ...@@ -25,7 +24,7 @@ RSpec.describe 'User searches for code', :js do
expect(page).to have_link('application.js', href: /master\/files\/js\/application.js/) expect(page).to have_link('application.js', href: /master\/files\/js\/application.js/)
end end
context 'when on a project page' do context 'when on a project page', :js do
before do before do
visit(search_path) visit(search_path)
find('[data-testid="project-filter"]').click find('[data-testid="project-filter"]').click
...@@ -49,7 +48,7 @@ RSpec.describe 'User searches for code', :js do ...@@ -49,7 +48,7 @@ RSpec.describe 'User searches for code', :js do
expect(current_url).to match(/master\/.gitignore#L3/) expect(current_url).to match(/master\/.gitignore#L3/)
end end
it 'search multiple words with refs switching' do it 'search mutiple words with refs switching' do
expected_result = 'Use `snake_case` for naming files' expected_result = 'Use `snake_case` for naming files'
search = 'for naming files' search = 'for naming files'
...@@ -68,7 +67,7 @@ RSpec.describe 'User searches for code', :js do ...@@ -68,7 +67,7 @@ RSpec.describe 'User searches for code', :js do
end end
end end
context 'search code within refs' do context 'search code within refs', :js do
let(:ref_name) { 'v1.0.0' } let(:ref_name) { 'v1.0.0' }
before do before do
...@@ -86,9 +85,9 @@ RSpec.describe 'User searches for code', :js do ...@@ -86,9 +85,9 @@ RSpec.describe 'User searches for code', :js do
expect(find('.js-project-refs-dropdown')).to have_text(ref_name) expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
end end
# this example is use to test the design that the refs is not # this example is use to test the desgine that the refs is not
# only represent the branch as well as the tags. # only repersent the branch as well as the tags.
it 'ref switcher list all the branches and tags' do it 'ref swither list all the branchs and tags' do
find('.js-project-refs-dropdown').click find('.js-project-refs-dropdown').click
expect(find('.dropdown-page-one .dropdown-content')).to have_link('sha-starting-with-large-number') expect(find('.dropdown-page-one .dropdown-content')).to have_link('sha-starting-with-large-number')
expect(find('.dropdown-page-one .dropdown-content')).to have_link('v1.0.0') expect(find('.dropdown-page-one .dropdown-content')).to have_link('v1.0.0')
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'User searches for comments', :js do RSpec.describe 'User searches for comments' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:user) { create(:user) } let(:user) { create(:user) }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'User searches for users', :js do RSpec.describe 'User searches for users' do
let(:user1) { create(:user, username: 'gob_bluth', name: 'Gob Bluth') } let(:user1) { create(:user, username: 'gob_bluth', name: 'Gob Bluth') }
let(:user2) { create(:user, username: 'michael_bluth', name: 'Michael Bluth') } let(:user2) { create(:user, username: 'michael_bluth', name: 'Michael Bluth') }
let(:user3) { create(:user, username: 'gob_2018', name: 'George Oscar Bluth') } let(:user3) { create(:user, username: 'gob_2018', name: 'George Oscar Bluth') }
...@@ -12,7 +12,7 @@ RSpec.describe 'User searches for users', :js do ...@@ -12,7 +12,7 @@ RSpec.describe 'User searches for users', :js do
end end
context 'when on the dashboard' do context 'when on the dashboard' do
it 'finds the user' do it 'finds the user', :js do
visit dashboard_projects_path visit dashboard_projects_path
submit_search('gob') submit_search('gob')
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Search Snippets', :js do RSpec.describe 'Search Snippets' do
it 'user searches for snippets by title' do it 'user searches for snippets by title' do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle') public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End') private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`pages/search/show/refresh_counts fetches and displays search counts 1`] = `
"<div class=\\"badge\\">22</div>
<div class=\\"badge js-search-count\\" data-url=\\"http://test.host/search/count?search=lorem+ipsum&amp;project_id=3&amp;scope=issues\\">4</div>
<div class=\\"badge js-search-count\\" data-url=\\"http://test.host/search/count?search=lorem+ipsum&amp;project_id=3&amp;scope=merge_requests\\">5</div>"
`;
import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
import refreshCounts from '~/pages/search/show/refresh_counts';
const URL = `${TEST_HOST}/search/count?search=lorem+ipsum&project_id=3`;
const urlWithScope = (scope) => `${URL}&scope=${scope}`;
const counts = [
{ scope: 'issues', count: 4 },
{ scope: 'merge_requests', count: 5 },
];
const fixture = `<div class="badge">22</div>
<div class="badge js-search-count hidden" data-url="${urlWithScope('issues')}"></div>
<div class="badge js-search-count hidden" data-url="${urlWithScope('merge_requests')}"></div>`;
describe('pages/search/show/refresh_counts', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
setFixtures(fixture);
});
afterEach(() => {
mock.restore();
});
it('fetches and displays search counts', () => {
counts.forEach(({ scope, count }) => {
mock.onGet(urlWithScope(scope)).reply(200, { count });
});
// assert before act behavior
return refreshCounts().then(() => {
expect(document.body.innerHTML).toMatchSnapshot();
});
});
});
...@@ -61,28 +61,3 @@ export const MOCK_SORT_OPTIONS = [ ...@@ -61,28 +61,3 @@ export const MOCK_SORT_OPTIONS = [
}, },
}, },
]; ];
export const MOCK_SEARCH_COUNTS_INPUT = {
scopeTabs: ['issues', 'snippet_titles', 'merge_requests'],
activeCount: '15',
};
export const MOCK_SEARCH_COUNT = { scope: 'issues', count: '15' };
export const MOCK_SEARCH_COUNTS_SUCCESS = [
{ scope: 'issues', count: '15' },
{ scope: 'snippet_titles', count: '15' },
{ scope: 'merge_requests', count: '15' },
];
export const MOCK_SEARCH_COUNTS = [
{ scope: 'issues', count: '15' },
{ scope: 'snippet_titles', count: '5' },
{ scope: 'merge_requests', count: '1' },
];
export const MOCK_SCOPE_TABS = [
{ scope: 'issues', title: 'Issues', count: '15' },
{ scope: 'snippet_titles', title: 'Titles and Descriptions', count: '5' },
{ scope: 'merge_requests', title: 'Merge requests', count: '1' },
];
...@@ -7,15 +7,7 @@ import * as urlUtils from '~/lib/utils/url_utility'; ...@@ -7,15 +7,7 @@ import * as urlUtils from '~/lib/utils/url_utility';
import createState from '~/search/store/state'; import createState from '~/search/store/state';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECT, MOCK_PROJECTS } from '../mock_data';
MOCK_QUERY,
MOCK_GROUPS,
MOCK_PROJECT,
MOCK_PROJECTS,
MOCK_SEARCH_COUNT,
MOCK_SEARCH_COUNTS_SUCCESS,
MOCK_SEARCH_COUNTS_INPUT,
} from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({ jest.mock('~/lib/utils/url_utility', () => ({
...@@ -45,21 +37,19 @@ describe('Global Search Store Actions', () => { ...@@ -45,21 +37,19 @@ describe('Global Search Store Actions', () => {
}); });
describe.each` describe.each`
action | axiosMock | payload | type | expectedMutations | callback action | axiosMock | type | expectedMutations | callback
${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${null} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback} ${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 }} | ${null} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback} ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${null} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback} ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback}
${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${null} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback} ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback}
${actions.fetchSearchCounts} | ${{ method: 'onGet', code: 200, res: MOCK_SEARCH_COUNT }} | ${MOCK_SEARCH_COUNTS_INPUT} | ${'success'} | ${[{ type: types.REQUEST_SEARCH_COUNTS, payload: MOCK_SEARCH_COUNTS_INPUT }, { type: types.RECEIVE_SEARCH_COUNTS_SUCCESS, payload: MOCK_SEARCH_COUNTS_SUCCESS }]} | ${noCallback} `(`axios calls`, ({ action, axiosMock, type, expectedMutations, callback }) => {
${actions.fetchSearchCounts} | ${{ method: 'onGet', code: 500, res: null }} | ${MOCK_SEARCH_COUNTS_INPUT} | ${'error'} | ${[{ type: types.REQUEST_SEARCH_COUNTS, payload: MOCK_SEARCH_COUNTS_INPUT }]} | ${flashCallback}
`(`axios calls`, ({ action, axiosMock, payload, type, expectedMutations, callback }) => {
describe(action.name, () => { describe(action.name, () => {
describe(`on ${type}`, () => { describe(`on ${type}`, () => {
beforeEach(() => { beforeEach(() => {
mock[axiosMock.method]().reply(axiosMock.code, axiosMock.res); mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
}); });
it(`should dispatch the correct mutations`, () => { it(`should dispatch the correct mutations`, () => {
return testAction({ action, payload, state, expectedMutations }).then(() => callback()); return testAction({ action, state, expectedMutations }).then(() => callback());
}); });
}); });
}); });
...@@ -125,25 +115,9 @@ describe('Global Search Store Actions', () => { ...@@ -125,25 +115,9 @@ describe('Global Search Store Actions', () => {
page: null, page: null,
state: null, state: null,
confidential: null, confidential: null,
nav_source: null,
}); });
expect(urlUtils.visitUrl).toHaveBeenCalled(); expect(urlUtils.visitUrl).toHaveBeenCalled();
}); });
}); });
}); });
it('calls setUrlParams with snippets, group_id, and project_id when snippets param is true', () => {
return testAction(actions.resetQuery, true, state, [], [], () => {
expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
...state.query,
page: null,
state: null,
confidential: null,
nav_source: null,
group_id: null,
project_id: null,
snippets: true,
});
});
});
}); });
import mutations from '~/search/store/mutations'; import mutations from '~/search/store/mutations';
import createState from '~/search/store/state'; import createState from '~/search/store/state';
import * as types from '~/search/store/mutation_types'; import * as types from '~/search/store/mutation_types';
import { import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECTS } from '../mock_data';
MOCK_QUERY,
MOCK_GROUPS,
MOCK_PROJECTS,
MOCK_SEARCH_COUNTS,
MOCK_SCOPE_TABS,
} from '../mock_data';
describe('Global Search Store Mutations', () => { describe('Global Search Store Mutations', () => {
let state; let state;
...@@ -77,32 +71,4 @@ describe('Global Search Store Mutations', () => { ...@@ -77,32 +71,4 @@ describe('Global Search Store Mutations', () => {
expect(state.query[payload.key]).toBe(payload.value); expect(state.query[payload.key]).toBe(payload.value);
}); });
}); });
describe('REQUEST_SEARCH_COUNTS', () => {
it('sets the count to for the query.scope activeCount', () => {
const payload = { scopeTabs: ['issues'], activeCount: '22' };
mutations[types.REQUEST_SEARCH_COUNTS](state, payload);
expect(state.inflatedScopeTabs).toStrictEqual([
{ scope: 'issues', title: 'Issues', count: '22' },
]);
});
it('sets other scopes count to empty string', () => {
const payload = { scopeTabs: ['milestones'], activeCount: '22' };
mutations[types.REQUEST_SEARCH_COUNTS](state, payload);
expect(state.inflatedScopeTabs).toStrictEqual([
{ scope: 'milestones', title: 'Milestones', count: '' },
]);
});
});
describe('RECEIVE_SEARCH_COUNTS_SUCCESS', () => {
it('sets the count from the input for all tabs', () => {
mutations[types.RECEIVE_SEARCH_COUNTS_SUCCESS](state, MOCK_SEARCH_COUNTS);
expect(state.inflatedScopeTabs).toStrictEqual(MOCK_SCOPE_TABS);
});
});
}); });
...@@ -5,7 +5,6 @@ import { MOCK_QUERY } from 'jest/search/mock_data'; ...@@ -5,7 +5,6 @@ import { MOCK_QUERY } from 'jest/search/mock_data';
import GlobalSearchTopbar from '~/search/topbar/components/app.vue'; import GlobalSearchTopbar from '~/search/topbar/components/app.vue';
import GroupFilter from '~/search/topbar/components/group_filter.vue'; import GroupFilter from '~/search/topbar/components/group_filter.vue';
import ProjectFilter from '~/search/topbar/components/project_filter.vue'; import ProjectFilter from '~/search/topbar/components/project_filter.vue';
import ScopeTabs from '~/search/topbar/components/scope_tabs.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -43,7 +42,6 @@ describe('GlobalSearchTopbar', () => { ...@@ -43,7 +42,6 @@ describe('GlobalSearchTopbar', () => {
const findGroupFilter = () => wrapper.find(GroupFilter); const findGroupFilter = () => wrapper.find(GroupFilter);
const findProjectFilter = () => wrapper.find(ProjectFilter); const findProjectFilter = () => wrapper.find(ProjectFilter);
const findSearchButton = () => wrapper.find(GlButton); const findSearchButton = () => wrapper.find(GlButton);
const findScopeTabs = () => wrapper.find(ScopeTabs);
describe('template', () => { describe('template', () => {
beforeEach(() => { beforeEach(() => {
...@@ -54,18 +52,6 @@ describe('GlobalSearchTopbar', () => { ...@@ -54,18 +52,6 @@ describe('GlobalSearchTopbar', () => {
expect(findTopbarForm().exists()).toBe(true); expect(findTopbarForm().exists()).toBe(true);
}); });
describe('Scope Tabs', () => {
it('renders when search param is set', () => {
createComponent({ query: { search: 'test' } });
expect(findScopeTabs().exists()).toBe(true);
});
it('does not render search param is blank', () => {
createComponent({ query: {} });
expect(findScopeTabs().exists()).toBe(false);
});
});
describe('Search box', () => { describe('Search box', () => {
it('renders always', () => { it('renders always', () => {
expect(findGlSearchBox().exists()).toBe(true); expect(findGlSearchBox().exists()).toBe(true);
......
import Vuex from 'vuex';
import { createLocalVue, mount } from '@vue/test-utils';
import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { MOCK_QUERY, MOCK_SCOPE_TABS } from 'jest/search/mock_data';
import ScopeTabs from '~/search/topbar/components/scope_tabs.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('ScopeTabs', () => {
let wrapper;
const actionSpies = {
fetchSearchCounts: jest.fn(),
setQuery: jest.fn(),
resetQuery: jest.fn(),
};
const defaultProps = {
scopeTabs: ['issues', 'merge_requests', 'milestones'],
count: '20',
};
const createComponent = (props = {}, initialState = {}) => {
const store = new Vuex.Store({
state: {
query: {
...MOCK_QUERY,
search: 'test',
},
...initialState,
},
actions: actionSpies,
});
wrapper = extendedWrapper(
mount(ScopeTabs, {
localVue,
store,
propsData: {
...defaultProps,
...props,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findScopeTabs = () => wrapper.find(GlTabs);
const findTabs = () => wrapper.findAll(GlTab);
const findBadges = () => wrapper.findAll(GlBadge);
const findTabsTitle = () =>
wrapper.findAll('[data-testid="tab-title"]').wrappers.map((w) => w.text());
const findBadgesTitle = () => findBadges().wrappers.map((w) => w.text());
const findBadgeByScope = (scope) => wrapper.findByTestId(`badge-${scope}`);
const findTabByScope = (scope) => wrapper.findByTestId(`tab-${scope}`);
describe('template', () => {
beforeEach(() => {
createComponent({}, { inflatedScopeTabs: MOCK_SCOPE_TABS });
});
it('always renders Scope Tabs', () => {
expect(findScopeTabs().exists()).toBe(true);
});
describe('findTabs', () => {
it('renders a tab for each scope', () => {
expect(findTabs()).toHaveLength(defaultProps.scopeTabs.length);
expect(findTabsTitle()).toStrictEqual([
'Issues',
'Titles and Descriptions',
'Merge requests',
]);
});
});
describe('findBadges', () => {
it('renders a badge for each scope', () => {
expect(findBadges()).toHaveLength(defaultProps.scopeTabs.length);
expect(findBadgesTitle()).toStrictEqual(['15', '5', '1']);
});
it('sets the variant to neutral for active tab only', () => {
expect(findBadgeByScope('issues').classes()).toContain('badge-neutral');
expect(findBadgeByScope('snippet_titles').classes()).toContain('badge-muted');
expect(findBadgeByScope('merge_requests').classes()).toContain('badge-muted');
});
});
});
describe('methods', () => {
beforeEach(() => {
createComponent({}, { inflatedScopeTabs: MOCK_SCOPE_TABS });
findTabByScope('snippet_titles').vm.$emit('click');
});
describe('handleTabChange', () => {
it('calls setQuery with scope, applies any search params from ALL_SCOPE_TABS, and sends nulls for page, state, confidential, and nav_source', () => {
expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
key: 'scope',
value: 'snippet_titles',
});
});
it('calls resetQuery and sends true for snippet_titles tab', () => {
expect(actionSpies.resetQuery).toHaveBeenCalledWith(expect.any(Object), true);
});
it('calls resetQuery and does not send true for other tabs', () => {
findTabByScope('issues').vm.$emit('click');
expect(actionSpies.resetQuery).toHaveBeenCalledWith(expect.any(Object), false);
});
});
});
});
...@@ -392,6 +392,63 @@ RSpec.describe SearchHelper do ...@@ -392,6 +392,63 @@ RSpec.describe SearchHelper do
end end
end end
describe 'search_filter_link' do
it 'renders a search filter link for the current scope' do
@scope = 'projects'
@search_results = double
expect(@search_results).to receive(:formatted_count).with('projects').and_return('23')
link = search_filter_link('projects', 'Projects')
expect(link).to have_css('li.active')
expect(link).to have_link('Projects', href: search_path(scope: 'projects'))
expect(link).to have_css('span.badge.badge-pill:not(.js-search-count):not(.hidden):not([data-url])', text: '23')
end
it 'renders a search filter link for another scope' do
link = search_filter_link('projects', 'Projects')
count_path = search_count_path(scope: 'projects')
expect(link).to have_css('li:not([class="active"])')
expect(link).to have_link('Projects', href: search_path(scope: 'projects'))
expect(link).to have_css("span.badge.badge-pill.js-search-count.hidden[data-url='#{count_path}']", text: '')
end
it 'merges in the current search params and given params' do
expect(self).to receive(:params).and_return(
ActionController::Parameters.new(
search: 'hello',
scope: 'ignored',
other_param: 'ignored'
)
)
link = search_filter_link('projects', 'Projects', search: { project_id: 23 })
expect(link).to have_link('Projects', href: search_path(scope: 'projects', search: 'hello', project_id: 23))
end
it 'restricts the params' do
expect(self).to receive(:params).and_return(
ActionController::Parameters.new(
search: 'hello',
unknown: 42
)
)
link = search_filter_link('projects', 'Projects')
expect(link).to have_link('Projects', href: search_path(scope: 'projects', search: 'hello'))
end
it 'assigns given data attributes on the list container' do
link = search_filter_link('projects', 'Projects', data: { foo: 'bar' })
expect(link).to have_css('li[data-foo="bar"]')
end
end
describe '#show_user_search_tab?' do describe '#show_user_search_tab?' do
subject { show_user_search_tab? } subject { show_user_search_tab? }
...@@ -584,86 +641,4 @@ RSpec.describe SearchHelper do ...@@ -584,86 +641,4 @@ RSpec.describe SearchHelper do
expect(search_sort_options).to eq(mock_created_sort) expect(search_sort_options).to eq(mock_created_sort)
end end
end end
describe '#search_nav_tabs' do
subject { search_nav_tabs }
let(:current_user) { nil }
before do
allow(self).to receive(:current_user).and_return(current_user)
end
context 'when @show_snippets is present' do
before do
@show_snippets = 1
end
it { is_expected.to eq([:snippet_titles]) }
context 'and @project is present' do
before do
@project = 1
allow(self).to receive(:project_search_tabs?).with(anything).and_return(true)
end
it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits, :users]) }
end
end
context 'when @project is present' do
before do
@project = 1
end
context 'when user has access to project' do
before do
allow(self).to receive(:project_search_tabs?).with(anything).and_return(true)
end
it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits, :users]) }
end
context 'when user does not have access to project' do
before do
allow(self).to receive(:project_search_tabs?).with(anything).and_return(false)
end
it { is_expected.to eq([]) }
end
context 'when user does not have access to read members for project' do
before do
allow(self).to receive(:project_search_tabs?).with(:members).and_return(false)
allow(self).to receive(:project_search_tabs?).with(:merge_requests).and_return(true)
allow(self).to receive(:project_search_tabs?).with(:milestones).and_return(true)
allow(self).to receive(:project_search_tabs?).with(:wiki_blobs).and_return(true)
allow(self).to receive(:project_search_tabs?).with(:issues).and_return(true)
allow(self).to receive(:project_search_tabs?).with(:blobs).and_return(true)
allow(self).to receive(:project_search_tabs?).with(:notes).and_return(true)
allow(self).to receive(:project_search_tabs?).with(:commits).and_return(true)
end
it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits]) }
end
end
context 'when @show_snippets and @project are not present' do
context 'when user has access to read users' do
before do
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
end
it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones, :users]) }
end
context 'when user does not have access to read users' do
before do
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
end
it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones]) }
end
end
end
end end
...@@ -6,6 +6,7 @@ RSpec.describe 'search/show' do ...@@ -6,6 +6,7 @@ RSpec.describe 'search/show' do
let(:search_term) { nil } let(:search_term) { nil }
before do before do
stub_template "search/_category.html.haml" => 'Category Partial'
stub_template "search/_results.html.haml" => 'Results Partial' stub_template "search/_results.html.haml" => 'Results Partial'
@search_term = search_term @search_term = search_term
...@@ -20,6 +21,7 @@ RSpec.describe 'search/show' do ...@@ -20,6 +21,7 @@ RSpec.describe 'search/show' do
end end
it 'does not render partials' do it 'does not render partials' do
expect(rendered).not_to render_template('search/_category')
expect(rendered).not_to render_template('search/_results') expect(rendered).not_to render_template('search/_results')
end end
end end
...@@ -28,6 +30,7 @@ RSpec.describe 'search/show' do ...@@ -28,6 +30,7 @@ RSpec.describe 'search/show' do
let(:search_term) { 'Search Foo' } let(:search_term) { 'Search Foo' }
it 'renders partials' do it 'renders partials' do
expect(rendered).to render_template('search/_category')
expect(rendered).to render_template('search/_results') expect(rendered).to render_template('search/_results')
end end
......
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