diff --git a/angular-contenteditable-scenario.js b/angular-contenteditable-scenario.js new file mode 100644 index 0000000..2007088 --- /dev/null +++ b/angular-contenteditable-scenario.js @@ -0,0 +1,165 @@ + +/** + * we copied most of the 'element' scenario dsl so we can keep the old actions + * and also add the 'enter' and modified 'html' actions. + * @see https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js + * @see http://stackoverflow.com/questions/12575199/how-to-test-a-contenteditable-field-based-on-a-td-using-angularjs-e2e-testing + * + * Usage: + * element(selector, label).count() get the number of elements that match selector + * element(selector, label).click() clicks an element + * element(selector, label).mouseover() mouseover an element + * element(selector, label).mousedown() mousedown an element + * element(selector, label).mouseup() mouseup an element + * element(selector, label).query(fn) executes fn(selectedElements, done) + * element(selector, label).{method}() gets the value (as defined by jQuery, ex. val) + * element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val) + * element(selector, label).{method}(key) gets the value (as defined by jQuery, ex. attr) + * element(selector, label).{method}(key, value) sets the value (as defined by jQuery, ex. attr) + * element(selector, label).enter(value) sets the text if the element is contenteditable + */ +angular.scenario.dsl('element', function() { + var KEY_VALUE_METHODS = ['attr', 'css', 'prop']; + var VALUE_METHODS = [ + 'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width', + 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset' + ]; + var chain = {}; + + chain.count = function() { + return this.addFutureAction("element '" + this.label + "' count", function($window, $document, done) { + try { + done(null, $document.elements().length); + } catch (e) { + done(null, 0); + } + }); + }; + + chain.click = function() { + return this.addFutureAction("element '" + this.label + "' click", function($window, $document, done) { + var elements = $document.elements(); + var href = elements.attr('href'); + var eventProcessDefault = elements.trigger('click')[0]; + + if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { + this.application.navigateTo(href, function() { + done(); + }, done); + } else { + done(); + } + }); + }; + + chain.dblclick = function() { + return this.addFutureAction("element '" + this.label + "' dblclick", function($window, $document, done) { + var elements = $document.elements(); + var href = elements.attr('href'); + var eventProcessDefault = elements.trigger('dblclick')[0]; + + if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) { + this.application.navigateTo(href, function() { + done(); + }, done); + } else { + done(); + } + }); + }; + + chain.mouseover = function() { + return this.addFutureAction("element '" + this.label + "' mouseover", function($window, $document, done) { + var elements = $document.elements(); + elements.trigger('mouseover'); + done(); + }); + }; + + chain.mousedown = function() { + return this.addFutureAction("element '" + this.label + "' mousedown", function($window, $document, done) { + var elements = $document.elements(); + elements.trigger('mousedown'); + done(); + }); + }; + + chain.mouseup = function() { + return this.addFutureAction("element '" + this.label + "' mouseup", function($window, $document, done) { + var elements = $document.elements(); + elements.trigger('mouseup'); + done(); + }); + }; + + chain.query = function(fn) { + return this.addFutureAction('element ' + this.label + ' custom query', function($window, $document, done) { + fn.call(this, $document.elements(), done); + }); + }; + + angular.forEach(KEY_VALUE_METHODS, function(methodName) { + chain[methodName] = function(name, value) { + var args = arguments, + futureName = (args.length == 1) + ? "element '" + this.label + "' get " + methodName + " '" + name + "'" + : "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" + value + "'"; + + return this.addFutureAction(futureName, function($window, $document, done) { + var element = $document.elements(); + done(null, element[methodName].apply(element, args)); + }); + }; + }); + + angular.forEach(VALUE_METHODS, function(methodName) { + chain[methodName] = function(value) { + var args = arguments, + futureName = (args.length == 0) + ? "element '" + this.label + "' " + methodName + : futureName = "element '" + this.label + "' set " + methodName + " to '" + value + "'"; + + return this.addFutureAction(futureName, function($window, $document, done) { + var element = $document.elements(); + done(null, element[methodName].apply(element, args)); + }); + }; + }); + + // =============== These are the methods ================ \\ + chain.enter = function(value) { + return this.addFutureAction("element '" + this.label + "' enter '" + value + "'", function($window, $document, done) { + var element = $document.elements() + if (element.is('[contenteditable=""]') + || (element.attr('contenteditable') + && element.attr('contenteditable').match(/true/i))) { + element.text(value) + element.trigger('input') + } + done() + }) + } + + chain.html = function(value) { + var args = arguments, + futureName = (args.length == 0) + ? "element '" + this.label + "' html" + : futureName = "element '" + this.label + "' set html to '" + value + "'"; + return this.addFutureAction(futureName, function($window, $document, done) { + var element = $document.elements(); + element.html.apply(element, args) + if (args.length > 0 + && (element.is('[contenteditable=""]') + || (element.attr('contenteditable') + && element.attr('contenteditable').match(/true/i)))) { + element.trigger('input') + } + done(null, element.html.apply(element, args)); + }); + }; + + return function(selector, label) { + this.dsl.using(selector, label); + return chain; + }; +}); diff --git a/bower.json b/bower.json index d10ae2b..03c899c 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-contenteditable", - "version": "0.3.3", + "version": "0.3.4", "main": "angular-contenteditable.js", "ignore": [ ".*", diff --git a/test/e2e.karma.coffee b/test/e2e.karma.coffee index d7617fb..d0e82f4 100644 --- a/test/e2e.karma.coffee +++ b/test/e2e.karma.coffee @@ -19,6 +19,7 @@ module.exports = (karma) -> files: [ 'test/e2e/**/*.coffee' 'angular-contenteditable.js' + 'angular-contenteditable-scenario.js' ].concat toServe exclude: [] diff --git a/test/e2e/scenarios.coffee b/test/e2e/scenarios.coffee index d2c29e9..455a3ab 100644 --- a/test/e2e/scenarios.coffee +++ b/test/e2e/scenarios.coffee @@ -1,26 +1,3 @@ -angular.scenario.dsl 'contenteditable', -> - (name) -> - @name = name - enter: (value) -> - @addFutureAction "contenteditable '#{@name}' enter '#{value}'" - , ($window, $document, done) -> - elmt = $document.elements @name - elmt.text value - elmt.trigger 'input' - done() - html: (args...) -> - futureName = - if args.length == 0 - "contenteditable '#{@name}' html" - else - "contenteditable '#{@name}' set html to '#{args[0]}'" - @addFutureAction futureName - , ($window, $document, done) -> - elmt = $document.elements @name - elmt.html args - elmt.trigger 'input' - done(null, elmt.html(args)) - angular.scenario.dsl 'scope', -> (ctrl, arg = null) -> futureName = @@ -54,13 +31,13 @@ describe 'module contenteditable', -> browser().navigateTo 'base/test/fixtures/simple.html' it 'should update the model from the view (simple text)', -> - contenteditable('#input').enter('abc') + element('#input').enter('abc') expect(element('#input').html()).toBe 'abc' expect(scope('Ctrl', 'model')).toBe 'abc' expect(element('#output').html()).toBe 'abc' it 'should update the model from the view (text with spans)', -> - contenteditable('#input').html('abc red') + element('#input').html('abc red') expect(scope('Ctrl', 'model')).toBe 'abc red' expect(element('#input span').html()).toBe 'red' expect(element('#output').html()).toBe 'abc <span style="color:red">red</span>' @@ -73,6 +50,5 @@ describe 'module contenteditable', -> scope('Ctrl', ($scope) -> $scope.model = 'a red b') expect(element('#input').html()).toBe 'a red b' expect(element('#input span').html()).toBe 'red' - ## Why doesn't it work on this one??!! # expect(element('#output').html()).toBe 'oops'