Commit 1655824f authored by Regis's avatar Regis

Merge branch 'master' into auto-pipelines-vue

parents cf039f58 ec4fe443
......@@ -328,7 +328,7 @@ end
gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0'
gem 'octokit', '~> 4.6.2'
gem 'mail_room', '~> 0.9.0'
......
......@@ -420,8 +420,8 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3)
octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.4)
omniauth (1.3.1)
hashie (>= 1.2, < 4)
......@@ -650,9 +650,9 @@ GEM
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sawyer (0.7.0)
addressable (>= 2.3.5, < 2.5)
faraday (~> 0.8, < 0.10)
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
scss_lint (0.47.1)
rake (>= 0.9, < 11)
sass (~> 3.4.15)
......@@ -895,7 +895,7 @@ DEPENDENCIES
newrelic_rpm (~> 3.16)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.3.0)
octokit (~> 4.6.2)
oj (~> 2.17.4)
omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1)
......
......@@ -55,6 +55,13 @@
$('.dropdown-toggle-text', $dropdown).text(text);
$dropdownContainer.removeClass('open');
});
$dropdownContainer.on('click', '.dropdown-content a', (e) => {
$dropdown.prop('title', e.target.text.replace(/_+?/g, '-'));
if ($dropdown.hasClass('has-tooltip')) {
$dropdown.tooltip('fixTitle');
}
});
});
};
......
......@@ -48,6 +48,7 @@
},
DefaultOptions: {
sorter: function(query, items, searchKey) {
this.setting.highlightFirst = query.length > 0;
if (gl.GfmAutoComplete.isLoading(items)) {
return items;
}
......@@ -55,11 +56,9 @@
},
filter: function(query, data, searchKey) {
if (gl.GfmAutoComplete.isLoading(data)) {
gl.GfmAutoComplete.togglePreventSelection.call(this, true);
gl.GfmAutoComplete.fetchData(this.$inputor, this.at);
return data;
} else {
gl.GfmAutoComplete.togglePreventSelection.call(this, false);
return $.fn.atwho["default"].callbacks.filter(query, data, searchKey);
}
},
......@@ -257,9 +256,9 @@
insertTpl: '${atwho-at}${title}',
callbacks: {
matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter,
beforeInsert: this.DefaultOptions.beforeInsert,
filter: this.DefaultOptions.filter,
sorter: this.DefaultOptions.sorter,
beforeSave: function(merges) {
if (gl.GfmAutoComplete.isLoading(merges)) return merges;
var sanitizeLabelTitle;
......@@ -370,11 +369,7 @@
if (!data || !data.length) return false;
if (Array.isArray(data)) data = data[0];
return data === this.defaultLoadingData[0] || data.name === this.defaultLoadingData[0];
},
togglePreventSelection(isPrevented = !!this.setting.tabSelectsMatch) {
this.setting.tabSelectsMatch = !isPrevented;
this.setting.spaceSelectsMatch = !isPrevented;
},
}
};
}).call(this);
......@@ -97,8 +97,20 @@
padding: 5px 6px;
outline: 0;
&:hover,
&.disabled {
cursor: default;
&:hover,
&:focus,
&:active {
background-color: $white-light;
border-color: $border-color;
box-shadow: none;
}
}
&.active,
&:hover,
&:active {
background-color: $row-hover;
border-color: $row-hover-border;
......
......@@ -182,3 +182,52 @@ span.idiff {
border-bottom-right-radius: 2px;
}
}
.file-stats {
ul {
list-style: none;
margin: 0;
padding: 10px 0;
li {
padding: 3px 0;
line-height: 20px;
}
}
.new-file {
a {
color: $gl-text-green;
}
}
.renamed-file {
a {
color: $gl-text-orange;
}
}
.deleted-file {
a {
color: $gl-text-red;
}
}
.edit-file {
a {
color: $gl-text-color;
}
}
a {
text-decoration: none;
.new-file {
color: $notify-new-file;
}
.deleted-file {
color: $notify-deleted-file;
}
}
}
......@@ -234,14 +234,46 @@ ul.content-list {
}
}
.panel > .content-list > li {
padding: $gl-padding-top $gl-padding;
// Table list
.table-list {
display: table;
width: 100%;
.table-list-row {
display: table-row;
}
.table-list-cell {
display: table-cell;
vertical-align: top;
padding: 10px 16px;
border-bottom: 1px solid $gray-darker;
&.commit {
@media (min-width: $screen-sm-min) {
padding-left: 46px + $gl-padding;
&.avatar-cell {
width: 36px;
padding-right: 0;
img {
margin-right: 0;
}
}
}
&.table-wide {
.table-list-cell {
&:last-of-type {
padding-right: 0;
}
&:first-of-type {
padding-left: 0;
}
}
}
}
.panel > .content-list > li {
padding: $gl-padding-top $gl-padding;
}
ul.controls {
......
......@@ -48,3 +48,11 @@
line-height: inherit;
}
}
.panel-default {
.table-list-row:last-child {
.table-list-cell {
border-bottom: 0;
}
}
}
......@@ -376,7 +376,6 @@ $callout-success-color: #3c763d;
/*
* Commit Page
*/
$commit-committer-color: #999;
$commit-max-width-marker-color: rgba(0, 0, 0, 0.0);
$commit-message-text-area-bg: rgba(0, 0, 0, 0.0);
......
......@@ -18,15 +18,3 @@ p.details {
pre.commit-message {
white-space: pre-wrap;
}
.file-stats > a {
text-decoration: none;
> .new-file {
color: $notify-new-file;
}
> .deleted-file {
color: $notify-deleted-file;
}
}
.divergence-graph {
padding: 12px 12px 0 0;
float: right;
.graph-side {
position: relative;
width: 80px;
height: 22px;
padding: 5px 0 13px;
float: left;
.bar {
position: absolute;
height: 4px;
background-color: $divergence-graph-bar-bg;
}
.bar-behind {
right: 0;
border-radius: 3px 0 0 3px;
}
.bar-ahead {
left: 0;
border-radius: 0 3px 3px 0;
}
.count {
padding-top: 6px;
padding-bottom: 0;
font-size: 12px;
color: $gl-title-color;
display: block;
}
.count-behind {
padding-right: 4px;
text-align: right;
}
.count-ahead {
padding-left: 4px;
text-align: left;
}
}
.graph-separator {
position: relative;
width: 1px;
height: 18px;
margin: 5px 0 0;
float: left;
background-color: $divergence-graph-separator-bg;
}
}
.commit-title {
display: block;
}
.commit-author,
.commit-committer {
display: block;
color: $commit-committer-color;
font-weight: normal;
font-style: italic;
}
.commit-author strong,
.commit-committer strong {
font-weight: bold;
font-style: normal;
}
.commit-description {
background: none;
border: none;
margin: 0;
padding: 0;
margin-top: 10px;
word-break: normal;
white-space: pre-wrap;
}
.js-details-expand {
&:hover {
text-decoration: none;
}
}
.ci-status-link {
svg {
overflow: visible;
}
}
.commit-box {
border-top: 1px solid $border-color;
padding: $gl-padding 0;
.commit-title {
margin: 0;
font-size: 23px;
color: $gl-gray-dark;
}
.commit-description {
margin-top: 15px;
}
}
.commit-hash-full {
@media (max-width: $screen-sm-max) {
width: 80px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: bottom;
}
}
.file-stats {
ul {
list-style: none;
margin: 0;
padding: 10px 0;
li {
padding: 3px 0;
line-height: 20px;
}
}
.new-file {
a {
color: $gl-text-green;
}
}
.renamed-file {
a {
color: $gl-text-orange;
}
}
.deleted-file {
a {
color: $gl-text-red;
}
}
.edit-file {
a {
color: $gl-text-color;
}
}
}
/*
* Commit message textarea for web editor and
* custom merge request message
*/
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
overflow: hidden; // See https://gitlab.com/gitlab-org/gitlab-ce/issues/13987
.max-width-marker {
width: 72ch;
color: $commit-max-width-marker-color;
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, $white-light);
position: absolute;
z-index: 1;
}
> textarea {
background-color: $commit-message-text-area-bg;
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
.commit-description {
background: none;
border: none;
padding: 0;
margin-top: 10px;
word-break: normal;
white-space: pre-wrap;
}
.js-details-expand {
&:hover {
text-decoration: none;
}
}
.commit-box {
border-top: 1px solid $border-color;
padding: $gl-padding 0;
.commit-title {
margin: 0;
color: $gl-gray-dark;
}
.commit-description {
margin-top: 15px;
}
}
.commit-hash-full {
@media (max-width: $screen-sm-max) {
width: 80px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: bottom;
}
}
/*
* Commit message textarea for web editor and
* custom merge request message
*/
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
overflow: hidden; // See https://gitlab.com/gitlab-org/gitlab-ce/issues/13987
.max-width-marker {
width: 72ch;
color: $commit-max-width-marker-color;
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, $white-light);
position: absolute;
z-index: 1;
}
textarea {
background-color: $commit-message-text-area-bg;
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
.commits-compare-switch {
@include btn-default;
@include btn-white;
float: left;
margin-right: 9px;
}
......@@ -8,7 +77,6 @@
.commit-header {
padding: 5px 10px;
background-color: $gray-light;
border-top: 1px solid $gray-darker;
border-bottom: 1px solid $gray-darker;
font-size: 14px;
......@@ -18,8 +86,6 @@
}
.commit-row-title {
line-height: 1.35;
.notes_count {
float: right;
margin-right: 10px;
......@@ -32,7 +98,6 @@
.commit-row-message {
color: $gl-dark-link-color;
}
}
.text-expander {
......@@ -54,9 +119,8 @@
.commit-actions {
@media (min-width: $screen-sm-min) {
float: right;
margin-left: $gl-padding;
margin-top: 2px;
width: 300px;
text-align: right;
font-size: 0;
}
......@@ -86,16 +150,6 @@
.commit,
.generic_commit_status {
padding: 10px 0;
position: relative;
@media (min-width: $screen-sm-min) {
padding-left: 46px;
}
&:not(:last-child) {
border-bottom: 1px solid $gray-darker;
}
a,
button {
......@@ -103,18 +157,6 @@
vertical-align: baseline;
}
.avatar {
margin-left: -46px;
}
.item-title {
display: inline-block;
@media (min-width: $screen-sm-min) {
max-width: 70%;
}
}
.commit-row-description {
font-size: 14px;
border-left: 1px solid $white-normal;
......@@ -138,19 +180,6 @@
}
}
.commit-row-info {
color: $gl-gray;
line-height: 1.35;
a {
color: $gl-gray;
}
.avatar {
margin-right: 8px;
}
}
&.inline-commit {
.commit-row-title {
font-size: 13px;
......@@ -186,59 +215,3 @@
color: $gl-gray;
}
}
.divergence-graph {
padding: 12px 12px 0 0;
float: right;
.graph-side {
position: relative;
width: 80px;
height: 22px;
padding: 5px 0 13px;
float: left;
.bar {
position: absolute;
height: 4px;
background-color: $divergence-graph-bar-bg;
}
.bar-behind {
right: 0;
border-radius: 3px 0 0 3px;
}
.bar-ahead {
left: 0;
border-radius: 0 3px 3px 0;
}
.count {
padding-top: 6px;
padding-bottom: 0;
font-size: 12px;
color: $gl-title-color;
display: block;
}
.count-behind {
padding-right: 4px;
text-align: right;
}
.count-ahead {
padding-left: 4px;
text-align: left;
}
}
.graph-separator {
position: relative;
width: 1px;
height: 18px;
margin: 5px 0 0;
float: left;
background-color: $divergence-graph-separator-bg;
}
}
......@@ -5,6 +5,12 @@
}
}
.title {
padding: 0;
margin: 0;
border-bottom: none;
}
// Border around images in issue and MR descriptions.
.description img:not(.emoji) {
border: 1px solid $white-normal;
......
......@@ -310,10 +310,6 @@
left: 0;
top: 2px;
}
.commit-row-info {
line-height: 20px;
}
}
.btn-clipboard {
......
......@@ -43,7 +43,7 @@ ul.notes {
}
.system-note-message {
display: inline-block;
display: inline;
&::first-letter {
text-transform: lowercase;
......@@ -55,7 +55,7 @@ ul.notes {
}
p {
display: inline-block;
display: inline;
margin: 0;
&::first-letter {
......@@ -353,6 +353,14 @@ ul.notes {
font-size: 14px;
}
.note-headline-light {
display: inline;
@media (max-width: $screen-xs-min) {
display: block;
}
}
.note-headline-light,
.discussion-headline-light {
color: $notes-light-color;
......
......@@ -26,7 +26,7 @@
margin-bottom: 5px;
}
&> .form-group {
& > .form-group {
padding-left: 0;
}
}
......@@ -73,7 +73,7 @@
border: 1px solid $border-color;
}
&+ .select2 a {
& + .select2 a {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
......@@ -618,7 +618,6 @@ pre.light-well {
margin: 0;
}
.activity-filter-block {
.controls {
padding-bottom: 7px;
......@@ -811,7 +810,31 @@ pre.light-well {
.compare-form-group {
.dropdown-menu {
width: 300px;
width: 100%;
@media (min-width: $screen-sm-min) {
width: 300px;
}
}
+ .compare-ellipsis {
width: 100%;
vertical-align: middle;
text-align: center;
margin-top: -20px;
@media (min-width: $screen-sm-min) {
margin-top: 0;
width: auto;
}
}
.inline-input-group {
width: 100%;
@media (min-width: $screen-sm-min) {
width: 250px;
}
}
}
......
......@@ -135,3 +135,9 @@
left: 5px;
}
}
.ci-status-link {
svg {
overflow: visible;
}
}
......@@ -134,21 +134,18 @@
.blob-commit-info {
list-style: none;
padding: $gl-padding;
background: $gray-light;
padding: 6px 0;
border: 1px solid $border-color;
border-bottom: none;
margin: 0;
.commit {
padding-top: 0;
padding-bottom: 0;
.table-list-cell {
border-bottom: none;
}
.commit-row-title {
.commit-row-message {
font-weight: normal;
}
}
.commit-actions {
width: 200px;
}
}
......
......@@ -30,7 +30,7 @@ class Projects::MattermostsController < Projects::ApplicationController
def configure_params
params.require(:mattermost).permit(:trigger, :team_id).merge(
url: service_trigger_url(@service),
icon_url: asset_url('gitlab_logo.png'))
icon_url: asset_url('slash-command-logo.png'))
end
def teams
......
......@@ -98,7 +98,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
render_404
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
......
......@@ -7,17 +7,17 @@ class RegistrationsController < Devise::RegistrationsController
end
def create
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
# To avoid duplicate form fields on the login page, the registration form
# names fields using `new_user`, but Devise still wants the params in
# `user`.
if params["new_#{resource_name}"].present? && params[resource_name].blank?
params[resource_name] = params.delete(:"new_#{resource_name}")
end
# To avoid duplicate form fields on the login page, the registration form
# names fields using `new_user`, but Devise still wants the params in
# `user`.
if params["new_#{resource_name}"].present? && params[resource_name].blank?
params[resource_name] = params.delete(:"new_#{resource_name}")
end
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
super
else
flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code."
flash[:alert] = 'There was an error with the reCAPTCHA. Please re-solve the reCAPTCHA.'
flash.delete :recaptcha_error
render action: 'new'
end
......@@ -57,7 +57,7 @@ class RegistrationsController < Devise::RegistrationsController
end
def sign_up_params
params.require(:user).permit(:username, :email, :name, :password, :password_confirmation)
params.require(:user).permit(:username, :email, :email_confirmation, :name, :password)
end
def resource_name
......
......@@ -110,6 +110,28 @@ module GitlabMarkdownHelper
end
end
# Returns the text necessary to reference `entity` across projects
#
# project - Project to reference
# entity - Object that responds to `to_reference`
#
# Examples:
#
# cross_project_reference(project, project.issues.first)
# # => 'namespace1/project1#123'
#
# cross_project_reference(project, project.merge_requests.first)
# # => 'namespace1/project1!345'
#
# Returns a String
def cross_project_reference(project, entity)
if entity.respond_to?(:to_reference)
entity.to_reference(project, full: true)
else
''
end
end
private
# Return +text+, truncated to +max_chars+ characters, excluding any HTML
......@@ -158,28 +180,6 @@ module GitlabMarkdownHelper
end
end
# Returns the text necessary to reference `entity` across projects
#
# project - Project to reference
# entity - Object that responds to `to_reference`
#
# Examples:
#
# cross_project_reference(project, project.issues.first)
# # => 'namespace1/project1#123'
#
# cross_project_reference(project, project.merge_requests.first)
# # => 'namespace1/project1!345'
#
# Returns a String
def cross_project_reference(project, entity)
if entity.respond_to?(:to_reference)
entity.to_reference(project)
else
''
end
end
def markdown_toolbar_button(options = {})
data = options[:data].merge({ container: "body" })
content_tag :button,
......
......@@ -58,13 +58,13 @@ module IssuesHelper
end
def status_box_class(item)
if item.respond_to?(:expired?) && item.expired?
if item.try(:expired?)
'status-box-expired'
elsif item.respond_to?(:merged?) && item.merged?
elsif item.try(:merged?)
'status-box-merged'
elsif item.closed?
'status-box-closed'
elsif item.respond_to?(:upcoming?) && item.upcoming?
elsif item.try(:upcoming?)
'status-box-upcoming'
else
'status-box-open'
......@@ -128,8 +128,10 @@ module IssuesHelper
names.to_sentence
end
def award_active_class(awards, current_user)
if current_user && awards.find { |a| a.user_id == current_user.id }
def award_state_class(awards, current_user)
if !current_user
"disabled"
elsif current_user && awards.find { |a| a.user_id == current_user.id }
"active"
else
""
......
......@@ -91,8 +91,8 @@ class Commit
@link_reference_pattern ||= super("commit", /(?<commit>\h{7,40})/)
end
def to_reference(from_project = nil)
commit_reference(from_project, id)
def to_reference(from_project = nil, full: false)
commit_reference(from_project, id, full: full)
end
def reference_link_text(from_project = nil)
......@@ -320,8 +320,8 @@ class Commit
private
def commit_reference(from_project, referable_commit_id)
reference = project.to_reference(from_project)
def commit_reference(from_project, referable_commit_id, full: false)
reference = project.to_reference(from_project, full: full)
if reference.present?
"#{reference}#{self.class.reference_prefix}#{referable_commit_id}"
......
......@@ -89,8 +89,8 @@ class CommitRange
alias_method :id, :to_s
def to_reference(from_project = nil)
project_reference = project.to_reference(from_project)
def to_reference(from_project = nil, full: false)
project_reference = project.to_reference(from_project, full: full)
if project_reference.present?
project_reference + self.class.reference_prefix + self.id
......
......@@ -17,7 +17,7 @@ module Referable
# Issue.last.to_reference(other_project) # => "cross-project#1"
#
# Returns a String
def to_reference(_from_project = nil)
def to_reference(_from_project = nil, full:)
''
end
......
......@@ -38,7 +38,7 @@ class ExternalIssue
@reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
end
def to_reference(_from_project = nil)
def to_reference(_from_project = nil, full: nil)
id
end
......
......@@ -80,7 +80,7 @@ class Group < Namespace
end
end
def to_reference(_from_project = nil)
def to_reference(_from_project = nil, full: nil)
"#{self.class.reference_prefix}#{name}"
end
......
......@@ -8,8 +8,4 @@ class GroupLabel < Label
def subject_foreign_key
'group_id'
end
def to_reference(source_project = nil, target_project = nil, format: :id)
super(source_project, target_project, format: format)
end
end
......@@ -97,10 +97,10 @@ class Issue < ActiveRecord::Base
end
end
def to_reference(from_project = nil)
def to_reference(from_project = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference(from_project)}#{reference}"
"#{project.to_reference(from_project, full: full)}#{reference}"
end
def referenced_merge_requests(current_user = nil)
......
......@@ -146,17 +146,17 @@ class Label < ActiveRecord::Base
#
# Label.first.to_reference # => "~1"
# Label.first.to_reference(format: :name) # => "~\"bug\""
# Label.first.to_reference(project, same_namespace_project) # => "gitlab-ce~1"
# Label.first.to_reference(project, another_namespace_project) # => "gitlab-org/gitlab-ce~1"
# Label.first.to_reference(project, target_project: same_namespace_project) # => "gitlab-ce~1"
# Label.first.to_reference(project, target_project: another_namespace_project) # => "gitlab-org/gitlab-ce~1"
#
# Returns a String
#
def to_reference(source_project = nil, target_project = nil, format: :id)
def to_reference(from_project = nil, target_project: nil, format: :id, full: false)
format_reference = label_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}"
if source_project
"#{source_project.to_reference(target_project)}#{reference}"
if from_project
"#{from_project.to_reference(target_project, full: full)}#{reference}"
else
reference
end
......
......@@ -175,10 +175,10 @@ class MergeRequest < ActiveRecord::Base
work_in_progress?(title) ? title : "WIP: #{title}"
end
def to_reference(from_project = nil)
def to_reference(from_project = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference(from_project)}#{reference}"
"#{project.to_reference(from_project, full: full)}#{reference}"
end
def first_commit
......
......@@ -118,11 +118,11 @@ class Milestone < ActiveRecord::Base
# Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-ce%1"
# Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1"
#
def to_reference(from_project = nil, format: :iid)
def to_reference(from_project = nil, format: :iid, full: false)
format_reference = milestone_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}"
"#{project.to_reference(from_project)}#{reference}"
"#{project.to_reference(from_project, full: full)}#{reference}"
end
def reference_link_text(from_project = nil)
......
......@@ -589,8 +589,8 @@ class Project < ActiveRecord::Base
end
end
def to_reference(from_project = nil)
if cross_namespace_reference?(from_project)
def to_reference(from_project = nil, full: false)
if full || cross_namespace_reference?(from_project)
path_with_namespace
elsif cross_project_reference?(from_project)
path
......@@ -609,10 +609,6 @@ class Project < ActiveRecord::Base
Gitlab::Routing.url_helpers.namespace_project_url(self.namespace, self)
end
def web_url_without_protocol
web_url.split('://')[1]
end
def new_issue_address(author)
return unless Gitlab::IncomingEmail.supports_issue_creation? && author
......
......@@ -16,8 +16,8 @@ class ProjectLabel < Label
'project_id'
end
def to_reference(target_project = nil, format: :id)
super(project, target_project, format: format)
def to_reference(target_project = nil, format: :id, full: false)
super(project, target_project: target_project, format: format, full: full)
end
private
......
......@@ -64,11 +64,11 @@ class Snippet < ActiveRecord::Base
@link_reference_pattern ||= super("snippets", /(?<snippet>\d+)/)
end
def to_reference(from_project = nil)
def to_reference(from_project = nil, full: false)
reference = "#{self.class.reference_prefix}#{id}"
if project.present?
"#{project.to_reference(from_project)}#{reference}"
"#{project.to_reference(from_project, full: full)}#{reference}"
else
reference
end
......
......@@ -99,6 +99,7 @@ class User < ActiveRecord::Base
#
# Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true
validates_confirmation_of :email
validates :notification_email, presence: true
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
......@@ -332,7 +333,7 @@ class User < ActiveRecord::Base
username
end
def to_reference(_from_project = nil, _target_project = nil)
def to_reference(_from_project = nil, target_project: nil, full: nil)
"#{self.class.reference_prefix}#{username}"
end
......
......@@ -10,4 +10,15 @@ class AvatarUploader < GitlabUploader
def exists?
model.avatar.file && model.avatar.file.exists?
end
# We set move_to_store and move_to_cache to 'false' to prevent stealing
# the avatar file from a project when forking it.
# https://gitlab.com/gitlab-org/gitlab-ce/issues/26158
def move_to_store
false
end
def move_to_cache
false
end
end
......@@ -2,8 +2,7 @@
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
- awards_sort(grouped_emojis).each do |emoji, awards|
%button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button",
disabled: !current_user,
class: (award_active_class(awards, current_user)),
class: (award_state_class(awards, current_user)),
data: { placement: "bottom", title: award_user_list(awards, current_user) } }
= emoji_icon(emoji, sprite: false)
%span.award-control-text.js-counter
......
......@@ -15,6 +15,9 @@
.form-group
= f.label :email
= f.email_field :email, class: "form-control middle", required: true, title: "Please provide a valid email address."
%div.form-group
= f.label :email_confirmation
= f.email_field :email_confirmation, class: "form-control middle", required: true, title: "Please retype the email address."
.form-group.append-bottom-20#password-strength
= f.label :password
= f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
......
......@@ -18,7 +18,7 @@
- else
= link_to title, '#'
%ul.blob-commit-info.hidden-xs
%ul.blob-commit-info.table-list.hidden-xs
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project, ref: @ref
......
......@@ -9,33 +9,33 @@
- cache_key.push(commit.status(ref)) if commit.status(ref)
= cache(cache_key, expires_in: 1.day) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
= author_avatar(commit, size: 36)
%li.commit.table-list-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
.commit-info-block
.commit-row-title
%span.item-title
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
%span.commit-row-message.visible-xs-inline
&middot;
= commit.short_id
- if commit.status(ref)
.visible-xs-inline
= render_commit_status(commit, ref: ref)
- if commit.description?
%a.text-expander.hidden-xs.js-toggle-button ...
.table-list-cell.avatar-cell.hidden-xs
= author_avatar(commit, size: 36)
.commit-actions.hidden-xs
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
= clipboard_button(clipboard_text: commit.id)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
= link_to_browse_code(project, commit)
.table-list-cell.commit-content
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message item-title"
%span.commit-row-message.visible-xs-inline
&middot;
= commit.short_id
- if commit.status(ref)
.visible-xs-inline
= render_commit_status(commit, ref: ref)
- if commit.description?
%a.text-expander.hidden-xs.js-toggle-button ...
- if commit.description?
%pre.commit-row-description.js-toggle-content
= preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
.commiter
= commit_author_link(commit, avatar: false, size: 24)
committed
#{time_ago_with_tooltip(commit.committed_date)}
= commit_author_link(commit, avatar: false, size: 24)
committed
#{time_ago_with_tooltip(commit.committed_date)}
.table-list-cell.commit-actions.hidden-xs
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
= clipboard_button(clipboard_text: commit.id)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
= link_to_browse_code(project, commit)
......@@ -11,4 +11,4 @@
%li.warning-row.unstyled
#{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
- else
%ul.content-list= render commits, project: @project, ref: @ref
%ul.content-list.table-list= render commits, project: @project, ref: @ref
......@@ -4,7 +4,7 @@
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
%li.commit-header= "#{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}"
%li.commits-row
%ul.list-unstyled.commit-list
%ul.content-list.commit-list.table-list.table-wide
= render commits, project: project, ref: ref
- if hidden > 0
......
......@@ -2,21 +2,21 @@
.clearfix
- if params[:to] && params[:from]
.compare-switch-container
= link_to icon('exchange'), {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
= link_to icon('exchange'), {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Switch base of comparison'}
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
.input-group.inline-input-group
%span.input-group-addon from
= hidden_field_tag :from, params[:from]
= button_tag type: 'button', class: "form-control compare-dropdown-toggle js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text= params[:from] || 'Select branch/tag'
= button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag'
= render "ref_dropdown"
.compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.to.js-compare-to-dropdown
.input-group.inline-input-group
%span.input-group-addon to
= hidden_field_tag :to, params[:to]
= button_tag type: 'button', class: "form-control compare-dropdown-toggle js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
.dropdown-toggle-text= params[:to] || 'Select branch/tag'
= button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
.dropdown-toggle-text.str-truncated= params[:to] || 'Select branch/tag'
= render "ref_dropdown"
&nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
......
......@@ -2,6 +2,6 @@
.empty-stage
.icon-no-data
= custom_icon ('icon_no_data')
%h4 We dont have enough data to show this stage.
%h4 We don't have enough data to show this stage.
%p
{{currentStage.emptyStageText}}
......@@ -28,8 +28,8 @@
.container-fluid
.row
.col-sm-3.col-xs-12.column{ "v-for" => "item in state.summary" }
%h3.header {{item.value}}
%p.text {{item.title}}
%h3.header {{ item.value }}
%p.text {{ item.title }}
.col-sm-3.col-xs-12.column
.dropdown.inline.js-ca-dropdown
%button.dropdown-menu-toggle{ "data-toggle" => "dropdown", :type => "button" }
......
......@@ -42,7 +42,7 @@
= render "projects/merge_requests/widget/show.html.haml"
- if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user)
.merge-manually.light.prepend-top-default.append-bottom-default
.merge-manually.light.prepend-top-default
You can also accept this merge request manually using the
= succeed '.' do
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
......
......@@ -19,7 +19,7 @@
%ul
- @merge_request_diffs.each do |merge_request_diff|
%li
= link_to merge_request_version_path(@project, @merge_request, merge_request_diff), class: ('is-active' if merge_request_diff == @merge_request_diff) do
= link_to merge_request_version_path(@project, @merge_request, merge_request_diff, @start_sha), class: ('is-active' if merge_request_diff == @merge_request_diff) do
%strong
- if merge_request_diff.latest?
latest version
......
......@@ -50,7 +50,7 @@
.form-group
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
= image_tag(asset_url('gitlab_logo.png'), width: 36, height: 36)
= image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36)
= link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank')
.form-group
......
---
title: Resolves overflow in compare branch and tags dropdown
merge_request: 8118
author:
---
title: Add email confirmation field to registration form
merge_request: 7432
author:
---
title: Removed bottom padding from merge manually from CLI because of repositioning award emoji's
merge_request:
author:
---
title: Rename wiki_events to wiki_page_events in project hooks API to avoid errors
merge_request: Robert Schilling
author: 8425
---
title: Fix cross-project references copy to include the project reference
merge_request:
author:
---
title: Gitlab::LDAP::Person uses LDAP attributes configuration
merge_request: 8418
author:
---
title: Copy, don't move uploaded avatar files
merge_request: 8396
author:
---
title: Properly handle failed reCAPTCHA on user registration
merge_request: 8403
author:
---
title: Fixed regression of note-headline-light where it was always placed on 2 lines, even on wide viewports
merge_request:
author:
......@@ -40,6 +40,15 @@ Its simplest usage is to provide the value for `title`:
```text
$ bin/changelog 'Hey DZ, I added a feature to GitLab!'
```
The entry filename is based on the name of the current Git branch. If you run
the command above on a branch called `feature/hey-dz`, it will generate a
`changelogs/unreleased/feature-hey-dz.yml` file.
The command will output the path of the generated file and its contents:
```text
create changelogs/unreleased/my-feature.yml
---
title: Hey DZ, I added a feature to GitLab!
......@@ -47,10 +56,6 @@ merge_request:
author:
```
The entry filename is based on the name of the current Git branch. If you run
the command above on a branch called `feature/hey-dz`, it will generate a
`changelogs/unreleased/feature-hey-dz.yml` file.
### Arguments
| Argument | Shorthand | Purpose |
......@@ -139,7 +144,7 @@ Use the **`--git-username`** or **`-u`** argument to automatically fill in the
$ git config user.name
Jane Doe
$ bin/changelog --u 'Hey DZ, I added a feature to GitLab!'
$ bin/changelog -u 'Hey DZ, I added a feature to GitLab!'
create changelogs/unreleased/feature-hey-dz.yml
---
title: Hey DZ, I added a feature to GitLab!
......
......@@ -5,28 +5,18 @@ module API
before { authenticate! }
helpers do
def filter_issues_state(issues, state)
case state
when 'opened' then issues.opened
when 'closed' then issues.closed
else issues
end
end
# TODO: Remove in 9.0 and switch to IssueFinder-based label filtering
def filter_issues_labels(issues, labels)
issues.includes(:labels).where('labels.title' => labels.split(','))
end
def filter_issues_milestone(issues, milestone)
issues.includes(:milestone).where('milestones.title' => milestone)
end
params :issues_params do
optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return issues ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return issues sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return issues for a specific milestone'
use :pagination
end
......@@ -50,8 +40,7 @@ module API
use :issues_params
end
get do
issues = current_user.issues.inc_notes_with_associations
issues = filter_issues_state(issues, params[:state])
issues = IssuesFinder.new(current_user, scope: 'all', author_id: current_user.id, state: params[:state]).execute.inc_notes_with_associations
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = issues.reorder(params[:order_by] => params[:sort])
......@@ -99,16 +88,14 @@ module API
use :issues_params
end
get ":id/issues" do
issues = IssuesFinder.new(current_user, project_id: user_project.id).execute.inc_notes_with_associations
issues = filter_issues_state(issues, params[:state])
issues = IssuesFinder.new(current_user,
project_id: user_project.id,
state: params[:state],
milestone_title: params[:milestone]).execute.inc_notes_with_associations
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
unless params[:milestone].nil?
issues = filter_issues_milestone(issues, params[:milestone])
end
issues = issues.reorder(params[:order_by] => params[:sort])
present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project
end
......
......@@ -15,7 +15,7 @@ module API
optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events"
optional :build_events, type: Boolean, desc: "Trigger hook on build events"
optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events"
optional :wiki_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response"
end
......
......@@ -16,7 +16,7 @@ module API
optional :website_url, type: String, desc: 'The website of the user'
optional :organization, type: String, desc: 'The organization of the user'
optional :projects_limit, type: Integer, desc: 'The number of projects a user can create'
optional :extern_uid, type: Integer, desc: 'The external authentication provider UID'
optional :extern_uid, type: String, desc: 'The external authentication provider UID'
optional :provider, type: String, desc: 'The external provider'
optional :bio, type: String, desc: 'The biography of the user'
optional :location, type: String, desc: 'The location of the user'
......
......@@ -49,9 +49,9 @@ module Gitlab
end
def url(subject)
project = subject.project
namespace_project_build_url(project.namespace.becomes(Namespace), project, subject)
polymorphic_url(
[subject.project.namespace.becomes(Namespace), subject.project, subject]
)
end
end
end
......
......@@ -76,7 +76,7 @@ module Gitlab
if referable.respond_to?(:project)
referable.to_reference(target_project)
else
referable.to_reference(@source_project, target_project)
referable.to_reference(@source_project, target_project: target_project)
end
end
......
......@@ -28,7 +28,7 @@ module Gitlab
end
def name
entry.cn.first
attribute_value(:name)
end
def uid
......@@ -40,7 +40,7 @@ module Gitlab
end
def email
entry.try(:mail)
attribute_value(:email)
end
def dn
......@@ -56,6 +56,21 @@ module Gitlab
def config
@config ||= Gitlab::LDAP::Config.new(provider)
end
# Using the LDAP attributes configuration, find and return the first
# attribute with a value. For example, by default, when given 'email',
# this method looks for 'mail', 'email' and 'userPrincipalName' and
# returns the first with a value.
def attribute_value(attribute)
attributes = Array(config.attributes[attribute.to_sym])
selected_attr = attributes.find { |attr| entry.respond_to?(attr) }
return nil unless selected_attr
# Some LDAP attributes return an array,
# even if it is a single value (like 'cn')
Array(entry.public_send(selected_attr)).first
end
end
end
end
......@@ -2,30 +2,60 @@ require 'spec_helper'
describe RegistrationsController do
describe '#create' do
around(:each) do |example|
perform_enqueued_jobs do
example.run
let(:user_params) { { user: { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } } }
context 'email confirmation' do
around(:each) do |example|
perform_enqueued_jobs do
example.run
end
end
end
let(:user_params) { { user: { name: "new_user", username: "new_username", email: "new@user.com", password: "Any_password" } } }
context 'when send_user_confirmation_email is false' do
it 'signs the user in' do
allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false)
expect { post(:create, user_params) }.not_to change{ ActionMailer::Base.deliveries.size }
expect(subject.current_user).not_to be_nil
end
end
context 'when sending email confirmation' do
before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false) }
context 'when send_user_confirmation_email is true' do
it 'does not authenticate user and sends confirmation email' do
allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true)
it 'logs user in directly' do
expect { post(:create, user_params) }.not_to change{ ActionMailer::Base.deliveries.size }
expect(subject.current_user).not_to be_nil
post(:create, user_params)
expect(ActionMailer::Base.deliveries.last.to.first).to eq(user_params[:user][:email])
expect(subject.current_user).to be_nil
end
end
end
context 'when not sending email confirmation' do
before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) }
context 'when reCAPTCHA is enabled' do
before do
stub_application_setting(recaptcha_enabled: true)
end
it 'displays an error when the reCAPTCHA is not solved' do
# Without this, `verify_recaptcha` arbitraily returns true in test env
Recaptcha.configuration.skip_verify_env.delete('test')
it 'does not authenticate user and sends confirmation email' do
post(:create, user_params)
expect(ActionMailer::Base.deliveries.last.to.first).to eq(user_params[:user][:email])
expect(subject.current_user).to be_nil
expect(response).to render_template(:new)
expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please re-solve the reCAPTCHA.'
end
it 'redirects to the dashboard when the recaptcha is solved' do
# Avoid test ordering issue and ensure `verify_recaptcha` returns true
unless Recaptcha.configuration.skip_verify_env.include?('test')
Recaptcha.configuration.skip_verify_env << 'test'
end
post(:create, user_params)
expect(flash[:notice]).to include 'Welcome! You have signed up successfully.'
end
end
end
......
require 'spec_helper'
feature 'Cycle Analytics', feature: true, js: true do
include WaitForAjax
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:guest) { create(:user) }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
let(:milestone) { create(:milestone, project: project) }
let(:mr) { create_merge_request_closing_issue(issue) }
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha) }
context 'as an allowed user' do
context 'when project is new' do
before do
project.team << [user, :master]
login_as(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_ajax
end
it 'shows introductory message' do
expect(page).to have_content('Introducing Cycle Analytics')
end
it 'shows active stage with empty message' do
expect(page).to have_selector('.stage-nav-item.active', text: 'Issue')
expect(page).to have_content("We don't have enough data to show this stage.")
end
end
context "when there's cycle analytics data" do
before do
project.team << [user, :master]
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
create_cycle
deploy_master
login_as(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
end
it 'shows data on each stage' do
expect_issue_to_be_present
click_stage('Plan')
expect(find('.stage-events')).to have_content(mr.commits.last.title)
click_stage('Code')
expect_merge_request_to_be_present
click_stage('Test')
expect_build_to_be_present
click_stage('Review')
expect_merge_request_to_be_present
click_stage('Staging')
expect_build_to_be_present
click_stage('Production')
expect_issue_to_be_present
end
end
end
context "as a guest" do
before do
project.team << [guest, :guest]
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
create_cycle
deploy_master
login_as(guest)
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_ajax
end
it 'needs permissions to see restricted stages' do
expect(find('.stage-events')).to have_content(issue.title)
click_stage('Code')
expect(find('.stage-events')).to have_content('You need permission.')
click_stage('Review')
expect(find('.stage-events')).to have_content('You need permission.')
end
end
def expect_issue_to_be_present
expect(find('.stage-events')).to have_content(issue.title)
expect(find('.stage-events')).to have_content(issue.author.name)
expect(find('.stage-events')).to have_content("##{issue.iid}")
end
def expect_build_to_be_present
expect(find('.stage-events')).to have_content(@build.ref)
expect(find('.stage-events')).to have_content(@build.short_sha)
expect(find('.stage-events')).to have_content("##{@build.id}")
end
def expect_merge_request_to_be_present
expect(find('.stage-events')).to have_content(mr.title)
expect(find('.stage-events')).to have_content(mr.author.name)
expect(find('.stage-events')).to have_content("!#{mr.iid}")
end
def create_cycle
issue.update(milestone: milestone)
pipeline.run
@build = create(:ci_build, pipeline: pipeline, status: :success, author: user)
merge_merge_requests_closing_issue(issue)
ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.to_hash)
end
def click_stage(stage_name)
find('.stage-nav li', text: stage_name).click
wait_for_ajax
end
end
......@@ -76,7 +76,7 @@ describe 'Awards Emoji', feature: true do
end
it 'has disabled emoji button' do
expect(first('.award-control')[:disabled]).to be(true)
expect(first('.award-control')[:class]).to have_text('disabled')
end
end
......
......@@ -81,4 +81,52 @@ feature 'Merge Request versions', js: true, feature: true do
expect(page).to have_content '8 changed files'
end
end
describe 'compare with same version' do
before do
page.within '.mr-version-compare-dropdown' do
find('.btn-default').click
click_link 'version 1'
end
end
it 'should have 0 chages between versions' do
page.within '.mr-version-compare-dropdown' do
expect(page).to have_content 'version 1'
end
page.within '.mr-version-dropdown' do
find('.btn-default').click
find(:link, 'version 1').trigger('click')
end
expect(page).to have_content '0 changed files'
end
end
describe 'compare with newer version' do
before do
page.within '.mr-version-compare-dropdown' do
find('.btn-default').click
click_link 'version 2'
end
end
it 'should set the compared versions to be the same' do
page.within '.mr-version-compare-dropdown' do
expect(page).to have_content 'version 2'
end
page.within '.mr-version-dropdown' do
find('.btn-default').click
find(:link, 'version 1').trigger('click')
end
page.within '.mr-version-compare-dropdown' do
expect(page).to have_content 'version 1'
end
expect(page).to have_content '0 changed files'
end
end
end
......@@ -49,4 +49,10 @@ feature 'Setup Mattermost slash commands', feature: true do
end
end
end
describe 'stable logo url' do
it 'shows a publicly available logo' do
expect(File.exist?(Rails.root.join('public/slash-command-logo.png')))
end
end
end
......@@ -10,10 +10,11 @@ feature 'Signup', feature: true do
visit root_path
fill_in 'new_user_name', with: user.name
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: user.password
fill_in 'new_user_name', with: user.name
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_email_confirmation', with: user.email
fill_in 'new_user_password', with: user.password
click_button "Register"
expect(current_path).to eq users_almost_there_path
......@@ -29,10 +30,11 @@ feature 'Signup', feature: true do
visit root_path
fill_in 'new_user_name', with: user.name
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: user.password
fill_in 'new_user_name', with: user.name
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_email_confirmation', with: user.email
fill_in 'new_user_password', with: user.password
click_button "Register"
expect(current_path).to eq dashboard_projects_path
......@@ -55,8 +57,9 @@ feature 'Signup', feature: true do
click_button "Register"
expect(current_path).to eq user_registration_path
expect(page).to have_content("error prohibited this user from being saved")
expect(page).to have_content("errors prohibited this user from being saved")
expect(page).to have_content("Email has already been taken")
expect(page).to have_content("Email confirmation doesn't match")
end
it 'does not redisplay the password' do
......
......@@ -6,10 +6,11 @@ feature 'Users', feature: true, js: true do
scenario 'GET /users/sign_in creates a new user account' do
visit new_user_session_path
click_link 'Register'
fill_in 'new_user_name', with: 'Name Surname'
fill_in 'new_user_username', with: 'Great'
fill_in 'new_user_email', with: 'name@mail.com'
fill_in 'new_user_password', with: 'password1234'
fill_in 'new_user_name', with: 'Name Surname'
fill_in 'new_user_username', with: 'Great'
fill_in 'new_user_email', with: 'name@mail.com'
fill_in 'new_user_email_confirmation', with: 'name@mail.com'
fill_in 'new_user_password', with: 'password1234'
expect { click_button 'Register' }.to change { User.count }.by(1)
end
......@@ -33,10 +34,11 @@ feature 'Users', feature: true, js: true do
scenario 'Should show one error if email is already taken' do
visit new_user_session_path
click_link 'Register'
fill_in 'new_user_name', with: 'Another user name'
fill_in 'new_user_username', with: 'anotheruser'
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: '12341234'
fill_in 'new_user_name', with: 'Another user name'
fill_in 'new_user_username', with: 'anotheruser'
fill_in 'new_user_email', with: user.email
fill_in 'new_user_email_confirmation', with: user.email
fill_in 'new_user_password', with: '12341234'
expect { click_button 'Register' }.to change { User.count }.by(0)
expect(page).to have_text('Email has already been taken')
expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}'
......
......@@ -170,4 +170,14 @@ describe GitlabMarkdownHelper do
expect(doc.content).to eq "@#{user.username}, can you look at this?..."
end
end
describe '#cross_project_reference' do
it 'shows the full MR reference' do
expect(helper.cross_project_reference(project, merge_request)).to include(project.path_with_namespace)
end
it 'shows the full issue reference' do
expect(helper.cross_project_reference(project, issue)).to include(project.path_with_namespace)
end
end
end
......@@ -98,15 +98,15 @@ describe IssuesHelper do
end
end
describe '#award_active_class' do
describe '#award_state_class' do
let!(:upvote) { create(:award_emoji) }
it "returns empty string for unauthenticated user" do
expect(award_active_class(AwardEmoji.all, nil)).to eq("")
it "returns disabled string for unauthenticated user" do
expect(award_state_class(AwardEmoji.all, nil)).to eq("disabled")
end
it "returns active string for author" do
expect(award_active_class(AwardEmoji.all, upvote.user)).to eq("active")
expect(award_state_class(AwardEmoji.all, upvote.user)).to eq("active")
end
end
......
require 'spec_helper'
describe Gitlab::LDAP::Person do
include LdapHelpers
let(:entry) { ldap_user_entry('john.doe') }
before do
stub_ldap_config(
attributes: {
name: 'cn',
email: %w(mail email userPrincipalName)
}
)
end
describe '#name' do
it 'uses the configured name attribute and handles values as an array' do
name = 'John Doe'
entry['cn'] = [name]
person = Gitlab::LDAP::Person.new(entry, 'ldapmain')
expect(person.name).to eq(name)
end
end
describe '#email' do
it 'returns the value of mail, if present' do
mail = 'john@example.com'
entry['mail'] = mail
person = Gitlab::LDAP::Person.new(entry, 'ldapmain')
expect(person.email).to eq(mail)
end
it 'returns the value of userPrincipalName, if mail and email are not present' do
user_principal_name = 'john.doe@example.com'
entry['userPrincipalName'] = user_principal_name
person = Gitlab::LDAP::Person.new(entry, 'ldapmain')
expect(person.email).to eq(user_principal_name)
end
end
end
......@@ -43,7 +43,7 @@ describe GroupLabel, models: true do
let(:target_project) { build_stubbed(:empty_project, name: 'project-2', namespace: namespace) }
it 'returns a String reference to the object' do
expect(label.to_reference(source_project, target_project)).to eq %(project-1~#{label.id})
expect(label.to_reference(source_project, target_project: target_project)).to eq %(project-1~#{label.id})
end
end
......
......@@ -30,6 +30,10 @@ describe Issue, models: true do
expect(issue.to_reference).to eq "#1"
end
it 'returns a String reference with the full path' do
expect(issue.to_reference(full: true)).to eq(project.path_with_namespace + '#1')
end
it 'supports a cross-project reference' do
another_project = build(:project, name: 'another-project', namespace: project.namespace)
expect(issue.to_reference(another_project)).to eq "sample-project#1"
......
......@@ -153,6 +153,10 @@ describe MergeRequest, models: true do
another_project = build(:project, name: 'another-project', namespace: project.namespace)
expect(merge_request.to_reference(another_project)).to eq "sample-project!1"
end
it 'returns a String reference with the full path' do
expect(merge_request.to_reference(full: true)).to eq(project.path_with_namespace + '!1')
end
end
describe '#raw_diffs' do
......
......@@ -362,14 +362,6 @@ describe Project, models: true do
end
end
describe "#web_url_without_protocol" do
let(:project) { create(:empty_project, path: "somewhere") }
it 'returns the web URL without the protocol for this repo' do
expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/#{project.namespace.path}/somewhere")
end
end
describe "#new_issue_address" do
let(:project) { create(:empty_project, path: "somewhere") }
let(:user) { create(:user) }
......
......@@ -86,7 +86,8 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
describe "POST /projects/:id/hooks" do
it "adds hook to project" do
expect do
post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true
post api("/projects/#{project.id}/hooks", user),
url: "http://example.com", issues_events: true, wiki_page_events: true
end.to change {project.hooks.count}.by(1)
expect(response).to have_http_status(201)
......@@ -98,7 +99,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
expect(json_response['note_events']).to eq(false)
expect(json_response['build_events']).to eq(false)
expect(json_response['pipeline_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(true)
expect(json_response['enable_ssl_verification']).to eq(true)
expect(json_response).not_to include('token')
end
......
......@@ -317,9 +317,9 @@ describe API::Users, api: true do
end
it 'updates user with new identity' do
put api("/users/#{user.id}", admin), provider: 'github', extern_uid: '67890'
put api("/users/#{user.id}", admin), provider: 'github', extern_uid: 'john'
expect(response).to have_http_status(200)
expect(user.reload.identities.first.extern_uid).to eq('67890')
expect(user.reload.identities.first.extern_uid).to eq('john')
expect(user.reload.identities.first.provider).to eq('github')
end
......
......@@ -5,10 +5,12 @@ describe Projects::ForkService, services: true do
before do
@from_namespace = create(:namespace)
@from_user = create(:user, namespace: @from_namespace )
avatar = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
@from_project = create(:project,
creator_id: @from_user.id,
namespace: @from_namespace,
star_count: 107,
avatar: avatar,
description: 'wow such project')
@to_namespace = create(:namespace)
@to_user = create(:user, namespace: @to_namespace)
......@@ -36,6 +38,17 @@ describe Projects::ForkService, services: true do
it { expect(to_project.namespace).to eq(@to_user.namespace) }
it { expect(to_project.star_count).to be_zero }
it { expect(to_project.description).to eq(@from_project.description) }
it { expect(to_project.avatar.file).to be_exists }
# This test is here because we had a bug where the from-project lost its
# avatar after being forked.
# https://gitlab.com/gitlab-org/gitlab-ce/issues/26158
it "after forking the from-project still has its avatar" do
# If we do not fork the project first we cannot detect the bug.
expect(to_project).to be_persisted
expect(@from_project.avatar.file).to be_exists
end
end
end
......
......@@ -5,14 +5,14 @@ describe AvatarUploader do
subject { described_class.new(user) }
describe '#move_to_cache' do
it 'is true' do
expect(subject.move_to_cache).to eq(true)
it 'is false' do
expect(subject.move_to_cache).to eq(false)
end
end
describe '#move_to_store' do
it 'is true' do
expect(subject.move_to_store).to eq(true)
it 'is false' do
expect(subject.move_to_store).to eq(false)
end
end
end
......@@ -17,7 +17,7 @@ describe FileUploader do
describe '#image_or_video?' do
context 'given an image file' do
before do
@uploader.store!(File.new(Rails.root.join('spec', 'fixtures', 'rails_sample.jpg')))
@uploader.store!(fixture_file_upload(Rails.root.join('spec', 'fixtures', 'rails_sample.jpg')))
end
it 'detects an image based on file extension' do
......@@ -27,7 +27,7 @@ describe FileUploader do
context 'given an video file' do
before do
video_file = File.new(Rails.root.join('spec', 'fixtures', 'video_sample.mp4'))
video_file = fixture_file_upload(Rails.root.join('spec', 'fixtures', 'video_sample.mp4'))
@uploader.store!(video_file)
end
......@@ -37,7 +37,7 @@ describe FileUploader do
end
it 'does not return image_or_video? for other types' do
@uploader.store!(File.new(Rails.root.join('spec', 'fixtures', 'doc_sample.txt')))
@uploader.store!(fixture_file_upload(Rails.root.join('spec', 'fixtures', 'doc_sample.txt')))
expect(@uploader.image_or_video?).to be false
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