XPath selecting a node with some attribute value equals to some other same level node's attribute value - xpath

<form-validation>
<formset>
<form name="BookForm"> ... </form>
<form name="BookForm"> ... </form> <!-- This is what I want to locate -->
</formset>
<formset>
<form name="BookForm"> ... </form> <!-- but not locate this line -->
</formset>
</form-validation>
I want to locate the duplicated form element(name attribute value is the same as a previous one) in a single formset. How should I write the xPath expression?
Thank you!

You can use e.g. //formset/form[#name = 'BookForm'][2].
In your comment you said you want to find the names of duplicates, in XPath 3.1 that is possible with e.g.
map:merge(//formset/form/map { string(#name) : . }, map { 'duplicates' : 'combine' }) => map:for-each(function($k, $v) { $k[$v[2]] })

Related

How to get HTML attribute value in Cypress

I am starting to learn Cypress after few years working with Selenium. In Selenium i'm regularly using GetAttribute() method. As an exercise i'm trying to do the same with Cypress, to print class attribute value from the following HTML element:
<input class="form-control ng-touched ng-pristine ng-valid" max="21" min="1" type="number">
This is my code:
cy.log(cy.get('input').invoke('attr', 'class'));
Output:
log Object{5}
I tried to use Lakitna cypress-commands (https://github.com/Lakitna/cypress-commands) with the code:
cy.log(cy.get('input').attribute('class'));
Output:
cy commands are asynchronous so for logging you need to use .then:
cy.get('input').then(($input) => {
cy.log($input.attr('class'));
});
or
// with assertion
cy.get('input').should('have.attr', 'class').then(cy.log);
If you are looking for the value of the html tag and you end up here, this is the simplest way to do that:
cy.get(`[data-testid="${key}"]`).then(($input) => {
if($input.prop('nodeName') === "SELECT") {
//Select corresponding input to value provided in dropdown lists
} else {
//Input each value provided into each field
cy.get(`[data-testid="${key}"]`).clear().should('have.value', '').type(testMachine[key]).should('have.value', testMachine[key])
}

Output user first name

I want to get the name of the user to put it on an h1.
What dies this line stand for?
#select="option => selected = option">
I'm using Buefy for the vue components.
<template>
<section>
<div class="field">
<b-switch v-model="keepFirst">
Keep-first <small>(will always have first option pre-selected)</small>
</b-switch>
</div>
<p class="content"><b>Selected:</b> {{ selected }}</p>
<b-field label="Find a name">
<b-autocomplete
v-model="name"
placeholder="e.g. Anne"
:keep-first="keepFirst"
:data="filteredDataObj"
field="user.first_name"
#select="option => selected = option">
</b-autocomplete>
</b-field>
</section>
</template>
<script>
import data from '#/assets/data_test.json'
// Data example
// [{"id":1,"user":{"first_name":"Jesse","last_name":"Simmons"},"date":"2016-10-15 13:43:27","gender":"Male"},
// {"id":2,"user":{"first_name":"John","last_name":"Jacobs"},"date":"2016-12-15 06:00:53","gender":"Male"},
// {"id":3,"user":{"first_name":"Tina","last_name":"Gilbert"},"date":"2016-04-26 06:26:28","gender":"Female"},
// {"id":4,"user":{"first_name":"Clarence","last_name":"Flores"},"date":"2016-04-10 10:28:46","gender":"Male"},
// {"id":5,"user":{"first_name":"Anne","last_name":"Lee"},"date":"2016-12-06 14:38:38","gender":"Female"}]
export default {
data() {
return {
data,
keepFirst: false,
name: '',
selected: null
}
},
computed: {
filteredDataObj() {
return this.data.filter((option) => {
return option.user.first_name
.toString()
.toLowerCase()
.indexOf(this.name.toLowerCase()) >= 0
})
}
}
}
</script>
# is shorthand for v-on:, so it's handling a select event with a function that receives option as a parameter and assigns it to selected.
Since v-model is bound to name, you should be able to do <h1>{{name}}</h1> to have the same value show up in an H1.
The data section has the main variables for your object. name is there. There is also a computed (named filteredDataObj) that should return an array (length of zero or one) with the matching test data. If you want other fields (like id) you would need to look there. Something like
{{filteredDataObj.length ? filteredDataObj.id : ''}}
would give the id if name matched anything in the data set.

If search don't return values from database show an empty form

So i got a page that have a search form, and when the user search for a value if there are no records on database the form returns empty, but if there are records the form is populated with data.
What i was thinking was this
var db = Database.Open("myDataBase");
var selectCommand = "SELECT * FROM exportClient";
var searchTerm = "";
if(!Request.QueryString["searchField"].IsEmpty() ) {
selectCommand = "SELECT * FROM exportClient WHERE clientAccount = #0";
searchTerm = Request.QueryString["searchField"];
}
if(IsPost){
var selectedData = db.Query(selectCommand, searchTerm);
}
And Then:
<body>
<div class="col_12">
<form method="get">
<label>search</label><input type="text" class="col_3" name="searchField" />
<button type="submit" class="button red" value="search">search</button>
</form>
</div>
#if(!Request.QueryString["searchField"].IsEmpty() ){
foreach(var row in db.Query(selectCommand, searchTerm)) {
<div class="col_12 box">
<form method="post">
// HERE IS THE FORM POPULATED
</form>
</div>
}
} else {
<div class="col_12 box">
<form method="post">
// HERE IS THE FORM NOT POPULATED
</form>
</div>
}
</body>
But what is happening is that the form that is not populated is always showing up when i enter the page, and i need that the only thing that user see when enter the page is the input field to do the search.
What am i doing wrong ?
I'm not sure of having understood your goal, but in my opinion your main problem is to detect if either exists or not a query string.
I think that your code should be like this
#if(Request.QueryString.HasKeys())
{
if(!Request.QueryString["searchField"].IsEmpty() ){
<p>searchField has value</p>
} else {
<p>searchField hasn't value</p>
}
}
There are a number of potential issues I can see with your code, hopefully you can put these together to achieve what you wanted:
As Selva points out, you are missing the action attribute on your forms.
The selectedData variable you create inside your IsPost() block goes out of scope before you do anything with it. Perhaps you didn't include all your code though, so ignore this if it just isn't relevant to the question.
To answer the main question: if you don't want the empty form to appear when the user hasn't yet performed a search, surely you just need to completely remove the else block - including the empty form - from your HTML?
Hope that helps.

Can one avoid multiple DOM elements with same id, when using Backbone/Marionette view instances?

When I create multiple view instances of the Marionette view which is linked with a template html with ids, these would get duplicated for multiple instances of these views.
While it works correctly, I feel that there ought to be more architecturally correct way of doing this.
The example code is like below.
Template:
<script id="myTemplate" type="text/template">
<div id="myDiv">
<input type="text" id="myText"/>
<input type="button" id="myBtn" value="Click me!"/>
</div>
</script>
View:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
var v1= new MyView();
v1.render();
var v2= new MyView();
v2.render(); //Duplicate IDs now present in DOM
I need some unique identification of these DOM elements and hence the ids.
Even when tying the model to this view, we need some way to identify these DOM elements.
What is the correct way of doing this without duplicating the ids.
Just pass the id to the view when you create it:
Template:
<script id="myTemplate" type="text/template">
<input type="text" class="js-myText"/>
<input type="button" class="js-myBtn" value="Click me!"/>
</script>
View def:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
Instanciation:
var v1= new MyView({ id: "view" + number});
v1.render();
Then you can provide dynamic id values for your views (e.g. by using a model id).
That said, when using Marionette you shouldn't need to call render: you should instead show a view within a region. Take a look at the free sample to my Marionette book to get you up to speed.
If you must go for unique IDs to make sure no one accidentally duplicates a class name inside a view, you can use:
Underscore's uniqueId method to generate a unique ID for each DOM element inside the view, like: <input type="text" id= <%= _.uniqueId("myText_") %> /> This will just make sure that IDs are not duplicated. But they're not very helpful if you need to identify the elements by these IDs.
Marionette's TemplateHelpers which allow you to use helper functions from inside the templates:
//Define this inside your view:
templateHelpers: function() {
var that = this;
return {
getIdSuffix : function() { return that.idSuffix; }
/*Where idSuffix is passed to the view during instantiation
and assigned to this.idSuffix */
};
}
//In the template:
<input type="text" id= <%= "myText_" + getIdSuffix() %> />
You now know before runtime what DOM IDs you will have, provided care is taken not to give the same idSuffix to more than one view instance.
Simply put, don't use an id if it's not unique. Use a class or some other way of identifying the element.
You can use any jQuery selector to locate the element you want, ranging from the insane and brittle:
this.$('div > input:first'); // don't actually do this!
to the slower but semantically better:
this.$('[data-element-type="some-text-box-descriptive-name"]');
Although in reality, using a class is best, because that's what a class is for - for identifying a type of element. I can see that a maintainer might not know not to change your class in the template, so a data-attribute might be acceptable, or maybe even (in this case):
this.$('input[type=text]');

Validation of dynamic created form (AngularJS)

I try to made nested form with validation. All works fine, but when I remove one of nested form, validation continue to use removed form. I made jsfiddle example http://jsfiddle.net/sokolov_stas/VAyXu/
When example runs, form are valid. If click "+" button, nested form will be added and valid will be false. Then click "-" button, and valid will be false all the same.
The question is: How to remove dynamic created form from validation processing.
Well, for one thing, a <form> inside of a <form> is not valid HTML.
Second, you're not supposed to be doing DOM manipulation from inside the controller. The controller is for "business" logic. See the section on controllers here
For what you're doing, you'd probably be better off using one form, with an ng-repeat inside of it, and adding additional elements to an array:
<form name="myForm" ng-controller="FormCtrl" ng-submit="doSomething()">
<div ng-repeat="item in items">
<input ng-model="item" type="text" required/>
</div>
<a ng-click="addItem()">+</a>
<a ng-click="removeItem()">-</a>
<button type="submit">Submit</button>
<div>Form valid: {{myForm.$valid}}</div>
</form>
and the controller:
function FormCtrl($scope) {
$scope.items = [];
$scope.addItem = function() {
$scope.items.push(null);
};
$scope.removeItem = function() {
$scope.items.pop();
};
$scope.doSomething = function () {
//your submission stuff goes here.
};
}

Resources