Commit 074d013e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 8f9beefa
......@@ -163,7 +163,7 @@ frontend-fixtures:
frontend-fixtures-as-if-foss:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss
.frontend-job-base:
......@@ -206,7 +206,7 @@ karma:
karma-as-if-foss:
extends:
- .karma-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
......@@ -241,7 +241,7 @@ jest:
jest-as-if-foss:
extends:
- .jest-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
cache:
......@@ -250,7 +250,7 @@ jest-as-if-foss:
coverage-frontend:
extends:
- .default-retry
- .frontend:rules:default-frontend-jobs
- .frontend:rules:default-frontend-jobs-no-foss
needs: ["jest"]
stage: post-test
before_script:
......
......@@ -273,7 +273,7 @@
changes: *code-backstage-patterns
when: on_success
.frontend:rules:default-frontend-jobs-as-if-foss:
.frontend:rules:default-frontend-jobs-no-foss:
rules:
- <<: *if-not-ee
when: never
......
......@@ -200,6 +200,35 @@ GitlabSecurity/PublicSend:
- 'ee/lib/**/*.rake'
- 'ee/spec/**/*'
Gitlab/DuplicateSpecLocation:
Exclude:
- ee/spec/controllers/groups_controller_spec.rb
- ee/spec/controllers/projects/jobs_controller_spec.rb
- ee/spec/helpers/auth_helper_spec.rb
- ee/spec/lib/gitlab/gl_repository_spec.rb
- ee/spec/lib/gitlab/usage_data_spec.rb
- ee/spec/models/namespace_spec.rb
- ee/spec/models/note_spec.rb
- ee/spec/serializers/environment_entity_spec.rb
- ee/spec/services/issues/create_service_spec.rb
- ee/spec/services/merge_requests/create_service_spec.rb
- ee/spec/services/merge_requests/refresh_service_spec.rb
- ee/spec/services/merge_requests/update_service_spec.rb
- ee/spec/services/system_hooks_service_spec.rb
- ee/spec/controllers/ee/groups_controller_spec.rb
- ee/spec/controllers/ee/projects/jobs_controller_spec.rb
- ee/spec/helpers/ee/auth_helper_spec.rb
- ee/spec/lib/ee/gitlab/gl_repository_spec.rb
- ee/spec/lib/ee/gitlab/usage_data_spec.rb
- ee/spec/models/ee/namespace_spec.rb
- ee/spec/models/ee/note_spec.rb
- ee/spec/serializers/ee/environment_entity_spec.rb
- ee/spec/services/ee/issues/create_service_spec.rb
- ee/spec/services/ee/merge_requests/create_service_spec.rb
- ee/spec/services/ee/merge_requests/refresh_service_spec.rb
- ee/spec/services/ee/merge_requests/update_service_spec.rb
- ee/spec/services/ee/system_hooks_service_spec.rb
Cop/InjectEnterpriseEditionModule:
Enabled: true
Exclude:
......
<script>
import { escapeRegExp } from 'lodash';
import { GlBadge, GlLink, GlSkeletonLoading, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import { visitUrl, escapeFileUrl } from '~/lib/utils/url_utility';
import { escapeFileUrl } from '~/lib/utils/url_utility';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Icon from '~/vue_shared/components/icon.vue';
import { getIconName } from '../../utils/icon';
......@@ -117,39 +117,37 @@ export default {
return this.commit && this.commit.lockLabel;
},
},
methods: {
openRow(e) {
if (e.target.tagName === 'A') return;
if (this.isFolder && !e.metaKey) {
this.$router.push(this.routerLinkTo);
} else {
visitUrl(this.url, e.metaKey);
}
},
},
};
</script>
<template>
<tr :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name">
<tr class="tree-item">
<td class="tree-item-file-name cursor-default position-relative">
<gl-loading-icon
v-if="path === loadingPath"
size="sm"
inline
class="d-inline-block align-text-bottom fa-fw"
/>
<i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component
:is="linkComponent"
ref="link"
:to="routerLinkTo"
:href="url"
class="str-truncated"
:class="{
'is-submodule': isSubmodule,
}"
class="tree-item-link str-truncated"
data-qa-selector="file_name_link"
>
{{ fullPath }}
<i
v-if="!loadingPath"
:aria-label="type"
role="img"
:class="iconName"
class="fa fa-fw mr-1"
></i
><span class="position-relative">{{ fullPath }}</span>
</component>
<!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
<gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge>
......@@ -165,7 +163,7 @@ export default {
class="ml-2 vertical-align-middle"
/>
</td>
<td class="d-none d-sm-table-cell tree-commit">
<td class="d-none d-sm-table-cell tree-commit cursor-default">
<gl-link
v-if="commit"
:href="commit.commitPath"
......@@ -176,7 +174,7 @@ export default {
</gl-link>
<gl-skeleton-loading v-else :lines="1" class="h-auto" />
</td>
<td class="tree-time-ago text-right">
<td class="tree-time-ago text-right cursor-default">
<timeago-tooltip v-if="commit" :time="commit.committedDate" />
<gl-skeleton-loading v-else :lines="1" class="ml-auto h-auto w-50" />
</td>
......
......@@ -511,3 +511,21 @@ span.idiff {
.code-navigation-popover {
max-width: 450px;
}
.tree-item-link {
&:not(.is-submodule) {
span {
z-index: 2;
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
}
}
}
......@@ -65,7 +65,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
options = additional_attributes.merge(diff_view: diff_view)
if @merge_request.project.context_commits_enabled?
options[:context_commits] = @merge_request.context_commits
options[:context_commits] = @merge_request.recent_context_commits
end
render json: DiffsSerializer.new(request).represent(diffs, options)
......
......@@ -9,7 +9,7 @@ module Resolvers
def resolve(**args)
current_user = context[:current_user]
issue_id = GlobalID.parse(args[:id]).model_id
issue_id = GlobalID.parse(args[:id])&.model_id
# Get data from Sentry
response = ::ErrorTracking::IssueDetailsService.new(
......
......@@ -8,7 +8,7 @@ module Resolvers
description: 'ID of the Sentry issue'
def resolve(**args)
issue_id = GlobalID.parse(args[:id]).model_id
issue_id = GlobalID.parse(args[:id])&.model_id
# Get data from Sentry
response = ::ErrorTracking::IssueLatestEventService.new(
......
......@@ -410,8 +410,16 @@ class MergeRequest < ApplicationRecord
"#{project.to_reference_base(from, full: full)}#{reference}"
end
def context_commits
@context_commits ||= merge_request_context_commits.map(&:to_commit)
def context_commits(limit: nil)
@context_commits ||= merge_request_context_commits.limit(limit).map(&:to_commit)
end
def recent_context_commits
context_commits(limit: MergeRequestDiff::COMMITS_SAFE_SIZE)
end
def context_commits_count
context_commits.count
end
def commits(limit: nil)
......
# frozen_string_literal: true
class ResourceMilestoneEvent < ResourceEvent
include IgnorableColumns
belongs_to :issue
belongs_to :merge_request
belongs_to :milestone
......@@ -18,6 +20,8 @@ class ResourceMilestoneEvent < ResourceEvent
# state is used for issue and merge request states.
enum state: Issue.available_states.merge(MergeRequest.available_states)
ignore_columns %i[reference reference_html cached_markdown_version], remove_with: '13.1', remove_after: '2020-06-22'
def self.issuable_attrs
%i(issue merge_request).freeze
end
......
# frozen_string_literal: true
class CommitEntity < API::Entities::Commit
include MarkupHelper
include RequestAwareEntity
expose :author, using: UserEntity
expose :author_gravatar_url do |commit|
GravatarService.new.execute(commit.author_email) # rubocop: disable CodeReuse/ServiceClass
end
expose :commit_url do |commit, options|
project_commit_url(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :commit_path do |commit, options|
project_commit_path(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :description_html, if: { type: :full } do |commit|
markdown_field(commit, :description)
end
expose :title_html, if: { type: :full } do |commit|
markdown_field(commit, :title)
end
expose :signature_html, if: { type: :full } do |commit|
render('projects/commit/_signature', signature: commit.signature) if commit.has_signature?
end
expose :pipeline_status_path, if: { type: :full } do |commit, options|
pipeline_ref = options[:pipeline_ref]
pipeline_project = options[:pipeline_project] || commit.project
next unless pipeline_ref && pipeline_project
pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project)
next unless pipeline&.status
pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
end
def render(*args)
return unless request.respond_to?(:render) && request.render.respond_to?(:call)
request.render.call(*args)
end
class CommitEntity < API::Entities::CommitWithLink
end
# frozen_string_literal: true
class UserEntity < API::Entities::UserBasic
include RequestAwareEntity
include UserStatusTooltip
expose :path do |user|
user_path(user)
end
class UserEntity < API::Entities::UserPath
end
......@@ -74,7 +74,7 @@ module Issuable
if matching_destination_milestone.present?
event.attributes
.except('id', 'reference', 'reference_html')
.except('id')
.merge(entity_key => new_entity.id,
'milestone_id' => matching_destination_milestone.id,
'action' => ResourceMilestoneEvent.actions[event.action],
......
......@@ -6,7 +6,7 @@ module Metrics
module Dashboard
class CloneDashboardService < ::BaseService
ALLOWED_FILE_TYPE = '.yml'
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::ProjectDashboardService::DASHBOARD_ROOT
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT
class << self
def allowed_dashboard_templates
......@@ -52,7 +52,7 @@ module Metrics
def dashboard_details
{
path: new_dashboard_path,
display_name: ::Metrics::Dashboard::ProjectDashboardService.name_for_path(new_dashboard_path),
display_name: ::Metrics::Dashboard::CustomDashboardService.name_for_path(new_dashboard_path),
default: false,
system_dashboard: false
}
......
......@@ -5,7 +5,7 @@
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
module Metrics
module Dashboard
class ProjectDashboardService < ::Metrics::Dashboard::BaseService
class CustomDashboardService < ::Metrics::Dashboard::BaseService
DASHBOARD_ROOT = ".gitlab/dashboards"
class << self
......
......@@ -7,7 +7,7 @@ module Metrics
include Stepable
ALLOWED_FILE_TYPE = '.yml'
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::ProjectDashboardService::DASHBOARD_ROOT
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT
steps :check_push_authorized,
:check_branch_name,
......@@ -117,7 +117,7 @@ module Metrics
def dashboard_details
{
path: update_dashboard_path,
display_name: ::Metrics::Dashboard::ProjectDashboardService.name_for_path(update_dashboard_path),
display_name: ::Metrics::Dashboard::CustomDashboardService.name_for_path(update_dashboard_path),
default: false,
system_dashboard: false
}
......
......@@ -17,5 +17,6 @@
%ul.wiki-pages
= render @sidebar_wiki_entries, context: 'sidebar'
.block.w-100
- if @sidebar_wiki_entries&.length.to_i >= 15
= link_to project_wikis_pages_path(@project), class: 'btn btn-block' do
= s_("Wiki|More Pages")
= s_("Wiki|View All Pages")
......@@ -3,7 +3,7 @@
.card-header
%h5
= hook_class.underscore.humanize.titleize.pluralize
(#{hooks.count})
(#{hooks.load.size})
- if hooks.any?
%ul.content-list
......
......@@ -891,7 +891,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:idempotent: true
- :name: update_namespace_statistics:namespaces_schedule_aggregation
:feature_category: :source_code_management
:has_external_dependencies:
......
# frozen_string_literal: true
module Namespaces
class RootStatisticsWorker # rubocop:disable Scalability/IdempotentWorker
class RootStatisticsWorker
include ApplicationWorker
queue_namespace :update_namespace_statistics
feature_category :source_code_management
idempotent!
def perform(namespace_id)
namespace = Namespace.find(namespace_id)
......
---
title: Update More Pages button on Wiki Page
merge_request: 27499
author:
type: changed
---
title: Fix OpenAPI file detector
merge_request: 27321
author: Roger Meier
type: fixed
---
title: Reduce SQL queries when rendering webhook settings
merge_request: 27359
author:
type: performance
......@@ -3,9 +3,9 @@
require 'yaml'
SEE_DOC = "See [the documentation](https://docs.gitlab.com/ce/development/changelog.html)."
SEE_DOC = "See [the documentation](https://docs.gitlab.com/ee/development/changelog.html)."
CREATE_CHANGELOG_MESSAGE = <<~MSG
You can create one with:
If you want to create a changelog entry for GitLab FOSS, run the following:
```
bin/changelog -m %<mr_iid>s "%<mr_title>s"
......@@ -20,7 +20,7 @@ bin/changelog --ee -m %<mr_iid>s "%<mr_title>s"
Note: Merge requests with %<labels>s do not trigger this check.
MSG
def check_changelog(path)
def check_changelog_yaml(path)
yaml = YAML.safe_load(File.read(path))
fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
......@@ -28,8 +28,6 @@ def check_changelog(path)
if yaml["merge_request"].nil? && !helper.security_mr?
message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}"
elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !changelog.ce_port_changelog?(path)
fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
end
rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
# YAML could not be parsed, fail the build.
......@@ -38,6 +36,19 @@ rescue StandardError => e
warn "There was a problem trying to check the Changelog. Exception: #{e.name} - #{e.message}"
end
def check_changelog_path(path)
ee_changes = helper.all_ee_changes.dup
ee_changes.delete(path)
if ee_changes.any? && !changelog.ee_changelog?
warn "This MR has a Changelog file outside `ee/`, but code changes in `ee/`. Consider moving the Changelog file into `ee/`."
end
if ee_changes.empty? && changelog.ee_changelog?
warn "This MR has a Changelog file in `ee/`, but no code changes in `ee/`. Consider moving the Changelog file outside `ee/`."
end
end
def sanitized_mr_title
helper.sanitize_mr_title(gitlab.mr_json["title"])
end
......@@ -49,11 +60,10 @@ end
changelog_found = changelog.found
if changelog.needed?
if changelog_found
check_changelog(changelog_found)
else
message "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html)**: If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.\n\n" +
if changelog_found
check_changelog_yaml(changelog_found)
check_changelog_path(changelog_found)
elsif changelog.needed?
message "**[CHANGELOG missing](https://docs.gitlab.com/ee/development/changelog.html)**: If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.\n\n" +
format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: sanitized_mr_title, labels: changelog.presented_no_changelog_labels)
end
end
# frozen_string_literal: true
module API
module Entities
class CommitWithLink < Commit
include MarkupHelper
include RequestAwareEntity
expose :author, using: Entities::UserPath
expose :author_gravatar_url do |commit|
GravatarService.new.execute(commit.author_email)
end
expose :commit_url do |commit, options|
project_commit_url(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :commit_path do |commit, options|
project_commit_path(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :description_html, if: { type: :full } do |commit|
markdown_field(commit, :description)
end
expose :title_html, if: { type: :full } do |commit|
markdown_field(commit, :title)
end
expose :signature_html, if: { type: :full } do |commit|
render('projects/commit/_signature', signature: commit.signature) if commit.has_signature?
end
expose :pipeline_status_path, if: { type: :full } do |commit, options|
pipeline_ref = options[:pipeline_ref]
pipeline_project = options[:pipeline_project] || commit.project
next unless pipeline_ref && pipeline_project
pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project)
next unless pipeline&.status
pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
end
def render(*args)
return unless request.respond_to?(:render) && request.render.respond_to?(:call)
request.render.call(*args)
end
end
end
end
# frozen_string_literal: true
module API
module Entities
class UserPath < UserBasic
include RequestAwareEntity
include UserStatusTooltip
expose :path do |user|
user_path(user)
end
end
end
end
......@@ -306,7 +306,7 @@ module API
context_commits =
paginate(merge_request.merge_request_context_commits).map(&:to_commit)
present context_commits, with: Entities::Commit
present context_commits, with: Entities::CommitWithLink, type: :full, request: merge_request
end
params do
......
......@@ -11,7 +11,7 @@ module Gitlab
end
def found
git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
@found ||= git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
end
def presented_no_changelog_labels
......@@ -22,12 +22,8 @@ module Gitlab
gitlab.mr_json["title"].gsub(/^WIP: */, '').gsub(/`/, '\\\`')
end
def ee_changelog?(changelog_path)
changelog_path =~ /unreleased-ee/
end
def ce_port_changelog?(changelog_path)
helper.ee? && !ee_changelog?(changelog_path)
def ee_changelog?
found.start_with?('ee/')
end
private
......
......@@ -34,6 +34,10 @@ module Gitlab
.sort
end
def all_ee_changes
all_changed_files.grep(%r{\Aee/})
end
def ee?
# Support former project name for `dev` and support local Danger run
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?('../../ee')
......
......@@ -40,7 +40,7 @@ module Gitlab
yarn_lock: 'yarn.lock',
# OpenAPI Specification files
openapi: %r{.*(openapi|swagger).*\.(yaml|yml|json)\z}i
openapi: %r{[^/]*(openapi|swagger)[^/]*\.(yaml|yml|json)\z}i
}.freeze
# Returns an Array of file types based on the given paths.
......
......@@ -78,7 +78,7 @@ module Gitlab
end
def project_service
::Metrics::Dashboard::ProjectDashboardService
::Metrics::Dashboard::CustomDashboardService
end
def self_monitoring_service
......
......@@ -20,7 +20,7 @@ module Gitlab
::Metrics::Dashboard::SystemDashboardService,
::Metrics::Dashboard::PodDashboardService,
::Metrics::Dashboard::SelfMonitoringDashboardService,
::Metrics::Dashboard::ProjectDashboardService
::Metrics::Dashboard::CustomDashboardService
].freeze
# Returns a class which inherits from the BaseService
......
......@@ -5,8 +5,18 @@ require 'digest'
module Gitlab
module SidekiqMiddleware
module DuplicateJobs
def self.drop_duplicates?
Feature.enabled?(:drop_duplicate_sidekiq_jobs)
DROPPABLE_QUEUES = Set.new([
Namespaces::RootStatisticsWorker.queue
]).freeze
def self.drop_duplicates?(queue_name)
Feature.enabled?(:drop_duplicate_sidekiq_jobs) ||
drop_duplicates_for_queue?(queue_name)
end
private_class_method def self.drop_duplicates_for_queue?(queue_name)
DROPPABLE_QUEUES.include?(queue_name) &&
Feature.enabled?(:drop_duplicate_sidekiq_jobs_for_queue)
end
end
end
......
......@@ -67,7 +67,7 @@ module Gitlab
end
def droppable?
idempotent? && duplicate? && DuplicateJobs.drop_duplicates?
idempotent? && duplicate? && DuplicateJobs.drop_duplicates?(queue_name)
end
private
......
......@@ -9166,18 +9166,6 @@ msgstr ""
msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
msgid "GeoNodes|Learn more about Wiki checksum progress"
msgstr ""
msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
......@@ -9208,6 +9196,9 @@ msgstr ""
msgid "GeoNodes|Removing a Geo secondary node stops the synchronization to that node. Are you sure?"
msgstr ""
msgid "GeoNodes|Replicated data is verified with the %{nodeText} using checksums"
msgstr ""
msgid "GeoNodes|Replication slot WAL"
msgstr ""
......@@ -9217,12 +9208,6 @@ msgstr ""
msgid "GeoNodes|Repositories"
msgstr ""
msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|Repository checksum progress"
msgstr ""
......@@ -9274,16 +9259,16 @@ msgstr ""
msgid "GeoNodes|Wikis"
msgstr ""
msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
msgid "GeoNodes|With %{geo} you can install a special read-only and replicated instance anywhere. Before you add nodes, follow the %{instructions} in the exact order they appear."
msgstr ""
msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
msgid "GeoNodes|With %{geo} you can install a special read-only and replicated instance anywhere. Before you add nodes, follow the %{instructions} in the exact order they appear."
msgid "GeoNodes|primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgid "GeoNodes|secondary nodes"
msgstr ""
msgid "Geo|%{name} is scheduled for forced re-download"
......@@ -22762,9 +22747,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
msgid "Wiki|More Pages"
msgstr ""
msgid "Wiki|New page"
msgstr ""
......@@ -22783,6 +22765,9 @@ msgstr ""
msgid "Wiki|Title"
msgstr ""
msgid "Wiki|View All Pages"
msgstr ""
msgid "Wiki|Wiki Pages"
msgstr ""
......
......@@ -33,9 +33,9 @@ module QA
create_many_branches
create_many_new_files
create_mr_with_many_commits
create_many_issues
methods_arr = [
method(:create_many_issues),
method(:create_many_labels),
method(:create_many_todos),
method(:create_many_merge_requests),
......@@ -104,6 +104,7 @@ module QA
def create_many_new_files
create_a_new_file_api_req("hello.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
30.times do |i|
create_a_new_file_api_req("hello#{i}.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
create_a_new_file_api_req("hello#{i}.txt", "branch#{i}", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
end
......@@ -137,7 +138,7 @@ module QA
16.times do |i|
faker_line_arr = Faker::Lorem.sentences(1500)
content = faker_line_arr.join("\n\r")
create_a_new_file_api_req("hello#{i}.txt", "master", "#{@group_name}%2F#{@project_name}", "Add hello#{i}.txt", content)
create_a_new_file_api_req("hello#{i + 100}.txt", "master", "#{@group_name}%2F#{@project_name}", "Add hello#{i + 100}.txt", content)
content_arr[i] = faker_line_arr
end
......@@ -147,7 +148,7 @@ module QA
missed_line_array = content_arr[i].each_slice(2).map(&:first)
content = missed_line_array.join("\n\rIm new!:D \n\r ")
update_file_api_req("hello#{i}.txt", "performance", "#{@group_name}%2F#{@project_name}", "Update hello#{i}.txt", content)
update_file_api_req("hello#{i + 100}.txt", "performance", "#{@group_name}%2F#{@project_name}", "Update hello#{i + 100}.txt", content)
end
create_mr_response = create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "performance", "master", "Large_MR")
......
# frozen_string_literal: true
require 'rubocop/rspec/top_level_describe'
module RuboCop
module Cop
module Gitlab
# Cop that detects duplicate EE spec files
#
# There should not be files in both ee/spec/*/ee/my_spec.rb and ee/spec/*/my_spec.rb
#
# # bad
# ee/spec/controllers/my_spec.rb # describe MyClass
# ee/spec/controllers/ee/my_spec.rb # describe MyClass
#
# # good, spec for EE extension code
# ee/spec/controllers/ee/my_spec.rb # describe MyClass
#
# # good, spec for EE only code
# ee/spec/controllers/my_spec.rb # describe MyClass
#
class DuplicateSpecLocation < RuboCop::Cop::Cop
include RuboCop::RSpec::TopLevelDescribe
MSG = 'Duplicate spec location in `%<path>s`.'
def on_top_level_describe(node, _args)
path = file_path_for_node(node).sub(/\A#{rails_root}\//, '')
duplicate_path = find_duplicate_path(path)
if duplicate_path && File.exist?(File.join(rails_root, duplicate_path))
add_offense(node, message: format(MSG, path: duplicate_path))
end
end
private
def ee_spec?(path)
File.fnmatch?('ee/spec/**/*.rb', path, File::FNM_PATHNAME)
end
def find_duplicate_path(path)
return unless ee_spec?(path)
if File.fnmatch?('ee/spec/**/ee/**', path)
path.match('\A(ee/spec/[^/]+)/ee/(.+)') do |match|
File.join(match[1], match[2])
end
else
path.match('\A(ee/spec/[^/]+)/(.+)') do |match|
File.join(match[1], 'ee', match[2])
end
end
end
def file_path_for_node(node)
node.location.expression.source_buffer.name
end
def rails_root
File.expand_path('../../..', __dir__)
end
end
end
end
end
......@@ -21,7 +21,13 @@ GITLAB_DOCS_REPO = 'gitlab-org/gitlab-docs'.freeze
# kicked the review app.
#
def docs_branch
# Check if CI_MERGE_REQUEST_IID is present. This requires pipelines
# for merge requests to be enabled.
if ENV["CI_MERGE_REQUEST_IID"].nil?
"docs-preview-#{slug}-#{ENV["CI_COMMIT_REF_SLUG"]}"
else
"docs-preview-#{slug}-#{ENV["CI_MERGE_REQUEST_IID"]}"
end
end
#
......
......@@ -312,7 +312,6 @@ describe "User creates wiki page" do
visit(project_wikis_path(project))
expect(page).to have_content('another')
expect(page).to have_content('More Pages')
end
context 'when there is a customized sidebar' do
......@@ -324,10 +323,23 @@ describe "User creates wiki page" do
visit(project_wikis_path(project))
expect(page).to have_content('My customized sidebar')
expect(page).to have_content('More Pages')
expect(page).not_to have_content('Another')
end
end
end
context 'when there are more than 15 existing pages' do
before do
create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'home' })
(1..14).each { |i| create(:wiki_page, wiki: wiki, attrs: { title: "page-#{i}", content: "page #{i}" }) }
end
it 'renders a default sidebar when there is no customized sidebar' do
visit(project_wikis_path(project))
expect(page).to have_content('View All Pages')
expect(page).to have_content('page 1')
end
end
end
end
Element.prototype.scrollBy = jest.fn();
import './element_scroll_into_view';
import './element_scroll_by';
import './form_element';
import './get_client_rects';
import './inner_text';
......
......@@ -2,25 +2,28 @@
exports[`Repository table row component renders table row 1`] = `
<tr
class="tree-item file_1"
class="tree-item"
>
<td
class="tree-item-file-name"
class="tree-item-file-name cursor-default position-relative"
>
<i
aria-label="file"
class="fa fa-fw fa-file-text-o"
role="img"
/>
<!---->
<a
class="str-truncated"
class="tree-item-link str-truncated"
data-qa-selector="file_name_link"
href="https://test.com"
>
<i
aria-label="file"
class="fa fa-fw mr-1 fa-file-text-o"
role="img"
/>
<span
class="position-relative"
>
test
</span>
</a>
<!---->
......@@ -31,7 +34,7 @@ exports[`Repository table row component renders table row 1`] = `
</td>
<td
class="d-none d-sm-table-cell tree-commit"
class="d-none d-sm-table-cell tree-commit cursor-default"
>
<gl-skeleton-loading-stub
class="h-auto"
......@@ -40,7 +43,7 @@ exports[`Repository table row component renders table row 1`] = `
</td>
<td
class="tree-time-ago text-right"
class="tree-time-ago text-right cursor-default"
>
<gl-skeleton-loading-stub
class="ml-auto h-auto w-50"
......@@ -52,25 +55,28 @@ exports[`Repository table row component renders table row 1`] = `
exports[`Repository table row component renders table row for path with special character 1`] = `
<tr
class="tree-item file_1"
class="tree-item"
>
<td
class="tree-item-file-name"
class="tree-item-file-name cursor-default position-relative"
>
<i
aria-label="file"
class="fa fa-fw fa-file-text-o"
role="img"
/>
<!---->
<a
class="str-truncated"
class="tree-item-link str-truncated"
data-qa-selector="file_name_link"
href="https://test.com"
>
<i
aria-label="file"
class="fa fa-fw mr-1 fa-file-text-o"
role="img"
/>
<span
class="position-relative"
>
test
</span>
</a>
<!---->
......@@ -81,7 +87,7 @@ exports[`Repository table row component renders table row for path with special
</td>
<td
class="d-none d-sm-table-cell tree-commit"
class="d-none d-sm-table-cell tree-commit cursor-default"
>
<gl-skeleton-loading-stub
class="h-auto"
......@@ -90,7 +96,7 @@ exports[`Repository table row component renders table row for path with special
</td>
<td
class="tree-time-ago text-right"
class="tree-time-ago text-right cursor-default"
>
<gl-skeleton-loading-stub
class="ml-auto h-auto w-50"
......
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlBadge, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue';
import Icon from '~/vue_shared/components/icon.vue';
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
visitUrl: jest.fn(),
}));
let vm;
let $router;
......@@ -87,31 +81,6 @@ describe('Repository table row component', () => {
});
});
it.each`
type | pushes
${'tree'} | ${true}
${'file'} | ${false}
${'commit'} | ${false}
`('pushes new router if type $type is tree', ({ type, pushes }) => {
factory({
id: '1',
sha: '123',
path: 'test',
type,
currentPath: '/',
});
return vm.vm.$nextTick().then(() => {
vm.trigger('click');
if (pushes) {
expect($router.push).toHaveBeenCalledWith({ path: '/-/tree/master/test' });
} else {
expect($router.push).not.toHaveBeenCalled();
}
});
});
it.each`
path
${'test#'}
......@@ -132,7 +101,7 @@ describe('Repository table row component', () => {
});
});
it('pushes new route for directory with hash', () => {
it('renders link for directory with hash', () => {
factory({
id: '1',
sha: '123',
......@@ -142,36 +111,7 @@ describe('Repository table row component', () => {
});
return vm.vm.$nextTick().then(() => {
vm.trigger('click');
expect($router.push).toHaveBeenCalledWith({ path: '/-/tree/master/test%23' });
});
});
it.each`
type | pushes
${'tree'} | ${true}
${'file'} | ${false}
${'commit'} | ${false}
`('calls visitUrl if $type is not tree', ({ type, pushes }) => {
factory({
id: '1',
sha: '123',
path: 'test',
type,
currentPath: '/',
});
return vm.vm.$nextTick().then(() => {
vm.trigger('click');
if (pushes) {
expect(visitUrl).not.toHaveBeenCalled();
} else {
const [url, external] = visitUrl.mock.calls[0];
expect(url).toBe('https://test.com');
expect(external).toBeFalsy();
}
expect(vm.find('.tree-item-link').props('to')).toEqual({ path: '/-/tree/master/test%23' });
});
});
......
......@@ -18,6 +18,16 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
.and_return issue_details_service
end
shared_examples 'it resolves to nil' do
it 'resolves to nil' do
allow(issue_details_service).to receive(:execute)
.and_return(issue: nil)
result = resolve_error(args)
expect(result).to be_nil
end
end
describe '#resolve' do
let(:args) { { id: issue_global_id(1234) } }
......@@ -32,7 +42,7 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
before do
allow(issue_details_service).to receive(:execute)
.and_return({ issue: detailed_error })
.and_return(issue: detailed_error)
end
it 'resolves to a detailed error' do
......@@ -44,12 +54,14 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
end
end
it 'resolves to nil if no match' do
allow(issue_details_service).to receive(:execute)
.and_return({ issue: nil })
context 'if id does not match issue' do
it_behaves_like 'it resolves to nil'
end
result = resolve_error(args)
expect(result).to eq nil
context 'blank id' do
let(:args) { { id: '' } }
it_behaves_like 'it resolves to nil'
end
end
......
......@@ -76,10 +76,10 @@ describe Gitlab::Danger::Changelog do
context 'added files contain a changelog' do
[
'changelogs/unreleased/entry.md',
'ee/changelogs/unreleased/entry.md',
'changelogs/unreleased-ee/entry.md',
'ee/changelogs/unreleased-ee/entry.md'
'changelogs/unreleased/entry.yml',
'ee/changelogs/unreleased/entry.yml',
'changelogs/unreleased-ee/entry.yml',
'ee/changelogs/unreleased-ee/entry.yml'
].each do |file_path|
let(:added_files) { [file_path] }
......@@ -107,46 +107,22 @@ describe Gitlab::Danger::Changelog do
end
describe '#ee_changelog?' do
subject { changelog.ee_changelog? }
before do
allow(changelog).to receive(:found).and_return(file_path)
end
context 'is ee changelog' do
[
'changelogs/unreleased-ee/entry.md',
'ee/changelogs/unreleased-ee/entry.md'
].each do |file_path|
subject { changelog.ee_changelog?(file_path) }
let(:file_path) { 'ee/changelogs/unreleased/entry.yml' }
it { is_expected.to be_truthy }
end
end
context 'is not ee changelog' do
[
'changelogs/unreleased/entry.md',
'ee/changelogs/unreleased/entry.md'
].each do |file_path|
subject { changelog.ee_changelog?(file_path) }
let(:file_path) { 'changelogs/unreleased/entry.yml' }
it { is_expected.to be_falsy }
end
end
end
describe '#ce_port_changelog?' do
where(:helper_ee?, :file_path, :expected) do
true | 'changelogs/unreleased-ee/entry.md' | false
true | 'ee/changelogs/unreleased-ee/entry.md' | false
false | 'changelogs/unreleased-ee/entry.md' | false
false | 'ee/changelogs/unreleased-ee/entry.md' | false
true | 'changelogs/unreleased/entry.md' | true
true | 'ee/changelogs/unreleased/entry.md' | true
false | 'changelogs/unreleased/entry.md' | false
false | 'ee/changelogs/unreleased/entry.md' | false
end
with_them do
let(:ee?) { helper_ee? }
subject { changelog.ce_port_changelog?(file_path) }
it { is_expected.to eq(expected) }
end
end
end
......@@ -76,6 +76,16 @@ describe Gitlab::Danger::Helper do
end
end
describe '#all_ee_changes' do
subject { helper.all_ee_changes }
it 'returns all changed files starting with ee/' do
expect(helper).to receive(:all_changed_files).and_return(%w[fr/ee/beer.rb ee/wine.rb ee/lib/ido.rb ee.k])
is_expected.to match_array(%w[ee/wine.rb ee/lib/ido.rb])
end
end
describe '#ee?' do
subject { helper.ee? }
......
......@@ -96,14 +96,25 @@ describe Gitlab::FileDetector do
'swagger.yml', 'swagger.yaml', 'swagger.json',
'gitlab_swagger.yml', 'openapi_gitlab.yml',
'OpenAPI.YML', 'openapi.Yaml', 'openapi.JSON',
'openapi.gitlab.yml', 'gitlab.openapi.yml'
'openapi.gitlab.yml', 'gitlab.openapi.yml',
'attention/openapi.yml', 'attention/swagger.yml',
'attention/gitlab_swagger.yml', 'attention/openapi_gitlab.yml',
'openapi/openapi.yml', 'openapi/swagger.yml',
'openapi/my_openapi.yml', 'openapi/swagger_one.yml'
]
openapi_types.each do |type_name|
expect(described_class.type_of(type_name)).to eq(:openapi)
end
expect(described_class.type_of('openapiyml')).to be_nil
openapi_bad_types = [
'openapiyml',
'openapi/attention.yaml', 'swagger/attention.yaml'
]
openapi_bad_types.each do |type_name|
expect(described_class.type_of(type_name)).to be_nil
end
end
end
end
......@@ -15,7 +15,7 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do
context 'when just the dashboard path is provided' do
let(:arguments) { { dashboard_path: '.gitlab/dashboards/test.yml' } }
it { is_expected.to be Metrics::Dashboard::ProjectDashboardService }
it { is_expected.to be Metrics::Dashboard::CustomDashboardService }
context 'when the path is for the system dashboard' do
let(:arguments) { { dashboard_path: system_dashboard_path } }
......
......@@ -129,7 +129,8 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gitlab_r
before do
allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(idempotent)
allow(duplicate_job).to receive(:duplicate?).and_return(duplicate)
stub_feature_flags(drop_duplicate_sidekiq_jobs: feature_enabled)
allow(Gitlab::SidekiqMiddleware::DuplicateJobs)
.to receive(:drop_duplicates?).with(queue).and_return(feature_enabled)
end
it 'is droppable when all conditions are met' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::SidekiqMiddleware::DuplicateJobs do
using RSpec::Parameterized::TableSyntax
describe '.drop_duplicates?' do
where(:global_feature_enabled, :selected_queue_enabled, :queue, :expected) do
true | true | described_class::DROPPABLE_QUEUES.first | true
true | true | "other_queue" | true
true | false | described_class::DROPPABLE_QUEUES.first | true
true | false | "other_queue" | true
false | true | described_class::DROPPABLE_QUEUES.first | true
false | true | "other_queue" | false
false | false | described_class::DROPPABLE_QUEUES.first | false
false | false | "other_queue" | false
end
with_them do
before do
stub_feature_flags(drop_duplicate_sidekiq_jobs: global_feature_enabled,
drop_duplicate_sidekiq_jobs_for_queue: selected_queue_enabled)
end
it "allows dropping jobs when expected" do
expect(described_class.drop_duplicates?(queue)).to be(expected)
end
end
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/duplicate_spec_location'
describe RuboCop::Cop::Gitlab::DuplicateSpecLocation do
include RuboCop::RSpec::ExpectOffense
include CopHelper
subject(:cop) { described_class.new }
let(:rails_root) { '../../../../' }
def full_path(path)
File.expand_path(File.join(rails_root, path), __dir__)
end
context 'Non-EE spec file' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, full_path('spec/foo_spec.rb'))
describe 'Foo' do
end
SOURCE
end
end
context 'Non-EE application file' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, full_path('app/models/blog_post.rb'))
class BlogPost
end
SOURCE
end
end
context 'EE application file' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, full_path('ee/app/models/blog_post.rb'))
class BlogPost
end
SOURCE
end
end
context 'EE spec file for EE only code' do
let(:spec_file_path) { full_path('ee/spec/controllers/foo_spec.rb') }
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
end
SOURCE
end
context 'there is a duplicate file' do
before do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?)
.with(full_path('ee/spec/controllers/ee/foo_spec.rb'))
.and_return(true)
end
it 'marks the describe as offending' do
expect_offense(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
^^^^^^^^^^^^^^ Duplicate spec location in `ee/spec/controllers/ee/foo_spec.rb`.
end
SOURCE
end
end
end
context 'EE spec file for EE extension' do
let(:spec_file_path) { full_path('ee/spec/controllers/ee/foo_spec.rb') }
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
end
SOURCE
end
context 'there is a duplicate file' do
before do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?)
.with(full_path('ee/spec/controllers/foo_spec.rb'))
.and_return(true)
end
it 'marks the describe as offending' do
expect_offense(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
^^^^^^^^^^^^^^ Duplicate spec location in `ee/spec/controllers/foo_spec.rb`.
end
SOURCE
end
end
end
end
......@@ -112,9 +112,6 @@ describe Issuable::Clone::AttributesRewriter do
expect(event.milestone_id).to eq(expected_attrs[:milestone].id)
expect(event.action).to eq(expected_attrs[:action])
expect(event.state).to eq(expected_attrs[:state])
expect(event.reference).to be_nil
expect(event.reference_html).to be_nil
end
end
end
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe Metrics::Dashboard::ProjectDashboardService, :use_clean_rails_memory_store_caching do
describe Metrics::Dashboard::CustomDashboardService, :use_clean_rails_memory_store_caching do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
......
......@@ -74,4 +74,19 @@ describe Namespaces::RootStatisticsWorker, '#perform' do
worker.perform(group.id)
end
end
it_behaves_like 'an idempotent worker' do
let(:job_args) { [group.id] }
it 'deletes one aggregation schedule' do
# Make sure the group and it's aggregation schedule are created before
# counting
group
expect { worker.perform(*job_args) }
.to change { Namespace::AggregationSchedule.count }.by(-1)
expect { worker.perform(*job_args) }
.not_to change { Namespace::AggregationSchedule.count }
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment