From b4d76c5ac78ec9f690e2094fbf5f77331b9432c8 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 5 Oct 2022 06:08:05 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../content_editor/extensions/diagram.js | 5 +- .../javascripts/lib/utils/text_utility.js | 14 +- .../page_bundles/admin/geo_nodes.scss | 45 +++ .../page_bundles/admin/geo_replicable.scss | 18 ++ app/controllers/search_controller.rb | 5 + app/helpers/search_helper.rb | 36 +++ app/views/projects/_fork_suggestion.html.haml | 5 +- app/views/projects/blob/_editor.html.haml | 14 +- .../merge_requests/_nav_btns.html.haml | 7 +- .../_project_fields_form.html.haml | 2 +- .../shared/blob/_markdown_buttons.html.haml | 11 +- .../shared/issuable/_feed_buttons.html.haml | 8 +- .../shared/projects/_search_form.html.haml | 2 +- config/application.rb | 2 + .../development/search_page_vertical_nav.yml | 8 + .../gitlab_rails_cheat_sheet.md | 6 - locale/gitlab.pot | 6 + spec/features/dashboard/issues_filter_spec.rb | 2 +- .../markdown_golden_master_examples.yml | 8 + spec/frontend/__helpers__/dom_shims/index.js | 1 + .../__helpers__/dom_shims/text_encoder.js | 4 + .../jira_connect/subscriptions/pkce_spec.js | 4 - spec/frontend/lib/utils/text_utility_spec.js | 12 + spec/helpers/search_helper_spec.rb | 285 ++++++++++++++++++ 24 files changed, 470 insertions(+), 40 deletions(-) create mode 100644 app/assets/stylesheets/page_bundles/admin/geo_nodes.scss create mode 100644 app/assets/stylesheets/page_bundles/admin/geo_replicable.scss create mode 100644 config/feature_flags/development/search_page_vertical_nav.yml create mode 100644 spec/frontend/__helpers__/dom_shims/text_encoder.js diff --git a/app/assets/javascripts/content_editor/extensions/diagram.js b/app/assets/javascripts/content_editor/extensions/diagram.js index d9983b8c1c5..7c4a56468eb 100644 --- a/app/assets/javascripts/content_editor/extensions/diagram.js +++ b/app/assets/javascripts/content_editor/extensions/diagram.js @@ -1,5 +1,6 @@ import { lowlight } from 'lowlight/lib/core'; import { textblockTypeInputRule } from '@tiptap/core'; +import { base64DecodeUnicode } from '~/lib/utils/text_utility'; import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants'; import languageLoader from '../services/code_block_language_loader'; import CodeBlockHighlight from './code_block_highlight'; @@ -45,7 +46,9 @@ export default CodeBlockHighlight.extend({ priority: PARSE_HTML_PRIORITY_HIGHEST, tag: '[data-diagram]', getContent(element, schema) { - const source = atob(element.dataset.diagramSrc.replace('data:text/plain;base64,', '')); + const source = base64DecodeUnicode( + element.dataset.diagramSrc.replace('data:text/plain;base64,', ''), + ); const node = schema.node('paragraph', {}, [schema.text(source)]); return node.content; }, diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index 59645d50e29..367180714df 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -1,5 +1,5 @@ import { isString, memoize } from 'lodash'; - +import { base64ToBuffer, bufferToBase64 } from '~/authentication/webauthn/util'; import { TRUNCATE_WIDTH_DEFAULT_WIDTH, TRUNCATE_WIDTH_DEFAULT_FONT_SIZE, @@ -513,3 +513,15 @@ export const limitedCounterWithDelimiter = (count) => { return count > limit ? '1,000+' : count; }; + +// Encoding UTF8 ⇢ base64 +export function base64EncodeUnicode(str) { + const encoder = new TextEncoder('utf8'); + return bufferToBase64(encoder.encode(str)); +} + +// Decoding base64 ⇢ UTF8 +export function base64DecodeUnicode(str) { + const decoder = new TextDecoder('utf8'); + return decoder.decode(base64ToBuffer(str)); +} diff --git a/app/assets/stylesheets/page_bundles/admin/geo_nodes.scss b/app/assets/stylesheets/page_bundles/admin/geo_nodes.scss new file mode 100644 index 00000000000..b0aaa48569a --- /dev/null +++ b/app/assets/stylesheets/page_bundles/admin/geo_nodes.scss @@ -0,0 +1,45 @@ +@import '../mixins_and_variables_and_functions'; + +.geo-node-header-grid-columns { + grid-template-columns: 1fr auto; + grid-gap: $gl-spacing-scale-5; + + @include media-breakpoint-up(md) { + grid-template-columns: 3fr 1fr; + } +} + +.geo-node-details-grid-columns { + grid-gap: $gl-spacing-scale-5; + + @include media-breakpoint-up(lg) { + grid-template-columns: 1fr 3fr; + } +} + +.geo-node-core-details-grid-columns { + grid-template-columns: 1fr 1fr; + grid-gap: $gl-spacing-scale-5; +} + +.geo-node-replication-details-grid-columns { + grid-template-columns: 1fr 1fr; + grid-gap: 1rem; + + @include media-breakpoint-up(md) { + grid-template-columns: 1fr 1fr 2fr 2fr; + } +} + +.geo-node-filter-grid-columns { + grid-template-columns: 1fr; + + @include media-breakpoint-up(md) { + grid-template-columns: 3fr 1fr; + } +} + +.geo-node-replication-counts-grid { + grid-template-columns: 2fr 1fr 1fr; + grid-gap: 1rem; +} diff --git a/app/assets/stylesheets/page_bundles/admin/geo_replicable.scss b/app/assets/stylesheets/page_bundles/admin/geo_replicable.scss new file mode 100644 index 00000000000..691d4abd195 --- /dev/null +++ b/app/assets/stylesheets/page_bundles/admin/geo_replicable.scss @@ -0,0 +1,18 @@ +@import '../mixins_and_variables_and_functions'; + +.geo-replicable-item-grid { + grid-template-columns: 8ch 1fr auto; + grid-gap: 1rem; +} + +.geo-replicable-filter-grid { + grid-template-columns: 1fr; + + @include media-breakpoint-up(md) { + grid-template-columns: 2fr 1fr; + } + + @include media-breakpoint-up(xl) { + grid-template-columns: 1fr 1fr; + } +} diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 9f87ad6aaf6..b8bef385d04 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -242,6 +242,11 @@ class SearchController < ApplicationController def search_type 'basic' end + + before_action do + # Prefer to scope it per project or user e.g. + push_frontend_feature_flag(:search_page_vertical_nav, current_user) + end end SearchController.prepend_mod_with('SearchController') diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index b16235893ae..8ecb625a0fa 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -380,6 +380,42 @@ module SearchHelper end end + def search_filter_link_json(scope, label, data, search) + search_params = params.merge(search).merge({ scope: scope }).permit(SEARCH_GENERIC_PARAMS) + active_scope = @scope == scope + + result = { label: label, scope: scope, data: data, link: search_path(search_params), active: active_scope } + result[:count] = @search_results.formatted_count(scope) if active_scope && !@timeout + result[:count_link] = search_count_path(search_params) unless active_scope + + result + end + + # search page scope navigation + def search_navigation + { + projects: { label: _("Projects"), data: { qa_selector: 'projects_tab' }, condition: @project.nil? }, + blobs: { label: _("Code"), data: { qa_selector: 'code_tab' }, condition: project_search_tabs?(:blobs) || search_service.show_elasticsearch_tabs? || feature_flag_tab_enabled?(:global_search_code_tab) }, + epics: { label: _("Epics"), condition: @project.nil? && search_service.show_epics? }, + issues: { label: _("Issues"), condition: project_search_tabs?(:issues) || feature_flag_tab_enabled?(:global_search_issues_tab) }, + merge_requests: { label: _("Merge requests"), condition: project_search_tabs?(:merge_requests) || feature_flag_tab_enabled?(:global_search_merge_requests_tab) }, + wiki_blobs: { label: _("Wiki"), condition: project_search_tabs?(:wiki) || search_service.show_elasticsearch_tabs? }, + commits: { label: _("Commits"), condition: project_search_tabs?(:commits) || (search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)) }, + notes: { label: _("Comments"), condition: project_search_tabs?(:notes) || search_service.show_elasticsearch_tabs? }, + milestones: { label: _("Milestones"), condition: project_search_tabs?(:milestones) || @project.nil? }, + users: { label: _("Users"), condition: show_user_search_tab? }, + snippet_titles: { label: _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }, condition: @show_snippets.present? && @project.nil? } + } + end + + def search_navigation_json + result = {} + search_navigation.each do |scope, nav| + result[scope] = search_filter_link_json(scope.to_s, nav[:label], nav[:data], nav[:search]) if nav[:condition] + end + result.to_json + end + def search_filter_input_options(type, placeholder = _('Search or filter results...')) opts = { diff --git a/app/views/projects/_fork_suggestion.html.haml b/app/views/projects/_fork_suggestion.html.haml index 55e609c0ffb..47d60593b4a 100644 --- a/app/views/projects/_fork_suggestion.html.haml +++ b/app/views/projects/_fork_suggestion.html.haml @@ -2,6 +2,7 @@ - message = message_base.html_safe % { edit_start: ''.html_safe, edit_end: ''.html_safe } .js-file-fork-suggestion-section.file-fork-suggestion.hidden %span.file-fork-suggestion-note= message - = link_to s_('ForkSuggestion|Fork'), nil, method: :post, class: 'js-fork-suggestion-button gl-button btn btn-grouped btn-confirm-secondary' - %button.js-cancel-fork-suggestion-button.gl-button.btn.btn-grouped{ type: 'button' } + = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, category: :secondary, button_options: { class: "js-fork-suggestion-button btn-grouped" }) do + = s_('ForkSuggestion|Fork') + = render Pajamas::ButtonComponent.new(button_options: { class: "js-cancel-fork-suggestion-button btn-grouped" }) do = s_('ForkSuggestion|Cancel') diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index a47a490d7c5..bd08ab67cd3 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -29,15 +29,11 @@ .file-buttons.gl-display-flex.gl-align-items-center.gl-justify-content-end - if is_markdown = render 'shared/blob/markdown_buttons', show_fullscreen_button: false, supports_file_upload: false - = button_tag class: 'soft-wrap-toggle btn gl-button btn-default', type: 'button', tabindex: '-1' do - .no-wrap - = sprite_icon('soft-unwrap', css_class: 'gl-button-icon') - %span.gl-button-text - No wrap - .soft-wrap - = sprite_icon('soft-wrap', css_class: 'gl-button-icon') - %span.gl-button-text - Soft wrap + %span.soft-wrap-toggle + = render Pajamas::ButtonComponent.new(icon: 'soft-unwrap', button_options: { class: 'no-wrap' }) do + = _("No wrap") + = render Pajamas::ButtonComponent.new(icon: 'soft-wrap', button_options: { class: 'soft-wrap' }) do + = _("Soft wrap") .file-editor.code - if Feature.enabled?(:source_editor_toolbar, current_user) diff --git a/app/views/projects/merge_requests/_nav_btns.html.haml b/app/views/projects/merge_requests/_nav_btns.html.haml index 00d12423eb9..1efea6a1d37 100644 --- a/app/views/projects/merge_requests/_nav_btns.html.haml +++ b/app/views/projects/merge_requests/_nav_btns.html.haml @@ -5,7 +5,8 @@ .js-csv-import-export-buttons{ data: { show_export_button: "true", issuable_type: issuable_type, issuable_count: issuables_count_for_state(issuable_type.to_sym, params[:state]), email: notification_email, export_csv_path: export_csv_project_merge_requests_path(@project, request.query_parameters), container_class: 'gl-mr-3' } } - if @can_bulk_update - = button_tag _("Edit merge requests"), class: "gl-button btn btn-default gl-mr-3 js-bulk-update-toggle" + = render Pajamas::ButtonComponent.new(type: :submit, button_options: { class: 'gl-mr-3 js-bulk-update-toggle' }) do + = _("Edit merge requests") - if merge_project - = link_to new_merge_request_path, class: "gl-button btn btn-confirm", title: _("New merge request") do - = _('New merge request') + = render Pajamas::ButtonComponent.new(href: new_merge_request_path, variant: :confirm) do + = _("New merge request") diff --git a/app/views/projects/project_templates/_project_fields_form.html.haml b/app/views/projects/project_templates/_project_fields_form.html.haml index 7908550ca88..c3528b421b9 100644 --- a/app/views/projects/project_templates/_project_fields_form.html.haml +++ b/app/views/projects/project_templates/_project_fields_form.html.haml @@ -8,5 +8,5 @@ .selected-icon.gl-mr-3 .selected-template .input-group-append - %button.btn.gl-button.btn-default.change-template{ type: "button" } + = render Pajamas::ButtonComponent.new(button_options: { class: 'change-template' }) do = _('Change template') diff --git a/app/views/shared/blob/_markdown_buttons.html.haml b/app/views/shared/blob/_markdown_buttons.html.haml index ac64e659901..63ba8a2e0f9 100644 --- a/app/views/shared/blob/_markdown_buttons.html.haml +++ b/app/views/shared/blob/_markdown_buttons.html.haml @@ -37,11 +37,8 @@ title: _("Add a collapsible section") }) = markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: _("Add a table") }) - if supports_file_upload - %button.gl-button.btn.btn-default-tertiary.btn-icon.has-tooltip.js-attach-file-button{ type: 'button', - title: _("Attach a file or image"), - aria: { label: _("Attach a file or image") }, - data: { testid: 'button-attach-file', container: 'body' } } - = sprite_icon('paperclip') + = render Pajamas::ButtonComponent.new(icon: 'paperclip', category: :tertiary, button_options: { 'aria-label': _("Attach a file or image"), class: 'has-tooltip js-attach-file-button', data: { testid: 'button-attach-file', container: 'body' } }) do + = _("Attach a file or image") - if show_fullscreen_button - %button.gl-button.btn.btn-default-tertiary.btn-icon.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: _("Go full screen"), data: { container: "body" } } - = sprite_icon("maximize") + = render Pajamas::ButtonComponent.new(icon: 'maximize', category: :tertiary, button_options: { 'tabindex': -1, 'aria-label': _("Go full screen"), class: 'has-tooltip js-zen-enter', data: { container: 'body' } }) do + = _("Go full screen") diff --git a/app/views/shared/issuable/_feed_buttons.html.haml b/app/views/shared/issuable/_feed_buttons.html.haml index 69ff477d415..94b7fe14721 100644 --- a/app/views/shared/issuable/_feed_buttons.html.haml +++ b/app/views/shared/issuable/_feed_buttons.html.haml @@ -1,8 +1,8 @@ - show_calendar_button = local_assigns.fetch(:show_calendar_button, true) -= link_to safe_params.merge(rss_url_options), class: 'btn gl-button btn-default btn-icon has-tooltip', data: { container: 'body', testid: 'rss-feed-link' }, title: _('Subscribe to RSS feed') , 'aria-label': _('Subscribe to RSS feed') do - = sprite_icon('rss') += render Pajamas::ButtonComponent.new(href: safe_params.merge(rss_url_options), icon: 'rss', button_options: { class: 'has-tooltip', 'aria-label': _('Subscribe to RSS feed'), data: { container: 'body', testid: 'rss-feed-link' } }) do + = _('Subscribe to RSS feed') - if show_calendar_button - = link_to safe_params.merge(calendar_url_options), class: 'btn gl-button btn-default btn-icon has-tooltip', data: { container: 'body' }, title: _('Subscribe to calendar'), 'aria-label': _('Subscribe to calendar') do - = sprite_icon('calendar') + = render Pajamas::ButtonComponent.new(href: safe_params.merge(calendar_url_options), icon: 'calendar', button_options: { class: 'has-tooltip', 'aria-label': _('Subscribe to calendar'), data: { container: 'body' } }) do + = _('Subscribe to calendar') diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml index a5170b199e8..d342894e047 100644 --- a/app/views/shared/projects/_search_form.html.haml +++ b/app/views/shared/projects/_search_form.html.haml @@ -4,7 +4,7 @@ = form_tag filter_projects_path, method: :get, class: 'project-filter-form', 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 #{form_field_classes}", + class: "project-filter-form-field form-control gl-w-full! gl-pl-7 #{form_field_classes}", spellcheck: false, id: 'project-filter-form-field', autofocus: local_assigns[:autofocus] diff --git a/config/application.rb b/config/application.rb index a4607f914a6..0ed3e6fbd2d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -255,6 +255,8 @@ module Gitlab config.assets.precompile << "mailers/*.css" config.assets.precompile << "page_bundles/_mixins_and_variables_and_functions.css" config.assets.precompile << "page_bundles/admin/application_settings_metrics_and_profiling.css" + config.assets.precompile << "page_bundles/admin/geo_nodes.css" + config.assets.precompile << "page_bundles/admin/geo_replicable.css" config.assets.precompile << "page_bundles/admin/jobs_index.css" config.assets.precompile << "page_bundles/alert_management_details.css" config.assets.precompile << "page_bundles/alert_management_settings.css" diff --git a/config/feature_flags/development/search_page_vertical_nav.yml b/config/feature_flags/development/search_page_vertical_nav.yml new file mode 100644 index 00000000000..d33c80e5f2f --- /dev/null +++ b/config/feature_flags/development/search_page_vertical_nav.yml @@ -0,0 +1,8 @@ +--- +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/342621 +milestone: '15.5' +type: development +group: group::global search +default_enabled: false diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index 19dd40cc62f..c38e690000c 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -77,12 +77,6 @@ irb(#)> web_url # => "https://gitlab-example/root/discard" ``` -## View all keys in cache - -```ruby -Rails.cache.instance_variable_get(:@data).keys -``` - ## Profile a page ```ruby diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1599f92c38e..1a9d65e105c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -26991,6 +26991,9 @@ msgid_plural "No worries, you can still use all the %{strong}%{plan_name}%{stron msgstr[0] "" msgstr[1] "" +msgid "No wrap" +msgstr "" + msgid "No. of commits" msgstr "" @@ -37672,6 +37675,9 @@ msgstr "" msgid "Snowplow" msgstr "" +msgid "Soft wrap" +msgstr "" + msgid "Solid" msgstr "" diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb index 3c774f8b269..0d10aed955a 100644 --- a/spec/features/dashboard/issues_filter_spec.rb +++ b/spec/features/dashboard/issues_filter_spec.rb @@ -44,7 +44,7 @@ RSpec.describe 'Dashboard Issues filtering', :js do it 'updates atom feed link' do visit_issues(milestone_title: '', assignee_username: user.username) - link = find('.nav-controls a[title="Subscribe to RSS feed"]') + link = find('[data-testid="rss-feed-link"]') params = CGI.parse(URI.parse(link[:href]).query) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) diff --git a/spec/fixtures/markdown/markdown_golden_master_examples.yml b/spec/fixtures/markdown/markdown_golden_master_examples.yml index 495d00026d7..1c10f4fb9e0 100644 --- a/spec/fixtures/markdown/markdown_golden_master_examples.yml +++ b/spec/fixtures/markdown/markdown_golden_master_examples.yml @@ -427,6 +427,14 @@ html: |- +- name: diagram_plantuml_unicode + markdown: |- + ```plantuml + A -> B : Text with norwegian characters: æøå + ``` + html: |- + + - name: div markdown: |-
plain text
diff --git a/spec/frontend/__helpers__/dom_shims/index.js b/spec/frontend/__helpers__/dom_shims/index.js index 742d55196b4..3b41e2ca2a7 100644 --- a/spec/frontend/__helpers__/dom_shims/index.js +++ b/spec/frontend/__helpers__/dom_shims/index.js @@ -11,3 +11,4 @@ import './window_scroll_to'; import './scroll_by'; import './size_properties'; import './image_element_properties'; +import './text_encoder'; diff --git a/spec/frontend/__helpers__/dom_shims/text_encoder.js b/spec/frontend/__helpers__/dom_shims/text_encoder.js new file mode 100644 index 00000000000..d3d5221a003 --- /dev/null +++ b/spec/frontend/__helpers__/dom_shims/text_encoder.js @@ -0,0 +1,4 @@ +import { TextEncoder, TextDecoder } from 'util'; + +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; diff --git a/spec/frontend/jira_connect/subscriptions/pkce_spec.js b/spec/frontend/jira_connect/subscriptions/pkce_spec.js index 4ee88059b7a..671922c36d8 100644 --- a/spec/frontend/jira_connect/subscriptions/pkce_spec.js +++ b/spec/frontend/jira_connect/subscriptions/pkce_spec.js @@ -1,11 +1,7 @@ import crypto from 'crypto'; -import { TextEncoder, TextDecoder } from 'util'; import { createCodeVerifier, createCodeChallenge } from '~/jira_connect/subscriptions/pkce'; -global.TextEncoder = TextEncoder; -global.TextDecoder = TextDecoder; - describe('pkce', () => { beforeAll(() => { Object.defineProperty(global.self, 'crypto', { diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js index 49a160c9f23..f2572ca0ad2 100644 --- a/spec/frontend/lib/utils/text_utility_spec.js +++ b/spec/frontend/lib/utils/text_utility_spec.js @@ -386,4 +386,16 @@ describe('text_utility', () => { expect(textUtils.limitedCounterWithDelimiter(120)).toBe(120); }); }); + + describe('base64EncodeUnicode', () => { + it('encodes unicode characters', () => { + expect(textUtils.base64EncodeUnicode('😀')).toBe('8J+YgA=='); + }); + }); + + describe('base64DecodeUnicode', () => { + it('decodes unicode characters', () => { + expect(textUtils.base64DecodeUnicode('8J+YgA==')).toBe('😀'); + }); + }); }); diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index ad0705e4fbf..f3255a0c1d4 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -848,4 +848,289 @@ RSpec.describe SearchHelper do end end end + + describe '.search_navigation' do + using RSpec::Parameterized::TableSyntax + let(:user) { build(:user) } + let(:project) { build(:project) } + + before do + stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) + allow(self).to receive(:current_user).and_return(user) + allow(self).to receive(:can?).and_return(true) + allow(self).to receive(:project_search_tabs?).and_return(false) + allow(self).to receive(:feature_flag_tab_enabled?).and_return(false) + end + + context 'projects' do + where(:global_project, :condition) do + nil | true + ref(:project) | false + end + + with_them do + it 'data item condition is set correctly' do + @project = global_project + + expect(search_navigation[:projects][:condition]).to eq(condition) + end + end + end + + context 'code' do + where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :project_search_tabs, :condition) do + false | false | false | false + true | true | true | true + true | false | false | true + false | true | false | true + false | false | true | true + true | false | true | true + end + + with_them do + it 'data item condition is set correctly' do + allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs) + allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_code_tab).and_return(feature_flag_tab_enabled) + allow(self).to receive(:project_search_tabs?).with(:blobs).and_return(project_search_tabs) + + expect(search_navigation[:blobs][:condition]).to eq(condition) + end + end + end + + context 'epics' do + where(:global_project, :show_epics, :condition) do + nil | false | false + ref(:project) | true | false + ref(:project) | false | false + nil | true | true + end + + with_them do + it 'data item condition is set correctly' do + @project = global_project + allow(search_service).to receive(:show_epics?).and_return(show_epics) + + expect(search_navigation[:epics][:condition]).to eq(condition) + end + end + end + + context 'issues' do + where(:feature_flag_tab_enabled, :project_search_tabs, :condition) do + false | false | false + true | true | true + true | false | true + false | true | true + end + + with_them do + it 'data item condition is set correctly' do + allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_issues_tab).and_return(feature_flag_tab_enabled) + allow(self).to receive(:project_search_tabs?).with(:issues).and_return(project_search_tabs) + + expect(search_navigation[:issues][:condition]).to eq(condition) + end + end + end + + context 'merge requests' do + where(:feature_flag_tab_enabled, :project_search_tabs, :condition) do + false | false | false + true | true | true + true | false | true + false | true | true + end + + with_them do + it 'data item condition is set correctly' do + allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_merge_requests_tab).and_return(feature_flag_tab_enabled) + allow(self).to receive(:project_search_tabs?).with(:merge_requests).and_return(project_search_tabs) + + expect(search_navigation[:merge_requests][:condition]).to eq(condition) + end + end + end + + context 'wiki' do + where(:project_search_tabs, :show_elasticsearch_tabs, :condition) do + false | false | false + true | true | true + true | false | true + false | true | true + end + + with_them do + it 'data item condition is set correctly' do + allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs) + allow(self).to receive(:project_search_tabs?).with(:wiki).and_return(project_search_tabs) + + expect(search_navigation[:wiki_blobs][:condition]).to eq(condition) + end + end + end + + context 'commits' do + where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :project_search_tabs, :condition) do + false | false | false | false + true | true | true | true + true | false | false | false + false | true | true | true + end + + with_them do + it 'data item condition is set correctly' do + allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs) + allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_commits_tab).and_return(feature_flag_tab_enabled) + allow(self).to receive(:project_search_tabs?).with(:commits).and_return(project_search_tabs) + + expect(search_navigation[:commits][:condition]).to eq(condition) + end + end + end + + context 'comments' do + where(:show_elasticsearch_tabs, :project_search_tabs, :condition) do + true | true | true + false | false | false + true | false | true + false | true | true + end + + with_them do + it 'data item condition is set correctly' do + allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs) + allow(self).to receive(:project_search_tabs?).with(:notes).and_return(project_search_tabs) + + expect(search_navigation[:notes][:condition]).to eq(condition) + end + end + end + + context 'milestones' do + where(:global_project, :project_search_tabs, :condition) do + ref(:project) | true | true + nil | false | true + ref(:project) | false | false + nil | true | true + end + + with_them do + it 'data item condition is set correctly' do + @project = global_project + allow(self).to receive(:project_search_tabs?).with(:milestones).and_return(project_search_tabs) + + expect(search_navigation[:milestones][:condition]).to eq(condition) + end + end + end + + context 'users' do + where(:show_user_search_tab, :condition) do + true | true + false | false + end + + with_them do + it 'data item condition is set correctly' do + allow(self).to receive(:show_user_search_tab?).and_return(show_user_search_tab) + + expect(search_navigation[:users][:condition]).to eq(condition) + end + end + end + + context 'snippet_titles' do + where(:global_project, :global_show_snippets, :condition) do + ref(:project) | true | false + nil | false | false + ref(:project) | false | false + nil | true | true + end + + with_them do + it 'data item condition is set correctly' do + @show_snippets = global_show_snippets + @project = global_project + + expect(search_navigation[:snippet_titles][:condition]).to eq(condition) + end + end + end + end + + describe '.search_navigation_json' do + using RSpec::Parameterized::TableSyntax + context 'data' do + example_data_1 = { + projects: { label: _("Projects"), condition: true }, + blobs: { label: _("Code"), condition: false } + } + + example_data_2 = { + projects: { label: _("Projects"), condition: false }, + blobs: { label: _("Code"), condition: false } + } + + example_data_3 = { + projects: { label: _("Projects"), condition: true }, + blobs: { label: _("Code"), condition: true }, + epics: { label: _("Epics"), condition: true } + } + + where(:data, :matcher) do + example_data_1 | -> { include("projects") } + example_data_2 | -> { eq("{}") } + example_data_3 | -> { include("projects", "blobs", "epics") } + end + + with_them do + it 'converts correctly' do + allow(self).to receive(:search_navigation).with(no_args).and_return(data) + expect(search_navigation_json).to instance_exec(&matcher) + end + end + end + end + + describe '.search_filter_link_json' do + using RSpec::Parameterized::TableSyntax + + context 'data' do + where(:scope, :label, :data, :search, :active_scope) do + "projects" | "Projects" | { qa_selector: 'projects_tab' } | nil | "projects" + "snippet_titles" | "Titles and Descriptions" | nil | { snippets: "test" } | "code" + "projects" | "Projects" | { qa_selector: 'projects_tab' } | nil | "issue" + "snippet_titles" | "Titles and Descriptions" | nil | { snippets: "test" } | "snippet_titles" + end + + with_them do + it 'converts correctly' do + @timeout = false + @scope = active_scope + @search_results = double + dummy_count = 1000 + allow(self).to receive(:search_path).with(any_args).and_return("link test") + + allow(@search_results).to receive(:formatted_count).with(scope).and_return(dummy_count) + allow(self).to receive(:search_count_path).with(any_args).and_return("test count link") + + current_scope = scope == active_scope + + expected = { + label: label, + scope: scope, + data: data, + link: "link test", + active: current_scope + } + + expected[:count] = dummy_count if current_scope + expected[:count_link] = "test count link" unless current_scope + + expect(search_filter_link_json(scope, label, data, search)).to eq(expected) + end + end + end + end end