diff --git a/aio/src/app/shared/link.directive.spec.ts b/aio/src/app/shared/link.directive.spec.ts index 912138d8da..e81d516d33 100644 --- a/aio/src/app/shared/link.directive.spec.ts +++ b/aio/src/app/shared/link.directive.spec.ts @@ -1,7 +1,93 @@ +import { async, inject, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { Component } from '@angular/core'; +import { LocationService } from 'app/shared/location.service'; +import { MockLocationService } from 'testing/location.service'; import { LinkDirective } from './link.directive'; -xdescribe('LinkDirective', () => { - it('should attach to all anchor elements', () => {}); - it('should bind a property to the "href" attribute', () => {}); - it('should intercept clicks on the element and call `location.go()`', () => {}); +describe('LinkDirective', () => { + + @Component({ + template: 'Test Link' + }) + class TestComponent { + url: string; + } + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + LinkDirective, + TestComponent + ], + providers: [ + { provide: LocationService, useFactory: () => new MockLocationService('initial/url') } + ] + }) + .compileComponents(); + })); + + it('should attach to all anchor elements', () => { + const fixture = TestBed.createComponent(TestComponent); + const directiveElement = fixture.debugElement.query(By.directive(LinkDirective)); + expect(directiveElement.name).toEqual('a'); + }); + + it('should bind a property to the "href" attribute', () => { + const fixture = TestBed.createComponent(TestComponent); + const directiveElement = fixture.debugElement.query(By.directive(LinkDirective)); + + fixture.componentInstance.url = 'test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['href']).toEqual('test/url'); + }); + + it('should set the "target" attribute to "_blank" if the href is absolute, otherwise "_self"', () => { + const fixture = TestBed.createComponent(TestComponent); + const directiveElement = fixture.debugElement.query(By.directive(LinkDirective)); + + fixture.componentInstance.url = 'http://test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['target']).toEqual('_blank'); + + fixture.componentInstance.url = 'https://test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['target']).toEqual('_blank'); + + fixture.componentInstance.url = 'ftp://test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['target']).toEqual('_blank'); + + fixture.componentInstance.url = '//test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['target']).toEqual('_blank'); + + fixture.componentInstance.url = 'test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['target']).toEqual('_self'); + + fixture.componentInstance.url = '/test/url'; + fixture.detectChanges(); + expect(directiveElement.properties['target']).toEqual('_self'); + }); + + it('should intercept clicks for local urls and call `location.go()`', inject([LocationService], (location: LocationService) => { + const fixture = TestBed.createComponent(TestComponent); + const directiveElement = fixture.debugElement.query(By.directive(LinkDirective)); + fixture.componentInstance.url = 'some/local/url'; + fixture.detectChanges(); + location.go = jasmine.createSpy('Location.go'); + directiveElement.triggerEventHandler('click', null); + expect(location.go).toHaveBeenCalledWith('some/local/url'); + })); + + it('should not intercept clicks for absolute urls', inject([LocationService], (location: LocationService) => { + const fixture = TestBed.createComponent(TestComponent); + const directiveElement = fixture.debugElement.query(By.directive(LinkDirective)); + fixture.componentInstance.url = 'https://some/absolute/url'; + fixture.detectChanges(); + location.go = jasmine.createSpy('Location.go'); + directiveElement.triggerEventHandler('click', null); + expect(location.go).not.toHaveBeenCalled(); + })); }); diff --git a/aio/src/app/shared/link.directive.ts b/aio/src/app/shared/link.directive.ts index c58a8cfdf3..f0b56e410f 100644 --- a/aio/src/app/shared/link.directive.ts +++ b/aio/src/app/shared/link.directive.ts @@ -1,10 +1,10 @@ -import { Directive, HostListener, HostBinding, Input } from '@angular/core'; +import { Directive, HostListener, HostBinding, Input, OnChanges } from '@angular/core'; import { LocationService } from 'app/shared/location.service'; @Directive({ /* tslint:disable-next-line:directive-selector */ selector: 'a[href]' }) -export class LinkDirective { +export class LinkDirective implements OnChanges { // We need both these decorators to ensure that we can access // the href programmatically, and that it appears as a real @@ -13,11 +13,27 @@ export class LinkDirective { @HostBinding() href: string; + @HostBinding() + target: string; + @HostListener('click', ['$event']) onClick($event) { - this.location.go(this.href); - return false; + if (this.isAbsolute(this.href)) { + return true; + } else { + this.location.go(this.href); + return false; + } + } + + private isAbsolute(url) { + return /^[a-z]+:\/\/|\/\//i.test(url); } constructor(private location: LocationService) { } + + ngOnChanges() { + this.target = this.isAbsolute(this.href) ? '_blank' : '_self'; + } + }