Commit 76f2f70a authored by GitLab Bot's avatar GitLab Bot Committed by root

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-06-21

# Conflicts:
#	app/assets/javascripts/boards/filtered_search_boards.js

[ci skip]
parents 8999194e c0616ba8
...@@ -311,7 +311,6 @@ gem 'peek-sidekiq', '~> 1.0.3' ...@@ -311,7 +311,6 @@ gem 'peek-sidekiq', '~> 1.0.3'
# Metrics # Metrics
group :metrics do group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri
gem 'method_source', '~> 0.8', require: false gem 'method_source', '~> 0.8', require: false
gem 'influxdb', '~> 0.2', require: false gem 'influxdb', '~> 0.2', require: false
......
...@@ -49,7 +49,6 @@ GEM ...@@ -49,7 +49,6 @@ GEM
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.5)
arel (6.0.4) arel (6.0.4)
asana (0.6.0) asana (0.6.0)
faraday (~> 0.9) faraday (~> 0.9)
...@@ -1003,7 +1002,6 @@ DEPENDENCIES ...@@ -1003,7 +1002,6 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0) acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0)
asana (~> 0.6.0) asana (~> 0.6.0)
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
......
...@@ -52,7 +52,6 @@ GEM ...@@ -52,7 +52,6 @@ GEM
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.5)
arel (7.1.4) arel (7.1.4)
asana (0.6.0) asana (0.6.0)
faraday (~> 0.9) faraday (~> 0.9)
...@@ -1013,7 +1012,6 @@ DEPENDENCIES ...@@ -1013,7 +1012,6 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0) acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0)
asana (~> 0.6.0) asana (~> 0.6.0)
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
......
<<<<<<< HEAD
import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_token_keys_issues'; import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_token_keys_issues';
=======
>>>>>>> upstream/master
import FilteredSearchContainer from '../filtered_search/container'; import FilteredSearchContainer from '../filtered_search/container';
import FilteredSearchManager from '../filtered_search/filtered_search_manager'; import FilteredSearchManager from '../filtered_search/filtered_search_manager';
......
...@@ -117,7 +117,7 @@ export default { ...@@ -117,7 +117,7 @@ export default {
class="btn btn-primary btn-sm btn-block" class="btn btn-primary btn-sm btn-block"
@click="toggleIsSmall" @click="toggleIsSmall"
> >
{{ __('Commit') }} {{ __('Commit') }}
</button> </button>
<p <p
class="text-center" class="text-center"
......
...@@ -37,6 +37,11 @@ export default class SigninTabsMemoizer { ...@@ -37,6 +37,11 @@ export default class SigninTabsMemoizer {
const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`); const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`);
if (tab) { if (tab) {
tab.click(); tab.click();
} else {
const firstTab = document.querySelector(`${this.tabSelector} a`);
if (firstTab) {
firstTab.click();
}
} }
} }
} }
......
...@@ -558,7 +558,7 @@ ...@@ -558,7 +558,7 @@
background: $white-light; background: $white-light;
border-bottom: 1px solid $white-normal; border-bottom: 1px solid $white-normal;
.center-logo { .mx-auto {
margin: 8px 0; margin: 8px 0;
text-align: center; text-align: center;
......
...@@ -59,8 +59,6 @@ module Emails ...@@ -59,8 +59,6 @@ module Emails
def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil) def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id) setup_merge_request_mail(merge_request_id, recipient_id)
@reasons = MergeRequestPresenter.new(@merge_request, current_user: current_user).unmergeable_reasons
mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason)) mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
end end
......
...@@ -132,9 +132,18 @@ class MergeRequest < ActiveRecord::Base ...@@ -132,9 +132,18 @@ class MergeRequest < ActiveRecord::Base
end end
after_transition unchecked: :cannot_be_merged do |merge_request, transition| after_transition unchecked: :cannot_be_merged do |merge_request, transition|
begin
# Merge request can become unmergeable due to many reasons.
# We only notify if it is due to conflict.
unless merge_request.project.repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch)
NotificationService.new.merge_request_unmergeable(merge_request) NotificationService.new.merge_request_unmergeable(merge_request)
TodoService.new.merge_request_became_unmergeable(merge_request) TodoService.new.merge_request_became_unmergeable(merge_request)
end end
rescue Gitlab::Git::CommandError
# Checking mergeability can trigger exception, e.g. non-utf8
# We ignore this type of errors.
end
end
def check_state?(merge_status) def check_state?(merge_status)
[:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym) [:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym)
......
...@@ -22,17 +22,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated ...@@ -22,17 +22,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end end
end end
def unmergeable_reasons
strong_memoize(:unmergeable_reasons) do
reasons = []
reasons << "no commits" if merge_request.has_no_commits?
reasons << "source branch is missing" unless merge_request.source_branch_exists?
reasons << "target branch is missing" unless merge_request.target_branch_exists?
reasons << "has merge conflicts" unless merge_request.project.repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch)
reasons
end
end
def cancel_merge_when_pipeline_succeeds_path def cancel_merge_when_pipeline_succeeds_path
if can_cancel_merge_when_pipeline_succeeds?(current_user) if can_cancel_merge_when_pipeline_succeeds?(current_user)
cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request) cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request)
......
%p %p
Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} can no longer be merged due to the following #{'reason'.pluralize(@reasons.count)}: Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} can no longer be merged due to conflict.
%ul
- @reasons.each do |reason|
%li= reason
Merge Request #{@merge_request.to_reference} can no longer be merged due to the following #{'reason'.pluralize(@reasons.count)}: Merge Request #{@merge_request.to_reference} can no longer be merged due to conflict.
- @reasons.each do |reason|
* #{reason}
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
...@@ -16,5 +16,6 @@ ...@@ -16,5 +16,6 @@
= _('or') = _('or')
= link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer') = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer')
- else - else
.settings-message.text-center
- link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer') - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer')
= s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link } = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
.form-group.row .form-group.row
= f.label :start_date, "Start Date", class: "col-form-label col-sm-2" = f.label :start_date, "Start Date", class: "col-form-label col-sm-2"
.col-sm-10 .col-sm-10
= f.text_field :start_date, class: "datepicker form-control", placeholder: "Select start date" = f.text_field :start_date, class: "datepicker form-control", placeholder: "Select start date", autocomplete: 'off'
%a.inline.float-right.prepend-top-5.js-clear-start-date{ href: "#" } Clear start date %a.inline.float-right.prepend-top-5.js-clear-start-date{ href: "#" } Clear start date
.form-group.row .form-group.row
= f.label :due_date, "Due Date", class: "col-form-label col-sm-2" = f.label :due_date, "Due Date", class: "col-form-label col-sm-2"
.col-sm-10 .col-sm-10
= f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date" = f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date", autocomplete: 'off'
%a.inline.float-right.prepend-top-5.js-clear-due-date{ href: "#" } Clear due date %a.inline.float-right.prepend-top-5.js-clear-due-date{ href: "#" } Clear due date
---
title: Prevent browser autocomplete for milestone date fields
merge_request:
author:
type: fixed
---
title: Fix invalid fuzzy translations being generated during installation
merge_request: 20048
author:
type: fixed
---
title: Highlight cluster settings message
merge_request: 19996
author: George Tsiolis
type: changed
---
title: Add ellispsis to web ide commit button
merge_request: 20030
author:
type: other
---
title: Limit the action suffixes in transaction metrics
merge_request:
author:
type: fixed
---
title: Remove remaining traces of the Allocations Gem
merge_request:
author:
type: changed
...@@ -28,6 +28,7 @@ By default, QueryRecorder will ignore cached queries in the count. However, it m ...@@ -28,6 +28,7 @@ By default, QueryRecorder will ignore cached queries in the count. However, it m
all queries to avoid introducing an N+1 query that may be masked by the statement cache. To do this, all queries to avoid introducing an N+1 query that may be masked by the statement cache. To do this,
pass the `skip_cached` variable to `QueryRecorder` and use the `exceed_all_query_limit` matcher: pass the `skip_cached` variable to `QueryRecorder` and use the `exceed_all_query_limit` matcher:
```
it "avoids N+1 database queries" do it "avoids N+1 database queries" do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { visit_some_page }.count control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { visit_some_page }.count
create_list(:issue, 5) create_list(:issue, 5)
......
...@@ -1417,8 +1417,11 @@ module Gitlab ...@@ -1417,8 +1417,11 @@ module Gitlab
end end
def can_be_merged?(source_sha, target_branch) def can_be_merged?(source_sha, target_branch)
target_sha = find_branch(target_branch, true).target if target_sha = find_branch(target_branch, true)&.target
!gitaly_conflicts_client(source_sha, target_sha).conflicts? !gitaly_conflicts_client(source_sha, target_sha).conflicts?
else
false
end
end end
def search_files_by_name(query, ref) def search_files_by_name(query, ref)
......
...@@ -16,12 +16,6 @@ module Gitlab ...@@ -16,12 +16,6 @@ module Gitlab
@last_minor_gc = Delta.new(GC.stat[:minor_gc_count]) @last_minor_gc = Delta.new(GC.stat[:minor_gc_count])
@last_major_gc = Delta.new(GC.stat[:major_gc_count]) @last_major_gc = Delta.new(GC.stat[:major_gc_count])
if Gitlab::Metrics.mri?
require 'allocations'
Allocations.start
end
end end
def sample def sample
......
...@@ -20,16 +20,6 @@ module Gitlab ...@@ -20,16 +20,6 @@ module Gitlab
{} {}
end end
def initialize(interval)
super(interval)
if Metrics.mri?
require 'allocations'
Allocations.start
end
end
def init_metrics def init_metrics
metrics = {} metrics = {}
metrics[:sampler_duration] = Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', { worker: nil }) metrics[:sampler_duration] = Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', { worker: nil })
......
...@@ -3,6 +3,7 @@ module Gitlab ...@@ -3,6 +3,7 @@ module Gitlab
class WebTransaction < Transaction class WebTransaction < Transaction
CONTROLLER_KEY = 'action_controller.instance'.freeze CONTROLLER_KEY = 'action_controller.instance'.freeze
ENDPOINT_KEY = 'api.endpoint'.freeze ENDPOINT_KEY = 'api.endpoint'.freeze
ALLOWED_SUFFIXES = Set.new(%w[json js atom rss xml zip])
def initialize(env) def initialize(env)
super() super()
...@@ -32,9 +33,13 @@ module Gitlab ...@@ -32,9 +33,13 @@ module Gitlab
# Devise exposes a method called "request_format" that does the below. # Devise exposes a method called "request_format" that does the below.
# However, this method is not available to all controllers (e.g. certain # However, this method is not available to all controllers (e.g. certain
# Doorkeeper controllers). As such we use the underlying code directly. # Doorkeeper controllers). As such we use the underlying code directly.
suffix = controller.request.format.try(:ref) suffix = controller.request.format.try(:ref).to_s
if suffix && suffix != :html # Sometimes the request format is set to silly data such as
# "application/xrds+xml" or actual URLs. To prevent such values from
# increasing the cardinality of our metrics, we limit the number of
# possible suffixes.
if suffix && ALLOWED_SUFFIXES.include?(suffix)
action += ".#{suffix}" action += ".#{suffix}"
end end
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
private private
def all_relation def all_relation
Upload.all Upload.all.preload(:model)
end end
def local?(upload) def local?(upload)
......
...@@ -16,7 +16,6 @@ namespace :gettext do ...@@ -16,7 +16,6 @@ namespace :gettext do
# See: https://gitlab.com/gitlab-org/gitlab-ce/issues/33014#note_31218998 # See: https://gitlab.com/gitlab-org/gitlab-ce/issues/33014#note_31218998
FileUtils.touch(File.join(Rails.root, 'locale/gitlab.pot')) FileUtils.touch(File.join(Rails.root, 'locale/gitlab.pot'))
Rake::Task['gettext:pack'].invoke
Rake::Task['gettext:po_to_json'].invoke Rake::Task['gettext:po_to_json'].invoke
end end
......
...@@ -46,10 +46,13 @@ module QA ...@@ -46,10 +46,13 @@ module QA
autoload :Runner, 'qa/factory/resource/runner' autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token' autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster' autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
autoload :Wiki, 'qa/factory/resource/wiki'
end end
module Repository module Repository
autoload :Push, 'qa/factory/repository/push' autoload :Push, 'qa/factory/repository/push'
autoload :ProjectPush, 'qa/factory/repository/project_push'
autoload :WikiPush, 'qa/factory/repository/wiki_push'
end end
module Settings module Settings
...@@ -165,6 +168,16 @@ module QA ...@@ -165,6 +168,16 @@ module QA
autoload :Show, 'qa/page/project/operations/kubernetes/show' autoload :Show, 'qa/page/project/operations/kubernetes/show'
end end
end end
module Wiki
autoload :Edit, 'qa/page/project/wiki/edit'
autoload :New, 'qa/page/project/wiki/new'
autoload :Show, 'qa/page/project/wiki/show'
end
end
module Shared
autoload :ClonePanel, 'qa/page/shared/clone_panel'
end end
module Profile module Profile
......
module QA
module Factory
module Repository
class ProjectPush < Factory::Repository::Push
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-code'
project.description = 'Project with repository'
end
product :output do |factory|
factory.output
end
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
@commit_message = "This is a test commit"
@branch_name = 'master'
@new_branch = true
end
def repository_uri
@repository_uri ||= begin
project.visit!
Page::Project::Show.act do
choose_repository_clone_http
repository_location.uri
end
end
end
end
end
end
end
...@@ -5,25 +5,17 @@ module QA ...@@ -5,25 +5,17 @@ module QA
module Repository module Repository
class Push < Factory::Base class Push < Factory::Base
attr_accessor :file_name, :file_content, :commit_message, attr_accessor :file_name, :file_content, :commit_message,
:branch_name, :new_branch, :output :branch_name, :new_branch, :output, :repository_uri
attr_writer :remote_branch attr_writer :remote_branch
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-code'
project.description = 'Project with repository'
end
product :output do |factory|
factory.output
end
def initialize def initialize
@file_name = 'file.txt' @file_name = 'file.txt'
@file_content = '# This is test project' @file_content = '# This is test file'
@commit_message = "This is a test commit" @commit_message = "This is a test commit"
@branch_name = 'master' @branch_name = 'master'
@new_branch = true @new_branch = true
@repository_uri = ""
end end
def remote_branch def remote_branch
...@@ -37,14 +29,8 @@ module QA ...@@ -37,14 +29,8 @@ module QA
end end
def fabricate! def fabricate!
project.visit!
Git::Repository.perform do |repository| Git::Repository.perform do |repository|
repository.uri = Page::Project::Show.act do repository.uri = repository_uri
choose_repository_clone_http
repository_location.uri
end
repository.use_default_credentials repository.use_default_credentials
repository.clone repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com') repository.configure_identity('GitLab QA', 'root@gitlab.com')
......
module QA
module Factory
module Repository
class WikiPush < Factory::Repository::Push
dependency Factory::Resource::Wiki, as: :wiki do |wiki|
wiki.title = 'Home'
wiki.content = '# My First Wiki Content'
wiki.message = 'Update home'
end
def initialize
@file_name = 'Home.md'
@file_content = '# Welcome to My Wiki'
@commit_message = 'Updating Home Page'
@branch_name = 'master'
@new_branch = false
end
def repository_uri
@repository_uri ||= begin
wiki.visit!
Page::Project::Wiki::Show.act do
go_to_clone_repository
choose_repository_clone_http
repository_location.uri
end
end
end
end
end
end
end
...@@ -31,13 +31,13 @@ module QA ...@@ -31,13 +31,13 @@ module QA
def fabricate! def fabricate!
project.visit! project.visit!
Factory::Repository::Push.fabricate! do |resource| Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project resource.project = project
resource.file_name = 'kick-off.txt' resource.file_name = 'kick-off.txt'
resource.commit_message = 'First commit' resource.commit_message = 'First commit'
end end
branch = Factory::Repository::Push.fabricate! do |resource| branch = Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project resource.project = project
resource.file_name = 'README.md' resource.file_name = 'README.md'
resource.commit_message = 'Add readme' resource.commit_message = 'Add readme'
......
...@@ -21,14 +21,14 @@ module QA ...@@ -21,14 +21,14 @@ module QA
project.name = 'project-with-merge-request' project.name = 'project-with-merge-request'
end end
dependency Factory::Repository::Push, as: :target do |push, factory| dependency Factory::Repository::ProjectPush, as: :target do |push, factory|
factory.project.visit! factory.project.visit!
push.project = factory.project push.project = factory.project
push.branch_name = 'master' push.branch_name = 'master'
push.remote_branch = factory.target_branch push.remote_branch = factory.target_branch
end end
dependency Factory::Repository::Push, as: :source do |push, factory| dependency Factory::Repository::ProjectPush, as: :source do |push, factory|
push.project = factory.project push.project = factory.project
push.branch_name = factory.target_branch push.branch_name = factory.target_branch
push.remote_branch = factory.source_branch push.remote_branch = factory.source_branch
......
module QA
module Factory
module Resource
class Wiki < Factory::Base
attr_accessor :title, :content, :message
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-for-wikis'
project.description = 'project for adding wikis'
end
def fabricate!
Page::Menu::Side.act { click_wiki }
Page::Project::Wiki::New.perform do |page|
page.go_to_create_first_page
page.set_title(@title)
page.set_content(@content)
page.set_message(@message)
page.create_new_page
end
end
end
end
end
end
...@@ -13,6 +13,7 @@ module QA ...@@ -13,6 +13,7 @@ module QA
element :top_level_items, '.sidebar-top-level-items' element :top_level_items, '.sidebar-top-level-items'
element :operations_section, "class: 'shortcuts-operations'" element :operations_section, "class: 'shortcuts-operations'"
element :activity_link, "title: 'Activity'" element :activity_link, "title: 'Activity'"
element :wiki_link_text, "Wiki"
end end
view 'app/assets/javascripts/fly_out_nav.js' do view 'app/assets/javascripts/fly_out_nav.js' do
...@@ -61,6 +62,12 @@ module QA ...@@ -61,6 +62,12 @@ module QA
end end
end end
def click_wiki
within_sidebar do
click_link('Wiki')
end
end
private private
def hover_settings def hover_settings
......
...@@ -2,11 +2,7 @@ module QA ...@@ -2,11 +2,7 @@ module QA
module Page module Page
module Project module Project
class Show < Page::Base class Show < Page::Base
view 'app/views/shared/_clone_panel.html.haml' do include Page::Shared::ClonePanel
element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown'
element :project_repository_location, 'text_field_tag :project_clone'
end
view 'app/views/projects/_last_push.html.haml' do view 'app/views/projects/_last_push.html.haml' do
element :create_merge_request element :create_merge_request
...@@ -26,21 +22,6 @@ module QA ...@@ -26,21 +22,6 @@ module QA
element :branches_dropdown element :branches_dropdown
end end
def choose_repository_clone_http
choose_repository_clone('HTTP', 'http')
end
def choose_repository_clone_ssh
# It's not always beginning with ssh:// so detecting with @
# would be more reliable because ssh would always contain it.
# We can't use .git because HTTP also contain that part.
choose_repository_clone('SSH', '@')
end
def repository_location
Git::Location.new(find('#project_clone').value)
end
def project_name def project_name
find('.qa-project-name').text find('.qa-project-name').text
end end
...@@ -65,31 +46,11 @@ module QA ...@@ -65,31 +46,11 @@ module QA
click_element :create_merge_request click_element :create_merge_request
end end
def wait_for_push
sleep 5
refresh
end
def go_to_new_issue def go_to_new_issue
click_element :new_menu_toggle click_element :new_menu_toggle
click_link 'New issue' click_link 'New issue'
end end
private
def choose_repository_clone(kind, detect_text)
wait(reload: false) do
click_element :clone_dropdown
page.within('.clone-options-dropdown') do
click_link(kind)
end
# Ensure git clone textbox was updated
repository_location.git_uri.include?(detect_text)
end
end
end end
end end
end end
......
module QA
module Page
module Project
module Wiki
class Edit < Page::Base
view 'app/views/projects/wikis/_main_links.html.haml' do
element :new_page_link, 'New page'
element :page_history_link, 'Page history'
element :edit_page_link, 'Edit'
end
def go_to_new_page
click_on 'New page'
end
def got_to_view_history_page
click_on 'Page history'
end
def go_to_edit_page
click_on 'Edit'
end
end
end
end
end
end
module QA
module Page
module Project
module Wiki
class New < Page::Base
view 'app/views/projects/wikis/_form.html.haml' do
element :wiki_title_textbox, 'text_field :title'
element :wiki_content_textarea, "render 'projects/zen', f: f, attr: :content"
element :wiki_message_textbox, 'text_field :message'
element :save_changes_button, 'submit _("Save changes")'
element :create_page_button, 'submit s_("Wiki|Create page")'
end
view 'app/views/shared/empty_states/_wikis.html.haml' do
element :create_link, 'Create your first page'
end
def go_to_create_first_page
click_link 'Create your first page'
end
def set_title(title)
fill_in 'wiki_title', with: title
end
def set_content(content)
fill_in 'wiki_content', with: content
end
def set_message(message)
fill_in 'wiki_message', with: message
end
def save_changes
click_on 'Save changes'
end
def create_new_page
click_on 'Create page'
end
end
end
end
end
end
module QA
module Page
module Project
module Wiki
class Show < Page::Base
include Page::Shared::ClonePanel
view 'app/views/projects/wikis/pages.html.haml' do
element :clone_repository_link, 'Clone repository'
end
def go_to_clone_repository
click_on 'Clone repository'
end
end
end
end
end
end
module QA
module Page
module Shared
module ClonePanel
def self.included(base)
base.view 'app/views/shared/_clone_panel.html.haml' do
element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown'
element :project_repository_location, 'text_field_tag :project_clone'
end
end
def choose_repository_clone_http
choose_repository_clone('HTTP', 'http')
end
def choose_repository_clone_ssh
# It's not always beginning with ssh:// so detecting with @
# would be more reliable because ssh would always contain it.
# We can't use .git because HTTP also contain that part.
choose_repository_clone('SSH', '@')
end
def repository_location
Git::Location.new(find('#project_clone').value)
end
def wait_for_push
sleep 5
refresh
end
private
def choose_repository_clone(kind, detect_text)
wait(reload: false) do
click_element :clone_dropdown
page.within('.clone-options-dropdown') do
click_link(kind)
end
# Ensure git clone textbox was updated
repository_location.git_uri.include?(detect_text)
end
end
end
end
end
end
...@@ -3,6 +3,8 @@ module QA ...@@ -3,6 +3,8 @@ module QA
module Env module Env
extend self extend self
attr_writer :user_type
# set to 'false' to have Chrome run visibly instead of headless # set to 'false' to have Chrome run visibly instead of headless
def chrome_headless? def chrome_headless?
(ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0 (ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
...@@ -20,7 +22,9 @@ module QA ...@@ -20,7 +22,9 @@ module QA
# By default, "standard" denotes a standard GitLab user login. # By default, "standard" denotes a standard GitLab user login.
# Set this to "ldap" if the user should be logged in via LDAP. # Set this to "ldap" if the user should be logged in via LDAP.
def user_type def user_type
(ENV['GITLAB_USER_TYPE'] || 'standard').tap do |type| return @user_type if defined?(@user_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables
ENV.fetch('GITLAB_USER_TYPE', 'standard').tap do |type|
unless %w(ldap standard).include?(type) unless %w(ldap standard).include?(type)
raise ArgumentError.new("Invalid user type '#{type}': must be 'ldap' or 'standard'") raise ArgumentError.new("Invalid user type '#{type}': must be 'ldap' or 'standard'")
end end
......
module QA module QA
feature 'LDAP user login', :ldap do feature 'LDAP user login', :ldap do
before do
Runtime::Env.user_type = 'ldap'
end
scenario 'user logs in using LDAP credentials' do scenario 'user logs in using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_ldap_credentials } Page::Main::Login.act { sign_in_using_credentials }
# TODO, since `Signed in successfully` message was removed # TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly. # this is the only way to tell if user is signed in correctly.
......
...@@ -16,7 +16,7 @@ module QA ...@@ -16,7 +16,7 @@ module QA
merge_request.title = 'Needs rebasing' merge_request.title = 'Needs rebasing'
end end
Factory::Repository::Push.fabricate! do |push| Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project push.project = project
push.file_name = "other.txt" push.file_name = "other.txt"
push.file_content = "New file added!" push.file_content = "New file added!"
......
...@@ -13,7 +13,7 @@ module QA ...@@ -13,7 +13,7 @@ module QA
merge_request.title = 'Squashing commits' merge_request.title = 'Squashing commits'
end end
Factory::Repository::Push.fabricate! do |push| Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project push.project = project
push.commit_message = 'to be squashed' push.commit_message = 'to be squashed'
push.branch_name = merge_request.source_branch push.branch_name = merge_request.source_branch
......
...@@ -4,7 +4,7 @@ module QA ...@@ -4,7 +4,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
Factory::Repository::Push.fabricate! do |push| Factory::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md' push.file_name = 'README.md'
push.file_content = '# This is a test project' push.file_content = '# This is a test project'
push.commit_message = 'Add README.md' push.commit_message = 'Add README.md'
......
...@@ -16,7 +16,7 @@ module QA ...@@ -16,7 +16,7 @@ module QA
end end
# Create Auto Devops compatible repo # Create Auto Devops compatible repo
Factory::Repository::Push.fabricate! do |push| Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project push.project = project
push.directory = Pathname push.directory = Pathname
.new(__dir__) .new(__dir__)
......
...@@ -75,7 +75,7 @@ module QA ...@@ -75,7 +75,7 @@ module QA
- docker - docker
YAML YAML
Factory::Repository::Push.fabricate! do |resource| Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = @project resource.project = @project
resource.file_name = '.gitlab-ci.yml' resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml' resource.commit_message = 'Add .gitlab-ci.yml'
......
...@@ -40,7 +40,7 @@ module QA ...@@ -40,7 +40,7 @@ module QA
runner.tags = %w[qa test] runner.tags = %w[qa test]
end end
Factory::Repository::Push.fabricate! do |push| Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project push.project = project
push.file_name = '.gitlab-ci.yml' push.file_name = '.gitlab-ci.yml'
push.commit_message = 'Add .gitlab-ci.yml' push.commit_message = 'Add .gitlab-ci.yml'
......
module QA
feature 'Wiki Functionality', :core do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
def validate_content(content)
expect(page).to have_content('Wiki was successfully updated')
expect(page).to have_content(/#{content}/)
end
before do
login
end
scenario 'User creates, edits, clones, and pushes to the wiki' do
wiki = Factory::Resource::Wiki.fabricate! do |resource|
resource.title = 'Home'
resource.content = '# My First Wiki Content'
resource.message = 'Update home'
end
validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.act { go_to_edit_page }
Page::Project::Wiki::New.perform do |page|
page.set_content("My Second Wiki Content")
page.save_changes
end
validate_content('My Second Wiki Content')
Factory::Repository::WikiPush.fabricate! do |push|
push.wiki = wiki
push.file_name = 'Home.md'
push.file_content = '# My Third Wiki Content'
push.commit_message = 'Update Home.md'
end
Page::Menu::Side.act { click_wiki }
expect(page).to have_content('My Third Wiki Content')
end
end
end
...@@ -56,7 +56,7 @@ module QA ...@@ -56,7 +56,7 @@ module QA
end end
def push_new_file(branch) def push_new_file(branch)
Factory::Repository::Push.fabricate! do |resource| Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project resource.project = project
resource.file_name = 'new_file.md' resource.file_name = 'new_file.md'
resource.file_content = '# This is a new file' resource.file_content = '# This is a new file'
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
Factory::Repository::Push.fabricate! do |push| Factory::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md' push.file_name = 'README.md'
push.file_content = '# This is a test project' push.file_content = '# This is a test project'
push.commit_message = 'Add README.md' push.commit_message = 'Add README.md'
......
...@@ -165,6 +165,10 @@ module Trigger ...@@ -165,6 +165,10 @@ module Trigger
end end
JSON.parse(res.body)['status'].to_s.to_sym JSON.parse(res.body)['status'].to_s.to_sym
rescue JSON::ParserError
# Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
# timeout anyway.
:running
end end
end end
end end
......
...@@ -45,6 +45,21 @@ import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer'; ...@@ -45,6 +45,21 @@ import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer';
expect(fakeTab.click).toHaveBeenCalled(); expect(fakeTab.click).toHaveBeenCalled();
}); });
it('clicks the first tab if value in local storage is bad', () => {
createMemoizer().saveData('#bogus');
const fakeTab = {
click: () => {},
};
spyOn(document, 'querySelector').and.callFake(selector => (selector === `${tabSelector} a[href="#bogus"]` ? null : fakeTab));
spyOn(fakeTab, 'click');
memo.bootstrap();
// verify that triggers click on stored selector and fallback
expect(document.querySelector.calls.allArgs()).toEqual([['ul.new-session-tabs a[href="#bogus"]'], ['ul.new-session-tabs a']]);
expect(fakeTab.click).toHaveBeenCalled();
});
it('saves last selected tab on change', () => { it('saves last selected tab on change', () => {
createMemoizer(); createMemoizer();
......
...@@ -3,10 +3,6 @@ require 'spec_helper' ...@@ -3,10 +3,6 @@ require 'spec_helper'
describe Gitlab::Metrics::Samplers::InfluxSampler do describe Gitlab::Metrics::Samplers::InfluxSampler do
let(:sampler) { described_class.new(5) } let(:sampler) { described_class.new(5) }
after do
Allocations.stop if Gitlab::Metrics.mri?
end
describe '#start' do describe '#start' do
it 'runs once and gathers a sample at a given interval' do it 'runs once and gathers a sample at a given interval' do
expect(sampler).to receive(:sleep).with(a_kind_of(Numeric)).twice expect(sampler).to receive(:sleep).with(a_kind_of(Numeric)).twice
......
...@@ -8,10 +8,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do ...@@ -8,10 +8,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do
allow(Gitlab::Metrics::NullMetric).to receive(:instance).and_return(null_metric) allow(Gitlab::Metrics::NullMetric).to receive(:instance).and_return(null_metric)
end end
after do
Allocations.stop if Gitlab::Metrics.mri?
end
describe '#sample' do describe '#sample' do
it 'samples various statistics' do it 'samples various statistics' do
expect(Gitlab::Metrics::System).to receive(:memory_usage) expect(Gitlab::Metrics::System).to receive(:memory_usage)
......
...@@ -218,7 +218,7 @@ describe Gitlab::Metrics::WebTransaction do ...@@ -218,7 +218,7 @@ describe Gitlab::Metrics::WebTransaction do
expect(transaction.action).to eq('TestController#show') expect(transaction.action).to eq('TestController#show')
end end
context 'when the response content type is not :html' do context 'when the request content type is not :html' do
let(:request) { double(:request, format: double(:format, ref: :json)) } let(:request) { double(:request, format: double(:format, ref: :json)) }
it 'appends the mime type to the transaction action' do it 'appends the mime type to the transaction action' do
...@@ -226,6 +226,15 @@ describe Gitlab::Metrics::WebTransaction do ...@@ -226,6 +226,15 @@ describe Gitlab::Metrics::WebTransaction do
expect(transaction.action).to eq('TestController#show.json') expect(transaction.action).to eq('TestController#show.json')
end end
end end
context 'when the request content type is not' do
let(:request) { double(:request, format: double(:format, ref: 'http://example.com')) }
it 'does not append the MIME type to the transaction action' do
expect(transaction.labels).to eq({ controller: 'TestController', action: 'show' })
expect(transaction.action).to eq('TestController#show')
end
end
end end
it 'returns no labels when no route information is present in env' do it 'returns no labels when no route information is present in env' do
......
...@@ -47,6 +47,10 @@ describe Gitlab::Verify::Uploads do ...@@ -47,6 +47,10 @@ describe Gitlab::Verify::Uploads do
before do before do
stub_uploads_object_storage(AvatarUploader) stub_uploads_object_storage(AvatarUploader)
upload.update!(store: ObjectStorage::Store::REMOTE) upload.update!(store: ObjectStorage::Store::REMOTE)
end
describe 'returned hash object' do
before do
expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end end
...@@ -63,5 +67,30 @@ describe Gitlab::Verify::Uploads do ...@@ -63,5 +67,30 @@ describe Gitlab::Verify::Uploads do
expect(failure).to include('Remote object does not exist') expect(failure).to include('Remote object does not exist')
end end
end end
describe 'performance' do
before do
allow(file).to receive(:exists?)
allow(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
it "avoids N+1 queries" do
control_count = ActiveRecord::QueryRecorder.new { perform_task }
# Create additional uploads in object storage
projects = create_list(:project, 3, :with_avatar)
uploads = projects.flat_map(&:uploads)
uploads.each do |upload|
upload.update!(store: ObjectStorage::Store::REMOTE)
end
expect { perform_task }.not_to exceed_query_limit(control_count)
end
def perform_task
described_class.new(batch_size: 100).run_batches { }
end
end
end
end end
end end
...@@ -427,16 +427,10 @@ describe Notify do ...@@ -427,16 +427,10 @@ describe Notify do
end end
it 'has the correct subject and body' do it 'has the correct subject and body' do
reasons = %w[foo bar]
allow_any_instance_of(MergeRequestPresenter).to receive(:unmergeable_reasons).and_return(reasons)
aggregate_failures do aggregate_failures do
is_expected.to have_referable_subject(merge_request, reply: true) is_expected.to have_referable_subject(merge_request, reply: true)
is_expected.to have_body_text(project_merge_request_path(project, merge_request)) is_expected.to have_body_text(project_merge_request_path(project, merge_request))
is_expected.to have_body_text('following reasons:') is_expected.to have_body_text('due to conflict.')
reasons.each do |reason|
is_expected.to have_body_text(reason)
end
end end
end end
end end
......
...@@ -1540,6 +1540,7 @@ describe MergeRequest do ...@@ -1540,6 +1540,7 @@ describe MergeRequest do
context 'when broken' do context 'when broken' do
before do before do
allow(subject).to receive(:broken?) { true } allow(subject).to receive(:broken?) { true }
allow(project.repository).to receive(:can_be_merged?).and_return(false)
end end
it 'becomes unmergeable' do it 'becomes unmergeable' do
...@@ -2652,9 +2653,11 @@ describe MergeRequest do ...@@ -2652,9 +2653,11 @@ describe MergeRequest do
before do before do
allow(NotificationService).to receive(:new).and_return(notification_service) allow(NotificationService).to receive(:new).and_return(notification_service)
allow(TodoService).to receive(:new).and_return(todo_service) allow(TodoService).to receive(:new).and_return(todo_service)
allow(subject.project.repository).to receive(:can_be_merged?).and_return(false)
end end
it 'notifies, but does not notify again if rechecking still results in cannot_be_merged' do it 'notifies conflict, but does not notify again if rechecking still results in cannot_be_merged' do
expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once
expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once
...@@ -2663,7 +2666,7 @@ describe MergeRequest do ...@@ -2663,7 +2666,7 @@ describe MergeRequest do
subject.mark_as_unmergeable subject.mark_as_unmergeable
end end
it 'notifies whenever merge request is newly unmergeable' do it 'notifies conflict, whenever newly unmergeable' do
expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice
expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice
...@@ -2673,6 +2676,15 @@ describe MergeRequest do ...@@ -2673,6 +2676,15 @@ describe MergeRequest do
subject.mark_as_unchecked subject.mark_as_unchecked
subject.mark_as_unmergeable subject.mark_as_unmergeable
end end
it 'does not notify whenever merge request is newly unmergeable due to other reasons' do
allow(subject.project.repository).to receive(:can_be_merged?).and_return(true)
expect(notification_service).not_to receive(:merge_request_unmergeable)
expect(todo_service).not_to receive(:merge_request_became_unmergeable)
subject.mark_as_unmergeable
end
end end
describe 'check_state?' do describe 'check_state?' do
......
...@@ -70,41 +70,6 @@ describe MergeRequestPresenter do ...@@ -70,41 +70,6 @@ describe MergeRequestPresenter do
end end
end end
describe "#unmergeable_reasons" do
let(:presenter) { described_class.new(resource, current_user: user) }
before do
# Mergeable base state
allow(resource).to receive(:has_no_commits?).and_return(false)
allow(resource).to receive(:source_branch_exists?).and_return(true)
allow(resource).to receive(:target_branch_exists?).and_return(true)
allow(resource.project.repository).to receive(:can_be_merged?).and_return(true)
end
it "handles mergeable request" do
expect(presenter.unmergeable_reasons).to eq([])
end
it "handles no commit" do
allow(resource).to receive(:has_no_commits?).and_return(true)
expect(presenter.unmergeable_reasons).to eq(["no commits"])
end
it "handles branches missing" do
allow(resource).to receive(:source_branch_exists?).and_return(false)
allow(resource).to receive(:target_branch_exists?).and_return(false)
expect(presenter.unmergeable_reasons).to eq(["source branch is missing", "target branch is missing"])
end
it "handles merge conflict" do
allow(resource.project.repository).to receive(:can_be_merged?).and_return(false)
expect(presenter.unmergeable_reasons).to eq(["has merge conflicts"])
end
end
describe '#conflict_resolution_path' do describe '#conflict_resolution_path' do
let(:project) { create :project } let(:project) { create :project }
let(:user) { create :user } let(:user) { create :user }
......
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