Commit 1f32cbbf authored by Marin Jankovski's avatar Marin Jankovski

Merge branch 'ce-to-ee-2018-02-14' into 'master'

CE upstream - 2018-02-14 12:25 UTC

Closes gitlab-qa#148

See merge request gitlab-org/gitlab-ee!4535
parents 6e627c85 39879c7b
......@@ -298,6 +298,10 @@ module ProjectsHelper
nav_tabs << :pipelines
end
if project.external_issue_tracker
nav_tabs << :external_issue_tracker
end
tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project)
nav_tabs << tab
......
......@@ -317,7 +317,7 @@ class Member < ActiveRecord::Base
end
def notification_setting
@notification_setting ||= user.notification_settings_for(source)
@notification_setting ||= user&.notification_settings_for(source)
end
def notifiable?(type, opts = {})
......
......@@ -10,6 +10,8 @@ class JiraService < IssueTrackerService
before_update :reset_password
alias_method :project_url, :url
# This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service
# configuration screen.
......
......@@ -11,6 +11,7 @@ module Members
Member.transaction do
unassign_issues_and_merge_requests(member) unless member.invite?
member.notification_setting&.destroy
member.destroy
end
......
......@@ -141,6 +141,19 @@
= link_to project_milestones_path(@project), title: 'Milestones' do
%span
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
= 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:
}
```
## 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
> [Introduced][ce-8047] in GitLab 8.15.
......@@ -500,3 +535,4 @@ Example response:
[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-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026
......@@ -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. |
| `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:
- 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
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.
## Referencing issues in Bugzilla
......
......@@ -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.** |
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)
......
......@@ -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. |
| `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.
![Redmine configuration](img/redmine_configuration.png)
......
......@@ -161,6 +161,27 @@ module API
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
success Entities::CommitNote
end
......@@ -170,7 +191,7 @@ module API
optional :path, type: String, desc: 'The file path'
given :path do
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
post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
......
......@@ -311,6 +311,10 @@ module API
expose :project_id
end
class BasicRef < Grape::Entity
expose :type, :name
end
class Branch < Grape::Entity
expose :name
......
......@@ -117,9 +117,8 @@ module QA
autoload :Show, 'qa/page/project/pipeline/show'
end
module Pipeline
autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show'
module Job
autoload :Show, 'qa/page/project/job/show'
end
module Settings
......@@ -170,6 +169,7 @@ module QA
#
module Git
autoload :Repository, 'qa/git/repository'
autoload :Location, 'qa/git/location'
end
##
......
......@@ -4,7 +4,7 @@ module QA
module Factory
module Resource
class Runner < Factory::Base
attr_writer :name, :tags
attr_writer :name, :tags, :image
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-ci-cd'
......@@ -19,6 +19,10 @@ module QA
@tags || %w[qa e2e]
end
def image
@image || 'gitlab/gitlab-runner:alpine'
end
def fabricate!
project.visit!
......@@ -31,6 +35,7 @@ module QA
runner.token = runners.registration_token
runner.address = runners.coordinator_address
runner.tags = tags
runner.image = image
runner.register!
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'
module QA
......
......@@ -17,7 +17,8 @@ module QA
start = Time.now
while Time.now - start < max
return true if yield
result = yield
return result if result
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
end
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
......
......@@ -11,6 +11,7 @@ module QA::Page
view 'app/assets/javascripts/pipelines/components/graph/job_component.vue' do
element :job_component, /class.*ci-job-component.*/
element :job_link, /class.*js-pipeline-graph-job-link.*/
end
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
......@@ -30,6 +31,16 @@ module QA::Page
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
......@@ -22,22 +22,24 @@ module QA
end
def choose_repository_clone_http
wait(reload: false) do
click_element :clone_dropdown
page.within('.clone-options-dropdown') do
click_link('HTTP')
end
choose_repository_clone('HTTP', 'http')
end
# Ensure git clone textbox was updated to http URI
repository_location.include?('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
find('#project_clone').value
end
def repository_location_uri
Git::Location.new(repository_location)
end
def project_name
find('.qa-project-name').text
end
......@@ -56,6 +58,21 @@ module QA
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.include?(detect_text)
end
end
end
end
end
......
......@@ -7,7 +7,7 @@ module QA
extend Forwardable
attr_reader :key
def_delegators :@key, :fingerprint
def_delegators :@key, :fingerprint, :to_pem
def initialize(bits = 4096)
@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'
describe 'User activates Jira', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:service) { project.create_jira_service }
let(:url) { 'http://jira.example.com' }
let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' }
......@@ -26,7 +25,7 @@ describe 'User activates Jira', :js do
describe 'user sets and activates Jira Service' do
context 'when Jira connection test succeeds' do
it 'activates the JIRA service' do
before do
server_info = { key: 'value' }.to_json
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
fill_form
click_button('Test settings and save changes')
wait_for_requests
end
it 'activates the JIRA service' do
expect(page).to have_content('JIRA activated.')
expect(current_path).to eq(project_settings_integrations_path(project))
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
context 'when Jira connection test fails' do
......@@ -75,14 +82,20 @@ describe 'User activates Jira', :js do
end
describe 'user sets Jira Service but keeps it disabled' do
context 'when Jira connection test succeeds' do
it 'activates the JIRA service' do
click_link('JIRA')
fill_form(false)
click_button('Save changes')
before do
click_link('JIRA')
fill_form(false)
click_button('Save changes')
end
expect(page).to have_content('JIRA settings saved, but not activated.')
expect(current_path).to eq(project_settings_integrations_path(project))
it 'saves but does not activate the JIRA service' do
expect(page).to have_content('JIRA settings saved, but not activated.')
expect(current_path).to eq(project_settings_integrations_path(project))
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
......
......@@ -473,6 +473,72 @@ describe API::Commits do
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
let(:commit) { project.repository.commit }
let(:commit_id) { commit.id }
......
......@@ -21,6 +21,15 @@ describe Members::AuthorizedDestroyService do
.to change { Member.count }.from(3).to(2)
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
group.add_developer(member_user)
......@@ -29,38 +38,73 @@ describe Members::AuthorizedDestroyService do
expect { described_class.new(member, member_user).execute }
.to change { Member.count }.from(2).to(1)
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
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)
end
it "unassigns issues and merge requests" do
issue = create :issue, project: group_project, assignees: [member_user]
create :issue, assignees: [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
member = group.members.find_by(user_id: member_user.id)
expect { described_class.new(member, member_user).execute }
.to change { number_of_assigned_issuables(member_user) }.from(4).to(2)
expect(issue.reload.assignee_ids).to be_empty
expect(merge_request.reload.assignee_id).to be_nil
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
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)
end
it "unassigns issues and merge requests" do
create :issue, project: project, assignees: [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 }
.to change { number_of_assigned_issuables(member_user) }.from(2).to(0)
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
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