Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Jérome Perrin
gitlab-ce
Commits
c45e11f1
Commit
c45e11f1
authored
May 03, 2017
by
Jacob Schatz
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'async-milestone-tabs' into 'master'
Load milestone tabs asynchronously See merge request !10919
parents
349e4231
471888d6
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
266 additions
and
43 deletions
+266
-43
app/assets/javascripts/milestone.js
app/assets/javascripts/milestone.js
+57
-19
app/controllers/concerns/milestone_actions.rb
app/controllers/concerns/milestone_actions.rb
+53
-0
app/controllers/groups/milestones_controller.rb
app/controllers/groups/milestones_controller.rb
+3
-1
app/controllers/projects/milestones_controller.rb
app/controllers/projects/milestones_controller.rb
+4
-2
app/helpers/milestones_helper.rb
app/helpers/milestones_helper.rb
+24
-0
app/views/shared/milestones/_tab_loading.html.haml
app/views/shared/milestones/_tab_loading.html.haml
+2
-0
app/views/shared/milestones/_tabs.html.haml
app/views/shared/milestones/_tabs.html.haml
+16
-12
changelogs/unreleased/async-milestone-tabs.yml
changelogs/unreleased/async-milestone-tabs.yml
+4
-0
config/routes/group.rb
config/routes/group.rb
+7
-1
config/routes/project.rb
config/routes/project.rb
+3
-0
features/group/milestones.feature
features/group/milestones.feature
+1
-0
features/project/milestone.feature
features/project/milestone.feature
+0
-8
features/steps/group/milestones.rb
features/steps/group/milestones.rb
+3
-0
features/steps/project/project_milestone.rb
features/steps/project/project_milestone.rb
+3
-0
spec/controllers/groups/milestones_controller_spec.rb
spec/controllers/groups/milestones_controller_spec.rb
+12
-0
spec/controllers/projects/milestones_controller_spec.rb
spec/controllers/projects/milestones_controller_spec.rb
+3
-0
spec/features/milestones/milestones_spec.rb
spec/features/milestones/milestones_spec.rb
+3
-0
spec/support/milestone_tabs_examples.rb
spec/support/milestone_tabs_examples.rb
+68
-0
No files found.
app/assets/javascripts/milestone.js
View file @
c45e11f1
...
...
@@ -19,12 +19,10 @@
});
};
Milestone
.
sortIssues
=
function
(
data
)
{
var
sort_issues_url
;
sort_issues_url
=
location
.
href
+
"
/sort_issues
"
;
Milestone
.
sortIssues
=
function
(
url
,
data
)
{
return
$
.
ajax
({
type
:
"
PUT
"
,
url
:
sort_issues_url
,
url
,
data
:
data
,
success
:
function
(
_data
)
{
return
Milestone
.
successCallback
(
_data
);
...
...
@@ -36,12 +34,10 @@
});
};
Milestone
.
sortMergeRequests
=
function
(
data
)
{
var
sort_mr_url
;
sort_mr_url
=
location
.
href
+
"
/sort_merge_requests
"
;
Milestone
.
sortMergeRequests
=
function
(
url
,
data
)
{
return
$
.
ajax
({
type
:
"
PUT
"
,
url
:
sort_mr_url
,
url
,
data
:
data
,
success
:
function
(
_data
)
{
return
Milestone
.
successCallback
(
_data
);
...
...
@@ -81,42 +77,55 @@
};
function
Milestone
()
{
var
oldMouseStart
;
this
.
issuesSortEndpoint
=
$
(
'
#tab-issues
'
).
data
(
'
sort-endpoint
'
);
this
.
mergeRequestsSortEndpoint
=
$
(
'
#tab-merge-requests
'
).
data
(
'
sort-endpoint
'
);
this
.
bindIssuesSorting
();
this
.
bindMergeRequestSorting
();
this
.
bindTabsSwitching
();
// Load merge request tab if it is active
// merge request tab is active based on different conditions in the backend
this
.
loadTab
(
$
(
'
.js-milestone-tabs .active a
'
));
this
.
loadInitialTab
();
}
Milestone
.
prototype
.
bindIssuesSorting
=
function
()
{
if
(
!
this
.
issuesSortEndpoint
)
return
;
$
(
'
#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed
'
).
each
(
function
(
i
,
el
)
{
this
.
createSortable
(
el
,
{
group
:
'
issue-list
'
,
listEls
:
$
(
'
.issues-sortable-list
'
),
fieldName
:
'
issue
'
,
sortCallback
:
Milestone
.
sortIssues
,
sortCallback
:
(
data
)
=>
{
Milestone
.
sortIssues
(
this
.
issuesSortEndpoint
,
data
);
},
updateCallback
:
Milestone
.
updateIssue
,
});
}.
bind
(
this
));
};
Milestone
.
prototype
.
bindTabsSwitching
=
function
()
{
return
$
(
'
a[data-toggle="tab"]
'
).
on
(
'
show.bs.tab
'
,
function
(
e
)
{
var
currentTabClass
,
previousTabClass
;
currentTabClass
=
$
(
e
.
target
).
data
(
'
show
'
);
previousTabClass
=
$
(
e
.
relatedTarget
).
data
(
'
show
'
);
$
(
previousTabClass
).
hide
();
$
(
currentTabClass
).
removeClass
(
'
hidden
'
);
return
$
(
currentTabClass
).
show
();
return
$
(
'
a[data-toggle="tab"]
'
).
on
(
'
show.bs.tab
'
,
(
e
)
=>
{
const
$target
=
$
(
e
.
target
);
location
.
hash
=
$target
.
attr
(
'
href
'
);
this
.
loadTab
(
$target
);
});
};
Milestone
.
prototype
.
bindMergeRequestSorting
=
function
()
{
if
(
!
this
.
mergeRequestsSortEndpoint
)
return
;
$
(
"
#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed
"
).
each
(
function
(
i
,
el
)
{
this
.
createSortable
(
el
,
{
group
:
'
merge-request-list
'
,
listEls
:
$
(
"
.merge_requests-sortable-list:not(#merge_requests-list-merged)
"
),
fieldName
:
'
merge_request
'
,
sortCallback
:
Milestone
.
sortMergeRequests
,
sortCallback
:
(
data
)
=>
{
Milestone
.
sortMergeRequests
(
this
.
mergeRequestsSortEndpoint
,
data
);
},
updateCallback
:
Milestone
.
updateMergeRequest
,
});
}.
bind
(
this
));
...
...
@@ -169,6 +178,35 @@
});
};
Milestone
.
prototype
.
loadInitialTab
=
function
()
{
const
$target
=
$
(
`.js-milestone-tabs a[href="
${
location
.
hash
}
"]`
);
if
(
$target
.
length
)
{
$target
.
tab
(
'
show
'
);
}
};
Milestone
.
prototype
.
loadTab
=
function
(
$target
)
{
const
endpoint
=
$target
.
data
(
'
endpoint
'
);
const
tabElId
=
$target
.
attr
(
'
href
'
);
if
(
endpoint
&&
!
$target
.
hasClass
(
'
is-loaded
'
))
{
$
.
ajax
({
url
:
endpoint
,
dataType
:
'
JSON
'
,
})
.
fail
(()
=>
new
Flash
(
'
Error loading milestone tab
'
))
.
done
((
data
)
=>
{
$
(
tabElId
).
html
(
data
.
html
);
$target
.
addClass
(
'
is-loaded
'
);
if
(
tabElId
===
'
#tab-merge-requests
'
)
{
this
.
bindMergeRequestSorting
();
}
});
}
};
return
Milestone
;
})();
}).
call
(
window
);
app/controllers/concerns/milestone_actions.rb
0 → 100644
View file @
c45e11f1
module
MilestoneActions
extend
ActiveSupport
::
Concern
def
merge_requests
respond_to
do
|
format
|
format
.
html
{
redirect_to
milestone_redirect_path
}
format
.
json
do
render
json:
tabs_json
(
"shared/milestones/_merge_requests_tab"
,
{
merge_requests:
@milestone
.
merge_requests
,
show_project_name:
true
})
end
end
end
def
participants
respond_to
do
|
format
|
format
.
html
{
redirect_to
milestone_redirect_path
}
format
.
json
do
render
json:
tabs_json
(
"shared/milestones/_participants_tab"
,
{
users:
@milestone
.
participants
})
end
end
end
def
labels
respond_to
do
|
format
|
format
.
html
{
redirect_to
milestone_redirect_path
}
format
.
json
do
render
json:
tabs_json
(
"shared/milestones/_labels_tab"
,
{
labels:
@milestone
.
labels
})
end
end
end
private
def
tabs_json
(
partial
,
data
=
{})
{
html:
view_to_html_string
(
partial
,
data
)
}
end
def
milestone_redirect_path
if
@project
namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
@milestone
)
else
group_milestone_path
(
@group
,
@milestone
.
safe_title
,
title:
@milestone
.
title
)
end
end
end
app/controllers/groups/milestones_controller.rb
View file @
c45e11f1
class
Groups::MilestonesController
<
Groups
::
ApplicationController
include
MilestoneActions
before_action
:group_projects
before_action
:milestone
,
only:
[
:show
,
:update
]
before_action
:milestone
,
only:
[
:show
,
:update
,
:merge_requests
,
:participants
,
:labels
]
before_action
:authorize_admin_milestones!
,
only:
[
:new
,
:create
,
:update
]
def
index
...
...
app/controllers/projects/milestones_controller.rb
View file @
c45e11f1
class
Projects::MilestonesController
<
Projects
::
ApplicationController
include
MilestoneActions
before_action
:module_enabled
before_action
:milestone
,
only:
[
:edit
,
:update
,
:destroy
,
:show
,
:sort_issues
,
:sort_merge_requests
]
before_action
:milestone
,
only:
[
:edit
,
:update
,
:destroy
,
:show
,
:sort_issues
,
:sort_merge_requests
,
:merge_requests
,
:participants
,
:labels
]
# Allow read any milestone
before_action
:authorize_read_milestone!
# Allow admin milestone
before_action
:authorize_admin_milestone!
,
except:
[
:index
,
:show
]
before_action
:authorize_admin_milestone!
,
except:
[
:index
,
:show
,
:merge_requests
,
:participants
,
:labels
]
respond_to
:html
...
...
app/helpers/milestones_helper.rb
View file @
c45e11f1
...
...
@@ -115,4 +115,28 @@ module MilestonesHelper
end
end
end
def
milestone_merge_request_tab_path
(
milestone
)
if
@project
merge_requests_namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
milestone
,
format: :json
)
elsif
@group
merge_requests_group_milestone_path
(
@group
,
milestone
.
safe_title
,
title:
milestone
.
title
,
format: :json
)
end
end
def
milestone_participants_tab_path
(
milestone
)
if
@project
participants_namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
milestone
,
format: :json
)
elsif
@group
participants_group_milestone_path
(
@group
,
milestone
.
safe_title
,
title:
milestone
.
title
,
format: :json
)
end
end
def
milestone_labels_tab_path
(
milestone
)
if
@project
labels_namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
milestone
,
format: :json
)
elsif
@group
labels_group_milestone_path
(
@group
,
milestone
.
safe_title
,
title:
milestone
.
title
,
format: :json
)
end
end
end
app/views/shared/milestones/_tab_loading.html.haml
0 → 100644
View file @
c45e11f1
.text-center.prepend-top-default
=
icon
(
'spin spinner 2x'
,
'aria-hidden'
:
'true'
,
'aria-label'
:
'Loading tab content'
)
app/views/shared/milestones/_tabs.html.haml
View file @
c45e11f1
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left
=
icon
(
'angle-left'
)
.fade-right
=
icon
(
'angle-right'
)
%ul
.nav-links.scrolling-tabs
%ul
.nav-links.scrolling-tabs
.js-milestone-tabs
-
if
milestone
.
is_a?
(
GlobalMilestone
)
||
can?
(
current_user
,
:read_issue
,
@project
)
%li
.active
=
link_to
'#tab-issues'
,
'data-toggle'
=>
'tab'
,
'data-show'
=>
'.tab-issues-buttons'
do
Issues
%span
.badge
=
milestone
.
issues_visible_to_user
(
current_user
).
size
%li
=
link_to
'#tab-merge-requests'
,
'data-toggle'
=>
'tab'
,
'data-
show'
=>
'.tab-merge-requests-buttons'
do
=
link_to
'#tab-merge-requests'
,
'data-toggle'
=>
'tab'
,
'data-
endpoint'
:
milestone_merge_request_tab_path
(
milestone
)
do
Merge Requests
%span
.badge
=
milestone
.
merge_requests
.
size
-
else
%li
.active
=
link_to
'#tab-merge-requests'
,
'data-toggle'
=>
'tab'
,
'data-
show'
=>
'.tab-merge-requests-buttons'
do
=
link_to
'#tab-merge-requests'
,
'data-toggle'
=>
'tab'
,
'data-
endpoint'
:
milestone_merge_request_tab_path
(
milestone
)
do
Merge Requests
%span
.badge
=
milestone
.
merge_requests
.
size
%li
=
link_to
'#tab-participants'
,
'data-toggle'
=>
'tab'
do
=
link_to
'#tab-participants'
,
'data-toggle'
=>
'tab'
,
'data-endpoint'
:
milestone_participants_tab_path
(
milestone
)
do
Participants
%span
.badge
=
milestone
.
participants
.
count
%li
=
link_to
'#tab-labels'
,
'data-toggle'
=>
'tab'
do
=
link_to
'#tab-labels'
,
'data-toggle'
=>
'tab'
,
'data-endpoint'
:
milestone_labels_tab_path
(
milestone
)
do
Labels
%span
.badge
=
milestone
.
labels
.
count
...
...
@@ -30,14 +30,18 @@
.tab-content.milestone-content
-
if
milestone
.
is_a?
(
GlobalMilestone
)
||
can?
(
current_user
,
:read_issue
,
@project
)
.tab-pane.active
#tab-issues
.tab-pane.active
#tab-issues
{
data:
{
sort_endpoint:
(
sort_issues_namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
@milestone
)
if
@project
&&
current_user
)
}
}
=
render
'shared/milestones/issues_tab'
,
issues:
milestone
.
issues_visible_to_user
(
current_user
).
include_associations
,
show_project_name:
show_project_name
,
show_full_project_name:
show_full_project_name
.tab-pane
#tab-merge-requests
=
render
'shared/milestones/merge_requests_tab'
,
merge_requests:
milestone
.
merge_requests
,
show_project_name:
show_project_name
,
show_full_project_name:
show_full_project_name
.tab-pane
#tab-merge-requests
{
data:
{
sort_endpoint:
(
sort_merge_requests_namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
@milestone
)
if
@project
&&
current_user
)
}
}
-# loaded async
=
render
"shared/milestones/tab_loading"
-
else
.tab-pane.active
#tab-merge-requests
=
render
'shared/milestones/merge_requests_tab'
,
merge_requests:
milestone
.
merge_requests
,
show_project_name:
show_project_name
,
show_full_project_name:
show_full_project_name
.tab-pane.active
#tab-merge-requests
{
data:
{
sort_endpoint:
(
sort_merge_requests_namespace_project_milestone_path
(
@project
.
namespace
,
@project
,
@milestone
)
if
@project
&&
current_user
)
}
}
-# loaded async
=
render
"shared/milestones/tab_loading"
.tab-pane
#tab-participants
=
render
'shared/milestones/participants_tab'
,
users:
milestone
.
participants
-# loaded async
=
render
"shared/milestones/tab_loading"
.tab-pane
#tab-labels
=
render
'shared/milestones/labels_tab'
,
labels:
milestone
.
labels
-# loaded async
=
render
"shared/milestones/tab_loading"
changelogs/unreleased/async-milestone-tabs.yml
0 → 100644
View file @
c45e11f1
---
title
:
Load milestone tabs asynchronously to increase initial load performance
merge_request
:
author
:
config/routes/group.rb
View file @
c45e11f1
...
...
@@ -10,7 +10,13 @@ scope(path: 'groups/*group_id',
end
resource
:avatar
,
only:
[
:destroy
]
resources
:milestones
,
constraints:
{
id:
/[^\/]+/
},
only:
[
:index
,
:show
,
:update
,
:new
,
:create
]
resources
:milestones
,
constraints:
{
id:
/[^\/]+/
},
only:
[
:index
,
:show
,
:update
,
:new
,
:create
]
do
member
do
get
:merge_requests
get
:participants
get
:labels
end
end
resources
:labels
,
except:
[
:show
]
do
post
:toggle_subscription
,
on: :member
...
...
config/routes/project.rb
View file @
c45e11f1
...
...
@@ -207,6 +207,9 @@ constraints(ProjectUrlConstrainer.new) do
member
do
put
:sort_issues
put
:sort_merge_requests
get
:merge_requests
get
:participants
get
:labels
end
end
...
...
features/group/milestones.feature
View file @
c45e11f1
...
...
@@ -38,6 +38,7 @@ Feature: Group Milestones
And
I should see the
"feature"
label
And
I should see the project name in the Issue row
@javascript
Scenario
:
I
should see the Labels tab
Given
Group has projects with milestones
When
I visit group
"Owned"
page
...
...
features/project/milestone.feature
View file @
c45e11f1
...
...
@@ -7,14 +7,6 @@ Feature: Project Milestone
And milestone has issue "Bugfix1" with labels
:
"bug",
"feature"
And milestone has issue "Bugfix2" with labels
:
"bug",
"enhancement"
@javascript
Scenario
:
Listing issues from issues tab
Given
I visit project
"Shop"
milestones page
And
I click link
"v2.2"
Then
I should see the labels
"bug"
,
"enhancement"
and
"feature"
And
I should see the
"bug"
label listed only once
@javascript
Scenario
:
Listing labels from labels tab
Given
I visit project
"Shop"
milestones page
...
...
features/steps/group/milestones.rb
View file @
c45e11f1
class
Spinach::Features::GroupMilestones
<
Spinach
::
FeatureSteps
include
WaitForAjax
include
SharedAuthentication
include
SharedPaths
include
SharedGroup
...
...
@@ -90,6 +91,8 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
end
step
'I should see the list of labels'
do
wait_for_ajax
page
.
within
(
'#tab-labels'
)
do
expect
(
page
).
to
have_content
'bug'
expect
(
page
).
to
have_content
'feature'
...
...
features/steps/project/project_milestone.rb
View file @
c45e11f1
...
...
@@ -2,6 +2,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
include
SharedAuthentication
include
SharedProject
include
SharedPaths
include
WaitForAjax
step
'milestone has issue "Bugfix1" with labels: "bug", "feature"'
do
project
=
Project
.
find_by
(
name:
"Shop"
)
...
...
@@ -34,6 +35,8 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
end
step
'I should see the labels "bug", "enhancement" and "feature"'
do
wait_for_ajax
page
.
within
(
'#tab-issues'
)
do
expect
(
page
).
to
have_content
'bug'
expect
(
page
).
to
have_content
'enhancement'
...
...
spec/controllers/groups/milestones_controller_spec.rb
View file @
c45e11f1
...
...
@@ -6,6 +6,16 @@ describe Groups::MilestonesController do
let
(
:project2
)
{
create
(
:empty_project
,
group:
group
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:title
)
{
'肯定不是中文的问题'
}
let
(
:milestone
)
do
project_milestone
=
create
(
:milestone
,
project:
project
)
GroupMilestone
.
build
(
group
,
[
project
],
project_milestone
.
title
)
end
let
(
:milestone_path
)
{
group_milestone_path
(
group
,
milestone
.
safe_title
,
title:
milestone
.
title
)
}
before
do
sign_in
(
user
)
...
...
@@ -14,6 +24,8 @@ describe Groups::MilestonesController do
controller
.
instance_variable_set
(
:@group
,
group
)
end
it_behaves_like
'milestone tabs'
describe
"#create"
do
it
"creates group milestone with Chinese title"
do
post
:create
,
...
...
spec/controllers/projects/milestones_controller_spec.rb
View file @
c45e11f1
...
...
@@ -7,6 +7,7 @@ describe Projects::MilestonesController do
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
milestone:
milestone
)
}
let!
(
:label
)
{
create
(
:label
,
project:
project
,
title:
'Issue Label'
,
issues:
[
issue
])
}
let!
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
,
target_project:
project
,
milestone:
milestone
)
}
let
(
:milestone_path
)
{
namespace_project_milestone_path
}
before
do
sign_in
(
user
)
...
...
@@ -14,6 +15,8 @@ describe Projects::MilestonesController do
controller
.
instance_variable_set
(
:@project
,
project
)
end
it_behaves_like
'milestone tabs'
describe
"#show"
do
render_views
...
...
spec/features/milestones/milestones_spec.rb
View file @
c45e11f1
...
...
@@ -86,6 +86,9 @@ describe 'Milestone draggable', feature: true, js: true do
visit
namespace_project_milestone_path
(
project
.
namespace
,
project
,
milestone
)
page
.
find
(
"a[href='#tab-merge-requests']"
).
click
wait_for_ajax
scroll_into_view
(
'.milestone-content'
)
drag_to
(
selector:
'.merge_requests-sortable-list'
,
list_to_index:
1
)
...
...
spec/support/milestone_tabs_examples.rb
0 → 100644
View file @
c45e11f1
shared_examples
'milestone tabs'
do
def
go
(
path
,
extra_params
=
{})
params
=
if
milestone
.
is_a?
(
GlobalMilestone
)
{
group_id:
group
.
id
,
id:
milestone
.
safe_title
,
title:
milestone
.
title
}
else
{
namespace_id:
project
.
namespace
.
to_param
,
project_id:
project
,
id:
milestone
.
iid
}
end
get
path
,
params
.
merge
(
extra_params
)
end
describe
'#merge_requests'
do
context
'as html'
do
before
{
go
(
:merge_requests
,
format:
'html'
)
}
it
'redirects to milestone#show'
do
expect
(
response
).
to
redirect_to
(
milestone_path
)
end
end
context
'as json'
do
before
{
go
(
:merge_requests
,
format:
'json'
)
}
it
'renders the merge requests tab template to a string'
do
expect
(
response
).
to
render_template
(
'shared/milestones/_merge_requests_tab'
)
expect
(
json_response
).
to
have_key
(
'html'
)
end
end
end
describe
'#participants'
do
context
'as html'
do
before
{
go
(
:participants
,
format:
'html'
)
}
it
'redirects to milestone#show'
do
expect
(
response
).
to
redirect_to
(
milestone_path
)
end
end
context
'as json'
do
before
{
go
(
:participants
,
format:
'json'
)
}
it
'renders the participants tab template to a string'
do
expect
(
response
).
to
render_template
(
'shared/milestones/_participants_tab'
)
expect
(
json_response
).
to
have_key
(
'html'
)
end
end
end
describe
'#labels'
do
context
'as html'
do
before
{
go
(
:labels
,
format:
'html'
)
}
it
'redirects to milestone#show'
do
expect
(
response
).
to
redirect_to
(
milestone_path
)
end
end
context
'as json'
do
before
{
go
(
:labels
,
format:
'json'
)
}
it
'renders the labels tab template to a string'
do
expect
(
response
).
to
render_template
(
'shared/milestones/_labels_tab'
)
expect
(
json_response
).
to
have_key
(
'html'
)
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment