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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
5fd0d376
Commit
5fd0d376
authored
May 04, 2017
by
Adam Niedzielski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
EE port of "Display slash commands outcome when previewing Markdown"
parent
5c29411e
Changes
28
Show whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
804 additions
and
136 deletions
+804
-136
app/assets/javascripts/preview_markdown.js
app/assets/javascripts/preview_markdown.js
+40
-8
app/controllers/concerns/markdown_preview.rb
app/controllers/concerns/markdown_preview.rb
+0
-19
app/controllers/projects/wikis_controller.rb
app/controllers/projects/wikis_controller.rb
+8
-5
app/controllers/projects_controller.rb
app/controllers/projects_controller.rb
+9
-2
app/controllers/snippets_controller.rb
app/controllers/snippets_controller.rb
+8
-2
app/helpers/gitlab_routing_helper.rb
app/helpers/gitlab_routing_helper.rb
+4
-0
app/services/preview_markdown_service.rb
app/services/preview_markdown_service.rb
+45
-0
app/services/slash_commands/interpret_service.rb
app/services/slash_commands/interpret_service.rb
+174
-49
app/views/groups/milestones/new.html.haml
app/views/groups/milestones/new.html.haml
+1
-1
app/views/layouts/project.html.haml
app/views/layouts/project.html.haml
+0
-5
app/views/projects/_md_preview.html.haml
app/views/projects/_md_preview.html.haml
+5
-2
app/views/projects/milestones/_form.html.haml
app/views/projects/milestones/_form.html.haml
+1
-1
app/views/projects/notes/_edit_form.html.haml
app/views/projects/notes/_edit_form.html.haml
+1
-1
app/views/projects/notes/_form.html.haml
app/views/projects/notes/_form.html.haml
+5
-1
app/views/projects/notes/_notes_with_form.html.haml
app/views/projects/notes/_notes_with_form.html.haml
+1
-1
app/views/projects/releases/edit.html.haml
app/views/projects/releases/edit.html.haml
+1
-1
app/views/projects/tags/new.html.haml
app/views/projects/tags/new.html.haml
+1
-1
app/views/projects/wikis/_form.html.haml
app/views/projects/wikis/_form.html.haml
+1
-1
app/views/shared/issuable/_form.html.haml
app/views/shared/issuable/_form.html.haml
+1
-1
app/views/shared/issuable/form/_description.html.haml
app/views/shared/issuable/form/_description.html.haml
+10
-3
changelogs/unreleased/preview-separate-slash-commands.yml
changelogs/unreleased/preview-separate-slash-commands.yml
+4
-0
lib/gitlab/slash_commands/command_definition.rb
lib/gitlab/slash_commands/command_definition.rb
+36
-10
lib/gitlab/slash_commands/dsl.rb
lib/gitlab/slash_commands/dsl.rb
+47
-5
spec/lib/gitlab/slash_commands/command_definition_spec.rb
spec/lib/gitlab/slash_commands/command_definition_spec.rb
+52
-0
spec/lib/gitlab/slash_commands/dsl_spec.rb
spec/lib/gitlab/slash_commands/dsl_spec.rb
+49
-17
spec/services/preview_markdown_service_spec.rb
spec/services/preview_markdown_service_spec.rb
+67
-0
spec/services/slash_commands/interpret_service_spec.rb
spec/services/slash_commands/interpret_service_spec.rb
+218
-0
spec/support/features/issuable_slash_commands_shared_examples.rb
...pport/features/issuable_slash_commands_shared_examples.rb
+15
-0
No files found.
app/assets/javascripts/preview_markdown.js
View file @
5fd0d376
...
...
@@ -2,8 +2,9 @@
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
// and showing a warning when more than `x` users are referenced.
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview
// (including the explanation of slash commands), and showing a warning when
// more than `x` users are referenced.
//
(
function
()
{
var
lastTextareaPreviewed
;
...
...
@@ -17,32 +18,45 @@
// Minimum number of users referenced before triggering a warning
MarkdownPreview
.
prototype
.
referenceThreshold
=
10
;
MarkdownPreview
.
prototype
.
emptyMessage
=
'
Nothing to preview.
'
;
MarkdownPreview
.
prototype
.
ajaxCache
=
{};
MarkdownPreview
.
prototype
.
showPreview
=
function
(
$form
)
{
var
mdText
;
var
preview
=
$form
.
find
(
'
.js-md-preview
'
);
var
url
=
preview
.
data
(
'
url
'
);
if
(
preview
.
hasClass
(
'
md-preview-loading
'
))
{
return
;
}
mdText
=
$form
.
find
(
'
textarea.markdown-area
'
).
val
();
if
(
mdText
.
trim
().
length
===
0
)
{
preview
.
text
(
'
Nothing to preview.
'
);
preview
.
text
(
this
.
emptyMessage
);
this
.
hideReferencedUsers
(
$form
);
}
else
{
preview
.
addClass
(
'
md-preview-loading
'
).
text
(
'
Loading...
'
);
this
.
fetchMarkdownPreview
(
mdText
,
(
function
(
response
)
{
preview
.
removeClass
(
'
md-preview-loading
'
).
html
(
response
.
body
);
this
.
fetchMarkdownPreview
(
mdText
,
url
,
(
function
(
response
)
{
var
body
;
if
(
response
.
body
.
length
>
0
)
{
body
=
response
.
body
;
}
else
{
body
=
this
.
emptyMessage
;
}
preview
.
removeClass
(
'
md-preview-loading
'
).
html
(
body
);
preview
.
renderGFM
();
this
.
renderReferencedUsers
(
response
.
references
.
users
,
$form
);
if
(
response
.
references
.
commands
)
{
this
.
renderReferencedCommands
(
response
.
references
.
commands
,
$form
);
}
}).
bind
(
this
));
}
};
MarkdownPreview
.
prototype
.
fetchMarkdownPreview
=
function
(
text
,
success
)
{
if
(
!
window
.
preview_markdown_path
)
{
MarkdownPreview
.
prototype
.
fetchMarkdownPreview
=
function
(
text
,
url
,
success
)
{
if
(
!
url
)
{
return
;
}
if
(
text
===
this
.
ajaxCache
.
text
)
{
...
...
@@ -51,7 +65,7 @@
}
$
.
ajax
({
type
:
'
POST
'
,
url
:
window
.
preview_markdown_path
,
url
:
url
,
data
:
{
text
:
text
},
...
...
@@ -83,6 +97,22 @@
}
};
MarkdownPreview
.
prototype
.
hideReferencedCommands
=
function
(
$form
)
{
$form
.
find
(
'
.referenced-commands
'
).
hide
();
};
MarkdownPreview
.
prototype
.
renderReferencedCommands
=
function
(
commands
,
$form
)
{
var
referencedCommands
;
referencedCommands
=
$form
.
find
(
'
.referenced-commands
'
);
if
(
commands
.
length
>
0
)
{
referencedCommands
.
html
(
commands
);
referencedCommands
.
show
();
}
else
{
referencedCommands
.
html
(
''
);
referencedCommands
.
hide
();
}
};
return
MarkdownPreview
;
}());
...
...
@@ -137,6 +167,8 @@
$form
.
find
(
'
.md-write-holder
'
).
show
();
$form
.
find
(
'
textarea.markdown-area
'
).
focus
();
$form
.
find
(
'
.md-preview-holder
'
).
hide
();
markdownPreview
.
hideReferencedCommands
(
$form
);
});
$
(
document
).
on
(
'
markdown-preview:toggle
'
,
function
(
e
,
keyboardEvent
)
{
...
...
app/controllers/concerns/markdown_preview.rb
deleted
100644 → 0
View file @
5c29411e
module
MarkdownPreview
private
def
render_markdown_preview
(
text
,
markdown_context
=
{})
render
json:
{
body:
view_context
.
markdown
(
text
,
markdown_context
),
references:
{
users:
preview_referenced_users
(
text
)
}
}
end
def
preview_referenced_users
(
text
)
extractor
=
Gitlab
::
ReferenceExtractor
.
new
(
@project
,
current_user
)
extractor
.
analyze
(
text
,
author:
current_user
)
extractor
.
users
.
map
(
&
:username
)
end
end
app/controllers/projects/wikis_controller.rb
View file @
5fd0d376
class
Projects::WikisController
<
Projects
::
ApplicationController
include
MarkdownPreview
before_action
:authorize_read_wiki!
before_action
:authorize_create_wiki!
,
only:
[
:edit
,
:create
,
:history
]
before_action
:authorize_admin_wiki!
,
only: :destroy
...
...
@@ -103,9 +101,14 @@ class Projects::WikisController < Projects::ApplicationController
end
def
preview_markdown
context
=
{
pipeline: :wiki
,
project_wiki:
@project_wiki
,
page_slug:
params
[
:id
]
}
render_markdown_preview
(
params
[
:text
],
context
)
result
=
PreviewMarkdownService
.
new
(
@project
,
current_user
,
params
).
execute
render
json:
{
body:
view_context
.
markdown
(
result
[
:text
],
pipeline: :wiki
,
project_wiki:
@project_wiki
,
page_slug:
params
[
:id
]),
references:
{
users:
result
[
:users
]
}
}
end
private
...
...
app/controllers/projects_controller.rb
View file @
5fd0d376
class
ProjectsController
<
Projects
::
ApplicationController
include
IssuableCollections
include
ExtractsPath
include
MarkdownPreview
before_action
:authenticate_user!
,
except:
[
:index
,
:show
,
:activity
,
:refs
]
before_action
:project
,
except:
[
:index
,
:new
,
:create
]
...
...
@@ -241,7 +240,15 @@ class ProjectsController < Projects::ApplicationController
end
def
preview_markdown
render_markdown_preview
(
params
[
:text
])
result
=
PreviewMarkdownService
.
new
(
@project
,
current_user
,
params
).
execute
render
json:
{
body:
view_context
.
markdown
(
result
[
:text
]),
references:
{
users:
result
[
:users
],
commands:
view_context
.
markdown
(
result
[
:commands
])
}
}
end
private
...
...
app/controllers/snippets_controller.rb
View file @
5fd0d376
...
...
@@ -3,7 +3,6 @@ class SnippetsController < ApplicationController
include
ToggleAwardEmoji
include
SpammableActions
include
SnippetsActions
include
MarkdownPreview
include
RendersBlob
before_action
:snippet
,
only:
[
:show
,
:edit
,
:destroy
,
:update
,
:raw
,
:download
]
...
...
@@ -98,7 +97,14 @@ class SnippetsController < ApplicationController
end
def
preview_markdown
render_markdown_preview
(
params
[
:text
],
skip_project_check:
true
)
result
=
PreviewMarkdownService
.
new
(
@project
,
current_user
,
params
).
execute
render
json:
{
body:
view_context
.
markdown
(
result
[
:text
],
skip_project_check:
true
),
references:
{
users:
result
[
:users
]
}
}
end
protected
...
...
app/helpers/gitlab_routing_helper.rb
View file @
5fd0d376
...
...
@@ -122,6 +122,10 @@ module GitlabRoutingHelper
namespace_project_snippet_url
(
entity
.
project
.
namespace
,
entity
.
project
,
entity
,
*
args
)
end
def
preview_markdown_path
(
project
,
*
args
)
preview_markdown_namespace_project_path
(
project
.
namespace
,
project
,
*
args
)
end
def
toggle_subscription_path
(
entity
,
*
args
)
if
entity
.
is_a?
(
Issue
)
toggle_subscription_namespace_project_issue_path
(
entity
.
project
.
namespace
,
entity
.
project
,
entity
)
...
...
app/services/preview_markdown_service.rb
0 → 100644
View file @
5fd0d376
class
PreviewMarkdownService
<
BaseService
def
execute
text
,
commands
=
explain_slash_commands
(
params
[
:text
])
users
=
find_user_references
(
text
)
success
(
text:
text
,
users:
users
,
commands:
commands
.
join
(
' '
)
)
end
private
def
explain_slash_commands
(
text
)
return
text
,
[]
unless
%w(Issue MergeRequest)
.
include?
(
commands_target_type
)
slash_commands_service
=
SlashCommands
::
InterpretService
.
new
(
project
,
current_user
)
slash_commands_service
.
explain
(
text
,
find_commands_target
)
end
def
find_user_references
(
text
)
extractor
=
Gitlab
::
ReferenceExtractor
.
new
(
project
,
current_user
)
extractor
.
analyze
(
text
,
author:
current_user
)
extractor
.
users
.
map
(
&
:username
)
end
def
find_commands_target
if
commands_target_id
.
present?
finder
=
commands_target_type
==
'Issue'
?
IssuesFinder
:
MergeRequestsFinder
finder
.
new
(
current_user
,
project_id:
project
.
id
).
find
(
commands_target_id
)
else
collection
=
commands_target_type
==
'Issue'
?
project
.
issues
:
project
.
merge_requests
collection
.
build
end
end
def
commands_target_type
params
[
:slash_commands_target_type
]
end
def
commands_target_id
params
[
:slash_commands_target_id
]
end
end
app/services/slash_commands/interpret_service.rb
View file @
5fd0d376
...
...
@@ -2,7 +2,7 @@ module SlashCommands
class
InterpretService
<
BaseService
include
Gitlab
::
SlashCommands
::
Dsl
attr_reader
:issuable
,
:options
attr_reader
:issuable
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
...
...
@@ -12,23 +12,21 @@ module SlashCommands
@issuable
=
issuable
@updates
=
{}
opts
=
{
issuable:
issuable
,
current_user:
current_user
,
project:
project
,
params:
params
}
content
,
commands
=
extractor
.
extract_commands
(
content
,
opts
)
content
,
commands
=
extractor
.
extract_commands
(
content
,
context
)
extract_updates
(
commands
,
context
)
[
content
,
@updates
]
end
commands
.
each
do
|
name
,
arg
|
definition
=
self
.
class
.
command_definitions_by_name
[
name
.
to_sym
]
next
unless
definition
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and array of changes explained.
def
explain
(
content
,
issuable
)
return
[
content
,
[]]
unless
current_user
.
can?
(
:use_slash_commands
)
definition
.
execute
(
self
,
opts
,
arg
)
end
@issuable
=
issuable
[
content
,
@updates
]
content
,
commands
=
extractor
.
extract_commands
(
content
,
context
)
commands
=
explain_commands
(
commands
,
context
)
[
content
,
commands
]
end
private
...
...
@@ -40,6 +38,9 @@ module SlashCommands
desc
do
"Close this
#{
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
}
"
end
explanation
do
"Closes this
#{
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
}
."
end
condition
do
issuable
.
persisted?
&&
issuable
.
open?
&&
...
...
@@ -52,6 +53,9 @@ module SlashCommands
desc
do
"Reopen this
#{
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
}
"
end
explanation
do
"Reopens this
#{
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
}
."
end
condition
do
issuable
.
persisted?
&&
issuable
.
closed?
&&
...
...
@@ -62,6 +66,7 @@ module SlashCommands
end
desc
'Merge (when the pipeline succeeds)'
explanation
'Merges this merge request when the pipeline succeeds.'
condition
do
last_diff_sha
=
params
&&
params
[
:merge_request_diff_head_sha
]
issuable
.
is_a?
(
MergeRequest
)
&&
...
...
@@ -73,6 +78,9 @@ module SlashCommands
end
desc
'Change title'
explanation
do
|
title_param
|
"Changes the title to
\"
#{
title_param
}
\"
."
end
params
'<New title>'
condition
do
issuable
.
persisted?
&&
...
...
@@ -83,18 +91,25 @@ module SlashCommands
end
desc
'Assign'
explanation
do
|
user
|
"Assigns
#{
user
.
to_reference
}
."
if
user
end
params
'@user'
condition
do
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
project
)
end
command
:assign
do
|
assignee_param
|
user
=
extract_references
(
assignee_param
,
:user
).
first
user
||=
User
.
find_by
(
username:
assignee_param
)
parse_params
do
|
assignee_param
|
extract_references
(
assignee_param
,
:user
).
first
||
User
.
find_by
(
username:
assignee_param
)
end
command
:assign
do
|
user
|
@updates
[
:assignee_id
]
=
user
.
id
if
user
end
desc
'Remove assignee'
explanation
do
"Removes assignee
#{
issuable
.
assignee
.
to_reference
}
."
end
condition
do
issuable
.
persisted?
&&
issuable
.
assignee_id?
&&
...
...
@@ -105,19 +120,26 @@ module SlashCommands
end
desc
'Set milestone'
explanation
do
|
milestone
|
"Sets the milestone to
#{
milestone
.
to_reference
}
."
if
milestone
end
params
'%"milestone"'
condition
do
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
project
)
&&
project
.
milestones
.
active
.
any?
end
command
:milestone
do
|
milestone_param
|
milestone
=
extract_references
(
milestone_param
,
:milestone
).
first
milestone
||=
project
.
milestones
.
find_by
(
title:
milestone_param
.
strip
)
parse_params
do
|
milestone_param
|
extract_references
(
milestone_param
,
:milestone
).
first
||
project
.
milestones
.
find_by
(
title:
milestone_param
.
strip
)
end
command
:milestone
do
|
milestone
|
@updates
[
:milestone_id
]
=
milestone
.
id
if
milestone
end
desc
'Remove milestone'
explanation
do
"Removes
#{
issuable
.
milestone
.
to_reference
(
format: :name
)
}
milestone."
end
condition
do
issuable
.
persisted?
&&
issuable
.
milestone_id?
&&
...
...
@@ -128,6 +150,11 @@ module SlashCommands
end
desc
'Add label(s)'
explanation
do
|
labels_param
|
labels
=
find_label_references
(
labels_param
)
"Adds
#{
labels
.
join
(
' '
)
}
#{
'label'
.
pluralize
(
labels
.
count
)
}
."
if
labels
.
any?
end
params
'~label1 ~"label 2"'
condition
do
available_labels
=
LabelsFinder
.
new
(
current_user
,
project_id:
project
.
id
).
execute
...
...
@@ -147,6 +174,14 @@ module SlashCommands
end
desc
'Remove all or specific label(s)'
explanation
do
|
labels_param
=
nil
|
if
labels_param
.
present?
labels
=
find_label_references
(
labels_param
)
"Removes
#{
labels
.
join
(
' '
)
}
#{
'label'
.
pluralize
(
labels
.
count
)
}
."
if
labels
.
any?
else
'Removes all labels.'
end
end
params
'~label1 ~"label 2"'
condition
do
issuable
.
persisted?
&&
...
...
@@ -169,6 +204,10 @@ module SlashCommands
end
desc
'Replace all label(s)'
explanation
do
|
labels_param
|
labels
=
find_label_references
(
labels_param
)
"Replaces all labels with
#{
labels
.
join
(
' '
)
}
#{
'label'
.
pluralize
(
labels
.
count
)
}
."
if
labels
.
any?
end
params
'~label1 ~"label 2"'
condition
do
issuable
.
persisted?
&&
...
...
@@ -187,6 +226,7 @@ module SlashCommands
end
desc
'Add a todo'
explanation
'Adds a todo.'
condition
do
issuable
.
persisted?
&&
!
TodoService
.
new
.
todo_exist?
(
issuable
,
current_user
)
...
...
@@ -196,6 +236,7 @@ module SlashCommands
end
desc
'Mark todo as done'
explanation
'Marks todo as done.'
condition
do
issuable
.
persisted?
&&
TodoService
.
new
.
todo_exist?
(
issuable
,
current_user
)
...
...
@@ -205,6 +246,9 @@ module SlashCommands
end
desc
'Subscribe'
explanation
do
"Subscribes to this
#{
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
}
."
end
condition
do
issuable
.
persisted?
&&
!
issuable
.
subscribed?
(
current_user
,
project
)
...
...
@@ -214,6 +258,9 @@ module SlashCommands
end
desc
'Unsubscribe'
explanation
do
"Unsubscribes from this
#{
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
}
."
end
condition
do
issuable
.
persisted?
&&
issuable
.
subscribed?
(
current_user
,
project
)
...
...
@@ -223,18 +270,23 @@ module SlashCommands
end
desc
'Set due date'
explanation
do
|
due_date
|
"Sets the due date to
#{
due_date
.
to_s
(
:medium
)
}
."
if
due_date
end
params
'<in 2 days | this Friday | December 31st>'
condition
do
issuable
.
respond_to?
(
:due_date
)
&&
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
project
)
end
command
:due
do
|
due_date_param
|
due_date
=
Chronic
.
parse
(
due_date_param
).
try
(
:to_date
)
parse_params
do
|
due_date_param
|
Chronic
.
parse
(
due_date_param
).
try
(
:to_date
)
end
command
:due
do
|
due_date
|
@updates
[
:due_date
]
=
due_date
if
due_date
end
desc
'Remove due date'
explanation
'Removes the due date.'
condition
do
issuable
.
persisted?
&&
issuable
.
respond_to?
(
:due_date
)
&&
...
...
@@ -245,8 +297,11 @@ module SlashCommands
@updates
[
:due_date
]
=
nil
end
desc
do
"Toggle the Work In Progress status"
desc
'Toggle the Work In Progress status'
explanation
do
verb
=
issuable
.
work_in_progress?
?
'Unmarks'
:
'Marks'
noun
=
issuable
.
to_ability_name
.
humanize
(
capitalize:
false
)
"
#{
verb
}
this
#{
noun
}
as Work In Progress."
end
condition
do
issuable
.
persisted?
&&
...
...
@@ -257,45 +312,72 @@ module SlashCommands
@updates
[
:wip_event
]
=
issuable
.
work_in_progress?
?
'unwip'
:
'wip'
end
desc
'Toggle emoji reward'
desc
'Toggle emoji award'
explanation
do
|
name
|
"Toggles :
#{
name
}
: emoji award."
if
name
end
params
':emoji:'
condition
do
issuable
.
persisted?
end
command
:award
do
|
emoji
|
name
=
award_emoji_name
(
emoji
)
parse_params
do
|
emoji_param
|
match
=
emoji_param
.
match
(
Banzai
::
Filter
::
EmojiFilter
.
emoji_pattern
)
match
[
1
]
if
match
end
command
:award
do
|
name
|
if
name
&&
issuable
.
user_can_award?
(
current_user
,
name
)
@updates
[
:emoji_award
]
=
name
end
end
desc
'Set time estimate'
explanation
do
|
time_estimate
|
time_estimate
=
Gitlab
::
TimeTrackingFormatter
.
output
(
time_estimate
)
"Sets time estimate to
#{
time_estimate
}
."
if
time_estimate
end
params
'<1w 3d 2h 14m>'
condition
do
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
project
)
end
command
:estimate
do
|
raw_duration
|
time_estimate
=
Gitlab
::
TimeTrackingFormatter
.
parse
(
raw_duration
)
parse_params
do
|
raw_duration
|
Gitlab
::
TimeTrackingFormatter
.
parse
(
raw_duration
)
end
command
:estimate
do
|
time_estimate
|
if
time_estimate
@updates
[
:time_estimate
]
=
time_estimate
end
end
desc
'Add or substract spent time'
explanation
do
|
time_spent
|
if
time_spent
if
time_spent
>
0
verb
=
'Adds'
value
=
time_spent
else
verb
=
'Substracts'
value
=
-
time_spent
end
"
#{
verb
}
#{
Gitlab
::
TimeTrackingFormatter
.
output
(
value
)
}
spent time."
end
end
params
'<1h 30m | -1h 30m>'
condition
do
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
issuable
)
end
command
:spend
do
|
raw_duration
|
time_spent
=
Gitlab
::
TimeTrackingFormatter
.
parse
(
raw_duration
)
parse_params
do
|
raw_duration
|
Gitlab
::
TimeTrackingFormatter
.
parse
(
raw_duration
)
end
command
:spend
do
|
time_spent
|
if
time_spent
@updates
[
:spend_time
]
=
{
duration:
time_spent
,
user:
current_user
}
end
end
desc
'Remove time estimate'
explanation
'Removes time estimate.'
condition
do
issuable
.
persisted?
&&
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
project
)
...
...
@@ -305,6 +387,7 @@ module SlashCommands
end
desc
'Remove spent time'
explanation
'Removes spent time.'
condition
do
issuable
.
persisted?
&&
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
project
)
...
...
@@ -318,31 +401,41 @@ module SlashCommands
params
'@user'
command
:cc
desc
'Defines target branch for MR'
desc
'Define target branch for MR'
explanation
do
|
branch_name
|
"Sets target branch to
#{
branch_name
}
."
end
params
'<Local branch name>'
condition
do
issuable
.
respond_to?
(
:target_branch
)
&&
(
current_user
.
can?
(
:"update_
#{
issuable
.
to_ability_name
}
"
,
issuable
)
||
issuable
.
new_record?
)
end
command
:target_branch
do
|
target_branch_param
|
branch_name
=
target_branch_param
.
strip
parse_params
do
|
target_branch_param
|
target_branch_param
.
strip
end
command
:target_branch
do
|
branch_name
|
@updates
[
:target_branch
]
=
branch_name
if
project
.
repository
.
branch_names
.
include?
(
branch_name
)
end
desc
'Set weight'
explanation
do
|
weight
|
"Sets weight to
#{
weight
}
."
if
weight
end
params
Issue
::
WEIGHT_RANGE
.
to_s
.
squeeze
(
'.'
).
tr
(
'.'
,
'-'
)
condition
do
issuable
.
respond_to?
(
:weight
)
&&
current_user
.
can?
(
:"admin_
#{
issuable
.
to_ability_name
}
"
,
issuable
)
end
command
:weight
do
|
weight
|
if
Issue
.
weight_filter_options
.
include?
(
weight
.
to_i
)
@updates
[
:weight
]
=
weight
.
to_i
parse_params
do
|
weight
|
weight
.
to_i
if
Issue
.
weight_filter_options
.
include?
(
weight
.
to_i
)
end
command
:weight
do
|
weight
|
@updates
[
:weight
]
=
weight
if
weight
end
desc
'Clear weight'
explanation
'Clears weight.'
condition
do
issuable
.
persisted?
&&
issuable
.
respond_to?
(
:weight
)
&&
...
...
@@ -354,6 +447,10 @@ module SlashCommands
end
desc
'Move issue from one column of the board to another'
explanation
do
|
target_list_name
|
label
=
find_label_references
(
target_list_name
).
first
"Moves issue to
#{
label
}
column in the board."
if
label
end
params
'~"Target column"'
condition
do
issuable
.
is_a?
(
Issue
)
&&
...
...
@@ -375,11 +472,35 @@ module SlashCommands
end
end
def
find_labels
(
labels_param
)
extract_references
(
labels_param
,
:label
)
|
LabelsFinder
.
new
(
current_user
,
project_id:
project
.
id
,
name:
labels_param
.
split
).
execute
end
def
find_label_references
(
labels_param
)
find_labels
(
labels_param
).
map
(
&
:to_reference
)
end
def
find_label_ids
(
labels_param
)
label_ids_by_reference
=
extract_references
(
labels_param
,
:label
).
map
(
&
:id
)
labels_ids_by_name
=
LabelsFinder
.
new
(
current_user
,
project_id:
project
.
id
,
name:
labels_param
.
split
).
execute
.
select
(
:id
)
find_labels
(
labels_param
).
map
(
&
:id
)
end
def
explain_commands
(
commands
,
opts
)
commands
.
map
do
|
name
,
arg
|
definition
=
self
.
class
.
definition_by_name
(
name
)
next
unless
definition
definition
.
explain
(
self
,
opts
,
arg
)
end
.
compact
end
def
extract_updates
(
commands
,
opts
)
commands
.
each
do
|
name
,
arg
|
definition
=
self
.
class
.
definition_by_name
(
name
)
next
unless
definition
label_ids_by_reference
|
labels_ids_by_name
definition
.
execute
(
self
,
opts
,
arg
)
end
end
def
extract_references
(
arg
,
type
)
...
...
@@ -389,9 +510,13 @@ module SlashCommands
ext
.
references
(
type
)
end
def
award_emoji_name
(
emoji
)
match
=
emoji
.
match
(
Banzai
::
Filter
::
EmojiFilter
.
emoji_pattern
)
match
[
1
]
if
match
def
context
{
issuable:
issuable
,
current_user:
current_user
,
project:
project
,
params:
params
}
end
end
end
app/views/groups/milestones/new.html.haml
View file @
5fd0d376
...
...
@@ -26,7 +26,7 @@
.form-group.milestone-description
=
f
.
label
:description
,
"Description"
,
class:
"control-label"
.col-sm-10
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
''
}
do
=
render
'projects/zen'
,
f:
f
,
attr: :description
,
classes:
'note-textarea'
,
placeholder:
'Write milestone description...'
.clearfix
.error-alert
...
...
app/views/layouts/project.html.haml
View file @
5fd0d376
...
...
@@ -5,14 +5,9 @@
-
content_for
:project_javascripts
do
-
project
=
@target_project
||
@project
-
if
@project_wiki
&&
@page
-
preview_markdown_path
=
namespace_project_wiki_preview_markdown_path
(
project
.
namespace
,
project
,
@page
.
slug
)
-
else
-
preview_markdown_path
=
preview_markdown_namespace_project_path
(
project
.
namespace
,
project
)
-
if
current_user
:javascript
window
.
project_uploads_path
=
"
#{
namespace_project_uploads_path
project
.
namespace
,
project
}
"
;
window
.
preview_markdown_path
=
"
#{
preview_markdown_path
}
"
;
-
content_for
:header_content
do
.js-dropdown-menu-projects
...
...
app/views/projects/_md_preview.html.haml
View file @
5fd0d376
-
referenced_users
=
local_assigns
.
fetch
(
:referenced_users
,
nil
)
.md-area
.md-header
%ul
.nav-links.clearfix
...
...
@@ -28,9 +30,10 @@
.md-write-holder
=
yield
.md.md-preview-holder.js-md-preview.hide
{
class:
(
preview_class
if
defined?
(
preview_class
))
}
.md.md-preview-holder.js-md-preview.hide.md-preview
{
data:
{
url:
url
}
}
.referenced-commands.hide
-
if
defined?
(
referenced_users
)
&&
referenced_users
-
if
referenced_users
.referenced-users.hide
%span
=
icon
(
"exclamation-triangle"
)
...
...
app/views/projects/milestones/_form.html.haml
View file @
5fd0d376
...
...
@@ -9,7 +9,7 @@
.form-group.milestone-description
=
f
.
label
:description
,
"Description"
,
class:
"control-label"
.col-sm-10
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
preview_markdown_path
(
@project
)
}
do
=
render
'projects/zen'
,
f:
f
,
attr: :description
,
classes:
'note-textarea'
,
placeholder:
'Write milestone description...'
=
render
'projects/notes/hints'
.clearfix
...
...
app/views/projects/notes/_edit_form.html.haml
View file @
5fd0d376
...
...
@@ -2,7 +2,7 @@
=
form_tag
'#'
,
method: :put
,
class:
'edit-note common-note-form js-quick-submit'
do
=
hidden_field_tag
:target_id
,
''
,
class:
'js-form-target-id'
=
hidden_field_tag
:target_type
,
''
,
class:
'js-form-target-type'
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
'md-preview'
,
referenced_users:
true
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
preview_markdown_path
(
project
)
,
referenced_users:
true
}
do
=
render
'projects/zen'
,
attr:
'note[note]'
,
classes:
'note-textarea js-note-text js-task-list-field'
,
placeholder:
"Write a comment or drag your files here..."
=
render
'projects/notes/hints'
...
...
app/views/projects/notes/_form.html.haml
View file @
5fd0d376
-
supports_slash_commands
=
note_supports_slash_commands?
(
@note
)
-
if
supports_slash_commands
-
preview_url
=
preview_markdown_path
(
@project
,
slash_commands_target_type:
@note
.
noteable_type
,
slash_commands_target_id:
@note
.
noteable_id
)
-
else
-
preview_url
=
preview_markdown_path
(
@project
)
=
form_for
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
@note
],
remote:
true
,
html:
{
:'data-type'
=>
'json'
,
multipart:
true
,
id:
nil
,
class:
"new-note js-new-note-form js-quick-submit common-note-form"
,
"data-noteable-iid"
=>
@note
.
noteable
.
try
(
:iid
),
},
authenticity_token:
true
do
|
f
|
=
hidden_field_tag
:view
,
diff_view
...
...
@@ -18,7 +22,7 @@
-# DiffNote
=
f
.
hidden_field
:position
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
,
referenced_users:
true
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
preview_url
,
referenced_users:
true
}
do
=
render
'projects/zen'
,
f:
f
,
attr: :note
,
classes:
'note-textarea js-note-text'
,
...
...
app/views/projects/notes/_notes_with_form.html.haml
View file @
5fd0d376
%ul
#notes-list
.notes.main-notes-list.timeline
=
render
"shared/notes/notes"
=
render
'projects/notes/edit_form'
=
render
'projects/notes/edit_form'
,
project:
@project
%ul
.notes.notes-form.timeline
%li
.timeline-entry
...
...
app/views/projects/releases/edit.html.haml
View file @
5fd0d376
...
...
@@ -11,7 +11,7 @@
=
form_for
(
@release
,
method: :put
,
url:
namespace_project_tag_release_path
(
@project
.
namespace
,
@project
,
@tag
.
name
),
html:
{
class:
'form-horizontal common-note-form release-form js-quick-submit'
})
do
|
f
|
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
,
referenced_users:
true
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
preview_markdown_path
(
@project
)
,
referenced_users:
true
}
do
=
render
'projects/zen'
,
f:
f
,
attr: :description
,
classes:
'note-textarea'
,
placeholder:
"Write your release notes or drag files here..."
=
render
'projects/notes/hints'
.error-alert
...
...
app/views/projects/tags/new.html.haml
View file @
5fd0d376
...
...
@@ -28,7 +28,7 @@
.form-group
=
label_tag
:release_description
,
'Release notes'
,
class:
'control-label'
.col-sm-10
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
,
referenced_users:
true
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
preview_markdown_path
(
@project
)
,
referenced_users:
true
}
do
=
render
'projects/zen'
,
attr: :release_description
,
classes:
'note-textarea'
,
placeholder:
"Write your release notes or drag files here..."
=
render
'projects/notes/hints'
.help-block
Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.
...
...
app/views/projects/wikis/_form.html.haml
View file @
5fd0d376
...
...
@@ -12,7 +12,7 @@
.form-group
=
f
.
label
:content
,
class:
'control-label'
.col-sm-10
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
namespace_project_wiki_preview_markdown_path
(
@project
.
namespace
,
@project
,
@page
.
slug
)
}
do
=
render
'projects/zen'
,
f:
f
,
attr: :content
,
classes:
'note-textarea'
,
placeholder:
'Write your content or drag files here...'
=
render
'projects/notes/hints'
...
...
app/views/shared/issuable/_form.html.haml
View file @
5fd0d376
...
...
@@ -17,7 +17,7 @@
=
render
'shared/issuable/form/template_selector'
,
issuable:
issuable
=
render
'shared/issuable/form/title'
,
issuable:
issuable
,
form:
form
,
has_wip_commits:
commits
&&
commits
.
detect
(
&
:work_in_progress?
)
=
render
'shared/issuable/form/description'
,
issuable:
issuable
,
form:
form
=
render
'shared/issuable/form/description'
,
issuable:
issuable
,
form:
form
,
project:
project
-
if
issuable
.
respond_to?
(
:confidential
)
.form-group
...
...
app/views/shared/issuable/form/_description.html.haml
View file @
5fd0d376
-
project
=
local_assigns
.
fetch
(
:project
)
-
issuable
=
local_assigns
.
fetch
(
:issuable
)
-
form
=
local_assigns
.
fetch
(
:form
)
-
supports_slash_commands
=
issuable
.
new_record?
-
if
supports_slash_commands
-
preview_url
=
preview_markdown_path
(
project
,
slash_commands_target_type:
issuable
.
class
.
name
)
-
else
-
preview_url
=
preview_markdown_path
(
project
)
.form-group.detail-page-description
=
form
.
label
:description
,
'Description'
,
class:
'control-label'
.col-sm-10
=
render
layout:
'projects/md_preview'
,
locals:
{
preview_class:
"md-preview"
,
referenced_users:
true
}
do
=
render
layout:
'projects/md_preview'
,
locals:
{
url:
preview_url
,
referenced_users:
true
}
do
=
render
'projects/zen'
,
f:
form
,
attr: :description
,
classes:
'note-textarea'
,
placeholder:
"Write a comment or drag your files here..."
,
supports_slash_commands:
!
issuable
.
persisted?
=
render
'projects/notes/hints'
,
supports_slash_commands:
!
issuable
.
persisted?
supports_slash_commands:
supports_slash_commands
=
render
'projects/notes/hints'
,
supports_slash_commands:
supports_slash_commands
.clearfix
.error-alert
changelogs/unreleased/preview-separate-slash-commands.yml
0 → 100644
View file @
5fd0d376
---
title
:
Display slash commands outcome when previewing Markdown
merge_request
:
10054
author
:
Rares Sfirlogea
lib/gitlab/slash_commands/command_definition.rb
View file @
5fd0d376
module
Gitlab
module
SlashCommands
class
CommandDefinition
attr_accessor
:name
,
:aliases
,
:description
,
:params
,
:condition_block
,
:action_block
attr_accessor
:name
,
:aliases
,
:description
,
:explanation
,
:params
,
:condition_block
,
:parse_params_block
,
:action_block
def
initialize
(
name
,
attributes
=
{})
@name
=
name
@aliases
=
attributes
[
:aliases
]
||
[]
@description
=
attributes
[
:description
]
||
''
@explanation
=
attributes
[
:explanation
]
||
''
@params
=
attributes
[
:params
]
||
[]
@condition_block
=
attributes
[
:condition_block
]
@parse_params_block
=
attributes
[
:parse_params_block
]
@action_block
=
attributes
[
:action_block
]
end
...
...
@@ -28,14 +31,20 @@ module Gitlab
context
.
instance_exec
(
&
condition_block
)
end
def
explain
(
context
,
opts
,
arg
)
return
unless
available?
(
opts
)
if
explanation
.
respond_to?
(
:call
)
execute_block
(
explanation
,
context
,
arg
)
else
explanation
end
end
def
execute
(
context
,
opts
,
arg
)
return
if
noop?
||
!
available?
(
opts
)
if
arg
.
present?
context
.
instance_exec
(
arg
,
&
action_block
)
elsif
action_block
.
arity
==
0
context
.
instance_exec
(
&
action_block
)
end
execute_block
(
action_block
,
context
,
arg
)
end
def
to_h
(
opts
)
...
...
@@ -52,6 +61,23 @@ module Gitlab
params:
params
}
end
private
def
execute_block
(
block
,
context
,
arg
)
if
arg
.
present?
parsed
=
parse_params
(
arg
,
context
)
context
.
instance_exec
(
parsed
,
&
block
)
elsif
block
.
arity
==
0
context
.
instance_exec
(
&
block
)
end
end
def
parse_params
(
arg
,
context
)
return
arg
unless
parse_params_block
context
.
instance_exec
(
arg
,
&
parse_params_block
)
end
end
end
end
lib/gitlab/slash_commands/dsl.rb
View file @
5fd0d376
...
...
@@ -44,6 +44,22 @@ module Gitlab
@params
=
params
end
# Allows to give an explanation of what the command will do when
# executed. This explanation is shown when rendering the Markdown
# preview.
#
# Example:
#
# explanation do |arguments|
# "Adds label(s) #{arguments.join(' ')}"
# end
# command :command_key do |arguments|
# # Awesome code block
# end
def
explanation
(
text
=
''
,
&
block
)
@explanation
=
block_given?
?
block
:
text
end
# Allows to define conditions that must be met in order for the command
# to be returned by `.command_names` & `.command_definitions`.
# It accepts a block that will be evaluated with the context given to
...
...
@@ -61,6 +77,24 @@ module Gitlab
@condition_block
=
block
end
# Allows to perform initial parsing of parameters. The result is passed
# both to `command` and `explanation` blocks, instead of the raw
# parameters.
# It accepts a block that will be evaluated with the context given to
# `CommandDefintion#to_h`.
#
# Example:
#
# parse_params do |raw|
# raw.strip
# end
# command :command_key do |parsed|
# # Awesome code block
# end
def
parse_params
(
&
block
)
@parse_params_block
=
block
end
# Registers a new command which is recognizeable from body of email or
# comment.
# It accepts aliases and takes a block.
...
...
@@ -77,8 +111,10 @@ module Gitlab
name
,
aliases:
aliases
,
description:
@description
,
explanation:
@explanation
,
params:
@params
,
condition_block:
@condition_block
,
parse_params_block:
@parse_params_block
,
action_block:
block
)
...
...
@@ -89,8 +125,14 @@ module Gitlab
end
@description
=
nil
@explanation
=
nil
@params
=
nil
@condition_block
=
nil
@parse_params_block
=
nil
end
def
definition_by_name
(
name
)
command_definitions_by_name
[
name
.
to_sym
]
end
end
end
...
...
spec/lib/gitlab/slash_commands/command_definition_spec.rb
View file @
5fd0d376
...
...
@@ -167,6 +167,58 @@ describe Gitlab::SlashCommands::CommandDefinition do
end
end
end
context
'when the command defines parse_params block'
do
before
do
subject
.
parse_params_block
=
->
(
raw
)
{
raw
.
strip
}
subject
.
action_block
=
->
(
parsed
)
{
self
.
received_arg
=
parsed
}
end
it
'executes the command passing the parsed param'
do
subject
.
execute
(
context
,
{},
'something '
)
expect
(
context
.
received_arg
).
to
eq
(
'something'
)
end
end
end
end
end
describe
'#explain'
do
context
'when the command is not available'
do
before
do
subject
.
condition_block
=
proc
{
false
}
subject
.
explanation
=
'Explanation'
end
it
'returns nil'
do
result
=
subject
.
explain
({},
{},
nil
)
expect
(
result
).
to
be_nil
end
end
context
'when the explanation is a static string'
do
before
do
subject
.
explanation
=
'Explanation'
end
it
'returns this static string'
do
result
=
subject
.
explain
({},
{},
nil
)
expect
(
result
).
to
eq
'Explanation'
end
end
context
'when the explanation is dynamic'
do
before
do
subject
.
explanation
=
proc
{
|
arg
|
"Dynamic
#{
arg
}
"
}
end
it
'invokes the proc'
do
result
=
subject
.
explain
({},
{},
'explanation'
)
expect
(
result
).
to
eq
'Dynamic explanation'
end
end
end
...
...
spec/lib/gitlab/slash_commands/dsl_spec.rb
View file @
5fd0d376
...
...
@@ -11,67 +11,99 @@ describe Gitlab::SlashCommands::Dsl do
end
params
'The first argument'
command
:one_arg
,
:once
,
:first
do
|
arg1
|
arg1
explanation
'Static explanation'
command
:explanation_with_aliases
,
:once
,
:first
do
|
arg
|
arg
end
desc
do
"A dynamic description for
#{
noteable
.
upcase
}
"
end
params
'The first argument'
,
'The second argument'
command
:
two_args
do
|
arg1
,
arg2
|
[
arg1
,
arg2
]
command
:
dynamic_description
do
|
args
|
args
.
split
end
command
:cc
explanation
do
|
arg
|
"Action does something with
#{
arg
}
"
end
condition
do
project
==
'foo'
end
command
:cond_action
do
|
arg
|
arg
end
parse_params
do
|
raw_arg
|
raw_arg
.
strip
end
command
:with_params_parsing
do
|
parsed
|
parsed
end
end
end
describe
'.command_definitions'
do
it
'returns an array with commands definitions'
do
no_args_def
,
one_arg_def
,
two_args_def
,
cc_def
,
cond_action_def
=
DummyClass
.
command_definitions
no_args_def
,
explanation_with_aliases_def
,
dynamic_description_def
,
cc_def
,
cond_action_def
,
with_params_parsing_def
=
DummyClass
.
command_definitions
expect
(
no_args_def
.
name
).
to
eq
(
:no_args
)
expect
(
no_args_def
.
aliases
).
to
eq
([
:none
])
expect
(
no_args_def
.
description
).
to
eq
(
'A command with no args'
)
expect
(
no_args_def
.
explanation
).
to
eq
(
''
)
expect
(
no_args_def
.
params
).
to
eq
([])
expect
(
no_args_def
.
condition_block
).
to
be_nil
expect
(
no_args_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
no_args_def
.
parse_params_block
).
to
be_nil
expect
(
one_arg_def
.
name
).
to
eq
(
:one_arg
)
expect
(
one_arg_def
.
aliases
).
to
eq
([
:once
,
:first
])
expect
(
one_arg_def
.
description
).
to
eq
(
''
)
expect
(
one_arg_def
.
params
).
to
eq
([
'The first argument'
])
expect
(
one_arg_def
.
condition_block
).
to
be_nil
expect
(
one_arg_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
explanation_with_aliases_def
.
name
).
to
eq
(
:explanation_with_aliases
)
expect
(
explanation_with_aliases_def
.
aliases
).
to
eq
([
:once
,
:first
])
expect
(
explanation_with_aliases_def
.
description
).
to
eq
(
''
)
expect
(
explanation_with_aliases_def
.
explanation
).
to
eq
(
'Static explanation'
)
expect
(
explanation_with_aliases_def
.
params
).
to
eq
([
'The first argument'
])
expect
(
explanation_with_aliases_def
.
condition_block
).
to
be_nil
expect
(
explanation_with_aliases_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
explanation_with_aliases_def
.
parse_params_block
).
to
be_nil
expect
(
two_args_def
.
name
).
to
eq
(
:two_args
)
expect
(
two_args_def
.
aliases
).
to
eq
([])
expect
(
two_args_def
.
to_h
(
noteable:
"issue"
)[
:description
]).
to
eq
(
'A dynamic description for ISSUE'
)
expect
(
two_args_def
.
params
).
to
eq
([
'The first argument'
,
'The second argument'
])
expect
(
two_args_def
.
condition_block
).
to
be_nil
expect
(
two_args_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
dynamic_description_def
.
name
).
to
eq
(
:dynamic_description
)
expect
(
dynamic_description_def
.
aliases
).
to
eq
([])
expect
(
dynamic_description_def
.
to_h
(
noteable:
'issue'
)[
:description
]).
to
eq
(
'A dynamic description for ISSUE'
)
expect
(
dynamic_description_def
.
explanation
).
to
eq
(
''
)
expect
(
dynamic_description_def
.
params
).
to
eq
([
'The first argument'
,
'The second argument'
])
expect
(
dynamic_description_def
.
condition_block
).
to
be_nil
expect
(
dynamic_description_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
dynamic_description_def
.
parse_params_block
).
to
be_nil
expect
(
cc_def
.
name
).
to
eq
(
:cc
)
expect
(
cc_def
.
aliases
).
to
eq
([])
expect
(
cc_def
.
description
).
to
eq
(
''
)
expect
(
cc_def
.
explanation
).
to
eq
(
''
)
expect
(
cc_def
.
params
).
to
eq
([])
expect
(
cc_def
.
condition_block
).
to
be_nil
expect
(
cc_def
.
action_block
).
to
be_nil
expect
(
cc_def
.
parse_params_block
).
to
be_nil
expect
(
cond_action_def
.
name
).
to
eq
(
:cond_action
)
expect
(
cond_action_def
.
aliases
).
to
eq
([])
expect
(
cond_action_def
.
description
).
to
eq
(
''
)
expect
(
cond_action_def
.
explanation
).
to
be_a_kind_of
(
Proc
)
expect
(
cond_action_def
.
params
).
to
eq
([])
expect
(
cond_action_def
.
condition_block
).
to
be_a_kind_of
(
Proc
)
expect
(
cond_action_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
cond_action_def
.
parse_params_block
).
to
be_nil
expect
(
with_params_parsing_def
.
name
).
to
eq
(
:with_params_parsing
)
expect
(
with_params_parsing_def
.
aliases
).
to
eq
([])
expect
(
with_params_parsing_def
.
description
).
to
eq
(
''
)
expect
(
with_params_parsing_def
.
explanation
).
to
eq
(
''
)
expect
(
with_params_parsing_def
.
params
).
to
eq
([])
expect
(
with_params_parsing_def
.
condition_block
).
to
be_nil
expect
(
with_params_parsing_def
.
action_block
).
to
be_a_kind_of
(
Proc
)
expect
(
with_params_parsing_def
.
parse_params_block
).
to
be_a_kind_of
(
Proc
)
end
end
end
spec/services/preview_markdown_service_spec.rb
0 → 100644
View file @
5fd0d376
require
'spec_helper'
describe
PreviewMarkdownService
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:empty_project
)
}
before
do
project
.
add_developer
(
user
)
end
describe
'user references'
do
let
(
:params
)
{
{
text:
"Take a look
#{
user
.
to_reference
}
"
}
}
let
(
:service
)
{
described_class
.
new
(
project
,
user
,
params
)
}
it
'returns users referenced in text'
do
result
=
service
.
execute
expect
(
result
[
:users
]).
to
eq
[
user
.
username
]
end
end
context
'new note with slash commands'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let
(
:params
)
do
{
text:
"Please do it
\n
/assign
#{
user
.
to_reference
}
"
,
slash_commands_target_type:
'Issue'
,
slash_commands_target_id:
issue
.
id
}
end
let
(
:service
)
{
described_class
.
new
(
project
,
user
,
params
)
}
it
'removes slash commands from text'
do
result
=
service
.
execute
expect
(
result
[
:text
]).
to
eq
'Please do it'
end
it
'explains slash commands effect'
do
result
=
service
.
execute
expect
(
result
[
:commands
]).
to
eq
"Assigns
#{
user
.
to_reference
}
."
end
end
context
'merge request description'
do
let
(
:params
)
do
{
text:
"My work
\n
/estimate 2y"
,
slash_commands_target_type:
'MergeRequest'
}
end
let
(
:service
)
{
described_class
.
new
(
project
,
user
,
params
)
}
it
'removes slash commands from text'
do
result
=
service
.
execute
expect
(
result
[
:text
]).
to
eq
'My work'
end
it
'explains slash commands effect'
do
result
=
service
.
execute
expect
(
result
[
:commands
]).
to
eq
'Sets time estimate to 2y.'
end
end
end
spec/services/slash_commands/interpret_service_spec.rb
View file @
5fd0d376
...
...
@@ -825,4 +825,222 @@ describe SlashCommands::InterpretService, services: true do
end
end
end
describe
'#explain'
do
let
(
:service
)
{
described_class
.
new
(
project
,
developer
)
}
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
)
}
describe
'close command'
do
let
(
:content
)
{
'/close'
}
it
'includes issuable name'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Closes this issue.'
])
end
end
describe
'reopen command'
do
let
(
:content
)
{
'/reopen'
}
let
(
:merge_request
)
{
create
(
:merge_request
,
:closed
,
source_project:
project
)
}
it
'includes issuable name'
do
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Reopens this merge request.'
])
end
end
describe
'title command'
do
let
(
:content
)
{
'/title This is new title'
}
it
'includes new title'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Changes the title to "This is new title".'
])
end
end
describe
'assign command'
do
let
(
:content
)
{
"/assign @
#{
developer
.
username
}
do it!"
}
it
'includes only the user reference'
do
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
"Assigns @
#{
developer
.
username
}
."
])
end
end
describe
'unassign command'
do
let
(
:content
)
{
'/unassign'
}
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
assignee:
developer
)
}
it
'includes current assignee reference'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
"Removes assignee @
#{
developer
.
username
}
."
])
end
end
describe
'milestone command'
do
let
(
:content
)
{
'/milestone %wrong-milestone'
}
let!
(
:milestone
)
{
create
(
:milestone
,
project:
project
,
title:
'9.10'
)
}
it
'is empty when milestone reference is wrong'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([])
end
end
describe
'remove milestone command'
do
let
(
:content
)
{
'/remove_milestone'
}
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
,
milestone:
milestone
)
}
it
'includes current milestone name'
do
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Removes %"9.10" milestone.'
])
end
end
describe
'label command'
do
let
(
:content
)
{
'/label ~missing'
}
let!
(
:label
)
{
create
(
:label
,
project:
project
)
}
it
'is empty when there are no correct labels'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([])
end
end
describe
'unlabel command'
do
let
(
:content
)
{
'/unlabel'
}
it
'says all labels if no parameter provided'
do
merge_request
.
update!
(
label_ids:
[
bug
.
id
])
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Removes all labels.'
])
end
end
describe
'relabel command'
do
let
(
:content
)
{
'/relabel Bug'
}
let!
(
:bug
)
{
create
(
:label
,
project:
project
,
title:
'Bug'
)
}
let
(
:feature
)
{
create
(
:label
,
project:
project
,
title:
'Feature'
)
}
it
'includes label name'
do
issue
.
update!
(
label_ids:
[
feature
.
id
])
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
"Replaces all labels with ~
#{
bug
.
id
}
label."
])
end
end
describe
'subscribe command'
do
let
(
:content
)
{
'/subscribe'
}
it
'includes issuable name'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Subscribes to this issue.'
])
end
end
describe
'unsubscribe command'
do
let
(
:content
)
{
'/unsubscribe'
}
it
'includes issuable name'
do
merge_request
.
subscribe
(
developer
,
project
)
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Unsubscribes from this merge request.'
])
end
end
describe
'due command'
do
let
(
:content
)
{
'/due April 1st 2016'
}
it
'includes the date'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Sets the due date to Apr 1, 2016.'
])
end
end
describe
'wip command'
do
let
(
:content
)
{
'/wip'
}
it
'includes the new status'
do
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Marks this merge request as Work In Progress.'
])
end
end
describe
'award command'
do
let
(
:content
)
{
'/award :confetti_ball: '
}
it
'includes the emoji'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Toggles :confetti_ball: emoji award.'
])
end
end
describe
'estimate command'
do
let
(
:content
)
{
'/estimate 79d'
}
it
'includes the formatted duration'
do
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Sets time estimate to 3mo 3w 4d.'
])
end
end
describe
'spend command'
do
let
(
:content
)
{
'/spend -120m'
}
it
'includes the formatted duration and proper verb'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Substracts 2h spent time.'
])
end
end
describe
'target branch command'
do
let
(
:content
)
{
'/target_branch my-feature '
}
it
'includes the branch name'
do
_
,
explanations
=
service
.
explain
(
content
,
merge_request
)
expect
(
explanations
).
to
eq
([
'Sets target branch to my-feature.'
])
end
end
describe
'board move command'
do
let
(
:content
)
{
'/board_move ~bug'
}
let!
(
:bug
)
{
create
(
:label
,
project:
project
,
title:
'bug'
)
}
let!
(
:board
)
{
create
(
:board
,
project:
project
)
}
it
'includes the label name'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
"Moves issue to ~
#{
bug
.
id
}
column in the board."
])
end
end
# EE-specific tests
describe
'weight command'
do
let
(
:content
)
{
'/weight 4'
}
it
'includes the number'
do
_
,
explanations
=
service
.
explain
(
content
,
issue
)
expect
(
explanations
).
to
eq
([
'Sets weight to 4.'
])
end
end
end
end
spec/support/features/issuable_slash_commands_shared_examples.rb
View file @
5fd0d376
...
...
@@ -257,4 +257,19 @@ shared_examples 'issuable record that supports slash commands in its description
end
end
end
describe
"preview of note on
#{
issuable_type
}
"
do
it
'removes slash commands from note and explains them'
do
visit
public_send
(
"namespace_project_
#{
issuable_type
}
_path"
,
project
.
namespace
,
project
,
issuable
)
page
.
within
(
'.js-main-target-form'
)
do
fill_in
'note[note]'
,
with:
"Awesome!
\n
/assign @bob "
click_on
'Preview'
expect
(
page
).
to
have_content
'Awesome!'
expect
(
page
).
not_to
have_content
'/assign @bob'
expect
(
page
).
to
have_content
'Assigns @bob.'
end
end
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