Commit e17020b9 authored by Robert Speicher's avatar Robert Speicher

Add MergeRequestTabs specs

parent 8e72c65c
# MergeRequestTabs
#
# Handles persisting and restoring the current tab selection and lazily-loading
# content on the MergeRequests#show page.
#
# ### Example Markup
#
# <ul class="nav nav-tabs merge-request-tabs">
# <li class="notes-tab active">
# <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
# Discussion
# </a>
# </li>
# <li class="commits-tab">
# <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
# Commits
# </a>
# </li>
# <li class="diffs-tab">
# <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
# Diffs
# </a>
# </li>
# </ul>
#
# <div class="tab-content">
# <div class="notes tab-pane active" id="notes">
# Notes Content
# </div>
# <div class="commits tab-pane" id="commits">
# Commits Content
# </div>
# <div class="diffs tab-pane" id="diffs">
# Diffs Content
# </div>
# </div>
#
# <div class="mr-loading-status">
# <div class="loading">
# Loading Animation
# </div>
# </div>
#
class @MergeRequestTabs class @MergeRequestTabs
diffsLoaded: false diffsLoaded: false
commitsLoaded: false commitsLoaded: false
constructor: (@opts) -> constructor: (@opts = {}) ->
# Store the `location` object, allowing for easier stubbing in tests
@_location = location
@bindEvents() @bindEvents()
@activateTabFromPath() @activateTab(@opts.action)
switch @opts.action switch @opts.action
when 'commits' then @commitsLoaded = true when 'commits' then @commitsLoaded = true
when 'diffs' then @diffsLoaded = true when 'diffs' then @diffsLoaded = true
bindEvents: -> bindEvents: ->
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShow $(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
tabShow: (event) => tabShown: (event) =>
$target = $(event.target) $target = $(event.target)
action = $target.data('action') action = $target.data('action')
# Lazy-load commits if action == 'commits'
if action == 'commits' && !@commitsLoaded @loadCommits($target.attr('href'))
@loadCommits() else if action == 'diffs'
@loadDiff($target.attr('href'))
# Lazy-load diffs
if action == 'diffs' && !@diffsLoaded
@loadDiff()
@setCurrentAction(action) @setCurrentAction(action)
# Activate a tab based on the current URL path # Activate a tab based on the current action
# activateTab: (action) ->
# If the current action is 'show' or 'new' (i.e., initial page load), action = 'notes' if action == 'show'
# activates the first tab, otherwise activates the tab corresponding to the $(".merge-request-tabs a[data-action='#{action}']").tab('show')
# current action (diffs, commits).
activateTabFromPath: ->
if @opts.action == 'show' || @opts.action == 'new'
$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
else
$(".merge-request-tabs a[data-action='#{@opts.action}']").tab('show')
# Replaces the current Merge Request-specific action in the URL with a new one # Replaces the current Merge Request-specific action in the URL with a new one
# #
...@@ -56,19 +93,21 @@ class @MergeRequestTabs ...@@ -56,19 +93,21 @@ class @MergeRequestTabs
# location.pathname # => "/namespace/project/merge_requests/1/diffs" # location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('commits') # setCurrentAction('commits')
# location.pathname # => "/namespace/project/merge_requests/1/commits" # location.pathname # => "/namespace/project/merge_requests/1/commits"
setCurrentAction: (action) -> #
# Returns the new URL String
setCurrentAction: (action) =>
# Normalize action, just to be safe # Normalize action, just to be safe
action = 'notes' if action == 'show' action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs' # Remove a trailing '/commits' or '/diffs'
new_state = location.pathname.replace(/\/(commits|diffs)\/?$/, '') new_state = @_location.pathname.replace(/\/(commits|diffs)\/?$/, '')
# Append the new action if we're on a tab other than 'notes' # Append the new action if we're on a tab other than 'notes'
unless action == 'notes' unless action == 'notes'
new_state += "/#{action}" new_state += "/#{action}"
# Ensure parameters and hash come along for the ride # Ensure parameters and hash come along for the ride
new_state += location.search + location.hash new_state += @_location.search + @_location.hash
# Replace the current history state with the new one without breaking # Replace the current history state with the new one without breaking
# Turbolinks' history. # Turbolinks' history.
...@@ -76,33 +115,39 @@ class @MergeRequestTabs ...@@ -76,33 +115,39 @@ class @MergeRequestTabs
# See https://github.com/rails/turbolinks/issues/363 # See https://github.com/rails/turbolinks/issues/363
history.replaceState {turbolinks: true, url: new_state}, document.title, new_state history.replaceState {turbolinks: true, url: new_state}, document.title, new_state
loadCommits: -> new_state
$.ajax
type: 'GET' loadCommits: (source) ->
dataType: 'json' return if @commitsLoaded
url: $('.merge-request-tabs .commits-tab a').attr('href') + ".json"
beforeSend: @toggleLoading @_get
complete: => url: "#{source}.json"
@commits_loaded = true
@toggleLoading()
success: (data) => success: (data) =>
document.getElementById('commits').innerHTML = data.html document.getElementById('commits').innerHTML = data.html
$('.js-timeago').timeago() $('.js-timeago').timeago()
@commitsLoaded = true
loadDiff: -> loadDiff: (source) ->
$.ajax return if @diffsLoaded
type: 'GET'
dataType: 'json' @_get
url: $('.merge-request-tabs .diffs-tab a').attr('href') + ".json" url: "#{source}.json"
beforeSend: => @toggleLoading()
complete: =>
@diffs_loaded = true
@toggleLoading()
success: (data) => success: (data) =>
document.getElementById('diffs').innerHTML = data.html document.getElementById('diffs').innerHTML = data.html
$('.diff-header').trigger('sticky_kit:recalc') $('.diff-header').trigger('sticky_kit:recalc')
@diffsLoaded = true
toggleLoading: -> toggleLoading: ->
$('.mr-loading-status .loading').toggle() $('.mr-loading-status .loading').toggle()
_get: (options) ->
defaults = {
beforeSend: @toggleLoading
complete: @toggleLoading
dataType: 'json'
type: 'GET'
}
options = $.extend({}, defaults, options)
$.ajax(options)
%ul.nav.nav-tabs.merge-request-tabs
%li.notes-tab
%a{href: '/foo/bar/merge_requests/1', data: {target: '#notes', action: 'notes', toggle: 'tab'}}
Discussion
%li.commits-tab
%a{href: '/foo/bar/merge_requests/1/commits', data: {target: '#commits', action: 'commits', toggle: 'tab'}}
Commits
%li.diffs-tab
%a{href: '/foo/bar/merge_requests/1/diffs', data: {target: '#diffs', action: 'diffs', toggle: 'tab'}}
Diffs
.tab-content
#notes.notes.tab-pane
Notes Content
#commits.commits.tab-pane
Commits Content
#diffs.diffs.tab-pane
Diffs Content
.mr-loading-status
.loading
Loading Animation
#= require merge_request_tabs
describe 'MergeRequestTabs', ->
stubLocation = (stubs) ->
defaults = {pathname: '', search: '', hash: ''}
$.extend(defaults, stubs)
fixture.preload('merge_request_tabs.html')
beforeEach ->
@class = new MergeRequestTabs()
@spies = {
ajax: spyOn($, 'ajax').and.callFake ->
history: spyOn(history, 'replaceState').and.callFake ->
}
describe '#activateTab', ->
beforeEach ->
fixture.load('merge_request_tabs.html')
@subject = @class.activateTab
it 'shows the first tab when action is show', ->
@subject('show')
expect($('#notes')).toHaveClass('active')
it 'shows the notes tab when action is notes', ->
@subject('notes')
expect($('#notes')).toHaveClass('active')
it 'shows the commits tab when action is commits', ->
@subject('commits')
expect($('#commits')).toHaveClass('active')
it 'shows the diffs tab when action is diffs', ->
@subject('diffs')
expect($('#diffs')).toHaveClass('active')
describe '#setCurrentAction', ->
beforeEach ->
@subject = @class.setCurrentAction
it 'changes from commits', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/commits')
expect(@subject('notes')).toBe('/foo/bar/merge_requests/1')
expect(@subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs')
it 'changes from diffs', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/diffs')
expect(@subject('notes')).toBe('/foo/bar/merge_requests/1')
expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits')
it 'changes from notes', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1')
expect(@subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs')
expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits')
it 'includes search parameters and hash string', ->
@class._location = stubLocation({
pathname: '/foo/bar/merge_requests/1/diffs'
search: '?view=parallel'
hash: '#L15-35'
})
expect(@subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35')
it 'replaces the current history state', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1')
new_state = @subject('commits')
expect(@spies.history).toHaveBeenCalledWith(
{turbolinks: true, url: new_state},
document.title,
new_state
)
it 'treats "show" like "notes"', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/commits')
expect(@subject('show')).toBe('/foo/bar/merge_requests/1')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment