Cannot find the element for a button - angularjs

I have a test that searches for restaurants, i choose one, and then add it to a map. I can find the restaurant I want easily but cannot seem to get to the button I am trying to get at.
Here is the code I'm using to pick restaurant from list:
this.selectsAttraction = function() {
attraction = "El Jefe Burger Shack";
var addedAttraction = element(by.cssContainingText('div.name', attraction));
// get ancestor (that accepts mouseover to show icons)
var parent = addedAttraction.element(by.xpath('../..'));
if (parent) {
// scroll into view
browser.executeScript('arguments[0].scrollIntoView()', parent.getWebElement());
// mouse over the target row (in order to click on gear icon, it must be visible)
browser.actions().mouseMove(parent).perform();
browser.driver.sleep(3000);
// find edit icon - third child and then the first element under that
var editIcon = element(by.cssContainingText('span.button', 'Add')).click();
//also have tried:
/*var editIcon = parent.element(By.css("div:nth-child(2)")).element(By.css("span:nth-child(1)")); */
browser.driver.sleep(2000);
}
//The below works but only if I search the restaurant name which will only come back with one result, if I search restaurant there are multiple results to choose from
//element(by.css(".button.button-add")).click();
};
Here is the .html:
<div class="search-item">
<div class="description">
<div class="name" data-bind="text: name">El Jefe Burger Shack</div>
<div class="address" data-bind="text: $data.vicinity || ''">Rout 3, KM 34.5, Cll 14 De Julio, Luquillo</div>
</div>
<div class="btn-container">
<span class="button button-add" data-bind="click: $root.addAttraction">Add</span>
</div>
</div>
<div class="search-item">
Any help would be fantastic a bit lost here *this is a nonangular part of the site and Sync is off

You can actually get to the button directly from addedAttraction:
var addButton = addedAttraction.element(by.xpath("../following-sibling::div/span[. = 'Add']"));
We are going one level up, getting the following sibling containing span element with "Add" text.

Related

Scrolling a view or an element using webdriver

I have a dynamic list, containing varying no. of items, being displayed based on some selection.
If the list has more than 7 items, on hovering, the list shows a scroll bar. (If there are fewer items, there will be no scroll bar.)
I have found the element of the scroll bar based on its class name but tring to scroll using webdriver fails.
Here is the code:
//hover
Actions action = new Actions(driver);
WebElement placesList = driver.findElement(By.id("divPlaces"));
action.moveToElement(placesList).perform();
//scroll the list
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true)", element);
HTML code:
<div class="blk">
<div class="smlhead">
Places
</div>
<div id="divPlacesCont" class="sbarPrnt" style="height: 179.219px;">
<div id="divPlaces" class="tilewrap sbarcont" style="height: 179px;">
<div class="blksmlr smlhead sngline">
hyd
</div>
(removed 7 more items for simplicity...)
<div class="sbar" style="display: block;">
<div class="sbarpos" style="height: 152.576px; top: 0px;"></div>
</div>
</div>
</div>
</div>
Update: I got a thought to share. The list element is always visible on the left of the page (and I need not move/scroll to this element to make it visible as actions.moveToElement() does). When I hover, scroll bar within this list appears and I can click the bar, drag it down to see (last) items.
With the above code, it is moving the "frame" up i.e. everything from 'Country' to the list below move a bit up but still the last hidden item in the dynamic is not visible.

How to handle conditional sub-category in AngularJS product gallery

Just starting out learning AngularJS and decided to mock up a basic product gallery using what I've learned so far and I've hit a roadblock. Currently I have a simple product gallery with 3 templates(category listing, products in category listing and product overview). What I would like to do is set up some sort of conditional, where if the products in a selected category have a sub-category, it displays a list of sub-categories using the category-list template. If they don't have a sub-category it just goes straight to the product-list template.
I have created this Plunker showing where I am at so far.
In the above example, if someone clicks on "Cars" I want it to then show a listing of sub-categories using the category-list template. So when you click "Cars" it would take you to a screen with 2 buttons: 4-door and 2-door. Clicking on one of those buttons would then show you the products from those sub-categories using the product-list template. However, if you were to click on "Trucks" from the initial screen, it would just take you directly to the product-list template since the trucks don't have sub-categories.
Here is my category-list template:
<section id="categories" ng-hide="productsVisible">
<div ng-repeat="product in vm.products" class="category">
<div ng-click="vm.selectCategory(product); showProducts();">
<button>{{product.category}}</button>
</div>
</div>
</section>
And here is my product-list template:
<section id="products" ng-show="productsVisible">
<div ng-repeat="product in vm.selectedCategory.items" class="product">
<a href ng-click="vm.selectProduct(product); showResults();">{{product.name}}</a>
</div>
</section>
See my updated plunker
Basically, you need to extend the selectCategory method by grouping the sub-categories and checking whether we're about to enter this sub-category in subsequent click. Like this:
vm.selectCategory = function(category) {
var subCats = [],
map = {};
if (category.items && !category.items[0].subCategory){
vm.selectedCategory = category;
vm.inSubCat = true;
return;
}
vm.inSubCat = !category.items;
if (category.items) category.items.forEach(function(e){
if (!map[e.subCategory]) subCats.push({category: e.subCategory, name: category.category});
map[e.subCategory] = true;
});
vm.products = subCats;
if (vm.inSubCat) vm.selectedCategory = {items: vm.data.filter(function(c){
return c.category == category.name;
})[0].items.filter(function(p){
return p.subCategory == category.category;
}) };
}
I would suggest your data model could use some work, and put all the products in a single array with categories and subcategories as properties. However, you can get what you want with this change to the products-list.html.
<div ng-show="vm.selectedCategory.category=='Cars'">
<input type="radio" ng-model="subcategory" value="2-Door">Coupe
<input type="radio" ng-model="subcategory" value="4-Door">Sedan
</div>
<section id="products" ng-show="productsVisible">
<div ng-repeat="product in vm.selectedCategory.items" class="product">
<a ng-show="product.subCategory===subcategory" href ng-click="vm.selectProduct(product); showResults();">{{product.name}}</a>
</div>
</section>
I advice you to refactor the code in two possible ways:
a) Try to remove lines from controller that control the view (the process of displaying different directives) and use events in directives
b) Control your view by using ng-show and ng-hide directives that will show or hide some part of your code.

delete the selected item from list

Im using ionic framework and Im trying to create a phone list which can add and delete the user entered phone no.Here the user entered numbers are listed with check box on clicking the add button.When the user selects the check-box and clicks the delete button, he must be able to delete the selected check box phone number.Here the problem is while using the delete button, it doesn't delete the selected check box instead it is deleting the first value entered in the list. So please help me to delete only user selected check-box items.
html code:
<div>
<ion-checkbox ng-model="phoneno" ng-repeat="y in phonelist">
<span data-ng-bind="y"> {{y}}</span> </ion-checkbox>
<button ng-click="remove($index)" value="Delete">Delete</button><br>
</div>
<br>
<br>
<div>
<!label class="item item-input item-floating-label">
<input ng-maxlength="10" ng-model="phone"> <br>
<button ng-click="add()" value="Add">Add</button><br>
<!/label>
</div>
</ion-content >
</ion-view>
js code:
.controller('PlaylistCtrl', function($scope, $stateParams) {
})
.controller('addAdmin',['$scope',function($scope){
$scope.phonelist=[];
$scope.add=function(phone){
$scope.phonelist.push($scope.phone);
$scope.phone='';
}
$scope.remove=function(uuid){
var x=$scope.phonelist[uuid];
$scope.phonelist.splice(uuid,1);
}
}]);
Sorry for my english. I'm foreigner..
The problem is that your loop ends before the button, so when the button gets clicked the $index is always 0.
Because its not inside the element "ion-checkbox".
Here is my solution: put ng-click inside the checkbox and call to function with the $index.
And in the js, save the index on a scope var. So if the delete button gets clicked, delete the index that you saved on the previous function.
I hope that i helped.
It seems like you mean something else than your code says. You probably want to delete all phones that are checked, when clicking the button. Therefore you don't need the $index property, but just loop through the phones and delete the ones that are checked.
You will have to keep track of a 'checked' property of each phone, so you know which are checked. You can do this by using an object which holds the phone information, instead of just a string:
<div>
<!-- ng-model to a property of the phone that keeps track if the phone is checked -->
<ion-checkbox ng-model="y.checked"
ng-repeat="y in phonelist">
<span data-ng-bind="y.number">{{ y.number }}</span>
</ion-checkbox>
<button ng-click="removeSelected()" value="Delete">Delete</button><br>
</div>
<!-- ng-model to a property of the phone object -->
<input type="text" ng-model="phone.number" />
And in your controller:
$scope.add = function() {
$scope.phonelist.push($scope.phone);
}
$scope.removeSelected = function() {
var i = $scope.phonelist.length;
// reversed loop because you change the array
while (i--) {
var phone = $scope.phonelist[i];
// If phone is checked, remove from list
if(phone.checked) {
$scope.phonelist.splice(i, 1);
}
}
}
See this jsfiddle
Or see this jsfiddle where I included Ionic
It seems that you're mixing the add and remove functions, try to separate those as below.
.controller('addAdmin',['$scope',function($scope){
$scope.phonelist=[];
//Add function
$scope.add=function(phone){
$scope.phonelist.push(phone);
$scope.phone='';
}
//Remove function
$scope.remove = function(index){
$scope.phonelist.splice(index, 1);
};
}]);
add the following code in your controller
$scope.remove = function(index){
$scope.phonelist.splice(index, 1);
}
and that should work

Not able to click on hidden element in protractor?(Please go through the image.)

Here is the HTML code :
<li class="subdropdown">
Create Position
<ul class="list-unstyled dropdown-submenu" role="menu">
<li style="cursor: pointer;">
<a ng-click="openPositionModal($event)"><i class="glyphicon glyphicon-list-alt"></i> New Position</a>
</li>
Here is my test case:
1) Move the cursor on "Create position" toggle menu.
2) After the mouse hover, click on the "New position" menu list.
I used the below code to click on an hidden element. May be it might help someone.
Import statement:
import { browser, by, element } from 'protractor';
Code:
const hiddenElement = element(by.id('hiddenIcon'));
browser.driver.executeScript('arguments[0].click();', hiddenElement.getWebElement());
Just change the hiddenIcon to the id of your element.
browser.actions(), "by link text" and the "by partial link text" locators should help here:
var EC = protractor.ExpectedConditions;
// open up the menu
// choose position
var choosePosition = element(by.linkText('Create Position'));
browser.actions().mouseMove(choosePosition).perform();
// choose new position
var newPosition = $('a[ng-click*=openPositionModal]');
browser.wait(EC.elementToBeClickable(newPosition), 3000);
newPosition.click();

Pass object to template and render it on current view

There is a list of users loaded from an api
<div ng:controller="UserController">
<li ng-repeat="user in users">
<a ng:click="select(user)">
{{user.first_name}} {{user.last_name}}
</a>
</li>
</div>
When a user is clicked I want to open an extra view that shows some detailed information of the user. Imagine the view like this
The small blue area is the selected user and the big blue area the container that shows detailed user information.
function UserController($scope){
$scope.select = function(user){
console.log(user);
}
}
So when the user is clicked I can log the user object. It works until this point. But I absolutely have no idea how to open the extra container filled with user data.
Is there a way to simply load a template from file system, pass the object and append the whole thing to the current view? How would I do this with angular.js?
No need to load templates or anything. Just put the data in the scope, and use expressions in the big blue section that reference the data. Angular will handle changing the displayed content when the selection is changed. You can hide the relevant section with ng-show until the data get selected.
Here's a fiddle example.
function UserController($scope){
$scope.users = [{name:'me',email:'mine'}, {name:'you',email:'yours'}];
$scope.selectedUser = null;
$scope.select = function(user){
$scope.selectedUser = user;
}
}
and
<div ng-controller="UserController">
<button class="btn" ng-click="select(users[0])">User 1</button>
<button class="btn" ng-click="select(users[1])">User 2</button>
<hr>
<div ng-show="selectedUser != null">
<div>{{selectedUser.name}}</div>
<div>{{selectedUser.email}}</div>
</div>
You could highlight the corresponding small blue box with a similar ng-class or ng-style attribute (e.g. ng-class="{active: selectedUser == user}"). See What is the best way to conditionally apply a class?

Resources