describe 'up.link', -> u = up.util describe 'Javascript functions', -> describe 'up.follow', -> describeCapability 'canPushState', -> it 'loads the given link via AJAX and replaces the response in the given target', (done) -> affix('.before').text('old-before') affix('.middle').text('old-middle') affix('.after').text('old-after') $link = affix('a[href="/path"][up-target=".middle"]') promise = up.follow($link) @respondWith """
new-before
new-middle
new-after
""" promise.then -> expect($('.before')).toHaveText('old-before') expect($('.middle')).toHaveText('new-middle') expect($('.after')).toHaveText('old-after') done() it 'uses the method from a data-method attribute', -> $link = affix('a[href="/path"][data-method="PUT"]') up.follow($link) request = @lastRequest() expect(request).toHaveRequestMethod('PUT') it 'allows to refer to the link itself as "&" in the CSS selector', -> $container = affix('div') $link1 = $('first-link').appendTo($container) $link2 = $('second-link').appendTo($container) up.follow($link2) @respondWith '
second-div
' expect($container.text()).toBe('first-linksecond-div') it 'adds history entries and allows the user to use the back- and forward-buttons', (done) -> # By default, up.history will replace the tag when # the user presses the back-button. We reconfigure this # so we don't lose the Jasmine runner interface. up.history.config.popTargets = ['.container'] respondWith = (html) => @lastRequest().respondWith status: 200 contentType: 'text/html' responseText: "
#{html}
" followAndRespond = ($link, html) -> promise = up.follow($link) respondWith(html) promise $link1 = affix('a[href="/one"][up-target=".target"]') $link2 = affix('a[href="/two"][up-target=".target"]') $link3 = affix('a[href="/three"][up-target=".target"]') $container = affix('.container') $target = affix('.target').appendTo($container).text('original text') followAndRespond($link1, 'text from one').then => expect($('.target')).toHaveText('text from one') expect(location.pathname).toEqual('/one') followAndRespond($link2, 'text from two').then => expect($('.target')).toHaveText('text from two') expect(location.pathname).toEqual('/two') followAndRespond($link3, 'text from three').then => expect($('.target')).toHaveText('text from three') expect(location.pathname).toEqual('/three') history.back() @setTimer 50, => respondWith('restored text from two') expect($('.target')).toHaveText('restored text from two') expect(location.pathname).toEqual('/two') history.back() @setTimer 50, => respondWith('restored text from one') expect($('.target')).toHaveText('restored text from one') expect(location.pathname).toEqual('/one') history.forward() @setTimer 50, => # Since the response is cached, we don't have to respond expect($('.target')).toHaveText('restored text from two') expect(location.pathname).toEqual('/two') done() describe 'with { restoreScroll: true } option', -> it 'does not reveal, but instead restores the scroll positions of all viewports around the target', -> $viewport = affix('div[up-viewport] .element').css 'height': '100px' 'width': '100px' 'overflow-y': 'scroll' followLink = (options = {}) -> $link = $viewport.find('.link') up.follow($link, options) respond = (linkDestination) => @respondWith """
Link
""" up.replace('.element', '/foo') # Provide the content at /foo with a link to /bar in the HTML respond('/bar') $viewport.scrollTop(65) # Follow the link to /bar followLink() # Provide the content at /bar with a link back to /foo in the HTML respond('/foo') # Follow the link back to /foo, restoring the scroll position of 65px followLink(restoreScroll: true) # No need to respond because /foo has been cached before expect($viewport.scrollTop()).toEqual(65) # describe "when the browser is already on the link's destination", -> # # it "doesn't make a request and reveals the target container" # # it "doesn't make a request and reveals the target of a #hash in the URL" describe 'with { confirm } option', -> it 'follows the link after the user OKs a confirmation dialog', -> deferred = $.Deferred() spyOn(up.browser, 'confirm').and.returnValue(deferred) spyOn(up, 'replace') $link = affix('a[href="/danger"][up-target=".middle"]') up.follow($link, confirm: 'Do you really want to go there?') expect(up.browser.confirm).toHaveBeenCalledWith('Do you really want to go there?') expect(up.replace).not.toHaveBeenCalled() deferred.resolve() expect(up.replace).toHaveBeenCalled() it 'does not show a confirmation dialog if the option is not a present string', -> spyOn(up, 'replace') $link = affix('a[href="/danger"][up-target=".middle"]') up.follow($link, confirm: '') expect(up.replace).toHaveBeenCalled() describeFallback 'canPushState', -> it 'follows the given link', -> $link = affix('a[href="/path"]') spyOn(up.browser, 'loadPage') up.follow($link) expect(up.browser.loadPage).toHaveBeenCalledWith('/path', jasmine.anything()) it 'uses the method from a data-method attribute', -> $link = affix('a[href="/path"][data-method="PUT"]') spyOn(up.browser, 'loadPage') up.follow($link) expect(up.browser.loadPage).toHaveBeenCalledWith('/path', { method: 'PUT' }) describe 'up.visit', -> it 'should have tests' describe 'unobtrusive behavior', -> describe 'a[up-target]', -> it 'does not follow a form with up-target attribute (bugfix)', -> $form = affix('form[up-target]') up.hello($form) followSpy = up.link.knife.mock('follow') $form.click() expect(followSpy).not.toHaveBeenCalled() describeCapability 'canPushState', -> it 'adds a history entry', -> affix('.target') $link = affix('a[href="/path"][up-target=".target"]') $link.click() @respondWith('
new text
') expect($('.target')).toHaveText('new text') expect(location.pathname).toEqual('/path') it 'respects a X-Up-Location header that the server sends in case of a redirect', -> affix('.target') $link = affix('a[href="/path"][up-target=".target"]') $link.click() @respondWith responseText: '
new text
' responseHeaders: { 'X-Up-Location': '/other/path' } expect($('.target')).toHaveText('new text') expect(location.pathname).toEqual('/other/path') it 'does not add a history entry when an up-history attribute is set to "false"', -> oldPathname = location.pathname affix('.target') $link = affix('a[href="/path"][up-target=".target"][up-history="false"]') $link.click() @respondWith responseText: '
new text
' responseHeaders: { 'X-Up-Location': '/other/path' } expect($('.target')).toHaveText('new text') expect(location.pathname).toEqual(oldPathname) describe 'a[up-follow]', -> beforeEach -> @$link = affix('a[href="/path"][up-follow]') @followSpy = up.link.knife.mock('follow') @defaultSpy = up.link.knife.mock('allowDefault').and.callFake((event) -> event.preventDefault()) it "calls up.follow with the clicked link", -> Trigger.click(@$link) expect(@followSpy).toHaveBeenCalledWith(@$link) # IE does not call Javascript and always performs the default action on right clicks unless navigator.userAgent.match(/Trident/) it 'does nothing if the right mouse button is used', -> Trigger.click(@$link, button: 2) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing if shift is pressed during the click', -> Trigger.click(@$link, shiftKey: true) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing if ctrl is pressed during the click', -> Trigger.click(@$link, ctrlKey: true) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing if meta is pressed during the click', -> Trigger.click(@$link, metaKey: true) expect(@followSpy).not.toHaveBeenCalled() describe 'with [up-instant] modifier', -> beforeEach -> @$link.attr('up-instant', '') it 'follows a link on mousedown (instead of on click)', -> Trigger.mousedown(@$link) expect(@followSpy.calls.mostRecent().args[0]).toEqual(@$link) it 'does nothing on mouseup', -> Trigger.mouseup(@$link) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing on click', -> Trigger.click(@$link) expect(@followSpy).not.toHaveBeenCalled() # IE does not call Javascript and always performs the default action on right clicks unless navigator.userAgent.match(/Trident/) it 'does nothing if the right mouse button is pressed down', -> Trigger.mousedown(@$link, button: 2) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing if shift is pressed during mousedown', -> Trigger.mousedown(@$link, shiftKey: true) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing if ctrl is pressed during mousedown', -> Trigger.mousedown(@$link, ctrlKey: true) expect(@followSpy).not.toHaveBeenCalled() it 'does nothing if meta is pressed during mousedown', -> Trigger.mousedown(@$link, metaKey: true) expect(@followSpy).not.toHaveBeenCalled() describe '[up-expand]', -> it 'copies up-related attributes of a contained link', -> $area = affix('div[up-expand] a[href="/path"][up-target="selector"][up-instant][up-preload]') up.hello($area) expect($area.attr('up-target')).toEqual('selector') expect($area.attr('up-instant')).toEqual('') expect($area.attr('up-preload')).toEqual('') it "renames a contained link's href attribute to up-href so the container is considered a link", -> $area = affix('div[up-expand] a[up-follow][href="/path"]') up.hello($area) expect($area.attr('up-href')).toEqual('/path') it 'copies attributes from the first link if there are multiple links', -> $area = affix('div[up-expand]') $link1 = $area.affix('a[href="/path1"]') $link2 = $area.affix('a[href="/path2"]') up.hello($area) expect($area.attr('up-href')).toEqual('/path1') it "copies an contained non-link element with up-href attribute", -> $area = affix('div[up-expand] span[up-follow][up-href="/path"]') up.hello($area) expect($area.attr('up-href')).toEqual('/path') it 'adds an up-follow attribute if the contained link has neither up-follow nor up-target attributes', -> $area = affix('div[up-expand] a[href="/path"]') up.hello($area) expect($area.attr('up-follow')).toEqual('') describe 'with a CSS selector in the property value', -> it "expands the contained link that matches the selector", -> $area = affix('div[up-expand=".second"]') $link1 = $area.affix('a.first[href="/path1"]') $link2 = $area.affix('a.second[href="/path2"]') up.hello($area) expect($area.attr('up-href')).toEqual('/path2') it 'does nothing if no contained link matches the selector', -> $area = affix('div[up-expand=".foo"]') $link = $area.affix('a[href="/path1"]') up.hello($area) expect($area.attr('up-href')).toBeUndefined() it 'does not match an element that is not a descendant', -> $area = affix('div[up-expand=".second"]') $link1 = $area.affix('a.first[href="/path1"]') $link2 = affix('a.second[href="/path2"]') # not a child of $area up.hello($area) expect($area.attr('up-href')).toBeUndefined()