Protractor - how to select heavily nested dropdown element? - javascript

The site I am testing has a dropdown menu with the following code:
<ul class="nav navbar-nav navbar-right" data-ng-show="authentication.user">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span data-ng-bind="authentication.user.displayName" class="ng-binding">Testy McTesterson</span> <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
View Profile
</li>
<li>
Edit Profile
</li>
<li id="h_manage_social_accounts">
Manage Social Accounts
</li>
<li data-ng-show="authentication.user.provider === 'local'" class="">
Change Password
</li>
<li class="divider"></li>
<li>
Signout
</li>
</ul>
</li>
</ul>
How can I get protractor to click the links like "Signout" within the dropdown menu?

First, you need to click the toggle button, then choose the element from the dropdown:
element(by.css('li.dropdown > a.dropdown-toggle')).click();
element(by.linkText('Signout')).click();

Related

How to extract specific text from a html tag that is dynamically placed in the code

I want to extract a specific text which is located between the <li></li> tags in the navigation bar of a html page, but the code's not consistent. The order of the navbar elements changes depending on the conditions that I'm unable to predict. The only thing that doesn't change in the code is the <li></li> tags where the text I want to extract is embedded.
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active">Homepage</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Menu - 1<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>Menu - 1 - Option - 1</li>
<li>Menu - 1 - Option - 1</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Menu - 2<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>Menu - 2 - Option - 1</li>
<li>Menu - 2 - Option - 2</li>
<li role="separator" class="divider"></li>
<li>Menu - 2 - Option - 3</li>
<li>Menu - 2 - Option - 4</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Menu - 3<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>Menu - 3 - Option - 1</li>
<li>Menu - 3 - Option - 2</li>
</ul>
</li>
<!-- The element I need to access -->
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
------->[TEXT I WANT TO EXTRACT]<-------
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>My Account</li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Help</li>
<li>Contact Us</li>
<li>Logout</li>
</ul>
</li>
<!-- The element I need to access -->
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Menu - 5<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>Menu - 5 - Option - 1</li>
<li>Menu - 5 - Option - 2</li>
<li>Menu - 5 - Option - 3</li>
</ul>
</li>
</ul>
</div>
So, the following <li></li> tags of the code never changes, but its placement in the navbar changes:
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
------->[TEXT I WANT TO EXTRACT]<-------
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>My Account</li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Help</li>
<li>Contact Us</li>
<li>Logout</li>
</ul>
</li>
When I try to extract the text using querySelector from JavaScript, it sometimes doesn't work because the <li></li> element is no longer the fifth one in the list. Thus, the selector doesn't match:
document.querySelector('li.dropdown:nth-child(5) > a:nth-child(1)');
What would be the safest way for me to extract this text?
Maybe you can give a class to the li that you want to extract and then select that class using querySelector.
Here I am assuming you want to select the logout list element.
------->[TEXT I WANT TO EXTRACT]<-------
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>My Account</li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Help</li>
<li>Contact Us</li>
<li class="logout">Logout</li> //Here I have given a class 'logout'
</ul>
</li>
Select that li element using querySelector
let text = document.querySelector('.logout').innerText;
Using the filter method to find the right element is the solution to my issue. I just check whether the children of the specified tags contain a particular text that the element I want to access has. Then, I simply extract the text like the following:
// Retrieve all children inside of the navbar
Array.from(document.querySelectorAll(".nav > li.dropdown"))
// Filter the child that contains the text
.filter((el) => el.textContent.includes("Logout"))[0]
// Extract the text from the child tag
.querySelector("a").textContent.trim();

jQuery all list items in menu is set to active instead of just the selected one

This is the structure of my menu. I have attached the jQuery code below. The way this works is that when I click on Home Page, its parent element which is the list item has a class that is set to active.
At the moment, When I am at the Home Page, its list item has an active class. However, the list-items for Account Codes and Branches also have an active class set on it. How can I make sure that only my Home Page is affected?
Also, how can I change my jQuery code to work with this menu format where there is a new list within a list?
<ul class="sidebar-menu" id="navigation-menu">
<li><i class="far fa-square"></i><span>Home Page</span></li>
<li class="menu-header">Administrator Mode</li>
<li class="nav-item dropdown">
<i class="fas fa-fire"></i><span>Configuration</span>
<ul class="dropdown-menu">
<li>Account Codes</li>
<li>Branches</li>
</ul>
</li>
</ul>
<script>
$(document).ready(function () {
var current = location.pathname;
$("#navigation-menu a").each(function () {
var $this = $(this);
if ($this.attr('href').indexOf(current) !== -1) {
console.log($this);
$this.parent().addClass('active');
console.log("matched");
}
});
});
</script>
Dropdown Layout
The most effective HTML layout pattern for dropdown menus is to alternate between <ul>/<ol> and <li>:
<ul>
<li class='dropdown'>
Dropdown
<ul class='menu' style='display:none'>
<li>Item</li>
<li class='dropdown'>
<a href='#/'>Dropdown</a>
<ul class='menu' style='display:none'>
<li>Item</li>
<li>Item</li>
</ul>
</li>
</ul>
</li>
</ul>
Also, if you want the menus to be initially closed, add the following inline style:
<ul class='menu' style='display:none'>
The <a>nchors have default behavior that is hard to override when using a framework like Bootstrap or Foundation. Try toggling the .active class on .has-dropdown children tags.
Demo
Details commented in demo
/*
Delegate click event to each .has-dropdown
Toggle .active class on that particular .has-dropdown children tags
Get next .dropdown-menu and toggle it up/down and toggle .active
*/
$('.has-dropdown').on('click', function(e) {
$(this).children().toggleClass('active');
$(this).next('.dropdown-menu').slideToggle().toggleClass('active');
});
.active {
color: tomato
}
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundation/6.5.3/css/foundation.min.css' rel="stylesheet">
<link href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" rel="stylesheet" crossorigin="anonymous">
<ul id="navigation-menu" class="sidebar-menu">
<li>
<a href="#/" class="nav-link">
<i class="fas fa-home"></i><span> Home Page</span>
</a>
</li>
<li class="menu-header">
<a href="#/" class="nav-link has-dropdown">
<i class="fas fa-tools"></i><span> Administration</span>
</a>
<ul class="nav-item dropdown-menu" style='display:none'>
<li>
<a href="#/" class="nav-link has-dropdown">
<i class="fas fa-lock"></i><span> Security</span>
</a>
<ul class="dropdown-menu" style='display:none'>
<li>
<a href="#/" class="nav-link">
<span> Account Codes</span>
</a>
</li>
<li>
<a href="#/" class="nav-link">
<span> Branches</span>
</a>
</li>
</ul>
</li>
<li>
<a href="#/" class="nav-link has-dropdown">
<i class="fas fa-cog"></i><span> Configuration
</span></a>
<ul class="dropdown-menu" style='display:none'>
<li>
<span> Layout</span>
</li>
<li>
<span> Assets</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Bootstrap dropdown issues. (Dropdowns ans remains dropdowned)

I want to use bootstrap dropdown menu:
<li class="nav__item dropdown profile open">
<a id="openLessons" href="#" class=" nav__link dropdown-toggle" data-toggle="dropdown">
Открытые уроки
</a>
<ul class="dropdown-menu newmenu">
<li class="">Алгебра</li>
<li class="">Литература</li>
<li class="">Русский язык</li>
<li class="">История</li>
</ul>
</li>
For some reason it's dropdowned when the page loads and rmains dropdowned.
What have I forgotten?
Fiddle of course.
Remove .open
<li class="nav__item dropdown profile open">
<li class="nav__item dropdown profile open">
remove the class open.

generic function in javascript using backbone and jquery

I'm using backbone and Jquery and I have a backbone menu with a bunch of click able links.
<div id="navbarCollapse" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active">Home</li>
<li>Profile</li>
<li class="dropdown">
<a data-toggle="dropdown" id="n-btn" class="dropdown-toggle" href="#">Messages <b class="caret"></b></a>
<ul role="menu" class="dropdown-menu">
<li> dropdown1</li>
<li> dropdown2</li>
<li class="divider"></li>
<li>Trash</li>
</ul>
</li>
</ul>
I'd like to be able to do the following make a generic function such that I can click any of the links in the menu bar div above and have one generic function to handle all of the clicks.
Currently I have to create a separate function using jQuery for each id/herf
$(window).load(function(){
$("#dropdown1").on('click.dropdown.data-api', function(e) {
e.preventDefault();
alert("test");
});
});

Expand menu, show submenu

I'm trying to create an "expandable" menu. So, if a user clicks on option A two suboptions should show up. I'm trying to do something like this but in a menu...
This is my code so far...
<li>
<i class="icon-picture icon-white"></i> Galleries
<div class="nav-collapse88">
<ul>
<li>
<i class="icon-play"></i> Add
</li>
<li>
<i class="icon-play"></i> Delete
</li>
</ul>
</div>
</li>
So when I click on galleries the options Add and Delete should appear. Any suggestions?
I would nest ul's like so:
<ul>
<li>
<a class="expand">Link used to expand</a>
<ul>
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
</ul>
</li>
</ul>
The I would use this jquery:
$(document).on('click', 'a.expand', function(e) {
e.preventDefault();
$(this).siblings('ul').slideToggle();
});
You would need to set the sub menu CSS to display none.
Something along these lines should do the trick.
This works:
<ul>
<li class="dropdown">
Support
<ul class="dropdown-menu" role="menu">
<li><i class="icon-play"></i> Add</li>
<li><i class="icon-play"></i> Delete</li>
</ul>
</li>
</ul>
I believe you want the class to be dropdown not collapse. That's how the bootstrap documentation recommends it for menus.
Change the class to be dropdown, remove the inner div, and add the class dropdown-menu the inner ul.
<li class="dropdown">
<i class="icon-picture icon-white"></i>>Galleries
<ul class="dropdown-menu">
<li>
<i class="icon-play"></i> Add
</li>
<li>
<i class="icon-play"></i> Delete
</li>
</ul>
</li>
tyy this http://jsfiddle.net/68RXP/213/
$(function(){
$(".nav-collapse88").hide();
$("a").click(function(e){
e.preventDefault();
$(".nav-collapse88", $(this).parent()).slideToggle();
});
})​
<li>
<a href="?op=1">
<i class="icon-picture icon-white"></i> Galleries</a>
<div class="nav-collapse88">
<ul>
<li>
<i class="icon-play"></i> Add
</li>
<li>
<i class="icon-play"></i> Delete
</li>
</ul>
</div>
</li>​

Categories

Resources