-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1134 from christophercr/feature/input-mask-direct…
…ives feat(stark-ui): implement directives for email, number and timestamp masks
- Loading branch information
Showing
29 changed files
with
2,475 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
packages/stark-ui/src/modules/input-mask-directives/directives.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
export * from "./directives/email-mask.directive"; | ||
export * from "./directives/number-mask-config.intf"; | ||
export * from "./directives/number-mask.directive"; | ||
export * from "./directives/text-mask.constants"; | ||
export * from "./directives/text-mask.directive"; | ||
export * from "./directives/text-mask-config.intf"; | ||
export * from "./directives/timestamp-mask-config.intf"; | ||
export * from "./directives/timestamp-mask.directive"; | ||
export * from "./directives/timestamp-pipe.fn"; |
357 changes: 357 additions & 0 deletions
357
packages/stark-ui/src/modules/input-mask-directives/directives/email-mask.directive.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,357 @@ | ||
/* tslint:disable:completed-docs no-duplicate-string no-identical-functions no-big-function */ | ||
import { Component, DebugElement } from "@angular/core"; | ||
import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms"; | ||
import { By } from "@angular/platform-browser"; | ||
import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; | ||
import { Observer } from "rxjs"; | ||
import { StarkEmailMaskDirective } from "./email-mask.directive"; | ||
|
||
describe("EmailMaskDirective", () => { | ||
let fixture: ComponentFixture<TestComponent>; | ||
let hostComponent: TestComponent; | ||
let inputElement: DebugElement; | ||
|
||
@Component({ | ||
selector: "test-component", | ||
template: getTemplate("[starkEmailMask]='emailMaskConfig'") | ||
}) | ||
class TestComponent { | ||
public emailMaskConfig: boolean; | ||
public ngModelValue: string = ""; | ||
public formControl: FormControl = new FormControl(""); | ||
} | ||
|
||
function getTemplate(emailMaskDirective: string): string { | ||
return "<input " + "type='text' " + emailMaskDirective + ">"; | ||
} | ||
|
||
function initializeComponentFixture(): void { | ||
fixture = TestBed.createComponent(TestComponent); | ||
hostComponent = fixture.componentInstance; | ||
inputElement = fixture.debugElement.query(By.css("input")); | ||
// trigger initial data binding | ||
fixture.detectChanges(); | ||
} | ||
|
||
function changeInputValue(inputDebugElement: DebugElement, value: string, eventType: string = "input"): void { | ||
(<HTMLInputElement>inputDebugElement.nativeElement).value = value; | ||
|
||
// more verbose way to create and trigger an event (the only way it works in IE) | ||
// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events | ||
const ev: Event = document.createEvent("Event"); | ||
ev.initEvent(eventType, true, true); | ||
(<HTMLInputElement>inputDebugElement.nativeElement).dispatchEvent(ev); | ||
} | ||
|
||
// Inject module dependencies | ||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
declarations: [StarkEmailMaskDirective, TestComponent], | ||
imports: [FormsModule, ReactiveFormsModule], | ||
providers: [] | ||
}); | ||
}); | ||
|
||
describe("uncontrolled", () => { | ||
beforeEach(fakeAsync(() => { | ||
// compile template and css | ||
return TestBed.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
}); | ||
|
||
it("should render the appropriate content", () => { | ||
expect(inputElement.attributes["ng-reflect-mask-config"]).toBeDefined(); // starkEmailMask directive | ||
}); | ||
|
||
it("should update the input value and show the mask only when a valid event is triggered in the input field", () => { | ||
// Angular2 text-mask directive handles only the "input" event | ||
const validEvents: string[] = ["input"]; | ||
|
||
for (const eventType of validEvents) { | ||
changeInputValue(inputElement, ""); | ||
fixture.detectChanges(); | ||
expect(inputElement.nativeElement.value).toBe(""); | ||
|
||
changeInputValue(inputElement, "my-email@", eventType); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe("my-email@ ."); | ||
} | ||
|
||
const invalidEvents: string[] = ["blur", "keyup", "change", "focus", "keydown", "keypress", "click"]; | ||
|
||
for (const eventType of invalidEvents) { | ||
changeInputValue(inputElement, ""); | ||
fixture.detectChanges(); | ||
expect(inputElement.nativeElement.value).toBe(""); | ||
|
||
changeInputValue(inputElement, "my-email", eventType); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe("my-email"); // no mask shown | ||
} | ||
}); | ||
|
||
it("should prevent invalid values to be entered in the input field when the value is changed manually", () => { | ||
const invalidValues: string[] = ["@@", "@.a.", " @ .", "[email protected]@."]; | ||
|
||
for (const value of invalidValues) { | ||
changeInputValue(inputElement, value); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe(""); | ||
} | ||
}); | ||
|
||
it("should remove the mask only when the config is set to false", () => { | ||
changeInputValue(inputElement, "my-email@"); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe("my-email@ ."); | ||
|
||
hostComponent.emailMaskConfig = <any>undefined; | ||
fixture.detectChanges(); | ||
|
||
changeInputValue(inputElement, "[email protected]@."); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe("my-email@ ."); // the mask is enabled by default | ||
|
||
hostComponent.emailMaskConfig = <any>""; // use case when the directive is used with no inputs: <input type='text' starkEmailMask> | ||
fixture.detectChanges(); | ||
|
||
changeInputValue(inputElement, "[email protected]@."); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe("my-email@ ."); // the mask is enabled by default | ||
|
||
hostComponent.emailMaskConfig = false; | ||
fixture.detectChanges(); | ||
|
||
changeInputValue(inputElement, "what@@.ever@."); | ||
fixture.detectChanges(); | ||
|
||
expect(inputElement.nativeElement.value).toBe("what@@.ever@."); // no mask at all | ||
}); | ||
}); | ||
|
||
describe("with ngModel", () => { | ||
beforeEach(fakeAsync(() => { | ||
const newTemplate: string = getTemplate("[(ngModel)]='ngModelValue' [starkEmailMask]='emailMaskConfig'"); | ||
|
||
TestBed.overrideTemplate(TestComponent, newTemplate); | ||
|
||
// compile template and css | ||
return TestBed.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
}); | ||
|
||
it("should render the appropriate content", () => { | ||
expect(inputElement.attributes["ng-reflect-mask-config"]).toBeDefined(); // starkEmailMask directive | ||
}); | ||
|
||
it("should update the input value and show the mask only when a valid event is triggered in the input field", () => { | ||
// Angular2 text-mask directive handles only the "input" event | ||
const validEvents: string[] = ["input"]; | ||
|
||
for (const eventType of validEvents) { | ||
changeInputValue(inputElement, ""); | ||
fixture.detectChanges(); | ||
expect(hostComponent.ngModelValue).toBe(""); | ||
|
||
changeInputValue(inputElement, "my-email@", eventType); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.ngModelValue).toBe("my-email@ ."); | ||
} | ||
|
||
const invalidEvents: string[] = ["blur", "keyup", "change", "focus", "keydown", "keypress", "click"]; | ||
|
||
for (const eventType of invalidEvents) { | ||
changeInputValue(inputElement, ""); | ||
fixture.detectChanges(); | ||
expect(hostComponent.ngModelValue).toBe(""); | ||
|
||
changeInputValue(inputElement, "my-email@", eventType); | ||
fixture.detectChanges(); | ||
|
||
// IMPORTANT: the ngModel is not changed with invalid events, just with "input" events | ||
expect(hostComponent.ngModelValue).toBe(""); // no mask shown | ||
} | ||
}); | ||
|
||
it("should prevent invalid values to be entered in the input field when the value is changed manually", () => { | ||
const invalidValues: string[] = ["@@", "@.a.", " @ .", "[email protected]@."]; | ||
|
||
for (const value of invalidValues) { | ||
changeInputValue(inputElement, value); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.ngModelValue).toBe(""); | ||
} | ||
}); | ||
|
||
it("should remove the mask only when the config is set to false", () => { | ||
changeInputValue(inputElement, "my-email@"); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.ngModelValue).toBe("my-email@ ."); | ||
|
||
hostComponent.emailMaskConfig = <any>undefined; | ||
fixture.detectChanges(); | ||
|
||
changeInputValue(inputElement, "[email protected]@."); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.ngModelValue).toBe("my-email@ ."); // the mask is enabled by default | ||
|
||
hostComponent.emailMaskConfig = <any>""; // use case when the directive is used with no inputs: <input type='text' [(ngModel)]='ngModelValue' starkEmailMask> | ||
fixture.detectChanges(); | ||
|
||
changeInputValue(inputElement, "[email protected]@."); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.ngModelValue).toBe("my-email@ ."); // the mask is enabled by default | ||
|
||
hostComponent.emailMaskConfig = false; | ||
fixture.detectChanges(); | ||
|
||
changeInputValue(inputElement, "what@@.ever@."); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.ngModelValue).toBe("what@@.ever@."); // no mask at all | ||
}); | ||
}); | ||
|
||
describe("with FormControl", () => { | ||
let mockValueChangeObserver: jasmine.SpyObj<Observer<any>>; | ||
|
||
beforeEach(fakeAsync(() => { | ||
const newTemplate: string = getTemplate("[formControl]='formControl' [starkEmailMask]='emailMaskConfig'"); | ||
|
||
TestBed.overrideTemplate(TestComponent, newTemplate); | ||
|
||
// compile template and css | ||
return TestBed.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
|
||
mockValueChangeObserver = jasmine.createSpyObj<Observer<any>>("observerSpy", ["next", "error", "complete"]); | ||
hostComponent.formControl.valueChanges.subscribe(mockValueChangeObserver); | ||
}); | ||
|
||
it("should render the appropriate content", () => { | ||
expect(inputElement.attributes["ng-reflect-mask-config"]).toBeDefined(); // starkEmailMask directive | ||
}); | ||
|
||
it("should update the input value and show the mask only when a valid event is triggered in the input field", () => { | ||
// Angular2 text-mask directive handles only the "input" event | ||
const validEvents: string[] = ["input"]; | ||
|
||
for (const eventType of validEvents) { | ||
changeInputValue(inputElement, ""); | ||
fixture.detectChanges(); | ||
expect(hostComponent.formControl.value).toBe(""); | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
changeInputValue(inputElement, "my-email@", eventType); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.formControl.value).toBe("my-email@ ."); | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); | ||
expect(mockValueChangeObserver.complete).not.toHaveBeenCalled(); | ||
} | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
const invalidEvents: string[] = ["blur", "keyup", "change", "focus", "keydown", "keypress", "click"]; | ||
|
||
for (const eventType of invalidEvents) { | ||
changeInputValue(inputElement, ""); | ||
fixture.detectChanges(); | ||
expect(hostComponent.formControl.value).toBe(""); | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
changeInputValue(inputElement, "my-email@", eventType); | ||
fixture.detectChanges(); | ||
|
||
// IMPORTANT: the formControl is not changed with invalid events, just with "input" events | ||
expect(hostComponent.formControl.value).toBe(""); // no mask shown | ||
expect(mockValueChangeObserver.next).not.toHaveBeenCalled(); | ||
expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); | ||
expect(mockValueChangeObserver.complete).not.toHaveBeenCalled(); | ||
} | ||
}); | ||
|
||
it("should prevent invalid values to be entered in the input field when the value is changed manually", () => { | ||
const invalidValues: string[] = ["@@", "@.a.", " @ .", "[email protected]@."]; | ||
|
||
for (const value of invalidValues) { | ||
mockValueChangeObserver.next.calls.reset(); | ||
changeInputValue(inputElement, value); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.formControl.value).toBe(""); | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); | ||
expect(mockValueChangeObserver.complete).not.toHaveBeenCalled(); | ||
} | ||
}); | ||
|
||
it("should remove the mask when the config is undefined", () => { | ||
changeInputValue(inputElement, "my-email@"); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.formControl.value).toBe("my-email@ ."); | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
hostComponent.emailMaskConfig = <any>undefined; | ||
fixture.detectChanges(); | ||
expect(mockValueChangeObserver.next).not.toHaveBeenCalled(); // no value change, the mask is enabled by default | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
changeInputValue(inputElement, "[email protected]@."); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.formControl.value).toBe("my-email@ ."); // the mask is enabled by default | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
hostComponent.emailMaskConfig = <any>""; // use case when the directive is used with no inputs: <input type='text' [formControl]='formControl' starkEmailMask> | ||
fixture.detectChanges(); | ||
expect(mockValueChangeObserver.next).not.toHaveBeenCalled(); // no value change, the mask is enabled by default | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
changeInputValue(inputElement, "[email protected]@."); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.formControl.value).toBe("my-email@ ."); // the mask is enabled by default | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
hostComponent.emailMaskConfig = false; | ||
fixture.detectChanges(); | ||
expect(mockValueChangeObserver.next).not.toHaveBeenCalled(); // no value change, the mask was just disabled | ||
|
||
mockValueChangeObserver.next.calls.reset(); | ||
changeInputValue(inputElement, "what@@.ever@."); | ||
fixture.detectChanges(); | ||
|
||
expect(hostComponent.formControl.value).toBe("what@@.ever@."); // no mask at all | ||
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); | ||
expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); | ||
expect(mockValueChangeObserver.complete).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.