Commit 92551e98 authored by GitLab Bot's avatar GitLab Bot

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

# Conflicts:
#	doc/administration/incoming_email.md
#	doc/administration/index.md
#	doc/api/merge_requests.md
#	doc/user/project/issues/create_new_issue.md
#	ee/app/assets/javascripts/protected_branches/index.js

[ci skip]
parents 224c5a6a 405c4cae
...@@ -16,6 +16,7 @@ export default class FilteredSearchDropdownManager { ...@@ -16,6 +16,7 @@ export default class FilteredSearchDropdownManager {
page, page,
isGroup, isGroup,
isGroupAncestor, isGroupAncestor,
isGroupDecendent,
filteredSearchTokenKeys, filteredSearchTokenKeys,
}) { }) {
this.container = FilteredSearchContainer.container; this.container = FilteredSearchContainer.container;
...@@ -26,6 +27,7 @@ export default class FilteredSearchDropdownManager { ...@@ -26,6 +27,7 @@ export default class FilteredSearchDropdownManager {
this.page = page; this.page = page;
this.groupsOnly = isGroup; this.groupsOnly = isGroup;
this.groupAncestor = isGroupAncestor; this.groupAncestor = isGroupAncestor;
this.isGroupDecendent = isGroupDecendent;
this.setupMapping(); this.setupMapping();
......
...@@ -22,11 +22,13 @@ export default class FilteredSearchManager { ...@@ -22,11 +22,13 @@ export default class FilteredSearchManager {
page, page,
isGroup = false, isGroup = false,
isGroupAncestor = false, isGroupAncestor = false,
isGroupDecendent = false,
filteredSearchTokenKeys = FilteredSearchTokenKeys, filteredSearchTokenKeys = FilteredSearchTokenKeys,
stateFiltersSelector = '.issues-state-filters', stateFiltersSelector = '.issues-state-filters',
}) { }) {
this.isGroup = isGroup; this.isGroup = isGroup;
this.isGroupAncestor = isGroupAncestor; this.isGroupAncestor = isGroupAncestor;
this.isGroupDecendent = isGroupDecendent;
this.states = ['opened', 'closed', 'merged', 'all']; this.states = ['opened', 'closed', 'merged', 'all'];
this.page = page; this.page = page;
......
import _ from 'underscore'; import _ from 'underscore';
import AjaxCache from '../lib/utils/ajax_cache'; import AjaxCache from '~/lib/utils/ajax_cache';
import { objectToQueryString } from '~/lib/utils/common_utils';
import Flash from '../flash'; import Flash from '../flash';
import FilteredSearchContainer from './container'; import FilteredSearchContainer from './container';
import UsersCache from '../lib/utils/users_cache'; import UsersCache from '../lib/utils/users_cache';
...@@ -16,6 +17,21 @@ export default class FilteredSearchVisualTokens { ...@@ -16,6 +17,21 @@ export default class FilteredSearchVisualTokens {
}; };
} }
/**
* Returns a computed API endpoint
* and query string composed of values from endpointQueryParams
* @param {String} endpoint
* @param {String} endpointQueryParams
*/
static getEndpointWithQueryParams(endpoint, endpointQueryParams) {
if (!endpointQueryParams) {
return endpoint;
}
const queryString = objectToQueryString(JSON.parse(endpointQueryParams));
return `${endpoint}?${queryString}`;
}
static unselectTokens() { static unselectTokens() {
const otherTokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token .selectable.selected'); const otherTokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token .selectable.selected');
[].forEach.call(otherTokens, t => t.classList.remove('selected')); [].forEach.call(otherTokens, t => t.classList.remove('selected'));
...@@ -86,7 +102,10 @@ export default class FilteredSearchVisualTokens { ...@@ -86,7 +102,10 @@ export default class FilteredSearchVisualTokens {
static updateLabelTokenColor(tokenValueContainer, tokenValue) { static updateLabelTokenColor(tokenValueContainer, tokenValue) {
const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search'); const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
const baseEndpoint = filteredSearchInput.dataset.baseEndpoint; const baseEndpoint = filteredSearchInput.dataset.baseEndpoint;
const labelsEndpoint = `${baseEndpoint}/labels.json`; const labelsEndpoint = FilteredSearchVisualTokens.getEndpointWithQueryParams(
`${baseEndpoint}/labels.json`,
filteredSearchInput.dataset.endpointQueryParams,
);
return AjaxCache.retrieve(labelsEndpoint) return AjaxCache.retrieve(labelsEndpoint)
.then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint)) .then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint))
......
...@@ -302,6 +302,14 @@ export const parseQueryStringIntoObject = (query = '') => { ...@@ -302,6 +302,14 @@ export const parseQueryStringIntoObject = (query = '') => {
}, {}); }, {});
}; };
/**
* Converts object with key-value pairs
* into query-param string
*
* @param {Object} params
*/
export const objectToQueryString = (params = {}) => Object.keys(params).map(param => `${param}=${params[param]}`).join('&');
export const buildUrlWithCurrentLocation = param => (param ? `${window.location.pathname}${param}` : window.location.pathname); export const buildUrlWithCurrentLocation = param => (param ? `${window.location.pathname}${param}` : window.location.pathname);
/** /**
......
...@@ -39,8 +39,11 @@ import 'ee/main'; ...@@ -39,8 +39,11 @@ import 'ee/main';
import initDispatcher from './dispatcher'; import initDispatcher from './dispatcher';
// eslint-disable-next-line global-require, import/no-commonjs // inject test utilities if necessary
if (process.env.NODE_ENV !== 'production') require('./test_utils/'); if (process.env.NODE_ENV !== 'production' && gon && gon.test_env) {
$.fx.off = true;
import(/* webpackMode: "eager" */ './test_utils/');
}
svg4everybody(); svg4everybody();
......
...@@ -5,6 +5,7 @@ export default ({ ...@@ -5,6 +5,7 @@ export default ({
filteredSearchTokenKeys, filteredSearchTokenKeys,
isGroup, isGroup,
isGroupAncestor, isGroupAncestor,
isGroupDecendent,
stateFiltersSelector, stateFiltersSelector,
}) => { }) => {
const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search'); const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search');
...@@ -13,6 +14,7 @@ export default ({ ...@@ -13,6 +14,7 @@ export default ({
page, page,
isGroup, isGroup,
isGroupAncestor, isGroupAncestor,
isGroupDecendent,
filteredSearchTokenKeys, filteredSearchTokenKeys,
stateFiltersSelector, stateFiltersSelector,
}); });
......
...@@ -58,11 +58,37 @@ class SnippetsFinder < UnionFinder ...@@ -58,11 +58,37 @@ class SnippetsFinder < UnionFinder
.public_or_visible_to_user(current_user) .public_or_visible_to_user(current_user)
end end
# Returns a collection of projects that is either public or visible to the
# logged in user.
#
# A caller must pass in a block to modify individual parts of
# the query, e.g. to apply .with_feature_available_for_user on top of it.
# This is useful for performance as we can stick those additional filters
# at the bottom of e.g. the UNION.
def projects_for_user
return yield(Project.public_to_user) unless current_user
# If the current_user is allowed to see all projects,
# we can shortcut and just return.
return yield(Project.all) if current_user.full_private_access?
authorized_projects = yield(Project.where('EXISTS (?)', current_user.authorizations_for_projects))
levels = Gitlab::VisibilityLevel.levels_for_user(current_user)
visible_projects = yield(Project.where(visibility_level: levels))
# We use a UNION here instead of OR clauses since this results in better
# performance.
union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')])
Project.from("(#{union.to_sql}) AS #{Project.table_name}")
end
def feature_available_projects def feature_available_projects
# Don't return any project related snippets if the user cannot read cross project # Don't return any project related snippets if the user cannot read cross project
return table[:id].eq(nil) unless Ability.allowed?(current_user, :read_cross_project) return table[:id].eq(nil) unless Ability.allowed?(current_user, :read_cross_project)
projects = Project.public_or_visible_to_user(current_user, use_where_in: false) do |part| projects = projects_for_user do |part|
part.with_feature_available_for_user(:snippets, current_user) part.with_feature_available_for_user(:snippets, current_user)
end.select(:id) end.select(:id)
......
...@@ -322,42 +322,13 @@ class Project < ActiveRecord::Base ...@@ -322,42 +322,13 @@ class Project < ActiveRecord::Base
# Returns a collection of projects that is either public or visible to the # Returns a collection of projects that is either public or visible to the
# logged in user. # logged in user.
# def self.public_or_visible_to_user(user = nil)
# A caller may pass in a block to modify individual parts of if user
# the query, e.g. to apply .with_feature_available_for_user on top of it. where('EXISTS (?) OR projects.visibility_level IN (?)',
# This is useful for performance as we can stick those additional filters user.authorizations_for_projects,
# at the bottom of e.g. the UNION. Gitlab::VisibilityLevel.levels_for_user(user))
#
# Optionally, turning `use_where_in` off leads to returning a
# relation using #from instead of #where. This can perform much better
# but leads to trouble when used in conjunction with AR's #merge method.
def self.public_or_visible_to_user(user = nil, use_where_in: true, &block)
# If we don't get a block passed, use identity to avoid if/else repetitions
block = ->(part) { part } unless block_given?
return block.call(public_to_user) unless user
# If the user is allowed to see all projects,
# we can shortcut and just return.
return block.call(all) if user.full_private_access?
authorized = user
.project_authorizations
.select(1)
.where('project_authorizations.project_id = projects.id')
authorized_projects = block.call(where('EXISTS (?)', authorized))
levels = Gitlab::VisibilityLevel.levels_for_user(user)
visible_projects = block.call(where(visibility_level: levels))
# We use a UNION here instead of OR clauses since this results in better
# performance.
union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')])
if use_where_in
where("projects.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
else else
from("(#{union.to_sql}) AS #{table_name}") public_to_user
end end
end end
...@@ -376,14 +347,11 @@ class Project < ActiveRecord::Base ...@@ -376,14 +347,11 @@ class Project < ActiveRecord::Base
elsif user elsif user
column = ProjectFeature.quoted_access_level_column(feature) column = ProjectFeature.quoted_access_level_column(feature)
authorized = user.project_authorizations.select(1)
.where('project_authorizations.project_id = projects.id')
with_project_feature with_project_feature
.where("#{column} IN (?) OR (#{column} = ? AND EXISTS (?))", .where("#{column} IN (?) OR (#{column} = ? AND EXISTS (?))",
visible, visible,
ProjectFeature::PRIVATE, ProjectFeature::PRIVATE,
authorized) user.authorizations_for_projects)
else else
with_feature_access_level(feature, visible) with_feature_access_level(feature, visible)
end end
......
...@@ -99,7 +99,7 @@ class ChatNotificationService < Service ...@@ -99,7 +99,7 @@ class ChatNotificationService < Service
def get_message(object_kind, data) def get_message(object_kind, data)
case object_kind case object_kind
when "push", "tag_push" when "push", "tag_push"
ChatMessage::PushMessage.new(data) ChatMessage::PushMessage.new(data) if notify_for_ref?(data)
when "issue" when "issue"
ChatMessage::IssueMessage.new(data) unless update?(data) ChatMessage::IssueMessage.new(data) unless update?(data)
when "merge_request" when "merge_request"
...@@ -145,10 +145,16 @@ class ChatNotificationService < Service ...@@ -145,10 +145,16 @@ class ChatNotificationService < Service
end end
def notify_for_ref?(data) def notify_for_ref?(data)
return true if data[:object_attributes][:tag] return true if data.dig(:object_attributes, :tag)
return true unless notify_only_default_branch? return true unless notify_only_default_branch?
data[:object_attributes][:ref] == project.default_branch ref = if data[:ref]
Gitlab::Git.ref_name(data[:ref])
else
data.dig(:object_attributes, :ref)
end
ref == project.default_branch
end end
def notify_for_pipeline?(data) def notify_for_pipeline?(data)
......
...@@ -619,6 +619,15 @@ class User < ActiveRecord::Base ...@@ -619,6 +619,15 @@ class User < ActiveRecord::Base
authorized_projects(min_access_level).exists?({ id: project.id }) authorized_projects(min_access_level).exists?({ id: project.id })
end end
# Typically used in conjunction with projects table to get projects
# a user has been given access to.
#
# Example use:
# `Project.where('EXISTS(?)', user.authorizations_for_projects)`
def authorizations_for_projects
project_authorizations.select(1).where('project_authorizations.project_id = projects.id')
end
# Returns the projects this user has reporter (or greater) access to, limited # Returns the projects this user has reporter (or greater) access to, limited
# to at most the given projects. # to at most the given projects.
# #
......
...@@ -706,15 +706,15 @@ ...@@ -706,15 +706,15 @@
.checkbox .checkbox
= f.label :usage_ping_enabled do = f.label :usage_ping_enabled do
= f.check_box :usage_ping_enabled, disabled: !can_be_configured = f.check_box :usage_ping_enabled, disabled: !can_be_configured
Usage ping enabled Enable usage ping
= link_to icon('question-circle'), help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-ping")
.help-block .help-block
- if can_be_configured - if can_be_configured
Every week GitLab will report license usage back to GitLab, Inc. To help improve GitLab and its user experience, GitLab will
Disable this option if you do not want this to occur. To see the periodically collect usage information.
JSON payload that will be sent, visit the = link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-ping")
= succeed '.' do about what information is shared with GitLab Inc. Visit
= link_to "Cohorts page", admin_cohorts_path(anchor: 'usage-ping') = link_to 'Cohorts', admin_cohorts_path(anchor: 'usage-ping')
to see the JSON payload sent.
- else - else
The usage ping is disabled, and cannot be configured through this The usage ping is disabled, and cannot be configured through this
form. For more information, see the documentation on form. For more information, see the documentation on
......
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
= webpack_bundle_tag "common" = webpack_bundle_tag "common"
= webpack_bundle_tag "main" = webpack_bundle_tag "main"
= webpack_bundle_tag "raven" if Gitlab::CurrentSettings.clientside_sentry_enabled = webpack_bundle_tag "raven" if Gitlab::CurrentSettings.clientside_sentry_enabled
= webpack_bundle_tag "test" if Rails.env.test?
- if content_for?(:page_specific_javascripts) - if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts = yield :page_specific_javascripts
......
---
title: Fix Slack/Mattermost notifications not respecting `notify_only_default_branch` setting for pushes
merge_request: 17345
author:
type: fixed
---
title: Revert Project.public_or_visible_to_user changes and only apply to snippets
merge_request:
author:
type: fixed
...@@ -54,7 +54,6 @@ function generateEntries() { ...@@ -54,7 +54,6 @@ function generateEntries() {
main: './main.js', main: './main.js',
ide: './ide/index.js', ide: './ide/index.js',
raven: './raven/index.js', raven: './raven/index.js',
test: './test.js',
webpack_runtime: './webpack.js', webpack_runtime: './webpack.js',
// EE-only // EE-only
......
...@@ -10,8 +10,11 @@ GitLab has several features based on receiving incoming emails: ...@@ -10,8 +10,11 @@ GitLab has several features based on receiving incoming emails:
- [New merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email): - [New merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email):
allow GitLab users to create a new merge request by sending an email to a allow GitLab users to create a new merge request by sending an email to a
user-specific email address. user-specific email address.
<<<<<<< HEAD
- [Service Desk](../user/project/service_desk.md): provide e-mail support to - [Service Desk](../user/project/service_desk.md): provide e-mail support to
your customers through GitLab. your customers through GitLab.
=======
>>>>>>> upstream/master
## Requirements ## Requirements
......
...@@ -87,12 +87,15 @@ created in snippets, wikis, and repos. ...@@ -87,12 +87,15 @@ created in snippets, wikis, and repos.
- [Sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md): block email addresses of specific domains, or whitelist only specific domains. - [Sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md): block email addresses of specific domains, or whitelist only specific domains.
- [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS). - [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS).
- [Authentication/Authorization](../topics/authentication/index.md#gitlab-administrators): Enforce 2FA, configure external authentication with LDAP, SAML, CAS and additional Omniauth providers. - [Authentication/Authorization](../topics/authentication/index.md#gitlab-administrators): Enforce 2FA, configure external authentication with LDAP, SAML, CAS and additional Omniauth providers.
<<<<<<< HEAD
- **(Starter/Premium)** [Sync LDAP](auth/ldap-ee.md) - **(Starter/Premium)** [Sync LDAP](auth/ldap-ee.md)
- **(Starter/Premium)** [Kerberos authentication](../integration/kerberos.md) - **(Starter/Premium)** [Kerberos authentication](../integration/kerberos.md)
- **(Starter/Premium)** [Email users](../tools/email.md): Email GitLab users from within GitLab. - **(Starter/Premium)** [Email users](../tools/email.md): Email GitLab users from within GitLab.
- [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time. - [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
- **(Starter/Premium)** [Audit logs and events](audit_events.md): View the changes made within the GitLab server. - **(Starter/Premium)** [Audit logs and events](audit_events.md): View the changes made within the GitLab server.
- **(Premium)** [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. - **(Premium)** [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance.
=======
>>>>>>> upstream/master
- [Incoming email](incoming_email.md): Configure incoming emails to allow - [Incoming email](incoming_email.md): Configure incoming emails to allow
users to [reply by email], create [issues by email] and users to [reply by email], create [issues by email] and
[merge requests by email], and to enable [Service Desk]. [merge requests by email], and to enable [Service Desk].
...@@ -105,7 +108,10 @@ server with IMAP authentication on Ubuntu, to be used with Reply by email. ...@@ -105,7 +108,10 @@ server with IMAP authentication on Ubuntu, to be used with Reply by email.
[reply by email]: reply_by_email.md [reply by email]: reply_by_email.md
[issues by email]: ../user/project/issues/create_new_issue.md#new-issue-via-email [issues by email]: ../user/project/issues/create_new_issue.md#new-issue-via-email
[merge requests by email]: ../user/project/merge_requests/index.md#create-new-merge-requests-by-email [merge requests by email]: ../user/project/merge_requests/index.md#create-new-merge-requests-by-email
<<<<<<< HEAD
[Service Desk]: ../user/project/service_desk.md [Service Desk]: ../user/project/service_desk.md
=======
>>>>>>> upstream/master
## Project settings ## Project settings
......
...@@ -312,8 +312,11 @@ Parameters: ...@@ -312,8 +312,11 @@ Parameters:
"human_time_estimate": null, "human_time_estimate": null,
"human_total_time_spent": null "human_total_time_spent": null
}, },
<<<<<<< HEAD
"approvals_before_merge": null "approvals_before_merge": null
}, },
=======
>>>>>>> upstream/master
"closed_at": "2018-01-19T14:36:11.086Z", "closed_at": "2018-01-19T14:36:11.086Z",
"latest_build_started_at": null, "latest_build_started_at": null,
"latest_build_finished_at": null, "latest_build_finished_at": null,
......
...@@ -58,6 +58,7 @@ body becomes the issue description. [Markdown] and [quick actions] are ...@@ -58,6 +58,7 @@ body becomes the issue description. [Markdown] and [quick actions] are
supported. supported.
![Bottom of a project issues page](img/new_issue_from_email.png) ![Bottom of a project issues page](img/new_issue_from_email.png)
<<<<<<< HEAD
## New issue via Service Desk ## New issue via Service Desk
...@@ -84,3 +85,5 @@ create issues for the same project. ...@@ -84,3 +85,5 @@ create issues for the same project.
[Markdown]: ../../markdown.md [Markdown]: ../../markdown.md
[quick actions]: ../quick_actions.md [quick actions]: ../quick_actions.md
=======
>>>>>>> upstream/master
...@@ -128,6 +128,24 @@ describe('Filtered Search Visual Tokens', () => { ...@@ -128,6 +128,24 @@ describe('Filtered Search Visual Tokens', () => {
}); });
}); });
describe('getEndpointWithQueryParams', () => {
it('returns `endpoint` string as is when second param `endpointQueryParams` is undefined, null or empty string', () => {
const endpoint = 'foo/bar/labels.json';
expect(subject.getEndpointWithQueryParams(endpoint)).toBe(endpoint);
expect(subject.getEndpointWithQueryParams(endpoint, null)).toBe(endpoint);
expect(subject.getEndpointWithQueryParams(endpoint, '')).toBe(endpoint);
});
it('returns `endpoint` string with values of `endpointQueryParams`', () => {
const endpoint = 'foo/bar/labels.json';
const singleQueryParams = '{"foo":"true"}';
const multipleQueryParams = '{"foo":"true","bar":"true"}';
expect(subject.getEndpointWithQueryParams(endpoint, singleQueryParams)).toBe(`${endpoint}?foo=true`);
expect(subject.getEndpointWithQueryParams(endpoint, multipleQueryParams)).toBe(`${endpoint}?foo=true&bar=true`);
});
});
describe('unselectTokens', () => { describe('unselectTokens', () => {
it('does nothing when there are no tokens', () => { it('does nothing when there are no tokens', () => {
const beforeHTML = tokensContainer.innerHTML; const beforeHTML = tokensContainer.innerHTML;
......
...@@ -166,6 +166,21 @@ describe('common_utils', () => { ...@@ -166,6 +166,21 @@ describe('common_utils', () => {
}); });
}); });
describe('objectToQueryString', () => {
it('returns empty string when `param` is undefined, null or empty string', () => {
expect(commonUtils.objectToQueryString()).toBe('');
expect(commonUtils.objectToQueryString('')).toBe('');
});
it('returns query string with values of `params`', () => {
const singleQueryParams = { foo: true };
const multipleQueryParams = { foo: true, bar: true };
expect(commonUtils.objectToQueryString(singleQueryParams)).toBe('foo=true');
expect(commonUtils.objectToQueryString(multipleQueryParams)).toBe('foo=true&bar=true');
});
});
describe('buildUrlWithCurrentLocation', () => { describe('buildUrlWithCurrentLocation', () => {
it('should build an url with current location and given parameters', () => { it('should build an url with current location and given parameters', () => {
expect(commonUtils.buildUrlWithCurrentLocation()).toEqual(window.location.pathname); expect(commonUtils.buildUrlWithCurrentLocation()).toEqual(window.location.pathname);
......
...@@ -1684,6 +1684,32 @@ describe User do ...@@ -1684,6 +1684,32 @@ describe User do
end end
end end
describe '#authorizations_for_projects' do
let!(:user) { create(:user) }
subject { Project.where("EXISTS (?)", user.authorizations_for_projects) }
it 'includes projects that belong to a user, but no other projects' do
owned = create(:project, :private, namespace: user.namespace)
member = create(:project, :private).tap { |p| p.add_master(user) }
other = create(:project)
expect(subject).to include(owned)
expect(subject).to include(member)
expect(subject).not_to include(other)
end
it 'includes projects a user has access to, but no other projects' do
other_user = create(:user)
accessible = create(:project, :private, namespace: other_user.namespace) do |project|
project.add_developer(user)
end
other = create(:project)
expect(subject).to include(accessible)
expect(subject).not_to include(other)
end
end
describe '#authorized_projects', :delete do describe '#authorized_projects', :delete do
context 'with a minimum access level' do context 'with a minimum access level' do
it 'includes projects for which the user is an owner' do it 'includes projects for which the user is an owner' do
......
...@@ -337,6 +337,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do ...@@ -337,6 +337,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
before do before do
chat_service.notify_only_default_branch = true chat_service.notify_only_default_branch = true
WebMock.stub_request(:post, webhook_url)
end end
it 'does not call the Slack/Mattermost API for pipeline events' do it 'does not call the Slack/Mattermost API for pipeline events' do
...@@ -345,6 +346,23 @@ RSpec.shared_examples 'slack or mattermost notifications' do ...@@ -345,6 +346,23 @@ RSpec.shared_examples 'slack or mattermost notifications' do
expect(result).to be_falsy expect(result).to be_falsy
end end
it 'does not notify push events if they are not for the default branch' do
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
chat_service.execute(push_sample_data)
expect(WebMock).not_to have_requested(:post, webhook_url)
end
it 'notifies about push events for the default branch' do
push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
chat_service.execute(push_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
end end
context 'when disabled' do context 'when disabled' do
......
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