Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
39c2677e
Commit
39c2677e
authored
Apr 18, 2016
by
Kamil Trzcinski
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master' into after-script
parents
63bd1f92
2b8fc138
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
597 additions
and
246 deletions
+597
-246
CHANGELOG
CHANGELOG
+10
-1
app/assets/javascripts/gfm_auto_complete.js.coffee
app/assets/javascripts/gfm_auto_complete.js.coffee
+47
-16
app/assets/javascripts/notes.js.coffee
app/assets/javascripts/notes.js.coffee
+33
-3
app/assets/stylesheets/framework/issue_box.scss
app/assets/stylesheets/framework/issue_box.scss
+1
-1
app/assets/stylesheets/framework/mobile.scss
app/assets/stylesheets/framework/mobile.scss
+0
-7
app/assets/stylesheets/pages/detail_page.scss
app/assets/stylesheets/pages/detail_page.scss
+1
-6
app/assets/stylesheets/pages/issuable.scss
app/assets/stylesheets/pages/issuable.scss
+47
-4
app/assets/stylesheets/pages/issues.scss
app/assets/stylesheets/pages/issues.scss
+0
-40
app/controllers/help_controller.rb
app/controllers/help_controller.rb
+1
-0
app/helpers/issuables_helper.rb
app/helpers/issuables_helper.rb
+9
-0
app/models/external_issue.rb
app/models/external_issue.rb
+6
-0
app/models/repository.rb
app/models/repository.rb
+5
-3
app/services/projects/participants_service.rb
app/services/projects/participants_service.rb
+26
-17
app/views/devise/shared/_signup_box.html.haml
app/views/devise/shared/_signup_box.html.haml
+4
-5
app/views/help/ui.html.haml
app/views/help/ui.html.haml
+6
-6
app/views/projects/_builds_settings.html.haml
app/views/projects/_builds_settings.html.haml
+3
-0
app/views/projects/_md_preview.html.haml
app/views/projects/_md_preview.html.haml
+1
-1
app/views/projects/issues/show.html.haml
app/views/projects/issues/show.html.haml
+62
-65
app/views/projects/merge_requests/_show.html.haml
app/views/projects/merge_requests/_show.html.haml
+1
-2
app/views/projects/merge_requests/show/_mr_title.html.haml
app/views/projects/merge_requests/show/_mr_title.html.haml
+28
-31
app/views/projects/notes/_note.html.haml
app/views/projects/notes/_note.html.haml
+5
-4
app/views/shared/issuable/_form.html.haml
app/views/shared/issuable/_form.html.haml
+1
-1
doc/ci/quick_start/README.md
doc/ci/quick_start/README.md
+1
-1
features/steps/shared/issuable.rb
features/steps/shared/issuable.rb
+1
-1
lib/gitlab/metrics/instrumentation.rb
lib/gitlab/metrics/instrumentation.rb
+27
-10
spec/features/issues/move_spec.rb
spec/features/issues/move_spec.rb
+4
-6
spec/features/issues_spec.rb
spec/features/issues_spec.rb
+38
-0
spec/features/participants_autocomplete_spec.rb
spec/features/participants_autocomplete_spec.rb
+98
-0
spec/features/signup_spec.rb
spec/features/signup_spec.rb
+55
-0
spec/lib/gitlab/metrics/instrumentation_spec.rb
spec/lib/gitlab/metrics/instrumentation_spec.rb
+37
-14
spec/models/external_issue_spec.rb
spec/models/external_issue_spec.rb
+15
-0
spec/models/repository_spec.rb
spec/models/repository_spec.rb
+24
-1
No files found.
CHANGELOG
View file @
39c2677e
Please view this file on the master branch, on stable branches it's out of date.
v 8.7.0 (unreleased)
- Method instrumentation now uses Module#prepend instead of aliasing methods
- Repository.clean_old_archives is now instrumented
- The Projects::HousekeepingService class has extra instrumentation
- All service classes (those residing in app/services) are now instrumented
- Developers can now add custom tags to transactions
...
...
@@ -57,6 +59,7 @@ v 8.7.0 (unreleased)
- Decouple membership and notifications
- Fix creation of merge requests for orphaned branches (Stan Hu)
- API: Ability to retrieve a single tag (Robert Schilling)
- While signing up, don't persist the user password across form redisplays
- Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- Fix admin/projects when using visibility levels on search (PotHix)
...
...
@@ -73,13 +76,19 @@ v 8.7.0 (unreleased)
- Delete tags using Rugged for performance reasons (Robert Schilling)
- Diffs load at the correct point when linking from from number
- Selected diff rows highlight
- Fix emoji catgories in the emoji picker
- Fix emoji cat
e
gories in the emoji picker
- Add encrypted credentials for imported projects and migrate old ones
- Author and participants are displayed first on users autocompletion
- Show number sign on external issue reference text (Florent Baldino)
v 8.6.6
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
- Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
- Fix revoking of authorized OAuth applications (Connor Shea). !3690
- Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
- Project switcher uses new dropdown styling
- Issuable header is consistent between issues and merge requests
- Improved spacing in issuable header on mobile
v 8.6.5
- Fix importing from GitHub Enterprise. !3529
...
...
app/assets/javascripts/gfm_auto_complete.js.coffee
View file @
39c2677e
...
...
@@ -2,6 +2,8 @@
window
.
GitLab
?=
{}
GitLab
.
GfmAutoComplete
=
dataLoading
:
false
dataSource
:
''
# Emoji
...
...
@@ -17,17 +19,41 @@ GitLab.GfmAutoComplete =
template
:
'<li><small>${id}</small> ${title}</li>'
# Add GFM auto-completion to all input fields, that accept GFM input.
setup
:
->
input
=
$
(
'.js-gfm-input'
)
setup
:
(
wrap
)
->
@
input
=
$
(
'.js-gfm-input'
)
# destroy previous instances
@
destroyAtWho
()
# set up instances
@
setupAtWho
()
if
@
dataSource
if
!
@
dataLoading
@
dataLoading
=
true
# We should wait until initializations are done
# and only trigger the last .setup since
# The previous .dataSource belongs to the previous issuable
# and the last one will have the **proper** .dataSource property
# TODO: Make this a singleton and turn off events when moving to another page
setTimeout
(
=>
fetch
=
@
fetchData
(
@
dataSource
)
fetch
.
done
(
data
)
=>
@
dataLoading
=
false
@
loadData
(
data
)
,
1000
)
setupAtWho
:
->
# Emoji
input
.
atwho
@
input
.
atwho
at
:
':'
displayTpl
:
@
Emoji
.
template
insertTpl
:
':${name}:'
# Team Members
input
.
atwho
@
input
.
atwho
at
:
'@'
displayTpl
:
@
Members
.
template
insertTpl
:
'${atwho-at}${username}'
...
...
@@ -42,7 +68,7 @@ GitLab.GfmAutoComplete =
title
:
sanitize
(
title
)
search
:
sanitize
(
"
#{
m
.
username
}
#{
m
.
name
}
"
)
input
.
atwho
@
input
.
atwho
at
:
'#'
alias
:
'issues'
searchKey
:
'search'
...
...
@@ -55,7 +81,7 @@ GitLab.GfmAutoComplete =
title
:
sanitize
(
i
.
title
)
search
:
"
#{
i
.
iid
}
#{
i
.
title
}
"
input
.
atwho
@
input
.
atwho
at
:
'!'
alias
:
'mergerequests'
searchKey
:
'search'
...
...
@@ -68,13 +94,18 @@ GitLab.GfmAutoComplete =
title
:
sanitize
(
m
.
title
)
search
:
"
#{
m
.
iid
}
#{
m
.
title
}
"
if
@
dataSource
$
.
getJSON
(
@
dataSource
).
done
(
data
)
->
# load members
input
.
atwho
'load'
,
'@'
,
data
.
members
# load issues
input
.
atwho
'load'
,
'issues'
,
data
.
issues
# load merge requests
input
.
atwho
'load'
,
'mergerequests'
,
data
.
mergerequests
# load emojis
input
.
atwho
'load'
,
':'
,
data
.
emojis
destroyAtWho
:
->
@
input
.
atwho
(
'destroy'
)
fetchData
:
(
dataSource
)
->
$
.
getJSON
(
dataSource
)
loadData
:
(
data
)
->
# load members
@
input
.
atwho
'load'
,
'@'
,
data
.
members
# load issues
@
input
.
atwho
'load'
,
'issues'
,
data
.
issues
# load merge requests
@
input
.
atwho
'load'
,
'mergerequests'
,
data
.
mergerequests
# load emojis
@
input
.
atwho
'load'
,
':'
,
data
.
emojis
app/assets/javascripts/notes.js.coffee
View file @
39c2677e
...
...
@@ -75,6 +75,9 @@ class @Notes
# when issue status changes, we need to refresh data
$
(
document
).
on
"issuable:change"
,
@
refresh
# when a key is clicked on the notes
$
(
document
).
on
"keydown"
,
".js-note-text"
,
@
keydownNoteText
cleanBinding
:
->
$
(
document
).
off
"ajax:success"
,
".js-main-target-form"
$
(
document
).
off
"ajax:success"
,
".js-discussion-note-form"
...
...
@@ -92,10 +95,19 @@ class @Notes
$
(
document
).
off
"click"
,
".js-note-target-reopen"
$
(
document
).
off
"click"
,
".js-note-target-close"
$
(
document
).
off
"click"
,
".js-note-discard"
$
(
document
).
off
"keydown"
,
".js-note-text"
$
(
'.note .js-task-list-container'
).
taskList
(
'disable'
)
$
(
document
).
off
'tasklist:changed'
,
'.note .js-task-list-container'
keydownNoteText
:
(
e
)
->
$this
=
$
(
this
)
if
$this
.
val
()
is
''
and
e
.
which
is
38
#aka the up key
myLastNote
=
$
(
"li.note[data-author-id='
#{
gon
.
current_user_id
}
'][data-editable]:last"
)
if
myLastNote
.
length
myLastNoteEditBtn
=
myLastNote
.
find
(
'.js-note-edit'
)
myLastNoteEditBtn
.
trigger
(
'click'
,
[
true
,
myLastNote
])
initRefresh
:
->
clearInterval
(
Notes
.
interval
)
Notes
.
interval
=
setInterval
=>
...
...
@@ -343,7 +355,7 @@ class @Notes
Adds a hidden div with the original content of the note to fill the edit note form with
if the user cancels
###
showEditForm
:
(
e
)
->
showEditForm
:
(
e
,
scrollTo
,
myLastNote
)
->
e
.
preventDefault
()
note
=
$
(
this
).
closest
(
".note"
)
note
.
addClass
"is-editting"
...
...
@@ -354,9 +366,27 @@ class @Notes
# Show the attachment delete link
note
.
find
(
".js-note-attachment-delete"
).
show
()
new
GLForm
form
done
=
(
$noteText
)
->
# Neat little trick to put the cursor at the end
noteTextVal
=
$noteText
.
val
()
$noteText
.
val
(
''
).
val
(
noteTextVal
);
form
.
find
(
".js-note-text"
).
focus
()
new
GLForm
form
if
scrollTo
?
and
myLastNote
?
# scroll to the bottom
# so the open of the last element doesn't make a jump
$
(
'html, body'
).
scrollTop
(
$
(
document
).
height
());
$
(
'html, body'
).
animate
({
scrollTop
:
myLastNote
.
offset
().
top
-
150
},
500
,
->
$noteText
=
form
.
find
(
".js-note-text"
)
$noteText
.
focus
()
done
(
$noteText
)
);
else
$noteText
=
form
.
find
(
'.js-note-text'
)
$noteText
.
focus
()
done
(
$noteText
)
###
Called in response to clicking the edit note link
...
...
app/assets/stylesheets/framework/issue_box.scss
View file @
39c2677e
...
...
@@ -5,7 +5,7 @@
*/
.status-box
{
/* Extra small devices (phones, less than 768px) */
/* No media query since this is the default in Bootstrap */
padding
:
5px
11px
;
...
...
app/assets/stylesheets/framework/mobile.scss
View file @
39c2677e
...
...
@@ -70,13 +70,6 @@
display
:
none
;
}
.issue-details
{
.creator
,
.page-title
.btn-close
{
display
:
none
;
}
}
%ul
.notes
.note-role
,
.note-actions
{
display
:
none
;
}
...
...
app/assets/stylesheets/pages/detail_page.scss
View file @
39c2677e
.detail-page-header
{
padding
:
11px
0
;
padding
:
$gl-padding-top
0
;
border-bottom
:
1px
solid
$border-color
;
color
:
#5c5d5e
;
font-size
:
16px
;
...
...
@@ -16,11 +16,6 @@
.issue_created_ago
,
.author_link
{
white-space
:
nowrap
;
}
.issue-meta
{
display
:
inline-block
;
line-height
:
20px
;
}
}
.detail-page-description
{
...
...
app/assets/stylesheets/pages/issuable.scss
View file @
39c2677e
...
...
@@ -273,10 +273,6 @@
}
}
.btn-default.gutter-toggle
{
margin-top
:
4px
;
}
.detail-page-description
{
small
{
color
:
$gray-darkest
;
...
...
@@ -322,3 +318,50 @@
padding-top
:
7px
;
}
}
.issuable-status-box
{
float
:
none
;
display
:
inline-block
;
margin-top
:
0
;
@media
(
max-width
:
$screen-xs-max
)
{
position
:
absolute
;
top
:
0
;
left
:
0
;
}
}
.issuable-header
{
position
:
relative
;
padding-left
:
45px
;
padding-right
:
45px
;
line-height
:
35px
;
@media
(
min-width
:
$screen-sm-min
)
{
float
:
left
;
padding-left
:
0
;
padding-right
:
0
;
}
}
.issuable-actions
{
padding-top
:
10px
;
@media
(
min-width
:
$screen-sm-min
)
{
float
:
right
;
padding-top
:
0
;
}
}
.issuable-gutter-toggle
{
@media
(
max-width
:
$screen-sm-max
)
{
position
:
absolute
;
top
:
0
;
right
:
0
;
}
}
.issuable-meta
{
display
:
inline-block
;
line-height
:
18px
;
}
app/assets/stylesheets/pages/issues.scss
View file @
39c2677e
...
...
@@ -86,41 +86,9 @@ form.edit-issue {
@media
(
max-width
:
$screen-xs-max
)
{
.issue-btn-group
{
width
:
100%
;
margin-top
:
5px
;
.btn-group
{
width
:
100%
;
ul
{
width
:
100%
;
text-align
:
center
;
}
}
.btn
{
width
:
100%
;
&
:first-child:not
(
:last-child
)
{
}
&
:not
(
:first-child
)
:not
(
:last-child
)
{
margin-top
:
10px
;
}
&
:last-child:not
(
:first-child
)
{
margin-top
:
10px
;
}
}
}
.issue
{
&
:hover
.issue-actions
{
display
:
none
!
important
;
}
.issue-updated-at
{
display
:
none
;
}
}
}
...
...
@@ -133,11 +101,3 @@ form.edit-issue {
color
:
$gl-text-color
;
margin-left
:
52px
;
}
.editor-details
{
display
:
block
;
@media
(
min-width
:
$screen-sm-min
)
{
display
:
inline-block
;
}
}
app/controllers/help_controller.rb
View file @
39c2677e
...
...
@@ -51,6 +51,7 @@ class HelpController < ApplicationController
end
def
ui
@user
=
User
.
new
(
id:
0
,
name:
'John Doe'
,
username:
'@johndoe'
)
end
private
...
...
app/helpers/issuables_helper.rb
View file @
39c2677e
...
...
@@ -55,6 +55,15 @@ module IssuablesHelper
h
(
milestone_title
.
presence
||
default_label
)
end
def
issuable_meta
(
issuable
,
project
,
text
)
output
=
content_tag
:strong
,
"
#{
text
}
#{
issuable
.
to_reference
}
"
,
class:
"identifier"
output
<<
" opened
#{
time_ago_with_tooltip
(
issuable
.
created_at
)
}
by"
.
html_safe
output
<<
content_tag
(
:strong
)
do
author_output
=
link_to_member
(
project
,
issuable
.
author
,
size:
24
,
mobile_classes:
"hidden-xs"
)
author_output
<<
link_to_member
(
project
,
issuable
.
author
,
size:
24
,
by_username:
true
,
avatar:
false
,
mobile_classes:
"hidden-sm hidden-md hidden-lg"
)
end
end
private
def
sidebar_gutter_collapsed?
...
...
app/models/external_issue.rb
View file @
39c2677e
...
...
@@ -37,4 +37,10 @@ class ExternalIssue
def
to_reference
(
_from_project
=
nil
)
id
end
def
reference_link_text
(
from_project
=
nil
)
return
"#
#{
id
}
"
if
/^\d+$/
.
match
(
id
)
id
end
end
app/models/repository.rb
View file @
39c2677e
...
...
@@ -12,11 +12,13 @@ class Repository
attr_accessor
:path_with_namespace
,
:project
def
self
.
clean_old_archives
repository_downloads_path
=
Gitlab
.
config
.
gitlab
.
repository_downloads_path
Gitlab
::
Metrics
.
measure
(
:clean_old_archives
)
do
repository_downloads_path
=
Gitlab
.
config
.
gitlab
.
repository_downloads_path
return
unless
File
.
directory?
(
repository_downloads_path
)
return
unless
File
.
directory?
(
repository_downloads_path
)
Gitlab
::
Popen
.
popen
(
%W(find
#{
repository_downloads_path
}
-not -path
#{
repository_downloads_path
}
-mmin +120 -delete)
)
Gitlab
::
Popen
.
popen
(
%W(find
#{
repository_downloads_path
}
-not -path
#{
repository_downloads_path
}
-mmin +120 -delete)
)
end
end
def
initialize
(
path_with_namespace
,
project
)
...
...
app/services/projects/participants_service.rb
View file @
39c2677e
module
Projects
class
ParticipantsService
<
BaseService
def
execute
(
note_type
,
note_id
)
participating
=
if
note_type
&&
note_id
participants_in
(
note_type
,
note_id
)
else
[]
end
def
execute
(
noteable_type
,
noteable_id
)
@noteable_type
=
noteable_type
@noteable_id
=
noteable_id
project_members
=
sorted
(
project
.
team
.
members
)
participants
=
all_members
+
groups
+
project_members
+
participating
participants
=
target_owner
+
participants_in_target
+
all_members
+
groups
+
project_members
participants
.
uniq
end
def
participants_in
(
type
,
id
)
target
=
case
type
def
target
@target
||=
case
@noteable_
type
when
"Issue"
project
.
issues
.
find_by_iid
(
id
)
project
.
issues
.
find_by_iid
(
@noteable_
id
)
when
"MergeRequest"
project
.
merge_requests
.
find_by_iid
(
id
)
project
.
merge_requests
.
find_by_iid
(
@noteable_
id
)
when
"Commit"
project
.
commit
(
id
)
project
.
commit
(
@noteable_id
)
else
nil
end
end
def
target_owner
return
[]
unless
target
&&
target
.
author
.
present?
[{
name:
target
.
author
.
name
,
username:
target
.
author
.
username
}]
end
def
participants_in_target
return
[]
unless
target
users
=
target
.
participants
(
current_user
)
...
...
@@ -30,13 +39,13 @@ module Projects
end
def
sorted
(
users
)
users
.
uniq
.
to_a
.
compact
.
sort_by
(
&
:username
).
map
do
|
user
|
users
.
uniq
.
to_a
.
compact
.
sort_by
(
&
:username
).
map
do
|
user
|
{
username:
user
.
username
,
name:
user
.
name
}
end
end
def
groups
current_user
.
authorized_groups
.
sort_by
(
&
:path
).
map
do
|
group
|
current_user
.
authorized_groups
.
sort_by
(
&
:path
).
map
do
|
group
|
count
=
group
.
users
.
count
{
username:
group
.
path
,
name:
group
.
name
,
count:
count
}
end
...
...
app/views/devise/shared/_signup_box.html.haml
View file @
39c2677e
...
...
@@ -6,18 +6,17 @@
.login-heading
%h3
Create an account
.login-body
-
user
=
params
[
:user
].
present?
?
params
[
:user
]
:
{}
=
form_for
(
resource
,
as:
resource_name
,
url:
registration_path
(
resource_name
))
do
|
f
|
.devise-errors
=
devise_error_messages!
%div
=
f
.
text_field
:name
,
class:
"form-control top"
,
value:
user
[
:name
],
placeholder:
"Name"
,
required:
true
=
f
.
text_field
:name
,
class:
"form-control top"
,
placeholder:
"Name"
,
required:
true
%div
=
f
.
text_field
:username
,
class:
"form-control middle"
,
value:
user
[
:username
],
placeholder:
"Username"
,
required:
true
=
f
.
text_field
:username
,
class:
"form-control middle"
,
placeholder:
"Username"
,
required:
true
%div
=
f
.
email_field
:email
,
class:
"form-control middle"
,
value:
user
[
:email
],
placeholder:
"Email"
,
required:
true
=
f
.
email_field
:email
,
class:
"form-control middle"
,
placeholder:
"Email"
,
required:
true
.form-group.append-bottom-20
#password-strength
=
f
.
password_field
:password
,
class:
"form-control bottom"
,
value:
user
[
:password
],
id:
"user_password_sign_up"
,
placeholder:
"Password"
,
required:
true
=
f
.
password_field
:password
,
class:
"form-control bottom"
,
id:
"user_password_sign_up"
,
placeholder:
"Password"
,
required:
true
%div
-
if
current_application_settings
.
recaptcha_enabled
=
recaptcha_tags
...
...
app/views/help/ui.html.haml
View file @
39c2677e
...
...
@@ -345,11 +345,11 @@
%ul
%li
%a
.dropdown-menu-user-link.is-active
{
href:
"#"
}
=
link_to_member_avatar
(
current_
user
,
size:
30
)
=
link_to_member_avatar
(
@
user
,
size:
30
)
%strong
.dropdown-menu-user-full-name
=
current_
user
.
name
=
@
user
.
name
.dropdown-menu-user-username
=
current_
user
.
to_reference
=
@
user
.
to_reference
.example
%div
...
...
@@ -372,11 +372,11 @@
%ul
%li
%a
.dropdown-menu-user-link.is-active
{
href:
"#"
}
=
link_to_member_avatar
(
current_
user
,
size:
30
)
=
link_to_member_avatar
(
@
user
,
size:
30
)
%strong
.dropdown-menu-user-full-name
=
current_
user
.
name
=
@
user
.
name
.dropdown-menu-user-username
=
current_
user
.
to_reference
=
@
user
.
to_reference
.dropdown-page-two
.dropdown-title
%button
.dropdown-title-button.dropdown-menu-back
{
aria:
{
label:
"Go back"
}}
...
...
app/views/projects/_builds_settings.html.haml
View file @
39c2677e
...
...
@@ -52,6 +52,9 @@
%li
phpunit --coverage-text --colors=never (PHP) -
%code
^\s*Lines:\s*\d+.\d+\%
%li
gcovr (C/C++) -
%code
^TOTAL.*\s+(\d+\%)$
.form-group
.col-sm-offset-2.col-sm-10
...
...
app/views/projects/_md_preview.html.haml
View file @
39c2677e
.md-area
.md-header
%ul
.nav-links
%ul
.nav-links
.clearfix
%li
.active
%a
.js-md-write-button
{
href:
"#md-write-holder"
,
tabindex:
-
1
}
Write
...
...
app/views/projects/issues/show.html.haml
View file @
39c2677e
-
page_title
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
,
"Issues"
-
page_description
@issue
.
description
-
page_card_attributes
@issue
.
card_attributes
-
header_title
project_title
(
@project
,
"Issues"
,
namespace_project_issues_path
(
@project
.
namespace
,
@project
))
=
render
"header_title"
.clearfix.detail-page-header
.issuable-header
.issuable-status-box.status-box.status-box-closed
{
class:
issue_button_visibility
(
@issue
,
false
)
}
=
icon
(
'check'
,
class:
"hidden-sm hidden-md hidden-lg"
)
%span
.hidden-xs
Closed
.issuable-status-box.status-box.status-box-open
{
class:
issue_button_visibility
(
@issue
,
true
)
}
=
icon
(
'circle-o'
,
class:
"hidden-sm hidden-md hidden-lg"
)
%span
.hidden-xs
Open
.issue
.detail-page-header.issuable-header
.pull-left
.status-box
{
class:
"status-box-closed #{issue_button_visibility(@issue, false)}"
}
%span
.hidden-xs
Closed
%span
.hidden-sm.hidden-md.hidden-lg
=
icon
(
'check'
)
.status-box
{
class:
"status-box-open #{issue_button_visibility(@issue, true)}"
}
%span
.hidden-xs
Open
%span
.hidden-sm.hidden-md.hidden-lg
=
icon
(
'circle-o'
)
%a
.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.js-sidebar-toggle
{
href:
"#"
}
%a
.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle
{
href:
"#"
}
=
icon
(
'angle-double-left'
)
.issue-meta
.issu
abl
e-meta
=
confidential_icon
(
@issue
)
%strong
.identifier
Issue ##{@issue.iid}
%span
.creator
opened
.editor-details
.editor-details
=
time_ago_with_tooltip
(
@issue
.
created_at
)
by
%strong
=
link_to_member
(
@project
,
@issue
.
author
,
size:
24
,
mobile_classes:
"hidden-xs"
)
%strong
=
link_to_member
(
@project
,
@issue
.
author
,
size:
24
,
mobile_classes:
"hidden-sm hidden-md hidden-lg"
,
by_username:
true
,
avatar:
false
)
=
issuable_meta
(
@issue
,
@project
,
"Issue"
)
.pull-right.issue-btn-group
-
if
can?
(
current_user
,
:create_issue
,
@project
)
=
link_to
new_namespace_project_issue_path
(
@project
.
namespace
,
@project
),
class:
'btn btn-nr btn-grouped new-issue-link btn-success'
,
title:
'New issue'
,
id:
'new_issue_link'
do
=
icon
(
'plus'
)
New issue
-
if
can?
(
current_user
,
:update_issue
,
@issue
)
=
link_to
'Reopen issue'
,
issue_path
(
@issue
,
issue:
{
state_event: :reopen
},
status_only:
true
,
format:
'json'
),
data:
{
no_turbolink:
true
},
class:
"btn btn-nr btn-grouped btn-reopen
#{
issue_button_visibility
(
@issue
,
false
)
}
"
,
title:
'Reopen issue'
=
link_to
'Close issue'
,
issue_path
(
@issue
,
issue:
{
state_event: :close
},
status_only:
true
,
format:
'json'
),
data:
{
no_turbolink:
true
},
class:
"btn btn-nr btn-grouped btn-close
#{
issue_button_visibility
(
@issue
,
true
)
}
"
,
title:
'Close issue'
=
link_to
edit_namespace_project_issue_path
(
@project
.
namespace
,
@project
,
@issue
),
class:
'btn btn-nr btn-grouped issuable-edit'
do
=
icon
(
'pencil-square-o'
)
Edit
-
if
can?
(
current_user
,
:create_issue
,
@project
)
||
can?
(
current_user
,
:update_issue
,
@issue
)
.issuable-actions
.clearfix.issue-btn-group.dropdown
%button
.btn.btn-default.pull-left.hidden-md.hidden-lg
{
data:
{
toggle:
"dropdown"
}
}
%span
.caret
Options
.dropdown-menu.dropdown-menu-align-right.hidden-lg
%ul
-
if
can?
(
current_user
,
:create_issue
,
@project
)
%li
=
link_to
'New issue'
,
new_namespace_project_issue_path
(
@project
.
namespace
,
@project
),
title:
'New issue'
,
id:
'new_issue_link'
-
if
can?
(
current_user
,
:update_issue
,
@issue
)
%li
=
link_to
'Reopen issue'
,
issue_path
(
@issue
,
issue:
{
state_event: :reopen
},
status_only:
true
,
format:
'json'
),
data:
{
no_turbolink:
true
},
class:
"btn-reopen
#{
issue_button_visibility
(
@issue
,
false
)
}
"
,
title:
'Reopen issue'
%li
=
link_to
'Close issue'
,
issue_path
(
@issue
,
issue:
{
state_event: :close
},
status_only:
true
,
format:
'json'
),
data:
{
no_turbolink:
true
},
class:
"btn-close
#{
issue_button_visibility
(
@issue
,
true
)
}
"
,
title:
'Close issue'
%li
=
link_to
'Edit'
,
edit_namespace_project_issue_path
(
@project
.
namespace
,
@project
,
@issue
)
-
if
can?
(
current_user
,
:create_issue
,
@project
)
=
link_to
new_namespace_project_issue_path
(
@project
.
namespace
,
@project
),
class:
'hidden-xs hidden-sm btn btn-nr btn-grouped new-issue-link btn-success'
,
title:
'New issue'
,
id:
'new_issue_link'
do
=
icon
(
'plus'
)
New issue
-
if
can?
(
current_user
,
:update_issue
,
@issue
)
=
link_to
'Reopen issue'
,
issue_path
(
@issue
,
issue:
{
state_event: :reopen
},
status_only:
true
,
format:
'json'
),
data:
{
no_turbolink:
true
},
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen
#{
issue_button_visibility
(
@issue
,
false
)
}
"
,
title:
'Reopen issue'
=
link_to
'Close issue'
,
issue_path
(
@issue
,
issue:
{
state_event: :close
},
status_only:
true
,
format:
'json'
),
data:
{
no_turbolink:
true
},
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped btn-close
#{
issue_button_visibility
(
@issue
,
true
)
}
"
,
title:
'Close issue'
=
link_to
edit_namespace_project_issue_path
(
@project
.
namespace
,
@project
,
@issue
),
class:
'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit'
do
=
icon
(
'pencil-square-o'
)
Edit
.issue-details.issuable-details
.detail-page-description.content-block
%h2
.title
=
markdown
escape_once
(
@issue
.
title
),
pipeline: :single_line
%div
-
if
@issue
.
description
.
present?
.description
{
class:
can?
(
current_user
,
:update_issue
,
@issue
)
?
'js-task-list-container'
:
''
}
.wiki
=
preserve
do
=
markdown
(
@issue
.
description
,
cache_key:
[
@issue
,
"description"
])
%textarea
.hidden.js-task-list-field
=
@issue
.
description
=
edited_time_ago_with_tooltip
(
@issue
,
placement:
'bottom'
,
html_class:
'issue_edited_ago'
)
.issue-details.issuable-details
.detail-page-description.content-block
%h2
.title
=
markdown
escape_once
(
@issue
.
title
),
pipeline: :single_line
-
if
@issue
.
description
.
present?
.description
{
class:
can?
(
current_user
,
:update_issue
,
@issue
)
?
'js-task-list-container'
:
''
}
.wiki
=
preserve
do
=
markdown
(
@issue
.
description
,
cache_key:
[
@issue
,
"description"
])
%textarea
.hidden.js-task-list-field
=
@issue
.
description
=
edited_time_ago_with_tooltip
(
@issue
,
placement:
'bottom'
,
html_class:
'issue_edited_ago'
)
#merge-requests
{
'data-url'
=>
referenced_merge_requests_namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
}
// This element is filled in using JavaScript.
#merge-requests
{
data:
{
url:
referenced_merge_requests_namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
}
}
// This element is filled in using JavaScript.
#related-branches
{
'data-url'
=>
related_branches_namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
}
// This element is filled in using JavaScript.
#related-branches
{
data:
{
url:
related_branches_namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
}
}
// This element is filled in using JavaScript.
.content-block.content-block-small
=
render
'new_branch'
=
render
'votes/votes_block'
,
votable:
@issue
.content-block.content-block-small
=
render
'new_branch'
=
render
'votes/votes_block'
,
votable:
@issue
.row
%section
.col-md-12
.issuable-discussion
=
render
'projects/issues/discussion'
%section
.issuable-discussion
=
render
'projects/issues/discussion'
=
render
'shared/issuable/sidebar'
,
issuable:
@issue
app/views/projects/merge_requests/_show.html.haml
View file @
39c2677e
-
page_title
"
#{
@merge_request
.
title
}
(
#{
@merge_request
.
to_reference
}
)"
,
"Merge Requests"
-
page_description
@merge_request
.
description
-
page_card_attributes
@merge_request
.
card_attributes
=
render
"header_title"
-
header_title
project_title
(
@project
,
"Merge Requests"
,
namespace_project_merge_requests_path
(
@project
.
namespace
,
@project
))
-
if
params
[
:view
]
==
'parallel'
-
fluid_layout
true
...
...
app/views/projects/merge_requests/show/_mr_title.html.haml
View file @
39c2677e
.detail-page-header
.status-box
{
class:
status_box_class
(
@merge_request
)
}
%span
.hidden-xs
=
@merge_request
.
state_human_name
%span
.hidden-sm.hidden-md.hidden-lg
=
icon
(
@merge_request
.
state_icon_name
)
%a
.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.js-sidebar-toggle
{
href:
"#"
}
=
icon
(
'angle-double-left'
)
.issue-meta
%strong
.identifier
%span
.hidden-sm.hidden-md.hidden-lg
MR
.clearfix.detail-page-header
.issuable-header
.issuable-status-box.status-box
{
class:
status_box_class
(
@merge_request
)
}
=
icon
(
@merge_request
.
state_icon_name
,
class:
"hidden-sm hidden-md hidden-lg"
)
%span
.hidden-xs
Merge Request
!
#{@merge_request.iid}
%span
.creator
opened
.editor-details
=
time_ago_with_tooltip
(
@merge_request
.
created_at
)
by
%strong
=
link_to_member
(
@project
,
@merge_request
.
author
,
size:
24
,
mobile_classes:
"hidden-xs"
)
%strong
=
link_to_member
(
@project
,
@merge_request
.
author
,
size:
24
,
mobile_classes:
"hidden-sm hidden-md hidden-lg"
,
by_username:
true
,
avatar:
false
)
=
@merge_request
.
state_human_name
.issue-btn-group.pull-right
-
if
can?
(
current_user
,
:update_merge_request
,
@merge_request
)
-
if
@merge_request
.
open?
=
link_to
'Close'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :close
}),
method: :put
,
class:
'btn btn-nr btn-grouped btn-close'
,
title:
'Close merge request'
=
link_to
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
'btn btn-nr btn-grouped issuable-edit'
,
id:
'edit_merge_request'
do
%a
.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle
{
href:
"#"
}
=
icon
(
'angle-double-left'
)
.issuable-meta
=
issuable_meta
(
@merge_request
,
@project
,
"Merge Request"
)
-
if
can?
(
current_user
,
:update_merge_request
,
@merge_request
)
.issuable-actions
.clearfix.issue-btn-group.dropdown
%button
.btn.btn-default.pull-left.hidden-md.hidden-lg
{
data:
{
toggle:
"dropdown"
}
}
%span
.caret
Options
.dropdown-menu.dropdown-menu-align-right.hidden-lg
%ul
%li
{
class:
issue_button_visibility
(
@merge_request
,
true
)
}
=
link_to
'Close'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :close
}),
method: :put
,
title:
'Close merge request'
%li
{
class:
issue_button_visibility
(
@merge_request
,
false
)
}
=
link_to
'Reopen'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :reopen
}),
method: :put
,
class:
'reopen-mr-link'
,
title:
'Reopen merge request'
%li
=
link_to
'Edit'
,
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
'issuable-edit'
,
id:
'edit_merge_request'
=
link_to
'Close'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :close
}),
method: :put
,
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped btn-close
#{
issue_button_visibility
(
@merge_request
,
true
)
}
"
,
title:
'Close merge request'
=
link_to
'Reopen'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :reopen
}),
method: :put
,
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen reopen-mr-link
#{
issue_button_visibility
(
@merge_request
,
false
)
}
"
,
title:
'Reopen merge request'
=
link_to
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit"
,
id:
'edit_merge_request'
do
=
icon
(
'pencil-square-o'
)
Edit
-
if
@merge_request
.
closed?
=
link_to
'Reopen'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :reopen
}),
method: :put
,
class:
'btn btn-nr btn-grouped btn-reopen reopen-mr-link'
,
title:
'Reopen merge request'
app/views/projects/notes/_note.html.haml
View file @
39c2677e
%li
.timeline-entry
{
id:
dom_id
(
note
),
class:
[
dom_class
(
note
),
"note-row-#{note.id}"
,
(
'system-note'
if
note
.
system
)]
}
-
note_editable
=
note_editable?
(
note
)
%li
.timeline-entry
{
id:
dom_id
(
note
),
class:
[
dom_class
(
note
),
"note-row-#{note.id}"
,
(
'system-note'
if
note
.
system
)],
data:
{
author_id:
note
.
author
.
id
,
editable:
note_editable
}
}
.timeline-entry-inner
.timeline-icon
%a
{
href:
user_path
(
note
.
author
)}
...
...
@@ -15,16 +16,16 @@
-
if
access
%span
.note-role
=
access
-
if
note_editable
?
(
note
)
-
if
note_editable
=
link_to
'#'
,
title:
'Edit comment'
,
class:
'note-action-button js-note-edit'
do
=
icon
(
'pencil'
)
=
link_to
namespace_project_note_path
(
note
.
project
.
namespace
,
note
.
project
,
note
),
title:
'Remove comment'
,
method: :delete
,
data:
{
confirm:
'Are you sure you want to remove this comment?'
},
remote:
true
,
class:
'note-action-button js-note-delete danger'
do
=
icon
(
'trash-o'
)
.note-body
{
class:
note_editable
?
(
note
)
?
'js-task-list-container'
:
''
}
.note-body
{
class:
note_editable
?
'js-task-list-container'
:
''
}
.note-text
=
preserve
do
=
markdown
(
note
.
note
,
pipeline: :note
,
cache_key:
[
note
,
"note"
])
-
if
note_editable
?
(
note
)
-
if
note_editable
=
render
'projects/notes/edit_form'
,
note:
note
=
edited_time_ago_with_tooltip
(
note
,
placement:
'bottom'
,
html_class:
'note_edited_ago'
,
include_author:
true
)
...
...
app/views/shared/issuable/_form.html.haml
View file @
39c2677e
...
...
@@ -4,7 +4,7 @@
=
f
.
label
:title
,
class:
'control-label'
.col-sm-10
=
f
.
text_field
:title
,
maxlength:
255
,
autofocus:
true
,
autocomplete:
'off'
,
class:
'form-control pad
js-gfm-input
'
,
required:
true
class:
'form-control pad'
,
required:
true
-
if
issuable
.
is_a?
(
MergeRequest
)
%p
.help-block
...
...
doc/ci/quick_start/README.md
View file @
39c2677e
...
...
@@ -13,7 +13,7 @@ GitLab offers a [continuous integration][ci] service. If you
and configure your GitLab project to use a [Runner], then each merge request or
push triggers a build.
The
`.gitlab-ci.yml`
file tells the GitLab runner what
do t
o. By default it
The
`.gitlab-ci.yml`
file tells the GitLab runner what
to d
o. By default it
runs three [stages]:
`build`
,
`test`
, and
`deploy`
.
If everything runs OK (no non-zero return values), you'll get a nice green
...
...
features/steps/shared/issuable.rb
View file @
39c2677e
...
...
@@ -2,7 +2,7 @@ module SharedIssuable
include
Spinach
::
DSL
def
edit_issuable
find
(
:css
,
'.issuable-edit'
).
click
find
(
'.issuable-edit'
,
visible:
true
).
click
end
step
'project "Community" has "Community issue" open issue'
do
...
...
lib/gitlab/metrics/instrumentation.rb
View file @
39c2677e
...
...
@@ -11,6 +11,8 @@ module Gitlab
module
Instrumentation
SERIES
=
'method_calls'
PROXY_IVAR
=
:@__gitlab_instrumentation_proxy
def
self
.
configure
yield
self
end
...
...
@@ -91,6 +93,18 @@ module Gitlab
end
end
# Returns true if a module is instrumented.
#
# mod - The module to check
def
self
.
instrumented?
(
mod
)
mod
.
instance_variable_defined?
(
PROXY_IVAR
)
end
# Returns the proxy module (if any) of `mod`.
def
self
.
proxy_module
(
mod
)
mod
.
instance_variable_get
(
PROXY_IVAR
)
end
# Instruments a method.
#
# type - The type (:class or :instance) of method to instrument.
...
...
@@ -99,9 +113,8 @@ module Gitlab
def
self
.
instrument
(
type
,
mod
,
name
)
return
unless
Metrics
.
enabled?
name
=
name
.
to_sym
alias_name
=
:"_original_
#{
name
}
"
target
=
type
==
:instance
?
mod
:
mod
.
singleton_class
name
=
name
.
to_sym
target
=
type
==
:instance
?
mod
:
mod
.
singleton_class
if
type
==
:instance
target
=
mod
...
...
@@ -113,6 +126,12 @@ module Gitlab
method
=
mod
.
method
(
name
)
end
unless
instrumented?
(
target
)
target
.
instance_variable_set
(
PROXY_IVAR
,
Module
.
new
)
end
proxy_module
=
self
.
proxy_module
(
target
)
# Some code out there (e.g. the "state_machine" Gem) checks the arity of
# a method to make sure it only passes arguments when the method expects
# any. If we were to always overwrite a method to take an `*args`
...
...
@@ -125,17 +144,13 @@ module Gitlab
args_signature
=
'*args, &block'
end
send_signature
=
"__send__(
#{
alias_name
.
inspect
}
,
#{
args_signature
}
)"
target
.
class_eval
<<-
EOF
,
__FILE__
,
__LINE__
+
1
alias_method
#{
alias_name
.
inspect
}
,
#{
name
.
inspect
}
proxy_module
.
class_eval
<<-
EOF
,
__FILE__
,
__LINE__
+
1
def
#{
name
}
(
#{
args_signature
}
)
trans = Gitlab::Metrics::Instrumentation.transaction
if trans
start = Time.now
retval =
#{
send_signature
}
retval =
super
duration = (Time.now - start) * 1000.0
if duration >= Gitlab::Metrics.method_call_threshold
...
...
@@ -148,10 +163,12 @@ module Gitlab
retval
else
#{
send_signature
}
super
end
end
EOF
target
.
prepend
(
proxy_module
)
end
# Small layer of indirection to make it easier to stub out the current
...
...
spec/features/issues/move_spec.rb
View file @
39c2677e
...
...
@@ -42,11 +42,9 @@ feature 'issue move to another project' do
expect
(
current_url
).
to
include
project_path
(
new_project
)
page
.
within
(
'.issue'
)
do
expect
(
page
).
to
have_content
(
"Text with
#{
cross_reference
}
!1"
)
expect
(
page
).
to
have_content
(
"Moved from
#{
cross_reference
}
#1"
)
expect
(
page
).
to
have_content
(
issue
.
title
)
end
expect
(
page
).
to
have_content
(
"Text with
#{
cross_reference
}
!1"
)
expect
(
page
).
to
have_content
(
"Moved from
#{
cross_reference
}
#1"
)
expect
(
page
).
to
have_content
(
issue
.
title
)
end
context
'projects user does not have permission to move issue to exist'
do
...
...
@@ -74,7 +72,7 @@ feature 'issue move to another project' do
def
edit_issue
(
issue
)
visit
issue_path
(
issue
)
page
.
within
(
'.issuable-
header'
)
{
click_link
'Edit'
}
page
.
within
(
'.issuable-
actions'
)
{
first
(
:link
,
'Edit'
).
click
}
end
def
issue_path
(
issue
)
...
...
spec/features/issues_spec.rb
View file @
39c2677e
...
...
@@ -292,6 +292,23 @@ describe 'Issues', feature: true do
end
end
describe
'new issue'
do
context
'dropzone upload file'
,
js:
true
do
before
do
visit
new_namespace_project_issue_path
(
project
.
namespace
,
project
)
end
it
'should upload file when dragging into textarea'
do
drop_in_dropzone
test_image_file
# Wait for the file to upload
sleep
1
expect
(
page
.
find_field
(
"issue_description"
).
value
).
to
have_content
'banana_sample'
end
end
end
def
first_issue
page
.
all
(
'ul.issues-list > li'
).
first
.
text
end
...
...
@@ -299,4 +316,25 @@ describe 'Issues', feature: true do
def
last_issue
page
.
all
(
'ul.issues-list > li'
).
last
.
text
end
def
drop_in_dropzone
(
file_path
)
# Generate a fake input selector
page
.
execute_script
<<-
JS
var fakeFileInput = window.$('<input/>').attr(
{id: 'fakeFileInput', type: 'file'}
).appendTo('body');
JS
# Attach the file to the fake input selector with Capybara
attach_file
(
"fakeFileInput"
,
file_path
)
# Add the file to a fileList array and trigger the fake drop event
page
.
execute_script
<<-
JS
var fileList = [$('#fakeFileInput')[0].files[0]];
var e = jQuery.Event('drop', { dataTransfer : { files : fileList } });
$('.div-dropzone')[0].dropzone.listeners[0].events.drop(e);
JS
end
def
test_image_file
File
.
join
(
Rails
.
root
,
'spec'
,
'fixtures'
,
'banana_sample.gif'
)
end
end
spec/features/participants_autocomplete_spec.rb
0 → 100644
View file @
39c2677e
require
'spec_helper'
feature
'Member autocomplete'
,
feature:
true
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:participant
)
{
create
(
:user
)
}
let
(
:author
)
{
create
(
:user
)
}
before
do
allow_any_instance_of
(
Commit
).
to
receive
(
:author
).
and_return
(
author
)
login_as
user
end
shared_examples
"open suggestions"
do
it
'suggestions are displayed'
do
expect
(
page
).
to
have_selector
(
'.atwho-view'
,
visible:
true
)
end
it
'author is suggested'
do
page
.
within
(
'.atwho-view'
,
visible:
true
)
do
expect
(
page
).
to
have_content
(
author
.
username
)
end
end
it
'participant is suggested'
do
page
.
within
(
'.atwho-view'
,
visible:
true
)
do
expect
(
page
).
to
have_content
(
participant
.
username
)
end
end
end
context
'adding a new note on a Issue'
,
js:
true
do
before
do
issue
=
create
(
:issue
,
author:
author
,
project:
project
)
create
(
:note
,
note:
'Ultralight Beam'
,
noteable:
issue
,
author:
participant
)
visit_issue
(
project
,
issue
)
end
context
'when typing @'
do
include_examples
"open suggestions"
before
do
open_member_suggestions
end
end
end
context
'adding a new note on a Merge Request '
,
js:
true
do
before
do
merge
=
create
(
:merge_request
,
source_project:
project
,
target_project:
project
,
author:
author
)
create
(
:note
,
note:
'Ultralight Beam'
,
noteable:
merge
,
author:
participant
)
visit_merge_request
(
project
,
merge
)
end
context
'when typing @'
do
include_examples
"open suggestions"
before
do
open_member_suggestions
end
end
end
context
'adding a new note on a Commit '
,
js:
true
do
let
(
:commit
)
{
project
.
commit
}
before
do
allow
(
commit
).
to
receive
(
:author
).
and_return
(
author
)
create
(
:note_on_commit
,
author:
participant
,
project:
project
,
commit_id:
project
.
repository
.
commit
.
id
,
note:
'No More Parties in LA'
)
visit_commit
(
project
,
commit
)
end
context
'when typing @'
do
include_examples
"open suggestions"
before
do
open_member_suggestions
end
end
end
def
open_member_suggestions
sleep
1
page
.
within
(
'.new-note'
)
do
sleep
1
find
(
'#note_note'
).
native
.
send_keys
(
'@'
)
end
end
def
visit_issue
(
project
,
issue
)
visit
namespace_project_issue_path
(
project
.
namespace
,
project
,
issue
)
end
def
visit_merge_request
(
project
,
merge
)
visit
namespace_project_merge_request_path
(
project
.
namespace
,
project
,
merge
)
end
def
visit_commit
(
project
,
commit
)
visit
namespace_project_commit_path
(
project
.
namespace
,
project
,
commit
)
end
end
spec/features/signup_spec.rb
0 → 100644
View file @
39c2677e
require
'spec_helper'
feature
'Signup'
,
feature:
true
do
describe
'signup with no errors'
do
it
'creates the user account and sends a confirmation email'
do
user
=
build
(
:user
)
visit
root_path
fill_in
'user_name'
,
with:
user
.
name
fill_in
'user_username'
,
with:
user
.
username
fill_in
'user_email'
,
with:
user
.
email
fill_in
'user_password_sign_up'
,
with:
user
.
password
click_button
"Sign up"
expect
(
current_path
).
to
eq
user_session_path
expect
(
page
).
to
have_content
(
"A message with a confirmation link has been sent to your email address."
)
end
end
describe
'signup with errors'
do
it
"displays the errors"
do
existing_user
=
create
(
:user
)
user
=
build
(
:user
)
visit
root_path
fill_in
'user_name'
,
with:
user
.
name
fill_in
'user_username'
,
with:
user
.
username
fill_in
'user_email'
,
with:
existing_user
.
email
fill_in
'user_password_sign_up'
,
with:
user
.
password
click_button
"Sign up"
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
(
"Email has already been taken"
)
end
it
'does not redisplay the password'
do
existing_user
=
create
(
:user
)
user
=
build
(
:user
)
visit
root_path
fill_in
'user_name'
,
with:
user
.
name
fill_in
'user_username'
,
with:
user
.
username
fill_in
'user_email'
,
with:
existing_user
.
email
fill_in
'user_password_sign_up'
,
with:
user
.
password
click_button
"Sign up"
expect
(
current_path
).
to
eq
user_registration_path
expect
(
page
.
body
).
not_to
match
(
/
#{
user
.
password
}
/
)
end
end
end
spec/lib/gitlab/metrics/instrumentation_spec.rb
View file @
39c2677e
...
...
@@ -33,8 +33,16 @@ describe Gitlab::Metrics::Instrumentation do
described_class
.
instrument_method
(
@dummy
,
:foo
)
end
it
'renames the original method'
do
expect
(
@dummy
).
to
respond_to
(
:_original_foo
)
it
'instruments the Class'
do
target
=
@dummy
.
singleton_class
expect
(
described_class
.
instrumented?
(
target
)).
to
eq
(
true
)
end
it
'defines a proxy method'
do
mod
=
described_class
.
proxy_module
(
@dummy
.
singleton_class
)
expect
(
mod
.
method_defined?
(
:foo
)).
to
eq
(
true
)
end
it
'calls the instrumented method with the correct arguments'
do
...
...
@@ -76,6 +84,14 @@ describe Gitlab::Metrics::Instrumentation do
expect
(
dummy
.
method
(
:test
).
arity
).
to
eq
(
0
)
end
describe
'when a module is instrumented multiple times'
do
it
'calls the instrumented method with the correct arguments'
do
described_class
.
instrument_method
(
@dummy
,
:foo
)
expect
(
@dummy
.
foo
).
to
eq
(
'foo'
)
end
end
end
describe
'with metrics disabled'
do
...
...
@@ -86,7 +102,9 @@ describe Gitlab::Metrics::Instrumentation do
it
'does not instrument the method'
do
described_class
.
instrument_method
(
@dummy
,
:foo
)
expect
(
@dummy
).
to_not
respond_to
(
:_original_foo
)
target
=
@dummy
.
singleton_class
expect
(
described_class
.
instrumented?
(
target
)).
to
eq
(
false
)
end
end
end
...
...
@@ -100,8 +118,14 @@ describe Gitlab::Metrics::Instrumentation do
instrument_instance_method
(
@dummy
,
:bar
)
end
it
'renames the original method'
do
expect
(
@dummy
.
method_defined?
(
:_original_bar
)).
to
eq
(
true
)
it
'instruments instances of the Class'
do
expect
(
described_class
.
instrumented?
(
@dummy
)).
to
eq
(
true
)
end
it
'defines a proxy method'
do
mod
=
described_class
.
proxy_module
(
@dummy
)
expect
(
mod
.
method_defined?
(
:bar
)).
to
eq
(
true
)
end
it
'calls the instrumented method with the correct arguments'
do
...
...
@@ -144,7 +168,7 @@ describe Gitlab::Metrics::Instrumentation do
described_class
.
instrument_instance_method
(
@dummy
,
:bar
)
expect
(
@dummy
.
method_defined?
(
:_original_bar
)).
to
eq
(
false
)
expect
(
described_class
.
instrumented?
(
@dummy
)).
to
eq
(
false
)
end
end
end
...
...
@@ -167,18 +191,17 @@ describe Gitlab::Metrics::Instrumentation do
it
'recursively instruments a class hierarchy'
do
described_class
.
instrument_class_hierarchy
(
@dummy
)
expect
(
@child1
).
to
respond_to
(
:_original_child1_foo
)
expect
(
@child2
).
to
respond_to
(
:_original_child2_foo
)
expect
(
described_class
.
instrumented?
(
@child1
.
singleton_class
)).
to
eq
(
true
)
expect
(
described_class
.
instrumented?
(
@child2
.
singleton_class
)).
to
eq
(
true
)
expect
(
@child1
.
method_defined?
(
:_original_child1_bar
)).
to
eq
(
true
)
expect
(
@child2
.
method_defined?
(
:_original_child2_bar
)).
to
eq
(
true
)
expect
(
described_class
.
instrumented?
(
@child1
)).
to
eq
(
true
)
expect
(
described_class
.
instrumented?
(
@child2
)).
to
eq
(
true
)
end
it
'does not instrument the root module'
do
described_class
.
instrument_class_hierarchy
(
@dummy
)
expect
(
@dummy
).
to_not
respond_to
(
:_original_foo
)
expect
(
@dummy
.
method_defined?
(
:_original_bar
)).
to
eq
(
false
)
expect
(
described_class
.
instrumented?
(
@dummy
)).
to
eq
(
false
)
end
end
...
...
@@ -190,7 +213,7 @@ describe Gitlab::Metrics::Instrumentation do
it
'instruments all public class methods'
do
described_class
.
instrument_methods
(
@dummy
)
expect
(
@dummy
).
to
respond_to
(
:_original_foo
)
expect
(
described_class
.
instrumented?
(
@dummy
.
singleton_class
)).
to
eq
(
true
)
end
it
'only instruments methods directly defined in the module'
do
...
...
@@ -223,7 +246,7 @@ describe Gitlab::Metrics::Instrumentation do
it
'instruments all public instance methods'
do
described_class
.
instrument_instance_methods
(
@dummy
)
expect
(
@dummy
.
method_defined?
(
:_original_bar
)).
to
eq
(
true
)
expect
(
described_class
.
instrumented?
(
@dummy
)).
to
eq
(
true
)
end
it
'only instruments methods directly defined in the module'
do
...
...
spec/models/external_issue_spec.rb
View file @
39c2677e
...
...
@@ -36,4 +36,19 @@ describe ExternalIssue, models: true do
expect
(
issue
.
title
).
to
eq
"External Issue
#{
issue
}
"
end
end
describe
'#reference_link_text'
do
context
'if issue id has a prefix'
do
it
'returns the issue ID'
do
expect
(
issue
.
reference_link_text
).
to
eq
'EXT-1234'
end
end
context
'if issue id is a number'
do
let
(
:issue
)
{
described_class
.
new
(
'1234'
,
project
)
}
it
'returns the issue ID prefixed by #'
do
expect
(
issue
.
reference_link_text
).
to
eq
'#1234'
end
end
end
end
spec/models/repository_spec.rb
View file @
39c2677e
...
...
@@ -910,9 +910,32 @@ describe Repository, models: true do
end
end
describe
'.clean_old_archives'
do
let
(
:path
)
{
Gitlab
.
config
.
gitlab
.
repository_downloads_path
}
context
'when the downloads directory does not exist'
do
it
'does not remove any archives'
do
expect
(
File
).
to
receive
(
:directory?
).
with
(
path
).
and_return
(
false
)
expect
(
Gitlab
::
Popen
).
not_to
receive
(
:popen
)
described_class
.
clean_old_archives
end
end
context
'when the downloads directory exists'
do
it
'removes old archives'
do
expect
(
File
).
to
receive
(
:directory?
).
with
(
path
).
and_return
(
true
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen
)
described_class
.
clean_old_archives
end
end
end
def
create_remote_branch
(
remote_name
,
branch_name
,
target
)
rugged
=
repository
.
rugged
rugged
.
references
.
create
(
"refs/remotes/
#{
remote_name
}
/
#{
branch_name
}
"
,
target
)
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment