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
d072c18d
Commit
d072c18d
authored
Dec 18, 2017
by
Phil Hughes
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into ce-to-ee-2017-12-16
parents
458f54dc
0a395a08
Changes
49
Hide whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
1040 additions
and
1066 deletions
+1040
-1066
app/assets/javascripts/boards/components/board_sidebar.js
app/assets/javascripts/boards/components/board_sidebar.js
+1
-1
app/assets/javascripts/boards/models/issue.js
app/assets/javascripts/boards/models/issue.js
+0
-1
app/assets/javascripts/diff_notes/components/diff_note_avatars.js
...ts/javascripts/diff_notes/components/diff_note_avatars.js
+2
-2
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+3
-3
app/assets/javascripts/init_issuable_sidebar.js
app/assets/javascripts/init_issuable_sidebar.js
+2
-2
app/assets/javascripts/init_notes.js
app/assets/javascripts/init_notes.js
+4
-2
app/assets/javascripts/line_highlighter.js
app/assets/javascripts/line_highlighter.js
+1
-1
app/assets/javascripts/main.js
app/assets/javascripts/main.js
+0
-4
app/assets/javascripts/merge_request.js
app/assets/javascripts/merge_request.js
+128
-132
app/assets/javascripts/merge_request_tabs.js
app/assets/javascripts/merge_request_tabs.js
+2
-2
app/assets/javascripts/notes.js
app/assets/javascripts/notes.js
+6
-0
app/assets/javascripts/repo/components/repo_preview.vue
app/assets/javascripts/repo/components/repo_preview.vue
+1
-1
app/assets/javascripts/right_sidebar.js
app/assets/javascripts/right_sidebar.js
+220
-218
app/assets/javascripts/shortcuts_issuable.js
app/assets/javascripts/shortcuts_issuable.js
+2
-2
app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
...ts/vue_merge_request_widget/services/mr_widget_service.js
+1
-1
app/controllers/projects/merge_requests_controller.rb
app/controllers/projects/merge_requests_controller.rb
+6
-2
app/helpers/issuables_helper.rb
app/helpers/issuables_helper.rb
+2
-2
app/serializers/issuable_entity.rb
app/serializers/issuable_entity.rb
+0
-8
app/serializers/issuable_sidebar_entity.rb
app/serializers/issuable_sidebar_entity.rb
+1
-5
app/serializers/issue_entity.rb
app/serializers/issue_entity.rb
+8
-0
app/serializers/merge_request_serializer.rb
app/serializers/merge_request_serializer.rb
+3
-3
app/serializers/merge_request_widget_entity.rb
app/serializers/merge_request_widget_entity.rb
+2
-4
app/views/projects/merge_requests/show.html.haml
app/views/projects/merge_requests/show.html.haml
+1
-1
app/views/shared/empty_states/_issues.html.haml
app/views/shared/empty_states/_issues.html.haml
+7
-6
changelogs/unreleased/osw-isolate-mr-widget-exposed-attributes.yml
...s/unreleased/osw-isolate-mr-widget-exposed-attributes.yml
+5
-0
doc/articles/laravel_with_gitlab_and_envoy/index.md
doc/articles/laravel_with_gitlab_and_envoy/index.md
+1
-1
ee/app/serializers/ee/merge_request_widget_entity.rb
ee/app/serializers/ee/merge_request_widget_entity.rb
+1
-1
qa/qa/ee.rb
qa/qa/ee.rb
+6
-6
qa/qa/ee/factory/geo/node.rb
qa/qa/ee/factory/geo/node.rb
+3
-3
qa/qa/ee/factory/license.rb
qa/qa/ee/factory/license.rb
+19
-0
qa/qa/ee/scenario/license/add.rb
qa/qa/ee/scenario/license/add.rb
+0
-21
qa/qa/ee/scenario/test/geo.rb
qa/qa/ee/scenario/test/geo.rb
+3
-9
qa/qa/ee/strategy.rb
qa/qa/ee/strategy.rb
+1
-1
qa/qa/specs/features/ee/geo/replication_spec.rb
qa/qa/specs/features/ee/geo/replication_spec.rb
+3
-3
spec/controllers/projects/merge_requests_controller_spec.rb
spec/controllers/projects/merge_requests_controller_spec.rb
+4
-4
spec/ee/spec/serializers/epic_entity_spec.rb
spec/ee/spec/serializers/epic_entity_spec.rb
+1
-2
spec/ee/spec/serializers/merge_request_widget_entity_spec.rb
spec/ee/spec/serializers/merge_request_widget_entity_spec.rb
+1
-1
spec/features/issues_spec.rb
spec/features/issues_spec.rb
+538
-514
spec/features/merge_requests/mini_pipeline_graph_spec.rb
spec/features/merge_requests/mini_pipeline_graph_spec.rb
+4
-4
spec/fixtures/api/schemas/entities/merge_request_widget.json
spec/fixtures/api/schemas/entities/merge_request_widget.json
+4
-4
spec/javascripts/collapsed_sidebar_todo_spec.js
spec/javascripts/collapsed_sidebar_todo_spec.js
+1
-2
spec/javascripts/line_highlighter_spec.js
spec/javascripts/line_highlighter_spec.js
+1
-2
spec/javascripts/merge_request_notes_spec.js
spec/javascripts/merge_request_notes_spec.js
+1
-3
spec/javascripts/merge_request_spec.js
spec/javascripts/merge_request_spec.js
+1
-2
spec/javascripts/merge_request_tabs_spec.js
spec/javascripts/merge_request_tabs_spec.js
+9
-10
spec/javascripts/notes_spec.js
spec/javascripts/notes_spec.js
+1
-3
spec/javascripts/right_sidebar_spec.js
spec/javascripts/right_sidebar_spec.js
+1
-2
spec/serializers/merge_request_serializer_spec.rb
spec/serializers/merge_request_serializer_spec.rb
+27
-21
spec/serializers/merge_request_widget_entity_spec.rb
spec/serializers/merge_request_widget_entity_spec.rb
+1
-44
No files found.
app/assets/javascripts/boards/components/board_sidebar.js
View file @
d072c18d
/* eslint-disable comma-dangle, space-before-function-paren, no-new */
/* global MilestoneSelect */
/* global Sidebar */
import
Vue
from
'
vue
'
;
import
weight
from
'
ee/sidebar/components/weight/weight.vue
'
;
import
Flash
from
'
../../flash
'
;
import
Sidebar
from
'
../../right_sidebar
'
;
import
eventHub
from
'
../../sidebar/event_hub
'
;
import
assigneeTitle
from
'
../../sidebar/components/assignees/assignee_title
'
;
import
assignees
from
'
../../sidebar/components/assignees/assignees
'
;
...
...
app/assets/javascripts/boards/models/issue.js
View file @
d072c18d
...
...
@@ -25,7 +25,6 @@ class ListIssue {
this
.
isLoading
=
{
weight
:
false
,
};
this
.
isLoading
=
{};
this
.
sidebarInfoEndpoint
=
obj
.
issue_sidebar_endpoint
;
this
.
toggleSubscriptionEndpoint
=
obj
.
toggle_subscription_endpoint
;
this
.
milestone_id
=
obj
.
milestone_id
;
...
...
app/assets/javascripts/diff_notes/components/diff_note_avatars.js
View file @
d072c18d
/* global CommentsStore */
/* global notes */
import
Vue
from
'
vue
'
;
import
collapseIcon
from
'
../icons/collapse_icon.svg
'
;
import
Notes
from
'
../../notes
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
DiffNoteAvatars
=
Vue
.
extend
({
...
...
@@ -129,7 +129,7 @@ const DiffNoteAvatars = Vue.extend({
},
methods
:
{
clickedAvatar
(
e
)
{
notes
.
onAddDiffNote
(
e
);
Notes
.
instance
.
onAddDiffNote
(
e
);
// Toggle the active state of the toggle all button
this
.
toggleDiscussionsToggleState
();
...
...
app/assets/javascripts/dispatcher.js
View file @
d072c18d
...
...
@@ -11,7 +11,7 @@ import NotificationsForm from './notifications_form';
import
notificationsDropdown
from
'
./notifications_dropdown
'
;
import
groupAvatar
from
'
./group_avatar
'
;
import
GroupLabelSubscription
from
'
./group_label_subscription
'
;
/* global LineHighlighter */
import
LineHighlighter
from
'
./line_highlighter
'
;
import
BuildArtifacts
from
'
./build_artifacts
'
;
import
CILintEditor
from
'
./ci_lint_editor
'
;
import
groupsSelect
from
'
./groups_select
'
;
...
...
@@ -21,7 +21,7 @@ import NamespaceSelect from './namespace_select';
import
NewCommitForm
from
'
./new_commit_form
'
;
import
Project
from
'
./project
'
;
import
projectAvatar
from
'
./project_avatar
'
;
/* global MergeRequest */
import
MergeRequest
from
'
./merge_request
'
;
import
Compare
from
'
./compare
'
;
import
initCompareAutocomplete
from
'
./compare_autocomplete
'
;
/* global PathLocks */
...
...
@@ -30,7 +30,7 @@ import ProjectNew from './project_new';
import
projectImport
from
'
./project_import
'
;
import
Labels
from
'
./labels
'
;
import
LabelManager
from
'
./label_manager
'
;
/* global Sidebar */
import
Sidebar
from
'
./right_sidebar
'
;
/* global WeightSelect */
/* global AdminEmailSelect */
...
...
app/assets/javascripts/init_issuable_sidebar.js
View file @
d072c18d
...
...
@@ -3,7 +3,7 @@
/* global WeightSelect */
import
LabelsSelect
from
'
./labels_select
'
;
import
IssuableContext
from
'
./issuable_context
'
;
/* global Sidebar */
import
Sidebar
from
'
./right_sidebar
'
;
import
DueDateSelectors
from
'
./due_date_select
'
;
...
...
@@ -17,5 +17,5 @@ export default () => {
new
WeightSelect
();
new
IssuableContext
(
sidebarOptions
.
currentUser
);
new
DueDateSelectors
();
window
.
sidebar
=
new
Sidebar
();
Sidebar
.
initialize
();
};
app/assets/javascripts/init_notes.js
View file @
d072c18d
/* global Notes */
import
Notes
from
'
./notes
'
;
export
default
()
=>
{
const
dataEl
=
document
.
querySelector
(
'
.js-notes-data
'
);
...
...
@@ -10,5 +10,7 @@ export default () => {
autocomplete
,
}
=
JSON
.
parse
(
dataEl
.
innerHTML
);
window
.
notes
=
new
Notes
(
notesUrl
,
notesIds
,
now
,
diffView
,
autocomplete
);
// Create a singleton so that we don't need to assign
// into the window object, we can just access the current isntance with Notes.instance
Notes
.
initialize
(
notesUrl
,
notesIds
,
now
,
diffView
,
autocomplete
);
};
app/assets/javascripts/line_highlighter.js
View file @
d072c18d
...
...
@@ -175,4 +175,4 @@ LineHighlighter.prototype.__setLocationHash__ = function(value) {
},
document
.
title
,
value
);
};
window
.
LineHighlighter
=
LineHighlighter
;
export
default
LineHighlighter
;
app/assets/javascripts/main.js
View file @
d072c18d
...
...
@@ -45,14 +45,10 @@ import './layout_nav';
import
LazyLoader
from
'
./lazy_loader
'
;
import
'
./line_highlighter
'
;
import
initLogoAnimation
from
'
./logo
'
;
import
'
./merge_request
'
;
import
'
./merge_request_tabs
'
;
import
'
./milestone_select
'
;
import
'
./notes
'
;
import
'
./preview_markdown
'
;
import
'
./projects_dropdown
'
;
import
'
./render_gfm
'
;
import
'
./right_sidebar
'
;
import
initBreadcrumbs
from
'
./breadcrumb
'
;
// EE-only scripts
...
...
app/assets/javascripts/merge_request.js
View file @
d072c18d
...
...
@@ -7,142 +7,138 @@ import MergeRequestTabs from './merge_request_tabs';
import
IssuablesHelper
from
'
./helpers/issuables_helper
'
;
import
{
addDelimiter
}
from
'
./lib/utils/text_utility
'
;
(
function
()
{
this
.
MergeRequest
=
(
function
()
{
function
MergeRequest
(
opts
)
{
// Initialize MergeRequest behavior
//
// Options:
// action - String, current controller action
//
this
.
opts
=
opts
!=
null
?
opts
:
{};
this
.
submitNoteForm
=
this
.
submitNoteForm
.
bind
(
this
);
this
.
$el
=
$
(
'
.merge-request
'
);
this
.
$
(
'
.show-all-commits
'
).
on
(
'
click
'
,
(
function
(
_this
)
{
return
function
()
{
return
_this
.
showAllCommits
();
};
})(
this
));
this
.
initTabs
();
this
.
initMRBtnListeners
();
this
.
initCommitMessageListeners
();
this
.
closeReopenReportToggle
=
IssuablesHelper
.
initCloseReopenReport
();
if
(
$
(
"
a.btn-close
"
).
length
)
{
this
.
taskList
=
new
TaskList
({
dataType
:
'
merge_request
'
,
fieldName
:
'
description
'
,
selector
:
'
.detail-page-description
'
,
onSuccess
:
(
result
)
=>
{
document
.
querySelector
(
'
#task_status
'
).
innerText
=
result
.
task_status
;
document
.
querySelector
(
'
#task_status_short
'
).
innerText
=
result
.
task_status_short
;
}
});
}
}
// Local jQuery finder
MergeRequest
.
prototype
.
$
=
function
(
selector
)
{
return
this
.
$el
.
find
(
selector
);
function
MergeRequest
(
opts
)
{
// Initialize MergeRequest behavior
//
// Options:
// action - String, current controller action
//
this
.
opts
=
opts
!=
null
?
opts
:
{};
this
.
submitNoteForm
=
this
.
submitNoteForm
.
bind
(
this
);
this
.
$el
=
$
(
'
.merge-request
'
);
this
.
$
(
'
.show-all-commits
'
).
on
(
'
click
'
,
(
function
(
_this
)
{
return
function
()
{
return
_this
.
showAllCommits
();
};
MergeRequest
.
prototype
.
initTabs
=
function
()
{
if
(
window
.
mrTabs
)
{
window
.
mrTabs
.
unbindEvents
();
})(
this
));
this
.
initTabs
();
this
.
initMRBtnListeners
();
this
.
initCommitMessageListeners
();
this
.
closeReopenReportToggle
=
IssuablesHelper
.
initCloseReopenReport
();
if
(
$
(
"
a.btn-close
"
).
length
)
{
this
.
taskList
=
new
TaskList
({
dataType
:
'
merge_request
'
,
fieldName
:
'
description
'
,
selector
:
'
.detail-page-description
'
,
onSuccess
:
(
result
)
=>
{
document
.
querySelector
(
'
#task_status
'
).
innerText
=
result
.
task_status
;
document
.
querySelector
(
'
#task_status_short
'
).
innerText
=
result
.
task_status_short
;
}
window
.
mrTabs
=
new
MergeRequestTabs
(
this
.
opts
);
};
MergeRequest
.
prototype
.
showAllCommits
=
function
()
{
this
.
$
(
'
.first-commits
'
).
remove
();
return
this
.
$
(
'
.all-commits
'
).
removeClass
(
'
hide
'
);
};
MergeRequest
.
prototype
.
initMRBtnListeners
=
function
()
{
var
_this
;
_this
=
this
;
return
$
(
'
a.btn-close, a.btn-reopen
'
).
on
(
'
click
'
,
function
(
e
)
{
var
$this
,
shouldSubmit
;
$this
=
$
(
this
);
shouldSubmit
=
$this
.
hasClass
(
'
btn-comment
'
);
if
(
shouldSubmit
&&
$this
.
data
(
'
submitted
'
))
{
return
;
}
if
(
this
.
closeReopenReportToggle
)
this
.
closeReopenReportToggle
.
setDisable
();
if
(
shouldSubmit
)
{
if
(
$this
.
hasClass
(
'
btn-comment-and-close
'
)
||
$this
.
hasClass
(
'
btn-comment-and-reopen
'
))
{
e
.
preventDefault
();
e
.
stopImmediatePropagation
();
_this
.
submitNoteForm
(
$this
.
closest
(
'
form
'
),
$this
);
}
}
});
};
MergeRequest
.
prototype
.
submitNoteForm
=
function
(
form
,
$button
)
{
var
noteText
;
noteText
=
form
.
find
(
"
textarea.js-note-text
"
).
val
();
if
(
noteText
.
trim
().
length
>
0
)
{
form
.
submit
();
$button
.
data
(
'
submitted
'
,
true
);
return
$button
.
trigger
(
'
click
'
);
}
};
MergeRequest
.
prototype
.
initCommitMessageListeners
=
function
()
{
$
(
document
).
on
(
'
click
'
,
'
a.js-with-description-link
'
,
function
(
e
)
{
var
textarea
=
$
(
'
textarea.js-commit-message
'
);
e
.
preventDefault
();
});
}
}
// Local jQuery finder
MergeRequest
.
prototype
.
$
=
function
(
selector
)
{
return
this
.
$el
.
find
(
selector
);
};
MergeRequest
.
prototype
.
initTabs
=
function
()
{
if
(
window
.
mrTabs
)
{
window
.
mrTabs
.
unbindEvents
();
}
window
.
mrTabs
=
new
MergeRequestTabs
(
this
.
opts
);
};
MergeRequest
.
prototype
.
showAllCommits
=
function
()
{
this
.
$
(
'
.first-commits
'
).
remove
();
return
this
.
$
(
'
.all-commits
'
).
removeClass
(
'
hide
'
);
};
MergeRequest
.
prototype
.
initMRBtnListeners
=
function
()
{
var
_this
;
_this
=
this
;
return
$
(
'
a.btn-close, a.btn-reopen
'
).
on
(
'
click
'
,
function
(
e
)
{
var
$this
,
shouldSubmit
;
$this
=
$
(
this
);
shouldSubmit
=
$this
.
hasClass
(
'
btn-comment
'
);
if
(
shouldSubmit
&&
$this
.
data
(
'
submitted
'
))
{
return
;
}
textarea
.
val
(
textarea
.
data
(
'
messageWithDescription
'
));
$
(
'
.js-with-description-hint
'
).
hide
();
$
(
'
.js-without-description-hint
'
).
show
();
});
if
(
this
.
closeReopenReportToggle
)
this
.
closeReopenReportToggle
.
setDisable
();
$
(
document
).
on
(
'
click
'
,
'
a.js-without-description-link
'
,
function
(
e
)
{
var
textarea
=
$
(
'
textarea.js-commit-message
'
);
if
(
shouldSubmit
)
{
if
(
$this
.
hasClass
(
'
btn-comment-and-close
'
)
||
$this
.
hasClass
(
'
btn-comment-and-reopen
'
))
{
e
.
preventDefault
();
e
.
stopImmediatePropagation
();
textarea
.
val
(
textarea
.
data
(
'
messageWithoutDescription
'
));
$
(
'
.js-with-description-hint
'
).
show
();
$
(
'
.js-without-description-hint
'
).
hide
();
});
};
MergeRequest
.
prototype
.
updateStatusText
=
function
(
classToRemove
,
classToAdd
,
newStatusText
)
{
$
(
'
.detail-page-header .status-box
'
)
.
removeClass
(
classToRemove
)
.
addClass
(
classToAdd
)
.
find
(
'
span
'
)
.
text
(
newStatusText
);
};
MergeRequest
.
prototype
.
decreaseCounter
=
function
(
by
=
1
)
{
const
$el
=
$
(
'
.nav-links .js-merge-counter
'
);
const
count
=
Math
.
max
((
parseInt
(
$el
.
text
().
replace
(
/
[^\d]
/
,
''
),
10
)
-
by
),
0
);
$el
.
text
(
addDelimiter
(
count
));
};
MergeRequest
.
prototype
.
hideCloseButton
=
function
()
{
const
el
=
document
.
querySelector
(
'
.merge-request .js-issuable-actions
'
);
const
closeDropdownItem
=
el
.
querySelector
(
'
li.close-item
'
);
if
(
closeDropdownItem
)
{
closeDropdownItem
.
classList
.
add
(
'
hidden
'
);
// Selects the next dropdown item
el
.
querySelector
(
'
li.report-item
'
).
click
();
}
else
{
// No dropdown just hide the Close button
el
.
querySelector
(
'
.btn-close
'
).
classList
.
add
(
'
hidden
'
);
_this
.
submitNoteForm
(
$this
.
closest
(
'
form
'
),
$this
);
}
// Dropdown for mobile screen
el
.
querySelector
(
'
li.js-close-item
'
).
classList
.
add
(
'
hidden
'
);
};
return
MergeRequest
;
})();
}).
call
(
window
);
}
});
};
MergeRequest
.
prototype
.
submitNoteForm
=
function
(
form
,
$button
)
{
var
noteText
;
noteText
=
form
.
find
(
"
textarea.js-note-text
"
).
val
();
if
(
noteText
.
trim
().
length
>
0
)
{
form
.
submit
();
$button
.
data
(
'
submitted
'
,
true
);
return
$button
.
trigger
(
'
click
'
);
}
};
MergeRequest
.
prototype
.
initCommitMessageListeners
=
function
()
{
$
(
document
).
on
(
'
click
'
,
'
a.js-with-description-link
'
,
function
(
e
)
{
var
textarea
=
$
(
'
textarea.js-commit-message
'
);
e
.
preventDefault
();
textarea
.
val
(
textarea
.
data
(
'
messageWithDescription
'
));
$
(
'
.js-with-description-hint
'
).
hide
();
$
(
'
.js-without-description-hint
'
).
show
();
});
$
(
document
).
on
(
'
click
'
,
'
a.js-without-description-link
'
,
function
(
e
)
{
var
textarea
=
$
(
'
textarea.js-commit-message
'
);
e
.
preventDefault
();
textarea
.
val
(
textarea
.
data
(
'
messageWithoutDescription
'
));
$
(
'
.js-with-description-hint
'
).
show
();
$
(
'
.js-without-description-hint
'
).
hide
();
});
};
MergeRequest
.
prototype
.
updateStatusText
=
function
(
classToRemove
,
classToAdd
,
newStatusText
)
{
$
(
'
.detail-page-header .status-box
'
)
.
removeClass
(
classToRemove
)
.
addClass
(
classToAdd
)
.
find
(
'
span
'
)
.
text
(
newStatusText
);
};
MergeRequest
.
prototype
.
decreaseCounter
=
function
(
by
=
1
)
{
const
$el
=
$
(
'
.nav-links .js-merge-counter
'
);
const
count
=
Math
.
max
((
parseInt
(
$el
.
text
().
replace
(
/
[^\d]
/
,
''
),
10
)
-
by
),
0
);
$el
.
text
(
addDelimiter
(
count
));
};
MergeRequest
.
prototype
.
hideCloseButton
=
function
()
{
const
el
=
document
.
querySelector
(
'
.merge-request .js-issuable-actions
'
);
const
closeDropdownItem
=
el
.
querySelector
(
'
li.close-item
'
);
if
(
closeDropdownItem
)
{
closeDropdownItem
.
classList
.
add
(
'
hidden
'
);
// Selects the next dropdown item
el
.
querySelector
(
'
li.report-item
'
).
click
();
}
else
{
// No dropdown just hide the Close button
el
.
querySelector
(
'
.btn-close
'
).
classList
.
add
(
'
hidden
'
);
}
// Dropdown for mobile screen
el
.
querySelector
(
'
li.js-close-item
'
).
classList
.
add
(
'
hidden
'
);
};
export
default
MergeRequest
;
app/assets/javascripts/merge_request_tabs.js
View file @
d072c18d
/* eslint-disable no-new, class-methods-use-this */
/* global notes */
import
Cookies
from
'
js-cookie
'
;
import
Flash
from
'
./flash
'
;
...
...
@@ -16,6 +15,7 @@ import initDiscussionTab from './image_diff/init_discussion_tab';
import
Diff
from
'
./diff
'
;
import
{
localTimeAgo
}
from
'
./lib/utils/datetime_utility
'
;
import
syntaxHighlight
from
'
./syntax_highlight
'
;
import
Notes
from
'
./notes
'
;
/* eslint-disable max-len */
// MergeRequestTabs
...
...
@@ -324,7 +324,7 @@ export default class MergeRequestTabs {
if
(
anchor
&&
anchor
.
length
>
0
)
{
const
notesContent
=
anchor
.
closest
(
'
.notes_content
'
);
const
lineType
=
notesContent
.
hasClass
(
'
new
'
)
?
'
new
'
:
'
old
'
;
notes
.
toggleDiffNote
({
Notes
.
instance
.
toggleDiffNote
({
target
:
anchor
,
lineType
,
forceShow
:
true
,
...
...
app/assets/javascripts/notes.js
View file @
d072c18d
...
...
@@ -37,6 +37,12 @@ const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
const
REGEX_QUICK_ACTIONS
=
/^
\/\w
+.*$/gm
;
export
default
class
Notes
{
static
initialize
(
notes_url
,
note_ids
,
last_fetched_at
,
view
,
enableGFM
=
true
)
{
if
(
!
this
.
instance
)
{
this
.
instance
=
new
Notes
(
notes_url
,
note_ids
,
last_fetched_at
,
view
,
enableGFM
);
}
}
constructor
(
notes_url
,
note_ids
,
last_fetched_at
,
view
,
enableGFM
=
true
)
{
this
.
updateTargetButtons
=
this
.
updateTargetButtons
.
bind
(
this
);
this
.
updateComment
=
this
.
updateComment
.
bind
(
this
);
...
...
app/assets/javascripts/repo/components/repo_preview.vue
View file @
d072c18d
<
script
>
/* global LineHighlighter */
import
{
mapGetters
}
from
'
vuex
'
;
import
LineHighlighter
from
'
../../line_highlighter
'
;
import
syntaxHighlight
from
'
../../syntax_highlight
'
;
export
default
{
...
...
app/assets/javascripts/right_sidebar.js
View file @
d072c18d
...
...
@@ -3,226 +3,228 @@
import
_
from
'
underscore
'
;
import
Cookies
from
'
js-cookie
'
;
(
function
()
{
this
.
Sidebar
=
(
function
()
{
function
Sidebar
(
currentUser
)
{
this
.
toggleTodo
=
this
.
toggleTodo
.
bind
(
this
);
this
.
sidebar
=
$
(
'
aside
'
);
this
.
removeListeners
();
this
.
addEventListeners
();
function
Sidebar
(
currentUser
)
{
this
.
toggleTodo
=
this
.
toggleTodo
.
bind
(
this
);
this
.
sidebar
=
$
(
'
aside
'
);
this
.
removeListeners
();
this
.
addEventListeners
();
}
Sidebar
.
initialize
=
function
(
currentUser
)
{
if
(
!
this
.
instance
)
{
this
.
instance
=
new
Sidebar
(
currentUser
);
}
};
Sidebar
.
prototype
.
removeListeners
=
function
()
{
this
.
sidebar
.
off
(
'
click
'
,
'
.sidebar-collapsed-icon
'
);
this
.
sidebar
.
off
(
'
hidden.gl.dropdown
'
);
$
(
'
.dropdown
'
).
off
(
'
loading.gl.dropdown
'
);
$
(
'
.dropdown
'
).
off
(
'
loaded.gl.dropdown
'
);
$
(
document
).
off
(
'
click
'
,
'
.js-sidebar-toggle
'
);
};
Sidebar
.
prototype
.
addEventListeners
=
function
()
{
const
$document
=
$
(
document
);
this
.
sidebar
.
on
(
'
click
'
,
'
.sidebar-collapsed-icon
'
,
this
,
this
.
sidebarCollapseClicked
);
this
.
sidebar
.
on
(
'
hidden.gl.dropdown
'
,
this
,
this
.
onSidebarDropdownHidden
);
$
(
'
.dropdown
'
).
on
(
'
loading.gl.dropdown
'
,
this
.
sidebarDropdownLoading
);
$
(
'
.dropdown
'
).
on
(
'
loaded.gl.dropdown
'
,
this
.
sidebarDropdownLoaded
);
$document
.
on
(
'
click
'
,
'
.js-sidebar-toggle
'
,
this
.
sidebarToggleClicked
);
return
$
(
document
).
off
(
'
click
'
,
'
.js-issuable-todo
'
).
on
(
'
click
'
,
'
.js-issuable-todo
'
,
this
.
toggleTodo
);
};
Sidebar
.
prototype
.
sidebarToggleClicked
=
function
(
e
,
triggered
)
{
var
$allGutterToggleIcons
,
$this
,
$thisIcon
;
e
.
preventDefault
();
$this
=
$
(
this
);
$thisIcon
=
$this
.
find
(
'
i
'
);
$allGutterToggleIcons
=
$
(
'
.js-sidebar-toggle i
'
);
if
(
$thisIcon
.
hasClass
(
'
fa-angle-double-right
'
))
{
$allGutterToggleIcons
.
removeClass
(
'
fa-angle-double-right
'
).
addClass
(
'
fa-angle-double-left
'
);
$
(
'
aside.right-sidebar
'
).
removeClass
(
'
right-sidebar-expanded
'
).
addClass
(
'
right-sidebar-collapsed
'
);
$
(
'
.layout-page
'
).
removeClass
(
'
right-sidebar-expanded
'
).
addClass
(
'
right-sidebar-collapsed
'
);
}
else
{
$allGutterToggleIcons
.
removeClass
(
'
fa-angle-double-left
'
).
addClass
(
'
fa-angle-double-right
'
);
$
(
'
aside.right-sidebar
'
).
removeClass
(
'
right-sidebar-collapsed
'
).
addClass
(
'
right-sidebar-expanded
'
);
$
(
'
.layout-page
'
).
removeClass
(
'
right-sidebar-collapsed
'
).
addClass
(
'
right-sidebar-expanded
'
);
if
(
gl
.
lazyLoader
)
gl
.
lazyLoader
.
loadCheck
();
}
if
(
!
triggered
)
{
Cookies
.
set
(
"
collapsed_gutter
"
,
$
(
'
.right-sidebar
'
).
hasClass
(
'
right-sidebar-collapsed
'
));
}
};
Sidebar
.
prototype
.
toggleTodo
=
function
(
e
)
{
var
$btnText
,
$this
,
$todoLoading
,
ajaxType
,
url
;
$this
=
$
(
e
.
currentTarget
);
ajaxType
=
$this
.
attr
(
'
data-delete-path
'
)
?
'
DELETE
'
:
'
POST
'
;
if
(
$this
.
attr
(
'
data-delete-path
'
))
{
url
=
""
+
(
$this
.
attr
(
'
data-delete-path
'
));
}
else
{
url
=
""
+
(
$this
.
data
(
'
url
'
));
}
$this
.
tooltip
(
'
hide
'
);
return
$
.
ajax
({
url
:
url
,
type
:
ajaxType
,
dataType
:
'
json
'
,
data
:
{
issuable_id
:
$this
.
data
(
'
issuable-id
'
),
issuable_type
:
$this
.
data
(
'
issuable-type
'
)
},
beforeSend
:
(
function
(
_this
)
{
return
function
()
{
$
(
'
.js-issuable-todo
'
).
disable
()
.
addClass
(
'
is-loading
'
);
};
})(
this
)
}).
done
((
function
(
_this
)
{
return
function
(
data
)
{
return
_this
.
todoUpdateDone
(
data
);
};
})(
this
));
};
Sidebar
.
prototype
.
todoUpdateDone
=
function
(
data
)
{
const
deletePath
=
data
.
delete_path
?
data
.
delete_path
:
null
;
const
attrPrefix
=
deletePath
?
'
mark
'
:
'
todo
'
;
const
$todoBtns
=
$
(
'
.js-issuable-todo
'
);
$
(
document
).
trigger
(
'
todo:toggle
'
,
data
.
count
);
$todoBtns
.
each
((
i
,
el
)
=>
{
const
$el
=
$
(
el
);
const
$elText
=
$el
.
find
(
'
.js-issuable-todo-inner
'
);
$el
.
removeClass
(
'
is-loading
'
)
.
enable
()
.
attr
(
'
aria-label
'
,
$el
.
data
(
`
${
attrPrefix
}
-text`
))
.
attr
(
'
data-delete-path
'
,
deletePath
)
.
attr
(
'
title
'
,
$el
.
data
(
`
${
attrPrefix
}
-text`
));
if
(
$el
.
hasClass
(
'
has-tooltip
'
))
{
$el
.
tooltip
(
'
fixTitle
'
);
}
Sidebar
.
prototype
.
removeListeners
=
function
()
{
this
.
sidebar
.
off
(
'
click
'
,
'
.sidebar-collapsed-icon
'
);
this
.
sidebar
.
off
(
'
hidden.gl.dropdown
'
);
$
(
'
.dropdown
'
).
off
(
'
loading.gl.dropdown
'
);
$
(
'
.dropdown
'
).
off
(
'
loaded.gl.dropdown
'
);
$
(
document
).
off
(
'
click
'
,
'
.js-sidebar-toggle
'
);
};
Sidebar
.
prototype
.
addEventListeners
=
function
()
{
const
$document
=
$
(
document
);
this
.
sidebar
.
on
(
'
click
'
,
'
.sidebar-collapsed-icon
'
,
this
,
this
.
sidebarCollapseClicked
);
this
.
sidebar
.
on
(
'
hidden.gl.dropdown
'
,
this
,
this
.
onSidebarDropdownHidden
);
$
(
'
.dropdown
'
).
on
(
'
loading.gl.dropdown
'
,
this
.
sidebarDropdownLoading
);
$
(
'
.dropdown
'
).
on
(
'
loaded.gl.dropdown
'
,
this
.
sidebarDropdownLoaded
);
$document
.
on
(
'
click
'
,
'
.js-sidebar-toggle
'
,
this
.
sidebarToggleClicked
);
return
$
(
document
).
off
(
'
click
'
,
'
.js-issuable-todo
'
).
on
(
'
click
'
,
'
.js-issuable-todo
'
,
this
.
toggleTodo
);
};
Sidebar
.
prototype
.
sidebarToggleClicked
=
function
(
e
,
triggered
)
{
var
$allGutterToggleIcons
,
$this
,
$thisIcon
;
e
.
preventDefault
();
$this
=
$
(
this
);
$thisIcon
=
$this
.
find
(
'
i
'
);
$allGutterToggleIcons
=
$
(
'
.js-sidebar-toggle i
'
);
if
(
$thisIcon
.
hasClass
(
'
fa-angle-double-right
'
))
{
$allGutterToggleIcons
.
removeClass
(
'
fa-angle-double-right
'
).
addClass
(
'
fa-angle-double-left
'
);
$
(
'
aside.right-sidebar
'
).
removeClass
(
'
right-sidebar-expanded
'
).
addClass
(
'
right-sidebar-collapsed
'
);
$
(
'
.layout-page
'
).
removeClass
(
'
right-sidebar-expanded
'
).
addClass
(
'
right-sidebar-collapsed
'
);
}
else
{
$allGutterToggleIcons
.
removeClass
(
'
fa-angle-double-left
'
).
addClass
(
'
fa-angle-double-right
'
);
$
(
'
aside.right-sidebar
'
).
removeClass
(
'
right-sidebar-collapsed
'
).
addClass
(
'
right-sidebar-expanded
'
);
$
(
'
.layout-page
'
).
removeClass
(
'
right-sidebar-collapsed
'
).
addClass
(
'
right-sidebar-expanded
'
);
if
(
gl
.
lazyLoader
)
gl
.
lazyLoader
.
loadCheck
();
}
if
(
!
triggered
)
{
Cookies
.
set
(
"
collapsed_gutter
"
,
$
(
'
.right-sidebar
'
).
hasClass
(
'
right-sidebar-collapsed
'
));
}
};
Sidebar
.
prototype
.
toggleTodo
=
function
(
e
)
{
var
$btnText
,
$this
,
$todoLoading
,
ajaxType
,
url
;
$this
=
$
(
e
.
currentTarget
);
ajaxType
=
$this
.
attr
(
'
data-delete-path
'
)
?
'
DELETE
'
:
'
POST
'
;
if
(
$this
.
attr
(
'
data-delete-path
'
))
{
url
=
""
+
(
$this
.
attr
(
'
data-delete-path
'
));
}
else
{
url
=
""
+
(
$this
.
data
(
'
url
'
));
}
$this
.
tooltip
(
'
hide
'
);
return
$
.
ajax
({
url
:
url
,
type
:
ajaxType
,
dataType
:
'
json
'
,
data
:
{
issuable_id
:
$this
.
data
(
'
issuable-id
'
),
issuable_type
:
$this
.
data
(
'
issuable-type
'
)
},
beforeSend
:
(
function
(
_this
)
{
return
function
()
{
$
(
'
.js-issuable-todo
'
).
disable
()
.
addClass
(
'
is-loading
'
);
};
})(
this
)
}).
done
((
function
(
_this
)
{
return
function
(
data
)
{
return
_this
.
todoUpdateDone
(
data
);
};
})(
this
));
};
Sidebar
.
prototype
.
todoUpdateDone
=
function
(
data
)
{
const
deletePath
=
data
.
delete_path
?
data
.
delete_path
:
null
;
const
attrPrefix
=
deletePath
?
'
mark
'
:
'
todo
'
;
const
$todoBtns
=
$
(
'
.js-issuable-todo
'
);
$
(
document
).
trigger
(
'
todo:toggle
'
,
data
.
count
);
$todoBtns
.
each
((
i
,
el
)
=>
{
const
$el
=
$
(
el
);
const
$elText
=
$el
.
find
(
'
.js-issuable-todo-inner
'
);
$el
.
removeClass
(
'
is-loading
'
)
.
enable
()
.
attr
(
'
aria-label
'
,
$el
.
data
(
`
${
attrPrefix
}
-text`
))
.
attr
(
'
data-delete-path
'
,
deletePath
)
.
attr
(
'
title
'
,
$el
.
data
(
`
${
attrPrefix
}
-text`
));
if
(
$el
.
hasClass
(
'
has-tooltip
'
))
{
$el
.
tooltip
(
'
fixTitle
'
);
}
if
(
$el
.
data
(
`
${
attrPrefix
}
-icon`
))
{
$elText
.
html
(
$el
.
data
(
`
${
attrPrefix
}
-icon`
));
}
else
{
$elText
.
text
(
$el
.
data
(
`
${
attrPrefix
}
-text`
));
}
});
};
Sidebar
.
prototype
.
sidebarDropdownLoading
=
function
(
e
)
{
var
$loading
,
$sidebarCollapsedIcon
,
i
,
img
;
$sidebarCollapsedIcon
=
$
(
this
).
closest
(
'
.block
'
).
find
(
'
.sidebar-collapsed-icon
'
);
img
=
$sidebarCollapsedIcon
.
find
(
'
img
'
);
i
=
$sidebarCollapsedIcon
.
find
(
'
i
'
);
$loading
=
$
(
'
<i class="fa fa-spinner fa-spin"></i>
'
);
if
(
img
.
length
)
{
img
.
before
(
$loading
);
return
img
.
hide
();
}
else
if
(
i
.
length
)
{
i
.
before
(
$loading
);
return
i
.
hide
();
}
};
Sidebar
.
prototype
.
sidebarDropdownLoaded
=
function
(
e
)
{
var
$sidebarCollapsedIcon
,
i
,
img
;
$sidebarCollapsedIcon
=
$
(
this
).
closest
(
'
.block
'
).
find
(
'
.sidebar-collapsed-icon
'
);
img
=
$sidebarCollapsedIcon
.
find
(
'
img
'
);
$sidebarCollapsedIcon
.
find
(
'
i.fa-spin
'
).
remove
();
i
=
$sidebarCollapsedIcon
.
find
(
'
i
'
);
if
(
img
.
length
)
{
return
img
.
show
();
}
else
{
return
i
.
show
();
}
};
Sidebar
.
prototype
.
sidebarCollapseClicked
=
function
(
e
)
{
var
$block
,
sidebar
;
if
(
$
(
e
.
currentTarget
).
hasClass
(
'
dont-change-state
'
))
{
return
;
}
sidebar
=
e
.
data
;
e
.
preventDefault
();
$block
=
$
(
this
).
closest
(
'
.block
'
);
return
sidebar
.
openDropdown
(
$block
);
};
Sidebar
.
prototype
.
openDropdown
=
function
(
blockOrName
)
{
var
$block
;
$block
=
_
.
isString
(
blockOrName
)
?
this
.
getBlock
(
blockOrName
)
:
blockOrName
;
if
(
!
this
.
isOpen
())
{
this
.
setCollapseAfterUpdate
(
$block
);
this
.
toggleSidebar
(
'
open
'
);
}
// Wait for the sidebar to trigger('click') open
// so it doesn't cause our dropdown to close preemptively
setTimeout
(()
=>
{
$block
.
find
(
'
.js-sidebar-dropdown-toggle
'
).
trigger
(
'
click
'
);
});
};
Sidebar
.
prototype
.
setCollapseAfterUpdate
=
function
(
$block
)
{
$block
.
addClass
(
'
collapse-after-update
'
);
return
$
(
'
.layout-page
'
).
addClass
(
'
with-overlay
'
);
};
Sidebar
.
prototype
.
onSidebarDropdownHidden
=
function
(
e
)
{
var
$block
,
sidebar
;
sidebar
=
e
.
data
;
e
.
preventDefault
();
$block
=
$
(
e
.
target
).
closest
(
'
.block
'
);
return
sidebar
.
sidebarDropdownHidden
(
$block
);
};
Sidebar
.
prototype
.
sidebarDropdownHidden
=
function
(
$block
)
{
if
(
$block
.
hasClass
(
'
collapse-after-update
'
))
{
$block
.
removeClass
(
'
collapse-after-update
'
);
$
(
'
.layout-page
'
).
removeClass
(
'
with-overlay
'
);
return
this
.
toggleSidebar
(
'
hide
'
);
}
};
Sidebar
.
prototype
.
triggerOpenSidebar
=
function
()
{
return
this
.
sidebar
.
find
(
'
.js-sidebar-toggle
'
).
trigger
(
'
click
'
);
};
Sidebar
.
prototype
.
toggleSidebar
=
function
(
action
)
{
if
(
action
==
null
)
{
action
=
'
toggle
'
;
}
if
(
action
===
'
toggle
'
)
{
this
.
triggerOpenSidebar
();
}
if
(
action
===
'
open
'
)
{
if
(
!
this
.
isOpen
())
{
this
.
triggerOpenSidebar
();
}
}
if
(
action
===
'
hide
'
)
{
if
(
this
.
isOpen
())
{
return
this
.
triggerOpenSidebar
();
}
}
};
if
(
$el
.
data
(
`
${
attrPrefix
}
-icon`
))
{
$elText
.
html
(
$el
.
data
(
`
${
attrPrefix
}
-icon`
));
}
else
{
$elText
.
text
(
$el
.
data
(
`
${
attrPrefix
}
-text`
));
}
});
};
Sidebar
.
prototype
.
sidebarDropdownLoading
=
function
(
e
)
{
var
$loading
,
$sidebarCollapsedIcon
,
i
,
img
;
$sidebarCollapsedIcon
=
$
(
this
).
closest
(
'
.block
'
).
find
(
'
.sidebar-collapsed-icon
'
);
img
=
$sidebarCollapsedIcon
.
find
(
'
img
'
);
i
=
$sidebarCollapsedIcon
.
find
(
'
i
'
);
$loading
=
$
(
'
<i class="fa fa-spinner fa-spin"></i>
'
);
if
(
img
.
length
)
{
img
.
before
(
$loading
);
return
img
.
hide
();
}
else
if
(
i
.
length
)
{
i
.
before
(
$loading
);
return
i
.
hide
();
}
};
Sidebar
.
prototype
.
sidebarDropdownLoaded
=
function
(
e
)
{
var
$sidebarCollapsedIcon
,
i
,
img
;
$sidebarCollapsedIcon
=
$
(
this
).
closest
(
'
.block
'
).
find
(
'
.sidebar-collapsed-icon
'
);
img
=
$sidebarCollapsedIcon
.
find
(
'
img
'
);
$sidebarCollapsedIcon
.
find
(
'
i.fa-spin
'
).
remove
();
i
=
$sidebarCollapsedIcon
.
find
(
'
i
'
);
if
(
img
.
length
)
{
return
img
.
show
();
}
else
{
return
i
.
show
();
}
};
Sidebar
.
prototype
.
sidebarCollapseClicked
=
function
(
e
)
{
var
$block
,
sidebar
;
if
(
$
(
e
.
currentTarget
).
hasClass
(
'
dont-change-state
'
))
{
return
;
}
sidebar
=
e
.
data
;
e
.
preventDefault
();
$block
=
$
(
this
).
closest
(
'
.block
'
);
return
sidebar
.
openDropdown
(
$block
);
};
Sidebar
.
prototype
.
openDropdown
=
function
(
blockOrName
)
{
var
$block
;
$block
=
_
.
isString
(
blockOrName
)
?
this
.
getBlock
(
blockOrName
)
:
blockOrName
;
if
(
!
this
.
isOpen
())
{
this
.
setCollapseAfterUpdate
(
$block
);
this
.
toggleSidebar
(
'
open
'
);
}
// Wait for the sidebar to trigger('click') open
// so it doesn't cause our dropdown to close preemptively
setTimeout
(()
=>
{
$block
.
find
(
'
.js-sidebar-dropdown-toggle
'
).
trigger
(
'
click
'
);
});
};
Sidebar
.
prototype
.
setCollapseAfterUpdate
=
function
(
$block
)
{
$block
.
addClass
(
'
collapse-after-update
'
);
return
$
(
'
.layout-page
'
).
addClass
(
'
with-overlay
'
);
};
Sidebar
.
prototype
.
onSidebarDropdownHidden
=
function
(
e
)
{
var
$block
,
sidebar
;
sidebar
=
e
.
data
;
e
.
preventDefault
();
$block
=
$
(
e
.
target
).
closest
(
'
.block
'
);
return
sidebar
.
sidebarDropdownHidden
(
$block
);
};
Sidebar
.
prototype
.
sidebarDropdownHidden
=
function
(
$block
)
{
if
(
$block
.
hasClass
(
'
collapse-after-update
'
))
{
$block
.
removeClass
(
'
collapse-after-update
'
);
$
(
'
.layout-page
'
).
removeClass
(
'
with-overlay
'
);
return
this
.
toggleSidebar
(
'
hide
'
);
}
};
Sidebar
.
prototype
.
triggerOpenSidebar
=
function
()
{
return
this
.
sidebar
.
find
(
'
.js-sidebar-toggle
'
).
trigger
(
'
click
'
);
};
Sidebar
.
prototype
.
toggleSidebar
=
function
(
action
)
{
if
(
action
==
null
)
{
action
=
'
toggle
'
;
}
if
(
action
===
'
toggle
'
)
{
this
.
triggerOpenSidebar
();
}
if
(
action
===
'
open
'
)
{
if
(
!
this
.
isOpen
())
{
this
.
triggerOpenSidebar
();
}
}
if
(
action
===
'
hide
'
)
{
if
(
this
.
isOpen
())
{
return
this
.
triggerOpenSidebar
();
}
}
};
Sidebar
.
prototype
.
isOpen
=
function
()
{
return
this
.
sidebar
.
is
(
'
.right-sidebar-expanded
'
);
};
Sidebar
.
prototype
.
isOpen
=
function
()
{
return
this
.
sidebar
.
is
(
'
.right-sidebar-expanded
'
);
};
Sidebar
.
prototype
.
getBlock
=
function
(
name
)
{
return
this
.
sidebar
.
find
(
"
.block.
"
+
name
);
};
Sidebar
.
prototype
.
getBlock
=
function
(
name
)
{
return
this
.
sidebar
.
find
(
"
.block.
"
+
name
);
};
return
Sidebar
;
})();
}).
call
(
window
);
export
default
Sidebar
;
app/assets/javascripts/shortcuts_issuable.js
View file @
d072c18d
/* global Mousetrap */
/* global sidebar */
import
_
from
'
underscore
'
;
import
'
mousetrap
'
;
import
Sidebar
from
'
./right_sidebar
'
;
import
ShortcutsNavigation
from
'
./shortcuts_navigation
'
;
import
{
CopyAsGFM
}
from
'
./behaviors/copy_as_gfm
'
;
...
...
@@ -69,7 +69,7 @@ export default class ShortcutsIssuable extends ShortcutsNavigation {
}
static
openSidebarDropdown
(
name
)
{
sidebar
.
openDropdown
(
name
);
Sidebar
.
instance
.
openDropdown
(
name
);
return
false
;
}
}
app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js
View file @
d072c18d
...
...
@@ -6,7 +6,7 @@ Vue.use(VueResource);
export
default
class
MRWidgetService
{
constructor
(
endpoints
)
{
this
.
mergeResource
=
Vue
.
resource
(
endpoints
.
mergePath
);
this
.
mergeCheckResource
=
Vue
.
resource
(
endpoints
.
statusPath
);
this
.
mergeCheckResource
=
Vue
.
resource
(
`
${
endpoints
.
statusPath
}
?serializer=widget`
);
this
.
cancelAutoMergeResource
=
Vue
.
resource
(
endpoints
.
cancelAutoMergePath
);
this
.
removeWIPResource
=
Vue
.
resource
(
endpoints
.
removeWIPPath
);
this
.
removeSourceBranchResource
=
Vue
.
resource
(
endpoints
.
sourceBranchPath
);
...
...
app/controllers/projects/merge_requests_controller.rb
View file @
d072c18d
...
...
@@ -133,7 +133,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
.
new
(
project
,
current_user
,
wip_event:
'unwip'
)
.
execute
(
@merge_request
)
render
json:
serialize
r
.
represen
t
(
@merge_request
)
render
json:
serialize
_widge
t
(
@merge_request
)
end
def
commit_change_content
...
...
@@ -149,7 +149,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
.
new
(
@project
,
current_user
)
.
cancel
(
@merge_request
)
render
json:
serialize
r
.
represen
t
(
@merge_request
)
render
json:
serialize
_widge
t
(
@merge_request
)
end
def
merge
...
...
@@ -313,6 +313,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
end
def
serialize_widget
(
merge_request
)
serializer
.
represent
(
merge_request
,
serializer:
'widget'
)
end
def
serializer
MergeRequestSerializer
.
new
(
current_user:
current_user
,
project:
merge_request
.
project
)
end
...
...
app/helpers/issuables_helper.rb
View file @
d072c18d
...
...
@@ -32,7 +32,7 @@ module IssuablesHelper
end
end
def
serialize_issuable
(
issuable
)
def
serialize_issuable
(
issuable
,
serializer:
nil
)
serializer_klass
=
case
issuable
when
Issue
IssueSerializer
...
...
@@ -42,7 +42,7 @@ module IssuablesHelper
serializer_klass
.
new
(
current_user:
current_user
,
project:
issuable
.
project
)
.
represent
(
issuable
)
.
represent
(
issuable
,
serializer:
serializer
)
.
to_json
end
...
...
app/serializers/issuable_entity.rb
View file @
d072c18d
...
...
@@ -3,14 +3,6 @@ class IssuableEntity < Grape::Entity
expose
:id
expose
:iid
expose
:author_id
expose
:description
expose
:lock_version
expose
:milestone_id
expose
:title
expose
:updated_by_id
expose
:created_at
expose
:updated_at
expose
:milestone
,
using:
API
::
Entities
::
Milestone
expose
:labels
,
using:
LabelEntity
end
app/serializers/issuable_sidebar_entity.rb
View file @
d072c18d
class
IssuableSidebarEntity
<
Grape
::
Entity
include
TimeTrackableEntity
include
RequestAwareEntity
prepend
::
EE
::
IssuableSidebarEntity
...
...
@@ -9,9 +10,4 @@ class IssuableSidebarEntity < Grape::Entity
expose
:subscribed
do
|
issuable
|
issuable
.
subscribed?
(
request
.
current_user
,
issuable
.
project
)
end
expose
:time_estimate
expose
:total_time_spent
expose
:human_time_estimate
expose
:human_total_time_spent
end
app/serializers/issue_entity.rb
View file @
d072c18d
...
...
@@ -2,7 +2,15 @@ class IssueEntity < IssuableEntity
include
TimeTrackableEntity
expose
:state
expose
:milestone_id
expose
:updated_by_id
expose
:created_at
expose
:updated_at
expose
:deleted_at
expose
:milestone
,
using:
API
::
Entities
::
Milestone
expose
:labels
,
using:
LabelEntity
expose
:lock_version
expose
:author_id
expose
:confidential
expose
:discussion_locked
expose
:assignees
,
using:
API
::
Entities
::
UserBasic
...
...
app/serializers/merge_request_serializer.rb
View file @
d072c18d
class
MergeRequestSerializer
<
BaseSerializer
# This overrided method takes care of which entity should be used
# to serialize the `merge_request` based on `
basic
` key in `opts` param.
# to serialize the `merge_request` based on `
serializer
` key in `opts` param.
# Hence, `entity` doesn't need to be declared on the class scope.
def
represent
(
merge_request
,
opts
=
{})
entity
=
case
opts
[
:serializer
]
when
'basic'
,
'sidebar'
MergeRequestBasicEntity
else
MergeRequestEntity
when
'widget'
MergeRequest
Widget
Entity
end
super
(
merge_request
,
opts
,
entity
)
...
...
app/serializers/merge_request_entity.rb
→
app/serializers/merge_request_
widget_
entity.rb
View file @
d072c18d
class
MergeRequestEntity
<
IssuableEntity
include
TimeTrackableEntity
prepend
::
EE
::
MergeRequestEntity
class
MergeRequestWidgetEntity
<
IssuableEntity
prepend
::
EE
::
MergeRequestWidgetEntity
expose
:state
expose
:deleted_at
expose
:in_progress_merge_commit_sha
expose
:merge_commit_sha
expose
:merge_error
...
...
app/views/projects/merge_requests/show.html.haml
View file @
d072c18d
...
...
@@ -21,7 +21,7 @@
-# haml-lint:disable InlineJavaScript
:javascript
window
.
gl
=
window
.
gl
||
{};
window
.
gl
.
mrWidgetData
=
#{
serialize_issuable
(
@merge_request
)
}
window
.
gl
.
mrWidgetData
=
#{
serialize_issuable
(
@merge_request
,
serializer:
'widget'
)
}
// Append static, server-generated data not included in merge request entity (EE-Only)
// Object.assign would be useful here, but it blows up Phantom.js in tests
...
...
app/views/shared/empty_states/_issues.html.haml
View file @
d072c18d
...
...
@@ -8,16 +8,17 @@
=
image_tag
'illustrations/issues.svg'
.col-xs-12
.text-content
-
if
has_button
&&
current_user
-
if
current_user
%h4
=
_
(
"The Issue Tracker is the place to add things that need to be improved or solved in a project"
)
%p
=
_
(
"Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
)
.text-center
-
if
project_select_button
=
render
'shared/new_project_item_select'
,
path:
'issues/new'
,
label:
'New issue'
,
type: :issues
-
else
=
link_to
'New issue'
,
button_path
,
class:
'btn btn-success'
,
title:
'New issue'
,
id:
'new_issue_link'
-
if
has_button
.text-center
-
if
project_select_button
=
render
'shared/new_project_item_select'
,
path:
'issues/new'
,
label:
'New issue'
,
type: :issues
-
else
=
link_to
'New issue'
,
button_path
,
class:
'btn btn-success'
,
title:
'New issue'
,
id:
'new_issue_link'
-
else
%h4
.text-center
=
_
(
"There are no issues to show"
)
%p
...
...
changelogs/unreleased/osw-isolate-mr-widget-exposed-attributes.yml
0 → 100644
View file @
d072c18d
---
title
:
Stop sending milestone and labels data over the wire for MR widget requests
merge_request
:
author
:
type
:
performance
doc/articles/laravel_with_gitlab_and_envoy/index.md
View file @
d072c18d
...
...
@@ -502,8 +502,8 @@ stages:
unit_test
:
stage
:
test
script
:
-
composer install
-
cp .env.example .env
-
composer install
-
php artisan key:generate
-
php artisan migrate
-
vendor/bin/phpunit
...
...
ee/app/serializers/ee/merge_request_entity.rb
→
ee/app/serializers/ee/merge_request_
widget_
entity.rb
View file @
d072c18d
module
EE
module
MergeRequestEntity
module
MergeRequest
Widget
Entity
extend
ActiveSupport
::
Concern
prepended
do
...
...
qa/qa/ee.rb
View file @
d072c18d
...
...
@@ -16,18 +16,18 @@ module QA
end
end
module
Scenario
module
Factory
autoload
:License
,
'qa/ee/factory/license'
module
Geo
autoload
:Node
,
'qa/ee/
scenario
/geo/node'
autoload
:Node
,
'qa/ee/
factory
/geo/node'
end
end
module
Scenario
module
Test
autoload
:Geo
,
'qa/ee/scenario/test/geo'
end
module
License
autoload
:Add
,
'qa/ee/scenario/license/add'
end
end
end
end
qa/qa/ee/
scenario
/geo/node.rb
→
qa/qa/ee/
factory
/geo/node.rb
View file @
d072c18d
module
QA
module
EE
module
Scenario
module
Factory
module
Geo
class
Node
<
QA
::
Scenario
::
Templat
e
class
Node
<
QA
::
Factory
::
Bas
e
attr_accessor
:address
def
perform
def
fabricate!
QA
::
Page
::
Main
::
Login
.
act
{
sign_in_using_credentials
}
QA
::
Page
::
Main
::
Menu
.
act
{
go_to_admin_area
}
QA
::
Page
::
Admin
::
Menu
.
act
{
go_to_geo_nodes
}
...
...
qa/qa/ee/factory/license.rb
0 → 100644
View file @
d072c18d
module
QA
module
EE
module
Factory
class
License
<
QA
::
Factory
::
Base
def
fabricate!
(
license
)
QA
::
Page
::
Main
::
Login
.
act
{
sign_in_using_credentials
}
QA
::
Page
::
Main
::
Menu
.
act
{
go_to_admin_area
}
QA
::
Page
::
Admin
::
Menu
.
act
{
go_to_license
}
EE
::
Page
::
Admin
::
License
.
act
(
license
)
do
|
key
|
add_new_license
(
key
)
if
no_license?
end
QA
::
Page
::
Main
::
Menu
.
act
{
sign_out
}
end
end
end
end
end
qa/qa/ee/scenario/license/add.rb
deleted
100644 → 0
View file @
458f54dc
module
QA
module
EE
module
Scenario
module
License
class
Add
<
QA
::
Scenario
::
Template
def
perform
(
license
)
QA
::
Page
::
Main
::
Login
.
act
{
sign_in_using_credentials
}
QA
::
Page
::
Main
::
Menu
.
act
{
go_to_admin_area
}
QA
::
Page
::
Admin
::
Menu
.
act
{
go_to_license
}
EE
::
Page
::
Admin
::
License
.
act
(
license
)
do
|
key
|
add_new_license
(
key
)
if
no_license?
end
QA
::
Page
::
Main
::
Menu
.
act
{
sign_out
}
end
end
end
end
end
end
qa/qa/ee/scenario/test/geo.rb
View file @
d072c18d
...
...
@@ -39,32 +39,26 @@ module QA
end
def
add_license
# TODO EE license to Runtime.license, gitlab-org/gitlab-qa#86
#
puts
'Adding GitLab EE license ...'
QA
::
Runtime
::
Browser
.
visit
(
:geo_primary
,
QA
::
Page
::
Main
::
Login
)
do
Scenario
::
License
::
Add
.
perform
(
ENV
[
'EE_LICENSE'
])
Factory
::
License
.
fabricate!
(
ENV
[
'EE_LICENSE'
])
end
end
def
enable_hashed_storage
# TODO, Factory::HashedStorage - gitlab-org/gitlab-qa#86
#
puts
'Enabling hashed repository storage setting ...'
QA
::
Runtime
::
Browser
.
visit
(
:geo_primary
,
QA
::
Page
::
Main
::
Login
)
do
QA
::
Scenario
::
Gitlab
::
Admin
::
HashedStorage
.
perform
(
:enabled
)
QA
::
Factory
::
Settings
::
HashedStorage
.
fabricate!
(
:enabled
)
end
end
def
add_secondary_node
# TODO, Factory::Geo::Node - gitlab-org/gitlab-qa#86
#
puts
'Adding new Geo secondary node ...'
QA
::
Runtime
::
Browser
.
visit
(
:geo_primary
,
QA
::
Page
::
Main
::
Login
)
do
Scenario
::
Geo
::
Node
.
perform
do
|
node
|
Factory
::
Geo
::
Node
.
fabricate!
do
|
node
|
node
.
address
=
QA
::
Runtime
::
Scenario
.
geo_secondary_address
end
end
...
...
qa/qa/ee/strategy.rb
View file @
d072c18d
...
...
@@ -11,7 +11,7 @@ module QA
return
unless
ENV
[
'EE_LICENSE'
]
QA
::
Runtime
::
Browser
.
visit
(
:gitlab
,
QA
::
Page
::
Main
::
Login
)
do
EE
::
Scenario
::
License
::
Add
.
perform
(
ENV
[
'EE_LICENSE'
])
EE
::
Factory
::
License
.
fabricate!
(
ENV
[
'EE_LICENSE'
])
end
end
end
...
...
qa/qa/specs/features/ee/geo/replication_spec.rb
View file @
d072c18d
...
...
@@ -4,9 +4,9 @@ module QA
Runtime
::
Browser
.
visit
(
:geo_primary
,
QA
::
Page
::
Main
::
Login
)
do
Page
::
Main
::
Login
.
act
{
sign_in_using_credentials
}
Scenario
::
Gitlab
::
Project
::
Create
.
perform
do
|
scenario
|
scenario
.
name
=
'geo-project'
scenario
.
description
=
'Geo test project'
Factory
::
Resource
::
Project
.
fabricate!
do
|
project
|
project
.
name
=
'geo-project'
project
.
description
=
'Geo test project'
end
geo_project_name
=
Page
::
Project
::
Show
.
act
{
project_name
}
...
...
spec/controllers/projects/merge_requests_controller_spec.rb
View file @
d072c18d
...
...
@@ -91,11 +91,11 @@ describe Projects::MergeRequestsController do
end
end
context
'with
out basic
serializer param'
do
it
'renders
the merge request in the json format
'
do
go
(
format: :json
)
context
'with
widget
serializer param'
do
it
'renders
widget MR entity as json
'
do
go
(
serializer:
'widget'
,
format: :json
)
expect
(
response
).
to
match_response_schema
(
'entities/merge_request'
)
expect
(
response
).
to
match_response_schema
(
'entities/merge_request
_widget
'
)
end
end
end
...
...
spec/ee/spec/serializers/epic_entity_spec.rb
View file @
d072c18d
...
...
@@ -10,8 +10,7 @@ describe EpicEntity do
subject
{
described_class
.
new
(
resource
,
request:
request
).
as_json
}
it
'has Issuable attributes'
do
expect
(
subject
).
to
include
(
:id
,
:iid
,
:author_id
,
:description
,
:lock_version
,
:milestone_id
,
:title
,
:updated_by_id
,
:created_at
,
:updated_at
,
:milestone
,
:labels
)
expect
(
subject
).
to
include
(
:id
,
:iid
,
:description
,
:title
)
end
it
'has epic specific attributes'
do
...
...
spec/ee/spec/serializers/merge_request_entity_spec.rb
→
spec/ee/spec/serializers/merge_request_
widget_
entity_spec.rb
View file @
d072c18d
require
'spec_helper'
describe
MergeRequestEntity
do
describe
MergeRequest
Widget
Entity
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
:project
,
:repository
}
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
,
target_project:
project
)
}
...
...
spec/features/issues_spec.rb
View file @
d072c18d
...
...
@@ -8,765 +8,789 @@ describe 'Issues' do
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:project
,
:public
)
}
before
do
sign_in
(
user
)
user2
=
create
(
:user
)
project
.
team
<<
[[
user
,
user2
],
:developer
]
end
describe
'while user is signed out'
do
describe
'empty state'
do
it
'user sees empty state'
do
visit
project_issues_path
(
project
)
describe
'Edit issue'
do
let!
(
:issue
)
do
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
)
expect
(
page
).
to
have_content
(
'Register / Sign In'
)
expect
(
page
).
to
have_content
(
'The Issue Tracker is the place to add things that need to be improved or solved in a project.'
)
expect
(
page
).
to
have_content
(
'You can register or sign in to create issues for this project.'
)
end
end
end
describe
'while user is signed in'
do
before
do
visit
edit_project_issue_path
(
project
,
issue
)
find
(
'.js-zen-enter'
).
click
end
it
'opens new issue popup'
do
expect
(
page
).
to
have_content
(
"Issue #
#{
issue
.
iid
}
"
)
end
end
sign_in
(
user
)
user2
=
create
(
:user
)
describe
'Editing issue assignee'
do
let!
(
:issue
)
do
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
)
project
.
team
<<
[[
user
,
user2
],
:developer
]
end
it
'allows user to select unassigned'
,
:js
do
visit
edit_project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_content
"Assignee
#{
user
.
name
}
"
describe
'empty state'
do
it
'user sees empty state'
do
visit
project_issues_path
(
project
)
first
(
'.js-user-search'
).
click
click_link
'Unassigned'
expect
(
page
).
to
have_content
(
'The Issue Tracker is the place to add things that need to be improved or solved in a project'
)
expect
(
page
).
to
have_content
(
'Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.'
)
expect
(
page
).
to
have_content
(
'New issue'
)
end
end
click_button
'Save changes'
describe
'Edit issue'
do
let!
(
:issue
)
do
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
)
end
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
'No assignee - assign yourself'
before
do
visit
edit_project_issue_path
(
project
,
issue
)
find
(
'.js-zen-enter'
).
click
end
expect
(
issue
.
reload
.
assignees
).
to
be_empty
it
'opens new issue popup'
do
expect
(
page
).
to
have_content
(
"Issue #
#{
issue
.
iid
}
"
)
end
end
end
describe
'due date'
,
:js
do
context
'on new form'
do
before
do
visit
new_project_issue_path
(
project
)
describe
'Editing issue assignee'
do
let!
(
:issue
)
do
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
)
end
it
'saves with due date'
do
date
=
Date
.
today
.
at_beginning_of_month
fill_in
'issue_title'
,
with:
'bug 345'
fill_in
'issue_description'
,
with:
'bug description'
find
(
'#issuable-due-date'
).
click
it
'allows user to select unassigned'
,
:js
do
visit
edit_project_issue_path
(
project
,
issue
)
page
.
within
'.pika-single'
do
click_button
date
.
day
end
expect
(
page
).
to
have_content
"Assignee
#{
user
.
name
}
"
expect
(
find
(
'#issuable-due-date'
).
value
).
to
eq
date
.
to_s
first
(
'.js-user-search'
).
click
click_link
'Unassigned'
click_button
'S
ubmit issue
'
click_button
'S
ave changes
'
page
.
within
'.issuable-sidebar'
do
expect
(
page
).
to
have_content
date
.
to_s
(
:medium
)
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
'No assignee - assign yourself'
end
expect
(
issue
.
reload
.
assignees
).
to
be_empty
end
end
context
'on edit form'
do
let
(
:issue
)
{
create
(
:issue
,
author:
user
,
project:
project
,
due_date:
Date
.
today
.
at_beginning_of_month
.
to_s
)
}
describe
'due date'
,
:js
do
context
'on new form'
do
before
do
visit
new_project_issue_path
(
project
)
end
before
do
visit
edit_project_issue_path
(
project
,
issue
)
end
it
'saves with due date'
do
date
=
Date
.
today
.
at_beginning_of_month
it
'saves with due date'
do
date
=
Date
.
today
.
at_beginning_of_month
fill_in
'issue_title'
,
with:
'bug 345'
fill_in
'issue_description'
,
with:
'bug description'
find
(
'#issuable-due-date'
).
click
expect
(
find
(
'#issuable-due-date'
).
value
).
to
eq
date
.
to_s
page
.
within
'.pika-single'
do
click_button
date
.
day
end
date
=
date
.
tomorrow
expect
(
find
(
'#issuable-due-date'
).
value
).
to
eq
date
.
to_s
fill_in
'issue_title'
,
with:
'bug 345'
fill_in
'issue_description'
,
with:
'bug description'
find
(
'#issuable-due-date'
).
click
click_button
'Submit issue'
page
.
within
'.pika-single'
do
click_button
date
.
day
page
.
within
'.issuable-sidebar'
do
expect
(
page
).
to
have_content
date
.
to_s
(
:medium
)
end
end
end
expect
(
find
(
'#issuable-due-date'
).
value
).
to
eq
date
.
to_s
click_button
'Save changes'
context
'on edit form'
do
let
(
:issue
)
{
create
(
:issue
,
author:
user
,
project:
project
,
due_date:
Date
.
today
.
at_beginning_of_month
.
to_s
)
}
page
.
within
'.issuable-sidebar'
do
expect
(
page
).
to
have_content
date
.
to_s
(
:medium
)
before
do
visit
edit_project_issue_path
(
project
,
issue
)
end
end
it
'warns about version conflict
'
do
issue
.
update
(
title:
"New title"
)
it
'saves with due date
'
do
date
=
Date
.
today
.
at_beginning_of_month
fill_in
'issue_title'
,
with:
'bug 345'
fill_in
'issue_description'
,
with:
'bug description'
expect
(
find
(
'#issuable-due-date'
).
value
).
to
eq
date
.
to_s
click_button
'Save changes'
date
=
date
.
tomorrow
expect
(
page
).
to
have_content
'Someone edited the issue the same time you did'
end
end
end
fill_in
'issue_title'
,
with:
'bug 345'
fill_in
'issue_description'
,
with:
'bug description'
find
(
'#issuable-due-date'
).
click
describe
'Issue info
'
do
it
'links to current issue in breadcrubs'
do
issue
=
create
(
:issue
,
project:
project
)
page
.
within
'.pika-single
'
do
click_button
date
.
day
end
visit
project_issue_path
(
project
,
issue
)
expect
(
find
(
'#issuable-due-date'
).
value
).
to
eq
date
.
to_s
expect
(
find
(
'.breadcrumbs-sub-title a'
)[
:href
]).
to
end_with
(
issue_path
(
issue
))
end
click_button
'Save changes'
it
'excludes award_emoji from comment count'
do
issue
=
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
,
title:
'foobar'
)
create
(
:award_emoji
,
awardable:
issue
)
page
.
within
'.issuable-sidebar'
do
expect
(
page
).
to
have_content
date
.
to_s
(
:medium
)
end
end
visit
project_issues_path
(
project
,
assignee_id:
user
.
id
)
it
'warns about version conflict'
do
issue
.
update
(
title:
"New title"
)
expect
(
page
).
to
have_content
'foobar'
expect
(
page
.
all
(
'.no-comments'
).
first
.
text
).
to
eq
"0"
end
fill_in
'issue_title'
,
with:
'bug 345'
fill_in
'issue_description'
,
with:
'bug description'
it
'shows weight on issue row'
do
create
(
:issue
,
author:
user
,
project:
project
,
weight:
2
)
click_button
'Save changes'
visit
project_issues_path
(
project
)
page
.
within
(
first
(
'.issuable-info'
))
do
expect
(
page
).
to
have_selector
(
'.fa-balance-scale'
)
expect
(
page
).
to
have_content
(
2
)
expect
(
page
).
to
have_content
'Someone edited the issue the same time you did'
end
end
end
end
describe
'Filter issue'
do
before
do
%w(foobar barbaz gitlab)
.
each
do
|
title
|
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
,
title:
title
)
end
describe
'Issue info'
do
it
'links to current issue in breadcrubs'
do
issue
=
create
(
:issue
,
project:
project
)
@issue
=
Issue
.
find_by
(
title:
'foobar'
)
@issue
.
milestone
=
create
(
:milestone
,
project:
project
)
@issue
.
assignees
=
[]
@issue
.
save
end
visit
project_issue_path
(
project
,
issue
)
let
(
:issue
)
{
@issue
}
expect
(
find
(
'.breadcrumbs-sub-title a'
)[
:href
]).
to
end_with
(
issue_path
(
issue
))
end
it
'allows filtering by issues with no specified assignee'
do
visit
project_issues_path
(
project
,
assignee_id:
IssuableFinder
::
NONE
)
it
'excludes award_emoji from comment count'
do
issue
=
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
,
title:
'foobar'
)
create
(
:award_emoji
,
awardable:
issue
)
expect
(
page
).
to
have_content
'foobar'
expect
(
page
).
not_to
have_content
'barbaz'
expect
(
page
).
not_to
have_content
'gitlab'
end
visit
project_issues_path
(
project
,
assignee_id:
user
.
id
)
it
'allows filtering by a specified assignee'
do
visit
project_issues_path
(
project
,
assignee_id:
user
.
id
)
expect
(
page
).
to
have_content
'foobar'
expect
(
page
.
all
(
'.no-comments'
).
first
.
text
).
to
eq
"0"
end
expect
(
page
).
not_to
have_content
'foobar'
expect
(
page
).
to
have_content
'barbaz'
expect
(
page
).
to
have_content
'gitlab'
end
end
it
'shows weight on issue row'
do
create
(
:issue
,
author:
user
,
project:
project
,
weight:
2
)
describe
'filter issue'
do
titles
=
%w[foo bar baz]
titles
.
each_with_index
do
|
title
,
index
|
let!
(
title
.
to_sym
)
do
create
(
:issue
,
title:
title
,
project:
project
,
created_at:
Time
.
now
-
(
index
*
60
))
visit
project_issues_path
(
project
)
page
.
within
(
first
(
'.issuable-info'
))
do
expect
(
page
).
to
have_selector
(
'.fa-balance-scale'
)
expect
(
page
).
to
have_content
(
2
)
end
end
end
let
(
:newer_due_milestone
)
{
create
(
:milestone
,
due_date:
'2013-12-11'
)
}
let
(
:later_due_milestone
)
{
create
(
:milestone
,
due_date:
'2013-12-12'
)
}
it
'sorts by newest'
do
visit
project_issues_path
(
project
,
sort:
sort_value_created_date
)
describe
'Filter issue'
do
before
do
%w(foobar barbaz gitlab)
.
each
do
|
title
|
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
,
title:
title
)
end
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
last_issue
).
to
include
(
'baz'
)
end
@issue
=
Issue
.
find_by
(
title:
'foobar'
)
@issue
.
milestone
=
create
(
:milestone
,
project:
project
)
@issue
.
assignees
=
[]
@issue
.
save
end
it
'sorts by most recently updated'
do
baz
.
updated_at
=
Time
.
now
+
100
baz
.
save
visit
project_issues_path
(
project
,
sort:
sort_value_recently_updated
)
let
(
:issue
)
{
@issue
}
expect
(
first_issue
).
to
include
(
'baz'
)
end
it
'allows filtering by issues with no specified assignee'
do
visit
project_issues_path
(
project
,
assignee_id:
IssuableFinder
::
NONE
)
describe
'sorting by due date'
do
before
do
foo
.
update
(
due_date:
1
.
day
.
from_now
)
bar
.
update
(
due_date:
6
.
days
.
from_now
)
expect
(
page
).
to
have_content
'foobar'
expect
(
page
).
not_to
have_content
'barbaz'
expect
(
page
).
not_to
have_content
'gitlab'
end
it
'
sorts by due dat
e'
do
visit
project_issues_path
(
project
,
sort:
sort_value_due_date
)
it
'
allows filtering by a specified assigne
e'
do
visit
project_issues_path
(
project
,
assignee_id:
user
.
id
)
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
page
).
not_to
have_content
'foobar'
expect
(
page
).
to
have_content
'barbaz'
expect
(
page
).
to
have_content
'gitlab'
end
end
it
'sorts by due date by excluding nil due dates'
do
bar
.
update
(
due_date:
nil
)
describe
'filter issue'
do
titles
=
%w[foo bar baz]
titles
.
each_with_index
do
|
title
,
index
|
let!
(
title
.
to_sym
)
do
create
(
:issue
,
title:
title
,
project:
project
,
created_at:
Time
.
now
-
(
index
*
60
))
end
end
let
(
:newer_due_milestone
)
{
create
(
:milestone
,
due_date:
'2013-12-11'
)
}
let
(
:later_due_milestone
)
{
create
(
:milestone
,
due_date:
'2013-12-12'
)
}
visit
project_issues_path
(
project
,
sort:
sort_value_due_date
)
it
'sorts by newest'
do
visit
project_issues_path
(
project
,
sort:
sort_value_created_date
)
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
last_issue
).
to
include
(
'baz'
)
end
context
'with a filter on labels'
do
let
(
:label
)
{
create
(
:label
,
project:
project
)
}
it
'sorts by most recently updated'
do
baz
.
updated_at
=
Time
.
now
+
100
baz
.
save
visit
project_issues_path
(
project
,
sort:
sort_value_recently_updated
)
expect
(
first_issue
).
to
include
(
'baz'
)
end
describe
'sorting by due date'
do
before
do
create
(
:label_link
,
label:
label
,
target:
foo
)
foo
.
update
(
due_date:
1
.
day
.
from_now
)
bar
.
update
(
due_date:
6
.
days
.
from_now
)
end
it
'sorts by least recently due date by excluding nil due dates'
do
it
'sorts by due date'
do
visit
project_issues_path
(
project
,
sort:
sort_value_due_date
)
expect
(
first_issue
).
to
include
(
'foo'
)
end
it
'sorts by due date by excluding nil due dates'
do
bar
.
update
(
due_date:
nil
)
visit
project_issues_path
(
project
,
label_names:
[
label
.
name
],
sort:
sort_value_due_date_later
)
visit
project_issues_path
(
project
,
sort:
sort_value_due_date
)
expect
(
first_issue
).
to
include
(
'foo'
)
end
end
end
describe
'filtering by due date'
do
before
do
foo
.
update
(
due_date:
1
.
day
.
from_now
)
bar
.
update
(
due_date:
6
.
days
.
from_now
)
end
context
'with a filter on labels'
do
let
(
:label
)
{
create
(
:label
,
project:
project
)
}
before
do
create
(
:label_link
,
label:
label
,
target:
foo
)
end
it
'sorts by least recently due date by excluding nil due dates'
do
bar
.
update
(
due_date:
nil
)
it
'filters by none'
do
visit
project_issues_path
(
project
,
due_date:
Issue
::
NoDueDate
.
name
)
visit
project_issues_path
(
project
,
label_names:
[
label
.
name
],
sort:
sort_value_due_date_later
)
page
.
within
'.issues-holder'
do
expect
(
page
).
not_to
have_content
(
'foo'
)
expect
(
page
).
not_to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
expect
(
first_issue
).
to
include
(
'foo'
)
end
end
end
it
'filters by any'
do
visit
project_issues_path
(
project
,
due_date:
Issue
::
AnyDueDate
.
name
)
describe
'filtering by due date'
do
before
do
foo
.
update
(
due_date:
1
.
day
.
from_now
)
bar
.
update
(
due_date:
6
.
days
.
from_now
)
end
it
'filters by none'
do
visit
project_issues_path
(
project
,
due_date:
Issue
::
NoDueDate
.
name
)
page
.
within
'.issues-holder'
do
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
page
.
within
'.issues-holder'
do
expect
(
page
).
not_to
have_content
(
'foo'
)
expect
(
page
).
not_to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
end
end
it
'filters by any'
do
visit
project_issues_path
(
project
,
due_date:
Issue
::
AnyDueDate
.
name
)
page
.
within
'.issues-holder'
do
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
end
end
end
it
'filters by due this week'
do
foo
.
update
(
due_date:
Date
.
today
.
beginning_of_week
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
.
end_of_week
)
baz
.
update
(
due_date:
Date
.
today
-
8
.
days
)
it
'filters by due this week'
do
foo
.
update
(
due_date:
Date
.
today
.
beginning_of_week
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
.
end_of_week
)
baz
.
update
(
due_date:
Date
.
today
-
8
.
days
)
visit
project_issues_path
(
project
,
due_date:
Issue
::
DueThisWeek
.
name
)
visit
project_issues_path
(
project
,
due_date:
Issue
::
DueThisWeek
.
name
)
page
.
within
'.issues-holder'
do
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
page
.
within
'.issues-holder'
do
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
end
end
end
it
'filters by due this month'
do
foo
.
update
(
due_date:
Date
.
today
.
beginning_of_month
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
.
end_of_month
)
baz
.
update
(
due_date:
Date
.
today
-
50
.
days
)
it
'filters by due this month'
do
foo
.
update
(
due_date:
Date
.
today
.
beginning_of_month
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
.
end_of_month
)
baz
.
update
(
due_date:
Date
.
today
-
50
.
days
)
visit
project_issues_path
(
project
,
due_date:
Issue
::
DueThisMonth
.
name
)
visit
project_issues_path
(
project
,
due_date:
Issue
::
DueThisMonth
.
name
)
page
.
within
'.issues-holder'
do
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
page
.
within
'.issues-holder'
do
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
end
end
end
it
'filters by overdue'
do
foo
.
update
(
due_date:
Date
.
today
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
+
20
.
days
)
baz
.
update
(
due_date:
Date
.
yesterday
)
it
'filters by overdue'
do
foo
.
update
(
due_date:
Date
.
today
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
+
20
.
days
)
baz
.
update
(
due_date:
Date
.
yesterday
)
visit
project_issues_path
(
project
,
due_date:
Issue
::
Overdue
.
name
)
visit
project_issues_path
(
project
,
due_date:
Issue
::
Overdue
.
name
)
page
.
within
'.issues-holder'
do
expect
(
page
).
not_to
have_content
(
'foo'
)
expect
(
page
).
not_to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
page
.
within
'.issues-holder'
do
expect
(
page
).
not_to
have_content
(
'foo'
)
expect
(
page
).
not_to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
end
end
end
end
describe
'sorting by milestone'
do
before
do
foo
.
milestone
=
newer_due_milestone
foo
.
save
bar
.
milestone
=
later_due_milestone
bar
.
save
end
describe
'sorting by milestone'
do
before
do
foo
.
milestone
=
newer_due_milestone
foo
.
save
bar
.
milestone
=
later_due_milestone
bar
.
save
end
it
'sorts by milestone'
do
visit
project_issues_path
(
project
,
sort:
sort_value_milestone
)
it
'sorts by milestone'
do
visit
project_issues_path
(
project
,
sort:
sort_value_milestone
)
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
last_issue
).
to
include
(
'baz'
)
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
last_issue
).
to
include
(
'baz'
)
end
end
end
describe
'combine filter and sort'
do
let
(
:user2
)
{
create
(
:user
)
}
describe
'combine filter and sort'
do
let
(
:user2
)
{
create
(
:user
)
}
before
do
foo
.
assignees
<<
user2
foo
.
save
bar
.
assignees
<<
user2
bar
.
save
end
before
do
foo
.
assignees
<<
user2
foo
.
save
bar
.
assignees
<<
user2
bar
.
save
end
it
'sorts with a filter applied'
do
visit
project_issues_path
(
project
,
sort:
sort_value_created_date
,
assignee_id:
user2
.
id
)
it
'sorts with a filter applied'
do
visit
project_issues_path
(
project
,
sort:
sort_value_created_date
,
assignee_id:
user2
.
id
)
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
last_issue
).
to
include
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
expect
(
first_issue
).
to
include
(
'foo'
)
expect
(
last_issue
).
to
include
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
end
end
end
end
describe
'when I want to reset my incoming email token'
do
let
(
:project1
)
{
create
(
:project
,
namespace:
user
.
namespace
)
}
let!
(
:issue
)
{
create
(
:issue
,
project:
project1
)
}
describe
'when I want to reset my incoming email token'
do
let
(
:project1
)
{
create
(
:project
,
namespace:
user
.
namespace
)
}
let!
(
:issue
)
{
create
(
:issue
,
project:
project1
)
}
before
do
stub_incoming_email_setting
(
enabled:
true
,
address:
"p+%{key}@gl.ab"
)
project1
.
team
<<
[
user
,
:master
]
visit
namespace_project_issues_path
(
user
.
namespace
,
project1
)
end
before
do
stub_incoming_email_setting
(
enabled:
true
,
address:
"p+%{key}@gl.ab"
)
project1
.
team
<<
[
user
,
:master
]
visit
namespace_project_issues_path
(
user
.
namespace
,
project1
)
end
it
'changes incoming email address token'
,
:js
do
find
(
'.issuable-email-modal-btn'
).
click
previous_token
=
find
(
'input#issuable_email'
).
value
find
(
'.incoming-email-token-reset'
).
click
it
'changes incoming email address token'
,
:js
do
find
(
'.issuable-email-modal-btn'
).
click
previous_token
=
find
(
'input#issuable_email'
).
value
find
(
'.incoming-email-token-reset'
).
click
wait_for_requests
wait_for_requests
expect
(
page
).
to
have_no_field
(
'issuable_email'
,
with:
previous_token
)
new_token
=
project1
.
new_issuable_address
(
user
.
reload
,
'issue'
)
expect
(
page
).
to
have_field
(
'issuable_email'
,
with:
new_token
)
expect
(
page
).
to
have_no_field
(
'issuable_email'
,
with:
previous_token
)
new_token
=
project1
.
new_issuable_address
(
user
.
reload
,
'issue'
)
expect
(
page
).
to
have_field
(
'issuable_email'
,
with:
new_token
)
end
end
end
describe
'update labels from issue#show'
,
:js
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
,
assignees:
[
user
])
}
let!
(
:label
)
{
create
(
:label
,
project:
project
)
}
describe
'update labels from issue#show'
,
:js
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
,
assignees:
[
user
])
}
let!
(
:label
)
{
create
(
:label
,
project:
project
)
}
before
do
visit
project_issue_path
(
project
,
issue
)
end
before
do
visit
project_issue_path
(
project
,
issue
)
end
it
'will not send ajax request when no data is changed'
do
page
.
within
'.labels'
do
click_link
'Edit'
it
'will not send ajax request when no data is changed'
do
page
.
within
'.labels'
do
click_link
'Edit'
find
(
'.dropdown-menu-close'
,
match: :first
).
click
find
(
'.dropdown-menu-close'
,
match: :first
).
click
expect
(
page
).
not_to
have_selector
(
'.block-loading'
)
expect
(
page
).
not_to
have_selector
(
'.block-loading'
)
end
end
end
end
describe
'update assignee from issue#show'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
,
assignees:
[
user
])
}
describe
'update assignee from issue#show'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
,
assignees:
[
user
])
}
context
'by authorized user'
do
it
'allows user to select unassigned'
,
:js
do
visit
project_issue_path
(
project
,
issue
)
context
'by authorized user'
do
it
'allows user to select unassigned'
,
:js
do
visit
project_issue_path
(
project
,
issue
)
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
"
#{
user
.
name
}
"
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
"
#{
user
.
name
}
"
click_link
'Edit'
click_link
'Unassigned'
first
(
'.title'
).
click
expect
(
page
).
to
have_content
'No assignee'
end
click_link
'Edit'
click_link
'Unassigned'
first
(
'.title'
).
click
expect
(
page
).
to
have_content
'No assignee'
end
# wait_for_requests does not work with vue-resource at the moment
sleep
1
# wait_for_requests does not work with vue-resource at the moment
sleep
1
expect
(
issue
.
reload
.
assignees
).
to
be_empty
end
expect
(
issue
.
reload
.
assignees
).
to
be_empty
end
it
'allows user to select an assignee'
,
:js
do
issue2
=
create
(
:issue
,
project:
project
,
author:
user
)
visit
project_issue_path
(
project
,
issue2
)
it
'allows user to select an assignee'
,
:js
do
issue2
=
create
(
:issue
,
project:
project
,
author:
user
)
visit
project_issue_path
(
project
,
issue2
)
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
"No assignee"
end
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
"No assignee"
end
page
.
within
'.assignee'
do
click_link
'Edit'
end
page
.
within
'.assignee'
do
click_link
'Edit'
end
page
.
within
'.dropdown-menu-user'
do
click_link
user
.
name
end
page
.
within
'.dropdown-menu-user'
do
click_link
user
.
name
end
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
user
.
name
page
.
within
(
'.assignee'
)
do
expect
(
page
).
to
have_content
user
.
name
end
end
end
it
'allows user to unselect themselves'
,
:js
do
issue2
=
create
(
:issue
,
project:
project
,
author:
user
)
visit
project_issue_path
(
project
,
issue2
)
it
'allows user to unselect themselves'
,
:js
do
issue2
=
create
(
:issue
,
project:
project
,
author:
user
)
visit
project_issue_path
(
project
,
issue2
)
page
.
within
'.assignee'
do
click_link
'Edit'
click_link
user
.
name
page
.
within
'.assignee'
do
click_link
'Edit'
click_link
user
.
name
find
(
'.dropdown-menu-toggle'
).
click
find
(
'.dropdown-menu-toggle'
).
click
page
.
within
'.value .author'
do
expect
(
page
).
to
have_content
user
.
name
end
page
.
within
'.value .author'
do
expect
(
page
).
to
have_content
user
.
name
end
click_link
'Edit'
click_link
user
.
name
click_link
'Edit'
click_link
user
.
name
find
(
'.dropdown-menu-toggle'
).
click
find
(
'.dropdown-menu-toggle'
).
click
page
.
within
'.value .assign-yourself'
do
expect
(
page
).
to
have_content
"No assignee"
page
.
within
'.value .assign-yourself'
do
expect
(
page
).
to
have_content
"No assignee"
end
end
end
end
end
context
'by unauthorized user'
do
let
(
:guest
)
{
create
(
:user
)
}
context
'by unauthorized user'
do
let
(
:guest
)
{
create
(
:user
)
}
before
do
project
.
team
<<
[[
guest
],
:guest
]
end
before
do
project
.
team
<<
[[
guest
],
:guest
]
end
it
'shows assignee text'
,
:js
do
sign_out
(
:user
)
sign_in
(
guest
)
it
'shows assignee text'
,
:js
do
sign_out
(
:user
)
sign_in
(
guest
)
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_content
issue
.
assignees
.
first
.
name
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_content
issue
.
assignees
.
first
.
name
end
end
end
end
describe
'update weight from issue#show'
,
:js
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
describe
'update weight from issue#show'
,
:js
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
before
do
visit
project_issue_path
(
project
,
issue
)
end
before
do
visit
project_issue_path
(
project
,
issue
)
end
it
'allows user to update to a weight'
do
page
.
within
(
'.weight'
)
do
expect
(
page
).
to
have_content
"None"
click_link
'Edit'
it
'allows user to update to a weight'
do
page
.
within
(
'.weight'
)
do
expect
(
page
).
to
have_content
"None"
click_link
'Edit'
find
(
'.dropdown-content a'
,
text:
'1'
).
click
find
(
'.dropdown-content a'
,
text:
'1'
).
click
page
.
within
(
'.value'
)
do
expect
(
page
).
to
have_content
"1"
page
.
within
(
'.value'
)
do
expect
(
page
).
to
have_content
"1"
end
end
end
end
end
describe
'update milestone from issue#show'
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
)
}
let!
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
describe
'update milestone from issue#show'
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
)
}
let!
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
context
'by authorized user'
do
it
'allows user to select unassigned'
,
:js
do
visit
project_issue_path
(
project
,
issue
)
context
'by authorized user'
do
it
'allows user to select unassigned'
,
:js
do
visit
project_issue_path
(
project
,
issue
)
page
.
within
(
'.milestone'
)
do
expect
(
page
).
to
have_content
"None"
end
page
.
within
(
'.milestone'
)
do
expect
(
page
).
to
have_content
"None"
end
find
(
'.block.milestone .edit-link'
).
click
sleep
2
# wait for ajax stuff to complete
first
(
'.dropdown-content li'
).
click
sleep
2
page
.
within
(
'.milestone'
)
do
expect
(
page
).
to
have_content
'None'
end
find
(
'.block.milestone .edit-link'
).
click
sleep
2
# wait for ajax stuff to complete
first
(
'.dropdown-content li'
).
click
sleep
2
page
.
within
(
'.milestone'
)
do
expect
(
page
).
to
have_content
'None'
expect
(
issue
.
reload
.
milestone
).
to
be_nil
end
expect
(
issue
.
reload
.
milestone
).
to
be_nil
end
it
'allows user to de-select milestone'
,
:js
do
visit
project_issue_path
(
project
,
issue
)
it
'allows user to de-select milestone'
,
:js
do
visit
project_issue_path
(
project
,
issue
)
page
.
within
(
'.milestone'
)
do
click_link
'Edit'
click_link
milestone
.
title
page
.
within
(
'.milestone'
)
do
click_link
'Edit'
click_link
milestone
.
title
page
.
within
'.value'
do
expect
(
page
).
to
have_content
milestone
.
title
end
page
.
within
'.value'
do
expect
(
page
).
to
have_content
milestone
.
title
end
click_link
'Edit'
click_link
milestone
.
title
click_link
'Edit'
click_link
milestone
.
title
page
.
within
'.value'
do
expect
(
page
).
to
have_content
'None'
page
.
within
'.value'
do
expect
(
page
).
to
have_content
'None'
end
end
end
end
end
context
'by unauthorized user'
do
let
(
:guest
)
{
create
(
:user
)
}
context
'by unauthorized user'
do
let
(
:guest
)
{
create
(
:user
)
}
before
do
project
.
team
<<
[
guest
,
:guest
]
issue
.
milestone
=
milestone
issue
.
save
end
before
do
project
.
team
<<
[
guest
,
:guest
]
issue
.
milestone
=
milestone
issue
.
save
end
it
'shows milestone text'
,
:js
do
sign_out
(
:user
)
sign_in
(
guest
)
it
'shows milestone text'
,
:js
do
sign_out
(
:user
)
sign_in
(
guest
)
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_content
milestone
.
title
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_content
milestone
.
title
end
end
end
end
describe
'new issue'
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
describe
'new issue'
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
context
'by unauthenticated user'
do
before
do
sign_out
(
:user
)
end
context
'by unauthenticated user'
do
before
do
sign_out
(
:user
)
end
it
'redirects to signin then back to new issue after signin'
do
visit
project_issues_path
(
project
)
it
'redirects to signin then back to new issue after signin'
do
visit
project_issues_path
(
project
)
page
.
within
'.nav-controls'
do
click_link
'New issue'
end
page
.
within
'.nav-controls'
do
click_link
'New issue'
end
expect
(
current_path
).
to
eq
new_user_session_path
expect
(
current_path
).
to
eq
new_user_session_path
gitlab_sign_in
(
create
(
:user
))
gitlab_sign_in
(
create
(
:user
))
expect
(
current_path
).
to
eq
new_project_issue_path
(
project
)
expect
(
current_path
).
to
eq
new_project_issue_path
(
project
)
end
end
end
context
'dropzone upload file'
,
:js
do
before
do
visit
new_project_issue_path
(
project
)
end
context
'dropzone upload file'
,
:js
do
before
do
visit
new_project_issue_path
(
project
)
end
it
'uploads file when dragging into textarea'
do
dropzone_file
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'banana_sample.gif'
)
it
'uploads file when dragging into textarea'
do
dropzone_file
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'banana_sample.gif'
)
expect
(
page
.
find_field
(
"issue_description"
).
value
).
to
have_content
'banana_sample'
end
expect
(
page
.
find_field
(
"issue_description"
).
value
).
to
have_content
'banana_sample'
end
it
"doesn't add double newline to end of a single attachment markdown"
do
dropzone_file
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'banana_sample.gif'
)
it
"doesn't add double newline to end of a single attachment markdown"
do
dropzone_file
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'banana_sample.gif'
)
expect
(
page
.
find_field
(
"issue_description"
).
value
).
not_to
match
/\n\n$/
end
expect
(
page
.
find_field
(
"issue_description"
).
value
).
not_to
match
/\n\n$/
end
it
"cancels a file upload correctly"
do
slow_requests
do
dropzone_file
([
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'dk.png'
)],
0
,
false
)
it
"cancels a file upload correctly"
do
slow_requests
do
dropzone_file
([
Rails
.
root
.
join
(
'spec'
,
'fixtures'
,
'dk.png'
)],
0
,
false
)
click_button
'Cancel'
end
click_button
'Cancel'
end
expect
(
page
).
to
have_button
(
'Attach a file'
)
expect
(
page
).
not_to
have_button
(
'Cancel'
)
expect
(
page
).
not_to
have_selector
(
'.uploading-progress-container'
,
visible:
true
)
expect
(
page
).
to
have_button
(
'Attach a file'
)
expect
(
page
).
not_to
have_button
(
'Cancel'
)
expect
(
page
).
not_to
have_selector
(
'.uploading-progress-container'
,
visible:
true
)
end
end
end
context
'form filled by URL parameters'
do
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
before
do
project
.
repository
.
create_file
(
user
,
'.gitlab/issue_templates/bug.md'
,
'this is a test "bug" template'
,
message:
'added issue template'
,
branch_name:
'master'
)
context
'form filled by URL parameters'
do
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
visit
new_project_issue_path
(
project
,
issuable_template:
'bug'
)
end
before
do
project
.
repository
.
create_file
(
user
,
'.gitlab/issue_templates/bug.md'
,
'this is a test "bug" template'
,
message:
'added issue template'
,
branch_name:
'master'
)
visit
new_project_issue_path
(
project
,
issuable_template:
'bug'
)
end
it
'fills in template'
do
expect
(
find
(
'.js-issuable-selector .dropdown-toggle-text'
)).
to
have_content
(
'bug'
)
it
'fills in template'
do
expect
(
find
(
'.js-issuable-selector .dropdown-toggle-text'
)).
to
have_content
(
'bug'
)
end
end
end
end
describe
'new issue by email'
do
shared_examples
'show the email in the modal'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
describe
'new issue by email'
do
shared_examples
'show the email in the modal'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
before
do
project
.
issues
<<
issue
stub_incoming_email_setting
(
enabled:
true
,
address:
"p+%{key}@gl.ab"
)
before
do
project
.
issues
<<
issue
stub_incoming_email_setting
(
enabled:
true
,
address:
"p+%{key}@gl.ab"
)
visit
project_issues_path
(
project
)
click_button
(
'Email a new issue'
)
end
visit
project_issues_path
(
project
)
click_button
(
'Email a new issue'
)
end
it
'click the button to show modal for the new email'
do
page
.
within
'#issuable-email-modal'
do
email
=
project
.
new_issuable_address
(
user
,
'issue'
)
it
'click the button to show modal for the new email'
do
page
.
within
'#issuable-email-modal'
do
email
=
project
.
new_issuable_address
(
user
,
'issue'
)
expect
(
page
).
to
have_selector
(
"input[value='
#{
email
}
']"
)
expect
(
page
).
to
have_selector
(
"input[value='
#{
email
}
']"
)
end
end
end
end
context
'with existing issues'
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
)
}
context
'with existing issues'
do
let!
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
)
}
it_behaves_like
'show the email in the modal'
end
it_behaves_like
'show the email in the modal'
end
context
'without existing issues'
do
it_behaves_like
'show the email in the modal'
context
'without existing issues'
do
it_behaves_like
'show the email in the modal'
end
end
end
describe
'due date'
do
context
'update due on issue#show'
,
:js
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
,
assignees:
[
user
])
}
describe
'due date'
do
context
'update due on issue#show'
,
:js
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
user
,
assignees:
[
user
])
}
before
do
visit
project_issue_path
(
project
,
issue
)
end
before
do
visit
project_issue_path
(
project
,
issue
)
end
it
'adds due date to issue'
do
date
=
Date
.
today
.
at_beginning_of_month
+
2
.
days
it
'adds due date to issue'
do
date
=
Date
.
today
.
at_beginning_of_month
+
2
.
days
page
.
within
'.due_date'
do
click_link
'Edit'
page
.
within
'.due_date'
do
click_link
'Edit'
page
.
within
'.pika-single'
do
click_button
date
.
day
end
page
.
within
'.pika-single'
do
click_button
date
.
day
end
wait_for_requests
wait_for_requests
expect
(
find
(
'.value'
).
text
).
to
have_content
date
.
strftime
(
'%b %-d, %Y'
)
expect
(
find
(
'.value'
).
text
).
to
have_content
date
.
strftime
(
'%b %-d, %Y'
)
end
end
end
it
'removes due date from issue'
do
date
=
Date
.
today
.
at_beginning_of_month
+
2
.
days
it
'removes due date from issue'
do
date
=
Date
.
today
.
at_beginning_of_month
+
2
.
days
page
.
within
'.due_date'
do
click_link
'Edit'
page
.
within
'.due_date'
do
click_link
'Edit'
page
.
within
'.pika-single'
do
click_button
date
.
day
end
page
.
within
'.pika-single'
do
click_button
date
.
day
end
wait_for_requests
wait_for_requests
expect
(
page
).
to
have_no_content
'No due date'
expect
(
page
).
to
have_no_content
'No due date'
click_link
'remove due date'
expect
(
page
).
to
have_content
'No due date'
click_link
'remove due date'
expect
(
page
).
to
have_content
'No due date'
end
end
end
end
end
describe
'title issue#show'
,
:js
do
it
'updates the title'
,
:js
do
issue
=
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
,
title:
'new title'
)
describe
'title issue#show'
,
:js
do
it
'updates the title'
,
:js
do
issue
=
create
(
:issue
,
author:
user
,
assignees:
[
user
],
project:
project
,
title:
'new title'
)
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_text
(
"new title"
)
expect
(
page
).
to
have_text
(
"new title"
)
issue
.
update
(
title:
"updated title"
)
issue
.
update
(
title:
"updated title"
)
wait_for_requests
expect
(
page
).
to
have_text
(
"updated title"
)
wait_for_requests
expect
(
page
).
to
have_text
(
"updated title"
)
end
end
end
describe
'confidential issue#show'
,
:js
do
it
'shows confidential sibebar information as confidential and can be turned off'
do
issue
=
create
(
:issue
,
:confidential
,
project:
project
)
describe
'confidential issue#show'
,
:js
do
it
'shows confidential sibebar information as confidential and can be turned off'
do
issue
=
create
(
:issue
,
:confidential
,
project:
project
)
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
to
have_css
(
'.issuable-note-warning'
)
expect
(
find
(
'.issuable-sidebar-item.confidentiality'
)).
to
have_css
(
'.is-active'
)
expect
(
find
(
'.issuable-sidebar-item.confidentiality'
)).
not_to
have_css
(
'.not-active'
)
expect
(
page
).
to
have_css
(
'.issuable-note-warning'
)
expect
(
find
(
'.issuable-sidebar-item.confidentiality'
)).
to
have_css
(
'.is-active'
)
expect
(
find
(
'.issuable-sidebar-item.confidentiality'
)).
not_to
have_css
(
'.not-active'
)
find
(
'.confidential-edit'
).
click
expect
(
page
).
to
have_css
(
'.sidebar-item-warning-message'
)
find
(
'.confidential-edit'
).
click
expect
(
page
).
to
have_css
(
'.sidebar-item-warning-message'
)
within
(
'.sidebar-item-warning-message'
)
do
find
(
'.btn-close'
).
click
end
within
(
'.sidebar-item-warning-message'
)
do
find
(
'.btn-close'
).
click
end
wait_for_requests
wait_for_requests
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
expect
(
page
).
not_to
have_css
(
'.is-active'
)
expect
(
page
).
not_to
have_css
(
'.is-active'
)
end
end
end
end
spec/features/merge_requests/mini_pipeline_graph_spec.rb
View file @
d072c18d
...
...
@@ -15,8 +15,8 @@ feature 'Mini Pipeline Graph', :js do
visit_merge_request
end
def
visit_merge_request
(
format
=
:htm
l
)
visit
project_merge_request_path
(
project
,
merge_request
,
format:
format
)
def
visit_merge_request
(
format
: :html
,
serializer:
ni
l
)
visit
project_merge_request_path
(
project
,
merge_request
,
format:
format
,
serializer:
serializer
)
end
it
'should display a mini pipeline graph'
do
...
...
@@ -33,12 +33,12 @@ feature 'Mini Pipeline Graph', :js do
end
it
'avoids repeated database queries'
do
before
=
ActiveRecord
::
QueryRecorder
.
new
{
visit_merge_request
(
:json
)
}
before
=
ActiveRecord
::
QueryRecorder
.
new
{
visit_merge_request
(
format: :json
,
serializer:
'widget'
)
}
create
(
:ci_build
,
pipeline:
pipeline
,
legacy_artifacts_file:
artifacts_file2
)
create
(
:ci_build
,
pipeline:
pipeline
,
when:
'manual'
)
after
=
ActiveRecord
::
QueryRecorder
.
new
{
visit_merge_request
(
:json
)
}
after
=
ActiveRecord
::
QueryRecorder
.
new
{
visit_merge_request
(
format: :json
,
serializer:
'widget'
)
}
expect
(
before
.
count
).
to
eq
(
after
.
count
)
expect
(
before
.
cached_count
).
to
eq
(
after
.
cached_count
)
...
...
spec/fixtures/api/schemas/entities/merge_request.json
→
spec/fixtures/api/schemas/entities/merge_request
_widget
.json
View file @
d072c18d
...
...
@@ -81,15 +81,15 @@
"target_branch_tree_path"
:
{
"type"
:
"string"
},
"source_branch_path"
:
{
"type"
:
"string"
},
"conflict_resolution_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"cancel_merge_when_pipeline_succeeds_path"
:
{
"type"
:
"string"
},
"create_issue_to_resolve_discussions_path"
:
{
"type"
:
"string"
},
"merge_path"
:
{
"type"
:
"string"
},
"cancel_merge_when_pipeline_succeeds_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"create_issue_to_resolve_discussions_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"merge_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"cherry_pick_in_fork_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"revert_in_fork_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"email_patches_path"
:
{
"type"
:
"string"
},
"plain_diff_path"
:
{
"type"
:
"string"
},
"status_path"
:
{
"type"
:
"string"
},
"new_blob_path"
:
{
"type"
:
"string"
},
"new_blob_path"
:
{
"type"
:
[
"string"
,
"null"
]
},
"merge_check_path"
:
{
"type"
:
"string"
},
"ci_environments_status_path"
:
{
"type"
:
"string"
},
"merge_commit_message_with_description"
:
{
"type"
:
"string"
},
...
...
spec/javascripts/collapsed_sidebar_todo_spec.js
View file @
d072c18d
/* global Sidebar */
/* eslint-disable no-new */
import
_
from
'
underscore
'
;
import
'
~/right_sidebar
'
;
import
Sidebar
from
'
~/right_sidebar
'
;
describe
(
'
Issuable right sidebar collapsed todo toggle
'
,
()
=>
{
const
fixtureName
=
'
issues/open-issue.html.raw
'
;
...
...
spec/javascripts/line_highlighter_spec.js
View file @
d072c18d
/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, jasmine/no-spec-dupes, no-underscore-dangle, max-len */
/* global LineHighlighter */
import
'
~/line_highlighter
'
;
import
LineHighlighter
from
'
~/line_highlighter
'
;
(
function
()
{
describe
(
'
LineHighlighter
'
,
function
()
{
...
...
spec/javascripts/merge_request_notes_spec.js
View file @
d072c18d
/* global Notes */
import
'
autosize
'
;
import
'
~/gl_form
'
;
import
'
~/lib/utils/text_utility
'
;
import
'
~/render_gfm
'
;
import
'
~/render_math
'
;
import
'
~/notes
'
;
import
Notes
from
'
~/notes
'
;
const
upArrowKeyCode
=
38
;
...
...
spec/javascripts/merge_request_spec.js
View file @
d072c18d
/* eslint-disable space-before-function-paren, no-return-assign */
/* global MergeRequest */
import
'
~/merge_request
'
;
import
MergeRequest
from
'
~/merge_request
'
;
import
CloseReopenReportToggle
from
'
~/close_reopen_report_toggle
'
;
import
IssuablesHelper
from
'
~/helpers/issuables_helper
'
;
...
...
spec/javascripts/merge_request_tabs_spec.js
View file @
d072c18d
/* eslint-disable no-var, comma-dangle, object-shorthand */
/* global Notes */
import
*
as
urlUtils
from
'
~/lib/utils/url_utility
'
;
import
MergeRequestTabs
from
'
~/merge_request_tabs
'
;
...
...
@@ -7,7 +6,7 @@ import '~/commit/pipelines/pipelines_bundle';
import
'
~/breakpoints
'
;
import
'
~/lib/utils/common_utils
'
;
import
Diff
from
'
~/diff
'
;
import
'
~/notes
'
;
import
Notes
from
'
~/notes
'
;
import
'
vendor/jquery.scrollTo
'
;
(
function
()
{
...
...
@@ -279,8 +278,8 @@ import 'vendor/jquery.scrollTo';
loadFixtures
(
'
merge_requests/diff_comment.html.raw
'
);
$
(
'
body
'
).
attr
(
'
data-page
'
,
'
projects:merge_requests:show
'
);
window
.
gl
.
ImageFile
=
()
=>
{};
window
.
notes
=
new
Notes
(
''
,
[]);
spyOn
(
window
.
notes
,
'
toggleDiffNote
'
).
and
.
callThrough
();
Notes
.
initialize
(
''
,
[]);
spyOn
(
Notes
.
instance
,
'
toggleDiffNote
'
).
and
.
callThrough
();
});
afterEach
(()
=>
{
...
...
@@ -338,7 +337,7 @@ import 'vendor/jquery.scrollTo';
this
.
class
.
loadDiff
(
'
/foo/bar/merge_requests/1/diffs
'
);
expect
(
noteId
.
length
).
toBeGreaterThan
(
0
);
expect
(
window
.
notes
.
toggleDiffNote
).
toHaveBeenCalledWith
({
expect
(
Notes
.
instance
.
toggleDiffNote
).
toHaveBeenCalledWith
({
target
:
jasmine
.
any
(
Object
),
lineType
:
'
old
'
,
forceShow
:
true
,
...
...
@@ -349,7 +348,7 @@ import 'vendor/jquery.scrollTo';
spyOn
(
urlUtils
,
'
getLocationHash
'
).
and
.
returnValue
(
'
note_something-that-does-not-exist
'
);
this
.
class
.
loadDiff
(
'
/foo/bar/merge_requests/1/diffs
'
);
expect
(
window
.
notes
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
expect
(
Notes
.
instance
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
});
});
...
...
@@ -359,7 +358,7 @@ import 'vendor/jquery.scrollTo';
this
.
class
.
loadDiff
(
'
/foo/bar/merge_requests/1/diffs
'
);
expect
(
noteLineNumId
.
length
).
toBeGreaterThan
(
0
);
expect
(
window
.
notes
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
expect
(
Notes
.
instance
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
});
});
});
...
...
@@ -393,7 +392,7 @@ import 'vendor/jquery.scrollTo';
this
.
class
.
loadDiff
(
'
/foo/bar/merge_requests/1/diffs
'
);
expect
(
noteId
.
length
).
toBeGreaterThan
(
0
);
expect
(
window
.
notes
.
toggleDiffNote
).
toHaveBeenCalledWith
({
expect
(
Notes
.
instance
.
toggleDiffNote
).
toHaveBeenCalledWith
({
target
:
jasmine
.
any
(
Object
),
lineType
:
'
new
'
,
forceShow
:
true
,
...
...
@@ -404,7 +403,7 @@ import 'vendor/jquery.scrollTo';
spyOn
(
urlUtils
,
'
getLocationHash
'
).
and
.
returnValue
(
'
note_something-that-does-not-exist
'
);
this
.
class
.
loadDiff
(
'
/foo/bar/merge_requests/1/diffs
'
);
expect
(
window
.
notes
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
expect
(
Notes
.
instance
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
});
});
...
...
@@ -414,7 +413,7 @@ import 'vendor/jquery.scrollTo';
this
.
class
.
loadDiff
(
'
/foo/bar/merge_requests/1/diffs
'
);
expect
(
noteLineNumId
.
length
).
toBeGreaterThan
(
0
);
expect
(
window
.
notes
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
expect
(
Notes
.
instance
.
toggleDiffNote
).
not
.
toHaveBeenCalled
();
});
});
});
...
...
spec/javascripts/notes_spec.js
View file @
d072c18d
/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */
/* global Notes */
import
*
as
urlUtils
from
'
~/lib/utils/url_utility
'
;
import
'
autosize
'
;
import
'
~/gl_form
'
;
import
'
~/lib/utils/text_utility
'
;
import
'
~/render_gfm
'
;
import
'
~/notes
'
;
import
Notes
from
'
~/notes
'
;
(
function
()
{
window
.
gon
||
(
window
.
gon
=
{});
...
...
spec/javascripts/right_sidebar_spec.js
View file @
d072c18d
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-return-assign, new-cap, vars-on-top, max-len */
/* global Sidebar */
import
'
~/commons/bootstrap
'
;
import
'
~/right_sidebar
'
;
import
Sidebar
from
'
~/right_sidebar
'
;
(
function
()
{
var
$aside
,
$icon
,
$labelsIcon
,
$page
,
$toggle
,
assertSidebarState
;
...
...
spec/serializers/merge_request_serializer_spec.rb
View file @
d072c18d
require
'spec_helper'
describe
MergeRequestSerializer
do
let
(
:user
)
{
build_stubbed
(
:user
)
}
let
(
:merge_request
)
{
build_stubbed
(
:merge_request
)
}
let
(
:serializer
)
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:resource
)
{
create
(
:merge_request
)
}
let
(
:json_entity
)
do
described_class
.
new
(
current_user:
user
)
.
represent
(
resource
,
serializer:
serializer
)
.
with_indifferent_access
end
describe
'#represent'
do
let
(
:opts
)
{
{
serializer:
serializer_entity
}
}
subject
{
serializer
.
represent
(
merge_request
,
serializer:
serializer_entity
)
}
context
'widget merge request serialization'
do
let
(
:serializer
)
{
'widget'
}
context
'when passing basic serializer param'
do
let
(
:serializer_entity
)
{
'basic'
}
it
'matches issue json schema'
do
expect
(
json_entity
).
to
match_schema
(
'entities/merge_request_widget'
)
end
end
it
'calls super class #represent with correct params'
do
expect_any_instance_of
(
BaseSerializer
).
to
receive
(
:represent
)
.
with
(
merge_request
,
opts
,
MergeRequestBasicEntity
)
context
'sidebar merge request serialization'
do
let
(
:serializer
)
{
'sidebar'
}
subject
e
nd
it
'matches basic merge request json schema'
do
e
xpect
(
json_entity
).
to
match_schema
(
'entities/merge_request_basic'
)
end
end
context
'when serializer param is falsy'
do
let
(
:serializer_entity
)
{
nil
}
context
'basic merge request serialization'
do
let
(
:serializer
)
{
'basic'
}
it
'matches basic merge request json schema'
do
expect
(
json_entity
).
to
match_schema
(
'entities/merge_request_basic'
)
end
end
it
'calls super class #represent with correct params'
do
expect_any_instance_of
(
BaseSerializer
).
to
receive
(
:represent
)
.
with
(
merge_request
,
opts
,
MergeRequestEntity
)
context
'no serializer'
do
let
(
:serializer
)
{
nil
}
subject
e
nd
it
'raises an error'
do
e
xpect
{
json_entity
}.
to
raise_error
(
NoMethodError
)
end
end
end
spec/serializers/merge_request_entity_spec.rb
→
spec/serializers/merge_request_
widget_
entity_spec.rb
View file @
d072c18d
require
'spec_helper'
describe
MergeRequestEntity
do
describe
MergeRequest
Widget
Entity
do
let
(
:project
)
{
create
:project
,
:repository
}
let
(
:resource
)
{
create
(
:merge_request
,
source_project:
project
,
target_project:
project
)
}
let
(
:user
)
{
create
(
:user
)
}
...
...
@@ -35,37 +35,6 @@ describe MergeRequestEntity do
end
end
it
'includes issues_links'
do
issues_links
=
subject
[
:issues_links
]
expect
(
issues_links
).
to
include
(
:closing
,
:mentioned_but_not_closing
,
:assign_to_closing
)
end
it
'has Issuable attributes'
do
expect
(
subject
).
to
include
(
:id
,
:iid
,
:author_id
,
:description
,
:lock_version
,
:milestone_id
,
:title
,
:updated_by_id
,
:created_at
,
:updated_at
,
:milestone
,
:labels
)
end
it
'has time estimation attributes'
do
expect
(
subject
).
to
include
(
:time_estimate
,
:total_time_spent
,
:human_time_estimate
,
:human_total_time_spent
)
end
it
'has important MergeRequest attributes'
do
expect
(
subject
).
to
include
(
:state
,
:deleted_at
,
:diff_head_sha
,
:merge_commit_message
,
:has_conflicts
,
:has_ci
,
:merge_path
,
:conflict_resolution_path
,
:cancel_merge_when_pipeline_succeeds_path
,
:create_issue_to_resolve_discussions_path
,
:source_branch_path
,
:target_branch_commits_path
,
:target_branch_tree_path
,
:commits_count
,
:merge_ongoing
,
:ff_only_enabled
,
## EE
:can_push_to_source_branch
,
:approvals_before_merge
,
:squash
,
:rebase_commit_sha
,
:rebase_in_progress
,
:approvals_path
)
end
it
'has email_patches_path'
do
expect
(
subject
[
:email_patches_path
])
.
to
eq
(
"/
#{
resource
.
project
.
full_path
}
/merge_requests/
#{
resource
.
iid
}
.patch"
)
...
...
@@ -120,18 +89,6 @@ describe MergeRequestEntity do
end
end
it
'includes merge_event'
do
create
(
:event
,
:merged
,
author:
user
,
project:
resource
.
project
,
target:
resource
)
expect
(
subject
[
:merge_event
]).
to
include
(
:author
,
:updated_at
)
end
it
'includes closed_event'
do
create
(
:event
,
:closed
,
author:
user
,
project:
resource
.
project
,
target:
resource
)
expect
(
subject
[
:closed_event
]).
to
include
(
:author
,
:updated_at
)
end
describe
'diverged_commits_count'
do
context
'when MR open and its diverging'
do
it
'returns diverged commits count'
do
...
...
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