protractor test failing because ElementNotVisibleError after adding ng-if directive - angularjs

I have a web element which the test was able to find until we intruduce ng-if directive to the same. Now my test is not able to find the web element:
ElementNotVisibleError: element not visible
<select ng-if="tasktypepermission" id="elementType" ng-model="task.elementType" ng-options="key as value for (key, value) in elementTypeoptions" name="elementType" class="span3 ng-scope ng-pristine ng-valid errorForInput" data-trigger="blur focusout" tasknumber-validator="" ng-disabled="task.id">
<option class="blank" value="">Select</option>
<option value="1">Val1</option>
<option value="2">Val2</option>
<option value="3">Val3</option>
</select>
The way I am accessing the element is:
var taskType = function(){ return element(by.id('elementType'));};
What should I do to make it work? I am very new to protractor or even front end testing, so please bear with me.

I suggest to try using elementExplorer. you can try to find the requested element in multiple ways (by class,name, ng-model).
it could make things clearer - especially if you are new to protractor.
please let me know if you don't know how to use it.

Related

Angularjs ng-model - convert string to number

I see a blank drop down, which I suspect is due to an additional option that gets injected. If I am able to remove this additional option generated, I think, my problem will be solved.
Auto-injected Option:
<option value="? number:1 ?" selected="selected"></option>
My View:
<select id="myValue"
ng-model="myObject.value"
ng-change="foo()">
<option value="0"
localize="MyValue0"
ng-selected="myObject.value === 0"></option>
<option value="1"
localize="MyValue1"
ng-selected="myObject.value === 1"></option>
</select>
How can I conver myObject.value to number? I have tried ng-Model = "parseInt(myObject.value)" and ng-model = "myObject.value | number". Both throw a nonassignable element error. I am using Angularjs 1.6 and moving away from directives, so creating angular.directive function may not be an option
I believe it's Angular best practice to use the ngOptions directive, documented here.
Also, regarding the first option being <option value="? number:1 ?" selected="selected"></option>, I suspect that's because the initial value of myObject.value doesn't match any of the possible options values.
Try this:
<select id="myValue" ng-model="myObject.value" ng-change="foo()"
ng-options="item for item in options">
<option value="" disabled selected style="display:none;">Placeholder</option>
</select>
JSFiddle here showing that myObject.value is a number.
Updated JSFiddle to showcase the dropdown without a placeholder.

angularjs ng-model(s) $scope

I'm quite new in AngularJs, I just started to learn this World today. I think I got the point, but I do not understand the next problem.
I have a site which contains a lot of select input fields. I will explain my problem with two select fields
********** THE HTML FILE *************
enter code here
<div id="FormContainer" ng-app="PermissionsApp">
<div class="permission_container" ng-controller="PermsCtrl">
<select id="select_all" ng-model="select_all_var" ng-change="UpdateAll()">
<option value="" ng-hide="true">-</option>
<option value="0">-</option>
<option value="n">no</option>
<option value="a" ng-selected="true">all</option>
<option value="g">group</option>
<option value="o">own</option>
</select>
<select id="select_1_1" ng-model="select_1_1_var" ng-change="Update(1, 1)">
<option value="" ng-hide="true">--</option>
<option value="n">no</option>
<option value="a" ng-selected="true">all</option>
<option value="g">group</option>
<option value="o">own</option>
</select>
</div>
</div>
************** THE JS FILE **************
enter code here
var permissionsApp = angular.module("PermissionsApp", []);
permissionsApp.controller("PermsCtrl",['$scope', function($scope) {
$scope.UpdateAll = function () {
console.log($scope.select_all_var + " - " + $scope.select_1_1_var);
}
$scope.Update = function (modul_id, action_id) {
console.log($scope.select_all_var + " - " + $scope.select_1_1_var);
}
}]);
OK ...
FIRST ISSUE
IF I change FIRST the select_all field after page reload, I get the next on the console: g - undefined
After that I change the select_1_1 field, I get the next on the console: g - g
SECOND ISSUE
If I change FIRST the select_1_1 fieldl after page reload I get the next on the console: undefined - g
After that I change the select_all field, I get the next on the console: g - g
SO ... it seems for me the variables can be seen in the $scope just after the change event .... do I need to declare the variables first time, or what's the problem? But I there are about 200 select fields on the page ... I do not want to declare them one by one.
Thanks for your answers in advance!
FF
So there are 2 ways to fix this:
ng-init
You can initialize your model to the desired value using:
<select ng-model="select_1_1_var"
ng-init="select_1_1_var = 'a'">
I don't use ng-selected much so I'm not familiar with its inner workings, but I would make an educated guess that this works solely on the UI and that it argues with other directives regarding initializing model values.
example - https://plnkr.co/edit/eO60jeXIKlDhAFuopnUc?p=preview
ng-options
So there has been a lacking feature (in my opinion) that you couldn't initialize a select control with a pre-existing model value when declaring your option elements. This is especially problematic when trying to use ng-repeat. It is recommended that you use ng-options and set your options via the controller as well as initialize your model values there:
<select id="select_all"
ng-model="select_all_var"
ng-options="opt.value as opt.name for opt in opts"
ng-change="UpdateAll()">
</select>
example - https://plnkr.co/edit/M4qZEL4Y8EfRtabfSN8a?p=preview
bonus
If you want to display an empty option without muddying up your options array, please visit my answer here - Why does AngularJS include an empty option in select?

Why variable initialization is required in ng-init: AngularJS

I have started leaning AngularJS.
In AngularJS if we have to initialize an element lets take an example of select:
<select id="s1" ng-model="vsrc" ng-init="vsrc='a.mp4'">
<option value="a.mp4">First video</option>
<option value="b.mp4">Second video</option>
</select>
why initialization of vsrc='a.mp4' in ng-init is required, when I was giving its value like ng-init='a.mp4' it was not working I had to give like ng-init="vsrc='a.mp4'". In normal HTML statement we are directly giving default option by providing value='a.mp4'
<select id="s1" ng-model="vsrc" ng-init="vsrc='a.mp4'"> /*ng-init used to initialize a **variable** before html render, it is a directive which search left and right value always*/
<option value="a.mp4">First video</option> /*value is here as json object {"value":"a.mp4"}*/
<option value="b.mp4">Second video</option>
</select>
take a look over here about ng-init directive

Parameterizing options dropdown lists in Protractor E2E tests

I'm trying to get my protractor E2E tests to interact with a drop down menu.
In the page that I'm testing, we have a drop down for countries. The dropdown contains 4 selectable options "?" which is blank, and then US, UK, and Canada.
I'm obviously not calling the element correctly within my PO file, as the parameterization is not working. The tests "pass", however when you watch what is being driven, the country that is selected, is the "?" one(which is default). So the test is navigating to the page, selecting the element and not actually clicking on the option that I'd like.
From what I can tell, I need to somehow get the parameterized option that is inputed from the spec file to the element however i'm not really sure if this is possible.
What am I missing/doing wrong?
Spec file:
campaignPO.clickCountryLocation('US');
PageObject file:
this.countryType = by.css('select[ng-model="country"]');
this.clickCountryLocation = function(button) {
element(this.countryType).click(button);
};
Here is the element were working with:
<select class="form-control input-sm ng-pristine ng-invalid ng-invalid-required"
ng-size="11"
ng-model="country"
name="country"
ng-options="opt.value as opt.label for opt in countries"
ng-change="updateCountry()"
required="">
<option value="?" selected="selected" label=""></option>
<option value="0" label="US">US</option>
<option value="1" label="UK">UK</option>
<option value="2" label="Canada">Canada</option>
</select>
I'd use a select-option abstraction suggested here:
Select -> option abstraction
Example usage:
var SelectWrapper = require('select-wrapper');
var mySelect = new SelectWrapper(by.name('country'));
mySelect.selectByText('US');
First of all, you can simplify your select locator by using
var selectEl = element(by.model('country'));
I am not sure the attribute css works very well. To select options:
var optionEl = selectEl.element(by.cssContainingText('option', 'US'));
optionEl.click();
If you are just trying to open the select without selecting a value, don't. Selects are HTML components and you don't want to test them in this way. Even working with them in javascript is cumbersome. If you want to see what values are in there, list the option elements.

angular Protractor e2e tests and select value?

I'm having problems to get currently selected value of the select element. In some cases I can use:
element(by.css('#some-select')).$('[selected]').getText();
And it works just fine. But as I figured out it works only because selected attribute is present on option element. If I select option with javascript by setting select's value this way it's not possible to get element by selected attribute.
What is the proper way to get selected option or it's text because I can't find answer anywhere.
Thank you.
Ok, so right now I feel stupid and a little enraged at the same time.
It turns out that there is a simple way to get the option that is selected by simply calling:
element(by.selectedOption('model-name'));
Documentation here
I feel like this is a wrong way to do because instead of reusing element locator cache that I have defined (select element) I need to use new locator "selectedOption". It feels bad because naturally I would like to simply call something like element(by.css('#some-select')).getSelected();
But maybe there is a way that I could not just find?
So I just ran into this, and I couldn't find any answer that I really liked..
My Answer
First, I recommend changing the ng-options by adding track by statement.
for example:
<select ng-options="l.id as ('filters.languages.' + l.id | translate) for l in limitedLanguages track by l.id" ng-model="filterLanguage">
</select>
This will add the id on the option element so instead of getting
<select ng-options="l.id as ('filters.languages.' + l.id | translate) for l in languages" ng-model="filterLanguage" class="ng-pristine ng-valid ng-touched">
<option value="0">All</option>
<option value="1" selected="selected">English</option>
<option value="2">Hebrew</option>
<option value="3">Russian</option>
<option value="4">Arabic</option>
<option value="5">Other</option>
</select>
you get
<select ng-options="l.id as ('filters.languages.' + l.id | translate) for l in languages track by l.id" ng-model="filterLanguage" class="ng-valid ng-dirty ng-valid-parse ng-touched">
<option value="all" selected="selected">All</option>
<option value="english">English</option>
<option value="hebrew">Hebrew</option>
<option value="russian">Russian</option>
<option value="arabic">Arabic</option>
<option value="other">Other</option>
</select>
note that without the track by the value is simply the index (0,1,..) and with the track by the value is more readable/meaningful (all,english,...)
then you can use getAttribute on value to get the value as mentioned in other answers.

Resources