diff --git a/README.md b/README.md index c219308..235dff4 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,11 @@ All the scenarios are listed here below and nicely linked to the source file. * [custom filter pipe](./src/app/pipes/filter.pipe.spec.ts) * [async pipes within templates](./src/app/components/async-stream.component.spec.ts) _Shows how to correctly resolve async pipes and then verify they properly render in the HTML_ +1. [**Testing Custom Directives**](./src/app/directives) + * [custom email Validator directive](./src/app/directives/emailvalidator.spec.ts) + _Testing Custom Email Validator directive for template driven form_ + * [update on hover directive](./src/app/directives/updateonhover.directive.spec.ts) + _Testing simple custom directive which makes use of HostListener and HostBinding_ 1. [**Custom Matchers and Utilities**](./src/app/utils) * [Create your own custom Jasmine matchers](./src/app/utils/custom-matchers.ts) diff --git a/src/app/directives/emailvalidator.directive.ts b/src/app/directives/emailvalidator.directive.ts new file mode 100644 index 0000000..2948cfd --- /dev/null +++ b/src/app/directives/emailvalidator.directive.ts @@ -0,0 +1,30 @@ +import { Directive } from '@angular/core'; +import { NG_VALIDATORS, FormControl } from '@angular/forms'; + +@Directive({ + selector: '[validateEmail][ngModel],[validateEmail][formControl]', + providers: [ + { provide: NG_VALIDATORS, useClass: EmailValidator, multi: true } + ] +}) +export class EmailValidator { + + constructor() { } + + validate(formControl: FormControl) { + let EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; + + let temporaryDomains = ['yopmail.com', 'getnada.com', 'boximail.com', 'robot-mail.com', 'zetmail.com', 'givemail.com'] + + const cValue = formControl.value; + if(EMAIL_REGEXP.test(formControl.value) && temporaryDomains.indexOf(cValue.split('@')[1]) === -1 ) { + return null; + } else { + return { + validateEmail: { + valid: false + } + } + } + } +} \ No newline at end of file diff --git a/src/app/directives/emailvalidator.spec.ts b/src/app/directives/emailvalidator.spec.ts new file mode 100644 index 0000000..ebb9f6e --- /dev/null +++ b/src/app/directives/emailvalidator.spec.ts @@ -0,0 +1,78 @@ +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { FormsModule } from "@angular/forms"; +import { ViewChild, Component } from '@angular/core'; +import { EmailValidator } from './emailvalidator.directive'; + +@Component({ + template: ` +
+ + + + + +
+ ` +}) +class TestTemplateDrivenFormsComponent { + @ViewChild('f') form: any; + model = { + email: '', + password: '' + }; + constructor() { } + ngOnInit() { } +} + +describe('Directive: EmailValidator using TestTemplateDrivenFormsComponent', () => { + + let component: TestTemplateDrivenFormsComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + + TestBed.configureTestingModule({ + imports: [FormsModule], + declarations: [TestTemplateDrivenFormsComponent, EmailValidator] + }); + + fixture = TestBed.createComponent(TestTemplateDrivenFormsComponent); + + component = fixture.componentInstance; + + fixture.detectChanges(); + + }); + + it('Email invalid when not filled', () => { + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(component.form.valid).toBeFalsy(); + expect(component.form.controls.email.errors.required).toBeTruthy(); + }); + }); + + it('Email invalid when used wrong domain', () => { + fixture.whenStable().then( () => { + component.form.controls['email'].setValue('xyzqw@yopmail.com'); + fixture.detectChanges(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(component.form.controls.email.errors.required).toBeFalsy(); + expect(component.form.controls.email.errors.validateEmail).toBeTruthy(); + }); + }); + }); + + it('form valid when completely filled', () => { + fixture.whenStable().then( () => { + component.form.controls['email'].setValue('xyzqw@gmail.com'); + component.form.controls['password'].setValue('Qwertyui9!'); + fixture.detectChanges(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(component.form.valid).toBeTruthy(); + }); + }); + }); +}); diff --git a/src/app/directives/updateonhover.directive.spec.ts b/src/app/directives/updateonhover.directive.spec.ts new file mode 100644 index 0000000..dc1109f --- /dev/null +++ b/src/app/directives/updateonhover.directive.spec.ts @@ -0,0 +1,52 @@ +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { Component, DebugElement } from "@angular/core"; +import { By } from "@angular/platform-browser"; +import { UpdateOnHoverDirective } from './updateonhover.directive'; + +@Component({ + template: + ` + ` +}) +class TestHoverComponent { +} + + +describe('Directive: Hover', () => { + + let component: TestHoverComponent; + let fixture: ComponentFixture; + let element1: DebugElement; + let element2: DebugElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHoverComponent, UpdateOnHoverDirective] + }); + fixture = TestBed.createComponent(TestHoverComponent); + component = fixture.componentInstance; + element1 = fixture.debugElement.query(By.css('#button1')); + element2 = fixture.debugElement.query(By.css('#button2')); + fixture.detectChanges(); + }); + + it('hovering over button 1', () => { + element1.triggerEventHandler('mouseover', null); + fixture.detectChanges(); + expect(element1.nativeElement.style.backgroundColor).toBe('green'); + + element1.triggerEventHandler('mouseout', null); + fixture.detectChanges(); + expect(element1.nativeElement.style.backgroundColor).toBe('blue'); + }); + + it('hovering over button 2', () => { + element2.triggerEventHandler('mouseover', null); + fixture.detectChanges(); + expect(element2.nativeElement.style.backgroundColor).toBe('green'); + + element2.triggerEventHandler('mouseout', null); + fixture.detectChanges(); + expect(element2.nativeElement.style.backgroundColor).toBe('yellow'); + }); +}); \ No newline at end of file diff --git a/src/app/directives/updateonhover.directive.ts b/src/app/directives/updateonhover.directive.ts new file mode 100644 index 0000000..2dc16b1 --- /dev/null +++ b/src/app/directives/updateonhover.directive.ts @@ -0,0 +1,27 @@ +import { Directive, HostListener, HostBinding, Input } from '@angular/core'; + +@Directive({ + selector: '[hover]' +}) +export class UpdateOnHoverDirective { + + @Input() defaultColor: string + + @HostBinding("style.background-color") backgroundColor: string; + + @HostListener('mouseover') onHover() { + this.backgroundColor = 'green'; + } + + @HostListener('mouseout') onLeave() { + this.backgroundColor = this.defaultColor || 'blue'; + } + + ngOnInit() { + if (this.defaultColor) { + this.backgroundColor = this.defaultColor + } else { + this.backgroundColor = 'blue'; + } + } +} \ No newline at end of file