Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-01-30 18:09:47 +00:00
parent 412fe7ab55
commit cd99e8611a
68 changed files with 1051 additions and 1046 deletions

View File

@ -802,7 +802,7 @@ rspec fail-fast:
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"]
script:
- !reference [.base-script, script]
- rspec_fail_fast "${RSPEC_MATCHING_TESTS_PATH}" "--tag ~quarantine"
- rspec_fail_fast "${RSPEC_MATCHING_TESTS_PATH}" "--tag ~quarantine --tag ~zoekt"
artifacts:
expire_in: 7d
paths:

View File

@ -42,7 +42,7 @@ rspec foss-impact:
<% end %>
script:
- !reference [.base-script, script]
- rspec_paralellized_job "--tag ~quarantine --tag ~level:migration"
- rspec_paralellized_job "--tag ~quarantine --tag ~level:migration --tag ~zoekt"
artifacts:
expire_in: 7d
paths:

View File

@ -90,7 +90,7 @@ include:
.rspec-base-migration:
script:
- !reference [.base-script, script]
- rspec_paralellized_job "--tag ~quarantine"
- rspec_paralellized_job "--tag ~quarantine --tag ~zoekt"
.rspec-base-pg11:
extends:

View File

@ -104,7 +104,7 @@ export default {
</script>
<template>
<div class="gl-display-flex">
<div class="gl-display-flex gl-w-full">
<input
v-if="fieldName"
:name="fieldName"

View File

@ -1,24 +0,0 @@
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));
}

View File

@ -1,6 +1,5 @@
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
import { queryToObject } from '~/lib/utils/url_utility';
import refreshCounts from '~/pages/search/show/refresh_counts';
import syntaxHighlight from '~/syntax_highlight';
import { initSidebar, sidebarInitState } from './sidebar';
import { initSearchSort } from './sort';
@ -24,8 +23,4 @@ export const initSearchApp = () => {
setHighlightClass(query.search); // Code Highlighting
initBlobRefSwitcher(); // Code Search Branch Picker
if (!gon.features?.searchPageVerticalNav) {
refreshCounts(); // Other Scope Tab Counts
}
};

View File

@ -1,7 +1,6 @@
<script>
import { mapState } from 'vuex';
import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { SCOPE_ISSUES, SCOPE_MERGE_REQUESTS } from '../constants';
import ResultsFilters from './results_filters.vue';
@ -11,7 +10,6 @@ export default {
ResultsFilters,
ScopeNavigation,
},
mixins: [glFeatureFlagsMixin()],
computed: {
...mapState(['urlQuery']),
showFilters() {
@ -23,7 +21,7 @@ export default {
<template>
<section class="search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4 gl-mb-6 gl-mt-5">
<scope-navigation v-if="glFeatures.searchPageVerticalNav" />
<scope-navigation />
<results-filters v-if="showFilters" />
</section>
</template>

View File

@ -1,5 +1,4 @@
<script>
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { confidentialFilterData } from '../constants/confidential_filter_data';
import RadioFilter from './radio_filter.vue';
@ -8,19 +7,13 @@ export default {
components: {
RadioFilter,
},
mixins: [glFeatureFlagsMixin()],
computed: {
ffBasedXPadding() {
return this.glFeatures.searchPageVerticalNav ? 'gl-px-5' : 'gl-px-0';
},
},
confidentialFilterData,
};
</script>
<template>
<div>
<radio-filter :class="ffBasedXPadding" :filter-data="$options.confidentialFilterData" />
<radio-filter class="gl-px-5" :filter-data="$options.confidentialFilterData" />
<hr class="gl-my-5 gl-mx-5 gl-border-gray-100" />
</div>
</template>

View File

@ -1,7 +1,6 @@
<script>
import { GlButton, GlLink } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { confidentialFilterData } from '../constants/confidential_filter_data';
import { stateFilterData } from '../constants/state_filter_data';
import ConfidentialityFilter from './confidentiality_filter.vue';
@ -15,24 +14,17 @@ export default {
StatusFilter,
ConfidentialityFilter,
},
mixins: [glFeatureFlagsMixin()],
computed: {
...mapState(['urlQuery', 'sidebarDirty']),
showReset() {
return this.urlQuery.state || this.urlQuery.confidential;
},
searchPageVerticalNavFeatureFlag() {
return this.glFeatures.searchPageVerticalNav;
},
showConfidentialityFilter() {
return Object.values(confidentialFilterData.scopes).includes(this.urlQuery.scope);
},
showStatusFilter() {
return Object.values(stateFilterData.scopes).includes(this.urlQuery.scope);
},
ffBasedXPadding() {
return this.glFeatures.searchPageVerticalNav ? 'gl-px-5' : 'gl-px-0';
},
},
methods: {
...mapActions(['applyQuery', 'resetQuery']),
@ -42,13 +34,10 @@ export default {
<template>
<form class="gl-pt-5 gl-md-pt-0" @submit.prevent="applyQuery">
<hr
v-if="searchPageVerticalNavFeatureFlag"
class="gl-my-5 gl-mx-5 gl-border-gray-100 gl-display-none gl-md-display-block"
/>
<hr class="gl-my-5 gl-mx-5 gl-border-gray-100 gl-display-none gl-md-display-block" />
<status-filter v-if="showStatusFilter" />
<confidentiality-filter v-if="showConfidentialityFilter" />
<div class="gl-display-flex gl-align-items-center gl-mt-4" :class="ffBasedXPadding">
<div class="gl-display-flex gl-align-items-center gl-mt-4 gl-px-5">
<gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
{{ __('Apply') }}
</gl-button>

View File

@ -5,6 +5,7 @@ import { s__ } from '~/locale';
import Tracking from '~/tracking';
import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants';
import { formatSearchResultCount } from '../../store/utils';
import { slugifyWithUnderscore } from '../../../lib/utils/text_utility';
export default {
name: 'ScopeNavigation',
@ -46,6 +47,9 @@ export default {
isActive(scope, index) {
return this.urlQuery.scope ? this.urlQuery.scope === scope : index === 0;
},
qaSelectorValue(item) {
return `${slugifyWithUnderscore(item.label)}_tab`;
},
},
NAV_LINK_DEFAULT_CLASSES,
NAV_LINK_COUNT_DEFAULT_CLASSES,
@ -62,6 +66,7 @@ export default {
class="gl-mb-1"
:href="item.link"
:active="isActive(scope, index)"
:data-qa-selector="qaSelectorValue(item)"
@click="handleClick(scope)"
><span>{{ item.label }}</span
><span v-if="item.count" :class="countClasses(isActive(scope, index))">

View File

@ -1,5 +1,4 @@
<script>
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { stateFilterData } from '../constants/state_filter_data';
import RadioFilter from './radio_filter.vue';
@ -8,19 +7,13 @@ export default {
components: {
RadioFilter,
},
mixins: [glFeatureFlagsMixin()],
computed: {
ffBasedXPadding() {
return this.glFeatures.searchPageVerticalNav ? 'gl-px-5' : 'gl-px-0';
},
},
stateFilterData,
};
</script>
<template>
<div>
<radio-filter :class="ffBasedXPadding" :filter-data="$options.stateFilterData" />
<radio-filter class="gl-px-5" :filter-data="$options.stateFilterData" />
<hr class="gl-my-5 gl-mx-5 gl-border-gray-100" />
</div>
</template>

View File

@ -1,7 +1,6 @@
<script>
import { GlSearchBoxByClick, GlButton } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { s__ } from '~/locale';
import { parseBoolean } from '~/lib/utils/common_utils';
import MarkdownDrawer from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue';
@ -31,7 +30,6 @@ export default {
ProjectFilter,
MarkdownDrawer,
},
mixins: [glFeatureFlagsMixin()],
props: {
groupInitialJson: {
type: Object,
@ -70,9 +68,6 @@ export default {
showSyntaxOptions() {
return this.elasticsearchEnabled && this.isDefaultBranch;
},
hasVerticalNav() {
return this.glFeatures.searchPageVerticalNav;
},
isDefaultBranch() {
return !this.query.repository_ref || this.query.repository_ref === this.defaultBranchName;
},
@ -130,6 +125,6 @@ export default {
<project-filter :initial-data="projectInitialJson" />
</div>
</div>
<hr v-if="hasVerticalNav" class="gl-mt-5 gl-mb-0 gl-border-gray-100" />
<hr class="gl-mt-5 gl-mb-0 gl-border-gray-100" />
</section>
</template>

View File

@ -239,6 +239,8 @@ class Projects::BlobController < Projects::ApplicationController
@last_commit = @repository.last_commit_for_path(@commit.id, @blob.path, literal_pathspec: true)
@code_navigation_path = Gitlab::CodeNavigationPath.new(@project, @blob.commit_id).full_json_path_for(@blob.path)
allow_lfs_direct_download
render 'show'
end
@ -282,6 +284,30 @@ class Projects::BlobController < Projects::ApplicationController
def visitor_id
current_user&.id
end
def allow_lfs_direct_download
return unless directly_downloading_lfs_object? && content_security_policy_enabled?
return unless (lfs_object = @project.lfs_objects.find_by_oid(@blob.lfs_oid))
request.content_security_policy.directives['connect-src'] ||= []
request.content_security_policy.directives['connect-src'] << lfs_src(lfs_object)
end
def directly_downloading_lfs_object?
Gitlab.config.lfs.enabled &&
!Gitlab.config.lfs.object_store.proxy_download &&
@blob&.stored_externally?
end
def content_security_policy_enabled?
Gitlab.config.gitlab.content_security_policy.enabled
end
def lfs_src(lfs_object)
file = lfs_object.file
file = file.cdn_enabled_url(request.remote_ip) if file.respond_to?(:cdn_enabled_url)
file.url
end
end
Projects::BlobController.prepend_mod

View File

@ -30,9 +30,6 @@ class SearchController < ApplicationController
end
before_action :check_search_rate_limit!, only: search_rate_limited_endpoints
before_action only: :show do
push_frontend_feature_flag(:search_page_vertical_nav, current_user)
end
before_action only: :show do
update_scope_for_code_search
end

View File

@ -1,8 +1,9 @@
# frozen_string_literal: true
module BizibleHelper
def bizible_enabled?
Feature.enabled?(:ecomm_instrumentation, type: :ops) &&
def bizible_enabled?(invite_email = nil)
invite_email.blank? &&
Feature.enabled?(:ecomm_instrumentation, type: :ops) &&
Gitlab.config.extra.has_key?('bizible') &&
Gitlab.config.extra.bizible.present? &&
Gitlab.config.extra.bizible == true

View File

@ -15,6 +15,8 @@ module ExploreHelper
namespace_id: params[:namespace_id]
}
exist_opts[:language] = params[:language] if Feature.enabled?(:project_language_search, current_user)
options = exist_opts.merge(options).delete_if { |key, value| value.blank? }
request_path_with_options(options)
end

View File

@ -55,6 +55,8 @@ module Ci
has_one :"job_artifacts_#{key}", -> { where(file_type: value) }, class_name: 'Ci::JobArtifact', foreign_key: :job_id, inverse_of: :job
end
has_one :runner_machine, through: :metadata, class_name: 'Ci::RunnerMachine'
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, foreign_key: :build_id, inverse_of: :build
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', foreign_key: :build_id, inverse_of: :build
@ -179,6 +181,8 @@ module Ci
run_after_commit { build.execute_hooks }
end
after_commit :track_ci_secrets_management_id_tokens_usage, on: :create, if: :id_tokens?
class << self
# This is needed for url_for to work,
# as the controller is JobsController
@ -1281,6 +1285,10 @@ module Ci
.increment(status: status)
end
end
def track_ci_secrets_management_id_tokens_usage
::Gitlab::UsageDataCounters::HLLRedisCounter.track_event('i_ci_secrets_management_id_tokens_build_created', values: user_id)
end
end
end

View File

@ -8,6 +8,9 @@ module Ci
belongs_to :runner
has_many :build_metadata, class_name: 'Ci::BuildMetadata'
has_many :builds, through: :build_metadata, class_name: 'Ci::Build'
belongs_to :runner_version, inverse_of: :runner_machines, primary_key: :version, foreign_key: :version,
class_name: 'Ci::RunnerVersion'
validates :runner, presence: true
validates :machine_xid, presence: true, length: { maximum: 64 }

View File

@ -20,6 +20,8 @@ module Ci
recommended: 'Upgrade is available and recommended for the runner.'
}.freeze
has_many :runner_machines, inverse_of: :runner_version, foreign_key: :version, class_name: 'Ci::RunnerMachine'
# Override auto generated negative scope (from available) so the scope has expected behavior
scope :not_available, -> { where(status: :not_available) }

View File

@ -2,7 +2,7 @@
- add_page_specific_style 'page_bundles/search'
- params[:visibility_level] ||= []
.top-area
.top-area.gl-flex-direction-column-reverse
.scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0.gl-w-full
.fade-left= sprite_icon('chevron-lg-left', size: 12)
.fade-right= sprite_icon('chevron-lg-right', size: 12)
@ -12,16 +12,7 @@
= gl_tab_link_to _('Internal'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
= gl_tab_link_to _('Public'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
.nav-controls.gl-pl-2
.search-holder
= render 'shared/projects/search_form', autofocus: true, admin_view: true
- if params[:namespace_id].present?
- namespace = Namespace.find(params[:namespace_id])
- selected_text = "#{namespace.kind}: #{namespace.full_path}" if namespace
.js-namespace-select{ data: { field_name: 'namespace_id', selected_id: namespace&.id, selected_text: selected_text, update_location: 'true' } }
= link_to new_project_path, class: 'gl-button btn btn-confirm' do
= _('New Project')
.nav-controls
= render 'shared/projects/search_form', autofocus: true, admin_view: true
= render 'projects'

View File

@ -1,7 +1,7 @@
= content_for :flash_message do
= render 'shared/project_limit'
.page-title-holder.d-flex.align-items-center
.page-title-holder.gl-display-flex.gl-align-items-center
%h1.page-title.gl-font-size-h-display= _('Projects')
- if current_user.can_create_project?
@ -10,7 +10,7 @@
= _("New project")
.top-area
.scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0
.scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-flex-basis-0.gl-min-w-0
.fade-left= sprite_icon('chevron-lg-left', size: 12)
.fade-right= sprite_icon('chevron-lg-right', size: 12)
= render 'dashboard/projects_nav'

View File

@ -1,4 +1,4 @@
- if bizible_enabled?
- if bizible_enabled?(@invite_email)
<!-- Bizible -->
= javascript_tag nonce: content_security_policy_nonce do
:plain

View File

@ -1,37 +0,0 @@
- 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)
= gl_tabs_nav({ class: 'scrolling-tabs nav-links', data: { testid: 'search-filter' } }) do
- 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?(:wiki)
= search_filter_link 'wiki_blobs', _("Wiki")
- if project_search_tabs?(:commits)
= search_filter_link 'commits', _("Commits")
- if project_search_tabs?(:notes)
= search_filter_link 'notes', _("Comments")
- if project_search_tabs?(:milestones)
= search_filter_link 'milestones', _("Milestones")
= users
- elsif @search_service_presenter.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' }
= render_if_exists 'search/category_code' if feature_flag_tab_enabled?(:global_search_code_tab)
= render_if_exists 'search/epics_filter_link'
= search_filter_link 'issues', _("Issues") if feature_flag_tab_enabled?(:global_search_issues_tab)
= search_filter_link 'merge_requests', _("Merge requests") if feature_flag_tab_enabled?(:global_search_merge_requests_tab)
= render_if_exists 'search/category_wiki' if feature_flag_tab_enabled?(:global_search_wiki_tab)
= render_if_exists 'search/category_elasticsearch'
= search_filter_link 'milestones', _("Milestones")
= users

View File

@ -1,18 +1,9 @@
- search_bar_classes = 'search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4'
= render_if_exists 'shared/promotions/promote_advanced_search'
- if Feature.enabled?(:search_page_vertical_nav, current_user)
.results.gl-md-display-flex.gl-mt-0
#js-search-sidebar{ class: search_bar_classes, data: { navigation_json: search_navigation_json } }
.gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
= render partial: 'search/results_status' unless @search_objects.to_a.empty?
= render partial: 'search/results_list'
- else
= render partial: 'search/results_status' unless @search_objects.to_a.empty?
.results.gl-md-display-flex.gl-mt-3
- if %w[issues merge_requests].include?(@scope)
#js-search-sidebar{ class: search_bar_classes, data: { navigation_json: search_navigation_json } }
.gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
= render partial: 'search/results_list'
.results.gl-md-display-flex.gl-mt-0
#js-search-sidebar{ class: search_bar_classes, data: { navigation_json: search_navigation_json } }
.gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
= render partial: 'search/results_status' unless @search_objects.to_a.empty?
= render partial: 'search/results_list'

View File

@ -1,6 +1,25 @@
- return unless @search_service_presenter.show_results_status?
- if Feature.enabled?(:search_page_vertical_nav, current_user)
= render partial: 'search/results_status_vert_nav'
- else
= render partial: 'search/results_status_horiz_nav'
.search-results-status
.gl-display-flex.gl-flex-direction-column
.gl-p-5.gl-display-flex
.gl-md-display-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
- unless @search_service_presenter.without_count?
= search_entries_info(@search_objects, @scope, @search_term)
- unless @search_service_presenter.show_snippets?
- if @project
- link_to_project = link_to(@project.full_name, @project, class: 'ml-md-1')
- if @scope == 'blobs'
= _("in")
.mx-md-1
#js-blob-ref-switcher{ data: { "project-id" => @project.id, "ref" => repository_ref(@project), "field-name": "repository_ref" } }
= s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
- else
= _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
- elsif @group
- link_to_group = link_to(@group.name, @group, class: 'ml-md-1')
= _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
- if @search_service_presenter.show_sort_dropdown?
.gl-md-display-flex.gl-flex-direction-column
#js-search-sort{ data: { "search-sort-options" => search_sort_options.to_json } }
%hr.gl-mb-5.gl-mt-0.gl-border-gray-100.gl-w-full

View File

@ -1,22 +0,0 @@
.search-results-status
.row-content-block.gl-display-flex
.gl-md-display-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
- unless @search_service_presenter.without_count?
= search_entries_info(@search_objects, @scope, @search_term)
- unless @search_service_presenter.show_snippets?
- if @project
- link_to_project = link_to(@project.full_name, @project, class: 'ml-md-1')
- if @scope == 'blobs'
= _("in")
.mx-md-1
#js-blob-ref-switcher{ data: { "project-id" => @project.id, "ref" => repository_ref(@project), "field-name": "repository_ref" } }
= s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
- else
= _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
- elsif @group
- link_to_group = link_to(@group.name, @group, class: 'ml-md-1')
= _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
- if @search_service_presenter.show_sort_dropdown?
.gl-md-display-flex.gl-flex-direction-column
#js-search-sort{ data: { "search-sort-options" => search_sort_options.to_json } }

View File

@ -1,23 +0,0 @@
.search-results-status
.gl-display-flex.gl-flex-direction-column
.gl-p-5.gl-display-flex.gl-max-w-full.gl-sm-flex-direction-column
.gl-md-display-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1.gl-text-truncate
- unless @search_service_presenter.without_count?
= search_entries_info(@search_objects, @scope, @search_term)
- unless @search_service_presenter.show_snippets?
- if @project
- link_to_project = link_to(@project.full_name, @project, class: 'ml-md-1 gl-text-truncate search-wrap-f-md-down')
- if @scope == 'blobs'
= _("in")
.mx-md-1
#js-blob-ref-switcher{ data: { "project-id" => @project.id, "ref" => repository_ref(@project), "field-name": "repository_ref" } }
= s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
- else
= _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
- elsif @group
- link_to_group = link_to(@group.name, @group, class: 'ml-md-1')
= _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
- if @search_service_presenter.show_sort_dropdown?
.gl-md-display-flex.gl-flex-direction-column
#js-search-sort{ data: { "search-sort-options" => search_sort_options.to_json } }
%hr.gl-mb-5.gl-mt-0.gl-border-gray-100.gl-w-full

View File

@ -22,6 +22,4 @@
.gl-mt-3
#js-search-topbar{ data: { "group-initial-json": group_attributes.to_json, "project-initial-json": project_attributes.to_json, "elasticsearch-enabled": @search_service_presenter.advanced_search_enabled?.to_s, "default-branch-name": @project&.default_branch } }
- if @search_term
- if Feature.disabled?(:search_page_vertical_nav, current_user)
= render 'search/category'
= render 'search/results'

View File

@ -1,5 +1,5 @@
- @sort ||= sort_value_latest_activity
.dropdown.js-project-filter-dropdown-wrap.gl-display-inline
.dropdown.js-project-filter-dropdown-wrap.gl-display-inline{ class: 'gl-m-0!' }
= dropdown_toggle(projects_sort_options_hash[@sort], { toggle: 'dropdown', display: 'static' }, { id: 'sort-projects-dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header

View File

@ -1,9 +1,11 @@
- placeholder = local_assigns[:search_form_placeholder] ? search_form_placeholder : _('Filter by name')
- admin_view ||= false
- top_padding = admin_view ? 'gl-lg-pt-3' : ''
= form_tag filter_projects_path, method: :get, class: 'project-filter-form', data: { qa_selector: 'project_filter_form_container' }, id: 'project-filter-form' do |f|
= form_tag filter_projects_path, method: :get, class: "project-filter-form gl-display-flex! gl-flex-wrap-wrap gl-w-full gl-gap-3 #{top_padding}", data: { qa_selector: 'project_filter_form_container' }, id: 'project-filter-form' do |f|
= search_field_tag :name, params[:name],
placeholder: placeholder,
class: "project-filter-form-field form-control input-short js-projects-list-filter",
class: "project-filter-form-field form-control input-short js-projects-list-filter gl-m-0!",
spellcheck: false,
id: 'project-filter-form-field',
autofocus: local_assigns[:autofocus]
@ -27,14 +29,14 @@
= hidden_field_tag :language, params[:language]
- if Feature.enabled?(:project_language_search, current_user)
.dropdown.inline
.dropdown{ class: 'gl-m-0!' }
= dropdown_toggle(search_language_placeholder, { toggle: 'dropdown', testid: 'project-language-dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li
= link_to _('Any'), filter_projects_path(language: nil)
= link_to _('Any'), filter_projects_path(language: nil, name: nil)
- programming_languages.each do |language|
%li
= link_to filter_projects_path(language: language.id), class: language_state_class(language) do
= link_to filter_projects_path(language: language.id, name: nil), class: language_state_class(language) do
= language.name
= submit_tag nil, class: 'gl-display-none!'
@ -42,3 +44,13 @@
= render 'shared/projects/dropdown'
= render_if_exists 'shared/projects/search_fields'
- if admin_view
- if params[:namespace_id].present?
- namespace = Namespace.find(params[:namespace_id])
- selected_text = "#{namespace.kind}: #{namespace.full_path}" if namespace
.gl-display-flex.gl-w-full.gl-md-w-auto{ class: 'gl-m-0!' }
.js-namespace-select{ data: { field_name: 'namespace_id', selected_id: namespace&.id, selected_text: selected_text, update_location: 'true' } }
= link_to new_project_path, class: 'gl-button btn btn-confirm gl-display-inline gl-mb-0!' do
= _('New Project')

View File

@ -1,8 +0,0 @@
---
name: search_page_vertical_nav
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97784
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373613
milestone: '15.5'
type: development
group: group::global search
default_enabled: true

View File

@ -0,0 +1,26 @@
---
key_path: redis_hll_counters.ci_secrets_management.i_ci_secrets_management_id_tokens_build_created_monthly
description: Monthly count of unique users who created a pipeline with id_tokens (JWT tokens)
product_section: ops
product_stage: verify
product_group: pipeline_authoring
product_category: secrets_management
value_type: number
status: active
milestone: "15.9"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109686
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
options:
events:
- i_ci_secrets_management_id_tokens_build_created

View File

@ -0,0 +1,26 @@
---
key_path: redis_hll_counters.ci_secrets_management.i_ci_secrets_management_id_tokens_build_created_weekly
description: Weekly count of unique users who created a pipeline with id_tokens (JWT tokens)
product_section: ops
product_stage: verify
product_group: pipeline_authoring
product_category: secrets_management
value_type: number
status: active
milestone: "15.9"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109686
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
options:
events:
- i_ci_secrets_management_id_tokens_build_created

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddMergeRequestMetaToMergeRequestsComplianceViolations < Gitlab::Database::Migration[2.1]
def change
add_column :merge_requests_compliance_violations, :merged_at, :datetime_with_timezone
add_column :merge_requests_compliance_violations, :target_project_id, :integer
add_column :merge_requests_compliance_violations, :title, :text # rubocop:disable Migration/AddLimitToTextColumns
add_column :merge_requests_compliance_violations, :target_branch, :text # rubocop:disable Migration/AddLimitToTextColumns
end
end

View File

@ -0,0 +1 @@
1ed2531b3655b46f67c523f4a588471b1b0cb291b24c9491e6efe89d644546d8

View File

@ -17920,7 +17920,11 @@ CREATE TABLE merge_requests_compliance_violations (
violating_user_id bigint NOT NULL,
merge_request_id bigint NOT NULL,
reason smallint NOT NULL,
severity_level smallint DEFAULT 0 NOT NULL
severity_level smallint DEFAULT 0 NOT NULL,
merged_at timestamp with time zone,
target_project_id integer,
title text,
target_branch text
);
CREATE SEQUENCE merge_requests_compliance_violations_id_seq

View File

@ -43,6 +43,17 @@ a feature has become "implemented", major changes should get new blueprints.
The canonical place for the latest set of instructions (and the likely source
of this file) is [here](/doc/architecture/blueprints/_template.md).
Blueprint statuses you can use:
- "proposed"
- "ongoing"
- "accepted"
- "implemented"
- "rejected"
Any other one-word status should be fine, see which ones have colors defined:
https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/content/assets/stylesheets/labels.scss#L22
-->
# {+ Title of Blueprint +}

View File

@ -943,7 +943,7 @@ To open the Admin Area:
To select your avatar:
```markdown
1. On the top bar, in the top right corner, select your avatar.
1. On the top bar, in the upper-right corner, select your avatar.
```
To save the selection in some dropdown lists:

View File

@ -1288,6 +1288,12 @@ you're talking about the product version or the subscription tier.
See also [downgrade](#downgrade) and [roll back](#roll-back).
## upper left, upper right
Use **upper left** and **upper right** instead of **top left** and **top right**. Hyphenate as adjectives (for example, **upper-left corner**).
For details, see the [Microsoft style guide](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/u/upper-left-upper-right).
## useful
Do not use **useful**. If the user doesn't find the process to be useful, we lose their trust. ([Vale](../testing.md#vale) rule: [`Simplicity.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Simplicity.yml))

View File

@ -233,14 +233,40 @@ Sidekiq performance. Return them to their default values if you see increased `s
in your Sidekiq logs. For more information, see
[issue 322147](https://gitlab.com/gitlab-org/gitlab/-/issues/322147).
### Access requirements for self-managed AWS OpenSearch Service using fine-grained access control
### Access requirements
#### Elasticsearch with role privileges
To access Elasticsearch, you must have at least the following privileges in GitLab:
```json
{
"cluster": ["monitor"],
"indices": [
{
"names": ["gitlab-*"],
"privileges": [
"create_index",
"delete_index",
"view_index_metadata",
"read",
"manage",
"write"
]
}
]
}
```
For more information, see [Elasticsearch security privileges](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html).
#### AWS OpenSearch Service with fine-grained access control
To use the self-managed AWS OpenSearch Service with GitLab using fine-grained access control, try one of the
[recommended configurations](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-recommendations).
Configure your instance's domain access policies to allow `es:ESHttp*` actions. You can customize
the following example configuration to limit principals or resources.
See [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html) for details.
the following example configuration to limit principals or resources:
```json
{
@ -262,18 +288,20 @@ See [Identity and Access Management in Amazon OpenSearch Service](https://docs.a
}
```
#### Connecting with a master user in the internal database
For more information, see [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html).
##### Connecting with a master user in the internal database
When using fine-grained access control with a user in the internal database, you should use HTTP basic
authentication to connect to OpenSearch. You can provide the master username and password as part of the
OpenSearch URL or in the **Username** and **Password** text boxes in the Advanced Search settings. See
[Tutorial: Internal user database and HTTP basic authentication](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac-walkthrough-basic.html) for details.
#### Connecting with an IAM user
##### Connecting with an IAM user
When using fine-grained access control with IAM credentials, you can provide the credentials in the **AWS OpenSearch IAM credentials** section in the Advanced Search settings.
#### Permissions for fine-grained access control
##### Permissions for fine-grained access control
The following permissions are required for Advanced Search. See [Creating roles](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-roles) for details.

View File

@ -254,7 +254,7 @@
category: terraform
redis_slot: terraform
aggregation: weekly
# Pipeline Authoring
# Pipeline Authoring group
- name: o_pipeline_authoring_unique_users_committing_ciconfigfile
category: pipeline_authoring
redis_slot: pipeline_authoring
@ -263,6 +263,10 @@
category: pipeline_authoring
redis_slot: pipeline_authoring
aggregation: weekly
- name: i_ci_secrets_management_id_tokens_build_created
category: ci_secrets_management
redis_slot: ci_secrets_management
aggregation: weekly
# Merge request widgets
- name: users_expanding_secure_security_report
redis_slot: secure

View File

@ -4,9 +4,9 @@ module QA
module Page
module Search
class Results < QA::Page::Base
view 'app/views/search/_category.html.haml' do
element :code_tab
element :projects_tab
view 'app/assets/javascripts/search/sidebar/components/scope_navigation.vue' do
element :code_tab, ':data-qa-selector="qaSelectorValue(item)"' # rubocop:disable QA/ElementWithPattern
element :projects_tab, ':data-qa-selector="qaSelectorValue(item)"' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/search/results/_blob_data.html.haml' do

View File

@ -80,6 +80,7 @@ RSpec.describe 'Database schema', feature_category: :database do
ldap_group_links: %w[group_id],
members: %w[source_id created_by_id],
merge_requests: %w[last_edited_by_id state_id],
merge_requests_compliance_violations: %w[target_project_id],
merge_request_diff_commits: %w[commit_author_id committer_id],
namespaces: %w[owner_id parent_id],
notes: %w[author_id commit_id noteable_id updated_by_id resolved_by_id confirmed_by_id discussion_id],

View File

@ -20,4 +20,11 @@ FactoryBot.define do
trait :object_storage do
file_store { LfsObjectUploader::Store::REMOTE }
end
trait :with_lfs_object_dot_iso_file do
with_file
object_storage
oid { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
size { 133 }
end
end

View File

@ -253,9 +253,35 @@ FactoryBot.define do
create_templates { nil }
create_branch { nil }
create_tag { nil }
lfs { false }
end
after :create do |project, evaluator|
# Specify `lfs: true` to create the LfsObject for the LFS file in the test repo:
# https://gitlab.com/gitlab-org/gitlab-test/-/blob/master/files/lfs/lfs_object.iso
if evaluator.lfs
RSpec::Mocks.with_temporary_scope do
# If lfs object store is disabled we need to mock
unless Gitlab.config.lfs.object_store.enabled
config = Gitlab.config.lfs.object_store.merge('enabled' => true)
allow(LfsObjectUploader).to receive(:object_store_options).and_return(config)
Fog.mock!
Fog::Storage.new(LfsObjectUploader.object_store_credentials).tap do |connection|
connection.directories.create(key: config.remote_directory) # rubocop:disable Rails/SaveBang
# Cleanup remaining files
connection.directories.each do |directory|
directory.files.map(&:destroy)
end
rescue Excon::Error::Conflict
end
end
lfs_object = create(:lfs_object, :with_lfs_object_dot_iso_file)
create(:lfs_objects_project, project: project, lfs_object: lfs_object)
end
end
if evaluator.create_templates
templates_path = "#{evaluator.create_templates}_templates"
@ -286,7 +312,6 @@ FactoryBot.define do
"README on branch #{evaluator.create_branch}",
message: 'Add README.md',
branch_name: evaluator.create_branch)
end
if evaluator.create_tag

View File

@ -3,242 +3,235 @@
require 'spec_helper'
RSpec.describe 'User searches for code', :js, :disable_rate_limiter, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, :repository, namespace: user.namespace) }
where(search_page_vertical_nav_enabled: [true, false])
with_them do
context 'when signed in' do
context 'when signed in' do
before do
project.add_maintainer(user)
sign_in(user)
end
context 'when on a project page' do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
project.add_maintainer(user)
sign_in(user)
visit(project_path(project))
end
context 'when on a project page' do
it 'finds a file' do
submit_search('application.js')
select_search_scope('Code')
expect(page).to have_selector('.results', text: 'application.js')
expect(page).to have_selector('.file-content .code')
expect(page).to have_selector("span.line[lang='javascript']")
expect(page).to have_link('application.js', href: %r{master/files/js/application.js})
expect(page).to have_button('Copy file path')
end
end
context 'when on a project search page' do
before do
visit(search_path)
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
end
include_examples 'top right search form'
include_examples 'search timeouts', 'blobs' do
let(:additional_params) { { project_id: project.id } }
end
context 'when searching code' do
let(:expected_result) { 'Update capybara, rspec-rails, poltergeist to recent versions' }
before do
visit(project_path(project))
fill_in('dashboard_search', with: 'rspec')
find('.gl-search-box-by-click-search-button').click
end
it 'finds a file' do
submit_search('application.js')
it 'finds code and links to blob' do
expect(page).to have_selector('.results', text: expected_result)
find("#blob-L3").click
expect(current_url).to match(%r{blob/master/.gitignore#L3})
end
it 'finds code and links to blame' do
expect(page).to have_selector('.results', text: expected_result)
find("#blame-L3").click
expect(current_url).to match(%r{blame/master/.gitignore#L3})
end
it_behaves_like 'code highlight' do
subject { page }
end
end
it 'search multiple words with refs switching' do
expected_result = 'Use `snake_case` for naming files'
search = 'for naming files'
ref_selector = 'v1.0.0'
fill_in('dashboard_search', with: search)
find('.gl-search-box-by-click-search-button').click
expect(page).to have_selector('.results', text: expected_result)
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
fill_in 'Search by Git revision', with: ref_selector
wait_for_requests
find('li', text: ref_selector, match: :prefer_exact).click
end
expect(page).to have_selector('.results', text: expected_result)
expect(find_field('dashboard_search').value).to eq(search)
expect(find("#blob-L1502")[:href]).to match(%r{blob/v1.0.0/files/markdown/ruby-style-guide.md#L1502})
expect(find("#blame-L1502")[:href]).to match(%r{blame/v1.0.0/files/markdown/ruby-style-guide.md#L1502})
end
end
context 'when :new_header_search is true' do
context 'search code within refs' do
let(:ref_name) { 'v1.0.0' }
before do
# This feature is disabled by default in spec_helper.rb.
# We missed a feature breaking bug, so to prevent this regression, testing both scenarios for this spec.
# This can be removed as part of closing https://gitlab.com/gitlab-org/gitlab/-/issues/339348.
stub_feature_flags(new_header_search: true)
visit(project_tree_path(project, ref_name))
submit_search('gitlab-grack')
select_search_scope('Code')
expect(page).to have_selector('.results', text: 'application.js')
expect(page).to have_selector('.file-content .code')
expect(page).to have_selector("span.line[lang='javascript']")
expect(page).to have_link('application.js', href: %r{master/files/js/application.js})
expect(page).to have_button('Copy file path')
end
end
context 'when on a project search page' do
before do
visit(search_path)
find('[data-testid="project-filter"]').click
it 'shows ref switcher in code result summary' do
expect(find('.ref-selector')).to have_text(ref_name)
end
it 'persists branch name across search' do
find('.gl-search-box-by-click-search-button').click
expect(find('.ref-selector')).to have_text(ref_name)
end
# this example is use to test the design that the refs is not
# only represent the branch as well as the tags.
it 'ref switcher list all the branches and tags' do
find('.ref-selector').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
page.within('.ref-selector') do
expect(page).to have_selector('li', text: 'add-ipython-files')
expect(page).to have_selector('li', text: 'v1.0.0')
end
end
include_examples 'top right search form'
include_examples 'search timeouts', 'blobs' do
let(:additional_params) { { project_id: project.id } }
end
context 'when searching code' do
let(:expected_result) { 'Update capybara, rspec-rails, poltergeist to recent versions' }
before do
fill_in('dashboard_search', with: 'rspec')
find('.gl-search-box-by-click-search-button').click
end
it 'finds code and links to blob' do
expect(page).to have_selector('.results', text: expected_result)
find("#blob-L3").click
expect(current_url).to match(%r{blob/master/.gitignore#L3})
end
it 'finds code and links to blame' do
expect(page).to have_selector('.results', text: expected_result)
find("#blame-L3").click
expect(current_url).to match(%r{blame/master/.gitignore#L3})
end
it_behaves_like 'code highlight' do
subject { page }
end
end
it 'search multiple words with refs switching' do
expected_result = 'Use `snake_case` for naming files'
search = 'for naming files'
fill_in('dashboard_search', with: search)
find('.gl-search-box-by-click-search-button').click
expect(page).to have_selector('.results', text: expected_result)
it 'search result changes when refs switched' do
ref = 'master'
expect(find('.results')).not_to have_content('path = gitlab-grack')
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
find('li', text: 'v1.0.0').click
fill_in _('Search by Git revision'), with: ref
wait_for_requests
find('li', text: ref).click
end
expect(page).to have_selector('.results', text: expected_result)
expect(find_field('dashboard_search').value).to eq(search)
expect(find("#blob-L1502")[:href]).to match(%r{blob/v1.0.0/files/markdown/ruby-style-guide.md#L1502})
expect(find("#blame-L1502")[:href]).to match(%r{blame/v1.0.0/files/markdown/ruby-style-guide.md#L1502})
expect(page).to have_selector('.results', text: 'path = gitlab-grack')
end
end
context 'when :new_header_search is true' do
context 'search code within refs' do
let(:ref_name) { 'v1.0.0' }
before do
# This feature is disabled by default in spec_helper.rb.
# We missed a feature breaking bug, so to prevent this regression, testing both scenarios for this spec.
# This can be removed as part of closing https://gitlab.com/gitlab-org/gitlab/-/issues/339348.
stub_feature_flags(new_header_search: true)
visit(project_tree_path(project, ref_name))
submit_search('gitlab-grack')
select_search_scope('Code')
end
it 'shows ref switcher in code result summary' do
expect(find('.ref-selector')).to have_text(ref_name)
end
it 'persists branch name across search' do
find('.gl-search-box-by-click-search-button').click
expect(find('.ref-selector')).to have_text(ref_name)
end
# this example is use to test the design that the refs is not
# only represent the branch as well as the tags.
it 'ref switcher list all the branches and tags' do
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
expect(page).to have_selector('li', text: 'add-ipython-files')
expect(page).to have_selector('li', text: 'v1.0.0')
end
end
it 'search result changes when refs switched' do
ref = 'master'
expect(find('.results')).not_to have_content('path = gitlab-grack')
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
fill_in _('Search by Git revision'), with: ref
wait_for_requests
find('li', text: ref).click
end
expect(page).to have_selector('.results', text: 'path = gitlab-grack')
end
end
end
context 'when :new_header_search is false' do
context 'search code within refs' do
let(:ref_name) { 'v1.0.0' }
before do
# This feature is disabled by default in spec_helper.rb.
# We missed a feature breaking bug, so to prevent this regression, testing both scenarios for this spec.
# This can be removed as part of closing https://gitlab.com/gitlab-org/gitlab/-/issues/339348.
stub_feature_flags(new_header_search: false)
visit(project_tree_path(project, ref_name))
submit_search('gitlab-grack')
select_search_scope('Code')
end
it 'shows ref switcher in code result summary' do
expect(find('.ref-selector')).to have_text(ref_name)
end
it 'persists branch name across search' do
find('.gl-search-box-by-click-search-button').click
expect(find('.ref-selector')).to have_text(ref_name)
end
# this example is use to test the design that the refs is not
# only represent the branch as well as the tags.
it 'ref switcher list all the branches and tags' do
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
expect(page).to have_selector('li', text: 'add-ipython-files')
expect(page).to have_selector('li', text: 'v1.0.0')
end
end
it 'search result changes when refs switched' do
ref = 'master'
expect(find('.results')).not_to have_content('path = gitlab-grack')
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
fill_in _('Search by Git revision'), with: ref
wait_for_requests
find('li', text: ref).click
end
expect(page).to have_selector('.results', text: 'path = gitlab-grack')
end
end
end
it 'no ref switcher shown in issue result summary' do
issue = create(:issue, title: 'test', project: project)
visit(project_tree_path(project))
submit_search('test')
select_search_scope('Code')
expect(page).to have_selector('.ref-selector')
select_search_scope('Issues')
expect(find(:css, '.results')).to have_link(issue.title)
expect(page).not_to have_selector('.ref-selector')
end
end
context 'when signed out' do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
end
context 'when :new_header_search is false' do
context 'search code within refs' do
let(:ref_name) { 'v1.0.0' }
context 'when block_anonymous_global_searches is enabled' do
it 'is redirected to login page' do
visit(search_path)
before do
# This feature is disabled by default in spec_helper.rb.
# We missed a feature breaking bug, so to prevent this regression, testing both scenarios for this spec.
# This can be removed as part of closing https://gitlab.com/gitlab-org/gitlab/-/issues/339348.
stub_feature_flags(new_header_search: false)
visit(project_tree_path(project, ref_name))
expect(page).to have_content('You must be logged in to search across all of GitLab')
submit_search('gitlab-grack')
select_search_scope('Code')
end
it 'shows ref switcher in code result summary' do
expect(find('.ref-selector')).to have_text(ref_name)
end
it 'persists branch name across search' do
find('.gl-search-box-by-click-search-button').click
expect(find('.ref-selector')).to have_text(ref_name)
end
# this example is use to test the design that the refs is not
# only represent the branch as well as the tags.
it 'ref switcher list all the branches and tags' do
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
expect(page).to have_selector('li', text: 'add-ipython-files')
expect(page).to have_selector('li', text: 'v1.0.0')
end
end
it 'search result changes when refs switched' do
ref = 'master'
expect(find('.results')).not_to have_content('path = gitlab-grack')
find('.ref-selector').click
wait_for_requests
page.within('.ref-selector') do
fill_in _('Search by Git revision'), with: ref
wait_for_requests
find('li', text: ref).click
end
expect(page).to have_selector('.results', text: 'path = gitlab-grack')
end
end
end
it 'no ref switcher shown in issue result summary' do
issue = create(:issue, title: 'test', project: project)
visit(project_tree_path(project))
submit_search('test')
select_search_scope('Code')
expect(page).to have_selector('.ref-selector')
select_search_scope('Issues')
expect(find(:css, '.results')).to have_link(issue.title)
expect(page).not_to have_selector('.ref-selector')
end
end
context 'when signed out' do
context 'when block_anonymous_global_searches is enabled' do
it 'is redirected to login page' do
visit(search_path)
expect(page).to have_content('You must be logged in to search across all of GitLab')
end
end
end

View File

@ -3,51 +3,45 @@
require 'spec_helper'
RSpec.describe 'User searches for comments', :js, :disable_rate_limiter, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
where(search_page_vertical_nav_enabled: [true, false])
with_them do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
project.add_reporter(user)
sign_in(user)
before do
project.add_reporter(user)
sign_in(user)
visit(project_path(project))
end
visit(project_path(project))
end
include_examples 'search timeouts', 'notes' do
let(:additional_params) { { project_id: project.id } }
end
include_examples 'search timeouts', 'notes' do
let(:additional_params) { { project_id: project.id } }
end
context 'when a comment is in commits' do
context 'when comment belongs to an invalid commit' do
let(:comment) { create(:note_on_commit, author: user, project: project, commit_id: 12345678, note: 'Bug here') }
context 'when a comment is in commits' do
context 'when comment belongs to an invalid commit' do
let(:comment) { create(:note_on_commit, author: user, project: project, commit_id: 12345678, note: 'Bug here') }
it 'finds a commit' do
submit_search(comment.note)
select_search_scope('Comments')
page.within('.results') do
expect(page).to have_content('Commit deleted')
expect(page).to have_content('12345678')
end
end
end
end
context 'when a comment is in a snippet' do
let(:snippet) { create(:project_snippet, :private, project: project, author: user, title: 'Some title') }
let(:comment) { create(:note, noteable: snippet, author: user, note: 'Supercalifragilisticexpialidocious', project: project) }
it 'finds a snippet' do
it 'finds a commit' do
submit_search(comment.note)
select_search_scope('Comments')
expect(page).to have_selector('.results', text: snippet.title)
page.within('.results') do
expect(page).to have_content('Commit deleted')
expect(page).to have_content('12345678')
end
end
end
end
context 'when a comment is in a snippet' do
let(:snippet) { create(:project_snippet, :private, project: project, author: user, title: 'Some title') }
let(:comment) { create(:note, noteable: snippet, author: user, note: 'Supercalifragilisticexpialidocious', project: project) }
it 'finds a snippet' do
submit_search(comment.note)
select_search_scope('Comments')
expect(page).to have_selector('.results', text: snippet.title)
end
end
end

View File

@ -3,61 +3,55 @@
require 'spec_helper'
RSpec.describe 'User searches for commits', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let(:project) { create(:project, :repository) }
let(:sha) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
where(search_page_vertical_nav_enabled: [true, false])
with_them do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
project.add_reporter(user)
sign_in(user)
before do
project.add_reporter(user)
sign_in(user)
visit(search_path(project_id: project.id))
visit(search_path(project_id: project.id))
end
include_examples 'search timeouts', 'commits' do
let(:additional_params) { { project_id: project.id } }
end
context 'when searching by SHA' do
it 'finds a commit and redirects to its page' do
submit_search(sha)
expect(page).to have_current_path(project_commit_path(project, sha))
end
include_examples 'search timeouts', 'commits' do
let(:additional_params) { { project_id: project.id } }
it 'finds a commit in uppercase and redirects to its page' do
submit_search(sha.upcase)
expect(page).to have_current_path(project_commit_path(project, sha))
end
end
context 'when searching by message' do
it 'finds a commit and holds on /search page' do
project.repository.commit_files(
user,
message: 'Message referencing another sha: "deadbeef"',
branch_name: 'master',
actions: [{ action: :create, file_path: 'a/new.file', contents: 'new file' }]
)
submit_search('deadbeef')
expect(page).to have_current_path('/search', ignore_query: true)
end
context 'when searching by SHA' do
it 'finds a commit and redirects to its page' do
submit_search(sha)
it 'finds multiple commits' do
submit_search('See merge request')
select_search_scope('Commits')
expect(page).to have_current_path(project_commit_path(project, sha))
end
it 'finds a commit in uppercase and redirects to its page' do
submit_search(sha.upcase)
expect(page).to have_current_path(project_commit_path(project, sha))
end
end
context 'when searching by message' do
it 'finds a commit and holds on /search page' do
project.repository.commit_files(
user,
message: 'Message referencing another sha: "deadbeef"',
branch_name: 'master',
actions: [{ action: :create, file_path: 'a/new.file', contents: 'new file' }]
)
submit_search('deadbeef')
expect(page).to have_current_path('/search', ignore_query: true)
end
it 'finds multiple commits' do
submit_search('See merge request')
select_search_scope('Commits')
expect(page).to have_selector('.commit-row-description', visible: false, count: 9)
end
expect(page).to have_selector('.commit-row-description', visible: false, count: 9)
end
end
end

View File

@ -3,8 +3,6 @@
require 'spec_helper'
RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
@ -17,21 +15,107 @@ RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limitin
select_search_scope('Issues')
end
where(search_page_vertical_nav_enabled: [true, false])
context 'when signed in' do
before do
project.add_maintainer(user)
sign_in(user)
visit(search_path)
end
include_examples 'top right search form'
include_examples 'search timeouts', 'issues'
it 'finds an issue' do
search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_link(issue1.title)
expect(page).not_to have_link(issue2.title)
end
end
it 'hides confidential icon for non-confidential issues' do
search_for_issue(issue1.title)
page.within('.results') do
expect(page).not_to have_css('[data-testid="eye-slash-icon"]')
end
end
it 'shows confidential icon for confidential issues' do
search_for_issue(issue2.title)
page.within('.results') do
expect(page).to have_css('[data-testid="eye-slash-icon"]')
end
end
it 'shows correct badge for open issues' do
search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_css('.badge-success')
expect(page).not_to have_css('.badge-info')
end
end
it 'shows correct badge for closed issues' do
search_for_issue(issue2.title)
page.within('.results') do
expect(page).not_to have_css('.badge-success')
expect(page).to have_css('.badge-info')
end
end
it 'sorts by created date' do
search_for_issue('issue')
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(issue2.title)
expect(page.all('.search-result-row').last).to have_link(issue1.title)
end
find('[data-testid="sort-highest-icon"]').click
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(issue1.title)
expect(page.all('.search-result-row').last).to have_link(issue2.title)
end
end
context 'when on a project page' do
it 'finds an issue' do
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_link(issue1.title)
expect(page).not_to have_link(issue2.title)
end
end
end
end
context 'when signed out' do
context 'when block_anonymous_global_searches is disabled' do
let_it_be(:project) { create(:project, :public) }
with_them do
context 'when signed in' do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
project.add_maintainer(user)
sign_in(user)
stub_feature_flags(block_anonymous_global_searches: false)
visit(search_path)
end
include_examples 'top right search form'
include_examples 'search timeouts', 'issues'
it 'finds an issue' do
search_for_issue(issue1.title)
@ -41,109 +125,13 @@ RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limitin
expect(page).not_to have_link(issue2.title)
end
end
it 'hides confidential icon for non-confidential issues' do
search_for_issue(issue1.title)
page.within('.results') do
expect(page).not_to have_css('[data-testid="eye-slash-icon"]')
end
end
it 'shows confidential icon for confidential issues' do
search_for_issue(issue2.title)
page.within('.results') do
expect(page).to have_css('[data-testid="eye-slash-icon"]')
end
end
it 'shows correct badge for open issues' do
search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_css('.badge-success')
expect(page).not_to have_css('.badge-info')
end
end
it 'shows correct badge for closed issues' do
search_for_issue(issue2.title)
page.within('.results') do
expect(page).not_to have_css('.badge-success')
expect(page).to have_css('.badge-info')
end
end
it 'sorts by created date' do
search_for_issue('issue')
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(issue2.title)
expect(page.all('.search-result-row').last).to have_link(issue1.title)
end
find('[data-testid="sort-highest-icon"]').click
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(issue1.title)
expect(page.all('.search-result-row').last).to have_link(issue2.title)
end
end
context 'when on a project page' do
it 'finds an issue' do
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_link(issue1.title)
expect(page).not_to have_link(issue2.title)
end
end
end
end
context 'when signed out' do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
end
context 'when block_anonymous_global_searches is enabled' do
it 'is redirected to login page' do
visit(search_path)
context 'when block_anonymous_global_searches is disabled' do
let_it_be(:project) { create(:project, :public) }
before do
stub_feature_flags(block_anonymous_global_searches: false)
visit(search_path)
end
include_examples 'top right search form'
it 'finds an issue' do
search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_link(issue1.title)
expect(page).not_to have_link(issue2.title)
end
end
end
context 'when block_anonymous_global_searches is enabled' do
it 'is redirected to login page' do
visit(search_path)
expect(page).to have_content('You must be logged in to search across all of GitLab')
end
expect(page).to have_content('You must be logged in to search across all of GitLab')
end
end
end

View File

@ -3,12 +3,10 @@
require 'spec_helper'
RSpec.describe 'User searches for merge requests', :js, :clean_gitlab_redis_rate_limiting, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let!(:merge_request1) { create(:merge_request, title: 'Merge Request Foo', source_project: project, target_project: project, created_at: 1.hour.ago) }
let!(:merge_request2) { create(:merge_request, :simple, title: 'Merge Request Bar', source_project: project, target_project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:merge_request1) { create(:merge_request, title: 'Merge Request Foo', source_project: project, target_project: project, created_at: 1.hour.ago) }
let_it_be(:merge_request2) { create(:merge_request, :simple, title: 'Merge Request Bar', source_project: project, target_project: project) }
def search_for_mr(search)
fill_in('dashboard_search', with: search)
@ -16,64 +14,60 @@ RSpec.describe 'User searches for merge requests', :js, :clean_gitlab_redis_rate
select_search_scope('Merge requests')
end
where(search_page_vertical_nav_enabled: [true, false])
with_them do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
sign_in(user)
before do
sign_in(user)
visit(search_path)
visit(search_path)
end
include_examples 'top right search form'
include_examples 'search timeouts', 'merge_requests'
it 'finds a merge request' do
search_for_mr(merge_request1.title)
page.within('.results') do
expect(page).to have_link(merge_request1.title)
expect(page).not_to have_link(merge_request2.title)
# Each result should have MR refs like `gitlab-org/gitlab!1`
page.all('.search-result-row').each do |e|
expect(e.text).to match(/!\d+/)
end
end
end
it 'sorts by created date' do
search_for_mr('Merge Request')
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(merge_request2.title)
expect(page.all('.search-result-row').last).to have_link(merge_request1.title)
end
include_examples 'top right search form'
include_examples 'search timeouts', 'merge_requests'
find('[data-testid="sort-highest-icon"]').click
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(merge_request1.title)
expect(page.all('.search-result-row').last).to have_link(merge_request2.title)
end
end
context 'when on a project page' do
it 'finds a merge request' do
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
search_for_mr(merge_request1.title)
page.within('.results') do
expect(page).to have_link(merge_request1.title)
expect(page).not_to have_link(merge_request2.title)
# Each result should have MR refs like `gitlab-org/gitlab!1`
page.all('.search-result-row').each do |e|
expect(e.text).to match(/!\d+/)
end
end
end
it 'sorts by created date' do
search_for_mr('Merge Request')
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(merge_request2.title)
expect(page.all('.search-result-row').last).to have_link(merge_request1.title)
end
find('[data-testid="sort-highest-icon"]').click
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(merge_request1.title)
expect(page.all('.search-result-row').last).to have_link(merge_request2.title)
end
end
context 'when on a project page' do
it 'finds a merge request' do
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
search_for_mr(merge_request1.title)
page.within('.results') do
expect(page).to have_link(merge_request1.title)
expect(page).not_to have_link(merge_request2.title)
end
end
end
end

View File

@ -4,29 +4,42 @@ require 'spec_helper'
RSpec.describe 'User searches for milestones', :js, :clean_gitlab_redis_rate_limiting,
feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:milestone1) { create(:milestone, title: 'Foo', project: project) }
let_it_be(:milestone2) { create(:milestone, title: 'Bar', project: project) }
let!(:milestone1) { create(:milestone, title: 'Foo', project: project) }
let!(:milestone2) { create(:milestone, title: 'Bar', project: project) }
before do
project.add_maintainer(user)
sign_in(user)
where(search_page_vertical_nav_enabled: [true, false])
visit(search_path)
end
with_them do
before do
project.add_maintainer(user)
sign_in(user)
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
include_examples 'top right search form'
include_examples 'search timeouts', 'milestones'
visit(search_path)
it 'finds a milestone' do
fill_in('dashboard_search', with: milestone1.title)
find('.gl-search-box-by-click-search-button').click
select_search_scope('Milestones')
page.within('.results') do
expect(page).to have_link(milestone1.title)
expect(page).not_to have_link(milestone2.title)
end
end
include_examples 'top right search form'
include_examples 'search timeouts', 'milestones'
context 'when on a project page' do
it 'finds a milestone' do
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
fill_in('dashboard_search', with: milestone1.title)
find('.gl-search-box-by-click-search-button').click
select_search_scope('Milestones')
@ -36,26 +49,5 @@ feature_category: :global_search do
expect(page).not_to have_link(milestone2.title)
end
end
context 'when on a project page' do
it 'finds a milestone' do
find('[data-testid="project-filter"]').click
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
fill_in('dashboard_search', with: milestone1.title)
find('.gl-search-box-by-click-search-button').click
select_search_scope('Milestones')
page.within('.results') do
expect(page).to have_link(milestone1.title)
expect(page).not_to have_link(milestone2.title)
end
end
end
end
end

View File

@ -7,85 +7,80 @@ RSpec.describe 'User searches for users', :js, :clean_gitlab_redis_rate_limiting
let_it_be(:user2) { create(:user, username: 'michael_bluth', name: 'Michael Bluth') }
let_it_be(:user3) { create(:user, username: 'gob_2018', name: 'George Oscar Bluth') }
where(search_page_vertical_nav_enabled: [true, false])
with_them do
before do
sign_in(user1)
end
include_examples 'search timeouts', 'users' do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
sign_in(user1)
visit(search_path)
end
end
include_examples 'search timeouts', 'users' do
before do
visit(search_path)
context 'when on the dashboard' do
it 'finds the user' do
visit dashboard_projects_path
submit_search('gob')
select_search_scope('Users')
page.within('.results') do
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
end
end
end
context 'when on the dashboard' do
it 'finds the user' do
visit dashboard_projects_path
context 'when on the project page' do
let_it_be_with_reload(:project) { create(:project) }
submit_search('gob')
select_search_scope('Users')
page.within('.results') do
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
end
end
before do
project.add_developer(user1)
project.add_developer(user2)
end
context 'when on the project page' do
let_it_be_with_reload(:project) { create(:project) }
it 'finds the user belonging to the project' do
visit project_path(project)
before do
project.add_developer(user1)
project.add_developer(user2)
end
submit_search('gob')
select_search_scope('Users')
it 'finds the user belonging to the project' do
visit project_path(project)
page.within('.results') do
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
submit_search('gob')
select_search_scope('Users')
expect(page).not_to have_content('Michael Bluth')
expect(page).not_to have_content('@michael_bluth')
page.within('.results') do
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
expect(page).not_to have_content('Michael Bluth')
expect(page).not_to have_content('@michael_bluth')
expect(page).not_to have_content('George Oscar Bluth')
expect(page).not_to have_content('@gob_2018')
end
expect(page).not_to have_content('George Oscar Bluth')
expect(page).not_to have_content('@gob_2018')
end
end
end
context 'when on the group page' do
let(:group) { create(:group) }
context 'when on the group page' do
let(:group) { create(:group) }
before do
group.add_developer(user1)
group.add_developer(user2)
end
before do
group.add_developer(user1)
group.add_developer(user2)
end
it 'finds the user belonging to the group' do
visit group_path(group)
it 'finds the user belonging to the group' do
visit group_path(group)
submit_search('gob')
select_search_scope('Users')
submit_search('gob')
select_search_scope('Users')
page.within('.results') do
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
page.within('.results') do
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
expect(page).not_to have_content('Michael Bluth')
expect(page).not_to have_content('@michael_bluth')
expect(page).not_to have_content('Michael Bluth')
expect(page).not_to have_content('@michael_bluth')
expect(page).not_to have_content('George Oscar Bluth')
expect(page).not_to have_content('@gob_2018')
end
expect(page).not_to have_content('George Oscar Bluth')
expect(page).not_to have_content('@gob_2018')
end
end
end

View File

@ -4,58 +4,53 @@ require 'spec_helper'
RSpec.describe 'User searches for wiki pages', :js, :clean_gitlab_redis_rate_limiting,
feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, :wiki_repo, namespace: user.namespace) }
let_it_be(:wiki_page) do
create(:wiki_page, wiki: project.wiki, title: 'directory/title', content: 'Some Wiki content')
end
let(:project) { create(:project, :repository, :wiki_repo, namespace: user.namespace) }
let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, title: 'directory/title', content: 'Some Wiki content') }
before do
project.add_maintainer(user)
sign_in(user)
where(search_page_vertical_nav_enabled: [true, false])
with_them do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
project.add_maintainer(user)
sign_in(user)
visit(search_path)
end
visit(search_path)
end
include_examples 'top right search form'
include_examples 'search timeouts', 'wiki_blobs' do
let(:additional_params) { { project_id: project.id } }
end
include_examples 'top right search form'
include_examples 'search timeouts', 'wiki_blobs' do
let(:additional_params) { { project_id: project.id } }
end
shared_examples 'search wiki blobs' do
it 'finds a page' do
find('[data-testid="project-filter"]').click
shared_examples 'search wiki blobs' do
it 'finds a page' do
find('[data-testid="project-filter"]').click
wait_for_requests
wait_for_requests
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
fill_in('dashboard_search', with: search_term)
find('.gl-search-box-by-click-search-button').click
select_search_scope('Wiki')
page.within('.results') do
expect(page).to have_link(wiki_page.title, href: project_wiki_path(project, wiki_page.slug))
end
page.within('[data-testid="project-filter"]') do
click_on(project.name)
end
end
context 'when searching by content' do
it_behaves_like 'search wiki blobs' do
let(:search_term) { 'content' }
end
end
fill_in('dashboard_search', with: search_term)
find('.gl-search-box-by-click-search-button').click
select_search_scope('Wiki')
context 'when searching by title' do
it_behaves_like 'search wiki blobs' do
let(:search_term) { 'title' }
page.within('.results') do
expect(page).to have_link(wiki_page.title, href: project_wiki_path(project, wiki_page.slug))
end
end
end
context 'when searching by content' do
it_behaves_like 'search wiki blobs' do
let(:search_term) { 'content' }
end
end
context 'when searching by title' do
it_behaves_like 'search wiki blobs' do
let(:search_term) { 'title' }
end
end
end

View File

@ -1,7 +0,0 @@
// 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>"
`;

View File

@ -1,43 +0,0 @@
import MockAdapter from 'axios-mock-adapter';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
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);
setHTMLFixture(fixture);
});
afterEach(() => {
resetHTMLFixture();
});
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();
});
});
});

View File

@ -84,23 +84,14 @@ describe('GlobalSearchSidebar', () => {
expect(findFilters().exists()).toBe(true);
});
});
});
describe('when search_page_vertical_nav is enabled', () => {
beforeEach(() => {
createComponent({}, { searchPageVerticalNav: true });
});
it('shows the vertical navigation', () => {
expect(findSidebarNavigation().exists()).toBe(true);
});
});
describe('when search_page_vertical_nav is disabled', () => {
beforeEach(() => {
createComponent({}, { searchPageVerticalNav: false });
});
it('hides the vertical navigation', () => {
expect(findSidebarNavigation().exists()).toBe(false);
describe('renders navigation', () => {
beforeEach(() => {
createComponent({});
});
it('shows the vertical navigation', () => {
expect(findSidebarNavigation().exists()).toBe(true);
});
});
});
});

View File

@ -22,24 +22,4 @@ describe('ConfidentialityFilter', () => {
expect(findRadioFilter().exists()).toBe(true);
});
});
describe.each`
hasFeatureFlagEnabled | paddingClass
${true} | ${'gl-px-5'}
${false} | ${'gl-px-0'}
`(`RadioFilter`, ({ hasFeatureFlagEnabled, paddingClass }) => {
beforeEach(() => {
createComponent({
provide: {
glFeatures: {
searchPageVerticalNav: hasFeatureFlagEnabled,
},
},
});
});
it(`has ${paddingClass} class`, () => {
expect(findRadioFilter().classes(paddingClass)).toBe(true);
});
});
});

View File

@ -22,24 +22,4 @@ describe('StatusFilter', () => {
expect(findRadioFilter().exists()).toBe(true);
});
});
describe.each`
hasFeatureFlagEnabled | paddingClass
${true} | ${'gl-px-5'}
${false} | ${'gl-px-0'}
`(`RadioFilter`, ({ hasFeatureFlagEnabled, paddingClass }) => {
beforeEach(() => {
createComponent({
provide: {
glFeatures: {
searchPageVerticalNav: hasFeatureFlagEnabled,
},
},
});
});
it(`has ${paddingClass} class`, () => {
expect(findRadioFilter().classes(paddingClass)).toBe(true);
});
});
});

View File

@ -4,43 +4,43 @@ require "spec_helper"
RSpec.describe BizibleHelper do
describe '#bizible_enabled?' do
before do
stub_config(extra: { bizible: SecureRandom.uuid })
end
context 'when bizible is disabled' do
context 'when bizible config is not true' do
before do
allow(helper).to receive(:bizible_enabled?).and_return(false)
stub_config(extra: { bizible: false })
end
it { is_expected.to be_falsey }
it { expect(helper.bizible_enabled?).to be_falsy }
end
context 'when bizible is enabled' do
context 'when bizible config is enabled' do
before do
allow(helper).to receive(:bizible_enabled?).and_return(true)
stub_config(extra: { bizible: true })
end
it { is_expected.to be_truthy }
end
it { expect(helper.bizible_enabled?).to be_truthy }
subject(:bizible_enabled?) { helper.bizible_enabled? }
context 'with ecomm_instrumentation feature flag disabled' do
before do
stub_feature_flags(ecomm_instrumentation: false)
end
it { is_expected.to be_falsey }
end
context 'with ecomm_instrumentation feature flag enabled' do
context 'when no id is set' do
context 'with ecomm_instrumentation feature flag disabled' do
before do
stub_config(extra: {})
stub_feature_flags(ecomm_instrumentation: false)
end
it { is_expected.to be_falsey }
it { expect(helper.bizible_enabled?).to be_falsey }
end
context 'with ecomm_instrumentation feature flag enabled' do
before do
stub_feature_flags(ecomm_instrumentation: true)
end
it { expect(helper.bizible_enabled?).to be_truthy }
end
context 'with invite_email present' do
before do
stub_feature_flags(ecomm_instrumentation: true)
end
it { expect(helper.bizible_enabled?('test@test.com')).to be_falsy }
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities, feature_category: :pipeline_execution do
RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities, feature_category: :continuous_integration do
let(:project) { create(:project, :test_repo) }
let_it_be(:user) { create(:user) }

View File

@ -31,6 +31,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do
it { is_expected.to have_many(:pages_deployments).with_foreign_key(:ci_build_id) }
it { is_expected.to have_one(:deployment) }
it { is_expected.to have_one(:runner_machine).through(:metadata) }
it { is_expected.to have_one(:runner_session).with_foreign_key(:build_id) }
it { is_expected.to have_one(:trace_metadata).with_foreign_key(:build_id) }
it { is_expected.to have_one(:runtime_metadata).with_foreign_key(:build_id) }
@ -2047,6 +2048,16 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do
end
end
describe '#runner_machine' do
let_it_be(:runner) { create(:ci_runner) }
let_it_be(:runner_machine) { create(:ci_runner_machine, runner: runner) }
let_it_be(:build) { create(:ci_build, runner_machine: runner_machine) }
subject(:build_runner_machine) { described_class.find(build.id).runner_machine }
it { is_expected.to eq(runner_machine) }
end
describe '#tag_list' do
let_it_be(:build) { create(:ci_build, tag_list: ['tag']) }
@ -5781,4 +5792,43 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do
expect(build.metadata.partition_id).to eq(ci_testing_partition_id)
end
end
describe 'secrets management id_tokens usage data' do
context 'when ID tokens are defined' do
let(:ci_build) { FactoryBot.build(:ci_build, user: user, id_tokens: { 'ID_TOKEN_1' => { aud: 'developers' } }) }
context 'on create' do
it 'tracks event with user_id' do
expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event)
.with('i_ci_secrets_management_id_tokens_build_created', values: user.id)
ci_build.save!
end
end
context 'on update' do
before do
ci_build.save!
end
it 'does not track event' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
ci_build.success
end
end
end
context 'when ID tokens are not defined' do
let(:ci_build) { FactoryBot.build(:ci_build, user: user) }
context 'on create' do
it 'does not track event' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
ci_build.save!
end
end
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Ci::Processable do
RSpec.describe Ci::Processable, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
@ -83,7 +83,7 @@ RSpec.describe Ci::Processable do
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
sourced_pipelines sourced_pipeline artifacts_file_store artifacts_metadata_store
metadata runner_session trace_chunks upstream_pipeline_id
metadata runner_machine_id runner_machine runner_session trace_chunks upstream_pipeline_id
artifacts_file artifacts_metadata artifacts_size commands
resource resource_group_id processed security_scans author
pipeline_id report_results pending_state pages_deployments

View File

@ -6,7 +6,9 @@ RSpec.describe Ci::RunnerMachine, feature_category: :runner_fleet, type: :model
it_behaves_like 'having unique enum values'
it { is_expected.to belong_to(:runner) }
it { is_expected.to belong_to(:runner_version).with_foreign_key(:version) }
it { is_expected.to have_many(:build_metadata) }
it { is_expected.to have_many(:builds).through(:build_metadata) }
describe 'validation' do
it { is_expected.to validate_presence_of(:runner) }

View File

@ -11,6 +11,8 @@ RSpec.describe Ci::RunnerVersion, feature_category: :runner_fleet do
create(:ci_runner_version, version: 'abc123', status: :not_available)
end
it { is_expected.to have_many(:runner_machines).with_foreign_key(:version) }
it_behaves_like 'having unique enum values'
describe '.not_available' do

View File

@ -0,0 +1,87 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Blobs', feature_category: :source_code_management do
let_it_be(:project) { create(:project, :public, :repository, lfs: true) }
describe 'GET /:namespace_id/:project_id/-/blob/:id' do
subject(:request) do
get namespace_project_blob_path(namespace_id: project.namespace, project_id: project, id: id)
end
context 'with LFS file' do
let(:id) { 'master/files/lfs/lfs_object.iso' }
let(:object_store_host) { 'http://127.0.0.1:9000' }
let(:connect_src) do
csp = response.headers['Content-Security-Policy']
csp.split('; ').find { |src| src.starts_with?('connect-src') }
end
let(:gitlab_config) do
Gitlab.config.gitlab.deep_merge(
'content_security_policy' => {
'enabled' => content_security_policy_enabled
}
)
end
let(:lfs_config) do
Gitlab.config.lfs.deep_merge(
'enabled' => lfs_enabled,
'object_store' => {
'remote_directory' => 'lfs-objects',
'enabled' => true,
'proxy_download' => proxy_download,
'connection' => {
'endpoint' => object_store_host,
'path_style' => true
}
}
)
end
before do
stub_config_setting(gitlab_config)
stub_lfs_setting(lfs_config)
stub_lfs_object_storage(proxy_download: proxy_download)
request
end
describe 'directly downloading lfs file' do
let(:lfs_enabled) { true }
let(:proxy_download) { false }
let(:content_security_policy_enabled) { true }
it { expect(response).to have_gitlab_http_status(:success) }
it { expect(connect_src).to include(object_store_host) }
context 'when lfs is disabled' do
let(:lfs_enabled) { false }
it { expect(response).to have_gitlab_http_status(:success) }
it { expect(connect_src).not_to include(object_store_host) }
end
context 'when content_security_policy is disabled' do
let(:content_security_policy_enabled) { false }
it { expect(response).to have_gitlab_http_status(:success) }
it { expect(connect_src).not_to include(object_store_host) }
end
context 'when proxy download is enabled' do
let(:proxy_download) { true }
it { expect(response).to have_gitlab_http_status(:success) }
it { expect(connect_src).not_to include(object_store_host) }
end
end
end
end
end

View File

@ -78,7 +78,8 @@ RSpec.describe 'gitlab:usage data take tasks', :silence_stdout, feature_category
`git checkout -- #{Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH}`
end
it "generates #{Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH}" do
it "generates #{Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH}",
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/386191' do
run_rake_task('gitlab:usage_data:generate_ci_template_events')
expect(File.exist?(Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH)).to be true

View File

@ -3,8 +3,6 @@
require 'spec_helper'
RSpec.describe 'search/_results', feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let(:search_objects) { Issue.page(1).per(2) }
@ -32,30 +30,22 @@ RSpec.describe 'search/_results', feature_category: :global_search do
assign(:search_service_presenter, search_service_presenter)
end
where(search_page_vertical_nav_enabled: [true, false])
describe 'page size' do
context 'when search results have a count' do
it 'displays the page size' do
render
with_them do
describe 'page size' do
before do
stub_feature_flags(search_page_vertical_nav: search_page_vertical_nav_enabled)
expect(rendered).to have_content('Showing 1 - 2 of 3 issues for foo')
end
end
context 'when search results have a count' do
it 'displays the page size' do
render
context 'when search results do not have a count' do
let(:search_objects) { Issue.page(1).per(2).without_count }
expect(rendered).to have_content('Showing 1 - 2 of 3 issues for foo')
end
end
it 'does not display the page size' do
render
context 'when search results do not have a count' do
let(:search_objects) { Issue.page(1).per(2).without_count }
it 'does not display the page size' do
render
expect(rendered).not_to have_content(/Showing .* of .*/)
end
expect(rendered).not_to have_content(/Showing .* of .*/)
end
end
end

View File

@ -10,122 +10,96 @@ RSpec.describe 'search/show', feature_category: :global_search do
end
before do
stub_template "search/_category.html.haml" => 'Category Partial'
stub_template "search/_results.html.haml" => 'Results Partial'
allow(view).to receive(:current_user) { user }
assign(:search_service_presenter, search_service_presenter)
assign(:search_term, search_term)
end
context 'search_page_vertical_nav feature flag enabled' do
before do
allow(view).to receive(:current_user) { user }
assign(:search_term, search_term)
end
context 'when search term is supplied' do
let(:search_term) { 'Search Foo' }
context 'when search term is supplied' do
let(:search_term) { 'Search Foo' }
it 'renders the results partial' do
render
it 'will not render category partial' do
render
expect(rendered).not_to render_template('search/_category')
expect(rendered).to render_template('search/_results')
end
expect(rendered).to render_template('search/_results')
end
end
context 'search_page_vertical_nav feature flag disabled' do
context 'when the search page is opened' do
it 'displays the title' do
render
expect(rendered).to have_selector('h1.page-title', text: 'Search')
expect(rendered).not_to have_selector('h1.page-title code')
end
it 'does not render the results partial' do
render
expect(rendered).not_to render_template('search/_results')
end
end
context 'unfurling support' do
let(:group) { build(:group) }
let(:search_results) do
instance_double(Gitlab::GroupSearchResults).tap do |double|
allow(double).to receive(:formatted_count).and_return(0)
end
end
before do
stub_feature_flags(search_page_vertical_nav: false)
assign(:search_term, search_term)
assign(:search_results, search_results)
assign(:scope, 'issues')
assign(:group, group)
end
context 'when the search page is opened' do
it 'displays the title' do
render
expect(rendered).to have_selector('h1.page-title', text: 'Search')
expect(rendered).not_to have_selector('h1.page-title code')
context 'search with full count' do
let(:search_service_presenter) do
instance_double(SearchServicePresenter, without_count?: false, advanced_search_enabled?: false)
end
it 'does not render partials' do
it 'renders meta tags for a group' do
render
expect(rendered).not_to render_template('search/_category')
expect(rendered).not_to render_template('search/_results')
expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
end
it 'renders meta tags for both group and project' do
project = build(:project, group: group)
assign(:project, project)
render
expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
end
end
context 'when search term is supplied' do
let(:search_term) { 'Search Foo' }
it 'renders partials' do
render
expect(rendered).to render_template('search/_category')
expect(rendered).to render_template('search/_results')
context 'search without full count' do
let(:search_service_presenter) do
instance_double(SearchServicePresenter, without_count?: true, advanced_search_enabled?: false)
end
context 'unfurling support' do
let(:group) { build(:group) }
let(:search_results) do
instance_double(Gitlab::GroupSearchResults).tap do |double|
allow(double).to receive(:formatted_count).and_return(0)
end
end
it 'renders meta tags for a group' do
render
before do
assign(:search_results, search_results)
assign(:scope, 'issues')
assign(:group, group)
end
expect(view.page_description).to match(/issues results for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
end
context 'search with full count' do
let(:search_service_presenter) do
instance_double(SearchServicePresenter, without_count?: false, advanced_search_enabled?: false)
end
it 'renders meta tags for both group and project' do
project = build(:project, group: group)
assign(:project, project)
it 'renders meta tags for a group' do
render
render
expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
end
it 'renders meta tags for both group and project' do
project = build(:project, group: group)
assign(:project, project)
render
expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
end
end
context 'search without full count' do
let(:search_service_presenter) do
instance_double(SearchServicePresenter, without_count?: true, advanced_search_enabled?: false)
end
it 'renders meta tags for a group' do
render
expect(view.page_description).to match(/issues results for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
end
it 'renders meta tags for both group and project' do
project = build(:project, group: group)
assign(:project, project)
render
expect(view.page_description).to match(/issues results for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
end
end
expect(view.page_description).to match(/issues results for term '#{search_term}'/)
expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
end
end
end