Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f60abc4315
commit
a53033814d
68 changed files with 755 additions and 315 deletions
|
@ -36,7 +36,6 @@ Layout/HashAlignment:
|
|||
- 'spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb'
|
||||
- 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
|
||||
- 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb'
|
||||
- 'spec/lib/gitlab/git/repository_spec.rb'
|
||||
- 'spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/attributes_finder_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/group/object_builder_spec.rb'
|
||||
|
@ -55,10 +54,8 @@ Layout/HashAlignment:
|
|||
- 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb'
|
||||
- 'spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb'
|
||||
- 'spec/models/ci/build_spec.rb'
|
||||
- 'spec/models/ci/pipeline_spec.rb'
|
||||
- 'spec/models/ci/processable_spec.rb'
|
||||
- 'spec/models/clusters/platforms/kubernetes_spec.rb'
|
||||
- 'spec/models/commit_status_spec.rb'
|
||||
- 'spec/models/container_registry/event_spec.rb'
|
||||
- 'spec/models/deployment_spec.rb'
|
||||
- 'spec/models/design_management/version_spec.rb'
|
||||
|
@ -72,7 +69,6 @@ Layout/HashAlignment:
|
|||
- 'spec/models/remote_mirror_spec.rb'
|
||||
- 'spec/models/repository_spec.rb'
|
||||
- 'spec/models/user_spec.rb'
|
||||
- 'spec/presenters/clusters/cluster_presenter_spec.rb'
|
||||
- 'spec/presenters/project_presenter_spec.rb'
|
||||
- 'spec/routing/project_routing_spec.rb'
|
||||
- 'spec/serializers/ci/lint/job_entity_spec.rb'
|
||||
|
@ -80,30 +76,3 @@ Layout/HashAlignment:
|
|||
- 'spec/serializers/deployment_entity_spec.rb'
|
||||
- 'spec/serializers/environment_serializer_spec.rb'
|
||||
- 'spec/serializers/merge_request_metrics_helper_spec.rb'
|
||||
- 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
|
||||
- 'spec/services/ci/create_pipeline_service/logger_spec.rb'
|
||||
- 'spec/services/ci/create_pipeline_service/tags_spec.rb'
|
||||
- 'spec/services/ci/job_artifacts/create_service_spec.rb'
|
||||
- 'spec/services/ci/retry_job_service_spec.rb'
|
||||
- 'spec/services/deployments/link_merge_requests_service_spec.rb'
|
||||
- 'spec/services/discussions/capture_diff_note_positions_service_spec.rb'
|
||||
- 'spec/services/environments/stop_service_spec.rb'
|
||||
- 'spec/services/event_create_service_spec.rb'
|
||||
- 'spec/services/groups/import_export/import_service_spec.rb'
|
||||
- 'spec/services/issuable/bulk_update_service_spec.rb'
|
||||
- 'spec/services/issues/create_service_spec.rb'
|
||||
- 'spec/services/merge_requests/build_service_spec.rb'
|
||||
- 'spec/services/merge_requests/create_service_spec.rb'
|
||||
- 'spec/services/merge_requests/update_service_spec.rb'
|
||||
- 'spec/services/metrics/dashboard/clone_dashboard_service_spec.rb'
|
||||
- 'spec/services/notes/create_service_spec.rb'
|
||||
- 'spec/services/notes/destroy_service_spec.rb'
|
||||
- 'spec/services/packages/debian/parse_debian822_service_spec.rb'
|
||||
- 'spec/services/projects/destroy_service_spec.rb'
|
||||
- 'spec/services/service_ping/submit_service_ping_service_spec.rb'
|
||||
- 'spec/services/suggestions/apply_service_spec.rb'
|
||||
- 'spec/services/work_items/widgets/description_service/update_service_spec.rb'
|
||||
- 'spec/tooling/danger/datateam_spec.rb'
|
||||
- 'spec/views/projects/tags/index.html.haml_spec.rb'
|
||||
- 'spec/workers/emails_on_push_worker_spec.rb'
|
||||
- 'spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb'
|
||||
|
|
|
@ -10,7 +10,6 @@ body.gl-dark {
|
|||
--gray-50: #303030;
|
||||
--gray-100: #404040;
|
||||
--gray-200: #525252;
|
||||
--gray-600: #bfbfbf;
|
||||
--gray-700: #dbdbdb;
|
||||
--gray-900: #fafafa;
|
||||
--green-100: #0d532a;
|
||||
|
@ -1791,9 +1790,6 @@ body.gl-dark {
|
|||
.avatar {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
.nav-sidebar li a {
|
||||
color: var(--gray-600);
|
||||
}
|
||||
body.gl-dark {
|
||||
--gl-theme-accent: #868686;
|
||||
}
|
||||
|
|
|
@ -60,12 +60,6 @@
|
|||
}
|
||||
|
||||
.nav-sidebar {
|
||||
li {
|
||||
a {
|
||||
color: var(--gray-600);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-sub-level-items.fly-out-list {
|
||||
box-shadow: none;
|
||||
border: 1px solid $border-color;
|
||||
|
@ -78,7 +72,7 @@ aside.right-sidebar:not(.right-sidebar-merge-requests) {
|
|||
}
|
||||
|
||||
body.gl-dark {
|
||||
@include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $gray-900, $white);
|
||||
@include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $white);
|
||||
|
||||
.terms {
|
||||
.logo-text {
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$theme-blue-200,
|
||||
$theme-blue-500,
|
||||
$theme-blue-700,
|
||||
$gray-900,
|
||||
$theme-blue-900,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -7,7 +7,6 @@ body {
|
|||
$gray-300,
|
||||
$gray-500,
|
||||
$gray-900,
|
||||
$gray-900,
|
||||
$white
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$theme-green-200,
|
||||
$theme-green-500,
|
||||
$theme-green-700,
|
||||
$gray-900,
|
||||
$theme-green-900,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -6,18 +6,22 @@
|
|||
$search-and-nav-links,
|
||||
$accent,
|
||||
$border-and-box-shadow,
|
||||
$sidebar-text,
|
||||
$nav-svg-color,
|
||||
$color-alternate
|
||||
$navbar-theme-color,
|
||||
$navbar-theme-contrast-color
|
||||
) {
|
||||
// Set custom properties
|
||||
|
||||
--gl-theme-accent: #{$accent};
|
||||
|
||||
$search-and-nav-links-a20: rgba($search-and-nav-links, 0.2);
|
||||
$search-and-nav-links-a30: rgba($search-and-nav-links, 0.3);
|
||||
$search-and-nav-links-a40: rgba($search-and-nav-links, 0.4);
|
||||
$search-and-nav-links-a80: rgba($search-and-nav-links, 0.8);
|
||||
|
||||
// Header
|
||||
|
||||
.navbar-gitlab {
|
||||
background-color: $nav-svg-color;
|
||||
background-color: $navbar-theme-color;
|
||||
|
||||
.navbar-collapse {
|
||||
color: $search-and-nav-links;
|
||||
|
@ -37,7 +41,7 @@
|
|||
> button {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: rgba($search-and-nav-links, 0.2);
|
||||
background-color: $search-and-nav-links-a20;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,13 +49,13 @@
|
|||
&.dropdown.show {
|
||||
> a,
|
||||
> button {
|
||||
color: $nav-svg-color;
|
||||
background-color: $color-alternate;
|
||||
color: $navbar-theme-color;
|
||||
background-color: $navbar-theme-contrast-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.line-separator {
|
||||
border-left: 1px solid rgba($search-and-nav-links, 0.2);
|
||||
border-left: 1px solid $search-and-nav-links-a20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,12 +69,12 @@
|
|||
color: $search-and-nav-links;
|
||||
|
||||
&.header-search-new {
|
||||
color: $sidebar-text;
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
> a {
|
||||
.notification-dot {
|
||||
border: 2px solid $nav-svg-color;
|
||||
border: 2px solid $navbar-theme-color;
|
||||
}
|
||||
|
||||
&.header-help-dropdown-toggle {
|
||||
|
@ -88,7 +92,7 @@
|
|||
&:hover,
|
||||
&:focus {
|
||||
@include media-breakpoint-up(sm) {
|
||||
background-color: rgba($search-and-nav-links, 0.2);
|
||||
background-color: $search-and-nav-links-a20;
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@ -97,7 +101,7 @@
|
|||
|
||||
.notification-dot {
|
||||
will-change: border-color, background-color;
|
||||
border-color: adjust-color($nav-svg-color, $red: 33, $green: 33, $blue: 33);
|
||||
border-color: adjust-color($navbar-theme-color, $red: 33, $green: 33, $blue: 33);
|
||||
}
|
||||
|
||||
&.header-help-dropdown-toggle .notification-dot {
|
||||
|
@ -108,12 +112,12 @@
|
|||
|
||||
&.active > a,
|
||||
&.dropdown.show > a {
|
||||
color: $nav-svg-color;
|
||||
background-color: $color-alternate;
|
||||
color: $navbar-theme-color;
|
||||
background-color: $navbar-theme-contrast-color;
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
fill: $nav-svg-color;
|
||||
fill: $navbar-theme-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +127,7 @@
|
|||
|
||||
&.header-help-dropdown-toggle {
|
||||
.notification-dot {
|
||||
background-color: $nav-svg-color;
|
||||
background-color: $navbar-theme-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +135,7 @@
|
|||
.impersonated-user,
|
||||
.impersonated-user:hover {
|
||||
svg {
|
||||
fill: $nav-svg-color;
|
||||
fill: $navbar-theme-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,30 +146,30 @@
|
|||
> a {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: rgba($search-and-nav-links, 0.2);
|
||||
background-color: $search-and-nav-links-a20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-search {
|
||||
background-color: rgba($search-and-nav-links, 0.2) !important;
|
||||
background-color: $search-and-nav-links-a20 !important;
|
||||
border-radius: $border-radius-default;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($search-and-nav-links, 0.3) !important;
|
||||
background-color: $search-and-nav-links-a30 !important;
|
||||
}
|
||||
|
||||
svg.gl-search-box-by-type-search-icon {
|
||||
color: rgba($search-and-nav-links, 0.8);
|
||||
color: $search-and-nav-links-a80;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: transparent;
|
||||
color: rgba($search-and-nav-links, 0.8);
|
||||
box-shadow: inset 0 0 0 1px rgba($search-and-nav-links, 0.4);
|
||||
color: $search-and-nav-links-a80;
|
||||
box-shadow: inset 0 0 0 1px $search-and-nav-links-a40;
|
||||
|
||||
&::placeholder {
|
||||
color: rgba($search-and-nav-links, 0.8);
|
||||
color: $search-and-nav-links-a80;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
|
@ -178,27 +182,27 @@
|
|||
|
||||
.keyboard-shortcut-helper {
|
||||
color: $search-and-nav-links;
|
||||
background-color: rgba($search-and-nav-links, 0.2);
|
||||
background-color: $search-and-nav-links-a20;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
form {
|
||||
background-color: rgba($search-and-nav-links, 0.2);
|
||||
background-color: $search-and-nav-links-a20;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($search-and-nav-links, 0.3);
|
||||
background-color: $search-and-nav-links-a30;
|
||||
}
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: rgba($search-and-nav-links, 0.8);
|
||||
color: $search-and-nav-links-a80;
|
||||
}
|
||||
|
||||
.search-input-wrap {
|
||||
.search-icon,
|
||||
.clear-icon {
|
||||
fill: rgba($search-and-nav-links, 0.8);
|
||||
fill: $search-and-nav-links-a80;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +213,7 @@
|
|||
|
||||
.search-input-wrap {
|
||||
.search-icon {
|
||||
fill: rgba($search-and-nav-links, 0.8);
|
||||
fill: $search-and-nav-links-a80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +221,7 @@
|
|||
|
||||
// Sidebar
|
||||
.nav-sidebar li.active > a {
|
||||
color: $sidebar-text;
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
.nav-sidebar {
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$indigo-200,
|
||||
$indigo-500,
|
||||
$indigo-700,
|
||||
$gray-900,
|
||||
$indigo-900,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$theme-light-blue-200,
|
||||
$theme-light-blue-500,
|
||||
$theme-light-blue-500,
|
||||
$gray-900,
|
||||
$theme-light-blue-700,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$gray-500,
|
||||
$gray-700,
|
||||
$gray-500,
|
||||
$gray-900,
|
||||
$gray-50,
|
||||
$gray-500
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$theme-green-200,
|
||||
$theme-green-500,
|
||||
$theme-green-500,
|
||||
$gray-900,
|
||||
$theme-light-green-700,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$indigo-200,
|
||||
$indigo-500,
|
||||
$indigo-500,
|
||||
$gray-900,
|
||||
$indigo-700,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$theme-light-red-200,
|
||||
$theme-light-red-500,
|
||||
$theme-light-red-500,
|
||||
$gray-900,
|
||||
$theme-light-red-700,
|
||||
$white
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ body {
|
|||
$theme-red-200,
|
||||
$theme-red-500,
|
||||
$theme-red-700,
|
||||
$gray-900,
|
||||
$theme-red-900,
|
||||
$white
|
||||
);
|
||||
|
|
6
app/components/pajamas/badge_component.html.haml
Normal file
6
app/components/pajamas/badge_component.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
- if link?
|
||||
%a{ href: @href, **html_options }><
|
||||
= badge_content
|
||||
- else
|
||||
%span{ **html_options }><
|
||||
= badge_content
|
72
app/components/pajamas/badge_component.rb
Normal file
72
app/components/pajamas/badge_component.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Pajamas
|
||||
class BadgeComponent < Pajamas::Component
|
||||
def initialize(
|
||||
text = nil,
|
||||
icon: nil,
|
||||
icon_classes: [],
|
||||
icon_only: false,
|
||||
href: nil,
|
||||
size: :md,
|
||||
variant: :muted,
|
||||
**html_options
|
||||
)
|
||||
@text = text.presence
|
||||
@icon = icon.to_s.presence
|
||||
@icon_classes = Array.wrap(icon_classes)
|
||||
@icon_only = @icon && icon_only
|
||||
@href = href.presence
|
||||
@size = filter_attribute(size.to_sym, SIZE_OPTIONS, default: :md)
|
||||
@variant = filter_attribute(variant.to_sym, VARIANT_OPTIONS, default: :muted)
|
||||
@html_options = html_options
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
SIZE_OPTIONS = [:sm, :md, :lg].freeze
|
||||
VARIANT_OPTIONS = [:muted, :neutral, :info, :success, :warning, :danger].freeze
|
||||
|
||||
delegate :sprite_icon, to: :helpers
|
||||
|
||||
def badge_classes
|
||||
["gl-badge", "badge", "badge-pill", "badge-#{@variant}", @size.to_s]
|
||||
end
|
||||
|
||||
def icon_classes
|
||||
classes = %w[gl-icon gl-badge-icon] + @icon_classes
|
||||
classes.push("gl-mr-2") unless icon_only?
|
||||
classes.join(" ")
|
||||
end
|
||||
|
||||
def icon_only?
|
||||
@icon_only
|
||||
end
|
||||
|
||||
def link?
|
||||
@href.present?
|
||||
end
|
||||
|
||||
# Determines the rendered text content.
|
||||
# The content slot takes presedence over the text param.
|
||||
def text
|
||||
content || @text
|
||||
end
|
||||
|
||||
def badge_content
|
||||
if icon_only?
|
||||
sprite_icon(@icon, css_class: icon_classes)
|
||||
elsif @icon.present?
|
||||
sprite_icon(@icon, css_class: icon_classes) + text
|
||||
else
|
||||
text
|
||||
end
|
||||
end
|
||||
|
||||
def html_options
|
||||
options = format_options(options: @html_options, css_classes: badge_classes)
|
||||
options.merge!({ aria: { label: text }, role: "img" }) if icon_only?
|
||||
options
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,25 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BadgesHelper
|
||||
VARIANT_CLASSES = {
|
||||
muted: "badge-muted",
|
||||
neutral: "badge-neutral",
|
||||
info: "badge-info",
|
||||
success: "badge-success",
|
||||
warning: "badge-warning",
|
||||
danger: "badge-danger"
|
||||
}.tap { |hash| hash.default = hash.fetch(:muted) }.freeze
|
||||
|
||||
SIZE_CLASSES = {
|
||||
sm: "sm",
|
||||
md: "md",
|
||||
lg: "lg"
|
||||
}.tap { |hash| hash.default = hash.fetch(:md) }.freeze
|
||||
|
||||
GL_BADGE_CLASSES = %w[gl-badge badge badge-pill].freeze
|
||||
|
||||
GL_ICON_CLASSES = %w[gl-icon gl-badge-icon].freeze
|
||||
|
||||
# Creates a GitLab UI badge.
|
||||
#
|
||||
# Examples:
|
||||
|
@ -53,47 +34,16 @@ module BadgesHelper
|
|||
#
|
||||
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-badge--default.
|
||||
def gl_badge_tag(*args, &block)
|
||||
# Merge the options and html_options hashes if both are present,
|
||||
# because the badge component wants a flat list of keyword args.
|
||||
args.compact!
|
||||
hashes, params = args.partition { |a| a.is_a? Hash }
|
||||
options_hash = hashes.reduce({}, :merge)
|
||||
|
||||
if block
|
||||
build_gl_badge_tag(capture(&block), *args)
|
||||
render Pajamas::BadgeComponent.new(**options_hash), &block
|
||||
else
|
||||
build_gl_badge_tag(*args)
|
||||
render Pajamas::BadgeComponent.new(*params, **options_hash)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_gl_badge_tag(content, options = nil, html_options = nil)
|
||||
options ||= {}
|
||||
html_options ||= {}
|
||||
|
||||
icon_only = options[:icon_only]
|
||||
variant_class = VARIANT_CLASSES[options.fetch(:variant, :muted)]
|
||||
size_class = SIZE_CLASSES[options.fetch(:size, :md)]
|
||||
icon_classes = GL_ICON_CLASSES.dup << options.fetch(:icon_classes, nil)
|
||||
|
||||
html_options = html_options.merge(
|
||||
class: [
|
||||
*GL_BADGE_CLASSES,
|
||||
variant_class,
|
||||
size_class,
|
||||
*html_options[:class]
|
||||
]
|
||||
)
|
||||
|
||||
if icon_only
|
||||
html_options['aria-label'] = content
|
||||
html_options['role'] = 'img'
|
||||
end
|
||||
|
||||
if options[:icon]
|
||||
icon_classes << "gl-mr-2" unless icon_only
|
||||
icon = sprite_icon(options[:icon], css_class: icon_classes.join(' '))
|
||||
|
||||
content = icon_only ? icon : icon + content
|
||||
end
|
||||
|
||||
tag = html_options[:href].nil? ? :span : :a
|
||||
|
||||
content_tag(tag, content, html_options)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,5 +21,9 @@ module Users
|
|||
network: network
|
||||
).order(credit_card_validated_at: :desc).includes(:user)
|
||||
end
|
||||
|
||||
def similar_holder_names_count
|
||||
self.class.where('lower(holder_name) = :value', value: holder_name.downcase).count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,17 +15,13 @@ options:
|
|||
events:
|
||||
- i_package_composer_user
|
||||
- i_package_conan_user
|
||||
- i_package_container_user
|
||||
- i_package_debian_user
|
||||
- i_package_generic_user
|
||||
- i_package_golang_user
|
||||
- i_package_helm_user
|
||||
- i_package_maven_user
|
||||
- i_package_npm_user
|
||||
- i_package_nuget_user
|
||||
- i_package_pypi_user
|
||||
- i_package_rubygems_user
|
||||
- i_package_tag_user
|
||||
- i_package_terraform_module_user
|
||||
distribution:
|
||||
- ee
|
||||
|
|
|
@ -15,17 +15,13 @@ options:
|
|||
events:
|
||||
- i_package_composer_user
|
||||
- i_package_conan_user
|
||||
- i_package_container_user
|
||||
- i_package_debian_user
|
||||
- i_package_generic_user
|
||||
- i_package_golang_user
|
||||
- i_package_helm_user
|
||||
- i_package_maven_user
|
||||
- i_package_npm_user
|
||||
- i_package_nuget_user
|
||||
- i_package_pypi_user
|
||||
- i_package_rubygems_user
|
||||
- i_package_tag_user
|
||||
- i_package_terraform_module_user
|
||||
distribution:
|
||||
- ee
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ReplaceIndexOnCreditCardValidations < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
OLD_INDEX_NAME = 'index_user_credit_card_validations_meta_data_full_match'
|
||||
NEW_INDEX_NAME = 'index_user_credit_card_validations_meta_data_full_match_lower'
|
||||
OLD_FIELDS = [:holder_name, :expiration_date, :last_digits, :credit_card_validated_at]
|
||||
NEW_FIELDS = 'lower(holder_name), expiration_date, last_digits, credit_card_validated_at'
|
||||
|
||||
def up
|
||||
add_concurrent_index :user_credit_card_validations, NEW_FIELDS, name: NEW_INDEX_NAME
|
||||
remove_concurrent_index :user_credit_card_validations, OLD_FIELDS, name: OLD_INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :user_credit_card_validations, OLD_FIELDS, name: OLD_INDEX_NAME
|
||||
remove_concurrent_index :user_credit_card_validations, NEW_FIELDS, name: NEW_INDEX_NAME
|
||||
end
|
||||
end
|
1
db/schema_migrations/20220829183356
Normal file
1
db/schema_migrations/20220829183356
Normal file
|
@ -0,0 +1 @@
|
|||
4d8be5080046eff9c3736cd2494c02b2d2cb1eeea2753479617cb344bc5b1cbb
|
|
@ -30341,7 +30341,7 @@ CREATE UNIQUE INDEX index_user_canonical_emails_on_user_id ON user_canonical_ema
|
|||
|
||||
CREATE UNIQUE INDEX index_user_canonical_emails_on_user_id_and_canonical_email ON user_canonical_emails USING btree (user_id, canonical_email);
|
||||
|
||||
CREATE INDEX index_user_credit_card_validations_meta_data_full_match ON user_credit_card_validations USING btree (holder_name, expiration_date, last_digits, credit_card_validated_at);
|
||||
CREATE INDEX index_user_credit_card_validations_meta_data_full_match_lower ON user_credit_card_validations USING btree (lower(holder_name), expiration_date, last_digits, credit_card_validated_at);
|
||||
|
||||
CREATE INDEX index_user_credit_card_validations_meta_data_partial_match ON user_credit_card_validations USING btree (expiration_date, last_digits, network, credit_card_validated_at);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ required number of seconds.
|
|||
"required" : [
|
||||
"project",
|
||||
"user",
|
||||
"credit_card",
|
||||
"pipeline",
|
||||
"builds",
|
||||
"total_builds_count",
|
||||
|
@ -85,6 +86,17 @@ required number of seconds.
|
|||
"sign_in_count": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"credit_card": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"similar_cards_count",
|
||||
"similar_holder_names_count"
|
||||
],
|
||||
"properties": {
|
||||
"similar_cards_count": { "type": "integer" },
|
||||
"similar_holder_names_count": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"pipeline": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -33,6 +33,7 @@ GET /projects/:id/approvals
|
|||
{
|
||||
"approvals_before_merge": 2,
|
||||
"reset_approvals_on_push": true,
|
||||
"selective_code_owner_removals": false,
|
||||
"disable_overriding_approvers_per_merge_request": false,
|
||||
"merge_requests_author_approval": true,
|
||||
"merge_requests_disable_committers_approval": false,
|
||||
|
@ -57,16 +58,18 @@ POST /projects/:id/approvals
|
|||
| ------------------------------------------------ | ------- | -------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
|
||||
| `approvals_before_merge` | integer | no | How many approvals are required before an MR can be merged. Deprecated in 12.0 in favor of Approval Rules API. |
|
||||
| `reset_approvals_on_push` | boolean | no | Reset approvals on a new push |
|
||||
| `disable_overriding_approvers_per_merge_request` | boolean | no | Allow or prevent overriding approvers per MR |
|
||||
| `merge_requests_author_approval` | boolean | no | Allow or prevent authors from self approving merge requests; `true` means authors can self approve |
|
||||
| `merge_requests_disable_committers_approval` | boolean | no | Allow or prevent committers from self approving merge requests |
|
||||
| `require_password_to_approve` | boolean | no | Require approver to enter a password to authenticate before adding the approval |
|
||||
| `reset_approvals_on_push` | boolean | no | Reset approvals on a new push. |
|
||||
| `selective_code_owner_removals` | boolean | no | Reset approvals from Code Owners if their files changed. Can be enabled only if `reset_approvals_on_push` is disabled. |
|
||||
|
||||
```json
|
||||
{
|
||||
"approvals_before_merge": 2,
|
||||
"reset_approvals_on_push": true,
|
||||
"selective_code_owner_removals": false,
|
||||
"disable_overriding_approvers_per_merge_request": false,
|
||||
"merge_requests_author_approval": false,
|
||||
"merge_requests_disable_committers_approval": false,
|
||||
|
|
|
@ -159,21 +159,32 @@ and artifacts, will share the same value. We want to add the `partition_id`
|
|||
column into all 6 problematic tables because we can avoid backfilling this data
|
||||
when we decide it is time to start partitioning them.
|
||||
|
||||
We want to partition CI/CD data iteratively, so we will start with the
|
||||
pipelines table, and create at least one, but likely two, partitions. The
|
||||
pipelines table will be partitioned using the `LIST` partitioning strategy. It
|
||||
is possible that, after some time, `p_ci_pipelines` will store data in two
|
||||
partitions with IDs of `100` and `101`. Then we will try partitioning
|
||||
`ci_builds`. Therefore we might want to use `RANGE` partitioning in
|
||||
`p_ci_builds` with IDs `100` and `101`, because builds for the two logical
|
||||
partitions used will still be stored in a single table.
|
||||
We want to partition CI/CD data iteratively. We plan to start with the
|
||||
`ci_builds_metadata` table, because this is the fastest growing table in the CI
|
||||
database and want to contain this rapid growth. This table has also the most
|
||||
simple access patterns - a row from it is being read when a build is exposed to
|
||||
a runner, and other access patterns are relatively simple too. Starting with
|
||||
`p_ci_builds_metadata` will allow us to achieve tangible and quantifiable
|
||||
results earlier, and will become a new pattern that makes partitioning the
|
||||
largest table possible. We will partition builds metadata using the `LIST`
|
||||
partitioning strategy.
|
||||
|
||||
Once we have many partitions attached to `p_ci_builds_metadata`, with many
|
||||
`partition_ids` we will choose another CI table to partition next. In that case
|
||||
we might want to use `RANGE` partitioning in for that next table because
|
||||
`p_ci_builds_metadata` will already have many physical partitions, and
|
||||
therefore many logical `partition_ids` will be used at that time. For example,
|
||||
if we choose `ci_builds` as the next partitioning candidate, after having
|
||||
partitioned `p_ci_builds_metadata`, it will have many different values stored
|
||||
in `ci_builds.partition_id`. Using `RANGE` partitioning in that case might be
|
||||
easier.
|
||||
|
||||
Physical partitioning and logical partitioning will be separated, and a
|
||||
strategy will be determined when we implement partitioning for the respective
|
||||
database tables. Using `RANGE` partitioning works similarly to using `LIST`
|
||||
partitioning in database tables other than `ci_pipelines`, but because we can
|
||||
guarantee continuity of `partition_id` values, using `RANGE` partitioning might
|
||||
be a better strategy.
|
||||
strategy will be determined when we implement physical partitioning for the
|
||||
respective database tables. Using `RANGE` partitioning works similarly to using
|
||||
`LIST` partitioning in database tables, but because we can guarantee continuity
|
||||
of `partition_id` values, using `RANGE` partitioning might be a better
|
||||
strategy.
|
||||
|
||||
## Why do we want to use explicit logical partition ids?
|
||||
|
||||
|
@ -335,9 +346,116 @@ scope block takes an argument). Preloading instance dependent scopes is not
|
|||
supported.
|
||||
```
|
||||
|
||||
We also need to build a proof of concept for removing data on the PostgreSQL
|
||||
side (using foreign keys with `ON DELETE CASCADE`) and removing data through
|
||||
Rails associations, as this might be an important area of uncertainty.
|
||||
### Foreign keys
|
||||
|
||||
Foreign keys must reference columns that either are a primary key or form a
|
||||
unique constraint. We can define them using these strategies:
|
||||
|
||||
#### Between routing tables sharing partition ID
|
||||
|
||||
For relations that are part of the same pipeline hierarchy it is possible to
|
||||
share the `partition_id` column to define the foreign key constraint:
|
||||
|
||||
```plaintext
|
||||
p_ci_pipelines:
|
||||
- id
|
||||
- partition_id
|
||||
|
||||
p_ci_builds:
|
||||
- id
|
||||
- partition_id
|
||||
- pipeline_id
|
||||
```
|
||||
|
||||
In this case, `p_ci_builds.partition_id` indicates the partition for the build
|
||||
and also for the pipeline. We can add a FK on the routing table using:
|
||||
|
||||
```sql
|
||||
ALTER TABLE ONLY p_ci_builds
|
||||
ADD CONSTRAINT fk_on_pipeline_and_partition
|
||||
FOREIGN KEY (pipeline_id, partition_id)
|
||||
REFERENCES p_ci_pipelines(id, partition_id) ON DELETE CASCADE;
|
||||
```
|
||||
|
||||
#### Between routing tables with different partition IDs
|
||||
|
||||
It's not possible to reuse the `partition_id` for all relations in the CI domain,
|
||||
so in this case we'll need to store the value as a different attribute. For
|
||||
example, when canceling redundant pipelines we store on the old pipeline row
|
||||
the ID of the new pipeline that cancelled it as `auto_canceled_by_id`:
|
||||
|
||||
```plaintext
|
||||
p_ci_pipelines:
|
||||
- id
|
||||
- partition_id
|
||||
- auto_canceled_by_id
|
||||
- auto_canceled_by_partition_id
|
||||
```
|
||||
|
||||
In this case we can't ensure that the canceling pipeline is part of the same
|
||||
hierarchy as the canceled pipelines, so we need an extra attribute to store its
|
||||
partition, `auto_canceled_by_partition_id`, and the FK becomes:
|
||||
|
||||
```sql
|
||||
ALTER TABLE ONLY p_ci_pipelines
|
||||
ADD CONSTRAINT fk_cancel_redundant_pieplines
|
||||
FOREIGN KEY (auto_canceled_by_id, auto_canceled_by_partition_id)
|
||||
REFERENCES p_ci_pipelines(id, partition_id) ON DELETE SET NULL;
|
||||
```
|
||||
|
||||
#### Between routing tables and regular tables
|
||||
|
||||
Not all of the tables in the CI domain will be partitioned, so we'll have routing
|
||||
tables that will reference non-partitioned tables, for example we reference
|
||||
`external_pull_requests` from `ci_pipelines`:
|
||||
|
||||
```sql
|
||||
FOREIGN KEY (external_pull_request_id)
|
||||
REFERENCES external_pull_requests(id)
|
||||
ON DELETE SET NULL
|
||||
```
|
||||
|
||||
In this case we only need to move the FK definition from the partition level
|
||||
to the routing table so that new pipeline partitions may use it:
|
||||
|
||||
```sql
|
||||
ALTER TABLE p_ci_pipelines
|
||||
ADD CONSTRAINT fk_external_request
|
||||
FOREIGN KEY (external_pull_request_id)
|
||||
REFERENCES external_pull_requests(id) ON DELETE SET NULL;
|
||||
```
|
||||
|
||||
#### Between regular tables and routing tables
|
||||
|
||||
Most of the tables from the CI domain reference at least one table that will be
|
||||
turned into a routing tables, for example `ci_pipeline_messages` references
|
||||
`ci_pipelines`. These definitions will need to be updated to use the routing
|
||||
tables and for this they will need a `partition_id` column:
|
||||
|
||||
```plaintext
|
||||
p_ci_pipelines:
|
||||
- id
|
||||
- partition_id
|
||||
|
||||
ci_pipeline_messages:
|
||||
- id
|
||||
- pipeline_id
|
||||
- pipeline_partition_id
|
||||
```
|
||||
|
||||
The foreign key can be defined by using:
|
||||
|
||||
```sql
|
||||
ALTER TABLE ci_pipeline_messages ADD CONSTRAINT fk_pipeline_partitioned
|
||||
FOREIGN KEY (pipeline_id, pipeline_partition_id)
|
||||
REFERENCES p_ci_pipelines(id, partition_id) ON DELETE CASCADE;
|
||||
```
|
||||
|
||||
The old FK definition will need to be removed, otherwise new inserts in the
|
||||
`ci_pipeline_messages` with pipeline IDs from non-zero partition will fail with
|
||||
reference errors.
|
||||
|
||||
### Indexes
|
||||
|
||||
We [learned](https://gitlab.com/gitlab-org/gitlab/-/issues/360148) that `PostgreSQL`
|
||||
does not allow to create a single index (unique or otherwise) across all partitions of a table.
|
||||
|
|
|
@ -14,7 +14,10 @@ module Banzai
|
|||
return doc unless settings.kroki_enabled
|
||||
|
||||
diagram_selectors = ::Gitlab::Kroki.formats(settings)
|
||||
.map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
|
||||
.map do |diagram_type|
|
||||
%(pre[lang="#{diagram_type}"] > code,
|
||||
pre > code[lang="#{diagram_type}"])
|
||||
end
|
||||
.join(', ')
|
||||
|
||||
xpath = Gitlab::Utils::Nokogiri.css_to_xpath(diagram_selectors)
|
||||
|
@ -22,7 +25,7 @@ module Banzai
|
|||
|
||||
diagram_format = "svg"
|
||||
doc.xpath(xpath).each do |node|
|
||||
diagram_type = node.parent['lang']
|
||||
diagram_type = node.parent['lang'] || node['lang']
|
||||
diagram_src = node.content
|
||||
image_src = create_image_src(diagram_type, diagram_format, diagram_src)
|
||||
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />))
|
||||
|
@ -33,8 +36,8 @@ module Banzai
|
|||
img_tag.set_attribute('hidden', '') if lazy_load
|
||||
img_tag.set_attribute('class', 'js-render-kroki')
|
||||
|
||||
img_tag.set_attribute('data-diagram', node.parent['lang'])
|
||||
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
|
||||
img_tag.set_attribute('data-diagram', diagram_type)
|
||||
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
|
||||
|
||||
node.parent.replace(img_tag)
|
||||
end
|
||||
|
|
|
@ -9,8 +9,8 @@ module Banzai
|
|||
Filter::AssetProxyFilter,
|
||||
Filter::ExternalLinkFilter,
|
||||
Filter::PlantumlFilter,
|
||||
Filter::SyntaxHighlightFilter,
|
||||
Filter::KrokiFilter
|
||||
Filter::KrokiFilter,
|
||||
Filter::SyntaxHighlightFilter
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -97,6 +97,10 @@ module Gitlab
|
|||
last_sign_in_ip: current_user.last_sign_in_ip,
|
||||
sign_in_count: current_user.sign_in_count
|
||||
},
|
||||
credit_card: {
|
||||
similar_cards_count: current_user.credit_card_validation&.similar_records&.count.to_i,
|
||||
similar_holder_names_count: current_user.credit_card_validation&.similar_holder_names_count.to_i
|
||||
},
|
||||
pipeline: {
|
||||
sha: pipeline.sha,
|
||||
ref: pipeline.ref,
|
||||
|
|
|
@ -31,7 +31,6 @@ module Gitlab
|
|||
issues_edit
|
||||
pipeline_authoring
|
||||
quickactions
|
||||
user_packages
|
||||
].freeze
|
||||
|
||||
CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS = %w[
|
||||
|
@ -49,6 +48,7 @@ module Gitlab
|
|||
source_code
|
||||
terraform
|
||||
testing
|
||||
user_packages
|
||||
work_items
|
||||
].freeze
|
||||
|
||||
|
|
|
@ -11894,15 +11894,6 @@ msgstr ""
|
|||
msgid "DastProfiles|Delete profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Do you want to discard this scanner profile?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Do you want to discard this site profile?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Do you want to discard your changes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Edit profile"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27282,6 +27273,12 @@ msgstr ""
|
|||
msgid "OnDemandScans|Description (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Discard changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27303,6 +27300,9 @@ msgstr ""
|
|||
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Keep editing"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Manage scanner profiles"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27426,6 +27426,9 @@ msgstr ""
|
|||
msgid "OnDemandScans|View results"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|You have unsaved changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ module RuboCop
|
|||
|
||||
USAGE_DATA_COUNTERS_EVENTS_YAML_GLOBS = [
|
||||
File.expand_path("../../../config/metrics/aggregates/*.yml", __dir__),
|
||||
File.expand_path("../../../lib/gitlab/usage_data_counters/known_events/*.yml", __dir__)
|
||||
File.expand_path("../../../lib/gitlab/usage_data_counters/known_events/*.yml", __dir__),
|
||||
File.expand_path("../../../ee/lib/ee/gitlab/usage_data_counters/known_events/*.yml", __dir__)
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
|
|
148
spec/components/pajamas/badge_component_spec.rb
Normal file
148
spec/components/pajamas/badge_component_spec.rb
Normal file
|
@ -0,0 +1,148 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Pajamas::BadgeComponent, type: :component do
|
||||
let(:text) { "Hello" }
|
||||
let(:options) { {} }
|
||||
let(:html_options) { {} }
|
||||
|
||||
before do
|
||||
render_inline(described_class.new(text, **options, **html_options))
|
||||
end
|
||||
|
||||
describe "text param" do
|
||||
it "is shown inside the badge" do
|
||||
expect(page).to have_css ".gl-badge", text: text
|
||||
end
|
||||
end
|
||||
|
||||
describe "content slot" do
|
||||
it "can be used instead of the text param" do
|
||||
render_inline(described_class.new) do
|
||||
"Slot content"
|
||||
end
|
||||
expect(page).to have_css ".gl-badge", text: "Slot content"
|
||||
end
|
||||
|
||||
it "takes presendence over the text param" do
|
||||
render_inline(described_class.new(text)) do
|
||||
"Slot wins."
|
||||
end
|
||||
expect(page).to have_css ".gl-badge", text: "Slot wins."
|
||||
end
|
||||
end
|
||||
|
||||
describe "options" do
|
||||
describe "icon" do
|
||||
let(:options) { { icon: :tanuki } }
|
||||
|
||||
it "adds the correct icon and margin" do
|
||||
expect(page).to have_css ".gl-icon.gl-badge-icon.gl-mr-2[data-testid='tanuki-icon']"
|
||||
end
|
||||
end
|
||||
|
||||
describe "icon_classes" do
|
||||
let(:options) { { icon: :tanuki, icon_classes: icon_classes } }
|
||||
|
||||
context "as string" do
|
||||
let(:icon_classes) { "js-special-badge-icon js-extra-special" }
|
||||
|
||||
it "combines custom classes and component classes" do
|
||||
expect(page).to have_css \
|
||||
".gl-icon.gl-badge-icon.gl-mr-2.js-special-badge-icon.js-extra-special[data-testid='tanuki-icon']"
|
||||
end
|
||||
end
|
||||
|
||||
context "as array" do
|
||||
let(:icon_classes) { %w[js-special-badge-icon js-extra-special] }
|
||||
|
||||
it "combines custom classes and component classes" do
|
||||
expect(page).to have_css \
|
||||
".gl-icon.gl-badge-icon.gl-mr-2.js-special-badge-icon.js-extra-special[data-testid='tanuki-icon']"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "icon_only" do
|
||||
let(:options) { { icon: :tanuki, icon_only: true } }
|
||||
|
||||
it "adds no extra margin to the icon" do
|
||||
expect(page).not_to have_css ".gl-icon.gl-mr-2"
|
||||
end
|
||||
|
||||
it "adds the text as ARIA label" do
|
||||
expect(page).to have_css ".gl-badge[aria-label='#{text}'][role='img']"
|
||||
end
|
||||
end
|
||||
|
||||
describe "href" do
|
||||
let(:options) { { href: "/foo" } }
|
||||
|
||||
it "makes the a badge a link" do
|
||||
expect(page).to have_link text, class: "gl-badge", href: "/foo"
|
||||
end
|
||||
end
|
||||
|
||||
describe "size" do
|
||||
where(:size) { [:sm, :md, :lg] }
|
||||
|
||||
with_them do
|
||||
let(:options) { { size: size } }
|
||||
|
||||
it "adds size class" do
|
||||
expect(page).to have_css ".gl-badge.#{size}"
|
||||
end
|
||||
end
|
||||
|
||||
context "with unknown size" do
|
||||
let(:options) { { size: :xxl } }
|
||||
|
||||
it "adds the default size class" do
|
||||
expect(page).to have_css ".gl-badge.md"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "variant" do
|
||||
where(:variant) { [:muted, :neutral, :info, :success, :warning, :danger] }
|
||||
|
||||
with_them do
|
||||
let(:options) { { variant: variant } }
|
||||
|
||||
it "adds variant class" do
|
||||
expect(page).to have_css ".gl-badge.badge-#{variant}"
|
||||
end
|
||||
end
|
||||
|
||||
context "with unknown variant" do
|
||||
let(:options) { { variant: :foo } }
|
||||
|
||||
it "adds the default variant class" do
|
||||
expect(page).to have_css ".gl-badge.badge-muted"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "HTML options" do
|
||||
let(:html_options) { { id: "badge-33", data: { foo: "bar" } } }
|
||||
|
||||
it "get added as HTML attributes" do
|
||||
expect(page).to have_css ".gl-badge#badge-33[data-foo='bar']"
|
||||
end
|
||||
|
||||
it "can be combined with component options in no particular order" do
|
||||
render_inline(described_class.new(text, id: "badge-34", variant: :success, data: { foo: "baz" }, size: :sm))
|
||||
expect(page).to have_css ".gl-badge.badge-success.sm#badge-34[data-foo='baz']"
|
||||
end
|
||||
|
||||
context "with custom CSS classes" do
|
||||
let(:html_options) { { id: "badge-35", class: "js-special-badge" } }
|
||||
|
||||
it "combines custom classes and component classes" do
|
||||
expect(page).to have_css ".gl-badge.js-special-badge#badge-35"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
61
spec/components/previews/pajamas/badge_component_preview.rb
Normal file
61
spec/components/previews/pajamas/badge_component_preview.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Pajamas
|
||||
class BadgeComponentPreview < ViewComponent::Preview
|
||||
# Badge
|
||||
# ---
|
||||
#
|
||||
# See its design reference [here](https://design.gitlab.com/components/badge).
|
||||
#
|
||||
# @param icon select [~, star-o, issue-closed, tanuki]
|
||||
# @param icon_only toggle
|
||||
# @param href url
|
||||
# @param size select [sm, md, lg]
|
||||
# @param text text
|
||||
# @param variant select [muted, neutral, info, success, warning, danger]
|
||||
def default(icon: :tanuki, icon_only: false, href: nil, size: :md, text: "Tanuki", variant: :muted)
|
||||
render Pajamas::BadgeComponent.new(
|
||||
text,
|
||||
icon: icon,
|
||||
icon_only: icon_only,
|
||||
href: href,
|
||||
size: size,
|
||||
variant: variant
|
||||
)
|
||||
end
|
||||
|
||||
# Using the content slot
|
||||
# ---
|
||||
#
|
||||
# Use the content slot instead of the `text` param when things get more complicated than a plain string.
|
||||
# All other options (`icon`, `size`, etc.) work as usual.
|
||||
def slot
|
||||
render Pajamas::BadgeComponent.new(size: :lg, variant: :info) do
|
||||
"!ereht olleh".reverse.capitalize
|
||||
end
|
||||
end
|
||||
|
||||
# Custom HTML attributes and icon classes
|
||||
# ---
|
||||
#
|
||||
# Any extra options passed into the component are treated as HTML attributes.
|
||||
# This makes adding data or an id easy.
|
||||
#
|
||||
# CSS classes provided with the `class:` option are combined with the component classes.
|
||||
#
|
||||
# It is also possible to set custom `icon_classes:`.
|
||||
#
|
||||
# The order in which you provide these keywords doesn't matter.
|
||||
def custom
|
||||
render Pajamas::BadgeComponent.new(
|
||||
"I'm special.",
|
||||
class: "js-special-badge",
|
||||
data: { count: 1 },
|
||||
icon: :tanuki,
|
||||
icon_classes: ["js-special-badge-icon"],
|
||||
id: "special-badge-22",
|
||||
variant: :success
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -354,13 +354,14 @@ RSpec.describe Projects::PipelineSchedulesController do
|
|||
end
|
||||
|
||||
def go
|
||||
put :update, params: {
|
||||
put :update,
|
||||
params: {
|
||||
namespace_id: project.namespace.to_param,
|
||||
project_id: project,
|
||||
id: pipeline_schedule,
|
||||
schedule: schedule
|
||||
},
|
||||
as: :html
|
||||
as: :html
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Projects::ServiceDeskController do
|
||||
let_it_be(:project) do
|
||||
create(:project, :private, :custom_repo, service_desk_enabled: true,
|
||||
files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
|
||||
create(:project, :private, :custom_repo,
|
||||
service_desk_enabled: true,
|
||||
files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
|
||||
end
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"required" : [
|
||||
"project",
|
||||
"user",
|
||||
"credit_card",
|
||||
"pipeline",
|
||||
"builds",
|
||||
"total_builds_count"
|
||||
|
@ -43,6 +44,17 @@
|
|||
"sign_in_count": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"credit_card": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"similar_cards_count",
|
||||
"similar_holder_names_count"
|
||||
],
|
||||
"properties": {
|
||||
"similar_cards_count": { "type": "integer" },
|
||||
"similar_holder_names_count": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"pipeline": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -182,7 +182,7 @@ RSpec.describe TabHelper do
|
|||
context 'with data attributes' do
|
||||
it 'creates a tab counter badge with the data attributes' do
|
||||
expect(helper.gl_tab_counter_badge(1, { data: { some_attribute: 'foo' } })).to eq(
|
||||
'<span data-some-attribute="foo" class="gl-badge badge badge-pill badge-muted sm gl-tab-counter-badge">1</span>'
|
||||
'<span class="gl-badge badge badge-pill badge-muted sm gl-tab-counter-badge" data-some-attribute="foo">1</span>'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -46,4 +46,12 @@ RSpec.describe Banzai::Filter::KrokiFilter do
|
|||
|
||||
expect(doc.to_s).to start_with '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KyJyVNQiE5KTSxKidXVjS5ILCrKL4lFFrSyi07LL81RyM0vLckAysRGjxo8avCowaMGjxo8avCowaMGU8lgAE7mIdc=" hidden="" class="js-render-kroki" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDog'
|
||||
end
|
||||
|
||||
it 'allows the lang attribute on the code tag to support RST files processed by gitlab-markup gem' do
|
||||
stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000")
|
||||
text = '[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]' * 25
|
||||
doc = filter("<pre><code lang='nomnoml'>#{text}</code></pre>")
|
||||
|
||||
expect(doc.to_s).to start_with '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KyJyVNQiE5KTSxKidXVjS5ILCrKL4lFFrSyi07LL81RyM0vLckAysRGjxo8avCowaMGjxo8avCowaMGU8lgAE7mIdc=" hidden="" class="js-render-kroki" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDog'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -179,6 +179,70 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
|
|||
perform!
|
||||
end
|
||||
end
|
||||
|
||||
describe 'credit_card' do
|
||||
context 'with no registered credit_card' do
|
||||
it 'returns the expected credit card counts' do
|
||||
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
|
||||
payload = Gitlab::Json.parse(params[:body])
|
||||
|
||||
expect(payload['credit_card']['similar_cards_count']).to eq(0)
|
||||
expect(payload['credit_card']['similar_holder_names_count']).to eq(0)
|
||||
end
|
||||
|
||||
perform!
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a registered credit card' do
|
||||
let!(:credit_card) { create(:credit_card_validation, last_digits: 10, holder_name: 'Alice', user: user) }
|
||||
|
||||
it 'returns the expected credit card counts' do
|
||||
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
|
||||
payload = Gitlab::Json.parse(params[:body])
|
||||
|
||||
expect(payload['credit_card']['similar_cards_count']).to eq(1)
|
||||
expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
|
||||
end
|
||||
|
||||
perform!
|
||||
end
|
||||
|
||||
context 'with similar credit cards registered by other users' do
|
||||
before do
|
||||
create(:credit_card_validation, last_digits: 10, holder_name: 'Bob')
|
||||
end
|
||||
|
||||
it 'returns the expected credit card counts' do
|
||||
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
|
||||
payload = Gitlab::Json.parse(params[:body])
|
||||
|
||||
expect(payload['credit_card']['similar_cards_count']).to eq(2)
|
||||
expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
|
||||
end
|
||||
|
||||
perform!
|
||||
end
|
||||
end
|
||||
|
||||
context 'with similar holder names registered by other users' do
|
||||
before do
|
||||
create(:credit_card_validation, last_digits: 11, holder_name: 'Alice')
|
||||
end
|
||||
|
||||
it 'returns the expected credit card counts' do
|
||||
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
|
||||
payload = Gitlab::Json.parse(params[:body])
|
||||
|
||||
expect(payload['credit_card']['similar_cards_count']).to eq(1)
|
||||
expect(payload['credit_card']['similar_holder_names_count']).to eq(2)
|
||||
end
|
||||
|
||||
perform!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do
|
||||
|
|
|
@ -28,4 +28,15 @@ RSpec.describe Users::CreditCardValidation do
|
|||
expect(subject.similar_records).to eq([match2, match1, subject])
|
||||
end
|
||||
end
|
||||
|
||||
describe '.similar_holder_names_count' do
|
||||
subject!(:credit_card_validation) { create(:credit_card_validation, holder_name: 'ALICE M SMITH') }
|
||||
|
||||
let!(:match) { create(:credit_card_validation, holder_name: 'Alice M Smith') }
|
||||
let!(:non_match) { create(:credit_card_validation, holder_name: 'Bob B Brown') }
|
||||
|
||||
it 'returns the count of cards with similar case insensitive holder names' do
|
||||
expect(subject.similar_holder_names_count).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -440,10 +440,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
|
|||
|
||||
let!(:trigger_project_bridge) do
|
||||
create(
|
||||
:ci_bridge, status: :pending,
|
||||
user: user,
|
||||
options: trigger_downstream_project,
|
||||
pipeline: child_pipeline
|
||||
:ci_bridge, status: :pending, user: user, options: trigger_downstream_project, pipeline: child_pipeline
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
|
|||
let(:counters) do
|
||||
{
|
||||
'count' => a_kind_of(Numeric),
|
||||
'avg' => a_kind_of(Numeric),
|
||||
'max' => a_kind_of(Numeric),
|
||||
'min' => a_kind_of(Numeric)
|
||||
'avg' => a_kind_of(Numeric),
|
||||
'max' => a_kind_of(Numeric),
|
||||
'min' => a_kind_of(Numeric)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
|
|||
context 'tags persistence' do
|
||||
let(:config) do
|
||||
{
|
||||
build: {
|
||||
build: {
|
||||
script: 'ls',
|
||||
stage: 'build',
|
||||
tags: build_tag_list(label: 'build')
|
||||
|
|
|
@ -151,9 +151,8 @@ RSpec.describe Ci::JobArtifacts::CreateService do
|
|||
|
||||
expect { subject }.not_to change { Ci::JobArtifact.count }
|
||||
expect(subject).to match(
|
||||
a_hash_including(http_status: :bad_request,
|
||||
message: 'another artifact of the same type already exists',
|
||||
status: :error))
|
||||
a_hash_including(
|
||||
http_status: :bad_request, message: 'another artifact of the same type already exists', status: :error))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,14 +7,13 @@ RSpec.describe Ci::RetryJobService do
|
|||
let_it_be(:developer) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:pipeline) do
|
||||
create(:ci_pipeline, project: project,
|
||||
sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
|
||||
create(:ci_pipeline, project: project, sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
|
||||
end
|
||||
|
||||
let_it_be(:stage) do
|
||||
create(:ci_stage, project: project,
|
||||
pipeline: pipeline,
|
||||
name: 'test')
|
||||
pipeline: pipeline,
|
||||
name: 'test')
|
||||
end
|
||||
|
||||
let(:job_variables_attributes) { [{ key: 'MANUAL_VAR', value: 'manual test var' }] }
|
||||
|
@ -31,9 +30,8 @@ RSpec.describe Ci::RetryJobService do
|
|||
let_it_be(:downstream_project) { create(:project, :repository) }
|
||||
|
||||
let_it_be_with_refind(:job) do
|
||||
create(
|
||||
:ci_bridge, :success, pipeline: pipeline, downstream: downstream_project,
|
||||
description: 'a trigger job', stage_id: stage.id
|
||||
create(:ci_bridge, :success,
|
||||
pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', stage_id: stage.id
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -133,9 +131,7 @@ RSpec.describe Ci::RetryJobService do
|
|||
end
|
||||
|
||||
let!(:subsequent_bridge) do
|
||||
create(:ci_bridge, :skipped, stage_idx: 2,
|
||||
pipeline: pipeline,
|
||||
stage: 'deploy')
|
||||
create(:ci_bridge, :skipped, stage_idx: 2, pipeline: pipeline, stage: 'deploy')
|
||||
end
|
||||
|
||||
it 'resumes pipeline processing in the subsequent stage' do
|
||||
|
@ -245,10 +241,13 @@ RSpec.describe Ci::RetryJobService do
|
|||
let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' }
|
||||
|
||||
let!(:job) do
|
||||
create(:ci_build, :with_deployment, environment: environment_name,
|
||||
options: { environment: { name: environment_name } },
|
||||
pipeline: pipeline, stage_id: stage.id, project: project,
|
||||
user: other_developer)
|
||||
create(:ci_build, :with_deployment,
|
||||
environment: environment_name,
|
||||
options: { environment: { name: environment_name } },
|
||||
pipeline: pipeline,
|
||||
stage_id: stage.id,
|
||||
project: project,
|
||||
user: other_developer)
|
||||
end
|
||||
|
||||
it 'creates a new deployment' do
|
||||
|
|
|
@ -159,10 +159,10 @@ RSpec.describe Deployments::LinkMergeRequestsService do
|
|||
|
||||
it "doesn't link the same merge_request twice" do
|
||||
create(:merge_request, :merged, merge_commit_sha: mr1_merge_commit_sha,
|
||||
source_project: project)
|
||||
source_project: project)
|
||||
|
||||
picked_mr = create(:merge_request, :merged, merge_commit_sha: '123abc',
|
||||
source_project: project)
|
||||
source_project: project)
|
||||
|
||||
# the first MR includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
|
||||
create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
|
||||
|
|
|
@ -18,8 +18,8 @@ RSpec.describe Discussions::CaptureDiffNotePositionsService do
|
|||
|
||||
def build_position(diff_refs, new_line: nil, old_line: nil)
|
||||
path = 'files/markdown/ruby-style-guide.md'
|
||||
Gitlab::Diff::Position.new(old_path: path, new_path: path,
|
||||
new_line: new_line, old_line: old_line, diff_refs: diff_refs)
|
||||
Gitlab::Diff::Position.new(
|
||||
old_path: path, new_path: path, new_line: new_line, old_line: old_line, diff_refs: diff_refs)
|
||||
end
|
||||
|
||||
def note_for(new_line: nil, old_line: nil)
|
||||
|
|
|
@ -208,8 +208,11 @@ RSpec.describe Environments::StopService do
|
|||
|
||||
context 'when pipeline is a branch pipeline for merge request' do
|
||||
let(:pipeline) do
|
||||
create(:ci_pipeline, source: :push, project: project, sha: merge_request.diff_head_sha,
|
||||
merge_requests_as_head_pipeline: [merge_request])
|
||||
create(:ci_pipeline,
|
||||
source: :push,
|
||||
project: project,
|
||||
sha: merge_request.diff_head_sha,
|
||||
merge_requests_as_head_pipeline: [merge_request])
|
||||
end
|
||||
|
||||
it 'does not stop the active environment' do
|
||||
|
|
|
@ -420,9 +420,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
|
|||
service.save_designs(author, create: [design])
|
||||
|
||||
expect_snowplow_event(
|
||||
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||
action: 'create',
|
||||
namespace: design.project.namespace,
|
||||
namespace: design.project.namespace,
|
||||
user: author,
|
||||
project: design.project,
|
||||
label: 'design_users'
|
||||
|
@ -433,9 +433,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
|
|||
service.save_designs(author, update: [design])
|
||||
|
||||
expect_snowplow_event(
|
||||
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||
action: 'update',
|
||||
namespace: design.project.namespace,
|
||||
namespace: design.project.namespace,
|
||||
user: author,
|
||||
project: design.project,
|
||||
label: 'design_users'
|
||||
|
@ -481,9 +481,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
|
|||
service.destroy_designs([design], author)
|
||||
|
||||
expect_snowplow_event(
|
||||
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||
action: 'destroy',
|
||||
namespace: design.project.namespace,
|
||||
namespace: design.project.namespace,
|
||||
user: author,
|
||||
project: design.project,
|
||||
label: 'design_users'
|
||||
|
|
|
@ -149,9 +149,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
|
||||
it 'logs the import success' do
|
||||
expect(import_logger).to receive(:info).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: 'Group Import/Export: Import succeeded'
|
||||
message: 'Group Import/Export: Import succeeded'
|
||||
).once
|
||||
|
||||
subject
|
||||
|
@ -161,9 +161,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
context 'when user does not have correct permissions' do
|
||||
it 'logs the error and raises an exception' do
|
||||
expect(import_logger).to receive(:error).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: a_string_including('Errors occurred')
|
||||
message: a_string_including('Errors occurred')
|
||||
)
|
||||
|
||||
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
|
||||
|
@ -186,9 +186,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
|
||||
it 'logs the error and raises an exception' do
|
||||
expect(import_logger).to receive(:error).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: a_string_including('Errors occurred')
|
||||
message: a_string_including('Errors occurred')
|
||||
).once
|
||||
|
||||
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
|
||||
|
@ -267,9 +267,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
|
||||
it 'logs the import success' do
|
||||
expect(import_logger).to receive(:info).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: 'Group Import/Export: Import succeeded'
|
||||
message: 'Group Import/Export: Import succeeded'
|
||||
).once
|
||||
|
||||
subject
|
||||
|
@ -279,9 +279,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
context 'when user does not have correct permissions' do
|
||||
it 'logs the error and raises an exception' do
|
||||
expect(import_logger).to receive(:error).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: a_string_including('Errors occurred')
|
||||
message: a_string_including('Errors occurred')
|
||||
)
|
||||
|
||||
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
|
||||
|
@ -304,9 +304,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
|
||||
it 'logs the error and raises an exception' do
|
||||
expect(import_logger).to receive(:error).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: a_string_including('Errors occurred')
|
||||
message: a_string_including('Errors occurred')
|
||||
).once
|
||||
|
||||
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
|
||||
|
@ -328,9 +328,9 @@ RSpec.describe Groups::ImportExport::ImportService do
|
|||
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
|
||||
|
||||
expect(import_logger).to receive(:info).with(
|
||||
group_id: group.id,
|
||||
group_id: group.id,
|
||||
group_name: group.name,
|
||||
message: 'Group Import/Export: Import succeeded'
|
||||
message: 'Group Import/Export: Import succeeded'
|
||||
)
|
||||
|
||||
subject
|
||||
|
|
|
@ -47,7 +47,7 @@ RSpec.describe Issuable::BulkUpdateService do
|
|||
|
||||
let(:bulk_update_params) do
|
||||
{
|
||||
add_label_ids: add_labels.map(&:id),
|
||||
add_label_ids: add_labels.map(&:id),
|
||||
remove_label_ids: remove_labels.map(&:id)
|
||||
}
|
||||
end
|
||||
|
|
|
@ -416,7 +416,7 @@ RSpec.describe Issues::CreateService do
|
|||
context "when issuable feature is private" do
|
||||
before do
|
||||
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
|
||||
merge_requests_access_level: ProjectFeature::PRIVATE)
|
||||
merge_requests_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
|
||||
|
|
|
@ -20,18 +20,30 @@ RSpec.describe MergeRequests::BuildService do
|
|||
let(:merge_request) { service.execute }
|
||||
let(:compare) { double(:compare, commits: commits) }
|
||||
let(:commit_1) do
|
||||
double(:commit_1, sha: 'f00ba6', safe_message: 'Initial commit',
|
||||
gitaly_commit?: false, id: 'f00ba6', parent_ids: ['f00ba5'])
|
||||
double(:commit_1,
|
||||
sha: 'f00ba6',
|
||||
safe_message: 'Initial commit',
|
||||
gitaly_commit?: false,
|
||||
id: 'f00ba6',
|
||||
parent_ids: ['f00ba5'])
|
||||
end
|
||||
|
||||
let(:commit_2) do
|
||||
double(:commit_2, sha: 'f00ba7', safe_message: "Closes #1234 Second commit\n\nCreate the app",
|
||||
gitaly_commit?: false, id: 'f00ba7', parent_ids: ['f00ba6'])
|
||||
double(:commit_2,
|
||||
sha: 'f00ba7',
|
||||
safe_message: "Closes #1234 Second commit\n\nCreate the app",
|
||||
gitaly_commit?: false,
|
||||
id: 'f00ba7',
|
||||
parent_ids: ['f00ba6'])
|
||||
end
|
||||
|
||||
let(:commit_3) do
|
||||
double(:commit_3, sha: 'f00ba8', safe_message: 'This is a bad commit message!',
|
||||
gitaly_commit?: false, id: 'f00ba8', parent_ids: ['f00ba7'])
|
||||
double(:commit_3,
|
||||
sha: 'f00ba8',
|
||||
safe_message: 'This is a bad commit message!',
|
||||
gitaly_commit?: false,
|
||||
id: 'f00ba8',
|
||||
parent_ids: ['f00ba7'])
|
||||
end
|
||||
|
||||
let(:commits) { nil }
|
||||
|
|
|
@ -434,7 +434,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
|
|||
context "when issuable feature is private" do
|
||||
before do
|
||||
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
|
||||
merge_requests_access_level: ProjectFeature::PRIVATE)
|
||||
merge_requests_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
|
||||
|
|
|
@ -510,9 +510,9 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
|
|||
before do
|
||||
create(:ci_pipeline,
|
||||
project: project,
|
||||
ref: merge_request.source_branch,
|
||||
sha: merge_request.diff_head_sha,
|
||||
status: :success)
|
||||
ref: merge_request.source_branch,
|
||||
sha: merge_request.diff_head_sha,
|
||||
status: :success)
|
||||
|
||||
perform_enqueued_jobs do
|
||||
@merge_request = service.execute(merge_request)
|
||||
|
|
|
@ -62,7 +62,7 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor
|
|||
start_branch: project.default_branch,
|
||||
encoding: 'text',
|
||||
file_path: ".gitlab/dashboards/custom_dashboard.yml",
|
||||
file_content: file_content_hash.to_yaml
|
||||
file_content: file_content_hash.to_yaml
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -134,8 +134,7 @@ RSpec.describe Notes::CreateService do
|
|||
context 'in a merge request' do
|
||||
let_it_be(:project_with_repo) { create(:project, :repository) }
|
||||
let_it_be(:merge_request) do
|
||||
create(:merge_request, source_project: project_with_repo,
|
||||
target_project: project_with_repo)
|
||||
create(:merge_request, source_project: project_with_repo, target_project: project_with_repo)
|
||||
end
|
||||
|
||||
context 'noteable highlight cache clearing' do
|
||||
|
@ -181,8 +180,7 @@ RSpec.describe Notes::CreateService do
|
|||
|
||||
it 'does not clear cache when note is not the first of the discussion' do
|
||||
prev_note =
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
project: project_with_repo)
|
||||
create(:diff_note_on_merge_request, noteable: merge_request, project: project_with_repo)
|
||||
reply_opts =
|
||||
opts.merge(in_reply_to_discussion_id: prev_note.discussion_id,
|
||||
type: 'DiffNote',
|
||||
|
|
|
@ -57,13 +57,11 @@ RSpec.describe Notes::DestroyService do
|
|||
context 'in a merge request' do
|
||||
let_it_be(:repo_project) { create(:project, :repository) }
|
||||
let_it_be(:merge_request) do
|
||||
create(:merge_request, source_project: repo_project,
|
||||
target_project: repo_project)
|
||||
create(:merge_request, source_project: repo_project, target_project: repo_project)
|
||||
end
|
||||
|
||||
let_it_be(:note) do
|
||||
create(:diff_note_on_merge_request, project: repo_project,
|
||||
noteable: merge_request)
|
||||
create(:diff_note_on_merge_request, project: repo_project, noteable: merge_request)
|
||||
end
|
||||
|
||||
it 'does not track issue comment removal usage data' do
|
||||
|
@ -84,9 +82,8 @@ RSpec.describe Notes::DestroyService do
|
|||
end
|
||||
|
||||
it 'does not clear cache when note is not the first of the discussion' do
|
||||
reply_note = create(:diff_note_on_merge_request, in_reply_to: note,
|
||||
project: repo_project,
|
||||
noteable: merge_request)
|
||||
reply_note = create(:diff_note_on_merge_request,
|
||||
in_reply_to: note, project: repo_project, noteable: merge_request)
|
||||
|
||||
expect(merge_request).not_to receive(:diffs)
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ RSpec.describe Packages::Debian::ParseDebian822Service do
|
|||
'Depends' => '${shlibs:Depends}, ${misc:Depends}',
|
||||
'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph."
|
||||
},
|
||||
'Package: sample-udeb' => {
|
||||
'Package: sample-udeb' => {
|
||||
'Package' => 'sample-udeb',
|
||||
'Package-Type' => 'udeb',
|
||||
'Architecture' => 'any',
|
||||
|
|
|
@ -312,7 +312,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
|
|||
|
||||
before do
|
||||
stub_container_registry_tags(repository: project.full_path + '/image',
|
||||
tags: ['tag'])
|
||||
tags: ['tag'])
|
||||
project.container_repositories << container_repository
|
||||
end
|
||||
|
||||
|
@ -350,7 +350,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
|
|||
context 'when there are tags for legacy root repository' do
|
||||
before do
|
||||
stub_container_registry_tags(repository: project.full_path,
|
||||
tags: ['tag'])
|
||||
tags: ['tag'])
|
||||
end
|
||||
|
||||
context 'when image repository tags deletion succeeds' do
|
||||
|
|
|
@ -386,12 +386,12 @@ RSpec.describe ServicePing::SubmitService do
|
|||
let(:payload) do
|
||||
{
|
||||
uuid: 'uuid',
|
||||
metric_a: metric_double,
|
||||
metric_group: {
|
||||
metric_a: metric_double,
|
||||
metric_group: {
|
||||
metric_b: metric_double
|
||||
},
|
||||
metric_without_timing: "value",
|
||||
recorded_at: Time.current
|
||||
metric_without_timing: "value",
|
||||
recorded_at: Time.current
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -399,10 +399,10 @@ RSpec.describe ServicePing::SubmitService do
|
|||
{
|
||||
metadata: {
|
||||
uuid: 'uuid',
|
||||
metrics: [
|
||||
{ name: 'metric_a', time_elapsed: 123 },
|
||||
{ name: 'metric_group.metric_b', time_elapsed: 123 }
|
||||
]
|
||||
metrics: [
|
||||
{ name: 'metric_a', time_elapsed: 123 },
|
||||
{ name: 'metric_group.metric_b', time_elapsed: 123 }
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -35,7 +35,7 @@ RSpec.describe Suggestions::ApplyService do
|
|||
def apply(suggestions, custom_message = nil)
|
||||
result = apply_service.new(user, *suggestions, message: custom_message).execute
|
||||
|
||||
suggestions.map { |suggestion| suggestion.reload }
|
||||
suggestions.map(&:reload)
|
||||
|
||||
expect(result[:status]).to eq(:success)
|
||||
end
|
||||
|
@ -136,21 +136,20 @@ RSpec.describe Suggestions::ApplyService do
|
|||
end
|
||||
|
||||
let(:merge_request) do
|
||||
create(:merge_request, source_project: project,
|
||||
target_project: project,
|
||||
source_branch: 'master')
|
||||
create(:merge_request,
|
||||
source_project: project, target_project: project, source_branch: 'master')
|
||||
end
|
||||
|
||||
let(:position) { build_position }
|
||||
|
||||
let(:diff_note) do
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
position: position, project: project)
|
||||
create(:diff_note_on_merge_request,
|
||||
noteable: merge_request, position: position, project: project)
|
||||
end
|
||||
|
||||
let(:suggestion) do
|
||||
create(:suggestion, :content_from_repo, note: diff_note,
|
||||
to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
|
||||
create(:suggestion, :content_from_repo,
|
||||
note: diff_note, to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
|
||||
end
|
||||
|
||||
let(:suggestion2) do
|
||||
|
@ -311,9 +310,9 @@ RSpec.describe Suggestions::ApplyService do
|
|||
|
||||
context 'when HEAD from position is different from source branch HEAD on repo' do
|
||||
it 'returns error message' do
|
||||
allow(suggestion).to receive(:appliable?) { true }
|
||||
allow(suggestion.position).to receive(:head_sha) { 'old-sha' }
|
||||
allow(suggestion.noteable).to receive(:source_branch_sha) { 'new-sha' }
|
||||
allow(suggestion).to receive(:appliable?).and_return(true)
|
||||
allow(suggestion.position).to receive(:head_sha).and_return('old-sha')
|
||||
allow(suggestion.noteable).to receive(:source_branch_sha).and_return('new-sha')
|
||||
|
||||
result = apply_service.new(user, suggestion).execute
|
||||
|
||||
|
@ -430,7 +429,6 @@ RSpec.describe Suggestions::ApplyService do
|
|||
suggestion1_diff = fetch_raw_diff(suggestion1)
|
||||
suggestion2_diff = fetch_raw_diff(suggestion2)
|
||||
|
||||
# rubocop: disable Layout/TrailingWhitespace
|
||||
expected_suggestion1_diff = <<-CONTENT.strip_heredoc
|
||||
@@ -10,7 +10,7 @@ module Popen
|
||||
end
|
||||
|
@ -442,9 +440,6 @@ RSpec.describe Suggestions::ApplyService do
|
|||
"PWD" => path
|
||||
}
|
||||
CONTENT
|
||||
# rubocop: enable Layout/TrailingWhitespace
|
||||
|
||||
# rubocop: disable Layout/TrailingWhitespace
|
||||
expected_suggestion2_diff = <<-CONTENT.strip_heredoc
|
||||
@@ -28,7 +28,7 @@ module Popen
|
||||
|
||||
|
@ -455,8 +450,6 @@ RSpec.describe Suggestions::ApplyService do
|
|||
@cmd_status = wait_thr.value.exitstatus
|
||||
end
|
||||
CONTENT
|
||||
# rubocop: enable Layout/TrailingWhitespace
|
||||
|
||||
expect(suggestion1_diff.strip).to eq(expected_suggestion1_diff.strip)
|
||||
expect(suggestion2_diff.strip).to eq(expected_suggestion2_diff.strip)
|
||||
end
|
||||
|
@ -508,10 +501,8 @@ RSpec.describe Suggestions::ApplyService do
|
|||
end
|
||||
|
||||
let(:suggestion) do
|
||||
create(:suggestion, :content_from_repo, note: diff_note,
|
||||
lines_above: 2,
|
||||
lines_below: 3,
|
||||
to_content: "# multi\n# line\n")
|
||||
create(:suggestion, :content_from_repo,
|
||||
note: diff_note, lines_above: 2, lines_below: 3, to_content: "# multi\n# line\n")
|
||||
end
|
||||
|
||||
let(:suggestions) { [suggestion] }
|
||||
|
@ -568,7 +559,7 @@ RSpec.describe Suggestions::ApplyService do
|
|||
end
|
||||
|
||||
let(:suggestion) do
|
||||
create_suggestion( to_content: "", new_line: 13)
|
||||
create_suggestion(to_content: "", new_line: 13)
|
||||
end
|
||||
|
||||
let(:suggestions) { [suggestion] }
|
||||
|
@ -616,14 +607,12 @@ RSpec.describe Suggestions::ApplyService do
|
|||
|
||||
context 'no permission' do
|
||||
let(:merge_request) do
|
||||
create(:merge_request, source_project: project,
|
||||
target_project: project)
|
||||
create(:merge_request, source_project: project, target_project: project)
|
||||
end
|
||||
|
||||
let(:diff_note) do
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
position: position,
|
||||
project: project)
|
||||
create(:diff_note_on_merge_request,
|
||||
noteable: merge_request, position: position, project: project)
|
||||
end
|
||||
|
||||
context 'user cannot write in project repo' do
|
||||
|
@ -642,14 +631,12 @@ RSpec.describe Suggestions::ApplyService do
|
|||
|
||||
context 'patch is not appliable' do
|
||||
let(:merge_request) do
|
||||
create(:merge_request, source_project: project,
|
||||
target_project: project)
|
||||
create(:merge_request, source_project: project, target_project: project)
|
||||
end
|
||||
|
||||
let(:diff_note) do
|
||||
create(:diff_note_on_merge_request, noteable: merge_request,
|
||||
position: position,
|
||||
project: project)
|
||||
create(:diff_note_on_merge_request,
|
||||
noteable: merge_request, position: position, project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -669,7 +656,7 @@ RSpec.describe Suggestions::ApplyService do
|
|||
let(:result) { apply_service.new(user, suggestion).execute }
|
||||
|
||||
before do
|
||||
expect(suggestion.note).to receive(:latest_diff_file) { nil }
|
||||
expect(suggestion.note).to receive(:latest_diff_file).and_return(nil)
|
||||
end
|
||||
|
||||
it 'returns error message' do
|
||||
|
|
|
@ -13,7 +13,7 @@ RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
|
|||
let(:current_user) { author }
|
||||
let(:work_item) do
|
||||
create(:work_item, author: author, project: project, description: 'old description',
|
||||
last_edited_at: Date.yesterday, last_edited_by: random_user
|
||||
last_edited_at: Date.yesterday, last_edited_by: random_user
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ RSpec.describe Tooling::Danger::Datateam do
|
|||
impacted: true,
|
||||
impacted_files: %w(db/structure.sql)
|
||||
},
|
||||
'with structure.sql changes and Data Warehouse::Impact Check label' => {
|
||||
'with structure.sql changes and Data Warehouse::Impact Check label' => {
|
||||
modified_files: %w(db/structure.sql),
|
||||
changed_lines: ['+group_id bigint NOT NULL)'],
|
||||
mr_labels: ['Data Warehouse::Impact Check'],
|
||||
|
|
|
@ -7,8 +7,8 @@ RSpec.describe 'projects/tags/index.html.haml' do
|
|||
let_it_be(:git_tag) { project.repository.tags.last }
|
||||
let_it_be(:release) do
|
||||
create(:release, project: project,
|
||||
sha: git_tag.target_commit.sha,
|
||||
tag: 'v1.1.0')
|
||||
sha: git_tag.target_commit.sha,
|
||||
tag: 'v1.1.0')
|
||||
end
|
||||
|
||||
let(:pipeline) { create(:ci_pipeline, :success, project: project, ref: git_tag.name, sha: release.sha) }
|
||||
|
|
|
@ -51,7 +51,7 @@ RSpec.describe EmailsOnPushWorker, :mailer do
|
|||
context "when push is a force push to delete commits" do
|
||||
before do
|
||||
data_force_push = data.stringify_keys.merge(
|
||||
"after" => data[:before],
|
||||
"after" => data[:before],
|
||||
"before" => data[:after]
|
||||
)
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ RSpec.describe PagesDomainSslRenewalCronWorker do
|
|||
end
|
||||
|
||||
let!(:domain_with_failed_auto_ssl) do
|
||||
create(:pages_domain, :without_certificate, :without_key, project: project,
|
||||
auto_ssl_enabled: true, auto_ssl_failed: true)
|
||||
create(:pages_domain, :without_certificate, :without_key,
|
||||
project: project, auto_ssl_enabled: true, auto_ssl_failed: true)
|
||||
end
|
||||
|
||||
let!(:domain_with_expired_auto_ssl) do
|
||||
|
|
Loading…
Reference in a new issue