Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-06 03:08:18 +00:00
parent a807e50ade
commit 53a3791717
47 changed files with 852 additions and 467 deletions

View File

@ -5,26 +5,6 @@ Layout/FirstArrayElementIndentation:
Exclude:
- 'spec/lib/gitlab/github_import/importer/issues_importer_spec.rb'
- 'spec/lib/gitlab/search/found_blob_spec.rb'
- 'spec/lib/gitlab/serializer/ci/variables_spec.rb'
- 'spec/lib/gitlab/sidekiq_config_spec.rb'
- 'spec/lib/gitlab/ssh_public_key_spec.rb'
- 'spec/lib/gitlab/usage_data/topology_spec.rb'
- 'spec/lib/gitlab/usage_data_spec.rb'
- 'spec/lib/gitlab/utils_spec.rb'
- 'spec/lib/gitlab/webpack/manifest_spec.rb'
- 'spec/lib/google_api/cloud_platform/client_spec.rb'
- 'spec/lib/peek/views/bullet_detailed_spec.rb'
- 'spec/lib/system_check/incoming_email_check_spec.rb'
- 'spec/lib/unnested_in_filters/rewriter_spec.rb'
- 'spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb'
- 'spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb'
- 'spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb'
- 'spec/models/analytics/cycle_analytics/project_stage_spec.rb'
- 'spec/models/application_setting_spec.rb'
- 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/build_trace_spec.rb'
- 'spec/models/ci/daily_build_group_report_result_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
- 'spec/models/ci/runner_version_spec.rb'
- 'spec/models/repository_spec.rb'
- 'spec/models/user_preference_spec.rb'

View File

@ -30,6 +30,9 @@ module Mutations
argument :start_and_due_date_widget, ::Types::WorkItems::Widgets::StartAndDueDateUpdateInputType,
required: false,
description: 'Input for start and due date widget.'
argument :labels_widget, ::Types::WorkItems::Widgets::LabelsUpdateInputType,
required: false,
description: 'Input for labels widget.'
end
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module Types
module WorkItems
module Widgets
class LabelsUpdateInputType < BaseInputObject
graphql_name 'WorkItemWidgetLabelsUpdateInput'
argument :add_label_ids, [Types::GlobalIDType[::Label]],
required: false,
description: 'Global IDs of labels to be added to the work item.',
prepare: ->(label_ids, _ctx) { label_ids.map(&:model_id) }
argument :remove_label_ids, [Types::GlobalIDType[::Label]],
required: false,
description: 'Global IDs of labels to be removed from the work item.',
prepare: ->(label_ids, _ctx) { label_ids.map(&:model_id) }
end
end
end
end

View File

@ -14,7 +14,13 @@ module AppearancesHelper
end
def brand_image
image_tag(current_appearance.logo_path) if current_appearance&.logo?
image_tag(brand_image_path, alt: brand_title, class: 'gl-w-10')
end
def brand_image_path
return current_appearance.logo_path if current_appearance&.logo?
image_path('logo.svg')
end
def brand_text

View File

@ -65,7 +65,10 @@ module WikiHelper
reversed_direction = direction == 'desc' ? 'asc' : 'desc'
icon_class = direction == 'desc' ? 'highest' : 'lowest'
link_to(wiki_path(wiki, action: :pages, sort: sort, direction: reversed_direction),
link_options = { action: :pages, direction: reversed_direction }
link_options[:sort] = sort unless wiki.disable_sorting?
link_to(wiki_path(wiki, **link_options),
type: 'button', class: link_class, title: _('Sort direction')) do
sprite_icon("sort-#{icon_class}")
end

View File

@ -173,7 +173,7 @@ class Wiki
end
def empty?
!repository_exists? || list_pages(limit: 1).empty?
!repository_exists? || list_page_paths.empty?
end
def exists?
@ -191,13 +191,11 @@ class Wiki
# Returns an Array of GitLab WikiPage instances or an
# empty Array if this Wiki has no pages.
def list_pages(limit: 0, sort: nil, direction: DIRECTION_ASC, load_content: false)
wiki.list_pages(
limit: limit,
sort: sort,
direction_desc: direction == DIRECTION_DESC,
load_content: load_content
).map do |page|
WikiPage.new(self, page)
if list_pages_with_repository_rpcs?
create_wiki_repository unless repository_exists?
list_pages_with_repository_rpcs(limit: limit, sort: sort, direction: direction, load_content: load_content)
else
list_pages_with_legacy_wiki_service(limit: limit, sort: sort, direction: direction, load_content: load_content)
end
end
@ -383,6 +381,10 @@ class Wiki
false
end
def disable_sorting?
list_pages_with_repository_rpcs?
end
private
def multi_commit_options(action, message = nil, title = nil)
@ -452,14 +454,7 @@ class Wiki
def find_matched_file(title, version)
escaped_path = RE2::Regexp.escape(sluggified_title(title))
# We could not use ALLOWED_EXTENSIONS_REGEX constant or similar regexp with
# Regexp.union. The result combination complicated modifiers:
# /(?i-mx:md|mkdn?|mdown|markdown)|(?i-mx:rdoc).../
# Regexp used by Gitaly is Go's Regexp package. It does not support those
# features. So, we have to compose another more-friendly regexp to pass to
# Gitaly side.
extension_regexp = Wiki::MARKUPS.map { |_, format| format[:extension_regex].source }.join("|")
path_regexp = Gitlab::EncodingHelper.encode_utf8_no_detect("(?i)^#{escaped_path}\\.(#{extension_regexp})$")
path_regexp = Gitlab::EncodingHelper.encode_utf8_no_detect("(?i)^#{escaped_path}\\.(#{file_extension_regexp})$")
matched_files = repository.search_files_by_regexp(path_regexp, version)
return if matched_files.blank?
@ -487,7 +482,7 @@ class Wiki
format = find_page_format(path)
page = Gitlab::Git::WikiPage.new(
url_path: sluggified_title(path.sub(/\.[^.]+\z/, "")),
url_path: sluggified_title(strip_extension(path)),
title: canonicalize_filename(path),
format: format,
path: sluggified_title(path),
@ -509,6 +504,93 @@ class Wiki
Feature.enabled?(:wiki_find_page_with_normal_repository_rpcs, group, type: :development)
end
def file_extension_regexp
# We could not use ALLOWED_EXTENSIONS_REGEX constant or similar regexp with
# Regexp.union. The result combination complicated modifiers:
# /(?i-mx:md|mkdn?|mdown|markdown)|(?i-mx:rdoc).../
# Regexp used by Gitaly is Go's Regexp package. It does not support those
# features. So, we have to compose another more-friendly regexp to pass to
# Gitaly side.
Wiki::MARKUPS.map { |_, format| format[:extension_regex].source }.join("|")
end
def strip_extension(path)
path.sub(/\.[^.]+\z/, "")
end
def list_pages_with_repository_rpcs?
group =
if container.is_a?(::Group)
container
else
container.group
end
Feature.enabled?(:wiki_list_pages_with_normal_repository_rpcs, group, type: :development)
end
def list_page_paths
return [] if repository.empty?
path_regexp = Gitlab::EncodingHelper.encode_utf8_no_detect("(?i)\\.(#{file_extension_regexp})$")
repository.search_files_by_regexp(path_regexp, default_branch)
end
def list_pages_with_repository_rpcs(limit:, sort:, direction:, load_content:)
paths = list_page_paths
return [] if paths.empty?
pages = paths.map do |path|
page = Gitlab::Git::WikiPage.new(
url_path: sluggified_title(strip_extension(path)),
title: canonicalize_filename(path),
format: find_page_format(path),
path: sluggified_title(path),
raw_data: '',
name: canonicalize_filename(path),
historical: false
)
WikiPage.new(self, page)
end
sort_pages!(pages, sort, direction)
pages = pages.take(limit) if limit > 0
fetch_pages_content!(pages) if load_content
pages
end
def list_pages_with_legacy_wiki_service(limit:, sort:, direction:, load_content:)
wiki.list_pages(
limit: limit,
sort: sort,
direction_desc: direction == DIRECTION_DESC,
load_content: load_content
).map do |page|
WikiPage.new(self, page)
end
end
# After migrating to normal repository RPCs, it's very expensive to sort the
# pages by created_at. We have to either ListLastCommitsForTree RPC call or
# N+1 LastCommitForPath. Either are efficient for a large repository.
# Therefore, we decide to sort the title only.
def sort_pages!(pages, _sort, direction)
# Sort by path to ensure the files inside a sub-folder are grouped and sorted together
pages.sort_by!(&:path)
pages.reverse! if direction == DIRECTION_DESC
end
def fetch_pages_content!(pages)
blobs =
repository
.blobs_at(pages.map { |page| [default_branch, page.path] } )
.to_h { |blob| [blob.path, blob.data] }
pages.each do |page|
page.raw_content = blobs[page.path]
end
end
end
Wiki.prepend_mod_with('Wiki')

View File

@ -99,6 +99,13 @@ class WikiPage
attributes[:content] ||= page&.text_data
end
def raw_content=(content)
return if page.nil?
page.raw_data = content
attributes[:content] = page.text_data
end
# The hierarchy of the directory this page is contained in.
def directory
wiki.page_title_and_dir(slug)&.last.to_s
@ -118,7 +125,7 @@ class WikiPage
def version
return unless persisted?
@version ||= @page.version
@version ||= @page.version || last_version
end
def path
@ -151,7 +158,7 @@ class WikiPage
end
def last_version
@last_version ||= versions(limit: 1).first
@last_version ||= wiki.repository.last_commit_for_path(wiki.default_branch, page.path) if page
end
def last_commit_sha

View File

@ -2,18 +2,22 @@
module WorkItems
module WidgetableService
def execute_widgets(work_item:, callback:, widget_params: {})
def execute_widgets(work_item:, callback:, widget_params: {}, service_params: {})
work_item.widgets.each do |widget|
widget_service(widget).try(callback, params: widget_params[widget.class.api_symbol])
widget_service(widget, service_params).try(callback, params: widget_params[widget.class.api_symbol])
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def widget_service(widget)
def widget_service(widget, service_params)
@widget_services ||= {}
return @widget_services[widget] if @widget_services.has_key?(widget)
@widget_services[widget] = widget_service_class(widget)&.new(widget: widget, current_user: current_user)
@widget_services[widget] = widget_service_class(widget)&.new(
widget: widget,
current_user: current_user,
service_params: service_params
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables

View File

@ -266,6 +266,10 @@ class IssuableBaseService < ::BaseProjectService
# To be overridden by subclasses
end
def prepare_update_params(issuable)
# To be overridden by subclasses
end
def after_update(issuable)
handle_description_updated(issuable)
end
@ -277,6 +281,7 @@ class IssuableBaseService < ::BaseProjectService
end
def update(issuable)
prepare_update_params(issuable)
handle_quick_actions(issuable)
filter_params(issuable)

View File

@ -26,6 +26,17 @@ module WorkItems
private
def prepare_update_params(work_item)
execute_widgets(
work_item: work_item,
callback: :prepare_update_params,
widget_params: @widget_params,
service_params: params
)
super
end
def before_update(work_item, skip_spam_check: false)
execute_widgets(work_item: work_item, callback: :before_update_callback, widget_params: @widget_params)

View File

@ -5,12 +5,13 @@ module WorkItems
class BaseService < ::BaseService
WidgetError = Class.new(StandardError)
attr_reader :widget, :work_item, :current_user
attr_reader :widget, :work_item, :current_user, :service_params
def initialize(widget:, current_user:)
def initialize(widget:, current_user:, service_params: {})
@widget = widget
@work_item = widget.work_item
@current_user = current_user
@service_params = service_params
end
private

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module WorkItems
module Widgets
module LabelsService
class UpdateService < WorkItems::Widgets::BaseService
def prepare_update_params(params: {})
return if params.blank?
service_params.merge!(params.slice(:add_label_ids, :remove_label_ids))
end
end
end
end
end

View File

@ -13,9 +13,9 @@
= render "layouts/flash"
.mt-3
.col-sm-12.gl-text-center
%img.gl-w-10{ :alt => _("GitLab Logo"), :src => image_path('logo.svg') }
= brand_image
%h1.mb-3.gl-font-size-h2
= current_appearance&.title.presence || _('GitLab')
= brand_title
- if current_appearance&.description?
= brand_text
= render_if_exists 'layouts/devise_help_text'

View File

@ -15,7 +15,8 @@
.dropdown.inline.wiki-sort-dropdown
.btn-group{ role: 'group' }
= gl_redirect_listbox_tag wiki_sort_options, params[:sort], data: { right: true }
- unless @wiki.disable_sorting?
= gl_redirect_listbox_tag wiki_sort_options, params[:sort], data: { right: true }
= wiki_sort_controls(@wiki, params[:sort], params[:direction])
%ul.wiki-pages-list.content-list

View File

@ -0,0 +1,8 @@
---
name: wiki_list_pages_with_normal_repository_rpcs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96461
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372259
milestone: '15.4'
type: development
group: group::gitaly
default_enabled: false

View File

@ -5839,6 +5839,7 @@ Input type: `WorkItemUpdateInput`
| <a id="mutationworkitemupdatehierarchywidget"></a>`hierarchyWidget` | [`WorkItemWidgetHierarchyUpdateInput`](#workitemwidgethierarchyupdateinput) | Input for hierarchy widget. |
| <a id="mutationworkitemupdateid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. |
| <a id="mutationworkitemupdateiterationwidget"></a>`iterationWidget` | [`WorkItemWidgetIterationInput`](#workitemwidgetiterationinput) | Input for iteration widget. |
| <a id="mutationworkitemupdatelabelswidget"></a>`labelsWidget` | [`WorkItemWidgetLabelsUpdateInput`](#workitemwidgetlabelsupdateinput) | Input for labels widget. |
| <a id="mutationworkitemupdatestartandduedatewidget"></a>`startAndDueDateWidget` | [`WorkItemWidgetStartAndDueDateUpdateInput`](#workitemwidgetstartandduedateupdateinput) | Input for start and due date widget. |
| <a id="mutationworkitemupdatestateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. |
| <a id="mutationworkitemupdatetitle"></a>`title` | [`String`](#string) | Title of the work item. |
@ -23494,6 +23495,7 @@ A time-frame defined as a closed inclusive range of two dates.
| <a id="workitemupdatedtaskinputdescriptionwidget"></a>`descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description widget. |
| <a id="workitemupdatedtaskinputhierarchywidget"></a>`hierarchyWidget` | [`WorkItemWidgetHierarchyUpdateInput`](#workitemwidgethierarchyupdateinput) | Input for hierarchy widget. |
| <a id="workitemupdatedtaskinputid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. |
| <a id="workitemupdatedtaskinputlabelswidget"></a>`labelsWidget` | [`WorkItemWidgetLabelsUpdateInput`](#workitemwidgetlabelsupdateinput) | Input for labels widget. |
| <a id="workitemupdatedtaskinputstartandduedatewidget"></a>`startAndDueDateWidget` | [`WorkItemWidgetStartAndDueDateUpdateInput`](#workitemwidgetstartandduedateupdateinput) | Input for start and due date widget. |
| <a id="workitemupdatedtaskinputstateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. |
| <a id="workitemupdatedtaskinputtitle"></a>`title` | [`String`](#string) | Title of the work item. |
@ -23539,6 +23541,15 @@ A time-frame defined as a closed inclusive range of two dates.
| ---- | ---- | ----------- |
| <a id="workitemwidgetiterationinputiterationid"></a>`iterationId` | [`IterationID`](#iterationid) | Iteration to assign to the work item. |
### `WorkItemWidgetLabelsUpdateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="workitemwidgetlabelsupdateinputaddlabelids"></a>`addLabelIds` | [`[LabelID!]`](#labelid) | Global IDs of labels to be added to the work item. |
| <a id="workitemwidgetlabelsupdateinputremovelabelids"></a>`removeLabelIds` | [`[LabelID!]`](#labelid) | Global IDs of labels to be removed from the work item. |
### `WorkItemWidgetStartAndDueDateUpdateInput`
#### Arguments

View File

@ -41,6 +41,11 @@ module Gitlab
@text_data = @raw_data && Gitlab::EncodingHelper.encode!(@raw_data.dup)
end
def raw_data=(data)
@raw_data = data
@text_data = @raw_data && Gitlab::EncodingHelper.encode!(@raw_data.dup)
end
end
end
end

View File

@ -17964,9 +17964,6 @@ msgstr ""
msgid "GitLab KAS"
msgstr ""
msgid "GitLab Logo"
msgstr ""
msgid "GitLab Pages"
msgstr ""

View File

@ -12,14 +12,37 @@ RSpec.describe 'Project wikis', :js do
let(:wiki) { create(:project_wiki, user: user, project: project) }
let(:project) { create(:project, namespace: user.namespace, creator: user) }
it_behaves_like 'User creates wiki page'
it_behaves_like 'User deletes wiki page'
it_behaves_like 'User previews wiki changes'
it_behaves_like 'User updates wiki page'
it_behaves_like 'User uses wiki shortcuts'
it_behaves_like 'User views AsciiDoc page with includes'
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages'
it_behaves_like 'User views wiki sidebar'
it_behaves_like 'User views Git access wiki page'
context 'with legacy wiki rpcs' do
before do
stub_feature_flags(wiki_list_pages_with_normal_repository_rpcs: false)
end
it_behaves_like 'User creates wiki page'
it_behaves_like 'User deletes wiki page'
it_behaves_like 'User previews wiki changes'
it_behaves_like 'User updates wiki page'
it_behaves_like 'User uses wiki shortcuts'
it_behaves_like 'User views AsciiDoc page with includes'
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages'
it_behaves_like 'User views wiki sidebar'
it_behaves_like 'User views Git access wiki page'
end
context 'with normal repository rpcs' do
before do
stub_feature_flags(wiki_list_pages_with_normal_repository_rpcs: true)
end
it_behaves_like 'User creates wiki page'
it_behaves_like 'User deletes wiki page'
it_behaves_like 'User previews wiki changes'
it_behaves_like 'User updates wiki page'
it_behaves_like 'User uses wiki shortcuts'
it_behaves_like 'User views AsciiDoc page with includes'
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages', false
it_behaves_like 'User views wiki sidebar'
it_behaves_like 'User views Git access wiki page'
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Types::WorkItems::Widgets::LabelsUpdateInputType do
it { expect(described_class.graphql_name).to eq('WorkItemWidgetLabelsUpdateInput') }
it { expect(described_class.arguments.keys).to contain_exactly('addLabelIds', 'removeLabelIds') }
end

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe AppearancesHelper do
let_it_be(:gitlab_logo) { ActionController::Base.helpers.image_path('logo.svg') }
before do
user = create(:user)
allow(helper).to receive(:current_user).and_return(user)
@ -59,23 +61,59 @@ RSpec.describe AppearancesHelper do
end
describe '#brand_image' do
let!(:appearance) { create(:appearance, :with_logo) }
context 'when there is a logo' do
let!(:appearance) { create(:appearance, :with_logo) }
it 'returns a path' do
expect(helper.brand_image).to match(%r(img data-src="/uploads/-/system/appearance/.*png))
expect(helper.brand_image).to match(%r(img .* data-src="/uploads/-/system/appearance/.*png))
end
context 'when there is no associated upload' do
before do
# Legacy attachments were not tracked in the uploads table
appearance.logo.upload.destroy!
appearance.reload
end
it 'falls back to using the original path' do
expect(helper.brand_image).to match(%r(img .* data-src="/uploads/-/system/appearance/.*png))
end
end
end
context 'when there is a logo but no associated upload' do
before do
# Legacy attachments were not tracked in the uploads table
appearance.logo.upload.destroy!
appearance.reload
context 'when there is no logo' do
it 'returns path of GitLab logo' do
expect(helper.brand_image).to match(%r(img .* data-src="#{gitlab_logo}))
end
end
it 'falls back to using the original path' do
expect(helper.brand_image).to match(%r(img data-src="/uploads/-/system/appearance/.*png))
context 'when there is a title' do
let!(:appearance) { create(:appearance, title: 'My title') }
it 'returns the title' do
expect(helper.brand_image).to match(%r(img alt="My title"))
end
end
context 'when there is no title' do
it 'returns the default title' do
expect(helper.brand_image).to match(%r(img alt="GitLab))
end
end
end
describe '#brand_image_path' do
context 'with a custom logo' do
let!(:appearance) { create(:appearance, :with_logo) }
it 'returns path of custom logo' do
expect(helper.brand_image_path).to match(%r(/uploads/-/system/appearance/.*/dk.png))
end
end
context 'with no custom logo' do
it 'returns path of GitLab logo' do
expect(helper.brand_image_path).to eq(gitlab_logo)
end
end
end

View File

@ -79,37 +79,72 @@ RSpec.describe WikiHelper do
let(:classes) { "gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort" }
def expected_link(sort, direction, icon_class)
path = "/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}&sort=#{sort}"
path =
if sort
"/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}&sort=#{sort}"
else
"/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}"
end
helper.link_to(path, type: 'button', class: classes, title: 'Sort direction') do
helper.sprite_icon("sort-#{icon_class}")
end
end
context 'initial call' do
let(:sort) { nil }
let(:direction) { nil }
context 'wiki sorting enabled' do
before do
allow(wiki).to receive(:disable_sorting?).and_return(false)
end
it 'renders with default values' do
expect(wiki_link).to eq(expected_link('title', 'desc', 'lowest'))
context 'initial call' do
let(:sort) { nil }
let(:direction) { nil }
it 'renders with default values' do
expect(wiki_link).to eq(expected_link('title', 'desc', 'lowest'))
end
end
context 'sort by title' do
let(:sort) { 'title' }
let(:direction) { 'asc' }
it 'renders a link with opposite direction' do
expect(wiki_link).to eq(expected_link('title', 'desc', 'lowest'))
end
end
context 'sort by created_at' do
let(:sort) { 'created_at' }
let(:direction) { 'desc' }
it 'renders a link with opposite direction' do
expect(wiki_link).to eq(expected_link('created_at', 'asc', 'highest'))
end
end
end
context 'sort by title' do
let(:sort) { 'title' }
let(:direction) { 'asc' }
it 'renders a link with opposite direction' do
expect(wiki_link).to eq(expected_link('title', 'desc', 'lowest'))
context 'wiki sorting disabled' do
before do
allow(wiki).to receive(:disable_sorting?).and_return(true)
end
end
context 'sort by created_at' do
let(:sort) { 'created_at' }
let(:direction) { 'desc' }
context 'sort by created_at' do
let(:sort) { 'created_at' }
let(:direction) { 'asc' }
it 'renders a link with opposite direction' do
expect(wiki_link).to eq(expected_link('created_at', 'asc', 'highest'))
it 'ignores created_at and renders a link with opposite direction' do
expect(wiki_link).to eq(expected_link(nil, 'desc', 'lowest'))
end
end
context 'initial call' do
let(:sort) { nil }
let(:direction) { nil }
it 'renders with default values' do
expect(wiki_link).to eq(expected_link(nil, 'desc', 'lowest'))
end
end
end
end

View File

@ -118,7 +118,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
shared_examples 'blob search repository ref' do |entity_type, blob_type|
let(:query) { 'files' }
let(:file_finder) { double }
let(:project_branch) { 'project_branch' }
let(:project_branch) { blob_type == 'wiki_blobs' ? entity.default_branch : 'project_branch' }
subject(:objects) { results.objects(blob_type) }
@ -209,8 +209,11 @@ RSpec.describe Gitlab::ProjectSearchResults do
describe 'wiki search' do
let(:project) { create(:project, :public, :wiki_repo) }
let(:project_branch) { 'project_branch' }
before do
allow(project.wiki).to receive(:root_ref).and_return(project_branch)
project.wiki.create_page('Files/Title', 'Content')
project.wiki.create_page('CHANGELOG', 'Files example')
end

View File

@ -13,9 +13,10 @@ RSpec.describe Gitlab::Serializer::Ci::Variables do
end
it 'converts keys into strings and symbolizes hash' do
is_expected.to eq([
{ key: 'key', value: 'value', public: true },
{ key: 'wee', value: 1, public: false }
])
is_expected.to eq(
[
{ key: 'key', value: 'value', public: true },
{ key: 'wee', value: 1, public: false }
])
end
end

View File

@ -193,9 +193,7 @@ RSpec.describe Gitlab::SidekiqConfig do
it 'returns worker queue mappings that have queues in the current Sidekiq options' do
queues = described_class.routing_queues
expect(queues).to match_array(%w[
default mailers high_urgency gitaly
])
expect(queues).to match_array(%w[default mailers high_urgency gitaly])
expect(queues).not_to include('not_exist')
end
end

View File

@ -88,12 +88,12 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true, fips_mode: false do
it 'returns all supported algorithms' do
expect(described_class.supported_algorithms).to eq(
%w(
ssh-rsa
ssh-dss
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
ssh-ed25519
sk-ecdsa-sha2-nistp256@openssh.com
sk-ssh-ed25519@openssh.com
ssh-rsa
ssh-dss
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
ssh-ed25519
sk-ecdsa-sha2-nistp256@openssh.com
sk-ssh-ed25519@openssh.com
)
)
end
@ -102,12 +102,12 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true, fips_mode: false do
it 'returns all supported algorithms' do
expect(described_class.supported_algorithms).to eq(
%w(
ssh-rsa
ssh-dss
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
ssh-ed25519
sk-ecdsa-sha2-nistp256@openssh.com
sk-ssh-ed25519@openssh.com
ssh-rsa
ssh-dss
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
ssh-ed25519
sk-ecdsa-sha2-nistp256@openssh.com
sk-ssh-ed25519@openssh.com
)
)
end

View File

@ -523,210 +523,210 @@ RSpec.describe Gitlab::UsageData::Topology do
receive(:query)
.with(/gitlab_usage_ping:ops:rate/)
.and_return(result || [
{
'metric' => { 'component' => 'http_requests', 'service' => 'workhorse' },
'value' => [1000, '0.01']
}
])
{
'metric' => { 'component' => 'http_requests', 'service' => 'workhorse' },
'value' => [1000, '0.01']
}
])
end
def receive_query_apdex_ratio_query(result: nil)
receive(:query)
.with(/gitlab_usage_ping:sql_duration_apdex:ratio_rate5m/)
.and_return(result || [
{
'metric' => {},
'value' => [1000, '0.996']
}
])
{
'metric' => {},
'value' => [1000, '0.996']
}
])
end
def receive_node_memory_query(result: nil)
receive(:query)
.with(/node_memory_total_bytes/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '512']
},
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '1024']
}
])
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '512']
},
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '1024']
}
])
end
def receive_node_memory_utilization_query(result: nil)
receive(:query)
.with(/node_memory_utilization/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '0.45']
},
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '0.25']
}
])
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '0.45']
},
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '0.25']
}
])
end
def receive_node_cpu_count_query(result: nil)
receive(:query)
.with(/node_cpus/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '16']
},
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '8']
}
])
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '16']
},
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '8']
}
])
end
def receive_node_cpu_utilization_query(result: nil)
receive(:query)
.with(/node_cpu_utilization/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '0.2']
},
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '0.1']
}
])
{
'metric' => { 'instance' => 'instance2:8090' },
'value' => [1000, '0.2']
},
{
'metric' => { 'instance' => 'instance1:8080' },
'value' => [1000, '0.1']
}
])
end
def receive_node_uname_info_query(result: nil)
receive(:query)
.with('node_uname_info')
.and_return(result || [
{
"metric" => {
"__name__" => "node_uname_info",
"domainname" => "(none)",
"instance" => "instance1:9100",
"job" => "node_exporter",
"machine" => "x86_64",
"nodename" => "instance1",
"release" => "4.19.76-linuxkit",
"sysname" => "Linux"
},
"value" => [1592463033.359, "1"]
},
{
"metric" => {
"__name__" => "node_uname_info",
"domainname" => "(none)",
"instance" => "instance2:9100",
"job" => "node_exporter",
"machine" => "x86_64",
"nodename" => "instance2",
"release" => "4.15.0-101-generic",
"sysname" => "Linux"
},
"value" => [1592463033.359, "1"]
}
])
{
"metric" => {
"__name__" => "node_uname_info",
"domainname" => "(none)",
"instance" => "instance1:9100",
"job" => "node_exporter",
"machine" => "x86_64",
"nodename" => "instance1",
"release" => "4.19.76-linuxkit",
"sysname" => "Linux"
},
"value" => [1592463033.359, "1"]
},
{
"metric" => {
"__name__" => "node_uname_info",
"domainname" => "(none)",
"instance" => "instance2:9100",
"job" => "node_exporter",
"machine" => "x86_64",
"nodename" => "instance2",
"release" => "4.15.0-101-generic",
"sysname" => "Linux"
},
"value" => [1592463033.359, "1"]
}
])
end
def receive_node_service_memory_rss_query(result: nil)
receive(:query)
.with(/process_resident_memory_bytes/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '300']
},
{
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '303']
},
# instance 2: runs a dedicated Sidekiq + Redis (which uses a different metric name)
{
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '400']
},
{
'metric' => { 'instance' => 'instance2:9121', 'job' => 'redis' },
'value' => [1000, '402']
}
])
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '300']
},
{
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '303']
},
# instance 2: runs a dedicated Sidekiq + Redis (which uses a different metric name)
{
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '400']
},
{
'metric' => { 'instance' => 'instance2:9121', 'job' => 'redis' },
'value' => [1000, '402']
}
])
end
def receive_node_service_memory_uss_query(result: nil)
receive(:query)
.with(/process_unique_memory_bytes/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '301']
}
])
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '301']
}
])
end
def receive_node_service_memory_pss_query(result: nil)
receive(:query)
.with(/process_proportional_memory_bytes/, an_instance_of(Hash))
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '302']
},
{
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '401']
}
])
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '302']
},
{
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '401']
}
])
end
def receive_node_service_process_count_query(result: nil)
receive(:query)
.with(/service_process:count/, an_instance_of(Hash))
.and_return(result || [
# instance 1
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '10']
},
{
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '5']
},
# instance 2
{
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '15']
},
{
'metric' => { 'instance' => 'instance2:9121', 'job' => 'redis' },
'value' => [1000, '1']
},
{
'metric' => { 'instance' => 'instance2:8080', 'job' => 'registry' },
'value' => [1000, '1']
}
])
# instance 1
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
'value' => [1000, '10']
},
{
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '5']
},
# instance 2
{
'metric' => { 'instance' => 'instance2:8090', 'job' => 'gitlab-sidekiq' },
'value' => [1000, '15']
},
{
'metric' => { 'instance' => 'instance2:9121', 'job' => 'redis' },
'value' => [1000, '1']
},
{
'metric' => { 'instance' => 'instance2:8080', 'job' => 'registry' },
'value' => [1000, '1']
}
])
end
def receive_node_service_app_server_workers_query(result: nil)
receive(:query)
.with(/app_server_workers/, an_instance_of(Hash))
.and_return(result || [
# instance 1
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails', 'server' => 'puma' },
'value' => [1000, '2']
},
# instance 2
{
'metric' => { 'instance' => 'instance2:8080', 'job' => 'gitlab-rails', 'server' => 'puma' },
'value' => [1000, '1']
}
])
# instance 1
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails', 'server' => 'puma' },
'value' => [1000, '2']
},
# instance 2
{
'metric' => { 'instance' => 'instance2:8080', 'job' => 'gitlab-rails', 'server' => 'puma' },
'value' => [1000, '1']
}
])
end
end

View File

@ -1050,12 +1050,13 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
before do
allow(described_class).to receive(:operating_system).and_return('ubuntu-20.04')
expect(prometheus_client).to receive(:query).with(/gitlab_usage_ping:gitaly_apdex:ratio_avg_over_time_5m/).and_return([
{
'metric' => {},
'value' => [1616016381.473, '0.95']
}
])
expect(prometheus_client).to receive(:query)
.with(/gitlab_usage_ping:gitaly_apdex:ratio_avg_over_time_5m/)
.and_return(
[
{ 'metric' => {},
'value' => [1616016381.473, '0.95'] }
])
expect(described_class).to receive(:with_prometheus_client).and_yield(prometheus_client)
end

View File

@ -582,11 +582,12 @@ RSpec.describe Gitlab::Utils do
end
it 'sorts items like the regular sort_by' do
expect(sorted_list).to eq([
{ name: 'obj 2', priority: 1 },
{ name: 'obj 1', priority: 2 },
{ name: 'obj 3', priority: 3 }
])
expect(sorted_list).to eq(
[
{ name: 'obj 2', priority: 1 },
{ name: 'obj 1', priority: 2 },
{ name: 'obj 3', priority: 3 }
])
end
end
end

View File

@ -66,10 +66,11 @@ RSpec.describe Gitlab::Webpack::Manifest do
describe "webpack errors" do
context "when webpack has 'Module build failed' errors in its manifest" do
it "errors" do
error_manifest = Gitlab::Json.parse(manifest).merge("errors" => [
"somethingModule build failed something",
"I am an error"
]).to_json
error_manifest = Gitlab::Json.parse(manifest).merge("errors" =>
[
"somethingModule build failed something",
"I am an error"
]).to_json
stub_request(:get, "http://hostname:2000/public_path/my_manifest.json").to_return(body: error_manifest, status: 200)
expect { Gitlab::Webpack::Manifest.asset_paths("entry1") }.to raise_error(Gitlab::Webpack::Manifest::WebpackError)

View File

@ -17,17 +17,19 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
let(:tier) { 'mock-tier' }
let(:database_list) do
Google::Apis::SqladminV1beta4::ListDatabasesResponse.new(items: [
Google::Apis::SqladminV1beta4::Database.new(name: 'db_01', instance: database_instance),
Google::Apis::SqladminV1beta4::Database.new(name: 'db_02', instance: database_instance)
])
Google::Apis::SqladminV1beta4::ListDatabasesResponse.new(
items: [
Google::Apis::SqladminV1beta4::Database.new(name: 'db_01', instance: database_instance),
Google::Apis::SqladminV1beta4::Database.new(name: 'db_02', instance: database_instance)
])
end
let(:user_list) do
Google::Apis::SqladminV1beta4::ListUsersResponse.new(items: [
Google::Apis::SqladminV1beta4::User.new(name: 'user_01', instance: database_instance),
Google::Apis::SqladminV1beta4::User.new(name: 'user_02', instance: database_instance)
])
Google::Apis::SqladminV1beta4::ListUsersResponse.new(
items: [
Google::Apis::SqladminV1beta4::User.new(name: 'user_01', instance: database_instance),
Google::Apis::SqladminV1beta4::User.new(name: 'user_02', instance: database_instance)
])
end
describe '.session_key_for_redirect_uri' do

View File

@ -44,10 +44,11 @@ RSpec.describe Peek::Views::BulletDetailed do
expect(subject.key).to eq('bullet')
expect(subject.results[:calls]).to eq(2)
expect(subject.results[:warnings]).to eq([Peek::Views::BulletDetailed::WARNING_MESSAGE])
expect(subject.results[:details]).to eq([
{ notification: 'Title 1: Body 1', backtrace: "first\nsecond\n" },
{ notification: 'Title 2: Body 2', backtrace: "first\nsecond\n" }
])
expect(subject.results[:details]).to eq(
[
{ notification: 'Title 1: Body 1', backtrace: "first\nsecond\n" },
{ notification: 'Title 2: Body 2', backtrace: "first\nsecond\n" }
])
end
end
end

View File

@ -26,11 +26,12 @@ RSpec.describe SystemCheck::IncomingEmailCheck do
end
it 'runs IMAP and mailroom checks' do
expect(SystemCheck).to receive(:run).with('Reply by email', [
SystemCheck::IncomingEmail::ImapAuthenticationCheck,
SystemCheck::IncomingEmail::MailRoomEnabledCheck,
SystemCheck::IncomingEmail::MailRoomRunningCheck
])
expect(SystemCheck).to receive(:run).with('Reply by email',
[
SystemCheck::IncomingEmail::ImapAuthenticationCheck,
SystemCheck::IncomingEmail::MailRoomEnabledCheck,
SystemCheck::IncomingEmail::MailRoomRunningCheck
])
subject.multi_check
end
@ -42,10 +43,11 @@ RSpec.describe SystemCheck::IncomingEmailCheck do
end
it 'runs mailroom checks' do
expect(SystemCheck).to receive(:run).with('Reply by email', [
SystemCheck::IncomingEmail::MailRoomEnabledCheck,
SystemCheck::IncomingEmail::MailRoomRunningCheck
])
expect(SystemCheck).to receive(:run).with('Reply by email',
[
SystemCheck::IncomingEmail::MailRoomEnabledCheck,
SystemCheck::IncomingEmail::MailRoomRunningCheck
])
subject.multi_check
end

View File

@ -41,14 +41,15 @@ RSpec.describe UnnestedInFilters::Rewriter do
context 'when the order is a Keyset order' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'user_type',
order_expression: User.arel_table['user_type'].desc,
nullable: :not_nullable,
distinct: false
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'user_type',
order_expression: User.arel_table['user_type'].desc,
nullable: :not_nullable,
distinct: false
)
])
end
it { is_expected.to be_truthy }
@ -152,14 +153,15 @@ RSpec.describe UnnestedInFilters::Rewriter do
context 'when the order is a Keyset order' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'user_type',
order_expression: User.arel_table['user_type'].desc,
nullable: :not_nullable,
distinct: false
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'user_type',
order_expression: User.arel_table['user_type'].desc,
nullable: :not_nullable,
distinct: false
)
])
end
it 'changes the query' do

View File

@ -72,12 +72,14 @@ RSpec.describe AddPremiumAndUltimatePlanLimits, :migration do
it 'creates plan limits from the source plan' do
migrate!
expect(AddPremiumAndUltimatePlanLimits::PlanLimits.pluck(:plan_id, :storage_size_limit)).to match_array([
[silver.id, silver_limits.storage_size_limit],
[gold.id, gold_limits.storage_size_limit],
[premium.id, silver_limits.storage_size_limit],
[ultimate.id, gold_limits.storage_size_limit]
])
expect(AddPremiumAndUltimatePlanLimits::PlanLimits.pluck(:plan_id, :storage_size_limit))
.to match_array(
[
[silver.id, silver_limits.storage_size_limit],
[gold.id, gold_limits.storage_size_limit],
[premium.id, silver_limits.storage_size_limit],
[ultimate.id, gold_limits.storage_size_limit]
])
end
end
end

View File

@ -57,36 +57,39 @@ RSpec.describe BackfillEscalationPoliciesForOncallSchedules do
expect(new_polices).to all have_attributes(name: 'On-call Escalation Policy')
expect(new_policy_b1.description).to eq('Immediately notify Schedule B1')
expect(new_policy_c1.description).to eq('Immediately notify Schedule C1')
expect(policies.pluck(:project_id)).to eq([
project_d.id,
project_e.id,
project_f.id,
project_f.id,
project_b.id,
project_c.id
])
expect(policies.pluck(:project_id)).to eq(
[
project_d.id,
project_e.id,
project_f.id,
project_f.id,
project_b.id,
project_c.id
])
expect(new_rules).to all have_attributes(status: 1, elapsed_time_seconds: 0)
expect(rules.pluck(:policy_id)).to eq([
rule_d1.policy_id,
rule_e1.policy_id,
rule_f1.policy_id,
rule_f2.policy_id,
rule_f3.policy_id,
new_policy_b1.id,
new_policy_c1.id,
new_policy_c1.id
])
expect(rules.pluck(:oncall_schedule_id)).to eq([
rule_d1.oncall_schedule_id,
rule_e1.oncall_schedule_id,
rule_f1.oncall_schedule_id,
rule_f2.oncall_schedule_id,
rule_f3.oncall_schedule_id,
schedule_b1.id,
schedule_c1.id,
schedule_c2.id
])
expect(rules.pluck(:policy_id)).to eq(
[
rule_d1.policy_id,
rule_e1.policy_id,
rule_f1.policy_id,
rule_f2.policy_id,
rule_f3.policy_id,
new_policy_b1.id,
new_policy_c1.id,
new_policy_c1.id
])
expect(rules.pluck(:oncall_schedule_id)).to eq(
[
rule_d1.oncall_schedule_id,
rule_e1.oncall_schedule_id,
rule_f1.oncall_schedule_id,
rule_f2.oncall_schedule_id,
rule_f3.oncall_schedule_id,
schedule_b1.id,
schedule_c1.id,
schedule_c2.id
])
end
end

View File

@ -49,12 +49,13 @@ RSpec.describe SliceMergeRequestDiffCommitMigrations, :migration do
.pending
.to_a
expect(new_jobs.map(&:arguments)).to eq([
[1, 5_001],
[5_001, 10_001],
[10_001, 15_001],
[15_001, 20_001]
])
expect(new_jobs.map(&:arguments)).to eq(
[
[1, 5_001],
[5_001, 10_001],
[10_001, 15_001],
[15_001, 20_001]
])
end
it 'schedules a background migration for the first job' do

View File

@ -48,10 +48,11 @@ RSpec.describe Analytics::CycleAnalytics::ProjectStage do
subject(:distinct_start_and_end_event_identifiers) { described_class.distinct_stages_within_hierarchy(top_level_group).to_a.pluck(:start_event_identifier, :end_event_identifier) }
it 'returns distinct stages by start and end events (using stage_event_hash_id)' do
expect(distinct_start_and_end_event_identifiers).to match_array([
%w[issue_created issue_deployed_to_production],
%w[merge_request_created merge_request_merged]
])
expect(distinct_start_and_end_event_identifiers).to match_array(
[
%w[issue_created issue_deployed_to_production],
%w[merge_request_created merge_request_merged]
])
end
end
end

View File

@ -257,11 +257,12 @@ RSpec.describe ApplicationSetting do
subject.grafana_url = ' ' + http
expect(subject.save).to be false
expect(subject.errors[:grafana_url]).to eq([
'must be a valid relative or absolute URL. ' \
'Please check your Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
])
expect(subject.errors[:grafana_url]).to eq(
[
'must be a valid relative or absolute URL. ' \
'Please check your Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
])
end
end
@ -270,11 +271,12 @@ RSpec.describe ApplicationSetting do
subject.grafana_url = javascript
expect(subject.save).to be false
expect(subject.errors[:grafana_url]).to eq([
'is blocked: Only allowed schemes are http, https. Please check your ' \
'Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
])
expect(subject.errors[:grafana_url]).to eq(
[
'is blocked: Only allowed schemes are http, https. Please check your ' \
'Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
])
end
end
end

View File

@ -5275,10 +5275,11 @@ RSpec.describe Ci::Build do
it { expect(matchers.size).to eq(2) }
it 'groups build ids' do
expect(matchers.map(&:build_ids)).to match_array([
[build_without_tags.id],
match_array([build_with_tags.id, other_build_with_tags.id])
])
expect(matchers.map(&:build_ids)).to match_array(
[
[build_without_tags.id],
match_array([build_with_tags.id, other_build_with_tags.id])
])
end
it { expect(matchers.map(&:tag_list)).to match_array([[], %w[tag1 tag2]]) }
@ -5475,10 +5476,11 @@ RSpec.describe Ci::Build do
create(:ci_job_variable, job: build, key: 'TEST_KEY', value: 'old value')
create(:ci_job_variable, job: build, key: 'OLD_KEY', value: 'i will not live for long')
new_build = build.clone(current_user: user, new_job_variables_attributes: [
{ key: 'TEST_KEY', value: 'new value' },
{ key: 'NEW_KEY', value: 'exciting new value' }
])
new_build = build.clone(current_user: user, new_job_variables_attributes:
[
{ key: 'TEST_KEY', value: 'new value' },
{ key: 'NEW_KEY', value: 'exciting new value' }
])
new_build.save!
expect(new_build.job_variables.count).to be(2)
@ -5492,9 +5494,10 @@ RSpec.describe Ci::Build do
build = create(:ci_build)
create(:ci_job_variable, job: build, key: 'TEST_KEY', value: 'old value')
new_build = build.clone(current_user: user, new_job_variables_attributes: [
{ key: 'TEST_KEY', value: 'new value' }
])
new_build = build.clone(
current_user: user,
new_job_variables_attributes: [{ key: 'TEST_KEY', value: 'new value' }]
)
new_build.save!
expect(new_build.job_variables.count).to be(1)

View File

@ -28,9 +28,10 @@ RSpec.describe Ci::BuildTrace do
it_behaves_like 'delegates methods'
it 'returns formatted trace' do
expect(subject.lines).to eq([
{ offset: 0, content: [{ text: 'the-stream' }] }
])
expect(subject.lines).to eq(
[
{ offset: 0, content: [{ text: 'the-stream' }] }
])
end
context 'with invalid UTF-8 data' do

View File

@ -41,24 +41,25 @@ RSpec.describe Ci::DailyBuildGroupReportResult do
let!(:new_pipeline) { create(:ci_pipeline) }
it 'creates or updates matching report results' do
described_class.upsert_reports([
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
group_name: 'rspec',
data: { 'coverage' => 81.0 }
},
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
group_name: 'karma',
data: { 'coverage' => 87.0 }
}
])
described_class.upsert_reports(
[
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
group_name: 'rspec',
data: { 'coverage' => 81.0 }
},
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
group_name: 'karma',
data: { 'coverage' => 87.0 }
}
])
rspec_coverage.reload

View File

@ -4531,10 +4531,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'returns accessibility report with collected data' do
expect(subject.urls.keys).to match_array([
"https://pa11y.org/",
"https://about.gitlab.com/"
])
expect(subject.urls.keys).to match_array(
[
"https://pa11y.org/",
"https://about.gitlab.com/"
])
end
context 'when builds are retried' do

View File

@ -144,6 +144,78 @@ RSpec.describe 'Update a work item' do
end
end
context 'with labels widget input' do
shared_examples 'mutation updating work item labels' do
it 'updates labels' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
end.to change { work_item.labels.count }.to(expected_labels.count)
expect(work_item.labels).to match_array(expected_labels)
expect(mutation_response['workItem']['widgets']).to include(
'labels' => {
'nodes' => match_array(expected_labels.map { |l| { 'id' => l.to_gid.to_s } })
},
'type' => 'LABELS'
)
end
end
let_it_be(:existing_label) { create(:label, project: project) }
let_it_be(:label1) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
let(:fields) do
<<~FIELDS
workItem {
widgets {
type
... on WorkItemWidgetLabels {
labels {
nodes { id }
}
}
}
}
errors
FIELDS
end
let(:input) do
{ 'labelsWidget' => { 'addLabelIds' => add_label_ids, 'removeLabelIds' => remove_label_ids } }
end
let(:add_label_ids) { [] }
let(:remove_label_ids) { [] }
before_all do
work_item.update!(labels: [existing_label])
end
context 'when only removing labels' do
let(:remove_label_ids) { [existing_label.to_gid.to_s] }
let(:expected_labels) { [] }
it_behaves_like 'mutation updating work item labels'
end
context 'when only adding labels' do
let(:add_label_ids) { [label1.to_gid.to_s, label2.to_gid.to_s] }
let(:expected_labels) { [label1, label2, existing_label] }
it_behaves_like 'mutation updating work item labels'
end
context 'when adding and removing labels' do
let(:remove_label_ids) { [existing_label.to_gid.to_s] }
let(:add_label_ids) { [label1.to_gid.to_s, label2.to_gid.to_s] }
let(:expected_labels) { [label1, label2] }
it_behaves_like 'mutation updating work item labels'
end
end
context 'with due and start date widget input' do
let(:start_date) { Date.today }
let(:due_date) { 1.week.from_now.to_date }

View File

@ -4,7 +4,7 @@
# wiki
# user
RSpec.shared_examples 'User views wiki pages' do
RSpec.shared_examples 'User views wiki pages' do |support_sorting_by_created_at = true|
include WikiHelpers
let!(:wiki_page1) do
@ -54,34 +54,37 @@ RSpec.shared_examples 'User views wiki pages' do
end
end
context 'ordered by created_at' do
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3] }
if support_sorting_by_created_at
context 'ordered by created_at' do
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3] }
before do
page.within('.wiki-sort-dropdown') do
click_button('Title')
click_button('Created date')
end
end
context 'asc' do
it 'pages are displayed in direct order' do
pages.each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end
context 'desc' do
before do
stub_feature_flags(wiki_list_pages_with_normal_repository_rpcs: false)
page.within('.wiki-sort-dropdown') do
page.find('.rspec-reverse-sort').click
click_button('Title')
click_button('Created date')
end
end
it 'pages are displayed in reversed order' do
pages.reverse_each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
context 'asc' do
it 'pages are displayed in direct order' do
pages.each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end
context 'desc' do
before do
page.within('.wiki-sort-dropdown') do
page.find('.rspec-reverse-sort').click
end
end
it 'pages are displayed in reversed order' do
pages.reverse_each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end
end

View File

@ -180,70 +180,71 @@ RSpec.shared_examples 'wiki model' do
it 'returns false' do
expect(subject.empty?).to be(false)
end
it 'only instantiates a Wiki page once' do
expect(WikiPage).to receive(:new).once.and_call_original
subject.empty?
end
end
end
end
describe '#list_pages' do
let(:wiki_pages) { subject.list_pages }
shared_examples 'wiki model #list_pages' do
let(:wiki_pages) { subject.list_pages }
before do
subject.create_page('index', 'This is an index')
subject.create_page('index2', 'This is an index2')
subject.create_page('an index3', 'This is an index3')
end
before do
subject.create_page('index', 'This is an index')
subject.create_page('index2', 'This is an index2')
subject.create_page('an index3', 'This is an index3')
end
it 'returns an array of WikiPage instances' do
expect(wiki_pages).to be_present
expect(wiki_pages).to all(be_a(WikiPage))
end
it 'returns an array of WikiPage instances' do
expect(wiki_pages).to be_present
expect(wiki_pages).to all(be_a(WikiPage))
end
it 'does not load WikiPage content by default' do
wiki_pages.each do |page|
expect(page.content).to be_empty
it 'does not load WikiPage content by default' do
wiki_pages.each do |page|
expect(page.content).to be_empty
end
end
it 'returns all pages by default' do
expect(wiki_pages.count).to eq(3)
end
context 'with limit option' do
it 'returns limited set of pages' do
expect(subject.list_pages(limit: 1).count).to eq(1)
end
end
context 'with sorting options' do
it 'returns pages sorted by title by default' do
pages = ['an index3', 'index', 'index2']
expect(subject.list_pages.map(&:title)).to eq(pages)
expect(subject.list_pages(direction: 'desc').map(&:title)).to eq(pages.reverse)
end
end
context 'with load_content option' do
let(:pages) { subject.list_pages(load_content: true) }
it 'loads WikiPage content' do
expect(pages.first.content).to eq('This is an index3')
expect(pages.second.content).to eq('This is an index')
expect(pages.third.content).to eq('This is an index2')
end
end
end
it 'returns all pages by default' do
expect(wiki_pages.count).to eq(3)
context 'list pages with legacy wiki rpcs' do
before do
stub_feature_flags(wiki_list_page_with_normal_repository_rpcs: false)
end
it_behaves_like 'wiki model #list_pages'
end
context 'with limit option' do
it 'returns limited set of pages' do
expect(subject.list_pages(limit: 1).count).to eq(1)
end
end
context 'with sorting options' do
it 'returns pages sorted by title by default' do
pages = ['an index3', 'index', 'index2']
expect(subject.list_pages.map(&:title)).to eq(pages)
expect(subject.list_pages(direction: 'desc').map(&:title)).to eq(pages.reverse)
end
it 'returns pages sorted by created_at' do
pages = ['index', 'index2', 'an index3']
expect(subject.list_pages(sort: 'created_at').map(&:title)).to eq(pages)
expect(subject.list_pages(sort: 'created_at', direction: 'desc').map(&:title)).to eq(pages.reverse)
end
end
context 'with load_content option' do
let(:pages) { subject.list_pages(load_content: true) }
it 'loads WikiPage content' do
expect(pages.first.content).to eq('This is an index3')
expect(pages.second.content).to eq('This is an index')
expect(pages.third.content).to eq('This is an index2')
end
context 'list pages with normal repository rpcs' do
it_behaves_like 'wiki model #list_pages'
end
end
@ -531,7 +532,7 @@ RSpec.shared_examples 'wiki model' do
it 'sets the correct commit message' do
subject.create_page('test page', 'some content', :markdown, 'commit message')
expect(subject.list_pages.first.page.version.message).to eq('commit message')
expect(subject.list_pages.first.version.message).to eq('commit message')
end
it 'sets the correct commit email' do

View File

@ -4,4 +4,22 @@ require 'spec_helper'
RSpec.describe 'layouts/devise' do
it_behaves_like 'a layout which reflects the application theme setting'
describe 'logo' do
it 'renders GitLab logo' do
render
expect(rendered).to have_selector('img[alt^="GitLab"]')
end
context 'with custom logo' do
let_it_be(:appearance) { create(:appearance, logo: fixture_file_upload('spec/fixtures/dk.png')) }
it 'renders custom logo' do
render
expect(rendered).to have_selector('img[data-src$="dk.png"]')
end
end
end
end