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'
# Metrics
group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri
gem 'method_source', '~> 0.8', require: false
gem 'influxdb', '~> 0.2', require: false
......
......@@ -49,7 +49,6 @@ GEM
public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1)
akismet (2.0.0)
allocations (1.0.5)
arel (6.0.4)
asana (0.6.0)
faraday (~> 0.9)
......@@ -1003,7 +1002,6 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
allocations (~> 1.0)
asana (~> 0.6.0)
asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8)
......
......@@ -52,7 +52,6 @@ GEM
public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1)
akismet (2.0.0)
allocations (1.0.5)
arel (7.1.4)
asana (0.6.0)
faraday (~> 0.9)
......@@ -1013,7 +1012,6 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
allocations (~> 1.0)
asana (~> 0.6.0)
asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8)
......
<<<<<<< HEAD
import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_token_keys_issues';
=======
>>>>>>> upstream/master
import FilteredSearchContainer from '../filtered_search/container';
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
......
......@@ -117,7 +117,7 @@ export default {
class="btn btn-primary btn-sm btn-block"
@click="toggleIsSmall"
>
{{ __('Commit') }}
{{ __('Commit') }}
</button>
<p
class="text-center"
......
......@@ -37,6 +37,11 @@ export default class SigninTabsMemoizer {
const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`);
if (tab) {
tab.click();
} else {
const firstTab = document.querySelector(`${this.tabSelector} a`);
if (firstTab) {
firstTab.click();
}
}
}
}
......
......@@ -558,7 +558,7 @@
background: $white-light;
border-bottom: 1px solid $white-normal;
.center-logo {
.mx-auto {
margin: 8px 0;
text-align: center;
......
......@@ -59,8 +59,6 @@ module Emails
def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil)
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))
end
......
......@@ -132,8 +132,17 @@ class MergeRequest < ActiveRecord::Base
end
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
NotificationService.new.merge_request_unmergeable(merge_request)
TodoService.new.merge_request_became_unmergeable(merge_request)
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)
TodoService.new.merge_request_became_unmergeable(merge_request)
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)
......
......@@ -22,17 +22,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
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
if can_cancel_merge_when_pipeline_succeeds?(current_user)
cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request)
......
%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)}:
%ul
- @reasons.each do |reason|
%li= reason
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.
Merge Request #{@merge_request.to_reference} can no longer be merged due to the following #{'reason'.pluralize(@reasons.count)}:
- @reasons.each do |reason|
* #{reason}
Merge Request #{@merge_request.to_reference} can no longer be merged due to conflict.
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
......@@ -16,5 +16,6 @@
= _('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')
- else
- 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 }
.settings-message.text-center
- 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 }
......@@ -2,10 +2,10 @@
.form-group.row
= f.label :start_date, "Start Date", class: "col-form-label col-sm-2"
.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
.form-group.row
= f.label :due_date, "Due Date", class: "col-form-label col-sm-2"
.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
---
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
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:
```
it "avoids N+1 database queries" do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { visit_some_page }.count
create_list(:issue, 5)
......
......@@ -1417,8 +1417,11 @@ module Gitlab
end
def can_be_merged?(source_sha, target_branch)
target_sha = find_branch(target_branch, true).target
!gitaly_conflicts_client(source_sha, target_sha).conflicts?
if target_sha = find_branch(target_branch, true)&.target
!gitaly_conflicts_client(source_sha, target_sha).conflicts?
else
false
end
end
def search_files_by_name(query, ref)
......
......@@ -16,12 +16,6 @@ module Gitlab
@last_minor_gc = Delta.new(GC.stat[:minor_gc_count])
@last_major_gc = Delta.new(GC.stat[:major_gc_count])
if Gitlab::Metrics.mri?
require 'allocations'
Allocations.start
end
end
def sample
......
......@@ -20,16 +20,6 @@ module Gitlab
{}
end
def initialize(interval)
super(interval)
if Metrics.mri?
require 'allocations'
Allocations.start
end
end
def init_metrics
metrics = {}
metrics[:sampler_duration] = Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', { worker: nil })
......
......@@ -3,6 +3,7 @@ module Gitlab
class WebTransaction < Transaction
CONTROLLER_KEY = 'action_controller.instance'.freeze
ENDPOINT_KEY = 'api.endpoint'.freeze
ALLOWED_SUFFIXES = Set.new(%w[json js atom rss xml zip])
def initialize(env)
super()
......@@ -32,9 +33,13 @@ module Gitlab
# Devise exposes a method called "request_format" that does the below.
# However, this method is not available to all controllers (e.g. certain
# 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}"
end
......
......@@ -12,7 +12,7 @@ module Gitlab
private
def all_relation
Upload.all
Upload.all.preload(:model)
end
def local?(upload)
......
......@@ -16,7 +16,6 @@ namespace :gettext do
# See: https://gitlab.com/gitlab-org/gitlab-ce/issues/33014#note_31218998
FileUtils.touch(File.join(Rails.root, 'locale/gitlab.pot'))
Rake::Task['gettext:pack'].invoke
Rake::Task['gettext:po_to_json'].invoke
end
......
......@@ -46,10 +46,13 @@ module QA
autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
autoload :Wiki, 'qa/factory/resource/wiki'
end
module Repository
autoload :Push, 'qa/factory/repository/push'
autoload :ProjectPush, 'qa/factory/repository/project_push'
autoload :WikiPush, 'qa/factory/repository/wiki_push'
end
module Settings
......@@ -165,6 +168,16 @@ module QA
autoload :Show, 'qa/page/project/operations/kubernetes/show'
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
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
module Repository
class Push < Factory::Base
attr_accessor :file_name, :file_content, :commit_message,
:branch_name, :new_branch, :output
:branch_name, :new_branch, :output, :repository_uri
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
@file_name = 'file.txt'
@file_content = '# This is test project'
@file_content = '# This is test file'
@commit_message = "This is a test commit"
@branch_name = 'master'
@new_branch = true
@repository_uri = ""
end
def remote_branch
......@@ -37,14 +29,8 @@ module QA
end
def fabricate!
project.visit!
Git::Repository.perform do |repository|
repository.uri = Page::Project::Show.act do
choose_repository_clone_http
repository_location.uri
end
repository.uri = repository_uri
repository.use_default_credentials
repository.clone
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
def fabricate!
project.visit!
Factory::Repository::Push.fabricate! do |resource|
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.file_name = 'kick-off.txt'
resource.commit_message = 'First commit'
end
branch = Factory::Repository::Push.fabricate! do |resource|
branch = Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.file_name = 'README.md'
resource.commit_message = 'Add readme'
......
......@@ -21,14 +21,14 @@ module QA
project.name = 'project-with-merge-request'
end
dependency Factory::Repository::Push, as: :target do |push, factory|
dependency Factory::Repository::ProjectPush, as: :target do |push, factory|
factory.project.visit!
push.project = factory.project
push.branch_name = 'master'
push.remote_branch = factory.target_branch
end
dependency Factory::Repository::Push, as: :source do |push, factory|
dependency Factory::Repository::ProjectPush, as: :source do |push, factory|
push.project = factory.project
push.branch_name = factory.target_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
element :top_level_items, '.sidebar-top-level-items'
element :operations_section, "class: 'shortcuts-operations'"
element :activity_link, "title: 'Activity'"
element :wiki_link_text, "Wiki"
end
view 'app/assets/javascripts/fly_out_nav.js' do
......@@ -61,6 +62,12 @@ module QA
end
end
def click_wiki
within_sidebar do
click_link('Wiki')
end
end
private
def hover_settings
......
......@@ -2,11 +2,7 @@ module QA
module Page
module Project
class Show < Page::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
include Page::Shared::ClonePanel
view 'app/views/projects/_last_push.html.haml' do
element :create_merge_request
......@@ -26,21 +22,6 @@ module QA
element :branches_dropdown
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
find('.qa-project-name').text
end
......@@ -65,31 +46,11 @@ module QA
click_element :create_merge_request
end
def wait_for_push
sleep 5
refresh
end
def go_to_new_issue
click_element :new_menu_toggle
click_link 'New issue'
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
......
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
module Env
extend self
attr_writer :user_type
# set to 'false' to have Chrome run visibly instead of headless
def chrome_headless?
(ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
......@@ -20,7 +22,9 @@ module QA
# By default, "standard" denotes a standard GitLab user login.
# Set this to "ldap" if the user should be logged in via LDAP.
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)
raise ArgumentError.new("Invalid user type '#{type}': must be 'ldap' or 'standard'")
end
......
module QA
feature 'LDAP user login', :ldap do
before do
Runtime::Env.user_type = 'ldap'
end
scenario 'user logs in using LDAP credentials' do
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
# this is the only way to tell if user is signed in correctly.
......
......@@ -16,7 +16,7 @@ module QA
merge_request.title = 'Needs rebasing'
end
Factory::Repository::Push.fabricate! do |push|
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = "other.txt"
push.file_content = "New file added!"
......
......@@ -13,7 +13,7 @@ module QA
merge_request.title = 'Squashing commits'
end
Factory::Repository::Push.fabricate! do |push|
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.commit_message = 'to be squashed'
push.branch_name = merge_request.source_branch
......
......@@ -4,7 +4,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
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_content = '# This is a test project'
push.commit_message = 'Add README.md'
......
......@@ -16,7 +16,7 @@ module QA
end
# Create Auto Devops compatible repo
Factory::Repository::Push.fabricate! do |push|
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.directory = Pathname
.new(__dir__)
......
......@@ -75,7 +75,7 @@ module QA
- docker
YAML
Factory::Repository::Push.fabricate! do |resource|
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = @project
resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml'
......
......@@ -40,7 +40,7 @@ module QA
runner.tags = %w[qa test]
end
Factory::Repository::Push.fabricate! do |push|
Factory::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_name = '.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
end
def push_new_file(branch)
Factory::Repository::Push.fabricate! do |resource|
Factory::Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.file_name = 'new_file.md'
resource.file_content = '# This is a new file'
......
......@@ -5,7 +5,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
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_content = '# This is a test project'
push.commit_message = 'Add README.md'
......
......@@ -165,6 +165,10 @@ module Trigger
end
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
......
......@@ -45,6 +45,21 @@ import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer';
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', () => {
createMemoizer();
......
......@@ -3,10 +3,6 @@ require 'spec_helper'
describe Gitlab::Metrics::Samplers::InfluxSampler do
let(:sampler) { described_class.new(5) }
after do
Allocations.stop if Gitlab::Metrics.mri?
end
describe '#start' do
it 'runs once and gathers a sample at a given interval' do
expect(sampler).to receive(:sleep).with(a_kind_of(Numeric)).twice
......
......@@ -8,10 +8,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do
allow(Gitlab::Metrics::NullMetric).to receive(:instance).and_return(null_metric)
end
after do
Allocations.stop if Gitlab::Metrics.mri?
end
describe '#sample' do
it 'samples various statistics' do
expect(Gitlab::Metrics::System).to receive(:memory_usage)
......
......@@ -218,7 +218,7 @@ describe Gitlab::Metrics::WebTransaction do
expect(transaction.action).to eq('TestController#show')
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)) }
it 'appends the mime type to the transaction action' do
......@@ -226,6 +226,15 @@ describe Gitlab::Metrics::WebTransaction do
expect(transaction.action).to eq('TestController#show.json')
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
it 'returns no labels when no route information is present in env' do
......
......@@ -47,20 +47,49 @@ describe Gitlab::Verify::Uploads do
before do
stub_uploads_object_storage(AvatarUploader)
upload.update!(store: ObjectStorage::Store::REMOTE)
expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
it 'passes uploads in object storage that exist' do
expect(file).to receive(:exists?).and_return(true)
describe 'returned hash object' do
before do
expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
expect(failures).to eq({})
it 'passes uploads in object storage that exist' do
expect(file).to receive(:exists?).and_return(true)
expect(failures).to eq({})
end
it 'fails uploads in object storage that do not exist' do
expect(file).to receive(:exists?).and_return(false)
expect(failures.keys).to contain_exactly(upload)
expect(failure).to include('Remote object does not exist')
end
end
it 'fails uploads in object storage that do not exist' do
expect(file).to receive(:exists?).and_return(false)
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
expect(failures.keys).to contain_exactly(upload)
expect(failure).to include('Remote object does not exist')
def perform_task
described_class.new(batch_size: 100).run_batches { }
end
end
end
end
......
......@@ -427,16 +427,10 @@ describe Notify do
end
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
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('following reasons:')
reasons.each do |reason|
is_expected.to have_body_text(reason)
end
is_expected.to have_body_text('due to conflict.')
end
end
end
......
......@@ -1540,6 +1540,7 @@ describe MergeRequest do
context 'when broken' do
before do
allow(subject).to receive(:broken?) { true }
allow(project.repository).to receive(:can_be_merged?).and_return(false)
end
it 'becomes unmergeable' do
......@@ -2652,9 +2653,11 @@ describe MergeRequest do
before do
allow(NotificationService).to receive(:new).and_return(notification_service)
allow(TodoService).to receive(:new).and_return(todo_service)
allow(subject.project.repository).to receive(:can_be_merged?).and_return(false)
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(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once
......@@ -2663,7 +2666,7 @@ describe MergeRequest do
subject.mark_as_unmergeable
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(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice
......@@ -2673,6 +2676,15 @@ describe MergeRequest do
subject.mark_as_unchecked
subject.mark_as_unmergeable
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
describe 'check_state?' do
......
......@@ -70,41 +70,6 @@ describe MergeRequestPresenter do
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
let(:project) { create :project }
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