Commit cd358b7b authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'ce/master' into ce_upstream

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents f185d9e9 e7fdb1aa
......@@ -229,7 +229,7 @@ gem 'oj', '~> 2.17.4'
gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6'
gem 'sass-rails', '~> 5.0.6'
gem 'sassc-rails', '~> 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2'
gem 'gitlab-turbolinks-classic', '~> 2.5', '>= 2.5.6'
......@@ -266,7 +266,6 @@ group :development do
gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0'
gem 'rerun', '~> 0.11.0'
gem 'bullet', '~> 5.2.0', require: false
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0'
......@@ -297,7 +296,7 @@ group :development, :test do
gem 'minitest', '~> 5.7.0'
# Generate Fake data
gem 'ffaker', '~> 2.0.0'
gem 'ffaker', '~> 2.4'
gem 'capybara', '~> 2.6.2'
gem 'capybara-screenshot', '~> 1.0.0'
......
......@@ -211,7 +211,7 @@ GEM
faraday_middleware-multi_json (0.0.6)
faraday_middleware
multi_json
ffaker (2.0.0)
ffaker (2.4.0)
ffi (1.9.10)
flay (2.6.1)
ruby_parser (~> 3.0)
......@@ -431,9 +431,6 @@ GEM
xml-simple
licensee (8.0.0)
rugged (>= 0.24b)
listen (3.0.5)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
little-plugger (1.1.4)
logging (2.1.0)
little-plugger (~> 1.1)
......@@ -604,9 +601,6 @@ GEM
rainbow (2.1.0)
raindrops (0.17.0)
rake (10.5.0)
rb-fsevent (0.9.6)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2)
......@@ -635,8 +629,6 @@ GEM
redis-store (1.2.0)
redis (>= 2.2)
request_store (1.3.1)
rerun (0.11.0)
listen (~> 3.0)
responders (2.3.0)
railties (>= 4.2.0, < 5.1)
rest-client (2.0.0)
......@@ -699,12 +691,17 @@ GEM
sanitize (2.1.0)
nokogiri (>= 1.4.4)
sass (3.4.22)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sassc (1.11.1)
bundler
ffi (~> 1.9.6)
sass (>= 3.3.0)
sassc-rails (1.3.0)
railties (>= 4.0.0)
sass
sassc (~> 1.9)
sprockets (> 2.11)
sprockets-rails
tilt
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
......@@ -906,7 +903,7 @@ DEPENDENCIES
email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0)
factory_girl_rails (~> 4.7.0)
ffaker (~> 2.0.0)
ffaker (~> 2.4)
flay (~> 2.6.1)
fog-aws (~> 0.9)
fog-core (~> 1.40)
......@@ -1001,7 +998,6 @@ DEPENDENCIES
redis-namespace (~> 1.5.2)
redis-rails (~> 5.0.1)
request_store (~> 1.3)
rerun (~> 0.11.0)
responders (~> 2.0)
rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7)
......@@ -1013,7 +1009,7 @@ DEPENDENCIES
ruby-prof (~> 0.16.2)
rugged (~> 0.24.0)
sanitize (~> 2.0)
sass-rails (~> 5.0.6)
sassc-rails (~> 1.3.0)
scss_lint (~> 0.47.0)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
......
......@@ -20,7 +20,7 @@
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this));
this.highlighSelectedLine();
this.openAnchoredDiff();
}
handleClickUnfold(e) {
......@@ -61,13 +61,22 @@
$.get(link, params, response => $target.parent().replaceWith(response));
}
openAnchoredDiff(anchoredDiff, cb) {
const diffTitle = $(`#file-path-${anchoredDiff}`);
openAnchoredDiff(cb) {
const locationHash = gl.utils.getLocationHash();
const anchoredDiff = locationHash && locationHash.split('_')[0];
if (!anchoredDiff) return;
const diffTitle = $(`#${anchoredDiff}`);
const diffFile = diffTitle.closest('.diff-file');
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
if (nothingHereBlock.length) {
diffFile.singleFileDiff(true, cb);
} else {
const clickTarget = $('.file-title, .click-to-expand', diffFile);
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlighSelectedLine();
if (cb) cb();
});
} else if (cb) {
cb();
}
}
......
......@@ -476,7 +476,7 @@
this.removeArrayKeyEvent();
$input = this.dropdown.find(".dropdown-input-field");
if (this.options.filterable) {
$input.blur().val("");
$input.blur();
}
if (this.dropdown.find(".dropdown-toggle-page").length) {
$('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS);
......
......@@ -237,13 +237,8 @@
}
this.diffsLoaded = true;
const diffPage = new gl.Diff();
const locationHash = gl.utils.getLocationHash();
const anchoredDiff = locationHash && locationHash.split('_')[0];
if (anchoredDiff) {
diffPage.openAnchoredDiff(anchoredDiff, () => this.scrollToElement('#diffs'));
}
new gl.Diff();
this.scrollToElement('#diffs');
},
});
}
......
......@@ -12,6 +12,9 @@
selectable: true,
filterable: true,
fieldName: 'group_id',
search: {
fields: ['name']
},
data: function(term, callback) {
return Api.groups(term, {}, function(data) {
data.unshift({
......@@ -40,6 +43,9 @@
selectable: true,
filterable: true,
fieldName: 'project_id',
search: {
fields: ['name']
},
data: function(term, callback) {
return Api.projects(term, 'id', function(data) {
data.unshift({
......
/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, max-len */
/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
......@@ -14,8 +14,7 @@
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
function SingleFileDiff(file, forceLoad, cb) {
var clickTarget;
function SingleFileDiff(file) {
this.file = file;
this.toggleDiff = bind(this.toggleDiff, this);
this.content = $('.diff-content', this.file);
......@@ -33,14 +32,13 @@
this.content.after(this.collapsedContent);
this.$toggleIcon.addClass('fa-caret-down');
}
clickTarget = $('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff);
if (forceLoad) {
this.toggleDiff({ target: clickTarget }, cb);
}
$('.file-title, .click-to-expand', this.file).on('click', (function (e) {
this.toggleDiff($(e.target));
}).bind(this));
}
SingleFileDiff.prototype.toggleDiff = function(e, cb) {
var $target = $(e.target);
SingleFileDiff.prototype.toggleDiff = function($target, cb) {
if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
this.isOpen = !this.isOpen;
if (!this.isOpen && !this.hasError) {
......@@ -91,10 +89,10 @@
})();
$.fn.singleFileDiff = function(forceLoad, cb) {
$.fn.singleFileDiff = function() {
return this.each(function() {
if (!$.data(this, 'singleFileDiff') || forceLoad) {
return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this, forceLoad, cb));
if (!$.data(this, 'singleFileDiff')) {
return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this));
}
});
};
......
/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign, no-bitwise */
((gl) => {
gl.VueStage = Vue.extend({
data() {
return {
request: false,
count: 0,
builds: '',
spinner: '<span class="fa fa-spinner fa-spin"></span>',
};
......@@ -13,29 +13,23 @@
props: ['stage', 'svgs', 'match'],
methods: {
fetchBuilds() {
if (this.request) return this.clearBuilds();
if (this.count > 0) return null;
return this.$http.get(this.stage.dropdown_path)
.then((response) => {
this.request = true;
this.count += 1;
this.builds = JSON.parse(response.body).html;
}, () => {
const flash = new Flash('Something went wrong on our end.');
this.request = false;
return flash;
});
},
clearBuilds() {
this.builds = '';
this.request = false;
},
},
computed: {
buildsOrSpinner() {
return this.request ? this.builds : this.spinner;
return this.builds ? this.builds : this.spinner;
},
dropdownClass() {
if (this.request) return 'js-builds-dropdown-container';
if (this.builds) return 'js-builds-dropdown-container';
return 'js-builds-dropdown-loading builds-dropdown-loading';
},
buildStatus() {
......@@ -57,7 +51,6 @@
<div>
<button
@click='fetchBuilds'
@blur='fetchBuilds'
:class="triggerButtonClass"
:title='stage.title'
data-placement="top"
......
......@@ -50,3 +50,77 @@
.pulse {
@include webkit-prefix(animation-name, pulse);
}
/*
* General hover animations
*/
// Sass multiple transitions mixin | https://gist.github.com/tobiasahlin/7a421fb9306a4f518aab
// Usage: @include transition(width, height 0.3s ease-in-out);
// Output: -webkit-transition(width 0.2s, height 0.3s ease-in-out);
// transition(width 0.2s, height 0.3s ease-in-out);
//
// Pass in any number of transitions
@mixin transition($transitions...) {
$unfoldedTransitions: ();
@each $transition in $transitions {
$unfoldedTransitions: append($unfoldedTransitions, unfoldTransition($transition), comma);
}
transition: $unfoldedTransitions;
}
@function unfoldTransition ($transition) {
// Default values
$property: all;
$duration: $general-hover-transition-duration;
$easing: $general-hover-transition-curve; // Browser default is ease, which is what we want
$delay: null; // Browser default is 0, which is what we want
$defaultProperties: ($property, $duration, $easing, $delay);
// Grab transition properties if they exist
$unfoldedTransition: ();
@for $i from 1 through length($defaultProperties) {
$p: null;
@if $i <= length($transition) {
$p: nth($transition, $i);
} @else {
$p: nth($defaultProperties, $i);
}
$unfoldedTransition: append($unfoldedTransition, $p);
}
@return $unfoldedTransition;
}
.btn,
.side-nav-toggle {
@include transition(background-color, border-color, color, box-shadow);
}
.dropdown-menu-toggle,
.avatar-circle,
.header-user-avatar {
@include transition(border-color);
}
.note-action-button .link-highlight,
.toolbar-btn,
.dropdown-toggle-caret,
.fa:not(.fa-bell) {
@include transition(color);
}
a {
@include transition(background-color, color, border);
}
.tree-table td,
.well-list > li {
@include transition(background-color, border-color);
}
.stage-nav-item {
@include transition(background-color, box-shadow);
}
......@@ -52,6 +52,10 @@
border-radius: 0;
border: none;
}
&:not([href]):hover {
border-color: rgba($avatar-border, .2);
}
}
.identicon {
......
......@@ -57,6 +57,14 @@ header {
&.header-user-dropdown-toggle {
margin-left: 14px;
&:hover,
&:focus,
&:active {
.header-user-avatar {
border-color: rgba($avatar-border, .2);
}
}
}
&:hover,
......@@ -104,6 +112,7 @@ header {
&:hover {
background-color: $white-normal;
color: $gl-header-nav-hover-color;
}
}
}
......@@ -180,6 +189,7 @@ header {
&:hover {
text-decoration: underline;
color: $gl-header-nav-hover-color;
}
}
......@@ -198,7 +208,7 @@ header {
cursor: pointer;
&:hover {
color: darken($color: $gl-text-color, $amount: 30%);
color: $gl-header-nav-hover-color;
}
}
......@@ -271,4 +281,5 @@ header {
float: left;
margin-right: 5px;
border-radius: 50%;
border: 1px solid $avatar-border;
}
......@@ -101,7 +101,7 @@
&:hover,
&:active,
&:focus {
border-bottom: none;
border-color: transparent;
}
}
}
......
......@@ -104,6 +104,10 @@ $gl-text-red: #d12f19;
$gl-text-orange: #d90;
$gl-link-color: #3777b0;
$gl-grayish-blue: #7f8fa4;
$gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
$gl-header-color: #4c4e54;
$gl-header-nav-hover-color: #434343;
/*
* Lists
......@@ -175,9 +179,15 @@ $count-arrow-border: #dce0e5;
$save-project-loader-color: #555;
$divergence-graph-bar-bg: #ccc;
$divergence-graph-separator-bg: #ccc;
<<<<<<< HEAD
$issue-box-upcoming-bg: #8f8f8f;
$pages-group-name-color: #4c4e54;
$ldap-members-override-bg: #fff1e0;
=======
$general-hover-transition-duration: 150ms;
$general-hover-transition-curve: linear;
>>>>>>> ce/master
/*
* Common component specific colors
......
......@@ -20,6 +20,10 @@
.fa {
color: $cycle-analytics-light-gray;
&:hover {
color: $gl-text-color;
}
}
.stage-header {
......
......@@ -154,8 +154,8 @@
.edit-link {
color: $gl-text-color;
&:hover {
color: $md-link-color;
&:not([href]):hover {
color: rgba($avatar-border, .2);
}
}
}
......@@ -332,6 +332,10 @@
&:hover {
color: $md-link-color;
text-decoration: none;
.avatar {
border-color: rgba($avatar-border, .2);
}
}
}
......
......@@ -203,6 +203,10 @@
z-index: 3;
border-radius: $label-border-radius;
padding: 6px 10px 6px 9px;
&:hover {
box-shadow: inset 0 0 0 80px $label-remove-border;
}
}
.btn {
......
......@@ -216,8 +216,8 @@
}
}
.user-profile {
.user-profile {
.cover-controls a {
margin-left: 5px;
}
......@@ -231,8 +231,11 @@
}
}
@media (max-width: $screen-xs-max) {
.user-profile-nav {
font-size: 0;
}
@media (max-width: $screen-xs-max) {
.cover-block {
padding-top: 20px;
}
......@@ -253,6 +256,12 @@
}
}
}
.user-profile-nav {
a {
margin-right: 0;
}
}
}
}
......
......@@ -14,6 +14,20 @@
}
}
.search form:hover,
.file-finder-input:hover,
.issuable-search-form:hover,
.search-text-input:hover,
textarea:hover,
.form-control:hover {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
input[type="checkbox"]:hover {
box-shadow: 0 0 2px 2px lighten($search-input-focus-shadow-color, 20%), 0 0 0 1px lighten($search-input-focus-shadow-color, 20%);
}
.search {
margin-right: 10px;
margin-left: 10px;
......
......@@ -2,7 +2,7 @@ class BuildActionEntity < Grape::Entity
include RequestAwareEntity
expose :name do |build|
build.name.humanize
build.name
end
expose :path do |build|
......
......@@ -26,8 +26,26 @@ module Users
user.reload
end
# This method returns the updated User object.
def execute
lease_key = "refresh_authorized_projects:#{user.id}"
lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT)
until uuid = lease.try_obtain
# Keep trying until we obtain the lease. If we don't do so we may end up
# not updating the list of authorized projects properly. To prevent
# hammering Redis too much we'll wait for a bit between retries.
sleep(1)
end
begin
execute_without_lease
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
end
# This method returns the updated User object.
def execute_without_lease
current = current_authorizations_per_project
fresh = fresh_access_levels_per_project
......@@ -47,26 +65,7 @@ module Users
end
end
update_with_lease(remove, add)
end
# Updates the list of authorizations using an exclusive lease.
def update_with_lease(remove = [], add = [])
lease_key = "refresh_authorized_projects:#{user.id}"
lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT)
until uuid = lease.try_obtain
# Keep trying until we obtain the lease. If we don't do so we may end up
# not updating the list of authorized projects properly. To prevent
# hammering Redis too much we'll wait for a bit between retries.
sleep(1)
end
begin
update_authorizations(remove, add)
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
end
# Updates the list of authorizations for the current user.
......
......@@ -18,6 +18,7 @@
or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
.col-lg-9
.clearfix.avatar-image.append-bottom-default
= link_to avatar_icon(@user, 400), target: '_blank' do
= image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160'
%h5.prepend-top-0
Upload new avatar
......
......@@ -17,7 +17,10 @@
- if @project.protected_branch? branch.name
%span.label.label-success
<<<<<<< HEAD
= icon('lock')
=======
>>>>>>> ce/master
protected
- if @project.mirror_ever_updated_successfully? && @repository.diverged_from_upstream?(branch.name)
......
......@@ -86,7 +86,7 @@
%li
= link_to play_namespace_project_build_path(pipeline.project.namespace, pipeline.project, build), method: :post, rel: 'nofollow' do
= custom_icon('icon_play')
%span= build.name.humanize
%span= build.name
- if artifacts.present?
.btn-group
%button.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{ type: 'button', 'data-toggle' => 'dropdown' }
......
.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) }
.file-title{ id: "file-path-#{hexdigest(diff_file.file_path)}" }
.file-title
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}"
- unless diff_file.submodule?
......
---
title: Remove Lock Icon on Protected Tag
merge_request: 8513
author: Sergey Nikitin
---
title: Use original casing for build action text
merge_request: 8387
author:
---
title: Add various hover animations throughout the application
merge_request:
author:
---
title: Fix search group/project filtering to show results
merge_request:
author:
---
title: Synchronize all project authorization refreshing work to prevent race conditions
merge_request:
author:
---
title: Switch to sassc-rails for faster stylesheet compilation
merge_request: 8556
author: Richard Macklin
......@@ -7,9 +7,4 @@ namespace :dev do
Rake::Task["gitlab:setup"].invoke
Rake::Task["gitlab:shell:setup"].invoke
end
desc 'GitLab | Start/restart foreman and watch for changes'
task :foreman => :environment do
sh 'rerun --dir app,config,lib -- foreman start'
end
end
......@@ -125,7 +125,9 @@ describe 'Issue Boards', feature: true, js: true do
first('.card').click
end
page.within('.assignee') do
page.within(find('.assignee')) do
expect(page).to have_content('No assignee')
click_link 'assign yourself'
wait_for_vue_resource
......
......@@ -4,10 +4,10 @@ feature 'Expand and collapse diffs', js: true, feature: true do
include WaitForAjax
let(:branch) { 'expand-collapse-diffs' }
let(:project) { create(:project) }
before do
login_as :admin
project = create(:project)
# Ensure that undiffable.md is in .gitattributes
project.repository.copy_gitattributes(branch)
......@@ -31,6 +31,33 @@ feature 'Expand and collapse diffs', js: true, feature: true do
define_method(file.split('.').first) { file_container(file) }
end
it 'should show the diff content with a highlighted line when linking to line' do
expect(large_diff).not_to have_selector('.code')
expect(large_diff).to have_selector('.nothing-here-block')
visit namespace_project_commit_path(project.namespace, project, project.commit(branch), anchor: "#{large_diff[:id]}_0_1")
execute_script('window.location.reload()')
wait_for_ajax
expect(large_diff).to have_selector('.code')
expect(large_diff).not_to have_selector('.nothing-here-block')
expect(large_diff).to have_selector('.hll')
end
it 'should show the diff content when linking to file' do
expect(large_diff).not_to have_selector('.code')
expect(large_diff).to have_selector('.nothing-here-block')
visit namespace_project_commit_path(project.namespace, project, project.commit(branch), anchor: large_diff[:id])
execute_script('window.location.reload()')
wait_for_ajax
expect(large_diff).to have_selector('.code')
expect(large_diff).not_to have_selector('.nothing-here-block')
end
context 'visiting a commit with collapsed diffs' do
it 'shows small diffs immediately' do
expect(small_diff).to have_selector('.code')
......
......@@ -128,13 +128,13 @@ describe 'Pipelines', :feature, :js do
it 'has link to the manual action' do
find('.js-pipeline-dropdown-manual-actions').click
expect(page).to have_link('Manual build')
expect(page).to have_link('manual build')
end
context 'when manual action was played' do
before do
find('.js-pipeline-dropdown-manual-actions').click
click_link('Manual build')
click_link('manual build')
end
it 'enqueues manual action job' do
......
......@@ -50,6 +50,9 @@
selectable: true,
filterable: isFilterable,
data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData,
search: {
fields: ['name']
},
text: (project) => {
(project.name_with_namespace || project.name);
},
......@@ -167,5 +170,21 @@
expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
});
});
it('should still have input value on close and restore', () => {
let $searchInput = $(SEARCH_INPUT_SELECTOR);
initDropDown.call(this, false, true);
$searchInput
.trigger('focus')
.val('g')
.trigger('input');
expect($searchInput.val()).toEqual('g');
this.dropdownButtonElement.trigger('hidden.bs.dropdown');
$searchInput
.trigger('blur')
.trigger('focus');
expect($searchInput.val()).toEqual('g');
});
});
})();
......@@ -10,8 +10,8 @@ describe BuildActionEntity do
describe '#as_json' do
subject { entity.as_json }
it 'contains humanized build name' do
expect(subject[:name]).to eq 'Test build'
it 'contains original build name' do
expect(subject[:name]).to eq 'test_build'
end
it 'contains path to the action play' do
......
......@@ -26,6 +26,7 @@ describe Projects::UpdateService, services: true do
context 'when visibility levels are restricted to PUBLIC only' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
<<<<<<< HEAD
end
context 'when visibility_level is INTERNAL' do
......@@ -51,10 +52,21 @@ describe Projects::UpdateService, services: true do
expect(project).to be_public
end
end
=======
end
context 'when visibility_level is INTERNAL' do
it 'updates the project to internal' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
expect(result).to eq({ status: :success })
expect(project).to be_internal
end
>>>>>>> ce/master
end
end
end
<<<<<<< HEAD
describe 'visibility_level' do
let(:project) { create(:project, :internal) }
let(:forked_project) { create(:forked_project_with_submodules, :internal) }
......@@ -124,9 +136,67 @@ describe Projects::UpdateService, services: true do
expect(project).not_to receive(:change_repository_storage)
update_project(project, user, opts).inspect
=======
context 'when visibility_level is PUBLIC' do
it 'does not update the project to public' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :error, message: 'Visibility level unallowed' })
expect(project).to be_private
end
context 'when updated by an admin' do
it 'updates the project to public' do
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :success })
expect(project).to be_public
end
end
end
end
end
describe 'visibility_level' do
let(:project) { create(:project, :internal) }
let(:forked_project) { create(:forked_project_with_submodules, :internal) }
before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
forked_project.save
end
it 'updates forks visibility level when parent set to more restrictive' do
opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
expect(project).to be_internal
expect(forked_project).to be_internal
expect(update_project(project, admin, opts)).to eq({ status: :success })
expect(project).to be_private
expect(forked_project.reload).to be_private
end
it 'does not update forks visibility level when parent set to less restrictive' do
opts = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
expect(project).to be_internal
expect(forked_project).to be_internal
expect(update_project(project, admin, opts)).to eq({ status: :success })
expect(project).to be_public
expect(forked_project.reload).to be_internal
>>>>>>> ce/master
end
end
it 'returns an error result when record cannot be updated' do
result = update_project(project, admin, { name: 'foo&bar' })
expect(result).to eq({ status: :error, message: 'Project could not be updated' })
end
def update_project(project, user, opts)
described_class.new(project, user, opts).execute
end
......
......@@ -10,7 +10,21 @@ describe Users::RefreshAuthorizedProjectsService do
create!(project: project, user: user, access_level: access_level)
end
describe '#execute' do
describe '#execute', :redis do
it 'refreshes the authorizations using a lease' do
expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
and_return('foo')
expect(Gitlab::ExclusiveLease).to receive(:cancel).
with(an_instance_of(String), 'foo')
expect(service).to receive(:execute_without_lease)
service.execute
end
end
describe '#execute_without_lease' do
before do
user.project_authorizations.delete_all
end
......@@ -19,37 +33,23 @@ describe Users::RefreshAuthorizedProjectsService do
project2 = create(:empty_project)
to_remove = create_authorization(project2, user)
expect(service).to receive(:update_with_lease).
expect(service).to receive(:update_authorizations).
with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
service.execute
service.execute_without_lease
end
it 'sets the access level of a project to the highest available level' do
to_remove = create_authorization(project, user, Gitlab::Access::DEVELOPER)
expect(service).to receive(:update_with_lease).
expect(service).to receive(:update_authorizations).
with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
service.execute
service.execute_without_lease
end
it 'returns a User' do
expect(service.execute).to be_an_instance_of(User)
end
end
describe '#update_with_lease', :redis do
it 'refreshes the authorizations using a lease' do
expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
and_return('foo')
expect(Gitlab::ExclusiveLease).to receive(:cancel).
with(an_instance_of(String), 'foo')
expect(service).to receive(:update_authorizations).with([1], [])
service.update_with_lease([1])
expect(service.execute_without_lease).to be_an_instance_of(User)
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