UNPKG

textangular

Version:

A radically powerful Text-Editor/Wysiwyg editor for Angular.js

1,192 lines (1,102 loc) 55.4 kB
describe('textAngular', function(){ /* Display Tests */ 'use strict'; afterEach(inject(function($document){ $document.find('body').html(''); })); beforeEach(module('textAngular')); var $window, element, $rootScope, textAngularManager, editorScope; describe('Minimal Initiation', function(){ beforeEach(inject(function (_$compile_, _$rootScope_) { $rootScope = _$rootScope_; element = _$compile_('<text-angular><p>Test Content</p></text-angular>')($rootScope); $rootScope.$digest(); })); describe('Adds Correct Classes and Elements', function () { it('add .ta-root to base element', function(){ expect(jQuery(element).hasClass('.ta-root')); }); it('adds 2 .ta-editor elements', function(){ expect(jQuery('.ta-editor', element).length).toBe(2); }); it('adds the WYSIWYG div', function(){ expect(jQuery('div.ta-text.ta-editor', element).length).toBe(1); }); it('adds the textarea', function(){ expect(jQuery('textarea.ta-html.ta-editor', element).length).toBe(1); }); it('no hidden form submission element', function(){ expect(jQuery('input[type=hidden][name=test]', element).length).toBe(0); }); }); }); describe('Basic Initiation without ng-model', function(){ beforeEach(inject(function (_$compile_, _$rootScope_) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test"><p>Test Content</p></text-angular>')($rootScope); $rootScope.$digest(); })); describe('Adds Correct Classes and Elements', function () { it('add .ta-root to base element', function(){ expect(jQuery(element).hasClass('.ta-root')); }); it('adds 2 .ta-editor elements', function(){ expect(jQuery('.ta-editor', element).length).toBe(2); }); it('adds the WYSIWYG div', function(){ expect(jQuery('div.ta-text.ta-editor', element).length).toBe(1); }); it('adds the textarea', function(){ expect(jQuery('textarea.ta-html.ta-editor', element).length).toBe(1); }); it('adds one hidden form submission element', function(){ expect(jQuery('input[type=hidden][name=test]', element).length).toBe(1); }); }); describe('Gets the proper stripped text', function () { it('measure time', function () { var t0 = performance.now(); var stripped = stripHtmlToText('<div class="AppContainer" style="width: 1280px;color: rgb(0, 0, 0);"><div id="c_base" class="c_base"><div id="c_content" class="c_main"><div id="pageContent"><div id="pageInbox" class="v-Page"><div id="inboxControl0f"><div class="containsYSizerBar" style="height: 849px;width: 1280px;"><div class="ContentRight WithRightRail FullView"><div class="ContentRightInner t_mbgc t_qtc t_urtc" style="color: rgb(68, 68, 68);background-color: rgb(255, 255, 255);"><div id="inboxControl0fv-ReadMessageContainer" class="v-ReadMessageContainer slideOnResize"><div class="c-ReadMessage" style="height: 818.03125px;width: 895px;"><div class="rmMessages ClearBoth" id="ReadMessageScrollableSection"><div id="readMessagePartControl1604f" class="c-ReadMessagePart ReadMsgContainer HasLayout ClearBoth HideShadows FullPart NoHistory Read RmIc"><div class="c-ReadMessagePartBody"><div class="readMsgBody"><div id="bodyreadMessagePartBodyControl1609f" class="ExternalClass MsgBodyContainer"><p><u><b>Lorem ipsum</b></u></p><p><b>Lorem ipsum</b></p></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>'); var t1 = performance.now(); var duration = (t1 - t0).toFixed(4); //console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to do something!'); expect(duration < 0.001); expect(stripped).toBe('Lorem ipsumLorem ipsum'); stripped = stripHtmlToText(' \n &#8203;&#8203; &#8203; '); expect(stripped).toBe(''); }); }); describe('Gets the proper DOM', function () { it('from Html', function () { var dom = getDomFromHtml('<div><p></p></div>'); expect(dom.innerHTML).toBe('<div><p></p></div>'); }); }); }); describe('Add classes via attributes', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) { $rootScope = _$rootScope_; textAngularManager = _textAngularManager_; element = _$compile_('<text-angular name="test" ta-focussed-class="test-focus-class" ta-text-editor-class="test-text-class" ta-html-editor-class="test-html-class"></text-angular>')($rootScope); $rootScope.$digest(); })); describe('Adds Correct Classes', function () { it('initially has no focus class', function(){ expect(!jQuery(element).hasClass('.test-focus-class')); }); it('adds focus class when ta-text is focussed', function(){ textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('focus'); expect(jQuery(element).hasClass('.test-focus-class')); }); it('adds focus class when ta-html is focussed', function(){ jQuery('.ta-html', element).triggerHandler('focus'); expect(jQuery(element).hasClass('.test-focus-class')); }); it('adds text editor class', function(){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.hasClass('.test-text-class')); }); it('adds html editor class', function(){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.hasClass('.test-html-class')); }); }); }); describe('Change classes via decorator', function(){ beforeEach(module('textAngular', function($provide){ // change all the classes at once $provide.decorator('taOptions', ['$delegate', function(taOptions){ taOptions.classes.focussed = "test-focus-class"; taOptions.classes.disabled = "disabled-test"; taOptions.classes.textEditor = 'test-text-class'; taOptions.classes.htmlEditor = 'test-html-class'; return taOptions; }]); })); beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) { $rootScope = _$rootScope_; textAngularManager = _textAngularManager_; element = _$compile_('<text-angular name="test"></text-angular>')($rootScope); $rootScope.$digest(); })); it('should add .test-focus-class instead of default .focussed', function(){ textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('focus'); expect(jQuery(element).hasClass('.test-focus-class')); }); it('adds text editor class', function(){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.hasClass('.test-text-class')); }); it('adds html editor class', function(){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.hasClass('.test-html-class')); }); it('adds disabled class', function(){ expect(jQuery(element).hasClass('.disabled-test')); }); }); describe('Add tabindex attribute', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) { $rootScope = _$rootScope_; textAngularManager = _textAngularManager_; element = _$compile_('<text-angular name="test" tabindex="42"></text-angular>')($rootScope); $rootScope.$digest(); })); describe('Check moved across', function () { it('to textEditor', function(){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.attr('tabindex')).toBe('42'); }); it('to htmlEditor', function(){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.attr('tabindex')).toBe('42'); }); it('removed from .ta-root', function(){ expect(element.attr('tabindex')).toBeUndefined(); }); }); }); describe('Use serial attribute', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) { $rootScope = _$rootScope_; textAngularManager = _textAngularManager_; element = _$compile_('<text-angular serial="test"></text-angular>')($rootScope); $rootScope.$digest(); })); describe('generates id\'s and name attributes', function () { it('name of editor', function(){ expect(textAngularManager.retrieveEditor('textAngularEditortest')).not.toBeUndefined(); }); it('to textEditor', function(){ expect(textAngularManager.retrieveEditor('textAngularEditortest').scope.displayElements.text.attr('id')).toBe('taTextElementtest'); }); it('to htmlEditor', function(){ expect(textAngularManager.retrieveEditor('textAngularEditortest').scope.displayElements.html.attr('id')).toBe('taHtmlElementtest'); }); }); }); describe('Disable the editor', function(){ beforeEach(inject(function (_$compile_, _$rootScope_) { $rootScope = _$rootScope_; $rootScope.disabled = true; element = _$compile_('<text-angular name="test" ta-disabled="disabled"></text-angular>')($rootScope); $rootScope.$digest(); })); describe('check disabled class', function () { it('is added initially', function(){ expect(jQuery(element).hasClass('disaled')); }); it('is removed on change to false', function(){ $rootScope.disabled = false; $rootScope.$digest(); expect(!jQuery(element).hasClass('disabled')); }); it('is added on change to true', function(){ $rootScope.disabled = false; $rootScope.$digest(); $rootScope.disabled = true; $rootScope.$digest(); expect(jQuery(element).hasClass('disabled')); }); }); }); it('respects the taShowHtml attribute',inject(function ($compile, $rootScope, $document) { element = $compile('<text-angular name="test" ta-show-html="true"></text-angular>')($rootScope); $document.find('body').append(element); $rootScope.$digest(); expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(false); expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(true); element.remove(); })); it('respects the taDefaultTagAttributes attribute',inject(function ($compile, $rootScope, $document, textAngularManager) { $rootScope.taTestDefaultTagAttributes = {a:{target:"_blank"}, li:{test:"testing"}}; element = $compile('<text-angular name="test" ta-default-tag-attributes="{{taTestDefaultTagAttributes}}"></text-angular>')($rootScope); $document.find('body').append(element); editorScope = textAngularManager.retrieveEditor('test').scope; $rootScope.$digest(); expect(editorScope.defaultTagAttributes).toEqual($rootScope.taTestDefaultTagAttributes); element.remove(); })); it('uses the default defaultTagAttributes when the taDefaultTagAttributes attribute throws a JSON parse error',inject(function ($compile, $rootScope, $document, textAngularManager) { var taTestDefaultTagAttributes = {a:{target:""}}; element = $compile('<text-angular name="test" ta-default-tag-attributes="invalidJSON"></text-angular>')($rootScope); $document.find('body').append(element); editorScope = textAngularManager.retrieveEditor('test').scope; $rootScope.$digest(); expect(editorScope.defaultTagAttributes).toEqual(taTestDefaultTagAttributes); element.remove(); })); describe('Check view change', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager, $document) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test"></text-angular>')($rootScope); $document.find('body').append(element); editorScope = textAngularManager.retrieveEditor('test').scope; $rootScope.$digest(); })); afterEach(function(){ element.remove(); }); it('initially should hide .ta-html and show .ta-text', function(){ expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(true); expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(false); }); describe('from WYSIWYG text to RAW HTML view', function () { it('should hide .ta-text and show .ta-html', function(){ editorScope.switchView(); editorScope.$parent.$digest(); expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(false); expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(true); }); }); describe('from RAW HTML to WYSIWYG text view', function () { it('should hide .ta-html and show .ta-text', function(){ editorScope.switchView(); editorScope.$parent.$digest(); editorScope.switchView(); editorScope.$parent.$digest(); expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(true); expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(false); }); }); }); describe('Check focussed class adding', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test"></text-angular>')($rootScope); $rootScope.$digest(); editorScope = textAngularManager.retrieveEditor('test').scope; })); describe('should have added .focussed', function(){ it('on trigger focus on ta-text', function(){ editorScope.displayElements.text.triggerHandler('focus'); expect(element.hasClass('focussed')); }); it('on trigger focus on ta-html', function(){ editorScope.displayElements.html.triggerHandler('focus'); expect(element.hasClass('focussed')); }); }); describe('should have removed .focussed', function(){ it('on ta-text trigger blur', function(){ editorScope.displayElements.text.triggerHandler('focus'); $rootScope.$digest(); editorScope.displayElements.text.triggerHandler('blur'); expect(!element.hasClass('focussed')); }); it('on ta-html trigger blur', function(){ editorScope.displayElements.html.triggerHandler('focus'); $rootScope.$digest(); editorScope.displayElements.html.triggerHandler('blur'); expect(!element.hasClass('focussed')); }); }); }); describe('Check text and html editor setup functions', function(){ beforeEach(inject(function (_$compile_, _$rootScope_) { $rootScope = _$rootScope_; $rootScope.attrSetup = function($element){ $element.attr('testattr', 'trueish'); }; element = _$compile_('<text-angular ta-text-editor-setup="attrSetup" ta-html-editor-setup="attrSetup" name="test"></text-angular>')($rootScope); $rootScope.$digest(); })); describe('should have added attribute to', function(){ it('ta-text', inject(function(textAngularManager){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.attr('testattr')).toBe('trueish'); })); it('ta-html', inject(function(textAngularManager){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.attr('testattr')).toBe('trueish'); })); }); }); describe('Check placeholder passthrough', function(){ beforeEach(inject(function (_$compile_, _$rootScope_) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test" placeholder="Test Placeholder"></text-angular>')($rootScope); $rootScope.$digest(); })); describe('should have added placeholder to', function(){ it('ta-text', inject(function(textAngularManager){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.attr('placeholder')).toBe('Test Placeholder'); })); it('ta-html', inject(function(textAngularManager){ expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.attr('placeholder')).toBe('Test Placeholder'); })); }); }); describe('registration', function(){ it('should add itself to the textAngularManager', inject(function($rootScope, $compile, textAngularManager){ $compile('<text-angular name="test"></text-angular>')($rootScope); expect(textAngularManager.retrieveEditor('test')).not.toBeUndefined(); })); it('should register toolbars to itself', inject(function($rootScope, $compile, textAngularManager){ var toolbar1 = {name: 'test-toolbar1'}; var toolbar2 = {name: 'test-toolbar2'}; textAngularManager.registerToolbar(toolbar1); textAngularManager.registerToolbar(toolbar2); $compile('<text-angular name="test" ta-target-toolbars="test-toolbar1,test-toolbar2"></text-angular>')($rootScope); var _toolbars = textAngularManager.retrieveToolbarsViaEditor('test'); expect(_toolbars[0]).toBe(toolbar1); expect(_toolbars[1]).toBe(toolbar2); })); }); describe('unregistration', function(){ it('should remove itself from the textAngularManager on $destroy', inject(function($rootScope, $compile, textAngularManager){ element = $compile('<text-angular name="test"></text-angular>')($rootScope); $rootScope.$digest(); textAngularManager.retrieveEditor('test').scope.$destroy(); expect(textAngularManager.retrieveEditor('test')).toBeUndefined(); })); }); /* Form validation tests */ describe('form validation', function(){ describe('basic', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, _$window_, $document, textAngularManager) { $window = _$window_; $rootScope = _$rootScope_; $rootScope.html = ''; var _form = angular.element('<form name="form"></form>'); element = angular.element('<text-angular name="test" ng-model="html"></text-angular>'); _form.append(element); $document.find('body').append(_$compile_(_form)($rootScope)); element = textAngularManager.retrieveEditor('test').scope.displayElements.text; $rootScope.$digest(); })); describe('should start with', function () { it('pristine', function(){ expect($rootScope.form.$pristine).toBe(true); }); it('field pristine', function(){ expect($rootScope.form.test.$pristine).toBe(true); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); describe('should NOT change on direct model change', function () { beforeEach(function(){ $rootScope.htmlcontent = '<div>Test Change Content</div>'; $rootScope.$digest(); }); it('pristine', function(){ expect($rootScope.form.$pristine).toBe(true); }); it('field pristine', function(){ expect($rootScope.form.test.$pristine).toBe(true); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); describe('should change on input update', function () { beforeEach(inject(function(textAngularManager, $timeout){ element.html('<div>Test Change Content</div>'); element.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); })); it('not pristine', function(){ expect($rootScope.form.$pristine).toBe(false); }); it('field not pristine', function(){ expect($rootScope.form.test.$pristine).toBe(false); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); describe('should change on blur', function () { beforeEach(inject(function(textAngularManager){ element.html('<div>Test Change Content</div>'); element.triggerHandler('blur'); $rootScope.$digest(); })); it('not pristine', function(){ expect($rootScope.form.$pristine).toBe(false); }); it('field not pristine', function(){ expect($rootScope.form.test.$pristine).toBe(false); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); }); describe('with errors', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, _$window_, $document, textAngularManager) { $window = _$window_; $rootScope = _$rootScope_; $rootScope.html = ''; var _form = angular.element('<form name="form"></form>'); element = angular.element('<text-angular name="test" ng-model="html" required></text-angular>'); _form.append(element); $document.find('body').append(_$compile_(_form)($rootScope)); element = textAngularManager.retrieveEditor('test').scope.displayElements.text; $rootScope.$digest(); })); describe('should start with', function () { it('ng-required', function(){ expect($rootScope.form.test.$error.required).toBe(true); }); it('valid', function(){ expect($rootScope.form.$invalid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$invalid).toBe(true); }); }); describe('should change on direct model change', function () { beforeEach(function(){ $rootScope.html = '<div>Test Change Content</div>'; $rootScope.$digest(); }); it('ng-required', function(){ expect($rootScope.form.test.$error.required).toBe(undefined); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); describe('should change on input update', function() { beforeEach(inject(function(textAngularManager, $timeout){ element.html('<div>Test Change Content</div>'); element.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); })); it('ng-required', function(){ expect($rootScope.form.test.$error.required).toBe(undefined); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); describe('should change on blur', function () { beforeEach(inject(function(textAngularManager){ element.html('<div>Test Change Content</div>'); element.triggerHandler('blur'); $rootScope.$digest(); })); it('ng-required', function(){ expect($rootScope.form.test.$error.required).toBe(undefined); }); it('valid', function(){ expect($rootScope.form.$valid).toBe(true); }); it('field valid', function(){ expect($rootScope.form.test.$valid).toBe(true); }); }); }); }); /* Data Tests */ describe('Basic Initiation without ng-model', function(){ var displayElements; beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test"><p>Test Content</p></text-angular>')($rootScope); $rootScope.$digest(); displayElements = textAngularManager.retrieveEditor('test').scope.displayElements; })); describe('Inserts the current information into the fields', function(){ it('populates the WYSIWYG area', function(){ expect(displayElements.text.find('p').length).toBe(1); }); it('populates the textarea', function(){ expect(displayElements.html.val()).toBe('<p>Test Content</p>'); }); it('populates the hidden input value', function(){ expect(displayElements.forminput.val()).toBe('<p>Test Content</p>'); }); }); }); describe('Basic Initiation with ng-model', function(){ var displayElements; beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; $rootScope.html = '<p>Test Content</p>'; element = _$compile_('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); displayElements = textAngularManager.retrieveEditor('test').scope.displayElements; })); describe('Inserts the current information into the fields', function(){ it('populates the WYSIWYG area', function(){ expect(displayElements.text.find('p').length).toBe(1); }); it('populates the textarea', function(){ expect(displayElements.html.val()).toBe('<p>Test Content</p>'); }); it('populates the hidden input value', function(){ expect(displayElements.forminput.val()).toBe('<p>Test Content</p>'); }); }); }); describe('Basic Initiation with ng-model and originalContents', function(){ var displayElements; beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; $rootScope.html = '<p>Test Content</p>'; element = _$compile_('<text-angular name="test" ng-model="html"><p>Original Content</p></text-angular>')($rootScope); $rootScope.$digest(); displayElements = textAngularManager.retrieveEditor('test').scope.displayElements; })); describe('Inserts the current information into the fields', function(){ it('populates the WYSIWYG area', function(){ expect(displayElements.text.find('p').length).toBe(1); }); it('populates the textarea', function(){ expect(displayElements.html.val()).toBe('<p>Test Content</p>'); }); it('populates the hidden input value', function(){ expect(displayElements.forminput.val()).toBe('<p>Test Content</p>'); }); }); }); describe('should respect the ta-default-wrap value', function(){ it('with ng-model', inject(function($rootScope, $compile, textAngularManager){ $rootScope.html = ''; $compile('<text-angular ta-default-wrap="div" name="test" ng-model="html"></text-angular>')($rootScope); element = textAngularManager.retrieveEditor('test').scope.displayElements.text; $rootScope.$digest(); element.triggerHandler('focus'); $rootScope.$digest(); expect(element.html()).toBe('<div><br></div>'); })); it('without ng-model', inject(function($rootScope, $compile, textAngularManager){ $compile('<text-angular ta-default-wrap="div" name="test"></text-angular>')($rootScope); element = textAngularManager.retrieveEditor('test').scope.displayElements.text; $rootScope.$digest(); element.triggerHandler('focus'); $rootScope.$digest(); expect(element.html()).toBe('<div><br></div>'); })); }); describe('should respect taUnsafeSanitizer attribute', function () { var element2, displayElements, $timeout; beforeEach(inject(function(_$timeout_){ $timeout = _$timeout_; })); describe('without ng-model', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test" ta-unsafe-sanitizer="true"><p>Test Contents</p></text-angular>')($rootScope); $rootScope.$digest(); editorScope = textAngularManager.retrieveEditor('test').scope; element = textAngularManager.retrieveEditor('test').scope.displayElements.text; element2 = textAngularManager.retrieveEditor('test').scope.displayElements.html; })); it('allow bad tags', function () { element.append('<bad-tag>Test 2 Content</bad-tag>'); element.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); expect(element2.val()).toBe('<p>Test Contents</p>\n<p><bad-tag>Test 2 Content</bad-tag></p>'); }); it('not allow malformed html', function () { element.append('<bad-tag Test 2 Content</bad-tag>'); element.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); expect(element2.val()).toBe('<p>Test Contents</p>'); }); }); describe('with ng-model', function(){ beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; $rootScope.html = '<p>Test Contents</p>'; element = _$compile_('<text-angular name="test" ta-unsafe-sanitizer="true" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); editorScope = textAngularManager.retrieveEditor('test').scope; element = editorScope.displayElements.text; })); it('allow bad tags', function () { element.append('<bad-tag>Test 2 Content</bad-tag>'); element.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); expect($rootScope.html).toBe('<p>Test Contents</p><p><bad-tag>Test 2 Content</bad-tag></p>'); }); it('not allow malformed html', function () { element.append('<bad-tag Test 2 Content</bad-tag>'); element.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); expect($rootScope.html).toBe('<p>Test Contents</p>'); }); }); }); describe('Updates without ng-model', function(){ var displayElements; beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager, $timeout) { $rootScope = _$rootScope_; element = _$compile_('<text-angular name="test"><p>Test Content</p></text-angular>')($rootScope); $rootScope.$digest(); displayElements = textAngularManager.retrieveEditor('test').scope.displayElements; displayElements.text.html('<div>Test Change Content</div>'); displayElements.text.triggerHandler('keyup'); $rootScope.$digest(); $timeout.flush(); })); describe('updates from .ta-text', function(){ it('should update .ta-html', function(){ expect(displayElements.html.val()).toBe('<div>Test Change Content</div>'); }); it('should update input[type=hidden]', function(){ expect(displayElements.forminput.val()).toBe('<div>Test Change Content</div>'); }); }); describe('updates from .ta-html', function(){ it('should update .ta-text', function(){ expect(displayElements.text.html()).toBe('<div>Test Change Content</div>'); }); it('should update input[type=hidden]', function(){ expect(displayElements.forminput.val()).toBe('<div>Test Change Content</div>'); }); }); }); describe('Updates with ng-model', function(){ var displayElements; beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) { $rootScope = _$rootScope_; $rootScope.html = '<p>Test Content</p>'; element = _$compile_('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); $rootScope.html = '<div>Test Change Content</div>'; $rootScope.$digest(); displayElements = textAngularManager.retrieveEditor('test').scope.displayElements; })); describe('updates from model to display', function(){ it('should update .ta-html', function(){ expect(displayElements.html.val()).toBe('<div>Test Change Content</div>'); }); it('should update .ta-text', function(){ expect(displayElements.text.html()).toBe('<div>Test Change Content</div>'); }); it('should update input[type=hidden]', function(){ expect(displayElements.forminput.val()).toBe('<div>Test Change Content</div>'); }); }); }); describe('ng-model should handle undefined and null', function(){ it('should handle initial undefined to empty-string', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = undefined; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>'); })); it('should handle initial null to empty-string', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = null; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>'); })); it('should handle initial undefined to originalContents', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = undefined; element = $compile('<text-angular name="test" ng-model="html">Test Contents</text-angular>')($rootScope); $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p>Test Contents</p>'); })); it('should handle initial null to originalContents', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = null; element = $compile('<text-angular name="test" ng-model="html">Test Contents</text-angular>')($rootScope); $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p>Test Contents</p>'); })); describe('should reset', function(){ it('from undefined to empty-string', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = 'Test Content'; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); $rootScope.html = undefined; $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>'); })); it('from null to empty-string', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = 'Test Content'; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $rootScope.$digest(); $rootScope.html = null; $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>'); })); it('from undefined to blank/emptystring WITH originalContents', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = 'Test Content1'; element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope); $rootScope.$digest(); $rootScope.html = undefined; $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>'); })); it('from null to blank/emptystring WITH originalContents', inject(function ($compile, $rootScope, textAngularManager) { $rootScope.html = 'Test Content1'; element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope); $rootScope.$digest(); $rootScope.html = null; $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>'); })); }); }); describe('should have correct startAction and endAction functions', function(){ it('should have rangy loaded with save-restore module', function(){ expect(window.rangy).toBeDefined(); expect(window.rangy.saveSelection).toBeDefined(); }); var sel, range; beforeEach(inject(function($compile, $rootScope, textAngularManager, $document){ $rootScope.html = '<p>Lorem ipsum dolor sit amet, <i>consectetur adipisicing</i> elit, <strong>sed do eiusmod tempor incididunt</strong> ut labore et dolore magna aliqua.</p>'; element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope); $document.find('body').append(element); $rootScope.$digest(); editorScope = textAngularManager.retrieveEditor('test').scope; // setup selection sel = window.rangy.getSelection(); range = window.rangy.createRangyRange(); range.selectNodeContents(editorScope.displayElements.text.find('p').find('strong')[0]); sel.setSingleRange(range); })); afterEach(function(){ element.remove(); }); describe('startAction should return a function that will restore a selection', function(){ it('should start with the correct selection', function(){ expect(sel.getRangeAt(0).toHtml()).toBe('sed do eiusmod tempor incididunt'); }); it('should set _actionRunning to true', function(){ editorScope.startAction(); expect(editorScope._actionRunning); }); it('should return a function that resets the selection', function(){ var resetFunc = editorScope.startAction(); expect(sel.toHtml()).toBe('sed do eiusmod tempor incididunt'); // change selection var range = window.rangy.createRangyRange(); range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]); sel.setSingleRange(range); sel.refresh(); expect(sel.toHtml()).toBe('consectetur adipisicing'); // reset selection resetFunc(); sel.refresh(); expect(sel.toHtml()).toBe('sed do eiusmod tempor incididunt'); }); }); describe('endAction should remove the ability to restore selection', function(){ it('shouldn\'t affect the selection', function(){ editorScope.startAction(); editorScope.endAction(); expect(sel.toHtml()).toBe('sed do eiusmod tempor incididunt'); }); it('shouldn\'t restore the selection', function(){ var resetFunc = editorScope.startAction(); editorScope.endAction(); var range = window.rangy.createRangyRange(); range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]); sel.setSingleRange(range); sel.refresh(); expect(sel.toHtml()).toBe('consectetur adipisicing'); // reset selection - should do nothing now resetFunc(); sel.refresh(); expect(sel.toHtml()).toBe('consectetur adipisicing'); }); }); }); describe('Toolbar interaction functions work', function(){ var sel, range; beforeEach(inject(function(taRegisterTool, taOptions, taSelectableElements){ taSelectableElements.push('i'); taRegisterTool('testbutton', { buttontext: 'reactive action', action: function(){ return this.$element.attr('hit-this', 'true'); }, commandKeyCode: 21, activeState: function(){ return true; }, onElementSelect: { element: 'i', action: function(event, element, editorScope){ element.attr('hit-via-select', 'true'); this.$element.attr('hit-via-select', 'true'); } } }); taOptions.toolbar = [ ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre', 'quote', 'testbutton'], ['bold', 'italics', 'underline', 'strikeThrough', 'ul', 'ol', 'redo', 'undo', 'clear'], ['justifyLeft','justifyCenter','justifyRight','indent','outdent'], ['html', 'insertImage', 'insertLink', 'unlink'] ]; })); beforeEach(inject(function($compile, $rootScope, textAngularManager, $document){ $rootScope.html = '<p>Lorem ipsum <u>dolor sit amet<u>, consectetur <i>adipisicing elit, sed do eiusmod tempor incididunt</i> ut labore et <a>dolore</a> magna aliqua.</p>'; element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope); $document.find('body').append(element); editorScope = textAngularManager.retrieveEditor('test').scope; textAngularManager.retrieveEditor('test').editorFunctions.focus(); $rootScope.$digest(); // setup selection sel = window.rangy.getSelection(); range = window.rangy.createRangyRange(); range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]); sel.setSingleRange(range); })); afterEach(inject(function(taSelectableElements){ taSelectableElements.pop(); element.remove(); })); it('should not trigger focus out while an action is processing', inject(function($timeout){ editorScope.displayElements.text.triggerHandler('focus'); editorScope.$parent.$digest(); editorScope.startAction(); editorScope.displayElements.text.triggerHandler('blur'); editorScope.$parent.$digest(); $timeout.flush(); expect(jQuery(element[0]).find('button:disabled').length).toBe(0); })); it('keypress should call sendKeyCommand', function(){ editorScope.displayElements.text.triggerHandler('keypress', {metaKey: true, which: 21}); editorScope.$parent.$digest(); expect(jQuery(element[0]).find('.ta-toolbar button[name=testbutton]').attr('hit-this')).toBe('true'); }); describe('wrapSelection', function(){ it('should wrap the selected text in tags', function(){ editorScope.wrapSelection('bold'); expect(editorScope.displayElements.text.find('p').find('b').length).toBe(1); }); it('should unwrap the selected text in tags on re-call', function(){ editorScope.wrapSelection('bold'); editorScope.wrapSelection('bold'); expect(editorScope.displayElements.text.find('p').find('b').length).toBe(0); }); }); describe('queryFormatBlockState', function(){ it('should return true if formatted', function(){ editorScope.wrapSelection("formatBlock", "<PRE>"); expect(editorScope.queryFormatBlockState('PRE')).toBe(true); }); it('should return false if un-formatted', function(){ expect(editorScope.queryFormatBlockState('PRE')).toBe(false); }); }); describe('queryCommandState', function(){ it('should return value if not showHtml', function(){ editorScope.showHtml = false; editorScope.$parent.$digest(); expect(editorScope.queryCommandState('bold')).not.toBe(''); }); it('should return empty if showHtml', function(){ editorScope.showHtml = true; editorScope.$parent.$digest(); expect(editorScope.queryCommandState('bold')).toBe(''); }); }); describe('ta-element-select event', function(){ it('fires correctly on element selector', function(){ var triggerElement = editorScope.displayElements.text.find('i'); triggerElement.triggerHandler('click'); expect(triggerElement.attr('hit-via-select')).toBe('true'); }); it('fires correctly when filter returns true', inject(function(taTools){ taTools.testbutton.onElementSelect.filter = function(){ return true; }; var triggerElement = editorScope.displayElements.text.find('i'); triggerElement.triggerHandler('click'); expect(triggerElement.attr('hit-via-select')).toBe('true'); })); it('does not fire when filter returns false', inject(function(taTools){ taTools.testbutton.onElementSelect.filter = function(){ return false; }; var triggerElement = editorScope.displayElements.text.find('i'); triggerElement.triggerHandler('click'); expect(triggerElement.attr('hit-via-select')).toBeUndefined(); })); }); describe('popover', function(){ it('should show the popover', function(){ editorScope.showPopover(editorScope.displayElements.text.find('p').find('i')); editorScope.$parent.$digest(); expect(editorScope.displayElements.popover.hasClass('in')).toBe(true); }); describe('should hide the popover', function(){ beforeEach(inject(function($timeout){ editorScope.showPopover(editorScope.displayElements.text.find('p').find('i')); editorScope.$parent.$digest(); $timeout.flush(); editorScope.$parent.$digest(); })); it('on function call', function(){ editorScope.hidePopover(); editorScope.$parent.$digest(); expect(editorScope.displayElements.popover.hasClass('in')).toBe(false); }); it('on click in editor', inject(function($document){ $document.find('body').triggerHandler('click'); editorScope.$parent.$digest(); expect(editorScope.displayElements.popover.hasClass('in')).toBe(false); })); it('should prevent mousedown from propagating up from popover', function(){ var event; if(angular.element === jQuery){ event = jQuery.Event('mousedown'); editorScope.displayElements.popover.triggerHandler(event); editorScope.$parent.$digest(); expect(event.isDefaultPrevented()).toBe(true); }else{ var _defaultPrevented = false; event = {preventDefault: function(){ _defaultPrevented = true; }}; editorScope.displayElements.popover.triggerHandler('mousedown', event); editorScope.$parent.$digest(); expect(_defaultPrevented).toBe(true); } }); }); }); describe('updating styles', function(){ var iButton; beforeEach(function(){ //find italics button iButton = element.find('button[name=italics]'); }); it('should be initially active when selected', function(){ expect(iButton.hasClass('active')); }); it('should change on keypress', function(){ range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]); sel.setSingleRange(range); editorScope.displayElements.text.triggerHandler('keypress'); expect(!iButton.hasClass('active')); }); it('should change on keydown and stop on keyup', inject(function($timeout, $document){ $document.hasFocus = function(){return true;}; editorScope.displayElements.text.triggerHandler('keydown'); range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]); sel.setSingleRange(range); setTimeout(function(){ expect(!iButton.hasClass('active')); setTimeout(function(){ range = window.rangy.createRangyRange(); range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]); setTimeout(function(){ expect(iButton.hasClass('active')); editorScope.displayElements.text.triggerHandler('keydown'); setTimeout(function(){ range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]); setTimeout(function(){ expect(iButton.hasClass('active')); }, 201); }, 201); }, 201); }, 201); }, 201); })); it('should change on mouseup', function(){ range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]); sel.setSingleRange(range); editorScope.displayElements.text.triggerHandler('mouseup'); expect(!iButton.hasClass('active')); }); }); }); describe('File Drop Event', function(){ beforeEach(inject(function(_textAngularManager_){ textAngularManager = _textAngularManager_; })); afterEach(inject(function($timeout){ try{ $timeout.flush(); }catch(e){} })); it('should respect the function set in the attribute', inject(function($compile, $rootScope){ $rootScope.html = ''; var testvar = false; $rootScope.testhandler = function(){ testvar = true; }; element = $compile('<text-angular name="test" ta-file-drop="testhandler" ng-model="html"></text-angular>')($rootScope); if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); $rootScope.$digest(); expect(testvar).toBe(true); })); it('when no attribute should use the default handler in taOptions', inject(function($compile, $rootScope, taOptions){ $rootScope.html = ''; var testvar = false; taOptions.defaultFileDropHandler = function(){ testvar = true; }; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); $rootScope.$digest(); expect(testvar).toBe(true); })); it('handler not fired when no files and no errors', inject(function($compile, $rootScope, taOptions){ $rootScope.html = ''; var testvar = false; taOptions.defaultFileDropHandler = function(){ testvar = true; }; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: [], types: ['url or something']}}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: [], types: ['url or something']}}}); $rootScope.$digest(); expect(testvar).toBe(false); expect(function(){ if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {}}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {}}}); }).not.toThrow(); expect(function(){ if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {}}); }).not.toThrow(); })); describe('check handler return values respected', function(){ it('default inserts returned html', inject(function($compile, $rootScope, taOptions, $document){ $rootScope.html = ''; taOptions.defaultFileDropHandler = function(file, insertAction){ insertAction('insertHtml', '<img/>'); return true; }; element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope); $document.find('body').append(element); $rootScope.$digest(); if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><img><br></p>'); element.remove(); })); it('attr function inserts returned html', inject(function($compile, $rootScope, taOptions, $document){ $rootScope.html = ''; $rootScope.testhandler = function(file, insertAction){ insertAction('insertHtml', '<img/>'); return true; }; element = $compile('<text-angular name="test" ta-file-drop="testhandler" ng-model="html"></text-angular>')($rootScope); $document.find('body').append(element); $rootScope.$digest(); if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}}); $rootScope.$digest(); expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><img><br></p>'); element.remove(); })); it('attr function ove