UNPKG

novo-elements

Version:

Bullhorn's NOVO Element Repository for Angular 2

549 lines (479 loc) 21.7 kB
// NG2 import { TestBed, tick, fakeAsync } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; // App import { QuickNoteElement } from './QuickNote'; import { ComponentUtils } from '../../utils/component-utils/ComponentUtils'; import { KeyCodes } from '../../utils/key-codes/KeyCodes'; describe('Elements: QuickNoteElement', () => { // Mocks and fakes used in the tests let fixture; let component; let fakeParentForm; let fakeCkEditorInstance; let fakeResultsDropdown; /** * A fake QuickNoteResults dropdown that provides behavior that mimics enough of the real dropdown for testing. */ class FakeQuickNoteResults { public instance: any = this; public element: any = null; public parentComponent: any = null; public visible: boolean = false; public taggingMode: string = 'person'; public selectedIndex: number = 0; // 0 = nothing is selected public selectedValue: any = { value: 'j.bullhorn', label: 'John Bullhorn' }; public nativeElementProperties: any = {}; constructor(parentComponent: any) { this.parentComponent = parentComponent; // Create a fake nativeElement that captures the properties that are set on it. this.element = { nativeElement: { style: { setProperty: (property: string, value: string): void => { this.nativeElementProperties[property] = value; } } } }; } prevActiveMatch(): void { this.selectedIndex--; } nextActiveMatch(): void { this.selectedIndex++; } selectActiveMatch(): void { // Call onSelected and hideResults on the parent, if a match is selected, // just like the real QuickNoteResults if (this.selectedIndex) { this.parentComponent.onSelected(this.taggingMode, this.selectedValue); this.parentComponent.hideResults(); } component.hideResults(); } destroy(): void { this.visible = false; this.selectedIndex = 0; } } /** * A fake component utils that is injected in order to return a fake results dropdown. */ class FakeComponentUtils { appendNextToLocation() { fakeResultsDropdown.visible = true; return fakeResultsDropdown; } } beforeEach(() => { TestBed.configureTestingModule({ declarations: [ QuickNoteElement ], imports: [ FormsModule ], providers: [ { provide: ComponentUtils, useClass: FakeComponentUtils } ] }).compileComponents(); fixture = TestBed.createComponent(QuickNoteElement); component = fixture.debugElement.componentInstance; // Create a config for tagging persons using '@' component.config = { triggers: { person: '@' }, options: { person: ['John Bullhorn', 'Jane Bullhorn', 'Bob Bullhorn'] }, renderer: { person: (symbol: string, item: any): string => { return `<a href="http://www.bullhorn.com">${symbol}${item.label}</a>`; } } }; // Create a fake results dropdown fakeResultsDropdown = new FakeQuickNoteResults(component); /** * A fake CKEditor instance that allows simulating user input and testing changes from the component. * * Call valueSetByUser to simulate a user pasting text into the CKEditor and replacing all the existing text. * Call keyEnteredByUser to simulate a user pressing a key in CKEditor. * Call userPausedAfterEntry to simulate a user waiting for the keystrokes to be picked up. */ fakeCkEditorInstance = { isPlaceholderVisible: () => this.placeholderVisible, config: { height: 200 }, keyEnteredByUser: (key: string, keyCode: number): void => { if (key === 'Backspace') { this.editorValue = this.editorValue.slice(0, -1); this.currentWord = this.currentWord.slice(0, -1); } // Add the character to the editorValue if it's a character if (key.length === 1) { this.editorValue += key; this.currentWord += key; } // Return the CKEditor key event object this.keyEvent({ data: { domEvent: { $: { // The native element key: key, keyCode: keyCode } } }, cancel: () => {} }); }, blurByUser: (): void => { this.blurEvent({}); }, focusByUser: (): void => { this.focusEvent({}); }, valueSetByUser: (value: string): void => { // Call the changeEvent callback and simulate enough time for the debounce to occur this.editorValue = value; this.currentWord = ''; this.changeEvent(); tick(251); }, userPausedAfterEntry: (): void => { this.changeEvent(); tick(251); }, on: (name: string, callback: any): void => { if (name === 'key') { this.keyEvent = callback; } else if (name === 'change') { this.changeEvent = callback; } else if (name === 'blur') { this.blurEvent = callback; } else if (name === 'focus') { this.focusEvent = callback; } else if (name === 'instanceReady') { // Immediately invoke the instanceReady callback callback({}); } }, getData: (): any => { return this.editorValue; }, setData: (model: any): void => { this.editorValue = model; }, getSelection: (): any => { return { getRanges: () => { return [{ startContainer: { getParent: () => { return { getHtml: () => this.editorValue, setHtml: (html) => { this.editorValue = html; } }; }, getText: () => this.currentWord, type: 3, // CKEDITOR.NODE_TEXT $: { // The native element parentElement: { appendChild: () => {} } } }, startOffset: this.currentWord.length, moveToPosition: () => {} }]; }, selectRanges: () => {} }; }, editable: (): any => { return { $: { // The native element scrollTop: 50, scrollLeft: 0 }, getParent: (): any => { return { $: { // The native element appendChild: (node) => { this.placeholderVisible = true; }, removeChild: (node) => { this.placeholderVisible = false; } }, }; } }; }, document: { getBody: (): any => { return { getHtml: (): string => this.editorValue }; } }, focusManager: { blur: (): void => {} }, removeAllListeners: (): void => { }, destroy: (): void => { }, name: 'instance' }; // Create a fake CKEDITOR global object that returns the fake CKEditor instance. window['CKEDITOR'] = { NODE_TEXT: 3, replace: () => { return fakeCkEditorInstance; }, instances: { 'instance': { destroy: () => {} } } }; // Initialize the component component.ngOnInit(); component.ngAfterViewInit(); // Create a fake parent form that this component is a part of - the form use ngModel to propagate up changes. fakeParentForm = { getValue: (): any => this.value, onModelChange: (value: any): void => { this.value = value; }, onModelTouched: (): void => { this.touchCount = this.touchCount ? this.touchCount++ : 0; } }; // Initialize the component inside of the fake parent form component.registerOnChange(fakeParentForm.onModelChange); component.registerOnTouched(fakeParentForm.onModelTouched); }); afterAll(fakeAsync(() => { component.ngOnDestroy(); })); describe('QuickNote Functionality', () => { it('should add the selected item to the list of references and populate note.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); expect(fakeResultsDropdown.visible).toBe(false); expect(fakeResultsDropdown.nativeElementProperties).toEqual({}); fakeCkEditorInstance.userPausedAfterEntry(); expect(fakeResultsDropdown.visible).toBe(true); expect(fakeResultsDropdown.nativeElementProperties).toEqual({'margin-top': '30px'}); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: @john', references: {} }); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); fakeCkEditorInstance.valueSetByUser(''); expect(fakeParentForm.getValue()).toEqual(null); })); it('should remove references from the model when their rendered text is removed from the note.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); fakeCkEditorInstance.userPausedAfterEntry(); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); fakeCkEditorInstance.valueSetByUser('Note about: '); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: ', references: {} }); // Make sure that the model is set properly on the second time through fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); fakeCkEditorInstance.userPausedAfterEntry(); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); })); it('should not add duplicate references to the model.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); fakeCkEditorInstance.userPausedAfterEntry(); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); fakeCkEditorInstance.userPausedAfterEntry(); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); })); it('should handle some keyboard events within resultsComponent.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.userPausedAfterEntry(); expect(fakeResultsDropdown.visible).toBe(true); expect(fakeResultsDropdown.selectedIndex).toBe(0); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); expect(fakeResultsDropdown.visible).toBe(true); expect(fakeResultsDropdown.selectedIndex).toBe(2); fakeCkEditorInstance.keyEnteredByUser('UpArrow', KeyCodes.UP); expect(fakeResultsDropdown.visible).toBe(true); expect(fakeResultsDropdown.selectedIndex).toBe(1); fakeCkEditorInstance.keyEnteredByUser('Escape', KeyCodes.ESC); expect(fakeResultsDropdown.visible).toBe(false); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.userPausedAfterEntry(); expect(fakeResultsDropdown.visible).toBe(true); expect(fakeResultsDropdown.selectedIndex).toBe(0); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeResultsDropdown.visible).toBe(false); })); it('should hide resultsComponent when @ is backspaced over.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.userPausedAfterEntry(); expect(fakeResultsDropdown.visible).toBe(true); fakeCkEditorInstance.keyEnteredByUser('Backspace', KeyCodes.BACKSPACE); fakeCkEditorInstance.keyEnteredByUser('Backspace', KeyCodes.BACKSPACE); fakeCkEditorInstance.userPausedAfterEntry(); expect(fakeResultsDropdown.visible).toBe(false); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.userPausedAfterEntry(); expect(fakeResultsDropdown.visible).toBe(true); })); it('should handle searching with spaces.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); fakeCkEditorInstance.keyEnteredByUser(' '); fakeCkEditorInstance.keyEnteredByUser('b'); fakeCkEditorInstance.keyEnteredByUser('u'); fakeCkEditorInstance.userPausedAfterEntry(); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); })); it('should handle searching with a space afterwards.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser('Note about: '); fakeCkEditorInstance.keyEnteredByUser('@'); fakeCkEditorInstance.keyEnteredByUser('j'); fakeCkEditorInstance.keyEnteredByUser('o'); fakeCkEditorInstance.keyEnteredByUser('h'); fakeCkEditorInstance.keyEnteredByUser('n'); fakeCkEditorInstance.keyEnteredByUser(' '); fakeCkEditorInstance.userPausedAfterEntry(); fakeCkEditorInstance.keyEnteredByUser('DownArrow', KeyCodes.DOWN); fakeCkEditorInstance.keyEnteredByUser('Enter', KeyCodes.ENTER); expect(fakeParentForm.getValue()).toEqual({ note: 'Note about: <a href=\"http://www.bullhorn.com\">@John Bullhorn</a> ', references: { person: [{ value: 'j.bullhorn', label: 'John Bullhorn' }] } }); })); it('should show/hide placeholder text properly.', fakeAsync(() => { fakeCkEditorInstance.valueSetByUser(''); expect(fakeCkEditorInstance.isPlaceholderVisible()).toBe(true); fakeCkEditorInstance.focusByUser(); expect(fakeCkEditorInstance.isPlaceholderVisible()).toBe(false); fakeCkEditorInstance.blurByUser(); expect(fakeCkEditorInstance.isPlaceholderVisible()).toBe(true); fakeCkEditorInstance.focusByUser(); fakeCkEditorInstance.valueSetByUser('.'); fakeCkEditorInstance.blurByUser(); expect(fakeCkEditorInstance.isPlaceholderVisible()).toBe(false); fakeCkEditorInstance.focusByUser(); fakeCkEditorInstance.valueSetByUser(''); fakeCkEditorInstance.blurByUser(); expect(fakeCkEditorInstance.isPlaceholderVisible()).toBe(true); })); }); });