Commit 135a02cf authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into ph-axios-2

parents b1c8b3aa 5b73e0eb
...@@ -704,7 +704,9 @@ Style/RedundantSelf: ...@@ -704,7 +704,9 @@ Style/RedundantSelf:
# Configuration parameters: EnforcedStyle, AllowInnerSlashes. # Configuration parameters: EnforcedStyle, AllowInnerSlashes.
# SupportedStyles: slashes, percent_r, mixed # SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral: Style/RegexpLiteral:
Enabled: false Enabled: true
EnforcedStyle: mixed
AllowInnerSlashes: false
# Offense count: 36 # Offense count: 36
# Cop supports --auto-correct. # Cop supports --auto-correct.
......
...@@ -340,7 +340,7 @@ GEM ...@@ -340,7 +340,7 @@ GEM
mime-types (~> 3.0) mime-types (~> 3.0)
representable (~> 3.0) representable (~> 3.0)
retriable (>= 2.0, < 4.0) retriable (>= 2.0, < 4.0)
google-protobuf (3.5.1.1-universal-darwin) google-protobuf (3.5.1.1)
googleapis-common-protos-types (1.0.1) googleapis-common-protos-types (1.0.1)
google-protobuf (~> 3.0) google-protobuf (~> 3.0)
googleauth (0.5.3) googleauth (0.5.3)
...@@ -369,7 +369,7 @@ GEM ...@@ -369,7 +369,7 @@ GEM
rake rake
grape_logging (1.7.0) grape_logging (1.7.0)
grape grape
grpc (1.8.3-universal-darwin) grpc (1.8.3)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0) googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7) googleauth (>= 0.5.1, < 0.7)
......
/* eslint-disable class-methods-use-this */ /* eslint-disable class-methods-use-this */
import _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { s__ } from './locale'; import { __ } from './locale';
import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils'; import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils';
import flash from './flash'; import flash from './flash';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
...@@ -451,7 +451,7 @@ class AwardsHandler { ...@@ -451,7 +451,7 @@ class AwardsHandler {
callback(); callback();
} }
}) })
.catch(() => flash(s__('Something went wrong on our end.'))); .catch(() => flash(__('Something went wrong on our end.')));
} }
} }
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, object-shorthand, no-param-reassign, comma-dangle, prefer-template, no-unused-vars, no-return-assign */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, object-shorthand, no-param-reassign, comma-dangle, prefer-template, no-unused-vars, no-return-assign */
import fuzzaldrinPlus from 'fuzzaldrin-plus'; import fuzzaldrinPlus from 'fuzzaldrin-plus';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
const highlighter = function(element, text, matches) { const highlighter = function(element, text, matches) {
...@@ -72,19 +75,14 @@ export default class ProjectFindFile { ...@@ -72,19 +75,14 @@ export default class ProjectFindFile {
// files pathes load // files pathes load
load(url) { load(url) {
return $.ajax({ axios.get(url)
url: url, .then(({ data }) => {
method: "get", this.element.find('.loading').hide();
dataType: "json", this.filePaths = data;
success: (function(_this) { this.findFile();
return function(data) { this.element.find('.files-slider tr.tree-item').eq(0).addClass('selected').focus();
_this.element.find(".loading").hide(); })
_this.filePaths = data; .catch(() => flash(__('An error occurred while loading filenames')));
_this.findFile();
return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
};
})(this)
});
} }
// render result // render result
......
...@@ -7,7 +7,12 @@ ...@@ -7,7 +7,12 @@
// //
// <code class="js-render-math"></div> // <code class="js-render-math"></div>
// //
// Only load once
import { __ } from './locale';
import axios from './lib/utils/axios_utils';
import flash from './flash';
// Only load once
let katexLoaded = false; let katexLoaded = false;
// Loop over all math elements and render math // Loop over all math elements and render math
...@@ -33,19 +38,26 @@ export default function renderMath($els) { ...@@ -33,19 +38,26 @@ export default function renderMath($els) {
if (katexLoaded) { if (katexLoaded) {
renderWithKaTeX($els); renderWithKaTeX($els);
} else { } else {
$.get(gon.katex_css_url, () => { axios.get(gon.katex_css_url)
.then(() => {
const css = $('<link>', { const css = $('<link>', {
rel: 'stylesheet', rel: 'stylesheet',
type: 'text/css', type: 'text/css',
href: gon.katex_css_url, href: gon.katex_css_url,
}); });
css.appendTo('head'); css.appendTo('head');
})
// Load KaTeX js .then(() => axios.get(gon.katex_js_url, {
$.getScript(gon.katex_js_url, () => { responseType: 'text',
}))
.then(({ data }) => {
// Add katex js to our document
$.globalEval(data);
})
.then(() => {
katexLoaded = true; katexLoaded = true;
renderWithKaTeX($els); // Run KaTeX renderWithKaTeX($els); // Run KaTeX
}); })
}); .catch(() => flash(__('An error occurred while rendering KaTeX')));
} }
} }
import _ from 'underscore'; import _ from 'underscore';
import { scaleLinear, scaleThreshold } from 'd3-scale'; import { scaleLinear, scaleThreshold } from 'd3-scale';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import { getDayName, getDayDifference } from '../lib/utils/datetime_utility'; import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
const d3 = { select, scaleLinear, scaleThreshold }; const d3 = { select, scaleLinear, scaleThreshold };
...@@ -221,14 +224,16 @@ export default class ActivityCalendar { ...@@ -221,14 +224,16 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(), this.currentSelectedDate.getDate(),
].join('-'); ].join('-');
$.ajax({ $('.user-calendar-activities').html(LOADING_HTML);
url: this.calendarActivitiesPath,
data: { date }, axios.get(this.calendarActivitiesPath, {
cache: false, params: {
dataType: 'html', date,
beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML), },
success: data => $('.user-calendar-activities').html(data), responseType: 'text',
}); })
.then(({ data }) => $('.user-calendar-activities').html(data))
.catch(() => flash(__('An error occurred while retrieving calendar activity')));
} else { } else {
this.currentSelectedDate = ''; this.currentSelectedDate = '';
$('.user-calendar-activities').html(''); $('.user-calendar-activities').html('');
......
export default {
name: 'MRWidgetRelatedLinks',
props: {
relatedLinks: { type: Object, required: true },
state: { type: String, required: false },
},
computed: {
hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe;
},
closesText() {
if (this.state === 'merged') {
return 'Closed';
}
if (this.state === 'closed') {
return 'Did not close';
}
return 'Closes';
},
},
template: `
<section
v-if="hasLinks"
class="mr-info-list mr-links">
<p v-if="relatedLinks.closing">
{{closesText}} <span v-html="relatedLinks.closing"></span>
</p>
<p v-if="relatedLinks.mentioned">
Mentions <span v-html="relatedLinks.mentioned"></span>
</p>
<p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span>
</p>
</section>
`,
};
<script>
import { s__ } from '~/locale';
export default {
name: 'MRWidgetRelatedLinks',
props: {
relatedLinks: {
type: Object,
required: true,
default: () => ({}),
},
state: {
type: String,
required: false,
default: '',
},
},
computed: {
closesText() {
if (this.state === 'merged') {
return s__('mrWidget|Closed');
}
if (this.state === 'closed') {
return s__('mrWidget|Did not close');
}
return s__('mrWidget|Closes');
},
},
};
</script>
<template>
<section class="mr-info-list mr-links">
<p v-if="relatedLinks.closing">
{{ closesText }} <span v-html="relatedLinks.closing"></span>
</p>
<p v-if="relatedLinks.mentioned">
{{ s__("mrWidget|Mentions") }} <span v-html="relatedLinks.mentioned"></span>
</p>
<p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span>
</p>
</section>
</template>
...@@ -15,7 +15,7 @@ export { default as WidgetHeader } from './components/mr_widget_header'; ...@@ -15,7 +15,7 @@ export { default as WidgetHeader } from './components/mr_widget_header';
export { default as WidgetMergeHelp } from './components/mr_widget_merge_help'; export { default as WidgetMergeHelp } from './components/mr_widget_merge_help';
export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue'; export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
export { default as WidgetDeployment } from './components/mr_widget_deployment'; export { default as WidgetDeployment } from './components/mr_widget_deployment';
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links'; export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue';
export { default as MergedState } from './components/states/mr_widget_merged.vue'; export { default as MergedState } from './components/states/mr_widget_merged.vue';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue'; export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue';
export { default as ClosedState } from './components/states/mr_widget_closed.vue'; export { default as ClosedState } from './components/states/mr_widget_closed.vue';
......
...@@ -257,7 +257,8 @@ export default { ...@@ -257,7 +257,8 @@ export default {
<mr-widget-related-links <mr-widget-related-links
v-if="shouldRenderRelatedLinks" v-if="shouldRenderRelatedLinks"
:state="mr.state" :state="mr.state"
:related-links="mr.relatedLinks" /> :related-links="mr.relatedLinks"
/>
</div> </div>
<div <div
class="mr-widget-footer" class="mr-widget-footer"
......
...@@ -94,6 +94,7 @@ module IssuableCollections ...@@ -94,6 +94,7 @@ module IssuableCollections
@filter_params[:project_id] = @project.id @filter_params[:project_id] = @project.id
elsif @group elsif @group
@filter_params[:group_id] = @group.id @filter_params[:group_id] = @group.id
@filter_params[:include_subgroups] = true
else else
# TODO: this filter ignore issues/mr created in public or # TODO: this filter ignore issues/mr created in public or
# internal repos where you are not a member. Enable this filter # internal repos where you are not a member. Enable this filter
......
...@@ -118,10 +118,10 @@ class GroupsController < Groups::ApplicationController ...@@ -118,10 +118,10 @@ class GroupsController < Groups::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(group_params_ce) params.require(:group).permit(group_params_attributes)
end end
def group_params_ce def group_params_attributes
[ [
:avatar, :avatar,
:description, :description,
......
...@@ -5,7 +5,7 @@ class HelpController < ApplicationController ...@@ -5,7 +5,7 @@ class HelpController < ApplicationController
# Taken from Jekyll # Taken from Jekyll
# https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13 # https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13
YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
def index def index
# Remove YAML frontmatter so that it doesn't look weird # Remove YAML frontmatter so that it doesn't look weird
......
...@@ -403,6 +403,6 @@ class ProjectsController < Projects::ApplicationController ...@@ -403,6 +403,6 @@ class ProjectsController < Projects::ApplicationController
# to # to
# localhost/group/project # localhost/group/project
# #
redirect_to request.original_url.sub(/\.git\/?\Z/, '') if params[:format] == 'git' redirect_to request.original_url.sub(%r{\.git/?\Z}, '') if params[:format] == 'git'
end end
end end
...@@ -87,9 +87,18 @@ class GroupProjectsFinder < ProjectsFinder ...@@ -87,9 +87,18 @@ class GroupProjectsFinder < ProjectsFinder
options.fetch(:only_shared, false) options.fetch(:only_shared, false)
end end
# subgroups are supported only for owned projects not for shared
def include_subgroups?
options.fetch(:include_subgroups, false)
end
def owned_projects def owned_projects
if include_subgroups?
Project.where(namespace_id: group.self_and_descendants.select(:id))
else
group.projects group.projects
end end
end
def shared_projects def shared_projects
group.shared_projects group.shared_projects
......
...@@ -43,6 +43,7 @@ class IssuableFinder ...@@ -43,6 +43,7 @@ class IssuableFinder
search search
sort sort
state state
include_subgroups
].freeze ].freeze
ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze
...@@ -148,7 +149,8 @@ class IssuableFinder ...@@ -148,7 +149,8 @@ class IssuableFinder
if current_user && params[:authorized_only].presence && !current_user_related? if current_user && params[:authorized_only].presence && !current_user_related?
current_user.authorized_projects current_user.authorized_projects
elsif group elsif group
GroupProjectsFinder.new(group: group, current_user: current_user).execute finder_options = { include_subgroups: params[:include_subgroups], only_owned: true }
GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute
else else
ProjectsFinder.new(current_user: current_user, project_ids_relation: item_project_ids(items)).execute ProjectsFinder.new(current_user: current_user, project_ids_relation: item_project_ids(items)).execute
end end
......
module SidekiqHelper module SidekiqHelper
SIDEKIQ_PS_REGEXP = /\A SIDEKIQ_PS_REGEXP = %r{\A
(?<pid>\d+)\s+ (?<pid>\d+)\s+
(?<cpu>[\d\.,]+)\s+ (?<cpu>[\d\.,]+)\s+
(?<mem>[\d\.,]+)\s+ (?<mem>[\d\.,]+)\s+
(?<state>[DIEKNRSTVWXZNLpsl\+<>\/\d]+)\s+ (?<state>[DIEKNRSTVWXZNLpsl\+<>/\d]+)\s+
(?<start>.+?)\s+ (?<start>.+?)\s+
(?<command>(?:ruby\d+:\s+)?sidekiq.*\].*) (?<command>(?:ruby\d+:\s+)?sidekiq.*\].*)
\z/x \z}x
def parse_sidekiq_ps(line) def parse_sidekiq_ps(line)
match = line.strip.match(SIDEKIQ_PS_REGEXP) match = line.strip.match(SIDEKIQ_PS_REGEXP)
......
...@@ -11,7 +11,7 @@ module SubmoduleHelper ...@@ -11,7 +11,7 @@ module SubmoduleHelper
url = File.join(Gitlab.config.gitlab.url, @project.full_path) url = File.join(Gitlab.config.gitlab.url, @project.full_path)
end end
if url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/ if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z}
namespace, project = $1, $2 namespace, project = $1, $2
gitlab_hosts = [Gitlab.config.gitlab.url, gitlab_hosts = [Gitlab.config.gitlab.url,
Gitlab.config.gitlab_shell.ssh_path_prefix] Gitlab.config.gitlab_shell.ssh_path_prefix]
...@@ -23,7 +23,7 @@ module SubmoduleHelper ...@@ -23,7 +23,7 @@ module SubmoduleHelper
end end
end end
namespace.sub!(/\A\//, '') namespace.sub!(%r{\A/}, '')
project.rstrip! project.rstrip!
project.sub!(/\.git\z/, '') project.sub!(/\.git\z/, '')
...@@ -47,11 +47,11 @@ module SubmoduleHelper ...@@ -47,11 +47,11 @@ module SubmoduleHelper
protected protected
def github_dot_com_url?(url) def github_dot_com_url?(url)
url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/ url =~ %r{github\.com[/:][^/]+/[^/]+\Z}
end end
def gitlab_dot_com_url?(url) def gitlab_dot_com_url?(url)
url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/ url =~ %r{gitlab\.com[/:][^/]+/[^/]+\Z}
end end
def self_url?(url, namespace, project) def self_url?(url, namespace, project)
...@@ -65,7 +65,7 @@ module SubmoduleHelper ...@@ -65,7 +65,7 @@ module SubmoduleHelper
def relative_self_url?(url) def relative_self_url?(url)
# (./)?(../repo.git) || (./)?(../../project/repo.git) ) # (./)?(../repo.git) || (./)?(../../project/repo.git) )
url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*(\.git)?\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*(\.git)?\z/ url =~ %r{\A((\./)?(\.\./))(?!(\.\.)|(.*/)).*(\.git)?\z} || url =~ %r{\A((\./)?(\.\./){2})(?!(\.\.))([^/]*)/(?!(\.\.)|(.*/)).*(\.git)?\z}
end end
def standard_links(host, namespace, project, commit) def standard_links(host, namespace, project, commit)
......
...@@ -110,7 +110,7 @@ module TreeHelper ...@@ -110,7 +110,7 @@ module TreeHelper
# returns the relative path of the first subdir that doesn't have only one directory descendant # returns the relative path of the first subdir that doesn't have only one directory descendant
def flatten_tree(root_path, tree) def flatten_tree(root_path, tree)
return tree.flat_path.sub(/\A#{root_path}\//, '') if tree.flat_path.present? return tree.flat_path.sub(%r{\A#{root_path}/}, '') if tree.flat_path.present?
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path) subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
if subtree.count == 1 && subtree.first.dir? if subtree.count == 1 && subtree.first.dir?
......
...@@ -292,7 +292,7 @@ module Ci ...@@ -292,7 +292,7 @@ module Ci
def repo_url def repo_url
auth = "gitlab-ci-token:#{ensure_token!}@" auth = "gitlab-ci-token:#{ensure_token!}@"
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix| project.http_url_to_repo.sub(%r{^https?://}) do |prefix|
prefix + auth prefix + auth
end end
end end
......
...@@ -141,7 +141,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -141,7 +141,7 @@ class CommitStatus < ActiveRecord::Base
end end
def group_name def group_name
name.to_s.gsub(/\d+[\s:\/\\]+\d+\s*/, '').strip name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
end end
def failed_but_allowed? def failed_but_allowed?
......
...@@ -9,13 +9,13 @@ require 'task_list/filter' ...@@ -9,13 +9,13 @@ require 'task_list/filter'
module Taskable module Taskable
COMPLETED = 'completed'.freeze COMPLETED = 'completed'.freeze
INCOMPLETE = 'incomplete'.freeze INCOMPLETE = 'incomplete'.freeze
ITEM_PATTERN = / ITEM_PATTERN = %r{
^ ^
\s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list \s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list
\s+ # whitespace prefix has to be always presented for a list item \s+ # whitespace prefix has to be always presented for a list item
(\[\s\]|\[[xX]\]) # checkbox (\[\s\]|\[[xX]\]) # checkbox
(\s.+) # followed by whitespace and some text. (\s.+) # followed by whitespace and some text.
/x }x
def self.get_tasks(content) def self.get_tasks(content)
content.to_s.scan(ITEM_PATTERN).map do |checkbox, label| content.to_s.scan(ITEM_PATTERN).map do |checkbox, label|
......
...@@ -115,7 +115,7 @@ class Environment < ActiveRecord::Base ...@@ -115,7 +115,7 @@ class Environment < ActiveRecord::Base
def formatted_external_url def formatted_external_url
return nil unless external_url return nil unless external_url
external_url.gsub(/\A.*?:\/\//, '') external_url.gsub(%r{\A.*?://}, '')
end end
def stop_action? def stop_action?
......
...@@ -234,7 +234,7 @@ class Project < ActiveRecord::Base ...@@ -234,7 +234,7 @@ class Project < ActiveRecord::Base
validates :creator, presence: true, on: :create validates :creator, presence: true, on: :create
validates :description, length: { maximum: 2000 }, allow_blank: true validates :description, length: { maximum: 2000 }, allow_blank: true
validates :ci_config_path, validates :ci_config_path,
format: { without: /(\.{2}|\A\/)/, format: { without: %r{(\.{2}|\A/)},
message: 'cannot include leading slash or directory traversal.' }, message: 'cannot include leading slash or directory traversal.' },
length: { maximum: 255 }, length: { maximum: 255 },
allow_blank: true allow_blank: true
...@@ -1338,7 +1338,7 @@ class Project < ActiveRecord::Base ...@@ -1338,7 +1338,7 @@ class Project < ActiveRecord::Base
host = "#{subdomain}.#{Settings.pages.host}".downcase host = "#{subdomain}.#{Settings.pages.host}".downcase
# The host in URL always needs to be downcased # The host in URL always needs to be downcased
url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix| url = Gitlab.config.pages.url.sub(%r{^https?://}) do |prefix|
"#{prefix}#{subdomain}." "#{prefix}#{subdomain}."
end.downcase end.downcase
......
...@@ -84,7 +84,7 @@ http://app.asana.com/-/account_api' ...@@ -84,7 +84,7 @@ http://app.asana.com/-/account_api'
# - fix/ed/es/ing # - fix/ed/es/ing
# - close/s/d # - close/s/d
# - closing # - closing
issue_finder = /(fix\w*|clos[ei]\w*+)?\W*(?:https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)|#(\d+))/i issue_finder = %r{(fix\w*|clos[ei]\w*+)?\W*(?:https://app\.asana\.com/\d+/\d+/(\d+)|#(\d+))}i
message.scan(issue_finder).each do |tuple| message.scan(issue_finder).each do |tuple|
# tuple will be # tuple will be
......
...@@ -10,9 +10,9 @@ class IssueTrackerService < Service ...@@ -10,9 +10,9 @@ class IssueTrackerService < Service
# overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN # overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN
def self.reference_pattern(only_long: false) def self.reference_pattern(only_long: false)
if only_long if only_long
%r{(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)} /(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)/
else else
%r{(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)} /(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)/
end end
end end
......
...@@ -19,7 +19,7 @@ class JiraService < IssueTrackerService ...@@ -19,7 +19,7 @@ class JiraService < IssueTrackerService
# {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1 # {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1
def self.reference_pattern(only_long: true) def self.reference_pattern(only_long: true)
@reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)} @reference_pattern ||= /(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)/
end end
def initialize_properties def initialize_properties
......
...@@ -842,13 +842,13 @@ class User < ActiveRecord::Base ...@@ -842,13 +842,13 @@ class User < ActiveRecord::Base
end end
def full_website_url def full_website_url
return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\// return "http://#{website_url}" if website_url !~ %r{\Ahttps?://}
website_url website_url
end end
def short_website_url def short_website_url
website_url.sub(/\Ahttps?:\/\//, '') website_url.sub(%r{\Ahttps?://}, '')
end end
def all_ssh_keys def all_ssh_keys
......
---
title: Include subgroup issues and merge requests on the group page
merge_request:
author:
type: changed
---
title: Enable RuboCop Style/RegexpLiteral
merge_request: 16752
author: Takuya Noguchi
type: other
---
title: Update minimum git version to 2.9.5
merge_request: 16683
author:
type: other
...@@ -110,7 +110,7 @@ class Settings < Settingslogic ...@@ -110,7 +110,7 @@ class Settings < Settingslogic
url = "http://#{url}" unless url.start_with?('http') url = "http://#{url}" unless url.start_with?('http')
# Get rid of the path so that we don't even have to encode it # Get rid of the path so that we don't even have to encode it
url_without_path = url.sub(%r{(https?://[^\/]+)/?.*}, '\1') url_without_path = url.sub(%r{(https?://[^/]+)/?.*}, '\1')
URI.parse(url_without_path).host URI.parse(url_without_path).host
end end
...@@ -469,10 +469,10 @@ end ...@@ -469,10 +469,10 @@ end
# repository_downloads_path value. # repository_downloads_path value.
# #
repositories_storages = Settings.repositories.storages.values repositories_storages = Settings.repositories.storages.values
repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(/\/$/, '') repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(%r{/$}, '')
repository_downloads_full_path = File.expand_path(repository_downloads_path, Settings.gitlab['user_home']) repository_downloads_full_path = File.expand_path(repository_downloads_path, Settings.gitlab['user_home'])
if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs['path'].gsub(/\/$/, '')) } if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs['path'].gsub(%r{/$}, '')) }
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive')
end end
......
...@@ -54,7 +54,7 @@ elsif Gitlab::Database.mysql? ...@@ -54,7 +54,7 @@ elsif Gitlab::Database.mysql?
def initialize_type_map(mapping) def initialize_type_map(mapping)
super mapping super mapping
mapping.register_type(%r(timestamp)i) do |sql_type| mapping.register_type(/timestamp/i) do |sql_type|
precision = extract_precision(sql_type) precision = extract_precision(sql_type)
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlDateTimeWithTimeZone.new(precision: precision) ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlDateTimeWithTimeZone.new(precision: precision)
end end
......
namespace :admin do namespace :admin do
resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do resources :users, constraints: { id: %r{[a-zA-Z./0-9_\-]+} } do
resources :keys, only: [:show, :destroy] resources :keys, only: [:show, :destroy]
resources :identities, except: [:show] resources :identities, except: [:show]
resources :impersonation_tokens, only: [:index, :create] do resources :impersonation_tokens, only: [:index, :create] do
......
...@@ -35,7 +35,7 @@ constraints(GroupUrlConstrainer.new) do ...@@ -35,7 +35,7 @@ constraints(GroupUrlConstrainer.new) do
post :toggle_subscription, on: :member post :toggle_subscription, on: :member
end end
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :edit, :update, :new, :create] do resources :milestones, constraints: { id: %r{[^/]+} }, only: [:index, :show, :edit, :update, :new, :create] do
member do member do
get :merge_requests get :merge_requests
get :participants get :participants
...@@ -52,7 +52,7 @@ constraints(GroupUrlConstrainer.new) do ...@@ -52,7 +52,7 @@ constraints(GroupUrlConstrainer.new) do
resources :uploads, only: [:create] do resources :uploads, only: [:create] do
collection do collection do
get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ } get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }
end end
end end
end end
......
...@@ -40,7 +40,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -40,7 +40,7 @@ constraints(ProjectUrlConstrainer.new) do
# #
# Templates # Templates
# #
get '/templates/:template_type/:key' => 'templates#show', as: :template, constraints: { key: /[^\/]+/ } get '/templates/:template_type/:key' => 'templates#show', as: :template, constraints: { key: %r{[^/]+} }
resource :avatar, only: [:show, :destroy] resource :avatar, only: [:show, :destroy]
resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do
...@@ -55,7 +55,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -55,7 +55,7 @@ constraints(ProjectUrlConstrainer.new) do
end end
resource :pages, only: [:show, :destroy] do resource :pages, only: [:show, :destroy] do
resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: /[^\/]+/ } resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: %r{[^/]+} }
end end
resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do
...@@ -65,7 +65,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -65,7 +65,7 @@ constraints(ProjectUrlConstrainer.new) do
end end
end end
resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do resources :services, constraints: { id: %r{[^/]+} }, only: [:index, :edit, :update] do
member do member do
put :test put :test
end end
...@@ -346,7 +346,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -346,7 +346,7 @@ constraints(ProjectUrlConstrainer.new) do
end end
end end
resources :project_members, except: [:show, :new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do resources :project_members, except: [:show, :new, :edit], constraints: { id: %r{[a-zA-Z./0-9_\-#%+]+} }, concerns: :access_requestable do
collection do collection do
delete :leave delete :leave
...@@ -379,7 +379,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -379,7 +379,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :uploads, only: [:create] do resources :uploads, only: [:create] do
collection do collection do
get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ } get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }
end end
end end
......
...@@ -2,17 +2,17 @@ scope path: :uploads do ...@@ -2,17 +2,17 @@ scope path: :uploads do
# Note attachments and User/Group/Project avatars # Note attachments and User/Group/Project avatars
get "-/system/:model/:mounted_as/:id/:filename", get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show", to: "uploads#show",
constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ } constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: %r{[^/]+} }
# show uploads for models, snippets (notes) available for now # show uploads for models, snippets (notes) available for now
get '-/system/:model/:id/:secret/:filename', get '-/system/:model/:id/:secret/:filename',
to: 'uploads#show', to: 'uploads#show',
constraints: { model: /personal_snippet/, id: /\d+/, filename: /[^\/]+/ } constraints: { model: /personal_snippet/, id: /\d+/, filename: %r{[^/]+} }
# show temporary uploads # show temporary uploads
get '-/system/temp/:secret/:filename', get '-/system/temp/:secret/:filename',
to: 'uploads#show', to: 'uploads#show',
constraints: { filename: /[^\/]+/ } constraints: { filename: %r{[^/]+} }
# Appearance # Appearance
get "-/system/:model/:mounted_as/:id/:filename", get "-/system/:model/:mounted_as/:id/:filename",
...@@ -22,7 +22,7 @@ scope path: :uploads do ...@@ -22,7 +22,7 @@ scope path: :uploads do
# Project markdown uploads # Project markdown uploads
get ":namespace_id/:project_id/:secret/:filename", get ":namespace_id/:project_id/:secret/:filename",
to: "projects/uploads#show", to: "projects/uploads#show",
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: /[^\/]+/ } constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: %r{[^/]+} }
# create uploads for models, snippets (notes) available for now # create uploads for models, snippets (notes) available for now
post ':model', post ':model',
...@@ -34,4 +34,4 @@ end ...@@ -34,4 +34,4 @@ end
# Redirect old note attachments path to new uploads path. # Redirect old note attachments path to new uploads path.
get "files/note/:id/:filename", get "files/note/:id/:filename",
to: redirect("uploads/note/attachment/%{id}/%{filename}"), to: redirect("uploads/note/attachment/%{id}/%{filename}"),
constraints: { filename: /[^\/]+/ } constraints: { filename: %r{[^/]+} }
...@@ -5,17 +5,32 @@ Enterprise Edition (look for the [`CE Upstream` merge requests]). ...@@ -5,17 +5,32 @@ Enterprise Edition (look for the [`CE Upstream` merge requests]).
This merge is done automatically in a This merge is done automatically in a
[scheduled pipeline](https://gitlab.com/gitlab-org/release-tools/-/jobs/43201679). [scheduled pipeline](https://gitlab.com/gitlab-org/release-tools/-/jobs/43201679).
If a merge is already in progress, the job [doesn't create a new one](https://gitlab.com/gitlab-org/release-tools/-/jobs/43157687).
**If you are pinged in a `CE Upstream` merge request to resolve a conflict, ## What to do if you are pinged in a `CE Upstream` merge request to resolve a conflict?
please resolve the conflict as soon as possible or ask someone else to do it!**
1. Please resolve the conflict as soon as possible or ask someone else to do it
>**Note:** - It's ok to resolve more conflicts than the one that you are asked to resolve.
It's ok to resolve more conflicts than the one that you are asked to resolve. In In that case, it's a good habit to ask for a double-check on your resolution
that case, it's a good habit to ask for a double-check on your resolution by by someone who is familiar with the code you touched.
someone who is familiar with the code you touched. 1. Once you have resolved your conflicts, push to the branch (no force-push)
1. Assign the merge request to the next person that has to resolve a conflict
1. If all conflicts are resolved after your resolution is pushed, keep the merge
request assigned to you: **you are now responsible for the merge request to be
green**
1. If you need any help, you can ping the current [release managers], or ask in
the `#ce-to-ee` Slack channel
A few notes about the automatic CE->EE merge job:
- If a merge is already in progress, the job
[doesn't create a new one](https://gitlab.com/gitlab-org/release-tools/-/jobs/43157687).
- If there is nothing to merge (i.e. EE is up-to-date with CE), the job doesn't
create a new one
- The job posts messages to the `#ce-to-ee` Slack channel to inform what's the
current CE->EE merge status (e.g. "A new MR has been created", "A MR is still pending")
[`CE Upstream` merge requests]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests?label_name%5B%5D=CE+upstream [`CE Upstream` merge requests]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests?label_name%5B%5D=CE+upstream
[release managers]: https://about.gitlab.com/release-managers/
## Always merge EE merge requests before their CE counterparts ## Always merge EE merge requests before their CE counterparts
......
...@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed ...@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
# Install Git # Install Git
sudo apt-get install -y git-core sudo apt-get install -y git-core
# Make sure Git is version 2.14.3 or higher # Make sure Git is version 2.9.5 or higher
git --version git --version
Is the system packaged Git too old? Remove it and compile from source. Is the system packaged Git too old? Remove it and compile from source.
...@@ -93,9 +93,9 @@ Is the system packaged Git too old? Remove it and compile from source. ...@@ -93,9 +93,9 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source # Download and compile from source
cd /tmp cd /tmp
curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.8.4.tar.gz curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.14.3.tar.gz
echo '626e319f8a24fc0866167ea5f6bf3e2f38f69d6cb2e59e150f13709ca3ebf301 git-2.8.4.tar.gz' | shasum -a256 -c - && tar -xzf git-2.8.4.tar.gz echo '023ffff6d3ba8a1bea779dfecc0ed0bb4ad68ab8601d14435dd8c08416f78d7f git-2.14.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.14.3.tar.gz
cd git-2.8.4/ cd git-2.14.3/
./configure ./configure
make prefix=/usr/local all make prefix=/usr/local all
......
...@@ -90,7 +90,8 @@ structure. ...@@ -90,7 +90,8 @@ structure.
To create a subgroup: To create a subgroup:
1. In the group's dashboard go to the **Subgroups** page and click **New subgroup**. 1. In the group's dashboard expand the **New project** split button, select
**New subgroup** and click the **New subgroup** button.
![Subgroups page](img/create_subgroup_button.png) ![Subgroups page](img/create_subgroup_button.png)
......
...@@ -193,7 +193,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps ...@@ -193,7 +193,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end end
step 'The link with text "/ID" should have url "tree/markdownID"' do step 'The link with text "/ID" should have url "tree/markdownID"' do
find('a', text: /^\/#id$/)['href'] == current_host + project_tree_path(@project, "markdown") + '#id' find('a', text: %r{^/#id$})['href'] == current_host + project_tree_path(@project, "markdown") + '#id'
end end
step 'The link with text "README.mdID" '\ step 'The link with text "README.mdID" '\
...@@ -203,7 +203,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps ...@@ -203,7 +203,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'The link with text "d/README.mdID" should have '\ step 'The link with text "d/README.mdID" should have '\
'url "blob/markdown/d/README.mdID"' do 'url "blob/markdown/d/README.mdID"' do
find('a', text: /^d\/README.md#id$/)['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id' find('a', text: %r{^d/README.md#id$})['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id'
end end
step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do
...@@ -212,7 +212,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps ...@@ -212,7 +212,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end end
step 'The link with text "/ID" should have url "blob/markdown/README.mdID"' do step 'The link with text "/ID" should have url "blob/markdown/README.mdID"' do
find('a', text: /^\/#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' find('a', text: %r{^/#id$})['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id'
end end
# Wiki # Wiki
......
...@@ -17,15 +17,15 @@ module API ...@@ -17,15 +17,15 @@ module API
} }
}.freeze }.freeze
PROJECT_TEMPLATE_REGEX = PROJECT_TEMPLATE_REGEX =
/[\<\{\[] %r{[\<\{\[]
(project|description| (project|description|
one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
[\>\}\]]/xi.freeze [\>\}\]]}xi.freeze
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
FULLNAME_TEMPLATE_REGEX = FULLNAME_TEMPLATE_REGEX =
/[\<\{\[] %r{[\<\{\[]
(fullname|name\sof\s(author|copyright\sowner)) (fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]/xi.freeze [\>\}\]]}xi.freeze
helpers do helpers do
def parsed_license_template def parsed_license_template
......
...@@ -173,7 +173,7 @@ module API ...@@ -173,7 +173,7 @@ module API
use :sort_params use :sort_params
use :pagination use :pagination
end end
get "/search/:query", requirements: { query: /[^\/]+/ } do get "/search/:query", requirements: { query: %r{[^/]+} } do
search_service = Search::GlobalService.new(current_user, search: params[:query]).execute search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
projects = search_service.objects('projects', params[:page], false) projects = search_service.objects('projects', params[:page], false)
projects = projects.reorder(params[:order_by] => params[:sort]) projects = projects.reorder(params[:order_by] => params[:sort])
......
...@@ -16,15 +16,15 @@ module API ...@@ -16,15 +16,15 @@ module API
} }
}.freeze }.freeze
PROJECT_TEMPLATE_REGEX = PROJECT_TEMPLATE_REGEX =
/[\<\{\[] %r{[\<\{\[]
(project|description| (project|description|
one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
[\>\}\]]/xi.freeze [\>\}\]]}xi.freeze
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
FULLNAME_TEMPLATE_REGEX = FULLNAME_TEMPLATE_REGEX =
/[\<\{\[] %r{[\<\{\[]
(fullname|name\sof\s(author|copyright\sowner)) (fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]/xi.freeze [\>\}\]]}xi.freeze
DEPRECATION_MESSAGE = ' This endpoint is deprecated and has been removed in V4.'.freeze DEPRECATION_MESSAGE = ' This endpoint is deprecated and has been removed in V4.'.freeze
helpers do helpers do
......
...@@ -54,9 +54,9 @@ module Banzai ...@@ -54,9 +54,9 @@ module Banzai
# Build a regexp that matches all valid :emoji: names. # Build a regexp that matches all valid :emoji: names.
def self.emoji_pattern def self.emoji_pattern
@emoji_pattern ||= @emoji_pattern ||=
/(?<=[^[:alnum:]:]|\n|^) %r{(?<=[^[:alnum:]:]|\n|^)
:(#{Gitlab::Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}): :(#{Gitlab::Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):
(?=[^[:alnum:]:]|$)/x (?=[^[:alnum:]:]|$)}x
end end
# Build a regexp that matches all valid unicode emojis names. # Build a regexp that matches all valid unicode emojis names.
......
...@@ -51,10 +51,10 @@ module Banzai ...@@ -51,10 +51,10 @@ module Banzai
# See https://github.com/gollum/gollum/wiki # See https://github.com/gollum/gollum/wiki
# #
# Rubular: http://rubular.com/r/7dQnE5CUCH # Rubular: http://rubular.com/r/7dQnE5CUCH
TAGS_PATTERN = %r{\[\[(.+?)\]\]}.freeze TAGS_PATTERN = /\[\[(.+?)\]\]/.freeze
# Pattern to match allowed image extensions # Pattern to match allowed image extensions
ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i.freeze ALLOWED_IMAGE_EXTENSIONS = /.+(jpg|png|gif|svg|bmp)\z/i.freeze
def call def call
search_text_nodes(doc).each do |node| search_text_nodes(doc).each do |node|
......
...@@ -11,7 +11,7 @@ module ContainerRegistry ...@@ -11,7 +11,7 @@ module ContainerRegistry
private private
def default_path def default_path
@uri.sub(/^https?:\/\//, '') @uri.sub(%r{^https?://}, '')
end end
end end
end end
...@@ -56,7 +56,7 @@ module ExtractsPath ...@@ -56,7 +56,7 @@ module ExtractsPath
if valid_refs.length == 0 if valid_refs.length == 0
# No exact ref match, so just try our best # No exact ref match, so just try our best
pair = id.match(/([^\/]+)(.*)/).captures pair = id.match(%r{([^/]+)(.*)}).captures
else else
# There is a distinct possibility that multiple refs prefix the ID. # There is a distinct possibility that multiple refs prefix the ID.
# Use the longest match to maximize the chance that we have the # Use the longest match to maximize the chance that we have the
...@@ -68,7 +68,7 @@ module ExtractsPath ...@@ -68,7 +68,7 @@ module ExtractsPath
end end
# Remove ending slashes from path # Remove ending slashes from path
pair[1].gsub!(/^\/|\/$/, '') pair[1].gsub!(%r{^/|/$}, '')
pair pair
end end
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
# Ends with /:random_hex/:filename # Ends with /:random_hex/:filename
FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z} FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z}
FULL_PATH_CAPTURE = %r{\A(.+)#{FILE_UPLOADER_PATH}} FULL_PATH_CAPTURE = /\A(.+)#{FILE_UPLOADER_PATH}/
# These regex patterns are tested against a relative path, relative to # These regex patterns are tested against a relative path, relative to
# the upload directory. # the upload directory.
......
...@@ -97,7 +97,7 @@ module Gitlab ...@@ -97,7 +97,7 @@ module Gitlab
end end
def total_size def total_size
descendant_pattern = %r{^#{Regexp.escape(@path.to_s)}} descendant_pattern = /^#{Regexp.escape(@path.to_s)}/
entries.sum do |path, entry| entries.sum do |path, entry|
(entry[:size] if path =~ descendant_pattern).to_i (entry[:size] if path =~ descendant_pattern).to_i
end end
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def package_url(name) def package_url(name)
"https://packagist.org/packages/#{name}" if name =~ %r{\A#{REPO_REGEX}\z} "https://packagist.org/packages/#{name}" if name =~ /\A#{REPO_REGEX}\z/
end end
end end
end end
......
...@@ -15,7 +15,7 @@ module Gitlab ...@@ -15,7 +15,7 @@ module Gitlab
link_regex(/(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/, &method(:github_url)) link_regex(/(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/, &method(:github_url))
# Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo # Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo
link_regex(%r{(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]}, &:itself) link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself)
# Link `source "https://rubygems.org"` to https://rubygems.org # Link `source "https://rubygems.org"` to https://rubygems.org
link_method_call('source', URL_REGEX, &:itself) link_method_call('source', URL_REGEX, &:itself)
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
def link_dependencies def link_dependencies
link_method_call('homepage', URL_REGEX, &:itself) link_method_call('homepage', URL_REGEX, &:itself)
link_regex(%r{(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]}, &:itself) link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself)
link_method_call('license', &method(:license_url)) link_method_call('license', &method(:license_url))
link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url)) link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url))
......
...@@ -43,7 +43,7 @@ module Gitlab ...@@ -43,7 +43,7 @@ module Gitlab
return "" unless decoded return "" unless decoded
# Certain trigger phrases that means we didn't parse correctly # Certain trigger phrases that means we didn't parse correctly
if decoded =~ /(Content\-Type\:|multipart\/alternative|text\/plain)/ if decoded =~ %r{(Content\-Type\:|multipart/alternative|text/plain)}
return "" return ""
end end
......
...@@ -6,14 +6,14 @@ module Gitlab ...@@ -6,14 +6,14 @@ module Gitlab
module FileDetector module FileDetector
PATTERNS = { PATTERNS = {
# Project files # Project files
readme: /\Areadme[^\/]*\z/i, readme: %r{\Areadme[^/]*\z}i,
changelog: /\A(changelog|history|changes|news)[^\/]*\z/i, changelog: %r{\A(changelog|history|changes|news)[^/]*\z}i,
license: /\A(licen[sc]e|copying)(\.[^\/]+)?\z/i, license: %r{\A(licen[sc]e|copying)(\.[^/]+)?\z}i,
contributing: /\Acontributing[^\/]*\z/i, contributing: %r{\Acontributing[^/]*\z}i,
version: 'version', version: 'version',
avatar: /\Alogo\.(png|jpg|gif)\z/, avatar: /\Alogo\.(png|jpg|gif)\z/,
issue_template: /\A\.gitlab\/issue_templates\/[^\/]+\.md\z/, issue_template: %r{\A\.gitlab/issue_templates/[^/]+\.md\z},
merge_request_template: /\A\.gitlab\/merge_request_templates\/[^\/]+\.md\z/, merge_request_template: %r{\A\.gitlab/merge_request_templates/[^/]+\.md\z},
# Configuration files # Configuration files
gitignore: '.gitignore', gitignore: '.gitignore',
...@@ -22,17 +22,17 @@ module Gitlab ...@@ -22,17 +22,17 @@ module Gitlab
route_map: '.gitlab/route-map.yml', route_map: '.gitlab/route-map.yml',
# Dependency files # Dependency files
cartfile: /\ACartfile[^\/]*\z/, cartfile: %r{\ACartfile[^/]*\z},
composer_json: 'composer.json', composer_json: 'composer.json',
gemfile: /\A(Gemfile|gems\.rb)\z/, gemfile: /\A(Gemfile|gems\.rb)\z/,
gemfile_lock: 'Gemfile.lock', gemfile_lock: 'Gemfile.lock',
gemspec: /\A[^\/]*\.gemspec\z/, gemspec: %r{\A[^/]*\.gemspec\z},
godeps_json: 'Godeps.json', godeps_json: 'Godeps.json',
package_json: 'package.json', package_json: 'package.json',
podfile: 'Podfile', podfile: 'Podfile',
podspec_json: /\A[^\/]*\.podspec\.json\z/, podspec_json: %r{\A[^/]*\.podspec\.json\z},
podspec: /\A[^\/]*\.podspec\z/, podspec: %r{\A[^/]*\.podspec\z},
requirements_txt: /\A[^\/]*requirements\.txt\z/, requirements_txt: %r{\A[^/]*requirements\.txt\z},
yarn_lock: 'yarn.lock' yarn_lock: 'yarn.lock'
}.freeze }.freeze
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
include Gitlab::EncodingHelper include Gitlab::EncodingHelper
def ref_name(ref) def ref_name(ref)
encode!(ref).sub(/\Arefs\/(tags|heads|remotes)\//, '') encode!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '')
end end
def branch_name(ref) def branch_name(ref)
......
...@@ -107,7 +107,7 @@ module Gitlab ...@@ -107,7 +107,7 @@ module Gitlab
def find_entry_by_path(repository, root_id, path) def find_entry_by_path(repository, root_id, path)
root_tree = repository.lookup(root_id) root_tree = repository.lookup(root_id)
# Strip leading slashes # Strip leading slashes
path[/^\/*/] = '' path[%r{^/*}] = ''
path_arr = path.split('/') path_arr = path.split('/')
entry = root_tree.find do |entry| entry = root_tree.find do |entry|
...@@ -140,7 +140,7 @@ module Gitlab ...@@ -140,7 +140,7 @@ module Gitlab
def find_by_gitaly(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE) def find_by_gitaly(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE)
return unless path return unless path
path = path.sub(/\A\/*/, '') path = path.sub(%r{\A/*}, '')
path = '/' if path.empty? path = '/' if path.empty?
name = File.basename(path) name = File.basename(path)
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
class << self class << self
def normalize_path(filename) def normalize_path(filename)
# Strip all leading slashes so that //foo -> foo # Strip all leading slashes so that //foo -> foo
filename[/^\/*/] = '' filename[%r{^/*}] = ''
# Expand relative paths (e.g. foo/../bar) # Expand relative paths (e.g. foo/../bar)
filename = Pathname.new(filename) filename = Pathname.new(filename)
......
...@@ -23,7 +23,7 @@ module Gitlab ...@@ -23,7 +23,7 @@ module Gitlab
# Ex. # Ex.
# Ref.extract_branch_name('refs/heads/master') #=> 'master' # Ref.extract_branch_name('refs/heads/master') #=> 'master'
def self.extract_branch_name(str) def self.extract_branch_name(str)
str.gsub(/\Arefs\/heads\//, '') str.gsub(%r{\Arefs/heads/}, '')
end end
# Gitaly: this method will probably be migrated indirectly via its call sites. # Gitaly: this method will probably be migrated indirectly via its call sites.
......
...@@ -462,7 +462,6 @@ module Gitlab ...@@ -462,7 +462,6 @@ module Gitlab
path: nil, path: nil,
follow: false, follow: false,
skip_merges: false, skip_merges: false,
disable_walk: false,
after: nil, after: nil,
before: nil before: nil
} }
...@@ -494,11 +493,7 @@ module Gitlab ...@@ -494,11 +493,7 @@ module Gitlab
return [] return []
end end
if log_using_shell?(options)
log_by_shell(sha, options) log_by_shell(sha, options)
else
log_by_walk(sha, options)
end
end end
def count_commits(options) def count_commits(options)
...@@ -1397,7 +1392,7 @@ module Gitlab ...@@ -1397,7 +1392,7 @@ module Gitlab
end end
def search_files_by_name(query, ref) def search_files_by_name(query, ref)
safe_query = Regexp.escape(query.sub(/^\/*/, "")) safe_query = Regexp.escape(query.sub(%r{^/*}, ""))
return [] if empty? || safe_query.blank? return [] if empty? || safe_query.blank?
...@@ -1645,24 +1640,6 @@ module Gitlab ...@@ -1645,24 +1640,6 @@ module Gitlab
end end
end end
def log_using_shell?(options)
options[:path].present? ||
options[:disable_walk] ||
options[:skip_merges] ||
options[:after] ||
options[:before]
end
def log_by_walk(sha, options)
walk_options = {
show: sha,
sort: Rugged::SORT_NONE,
limit: options[:limit],
offset: options[:offset]
}
Rugged::Walker.walk(rugged, walk_options).to_a
end
# Gitaly note: JV: although #log_by_shell shells out to Git I think the # Gitaly note: JV: although #log_by_shell shells out to Git I think the
# complexity is such that we should migrate it as Ruby before trying to # complexity is such that we should migrate it as Ruby before trying to
# do it in Go. # do it in Go.
...@@ -2025,7 +2002,7 @@ module Gitlab ...@@ -2025,7 +2002,7 @@ module Gitlab
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target) target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit) Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
rescue Rugged::ReferenceError => e rescue Rugged::ReferenceError => e
raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ /'refs\/heads\/#{ref}'/ raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ %r{'refs/heads/#{ref}'}
raise InvalidRef.new("Invalid reference #{start_point}") raise InvalidRef.new("Invalid reference #{start_point}")
end end
......
...@@ -43,7 +43,7 @@ module Gitlab ...@@ -43,7 +43,7 @@ module Gitlab
branches = [] branches = []
rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref| rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref|
name = ref.name.sub(/\Arefs\/remotes\/#{remote_name}\//, '') name = ref.name.sub(%r{\Arefs/remotes/#{remote_name}/}, '')
begin begin
target_commit = Gitlab::Git::Commit.find(self, ref.target) target_commit = Gitlab::Git::Commit.find(self, ref.target)
......
...@@ -257,7 +257,7 @@ module Gitlab ...@@ -257,7 +257,7 @@ module Gitlab
offset: options[:offset], offset: options[:offset],
follow: options[:follow], follow: options[:follow],
skip_merges: options[:skip_merges], skip_merges: options[:skip_merges],
disable_walk: options[:disable_walk] disable_walk: true # This option is deprecated. The 'walk' implementation is being removed.
) )
request.after = GitalyClient.timestamp(options[:after]) if options[:after] request.after = GitalyClient.timestamp(options[:after]) if options[:after]
request.before = GitalyClient.timestamp(options[:before]) if options[:before] request.before = GitalyClient.timestamp(options[:before]) if options[:before]
......
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
:diff_hunk, :author, :note, :created_at, :updated_at, :diff_hunk, :author, :note, :created_at, :updated_at,
:github_id :github_id
NOTEABLE_ID_REGEX = /\/pull\/(?<iid>\d+)/i NOTEABLE_ID_REGEX = %r{/pull/(?<iid>\d+)}i
# Builds a diff note from a GitHub API response. # Builds a diff note from a GitHub API response.
# #
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
expose_attribute :noteable_id, :noteable_type, :author, :note, expose_attribute :noteable_id, :noteable_type, :author, :note,
:created_at, :updated_at, :github_id :created_at, :updated_at, :github_id
NOTEABLE_TYPE_REGEX = /\/(?<type>(pull|issues))\/(?<iid>\d+)/i NOTEABLE_TYPE_REGEX = %r{/(?<type>(pull|issues))/(?<iid>\d+)}i
# Builds a note from a GitHub API response. # Builds a note from a GitHub API response.
# #
......
...@@ -59,7 +59,7 @@ module Gitlab ...@@ -59,7 +59,7 @@ module Gitlab
end end
def extracted_files def extracted_files
Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| f =~ /.*\/\.{1,2}$/ } Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| f =~ %r{.*/\.{1,2}$} }
end end
end end
end end
......
...@@ -34,7 +34,7 @@ module Gitlab ...@@ -34,7 +34,7 @@ module Gitlab
end end
def relative_path(path) def relative_path(path)
path.gsub(/^#{Rails.root.to_s}\/?/, '') path.gsub(%r{^#{Rails.root.to_s}/?}, '')
end end
def values_for(event) def values_for(event)
......
...@@ -56,12 +56,12 @@ module Gitlab ...@@ -56,12 +56,12 @@ module Gitlab
end end
def strip_url(url) def strip_url(url)
url.gsub(/\Ahttps?:\/\//, '') url.gsub(%r{\Ahttps?://}, '')
end end
def project_path(request) def project_path(request)
path_info = request.env["PATH_INFO"] path_info = request.env["PATH_INFO"]
path_info.sub!(/^\//, '') path_info.sub!(%r{^/}, '')
project_path_match = "#{path_info}/".match(PROJECT_PATH_REGEX) project_path_match = "#{path_info}/".match(PROJECT_PATH_REGEX)
return unless project_path_match return unless project_path_match
......
module Gitlab module Gitlab
module Middleware module Middleware
class Static < ActionDispatch::Static class Static < ActionDispatch::Static
UPLOADS_REGEX = /\A\/uploads(\/|\z)/.freeze UPLOADS_REGEX = %r{\A/uploads(/|\z)}.freeze
def call(env) def call(env)
return @app.call(env) if env['PATH_INFO'] =~ UPLOADS_REGEX return @app.call(env) if env['PATH_INFO'] =~ UPLOADS_REGEX
......
...@@ -9,7 +9,7 @@ module Gitlab ...@@ -9,7 +9,7 @@ module Gitlab
# if date doesn't present return time with current date # if date doesn't present return time with current date
# in other cases return nil # in other cases return nil
class SpendTimeAndDateSeparator class SpendTimeAndDateSeparator
DATE_REGEX = /(\d{2,4}[\/\-.]\d{1,2}[\/\-.]\d{1,2})/ DATE_REGEX = %r{(\d{2,4}[/\-.]\d{1,2}[/\-.]\d{1,2})}
def initialize(spend_command_arg) def initialize(spend_command_arg)
@spend_arg = spend_command_arg @spend_arg = spend_command_arg
......
...@@ -30,7 +30,7 @@ module Gitlab ...@@ -30,7 +30,7 @@ module Gitlab
raise NotFoundError.new("No known storage path matches #{repo_path.inspect}") raise NotFoundError.new("No known storage path matches #{repo_path.inspect}")
end end
result.sub(/\A\/*/, '') result.sub(%r{\A/*}, '')
end end
def self.find_project(project_path) def self.find_project(project_path)
......
...@@ -31,7 +31,7 @@ module Gitlab ...@@ -31,7 +31,7 @@ module Gitlab
storages << { name: 'test_second_storage', path: Rails.root.join('tmp', 'tests', 'second_storage').to_s } storages << { name: 'test_second_storage', path: Rails.root.join('tmp', 'tests', 'second_storage').to_s }
end end
config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages } config = { socket_path: address.sub(/\Aunix:/, ''), storage: storages }
config[:auth] = { token: 'secret' } if Rails.env.test? config[:auth] = { token: 'secret' } if Rails.env.test?
config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby
config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path } config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
......
...@@ -16,7 +16,7 @@ module Gitlab ...@@ -16,7 +16,7 @@ module Gitlab
end end
def relative_path def relative_path
@relative_path ||= @file.gsub(/^#{Rails.root.to_s}\/?/, '') @relative_path ||= @file.gsub(%r{^#{Rails.root.to_s}/?}, '')
end end
def to_param def to_param
......
...@@ -2,7 +2,7 @@ module Gitlab ...@@ -2,7 +2,7 @@ module Gitlab
module Sherlock module Sherlock
# Rack middleware used for tracking request metrics. # Rack middleware used for tracking request metrics.
class Middleware class Middleware
CONTENT_TYPES = /text\/html|application\/json/i CONTENT_TYPES = %r{text/html|application/json}i
IGNORE_PATHS = %r{^/sherlock} IGNORE_PATHS = %r{^/sherlock}
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
attr_reader :id, :query, :started_at, :finished_at, :backtrace attr_reader :id, :query, :started_at, :finished_at, :backtrace
# SQL identifiers that should be prefixed with newlines. # SQL identifiers that should be prefixed with newlines.
PREFIX_NEWLINE = / PREFIX_NEWLINE = %r{
\s+(FROM \s+(FROM
|(LEFT|RIGHT)?INNER\s+JOIN |(LEFT|RIGHT)?INNER\s+JOIN
|(LEFT|RIGHT)?OUTER\s+JOIN |(LEFT|RIGHT)?OUTER\s+JOIN
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
|GROUP\s+BY |GROUP\s+BY
|ORDER\s+BY |ORDER\s+BY
|LIMIT |LIMIT
|OFFSET)\s+/ix # Vim indent breaks when this is on a newline :< |OFFSET)\s+}ix # Vim indent breaks when this is on a newline :<
# Creates a new Query using a String and a separate Array of bindings. # Creates a new Query using a String and a separate Array of bindings.
# #
......
...@@ -49,7 +49,7 @@ module Gitlab ...@@ -49,7 +49,7 @@ module Gitlab
def fetch_git_tags def fetch_git_tags
remote_tags, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git)) remote_tags, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git))
remote_tags.split("\n").grep(/tags\/v#{current_version.major}/) remote_tags.split("\n").grep(%r{tags/v#{current_version.major}})
end end
def update_commands def update_commands
......
...@@ -5,7 +5,7 @@ module SystemCheck ...@@ -5,7 +5,7 @@ module SystemCheck
set_check_pass -> { "yes (#{self.current_version})" } set_check_pass -> { "yes (#{self.current_version})" }
def self.required_version def self.required_version
@required_version ||= Gitlab::VersionInfo.new(2, 7, 3) @required_version ||= Gitlab::VersionInfo.new(2, 9, 5)
end end
def self.current_version def self.current_version
......
require 'securerandom'
module QA module QA
module Factory module Factory
module Resource module Resource
......
...@@ -7,7 +7,7 @@ module QA ...@@ -7,7 +7,7 @@ module QA
element :settings_link, 'link_to edit_project_path' element :settings_link, 'link_to edit_project_path'
element :repository_link, "title: 'Repository'" element :repository_link, "title: 'Repository'"
element :pipelines_settings_link, "title: 'CI / CD'" element :pipelines_settings_link, "title: 'CI / CD'"
element :issues_link, %r{link_to.*shortcuts-issues} element :issues_link, /link_to.*shortcuts-issues/
element :issues_link_text, "Issues" element :issues_link_text, "Issues"
element :top_level_items, '.sidebar-top-level-items' element :top_level_items, '.sidebar-top-level-items'
element :activity_link, "title: 'Activity'" element :activity_link, "title: 'Activity'"
......
module QA
feature 'creates issue', :core do
let(:issue_title) { 'issue title' }
scenario 'user creates issue' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Issue.fabricate! do |issue|
issue.title = issue_title
end
Page::Menu::Side.act { click_issues }
expect(page).to have_content(issue_title)
end
end
end
...@@ -3,7 +3,7 @@ describe QA::Runtime::RSAKey do ...@@ -3,7 +3,7 @@ describe QA::Runtime::RSAKey do
subject { described_class.new.public_key } subject { described_class.new.public_key }
it 'generates a public RSA key' do it 'generates a public RSA key' do
expect(subject).to match(/\Assh\-rsa AAAA[0-9A-Za-z+\/]+={0,3}\z/) expect(subject).to match(%r{\Assh\-rsa AAAA[0-9A-Za-z+/]+={0,3}\z})
end end
end end
end end
...@@ -29,7 +29,7 @@ module RuboCop ...@@ -29,7 +29,7 @@ module RuboCop
path = Pathname.new(source_name).relative_path_from(rails_root) path = Pathname.new(source_name).relative_path_from(rails_root)
dirname = File.dirname(path) dirname = File.dirname(path)
.sub(%r{\Adb/(migrate|post_migrate)}, 'spec/migrations') .sub(%r{\Adb/(migrate|post_migrate)}, 'spec/migrations')
filename = File.basename(source_name, '.rb').sub(%r{\A\d+_}, '') filename = File.basename(source_name, '.rb').sub(/\A\d+_/, '')
File.join(dirname, "#{filename}_spec.rb") File.join(dirname, "#{filename}_spec.rb")
end end
......
...@@ -137,8 +137,8 @@ describe Projects::JobsController do ...@@ -137,8 +137,8 @@ describe Projects::JobsController do
it 'exposes needed information' do it 'exposes needed information' do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['raw_path']).to match(/jobs\/\d+\/raw\z/) expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z})
expect(json_response.dig('merge_request', 'path')).to match(/merge_requests\/\d+\z/) expect(json_response.dig('merge_request', 'path')).to match(%r{merge_requests/\d+\z})
expect(json_response['new_issue_path']) expect(json_response['new_issue_path'])
.to include('/issues/new') .to include('/issues/new')
end end
......
...@@ -36,7 +36,7 @@ describe Projects::TodosController do ...@@ -36,7 +36,7 @@ describe Projects::TodosController do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['count']).to eq 1 expect(json_response['count']).to eq 1
expect(json_response['delete_path']).to match(/\/dashboard\/todos\/\d{1}/) expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
end end
end end
...@@ -104,7 +104,7 @@ describe Projects::TodosController do ...@@ -104,7 +104,7 @@ describe Projects::TodosController do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['count']).to eq 1 expect(json_response['count']).to eq 1
expect(json_response['delete_path']).to match(/\/dashboard\/todos\/\d{1}/) expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
end end
end end
......
...@@ -64,7 +64,7 @@ describe "User Feed" do ...@@ -64,7 +64,7 @@ describe "User Feed" do
end end
it 'has XHTML summaries in issue descriptions' do it 'has XHTML summaries in issue descriptions' do
expect(body).to match /<hr ?\/>/ expect(body).to match %r{<hr ?/>}
end end
it 'has XHTML summaries in notes' do it 'has XHTML summaries in notes' do
...@@ -72,7 +72,7 @@ describe "User Feed" do ...@@ -72,7 +72,7 @@ describe "User Feed" do
end end
it 'has XHTML summaries in merge request descriptions' do it 'has XHTML summaries in merge request descriptions' do
expect(body).to match /Here is the fix: <a[^>]*><img[^>]*\/><\/a>/ expect(body).to match %r{Here is the fix: <a[^>]*><img[^>]*/></a>}
end end
it 'has push event commit ID' do it 'has push event commit ID' do
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
feature 'Group issues page' do feature 'Group issues page' do
include FilteredSearchHelpers include FilteredSearchHelpers
context 'with shared examples' do
let(:path) { issues_group_path(group) } let(:path) { issues_group_path(group) }
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")} let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
...@@ -39,4 +40,24 @@ feature 'Group issues page' do ...@@ -39,4 +40,24 @@ feature 'Group issues page' do
expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name) expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
end end
end end
end
context 'issues list', :nested_groups do
let(:group) { create(:group)}
let(:subgroup) { create(:group, parent: group) }
let(:project) { create(:project, :public, group: group)}
let(:subgroup_project) { create(:project, :public, group: subgroup)}
let!(:issue) { create(:issue, project: project, title: 'root group issue') }
let!(:subgroup_issue) { create(:issue, project: subgroup_project, title: 'subgroup issue') }
it 'returns all group and subgroup issues' do
visit issues_group_path(group)
page.within('.issuable-list') do
expect(page).to have_selector('li.issue', count: 2)
expect(page).to have_content('root group issue')
expect(page).to have_content('subgroup issue')
end
end
end
end end
...@@ -100,12 +100,12 @@ describe 'Merge request > User resolves conflicts', :js do ...@@ -100,12 +100,12 @@ describe 'Merge request > User resolves conflicts', :js do
end end
it 'shows a link to the conflict resolution page' do it 'shows a link to the conflict resolution page' do
expect(page).to have_link('conflicts', href: /\/conflicts\Z/) expect(page).to have_link('conflicts', href: %r{/conflicts\Z})
end end
context 'in Inline view mode' do context 'in Inline view mode' do
before do before do
click_link('conflicts', href: /\/conflicts\Z/) click_link('conflicts', href: %r{/conflicts\Z})
end end
include_examples "conflicts are resolved in Interactive mode" include_examples "conflicts are resolved in Interactive mode"
...@@ -114,7 +114,7 @@ describe 'Merge request > User resolves conflicts', :js do ...@@ -114,7 +114,7 @@ describe 'Merge request > User resolves conflicts', :js do
context 'in Parallel view mode' do context 'in Parallel view mode' do
before do before do
click_link('conflicts', href: /\/conflicts\Z/) click_link('conflicts', href: %r{/conflicts\Z})
click_button 'Side-by-side' click_button 'Side-by-side'
end end
...@@ -128,7 +128,7 @@ describe 'Merge request > User resolves conflicts', :js do ...@@ -128,7 +128,7 @@ describe 'Merge request > User resolves conflicts', :js do
before do before do
visit project_merge_request_path(project, merge_request) visit project_merge_request_path(project, merge_request)
click_link('conflicts', href: /\/conflicts\Z/) click_link('conflicts', href: %r{/conflicts\Z})
end end
it 'conflicts can not be resolved in Interactive mode' do it 'conflicts can not be resolved in Interactive mode' do
...@@ -181,7 +181,7 @@ describe 'Merge request > User resolves conflicts', :js do ...@@ -181,7 +181,7 @@ describe 'Merge request > User resolves conflicts', :js do
end end
it 'does not show a link to the conflict resolution page' do it 'does not show a link to the conflict resolution page' do
expect(page).not_to have_link('conflicts', href: /\/conflicts\Z/) expect(page).not_to have_link('conflicts', href: %r{/conflicts\Z})
end end
it 'shows an error if the conflicts page is visited directly' do it 'shows an error if the conflicts page is visited directly' do
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe GroupProjectsFinder do describe GroupProjectsFinder do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:options) { {} } let(:options) { {} }
...@@ -12,6 +13,8 @@ describe GroupProjectsFinder do ...@@ -12,6 +13,8 @@ describe GroupProjectsFinder do
let!(:shared_project_1) { create(:project, :public, path: '3') } let!(:shared_project_1) { create(:project, :public, path: '3') }
let!(:shared_project_2) { create(:project, :private, path: '4') } let!(:shared_project_2) { create(:project, :private, path: '4') }
let!(:shared_project_3) { create(:project, :internal, path: '5') } let!(:shared_project_3) { create(:project, :internal, path: '5') }
let!(:subgroup_project) { create(:project, :public, path: '6', group: subgroup) }
let!(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
before do before do
shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group) shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
...@@ -35,13 +38,33 @@ describe GroupProjectsFinder do ...@@ -35,13 +38,33 @@ describe GroupProjectsFinder do
context "only owned" do context "only owned" do
let(:options) { { only_owned: true } } let(:options) { { only_owned: true } }
context 'with subgroups projects', :nested_groups do
before do
options[:include_subgroups] = true
end
it { is_expected.to match_array([private_project, public_project, subgroup_project, subgroup_private_project]) }
end
context 'without subgroups projects' do
it { is_expected.to match_array([private_project, public_project]) } it { is_expected.to match_array([private_project, public_project]) }
end end
end
context "all" do context "all" do
context 'with subgroups projects', :nested_groups do
before do
options[:include_subgroups] = true
end
it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, private_project, public_project, subgroup_project, subgroup_private_project]) }
end
context 'without subgroups projects' do
it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) } it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) }
end end
end end
end
describe 'without group member current_user' do describe 'without group member current_user' do
before do before do
...@@ -71,24 +94,55 @@ describe GroupProjectsFinder do ...@@ -71,24 +94,55 @@ describe GroupProjectsFinder do
context "without external user" do context "without external user" do
before do before do
private_project.add_master(current_user) private_project.add_master(current_user)
subgroup_private_project.add_master(current_user)
end end
context 'with subgroups projects', :nested_groups do
before do
options[:include_subgroups] = true
end
it { is_expected.to match_array([private_project, public_project, subgroup_project, subgroup_private_project]) }
end
context 'without subgroups projects' do
it { is_expected.to match_array([private_project, public_project]) } it { is_expected.to match_array([private_project, public_project]) }
end end
end
context "with external user" do context "with external user" do
before do before do
current_user.update_attributes(external: true) current_user.update_attributes(external: true)
end end
context 'with subgroups projects', :nested_groups do
before do
options[:include_subgroups] = true
end
it { is_expected.to match_array([public_project, subgroup_project]) }
end
context 'without subgroups projects' do
it { is_expected.to eq([public_project]) } it { is_expected.to eq([public_project]) }
end end
end end
end
context "all" do context "all" do
context 'with subgroups projects', :nested_groups do
before do
options[:include_subgroups] = true
end
it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, public_project, subgroup_project]) }
end
context 'without subgroups projects' do
it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, public_project]) } it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, public_project]) }
end end
end end
end
describe "no user" do describe "no user" do
context "only shared" do context "only shared" do
...@@ -100,7 +154,17 @@ describe GroupProjectsFinder do ...@@ -100,7 +154,17 @@ describe GroupProjectsFinder do
context "only owned" do context "only owned" do
let(:options) { { only_owned: true } } let(:options) { { only_owned: true } }
context 'with subgroups projects', :nested_groups do
before do
options[:include_subgroups] = true
end
it { is_expected.to match_array([public_project, subgroup_project]) }
end
context 'without subgroups projects' do
it { is_expected.to eq([public_project]) } it { is_expected.to eq([public_project]) }
end end
end end
end
end end
...@@ -3,13 +3,17 @@ require 'spec_helper' ...@@ -3,13 +3,17 @@ require 'spec_helper'
describe IssuesFinder do describe IssuesFinder do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:user2) { create(:user) } set(:user2) { create(:user) }
set(:project1) { create(:project) } set(:group) { create(:group) }
set(:subgroup) { create(:group, parent: group) }
set(:project1) { create(:project, group: group) }
set(:project2) { create(:project) } set(:project2) { create(:project) }
set(:project3) { create(:project, group: subgroup) }
set(:milestone) { create(:milestone, project: project1) } set(:milestone) { create(:milestone, project: project1) }
set(:label) { create(:label, project: project2) } set(:label) { create(:label, project: project2) }
set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago) } set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago) }
set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab') } set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab') }
set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 1.week.from_now) } set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 1.week.from_now) }
set(:issue4) { create(:issue, project: project3) }
set(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue1) } set(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue1) }
set(:award_emoji2) { create(:award_emoji, name: 'thumbsup', user: user2, awardable: issue2) } set(:award_emoji2) { create(:award_emoji, name: 'thumbsup', user: user2, awardable: issue2) }
set(:award_emoji3) { create(:award_emoji, name: 'thumbsdown', user: user, awardable: issue3) } set(:award_emoji3) { create(:award_emoji, name: 'thumbsdown', user: user, awardable: issue3) }
...@@ -25,10 +29,12 @@ describe IssuesFinder do ...@@ -25,10 +29,12 @@ describe IssuesFinder do
project1.add_master(user) project1.add_master(user)
project2.add_developer(user) project2.add_developer(user)
project2.add_developer(user2) project2.add_developer(user2)
project3.add_developer(user)
issue1 issue1
issue2 issue2
issue3 issue3
issue4
award_emoji1 award_emoji1
award_emoji2 award_emoji2
...@@ -39,7 +45,7 @@ describe IssuesFinder do ...@@ -39,7 +45,7 @@ describe IssuesFinder do
let(:scope) { 'all' } let(:scope) { 'all' }
it 'returns all issues' do it 'returns all issues' do
expect(issues).to contain_exactly(issue1, issue2, issue3) expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
end end
context 'filtering by assignee ID' do context 'filtering by assignee ID' do
...@@ -50,6 +56,26 @@ describe IssuesFinder do ...@@ -50,6 +56,26 @@ describe IssuesFinder do
end end
end end
context 'filtering by group_id' do
let(:params) { { group_id: group.id } }
context 'when include_subgroup param not set' do
it 'returns all group issues' do
expect(issues).to contain_exactly(issue1)
end
end
context 'when include_subgroup param is true', :nested_groups do
before do
params[:include_subgroups] = true
end
it 'returns all group and subgroup issues' do
expect(issues).to contain_exactly(issue1, issue4)
end
end
end
context 'filtering by author ID' do context 'filtering by author ID' do
let(:params) { { author_id: user2.id } } let(:params) { { author_id: user2.id } }
...@@ -87,7 +113,7 @@ describe IssuesFinder do ...@@ -87,7 +113,7 @@ describe IssuesFinder do
let(:params) { { milestone_title: Milestone::None.title } } let(:params) { { milestone_title: Milestone::None.title } }
it 'returns issues with no milestone' do it 'returns issues with no milestone' do
expect(issues).to contain_exactly(issue2, issue3) expect(issues).to contain_exactly(issue2, issue3, issue4)
end end
end end
...@@ -185,7 +211,7 @@ describe IssuesFinder do ...@@ -185,7 +211,7 @@ describe IssuesFinder do
let(:params) { { label_name: Label::None.title } } let(:params) { { label_name: Label::None.title } }
it 'returns issues with no labels' do it 'returns issues with no labels' do
expect(issues).to contain_exactly(issue1, issue3) expect(issues).to contain_exactly(issue1, issue3, issue4)
end end
end end
...@@ -210,7 +236,7 @@ describe IssuesFinder do ...@@ -210,7 +236,7 @@ describe IssuesFinder do
let(:params) { { state: 'opened' } } let(:params) { { state: 'opened' } }
it 'returns only opened issues' do it 'returns only opened issues' do
expect(issues).to contain_exactly(issue1, issue2, issue3) expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
end end
end end
...@@ -226,7 +252,7 @@ describe IssuesFinder do ...@@ -226,7 +252,7 @@ describe IssuesFinder do
let(:params) { { state: 'all' } } let(:params) { { state: 'all' } }
it 'returns all issues' do it 'returns all issues' do
expect(issues).to contain_exactly(issue1, issue2, issue3, closed_issue) expect(issues).to contain_exactly(issue1, issue2, issue3, closed_issue, issue4)
end end
end end
...@@ -234,7 +260,7 @@ describe IssuesFinder do ...@@ -234,7 +260,7 @@ describe IssuesFinder do
let(:params) { { state: 'invalid_state' } } let(:params) { { state: 'invalid_state' } }
it 'returns all issues' do it 'returns all issues' do
expect(issues).to contain_exactly(issue1, issue2, issue3, closed_issue) expect(issues).to contain_exactly(issue1, issue2, issue3, closed_issue, issue4)
end end
end end
end end
...@@ -338,7 +364,7 @@ describe IssuesFinder do ...@@ -338,7 +364,7 @@ describe IssuesFinder do
end end
it "doesn't return issues if feature disabled" do it "doesn't return issues if feature disabled" do
[project1, project2].each do |project| [project1, project2, project3].each do |project|
project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED) project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED)
end end
...@@ -351,7 +377,7 @@ describe IssuesFinder do ...@@ -351,7 +377,7 @@ describe IssuesFinder do
it 'returns the number of rows for the default state' do it 'returns the number of rows for the default state' do
finder = described_class.new(user) finder = described_class.new(user)
expect(finder.row_count).to eq(3) expect(finder.row_count).to eq(4)
end end
it 'returns the number of rows for a given state' do it 'returns the number of rows for a given state' do
......
...@@ -6,31 +6,36 @@ describe MergeRequestsFinder do ...@@ -6,31 +6,36 @@ describe MergeRequestsFinder do
let(:user) { create :user } let(:user) { create :user }
let(:user2) { create :user } let(:user2) { create :user }
let(:project1) { create(:project, :public) } let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:project1) { create(:project, :public, group: group) }
let(:project2) { fork_project(project1, user) } let(:project2) { fork_project(project1, user) }
let(:project3) do let(:project3) do
p = fork_project(project1, user) p = fork_project(project1, user)
p.update!(archived: true) p.update!(archived: true)
p p
end end
let(:project4) { create(:project, :public, group: subgroup) }
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') } let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') }
let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) } let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) } let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) }
let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) }
before do before do
project1.add_master(user) project1.add_master(user)
project2.add_developer(user) project2.add_developer(user)
project3.add_developer(user) project3.add_developer(user)
project2.add_developer(user2) project2.add_developer(user2)
project4.add_developer(user)
end end
describe "#execute" do describe "#execute" do
it 'filters by scope' do it 'filters by scope' do
params = { scope: 'authored', state: 'opened' } params = { scope: 'authored', state: 'opened' }
merge_requests = described_class.new(user, params).execute merge_requests = described_class.new(user, params).execute
expect(merge_requests.size).to eq(3) expect(merge_requests.size).to eq(4)
end end
it 'filters by project' do it 'filters by project' do
...@@ -39,10 +44,26 @@ describe MergeRequestsFinder do ...@@ -39,10 +44,26 @@ describe MergeRequestsFinder do
expect(merge_requests.size).to eq(1) expect(merge_requests.size).to eq(1)
end end
it 'filters by group' do
params = { group_id: group.id }
merge_requests = described_class.new(user, params).execute
expect(merge_requests.size).to eq(2)
end
it 'filters by group including subgroups', :nested_groups do
params = { group_id: group.id, include_subgroups: true }
merge_requests = described_class.new(user, params).execute
expect(merge_requests.size).to eq(3)
end
it 'filters by non_archived' do it 'filters by non_archived' do
params = { non_archived: true } params = { non_archived: true }
merge_requests = described_class.new(user, params).execute merge_requests = described_class.new(user, params).execute
expect(merge_requests.size).to eq(3) expect(merge_requests.size).to eq(4)
end end
it 'filters by iid' do it 'filters by iid' do
...@@ -73,14 +94,14 @@ describe MergeRequestsFinder do ...@@ -73,14 +94,14 @@ describe MergeRequestsFinder do
end end
context 'with created_after and created_before params' do context 'with created_after and created_before params' do
let(:project4) { create(:project, forked_from_project: project1) } let(:new_project) { create(:project, forked_from_project: project1) }
let!(:new_merge_request) do let!(:new_merge_request) do
create(:merge_request, create(:merge_request,
:simple, :simple,
author: user, author: user,
created_at: 1.week.from_now, created_at: 1.week.from_now,
source_project: project4, source_project: new_project,
target_project: project1) target_project: project1)
end end
...@@ -89,12 +110,12 @@ describe MergeRequestsFinder do ...@@ -89,12 +110,12 @@ describe MergeRequestsFinder do
:simple, :simple,
author: user, author: user,
created_at: 1.week.ago, created_at: 1.week.ago,
source_project: project4, source_project: new_project,
target_project: project4) target_project: new_project)
end end
before do before do
project4.add_master(user) new_project.add_master(user)
end end
it 'filters by created_after' do it 'filters by created_after' do
...@@ -106,7 +127,7 @@ describe MergeRequestsFinder do ...@@ -106,7 +127,7 @@ describe MergeRequestsFinder do
end end
it 'filters by created_before' do it 'filters by created_before' do
params = { project_id: project4.id, created_before: old_merge_request.created_at + 1.second } params = { project_id: new_project.id, created_before: old_merge_request.created_at + 1.second }
merge_requests = described_class.new(user, params).execute merge_requests = described_class.new(user, params).execute
...@@ -119,7 +140,7 @@ describe MergeRequestsFinder do ...@@ -119,7 +140,7 @@ describe MergeRequestsFinder do
it 'returns the number of rows for the default state' do it 'returns the number of rows for the default state' do
finder = described_class.new(user) finder = described_class.new(user)
expect(finder.row_count).to eq(3) expect(finder.row_count).to eq(4)
end end
it 'returns the number of rows for a given state' do it 'returns the number of rows for a given state' do
......
...@@ -105,7 +105,7 @@ describe GroupsHelper do ...@@ -105,7 +105,7 @@ describe GroupsHelper do
it 'outputs the groups in the correct order' do it 'outputs the groups in the correct order' do
expect(helper.group_title(very_deep_nested_group)) expect(helper.group_title(very_deep_nested_group))
.to match(/<li style="text-indent: 16px;"><a.*>#{deep_nested_group.name}.*<\/li>.*<a.*>#{very_deep_nested_group.name}<\/a>/m) .to match(%r{<li style="text-indent: 16px;"><a.*>#{deep_nested_group.name}.*</li>.*<a.*>#{very_deep_nested_group.name}</a>}m)
end end
end end
...@@ -120,7 +120,7 @@ describe GroupsHelper do ...@@ -120,7 +120,7 @@ describe GroupsHelper do
let(:possible_help_texts) do let(:possible_help_texts) do
{ {
default_help: "This setting will be applied to all subgroups unless overridden by a group owner", default_help: "This setting will be applied to all subgroups unless overridden by a group owner",
ancestor_locked_but_you_can_override: /This setting is applied on <a .+>.+<\/a>\. You can override the setting or .+/, ancestor_locked_but_you_can_override: %r{This setting is applied on <a .+>.+</a>\. You can override the setting or .+},
ancestor_locked_so_ask_the_owner: /This setting is applied on .+\. To share projects in this group with another group, ask the owner to override the setting or remove the share with group lock from .+/, ancestor_locked_so_ask_the_owner: /This setting is applied on .+\. To share projects in this group with another group, ask the owner to override the setting or remove the share with group lock from .+/,
ancestor_locked_and_has_been_overridden: /This setting is applied on .+ and has been overridden on this subgroup/ ancestor_locked_and_has_been_overridden: /This setting is applied on .+ and has been overridden on this subgroup/
} }
......
...@@ -104,7 +104,7 @@ describe LabelsHelper do ...@@ -104,7 +104,7 @@ describe LabelsHelper do
context 'with a tooltip argument' do context 'with a tooltip argument' do
context 'set to false' do context 'set to false' do
it 'does not include the has-tooltip class' do it 'does not include the has-tooltip class' do
expect(link_to_label(label, tooltip: false)).not_to match %r{has-tooltip} expect(link_to_label(label, tooltip: false)).not_to match /has-tooltip/
end end
end end
end end
......
...@@ -27,7 +27,7 @@ describe VersionCheckHelper do ...@@ -27,7 +27,7 @@ describe VersionCheckHelper do
end end
it 'should have a VersionCheck url as the src' do it 'should have a VersionCheck url as the src' do
expect(@image_tag).to match(/src="https:\/\/version\.host\.com\/check\.svg\?gitlab_info=xxx"/) expect(@image_tag).to match(%r{src="https://version\.host\.com/check\.svg\?gitlab_info=xxx"})
end end
end end
end end
......
import Vue from 'vue'; import Vue from 'vue';
import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links'; import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
const createComponent = (data) => {
const Component = Vue.extend(relatedLinksComponent);
return new Component({
el: document.createElement('div'),
propsData: data,
});
};
describe('MRWidgetRelatedLinks', () => { describe('MRWidgetRelatedLinks', () => {
describe('props', () => { let vm;
it('should have props', () => {
const { relatedLinks } = relatedLinksComponent.props;
expect(relatedLinks).toBeDefined(); const createComponent = (data) => {
expect(relatedLinks.type instanceof Object).toBeTruthy(); const Component = Vue.extend(relatedLinksComponent);
expect(relatedLinks.required).toBeTruthy();
});
});
describe('computed', () => { return mountComponent(Component, data);
const data = {
relatedLinks: {
closing: '/foo',
mentioned: '/foo',
assignToMe: '/foo',
},
}; };
describe('hasLinks', () => { afterEach(() => {
it('should return correct value when we have links reference', () => { vm.$destroy();
const vm = createComponent(data);
expect(vm.hasLinks).toBeTruthy();
vm.relatedLinks.closing = null;
expect(vm.hasLinks).toBeTruthy();
vm.relatedLinks.mentioned = null;
expect(vm.hasLinks).toBeTruthy();
vm.relatedLinks.assignToMe = null;
expect(vm.hasLinks).toBeFalsy();
});
}); });
describe('computed', () => {
describe('closesText', () => { describe('closesText', () => {
it('returns correct text for open merge request', () => { it('returns Closes text for open merge request', () => {
data.state = 'open'; vm = createComponent({ state: 'open', relatedLinks: {} });
const vm = createComponent(data);
expect(vm.closesText).toEqual('Closes'); expect(vm.closesText).toEqual('Closes');
}); });
it('returns correct text for closed merge request', () => { it('returns correct text for closed merge request', () => {
data.state = 'closed'; vm = createComponent({ state: 'closed', relatedLinks: {} });
const vm = createComponent(data);
expect(vm.closesText).toEqual('Did not close'); expect(vm.closesText).toEqual('Did not close');
}); });
it('returns correct tense for merged request', () => { it('returns correct tense for merged request', () => {
data.state = 'merged'; vm = createComponent({ state: 'merged', relatedLinks: {} });
const vm = createComponent(data);
expect(vm.closesText).toEqual('Closed'); expect(vm.closesText).toEqual('Closed');
}); });
}); });
}); });
describe('template', () => {
it('should have only have closing issues text', () => { it('should have only have closing issues text', () => {
const vm = createComponent({ vm = createComponent({
relatedLinks: { relatedLinks: {
closing: '<a href="#">#23</a> and <a>#42</a>', closing: '<a href="#">#23</a> and <a>#42</a>',
}, },
...@@ -81,7 +47,7 @@ describe('MRWidgetRelatedLinks', () => { ...@@ -81,7 +47,7 @@ describe('MRWidgetRelatedLinks', () => {
}); });
it('should have only have mentioned issues text', () => { it('should have only have mentioned issues text', () => {
const vm = createComponent({ vm = createComponent({
relatedLinks: { relatedLinks: {
mentioned: '<a href="#">#7</a>', mentioned: '<a href="#">#7</a>',
}, },
...@@ -92,7 +58,7 @@ describe('MRWidgetRelatedLinks', () => { ...@@ -92,7 +58,7 @@ describe('MRWidgetRelatedLinks', () => {
}); });
it('should have closing and mentioned issues at the same time', () => { it('should have closing and mentioned issues at the same time', () => {
const vm = createComponent({ vm = createComponent({
relatedLinks: { relatedLinks: {
closing: '<a href="#">#7</a>', closing: '<a href="#">#7</a>',
mentioned: '<a href="#">#23</a> and <a>#42</a>', mentioned: '<a href="#">#23</a> and <a>#42</a>',
...@@ -105,7 +71,7 @@ describe('MRWidgetRelatedLinks', () => { ...@@ -105,7 +71,7 @@ describe('MRWidgetRelatedLinks', () => {
}); });
it('should have assing issues link', () => { it('should have assing issues link', () => {
const vm = createComponent({ vm = createComponent({
relatedLinks: { relatedLinks: {
assignToMe: '<a href="#">Assign yourself to these issues</a>', assignToMe: '<a href="#">Assign yourself to these issues</a>',
}, },
...@@ -113,5 +79,4 @@ describe('MRWidgetRelatedLinks', () => { ...@@ -113,5 +79,4 @@ describe('MRWidgetRelatedLinks', () => {
expect(vm.$el.innerText).toContain('Assign yourself to these issues'); expect(vm.$el.innerText).toContain('Assign yourself to these issues');
}); });
});
}); });
...@@ -53,7 +53,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do ...@@ -53,7 +53,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
doc = reference_filter("See (#{reference}.)") doc = reference_filter("See (#{reference}.)")
exp = Regexp.escape(range.reference_link_text) exp = Regexp.escape(range.reference_link_text)
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{exp}</a>\.\)})
end end
it 'ignores invalid commit IDs' do it 'ignores invalid commit IDs' do
...@@ -222,7 +222,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do ...@@ -222,7 +222,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
doc = reference_filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
exp = Regexp.escape(range.reference_link_text(project)) exp = Regexp.escape(range.reference_link_text(project))
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{exp}</a>\.\)})
end end
it 'ignores invalid commit IDs on the referenced project' do it 'ignores invalid commit IDs on the referenced project' do
......
...@@ -42,7 +42,7 @@ describe Banzai::Filter::CommitReferenceFilter do ...@@ -42,7 +42,7 @@ describe Banzai::Filter::CommitReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("See (#{reference}.)") doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{commit.short_id}</a>\.\)})
end end
it 'ignores invalid commit IDs' do it 'ignores invalid commit IDs' do
...@@ -199,12 +199,12 @@ describe Banzai::Filter::CommitReferenceFilter do ...@@ -199,12 +199,12 @@ describe Banzai::Filter::CommitReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{commit.reference_link_text(project)}<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{commit.reference_link_text(project)}</a>\.\)})
end end
it 'ignores invalid commit IDs on the referenced project' do it 'ignores invalid commit IDs on the referenced project' do
act = "Committed #{invalidate_reference(reference)}" act = "Committed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/) expect(reference_filter(act).to_html).to match(%r{<a.+>#{Regexp.escape(invalidate_reference(reference))}</a>})
end end
end end
end end
...@@ -49,7 +49,7 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do ...@@ -49,7 +49,7 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Issue (#{reference}.)") doc = filter("Issue (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{reference}<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{reference}</a>\.\)})
end end
it 'includes a title attribute' do it 'includes a title attribute' do
......
...@@ -14,7 +14,7 @@ describe Banzai::Filter::ImageLinkFilter do ...@@ -14,7 +14,7 @@ describe Banzai::Filter::ImageLinkFilter do
it 'does not wrap a duplicate link' do it 'does not wrap a duplicate link' do
doc = filter(%Q(<a href="/whatever">#{image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')}</a>)) doc = filter(%Q(<a href="/whatever">#{image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')}</a>))
expect(doc.to_html).to match /^<a href="\/whatever"><img[^>]*><\/a>$/ expect(doc.to_html).to match %r{^<a href="/whatever"><img[^>]*></a>$}
end end
it 'works with external images' do it 'works with external images' do
...@@ -24,6 +24,6 @@ describe Banzai::Filter::ImageLinkFilter do ...@@ -24,6 +24,6 @@ describe Banzai::Filter::ImageLinkFilter do
it 'works with inline images' do it 'works with inline images' do
doc = filter(%Q(<p>test #{image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')} inline</p>)) doc = filter(%Q(<p>test #{image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')} inline</p>))
expect(doc.to_html).to match /^<p>test <a[^>]*><img[^>]*><\/a> inline<\/p>$/ expect(doc.to_html).to match %r{^<p>test <a[^>]*><img[^>]*></a> inline</p>$}
end end
end end
...@@ -288,7 +288,7 @@ describe Banzai::Filter::IssueReferenceFilter do ...@@ -288,7 +288,7 @@ describe Banzai::Filter::IssueReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)</a>\.\)})
end end
it 'includes default classes' do it 'includes default classes' do
...@@ -317,7 +317,7 @@ describe Banzai::Filter::IssueReferenceFilter do ...@@ -317,7 +317,7 @@ describe Banzai::Filter::IssueReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference_link}.)") doc = reference_filter("Fixed (#{reference_link}.)")
expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>Reference</a>\.\)})
end end
it 'includes default classes' do it 'includes default classes' do
...@@ -346,7 +346,7 @@ describe Banzai::Filter::IssueReferenceFilter do ...@@ -346,7 +346,7 @@ describe Banzai::Filter::IssueReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference_link}.)") doc = reference_filter("Fixed (#{reference_link}.)")
expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>Reference</a>\.\)})
end end
it 'includes default classes' do it 'includes default classes' do
......
...@@ -42,7 +42,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do ...@@ -42,7 +42,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Merge (#{reference}.)") doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{Regexp.escape(reference)}</a>\.\)})
end end
it 'ignores invalid merge IDs' do it 'ignores invalid merge IDs' do
...@@ -211,7 +211,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do ...@@ -211,7 +211,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Merge (#{reference}.)") doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/) expect(doc.to_html).to match(%r{\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)</a>\.\)})
end end
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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