Commit ba085c6d authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-02-14

# Conflicts:
#	lib/api/entities.rb
#	lib/api/search.rb
#	lib/api/users.rb
#	qa/qa.rb

[ci skip]
parents 9cf49145 12447f12
...@@ -298,6 +298,10 @@ module ProjectsHelper ...@@ -298,6 +298,10 @@ module ProjectsHelper
nav_tabs << :pipelines nav_tabs << :pipelines
end end
if project.external_issue_tracker
nav_tabs << :external_issue_tracker
end
tab_ability_map.each do |tab, ability| tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project) if can?(current_user, ability, project)
nav_tabs << tab nav_tabs << tab
......
...@@ -317,7 +317,7 @@ class Member < ActiveRecord::Base ...@@ -317,7 +317,7 @@ class Member < ActiveRecord::Base
end end
def notification_setting def notification_setting
@notification_setting ||= user.notification_settings_for(source) @notification_setting ||= user&.notification_settings_for(source)
end end
def notifiable?(type, opts = {}) def notifiable?(type, opts = {})
......
...@@ -10,6 +10,8 @@ class JiraService < IssueTrackerService ...@@ -10,6 +10,8 @@ class JiraService < IssueTrackerService
before_update :reset_password before_update :reset_password
alias_method :project_url, :url
# This is confusing, but JiraService does not really support these events. # This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service # The values here are required to display correct options in the service
# configuration screen. # configuration screen.
......
...@@ -11,6 +11,7 @@ module Members ...@@ -11,6 +11,7 @@ module Members
Member.transaction do Member.transaction do
unassign_issues_and_merge_requests(member) unless member.invite? unassign_issues_and_merge_requests(member) unless member.invite?
member.notification_setting&.destroy
member.destroy member.destroy
end end
......
...@@ -141,6 +141,19 @@ ...@@ -141,6 +141,19 @@
= link_to project_milestones_path(@project), title: 'Milestones' do = link_to project_milestones_path(@project), title: 'Milestones' do
%span %span
Milestones Milestones
- if project_nav_tab? :external_issue_tracker
= nav_link do
- issue_tracker = @project.external_issue_tracker
= link_to issue_tracker.issue_tracker_path, class: 'shortcuts-external_tracker' do
.nav-icon-container
= sprite_icon('issue-external')
%span.nav-item-name
= issue_tracker.title
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(html_options: { class: "fly-out-top-item" } ) do
= link_to issue_tracker.issue_tracker_path do
%strong.fly-out-top-item-name
= issue_tracker.title
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
......
---
title: Display a link to external issue tracker when enabled
merge_request:
author:
type: changed
---
title: Remove user notification settings for groups and projects when user leaves
merge_request: 16906
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: 'API: Get references a commit is pushed to'
merge_request: 15026
author: Robert Schilling
type: added
...@@ -198,6 +198,41 @@ Example response: ...@@ -198,6 +198,41 @@ Example response:
} }
``` ```
## Get references a commit is pushed to
> [Introduced][ce-15026] in GitLab 10.6
Get all references (from branches or tags) a commit is pushed to.
The pagination parameters `page` and `per_page` can be used to restrict the list of references.
```
GET /projects/:id/repository/commits/:sha/refs
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit hash |
| `type` | string | no | The scope of commits. Possible values `branch`, `tag`, `all`. Default is `all`. |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/5937ac0a7beb003549fc5fd26fc247adbce4a52e/refs?type=all"
```
Example response:
```json
[
{"type": "branch", "name": "'test'"},
{"type": "branch", "name": "add-balsamiq-file"},
{"type": "branch", "name": "wip"},
{"type": "tag", "name": "v1.1.0"}
]
```
## Cherry pick a commit ## Cherry pick a commit
> [Introduced][ce-8047] in GitLab 8.15. > [Introduced][ce-8047] in GitLab 8.15.
...@@ -500,3 +535,4 @@ Example response: ...@@ -500,3 +535,4 @@ Example response:
[ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit" [ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit"
[ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047 [ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047
[ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026
...@@ -11,11 +11,7 @@ in the table below. ...@@ -11,11 +11,7 @@ in the table below.
| `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | | `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | | `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. |
Once you have configured and enabled Bugzilla: Once you have configured and enabled Bugzilla you'll see the Bugzilla link on the GitLab project pages that takes you to the appropriate Bugzilla project.
- the **Issues** link on the GitLab project pages takes you to the appropriate
Bugzilla product page
- clicking **New issue** on the project dashboard takes you to Bugzilla for entering a new issue
## Referencing issues in Bugzilla ## Referencing issues in Bugzilla
......
...@@ -118,7 +118,7 @@ in the table below. ...@@ -118,7 +118,7 @@ in the table below.
| `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** | | `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
After saving the configuration, your GitLab project will be able to interact After saving the configuration, your GitLab project will be able to interact
with all JIRA projects in your JIRA instance. with all JIRA projects in your JIRA instance and you'll see the JIRA link on the GitLab project pages that takes you to the appropriate JIRA project.
![JIRA service page](img/jira_service_page.png) ![JIRA service page](img/jira_service_page.png)
......
...@@ -12,6 +12,8 @@ in the table below. ...@@ -12,6 +12,8 @@ in the table below.
| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | | `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** | | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** |
Once you have configured and enabled Redmine you'll see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
As an example, below is a configuration for a project named gitlab-ci. As an example, below is a configuration for a project named gitlab-ci.
![Redmine configuration](img/redmine_configuration.png) ![Redmine configuration](img/redmine_configuration.png)
......
...@@ -161,6 +161,27 @@ module API ...@@ -161,6 +161,27 @@ module API
end end
end end
desc 'Get all references a commit is pushed to' do
detail 'This feature was introduced in GitLab 10.6'
success Entities::BasicRef
end
params do
requires :sha, type: String, desc: 'A commit sha'
optional :type, type: String, values: %w[branch tag all], default: 'all', desc: 'Scope'
use :pagination
end
get ':id/repository/commits/:sha/refs', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found!('Commit') unless commit
refs = []
refs.concat(user_project.repository.branch_names_contains(commit.id).map {|name| { type: 'branch', name: name }}) unless params[:type] == 'tag'
refs.concat(user_project.repository.tag_names_contains(commit.id).map {|name| { type: 'tag', name: name }}) unless params[:type] == 'branch'
refs = Kaminari.paginate_array(refs)
present paginate(refs), with: Entities::BasicRef
end
desc 'Post comment to commit' do desc 'Post comment to commit' do
success Entities::CommitNote success Entities::CommitNote
end end
...@@ -170,7 +191,7 @@ module API ...@@ -170,7 +191,7 @@ module API
optional :path, type: String, desc: 'The file path' optional :path, type: String, desc: 'The file path'
given :path do given :path do
requires :line, type: Integer, desc: 'The line number' requires :line, type: Integer, desc: 'The line number'
requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line' requires :line_type, type: String, values: %w[new old], default: 'new', desc: 'The type of the line'
end end
end end
post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
......
...@@ -309,6 +309,13 @@ module API ...@@ -309,6 +309,13 @@ module API
expose :status expose :status
expose :last_pipeline, using: 'API::Entities::PipelineBasic' expose :last_pipeline, using: 'API::Entities::PipelineBasic'
expose :project_id expose :project_id
<<<<<<< HEAD
=======
end
class BasicRef < Grape::Entity
expose :type, :name
>>>>>>> upstream/master
end end
class Branch < Grape::Entity class Branch < Grape::Entity
......
...@@ -108,8 +108,11 @@ module API ...@@ -108,8 +108,11 @@ module API
use :pagination use :pagination
end end
get ':id/-/search' do get ':id/-/search' do
<<<<<<< HEAD
check_elasticsearch_scope! check_elasticsearch_scope!
=======
>>>>>>> upstream/master
present search(group_id: user_group.id), with: entity present search(group_id: user_group.id), with: entity
end end
end end
......
...@@ -75,9 +75,12 @@ module API ...@@ -75,9 +75,12 @@ module API
use :sort_params use :sort_params
use :pagination use :pagination
use :with_custom_attributes use :with_custom_attributes
<<<<<<< HEAD
# EE # EE
optional :skip_ldap, type: Boolean, default: false, desc: 'Skip LDAP users' optional :skip_ldap, type: Boolean, default: false, desc: 'Skip LDAP users'
=======
>>>>>>> upstream/master
end end
get do get do
authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?) authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
......
...@@ -117,9 +117,14 @@ module QA ...@@ -117,9 +117,14 @@ module QA
autoload :Show, 'qa/page/project/pipeline/show' autoload :Show, 'qa/page/project/pipeline/show'
end end
<<<<<<< HEAD
module Pipeline module Pipeline
autoload :Index, 'qa/page/project/pipeline/index' autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show' autoload :Show, 'qa/page/project/pipeline/show'
=======
module Job
autoload :Show, 'qa/page/project/job/show'
>>>>>>> upstream/master
end end
module Settings module Settings
...@@ -170,6 +175,7 @@ module QA ...@@ -170,6 +175,7 @@ module QA
# #
module Git module Git
autoload :Repository, 'qa/git/repository' autoload :Repository, 'qa/git/repository'
autoload :Location, 'qa/git/location'
end end
## ##
......
...@@ -4,7 +4,7 @@ module QA ...@@ -4,7 +4,7 @@ module QA
module Factory module Factory
module Resource module Resource
class Runner < Factory::Base class Runner < Factory::Base
attr_writer :name, :tags attr_writer :name, :tags, :image
dependency Factory::Resource::Project, as: :project do |project| dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-ci-cd' project.name = 'project-with-ci-cd'
...@@ -19,6 +19,10 @@ module QA ...@@ -19,6 +19,10 @@ module QA
@tags || %w[qa e2e] @tags || %w[qa e2e]
end end
def image
@image || 'gitlab/gitlab-runner:alpine'
end
def fabricate! def fabricate!
project.visit! project.visit!
...@@ -31,6 +35,7 @@ module QA ...@@ -31,6 +35,7 @@ module QA
runner.token = runners.registration_token runner.token = runners.registration_token
runner.address = runners.coordinator_address runner.address = runners.coordinator_address
runner.tags = tags runner.tags = tags
runner.image = image
runner.register! runner.register!
end end
end end
......
require 'uri'
require 'forwardable'
module QA
module Git
class Location
extend Forwardable
attr_reader :git_uri, :uri
def_delegators :@uri, :user, :host, :path
# See: config/initializers/1_settings.rb
# Settings#build_gitlab_shell_ssh_path_prefix
def initialize(git_uri)
@git_uri = git_uri
@uri =
if git_uri.start_with?('ssh://')
URI.parse(git_uri)
else
*rest, path = git_uri.split(':')
# Host cannot have : so we'll need to escape it
user_host = rest.join('%3A').sub(/\A\[(.+)\]\z/, '\1')
URI.parse("ssh://#{user_host}/#{path}")
end
end
def port
uri.port || 22
end
end
end
end
require 'cgi'
require 'uri' require 'uri'
module QA module QA
......
...@@ -17,7 +17,8 @@ module QA ...@@ -17,7 +17,8 @@ module QA
start = Time.now start = Time.now
while Time.now - start < max while Time.now - start < max
return true if yield result = yield
return result if result
sleep(time) sleep(time)
......
module QA::Page
module Project::Job
class Show < QA::Page::Base
view 'app/views/projects/jobs/show.html.haml' do
element :build_output, '.js-build-output'
end
def output
css = '.js-build-output'
wait(reload: false) do
has_css?(css)
end
find(css).text
end
end
end
end
...@@ -6,7 +6,13 @@ module QA::Page ...@@ -6,7 +6,13 @@ module QA::Page
end end
def go_to_latest_pipeline def go_to_latest_pipeline
first('.js-pipeline-url-link').click css = '.js-pipeline-url-link'
link = wait(reload: false) do
first(css)
end
link.click
end end
end end
end end
......
...@@ -11,6 +11,7 @@ module QA::Page ...@@ -11,6 +11,7 @@ module QA::Page
view 'app/assets/javascripts/pipelines/components/graph/job_component.vue' do view 'app/assets/javascripts/pipelines/components/graph/job_component.vue' do
element :job_component, /class.*ci-job-component.*/ element :job_component, /class.*ci-job-component.*/
element :job_link, /class.*js-pipeline-graph-job-link.*/
end end
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
...@@ -30,6 +31,16 @@ module QA::Page ...@@ -30,6 +31,16 @@ module QA::Page
end end
end end
end end
def go_to_first_job
css = '.js-pipeline-graph-job-link'
wait(reload: false) do
has_css?(css)
end
first(css).click
end
end end
end end
end end
...@@ -22,22 +22,24 @@ module QA ...@@ -22,22 +22,24 @@ module QA
end end
def choose_repository_clone_http def choose_repository_clone_http
wait(reload: false) do choose_repository_clone('HTTP', 'http')
click_element :clone_dropdown
page.within('.clone-options-dropdown') do
click_link('HTTP')
end end
# Ensure git clone textbox was updated to http URI def choose_repository_clone_ssh
repository_location.include?('http') # It's not always beginning with ssh:// so detecting with @
end # 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 end
def repository_location def repository_location
find('#project_clone').value find('#project_clone').value
end end
def repository_location_uri
Git::Location.new(repository_location)
end
def project_name def project_name
find('.qa-project-name').text find('.qa-project-name').text
end end
...@@ -56,6 +58,21 @@ module QA ...@@ -56,6 +58,21 @@ module QA
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.include?(detect_text)
end
end
end end
end end
end end
......
...@@ -7,7 +7,7 @@ module QA ...@@ -7,7 +7,7 @@ module QA
extend Forwardable extend Forwardable
attr_reader :key attr_reader :key
def_delegators :@key, :fingerprint def_delegators :@key, :fingerprint, :to_pem
def initialize(bits = 4096) def initialize(bits = 4096)
@key = OpenSSL::PKey::RSA.new(bits) @key = OpenSSL::PKey::RSA.new(bits)
......
require 'digest/sha1'
module QA
feature 'cloning code using a deploy key', :core, :docker do
let(:runner_name) { "qa-runner-#{Time.now.to_i}" }
let(:key) { Runtime::RSAKey.new }
given(:project) do
Factory::Resource::Project.fabricate! do |resource|
resource.name = 'deploy-key-clone-project'
end
end
after do
Service::Runner.new(runner_name).remove!
end
scenario 'user sets up a deploy key to clone code using pipelines' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Runner.fabricate! do |resource|
resource.project = project
resource.name = runner_name
resource.tags = %w[qa docker]
resource.image = 'gitlab/gitlab-runner:ubuntu'
end
Factory::Resource::DeployKey.fabricate! do |resource|
resource.project = project
resource.title = 'deploy key title'
resource.key = key.public_key
end
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.project = project
resource.key = 'DEPLOY_KEY'
resource.value = key.to_pem
end
project.visit!
repository_uri = Page::Project::Show.act do
choose_repository_clone_ssh
repository_location_uri
end
gitlab_ci = <<~YAML
cat-config:
script:
- mkdir -p ~/.ssh
- ssh-keyscan -p #{repository_uri.port} #{repository_uri.host} >> ~/.ssh/known_hosts
- eval $(ssh-agent -s)
- echo "$DEPLOY_KEY" | ssh-add -
- git clone #{repository_uri.git_uri}
- sha1sum #{project.name}/.gitlab-ci.yml
tags:
- qa
- docker
YAML
Factory::Repository::Push.fabricate! do |resource|
resource.project = project
resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml'
resource.file_content = gitlab_ci
end
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
Page::Project::Show.act { wait_for_push }
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.act { go_to_first_job }
Page::Project::Job::Show.perform do |job|
expect(job.output).to include(sha1sum)
end
end
end
end
describe QA::Git::Location do
describe '.new' do
context 'when URI starts with ssh://' do
context 'when URI has port' do
it 'parses correctly' do
uri = described_class
.new('ssh://git@qa.test:2222/sandbox/qa/repo.git')
expect(uri.user).to eq('git')
expect(uri.host).to eq('qa.test')
expect(uri.port).to eq(2222)
expect(uri.path).to eq('/sandbox/qa/repo.git')
end
end
context 'when URI does not have port' do
it 'parses correctly' do
uri = described_class
.new('ssh://git@qa.test/sandbox/qa/repo.git')
expect(uri.user).to eq('git')
expect(uri.host).to eq('qa.test')
expect(uri.port).to eq(22)
expect(uri.path).to eq('/sandbox/qa/repo.git')
end
end
end
context 'when URI does not start with ssh://' do
context 'when host does not have colons' do
it 'parses correctly' do
uri = described_class
.new('git@qa.test:sandbox/qa/repo.git')
expect(uri.user).to eq('git')
expect(uri.host).to eq('qa.test')
expect(uri.port).to eq(22)
expect(uri.path).to eq('/sandbox/qa/repo.git')
end
end
context 'when host has a colon' do
it 'parses correctly' do
uri = described_class
.new('[git@qa:test]:sandbox/qa/repo.git')
expect(uri.user).to eq('git')
expect(uri.host).to eq('qa%3Atest')
expect(uri.port).to eq(22)
expect(uri.path).to eq('/sandbox/qa/repo.git')
end
end
end
end
end
#!/usr/bin/env ruby
require 'digest'
require 'fileutils'
harness_path = File.expand_path('../.git/security_harness', __dir__)
hook_path = File.expand_path("../.git/hooks/pre-push", __dir__)
if File.exist?(hook_path)
# Deal with a pre-existing hook
source_sum = Digest::SHA256.hexdigest(DATA.read)
dest_sum = Digest::SHA256.file(hook_path).hexdigest
if source_sum != dest_sum
puts "#{hook_path} exists and is different from our hook!"
puts "Remove it and re-run this script to continue."
exit 1
end
else
File.open(hook_path, 'w') do |file|
IO.copy_stream(DATA, file)
end
end
# Toggle the harness on or off
if File.exist?(harness_path)
FileUtils.rm(harness_path)
puts "Security harness removed -- you can now push to all remotes."
else
FileUtils.touch(harness_path)
puts "Security harness installed -- you will only be able to push to dev.gitlab.org!"
end
__END__
#!/bin/sh
set -e
url="$2"
harness=`dirname "$0"`/../security_harness
if [ -e "$harness" ]
then
if [[ "$url" != *"dev.gitlab.org"* ]]
then
echo "Pushing to remotes other than dev.gitlab.org has been disabled!"
echo "Run scripts/security-harness to disable this check."
echo
exit 1
fi
fi
require 'spec_helper'
describe 'User activates issue tracker', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:url) { 'http://tracker.example.com' }
def fill_form(active = true)
check 'Active' if active
fill_in 'service_project_url', with: url
fill_in 'service_issues_url', with: "#{url}/:id"
fill_in 'service_new_issue_url', with: url
end
before do
project.add_master(user)
sign_in(user)
visit project_settings_integrations_path(project)
end
shared_examples 'external issue tracker activation' do |tracker:|
describe 'user sets and activates the Service' do
context 'when the connection test succeeds' do
before do
stub_request(:head, url).to_return(headers: { 'Content-Type' => 'application/json' })
click_link(tracker)
fill_form
click_button('Test settings and save changes')
wait_for_requests
end
it 'activates the service' do
expect(page).to have_content("#{tracker} activated.")
expect(current_path).to eq(project_settings_integrations_path(project))
end
it 'shows the link in the menu' do
page.within('.nav-sidebar') do
expect(page).to have_link(tracker, href: url)
end
end
end
context 'when the connection test fails' do
it 'activates the service' do
stub_request(:head, url).to_raise(HTTParty::Error)
click_link(tracker)
fill_form
click_button('Test settings and save changes')
wait_for_requests
expect(find('.flash-container-page')).to have_content 'Test failed.'
expect(find('.flash-container-page')).to have_content 'Save anyway'
find('.flash-alert .flash-action').click
wait_for_requests
expect(page).to have_content("#{tracker} activated.")
expect(current_path).to eq(project_settings_integrations_path(project))
end
end
end
describe 'user sets the service but keeps it disabled' do
before do
click_link(tracker)
fill_form(false)
click_button('Save changes')
end
it 'saves but does not activate the service' do
expect(page).to have_content("#{tracker} settings saved, but not activated.")
expect(current_path).to eq(project_settings_integrations_path(project))
end
it 'does not show the external tracker link in the menu' do
page.within('.nav-sidebar') do
expect(page).not_to have_link(tracker, href: url)
end
end
end
end
it_behaves_like 'external issue tracker activation', tracker: 'Redmine'
it_behaves_like 'external issue tracker activation', tracker: 'Bugzilla'
it_behaves_like 'external issue tracker activation', tracker: 'Custom Issue Tracker'
end
...@@ -3,7 +3,6 @@ require 'spec_helper' ...@@ -3,7 +3,6 @@ require 'spec_helper'
describe 'User activates Jira', :js do describe 'User activates Jira', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:service) { project.create_jira_service }
let(:url) { 'http://jira.example.com' } let(:url) { 'http://jira.example.com' }
let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' } let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' }
...@@ -26,7 +25,7 @@ describe 'User activates Jira', :js do ...@@ -26,7 +25,7 @@ describe 'User activates Jira', :js do
describe 'user sets and activates Jira Service' do describe 'user sets and activates Jira Service' do
context 'when Jira connection test succeeds' do context 'when Jira connection test succeeds' do
it 'activates the JIRA service' do before do
server_info = { key: 'value' }.to_json server_info = { key: 'value' }.to_json
WebMock.stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info) WebMock.stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info)
...@@ -34,10 +33,18 @@ describe 'User activates Jira', :js do ...@@ -34,10 +33,18 @@ describe 'User activates Jira', :js do
fill_form fill_form
click_button('Test settings and save changes') click_button('Test settings and save changes')
wait_for_requests wait_for_requests
end
it 'activates the JIRA service' do
expect(page).to have_content('JIRA activated.') expect(page).to have_content('JIRA activated.')
expect(current_path).to eq(project_settings_integrations_path(project)) expect(current_path).to eq(project_settings_integrations_path(project))
end end
it 'shows the JIRA link in the menu' do
page.within('.nav-sidebar') do
expect(page).to have_link('JIRA', href: url)
end
end
end end
context 'when Jira connection test fails' do context 'when Jira connection test fails' do
...@@ -75,15 +82,21 @@ describe 'User activates Jira', :js do ...@@ -75,15 +82,21 @@ describe 'User activates Jira', :js do
end end
describe 'user sets Jira Service but keeps it disabled' do describe 'user sets Jira Service but keeps it disabled' do
context 'when Jira connection test succeeds' do before do
it 'activates the JIRA service' do
click_link('JIRA') click_link('JIRA')
fill_form(false) fill_form(false)
click_button('Save changes') click_button('Save changes')
end
it 'saves but does not activate the JIRA service' do
expect(page).to have_content('JIRA settings saved, but not activated.') expect(page).to have_content('JIRA settings saved, but not activated.')
expect(current_path).to eq(project_settings_integrations_path(project)) expect(current_path).to eq(project_settings_integrations_path(project))
end end
it 'does not show the JIRA link in the menu' do
page.within('.nav-sidebar') do
expect(page).not_to have_link('JIRA', href: url)
end
end end
end end
end end
...@@ -473,6 +473,72 @@ describe API::Commits do ...@@ -473,6 +473,72 @@ describe API::Commits do
end end
end end
describe 'GET /projects/:id/repository/commits/:sha/refs' do
let(:project) { create(:project, :public, :repository) }
let(:tag) { project.repository.find_tag('v1.1.0') }
let(:commit_id) { tag.dereferenced_target.id }
let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/refs" }
context 'when ref does not exist' do
let(:commit_id) { 'unknown' }
it_behaves_like '404 response' do
let(:request) { get api(route, current_user) }
let(:message) { '404 Commit Not Found' }
end
end
context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do
let(:request) { get api(route, current_user) }
end
end
context 'for a valid commit' do
it 'returns all refs with no scope' do
get api(route, current_user), per_page: 100
refs = project.repository.branch_names_contains(commit_id).map {|name| ['branch', name]}
refs.concat(project.repository.tag_names_contains(commit_id).map {|name| ['tag', name]})
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
end
it 'returns all refs' do
get api(route, current_user), type: 'all', per_page: 100
refs = project.repository.branch_names_contains(commit_id).map {|name| ['branch', name]}
refs.concat(project.repository.tag_names_contains(commit_id).map {|name| ['tag', name]})
expect(response).to have_gitlab_http_status(200)
expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
end
it 'returns the branch refs' do
get api(route, current_user), type: 'branch', per_page: 100
refs = project.repository.branch_names_contains(commit_id).map {|name| ['branch', name]}
expect(response).to have_gitlab_http_status(200)
expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
end
it 'returns the tag refs' do
get api(route, current_user), type: 'tag', per_page: 100
refs = project.repository.tag_names_contains(commit_id).map {|name| ['tag', name]}
expect(response).to have_gitlab_http_status(200)
expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
end
end
end
describe 'GET /projects/:id/repository/commits/:sha' do describe 'GET /projects/:id/repository/commits/:sha' do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
let(:commit_id) { commit.id } let(:commit_id) { commit.id }
......
...@@ -21,6 +21,15 @@ describe Members::AuthorizedDestroyService do ...@@ -21,6 +21,15 @@ describe Members::AuthorizedDestroyService do
.to change { Member.count }.from(3).to(2) .to change { Member.count }.from(3).to(2)
end end
it "doesn't destroy invited project member notification_settings" do
project.add_developer(member_user)
member = create :project_member, :invited, project: project
expect { described_class.new(member, member_user).execute }
.not_to change { NotificationSetting.count }
end
it 'destroys invited group member' do it 'destroys invited group member' do
group.add_developer(member_user) group.add_developer(member_user)
...@@ -29,38 +38,73 @@ describe Members::AuthorizedDestroyService do ...@@ -29,38 +38,73 @@ describe Members::AuthorizedDestroyService do
expect { described_class.new(member, member_user).execute } expect { described_class.new(member, member_user).execute }
.to change { Member.count }.from(2).to(1) .to change { Member.count }.from(2).to(1)
end end
it "doesn't destroy invited group member notification_settings" do
group.add_developer(member_user)
member = create :group_member, :invited, group: group
expect { described_class.new(member, member_user).execute }
.not_to change { NotificationSetting.count }
end
end
context 'Requested user' do
it "doesn't destroy member notification_settings" do
member = create(:project_member, user: member_user, requested_at: Time.now)
expect { described_class.new(member, member_user).execute }
.not_to change { NotificationSetting.count }
end
end end
context 'Group member' do context 'Group member' do
it "unassigns issues and merge requests" do let(:member) { group.members.find_by(user_id: member_user.id) }
before do
group.add_developer(member_user) group.add_developer(member_user)
end
it "unassigns issues and merge requests" do
issue = create :issue, project: group_project, assignees: [member_user] issue = create :issue, project: group_project, assignees: [member_user]
create :issue, assignees: [member_user] create :issue, assignees: [member_user]
merge_request = create :merge_request, target_project: group_project, source_project: group_project, assignee: member_user merge_request = create :merge_request, target_project: group_project, source_project: group_project, assignee: member_user
create :merge_request, target_project: project, source_project: project, assignee: member_user create :merge_request, target_project: project, source_project: project, assignee: member_user
member = group.members.find_by(user_id: member_user.id)
expect { described_class.new(member, member_user).execute } expect { described_class.new(member, member_user).execute }
.to change { number_of_assigned_issuables(member_user) }.from(4).to(2) .to change { number_of_assigned_issuables(member_user) }.from(4).to(2)
expect(issue.reload.assignee_ids).to be_empty expect(issue.reload.assignee_ids).to be_empty
expect(merge_request.reload.assignee_id).to be_nil expect(merge_request.reload.assignee_id).to be_nil
end end
it 'destroys member notification_settings' do
group.add_developer(member_user)
member = group.members.find_by(user_id: member_user.id)
expect { described_class.new(member, member_user).execute }
.to change { member_user.notification_settings.count }.by(-1)
end
end end
context 'Project member' do context 'Project member' do
it "unassigns issues and merge requests" do let(:member) { project.members.find_by(user_id: member_user.id) }
before do
project.add_developer(member_user) project.add_developer(member_user)
end
it "unassigns issues and merge requests" do
create :issue, project: project, assignees: [member_user] create :issue, project: project, assignees: [member_user]
create :merge_request, target_project: project, source_project: project, assignee: member_user create :merge_request, target_project: project, source_project: project, assignee: member_user
member = project.members.find_by(user_id: member_user.id)
expect { described_class.new(member, member_user).execute } expect { described_class.new(member, member_user).execute }
.to change { number_of_assigned_issuables(member_user) }.from(2).to(0) .to change { number_of_assigned_issuables(member_user) }.from(2).to(0)
end end
it 'destroys member notification_settings' do
expect { described_class.new(member, member_user).execute }
.to change { member_user.notification_settings.count }.by(-1)
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment