Is there a Gebish way of selecting elements between two elements? - geb

I'm trying to write a Geb Module that gets all div elements between two elements and can't see a nice way of doing that.
I can probably do some sort of groovy list manipulation or iterative method but thought I'd check first to see if I am missing a Gebish way.
The html is roughly structured like this
<div id="theParent">
<div class="v-gridlayout-slot">
<div id="sectionHeader">Header 1</div>
</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">
<div id="anotherSectionHeader">Header 2</div>
</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">
<div id="yetAnotherSectionHeader">Header 3</div>
</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">...</div>
<div class="v-gridlayout-slot">
<div id="actionButton"></div>
</div>
The content section of my module looks like
static content = {
headerOne { $("#sectionHeader")}
headerTwo { $("#anotherSectionHeader")}
headerThree { $("#yetAnotherSectionHeader")}
...
}
I was hoping there is a Gebish way of collecting all $("div.v-gridlayout-slot") that are between headerOne and headerTwo but can't see anything appropriate.
Solution from erdi:
Navigator getElementsBetween(def fromElement, def toElement) {
String nextId = toElement.attr('id')
def betweenElements = fromElement.parent().nextAll("div.v-gridlayout-slot").takeWhile {
!it.has(id: nextId)
}
$(*betweenElements)
}

To be precise, according to the html structure you posted you are not looking for $("div.v-gridlayout-slot") that are between headerOne and headerTwo but are between the parrents of headerOne and headerTwo. I'd write it this way:
def nextHeaderId = headerTwo.attr('id')
def betweenHeaders = headerOne.parent.nextAll("div.v-gridlayout-slot").takeWhile { it.hasNot(id: nextHeaderId) }
betweenHeaders will end up being a Collection<Navigator> (because of use of until which is a default Groovy method on Iterable with Navigator being Iterable<Navigator> and not a method on Navigator) so if you need to turn it into a navigator you'll have to call sum() on it:
betweenHeaders.sum()
or spread it and pass it to $():
$(*betweenHeaders)

Related

how to select nth item from a CSS selector

[data-short-caption="itemName" i] .circle-base,
this selector is identifying two items in the DOM, I need to select the second item, is there any way like we have in xpath to select the second item ?
The HTML Structure is something like this :
<div class="selection" data-select-item="select-item">
<div data-short-caption='itemName'>
<div class=circle-base> </div>
</div>
<div data-short-caption='itemName'>
<div class=circle-base> </div>
</div>
</div>
As per the HTML:
<div class="selection" data-select-item="select-item">
<div data-short-caption='itemName'>
<div class=circle-base> </div>
</div>
<div data-short-caption='itemName'>
<div class=circle-base> </div>
</div>
</div>
To identify only the second item you can use either of the following css-selectors based Locator Strategy:
Using nth-child():
div.selection div:nth-child(2) > div.circle-base
Using nth-of-type():
div.selection div:nth-of-type(2) > div.circle-base
If I could correctly understand your query, there are several ways to achieve it via css pseudo-selectors. Please check the code if it helps.
/*Method 1 : using last-of-type pseudo-selector*/
[data-short-caption="itemName" i]:last-of-type .circle-base{
background-color: #efefef;
}
/*Method 2 : using last-child pseudo-selector*/
[data-short-caption="itemName" i]:last-child .circle-base{
background-color: #efefef;
}
/*Method 3 : using nth-of-type pseudo-selector*/
[data-short-caption="itemName" i]:nth-of-type(2) .circle-base{
background-color: #efefef;
}
/*Method 4 : using nth-child pseudo-selector*/
[data-short-caption="itemName" i]:nth-child(2) .circle-base{
background-color: #efefef;
}
<div class="selection" data-select-item="select-item">
<div data-short-caption='itemName'>
<div class=circle-base> first child </div>
</div>
<div data-short-caption='itemName'>
<div class=circle-base> second child </div>
</div>
</div>

Populate Knockout's observable array from Razor

I'm trying to implement error handling in ASP.NET so that if there is an error the user will get the error message, then be able to go back and have the previous state restored. I'm using ASP.NET Core and Knockout (not my implementation). I want to update "signerFields" with the model from the server (Model.SignersJson). How would I do this?
Signer.js
function SignerViewModel() {
var self = this;
self.signerFields = ko.observableArray([]);
self.guarantorFields = ko.observableArray([]);
self.companyGuarantorFields = ko.observableArray([]);
...
Signer.cshtml
<div data-bind="foreach: signerFields, visible: signerFields().length > 0">
<div class="row">
<div class="col-lg-10">
<div>
#*Header Company signers section*#
<div class="row" data-bind="visible: isCompany() && !anySigner() && !isInvitation()" style="display: none">
<div class="col-lg-4">
<b>FullName</b>
</div>
#*<div class="col-lg-3">
<b>LastName </b>
</div>*#
<div class="col-lg-4">
<b>Role </b>
</div>
<div class="col-lg-3">
<b>Contact_Information</b>
</div>
</div>
</div>
</div>
...
#section scripts
{
<script src="~/Scripts/Signer.js"></script>
var serverSigners = JSON.parse(#Html.Raw(Json.Encode(Model.SignersJson)));
var observableData = ko.mapping.fromJS(serverSigners);
var viewModel = new SignerViewModel();
viewModel.signerFields(observableData); // <-- How?
}
I get no error messages, nothing.
There is a lot of unknowns with this one, but here is a working example using the information we have at hand. One thing I noticed was when creating this sample was that I assumed the data comming from Razor was in an array. and when the array is passed into the mapping component it comes out as an observable array. This meant that the data going into the signerFields was probably not what you were expecting and ended up having an observableArray with one object which itself was an observable array. Adding round brackets to observableData() means that you get the data out of the observable and you can then pass it into the signerFields as an array of objects.
Hope that made sense.
function SignerViewModel() {
var self = this;
self.signerFields = ko.observableArray([]);
self.guarantorFields = ko.observableArray([]);
self.companyGuarantorFields = ko.observableArray([]);
}
var serverSigners = [{'fullname':'Test Name', 'lastName': 'Name', 'role': 'Test Role', 'contactInformation': '123 Seasame Street NY, US', 'isCompany': true, 'anySigner': false, 'isInvitation': false}];
var observableData = ko.mapping.fromJS(serverSigners);
var viewModel = new SignerViewModel();
viewModel.signerFields(observableData());
ko.applyBindings(viewModel)
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div data-bind="foreach: signerFields, visible: signerFields().length > 0">
<div class="row">
<div class="col-lg-10">
<div>
<div class="row" data-bind="visible: isCompany() && !anySigner() && !isInvitation()">
<div class="col-lg-4">
<b>FullName: </b><span data-bind="text: fullname"></span>
</div>
<div class="col-lg-3">
<b>LastName: </b><span data-bind="text: lastName"></span>
</div>
<div class="col-lg-4">
<b>Role: </b><span data-bind="text: role"></span>
</div>
<div class="col-lg-3">
<b>Contact Information: </b><span data-bind="text: contactInformation"></span>
</div>
</div>
</div>
</div>
</div>
</div>

Dynamically bind to Google tagging attribute data-tracking-label=""

I am trying to bind data-tracking-label="plan.priceplan_name".
Not sure how do i go about binding the priceplan_name dynamically to the data-tracking-label.
Already tried a few options including:
data-tracking-label={{plan.priceplan_name}} - which has error :
Uncaught Error: Template parse errors:
Can't bind to 'tracking-label' since it isn't a known property of 'div'.
Below is a snippet of my code.
<div *ngFor="let plan of plans; let i = index" class="col-md-3 pt-2 pb-2 pl-2 pr-2">
<div class="card">
<div class="card-body row p-0" (click)="selectPlan(plan, i)" data-tracking-category="SEA - Choose-plan" data-tracking-action="Click" data-tracking-label={{plan.priceplan_name}}>
<div class="first-sec col-6 pt-2 pb-2 rounded-left">
<h3>{{plan.priceplan_name}}</h3>
<div class="position-bottom">
<h2 class="mb-0">R{{plan.base_priceplan_cost | number:'1.0-2'}}pm</h2>
<h5>x{{plan.contract_duration}}</h5>
</div>
</div>
</div>
</div>
</div>
Here is another way to add the tags which is being used.
Note that a service with a windowRef provider was added to the app.module.ts and imported in every controller that makes use of the dataLayer.
(click)="myFunstion(param); dataLayer.push({event:'MyEvent', category:'MyCategory', action:'Click', label:plan.priceplan_name})
And in the controller:
import { WindowRef } from '../WindowRef';
Declare var before constructor:
dataLayer = this.winRef.nativeWindow.dataLayer;
And add variable inside constructor:
private winRef: WindowRef

know number of elements with vba scraping web

hello everyone I would like to know numer of elements like the following
<div id="datatable">
<form id="theForm" name="theForm" >......</form>
<div class="no_data_dd" id="no_data" >....
<div class= ....>.....
<div class= ....>
</div>
</div>
</div>
<div class="score_row score_header">.../div>
<div class="score_row match_line e_true" >..</div>
<div class="score_row padded_date ">..</div>
<div class= ....>.....</div>
</div>
I tried with
Set itemEle = objIE.document.getElementById("scoretable")
Length = itemEle.getElementsByTagName("class").Length
length = 0 and nont =5
why?
What if you try get "elements" by instead of get "element" that might be a reason for the answer

Visualforce remote call not working with safari

I am developing an application using angularjs and visualforce remote calling.
The application works right in all the browser except in Safari. When the application is run on Safari browser it is not able to communicate to the server
returning
"Unable to connect to server( Communication failed)".
Code snippet
Apex Controller
global class UsaTAevaluationCtrl{
#RemoteAction
public static string getkeyvalue(string username,string key)
{
string interviewInviteId1;
system.debug(username+'OOOOOOOOOO'+key);
interviewInviteId1='False';
Tech_Screen_Invites__c t=new Tech_Screen_Invites__c();
try
{
t=[select id,name from Tech_Screen_Invites__c where Name=:username];
string recordkey=t.id;
if(t.id!=null)
{
interviewInviteId1=t.id;
recordkey=string.valueof(t.id);
if(recordkey.length()==18)
recordkey=recordkey.left(15);
if(recordkey.right(3)==key)
interviewInviteId1=t.id;
}
else
{}
}catch(exception e){}
return interviewInviteId1;
}
}
Visualforce page
<apex:page controller="UsaTAevaluationCtrl" showHeader="false" standardStylesheets="false" docType="html-5.0">
<apex:form >
// include statements
....
<script>
var services = (function(){
return function (userName, password) {
var deferred = $.Deferred();
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.UsaTAevaluationCtrl.getkeyvalue}',
userName,
password,
function (result, event){
console.log("Result -> ", result);
});
return deferred.promise();
}
})()
</script>
....
<div class="container" ng-app="TechEval" ng-controller="evalform" ng-init="initEvent()">
<div class="row evaluation-row">
<div class="col-md-12 col-xs-12"> </div>
</div>
<div class="row evaluation-row" ng-show="isAuthenticationSuccess">
<div class="col-md-12 col-xs-12 alert alert-danger score-alert">Invalid Credentials</div>
</div>
<div class="row evaluation-row">
<div class="col-md-3 col-xs-12">
<h1 class="skill-title">User Name</h1>
</div>
<div class="col-md-9 col-xs-12">
<div class="skill-new">
<input name='_username' id='userinput' ng-model='_username' required="required"></input>
</div>
</div>
</div>
<div class="row evaluation-row">
<div class="col-md-12 col-xs-12"></div>
</div>
<div class="row evaluation-row">
<div class="col-md-3 col-xs-12">
<h1 class="skill-title">Token</h1>
</div>
<div class="col-md-9 col-xs-12">
<div class="skill-new">
<input name="_password" id="passvalue" type="password" ng-model='_password' required="required"></input>
</div>
</div>
</div>
<div class="row btn-row">
<div class="col-md-3 col-xs-12"> </div>
<div class="col-md-9 col-xs-12">
<div class="skill-new">
<button class="btn btn-primary" style="margin-left:92px;" ng-click="Authentication()">Submit</button>
</div>
</div>
</div>
</div>
</apex:form>
</apex:page>
Code in Angular Controller
var EvalApp = angular.module('TechEval', ['ui.bootstrap']);
EvalApp.controller('evalform', function ($scope, $log, $q) {
$scope.Authentication = function () {
services.getKeyValue($scope._username,
$scope._password,
function (result, event){
console.log("Result -> ", result);
});
}
}
Functionality
When user enter username and password and hits submit button the remote service is to be called.
If you check Supported Browsers for Salesforce Classic page, you find:
Apple Safari versions 8.x on Mac OS X There are no configuration
recommendations for Safari. Apple Safari on iOS isn’t supported for
the full Salesforce site. Safari isn’t supported for:
The Salesforce console
Salesforce CRM Call Center built with CTI Toolkit versions below 4.0
Salesforce Wave Analytics
Are you sure that your OS/Safari version are compatible with it?