Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

angular.element.val() and jquery.val() should call $setViewValue() when necessary #6523

Closed
benlesh opened this issue Mar 3, 2014 · 10 comments

Comments

@benlesh
Copy link
Contributor

benlesh commented Mar 3, 2014

This issue stems from trying to e2e test HTML5 inputs with protractor.

Problem

WebDriver/Selenium is unable to "sendkeys" to all browsers' implementations of HTML5 controls such as date, time, datetime-local, color, etc. Consequently, these things are hard to test.

Current workaround

Currently in order to set the value of the element with Protractor you need to use browser.executeScript(). For example:

browser.executeScript("document.getElementById('someElem').value = '2011-11-11';");

This does the work of setting the value of the input, however, the way Angular is wiring up validation, the validation will not be fired. This means you also have to manually call $setViewValue() on the model:

var updateInput = "var input = document.getElementById('someInput');" +
    "input.value = '2011-11-11';" + 
    "angular.element(input).scope().$apply(function(s) { s.formName[input.name].$setViewValue(input.value);";
browser.executeScript(updateInput);

... which is, of course, ugly as sin.

Proposed solution

It seems to make sense to have angular.element.val() check to see if the input is part of a form that has a model and force the validation. Likewise, if JQuery is being used, the val() extension could be wrapped to support this behavior. Of course val() should perform as expected on any other input that doesn't have a model bound to it.

Related

angular/protractor#562

#5864

@caitp
Copy link
Contributor

caitp commented Mar 3, 2014

giving this a quick read, this doesn't seem like the right solution --- jQuery should not have any special connection to ngModel

@benlesh
Copy link
Contributor Author

benlesh commented Mar 3, 2014

@caitp: It wouldn't be "connected", at all. The idea would be to wrap the call and simply make sure $setViewValue was called afterwards if need be.

Currently, if you programmatically update the value of an input, tied to a model, it will not trigger validation.

Programmatically updating the input is the only way to e2e test HTML5 inputs like "date" with Protractor. Needless to say, as time passes, this need will be more and more frequent.

The proposed solution will make this:

var script = "var input = document.getElementById('someInput');" + 
"input.value = '2011-11-11';" + 
"angular.element(input).scope().$apply(function(s) { s[input[0].form.name][input.name].$setViewValue('2011-11-11');";
browser.executeScript(script);

into

browser.executeScript("angular.element(document.getElementById('someInput')).val('2011-11-11')");

@benlesh
Copy link
Contributor Author

benlesh commented Mar 3, 2014

I'm always open to a custom protractor helper that does all of the above as well, but that seems wonky.... and even so, Protractor could benefit from this functionality.

@caitp
Copy link
Contributor

caitp commented Mar 3, 2014

I don't think this is right, but your PRs have been reviewed by @tbosch I think, so maybe he could look at this suggestion and see what he thinks

@benlesh
Copy link
Contributor Author

benlesh commented Mar 3, 2014

I don't think this is right

lol... Which "this"?

If I'm totally honest, I'm not entirely sure this is the best solution... I'm only sure of three things:

  1. Testing HTML5 controls with protractor is a PITA, especially in regards to Angular's validation.
  2. Getting Angular to validate programmatic changes to inputs in general smells.
  3. It all seems fixable. ... somehow.

It won't hurt my feelings if my proposed solution is rejected, at all. Just as long as there is some sort of identified fix to the issue at hand, which is testing HTML5 controls with protractor and making sure Angular validates it is a real pain.

@benlesh
Copy link
Contributor Author

benlesh commented Mar 3, 2014

Argh... I said Protractor wasn't an Angular specific tool above and that's completely false... I'm an "Angular specific tool".

I'm tired...

Maybe the solution is a helper in Protractor? @juliemr may be a helpful resource here.

@lgalfaso
Copy link
Contributor

lgalfaso commented Mar 3, 2014

I do not think that making any change to jqLite.val or jQuery.val is the right way as it is sometimes needed to have full control on when a value is changed and an event is triggered. We do not use Protractor, anyhow we do use a helper function that does

    this.isVisible = function (selector) {
      return !$rootElement.find(selector).hasClass('ng-hide');
    };
    this.editText = function (selector, text) {
      var element = $rootElement.find(selector);

      expect(this.isVisible(selector)).toBe(true);
      element.val(text);
      browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change');
    }

[...]

function browserTrigger(element, eventType) {
  'use strict';
  element = element[0];
  if (window.document.createEvent) {
    var event = window.document.createEvent('MouseEvents');
    event.initMouseEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false,
      false, false, 0, element);
    element.dispatchEvent(event);
  } else {
    if ((element.type === "checkbox" || element.type === "radio") && element.click) {
      element.click();
    } else {
      element.fireEvent('on' + eventType);
    }
  }
}

@tbosch
Copy link
Contributor

tbosch commented Mar 4, 2014

I think the right solution would have a temporary fix in protractor until webdriver supports this correctly. Protractor can monkey patch jQuery Lite / provide a new method there to do what is needed.

So solving angular/protractor#562 would be the right thing to do.

@tbosch
Copy link
Contributor

tbosch commented Mar 4, 2014

Can we close this here then?

@benlesh
Copy link
Contributor Author

benlesh commented Mar 4, 2014

@tbosch, the more I think about it, I'm inclined to agree. I talked to @juliemr, and I think I'll have a PR for that issue in protractor soon. We can definitely close this.

@benlesh benlesh closed this as completed Mar 4, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants