getElement by id and loop through all elements inside in nightwatch - selenium

<div id="productcontainer" class="products-list" style="display: block;">
<a id="prd_Item_0" class="item-list-300x250">links</a>
<a id="prd_Item_1" class="item-list-300x250">links</a>
<a id="prd_Item_1" class="item-list-300x250">links</a>
<a id="prd_Item_1" class="item-list-300x250">links</a></div>
var ele=document.getElementById("productcontainer");
if(ele)
{
tags=ele.getElementsByTagName("a");
for(i=0;i<tags.length;i++)
{
if(tags[i])
{
tags[i].click();
}
}
}
I want to do like above in nightwatch js, can anyone help me how to do it like above using nightwtch js.
Thanks in advance

Use the promise returned by element finder findElements and then loop through the anchor elements to perform any operations that you want. Try the below code -
driver.findElements(By.cssSelector('#productcontainer a')).then(function(tags){
for (var i = 0; i< tags.length; i++){
if(tags[i])
tags[i].click();
}
});
Hope this helps.

This should do the job ..
module.exports = {
'Iterate over elements and click them': function(browser) {
function iterate(elements) {
elements.value.forEach(function(el) {
browser.click(el.ELEMENT, function(r) {
browser.assert.ok(r.status === 0);
});
});
}
browser
.url('..')
.elements('css selector', 'div#productcontainer a', iterate)
.end();
}
};

Related

how to get axios to load when row is expanded?

I have a dynamic table that has two axios.get methods. My methods work and the data get added to the table but I would like networkGroup to load only when I click toggle to expand a row. I can only get NetworkGroup to load automatically. How do I get it to work?
Here is my code:
toggle(gpId)
{
this.$set(this.networkGroups[audId], 'isExpanded', !this.networkGroups[gpId].isExpanded);
},
async getData(currPage)
{
const skip = (currPage - 1) * this.page.top;
const response = await this.axios.get(`/s/groups`, {
params: {
skip: skip,
top: this.page.top,
},
});
this.page = response.data.pages;
for (const audience of response.data.data)
await this.getNetworks(group.id);
this.groups = response.data.data;
},
async getNetworks(audId)
{
try
{
const response = await this.axios.get("/s/groups/network-groups?gpId=" + gpId);
if (!this.networkGroups[gpId])
{
this.$set(this.networkGroups, gpId, {});
this.$set(this.networkGroups[gpId], 'isExpanded', false);
}
this.$set(this.networkGroups[audId], 'data', response.data ? response.data.data : []);
}
catch (error)
{
if (error.message && error.message.includes("404"))
{
if (!this.networkGroups[gpId])
{
this.$set(this.networkGroups, gpId, {});
this.$set(this.networkGroups[gpId], 'isExpanded', false);
}
this.$set(this.networkGroups[gpId], 'data', []);
}
else
{
console.log(error.message);
}
}
},
<td class="audience-column">
<!-- expand button -->
<div class="ex_btn">
<span #click.prevent="toggle(group.id)">
<font-awesome-icon
:icon="
(networkGroups[group.id].isExpanded)
? 'caret-down'
: 'caret-right'
"
:class="[
'dropdown--arrow fa-2x cl-blue-primary relative t-3 g-link',
]"
/>
</span>
</div>
</td>
Not sure what I should change. Thanks!
seems it's not calling anything whenever you toggle the expand button, toggle method should call the getNetwork methods
try this:
toggle(gpId) {
this.$set(this.networkGroups[audId], 'isExpanded', !this.networkGroups[gpId].isExpanded); <<<--- change this
this.getNetwork(gpId) <<-- into this
},
and i think you set the wrong argument on getNetwork method:
getNetworks(audId) <<--- it says audId
{
try
{
const response = await this.axios.get("/s/groups/network-groups?gpId=" + gpId); <<--- but this calls gpId
....
hope my answer helps you!

Vue Bind Click On Classes

I am a newbie in Vue.js.
In old jQuery, it was simple to bind a click on some classes:
$('body').on('click','some-class-*', function(e) {})
Bu how to do it in Vue? I read that I can make a bind on a single element like
<span v-bind:click="dosomething" classes="some-class-1"></span>
You can archive this with normal JS:
var mySelection = document.getElementsByClassName('some-class');
for(var i = 0; i < blockSelection.length; i++) {
(function(index) {
mySelection[index].addEventListener("click", function() {
// Do something
})
})(i);
}

Updating Vue.Js data with secondary ajax request

I have some working code with Vue.js v2.5.16 that updates existing data, but I am not sure if I am following best practices.
What I am doing:
I call a properties.view() method (which I omitted below for brevity) to quickly grab basic data from a redis cache on load. I then call properties.available() to retrieve non-cached inventory data.
Questions:
Is there a efficient way to update my properties objects without having to "manually" do it like via some sort of "magic" data binding? I tried the v-model directive but couldn't get it jiving.
When updating property.rate.low.total.nightly_avg I can only reference the first level attribute "rate" I then have to build out the object I am going to update to get all the way down to the child. Is there a cleaner way of doing this?
JS Code:
var properties = new Vue({
el: '#properties',
data: {
properties: []
},
methods: {
available: function(){
let self = this;
axios
.get(searchJs.getAttribute('webroot') + 'properties/available', {
headers: {
'Content-Type': 'application/json'
}
})
.then(function(response){
for (let i=0; i < response.data.length; i++) {
let p = response.data[i];
let vData = self.properties.filter(function(property) {
if (property.id == p.id) {
return property;
}
});
self.$set(vData[0], 'rate', {
'low':{
'total': {
'nightly_avg': p.rate.low.total.nightly_avg,
'original_nightly_avg': p.rate.low.total.original_nightly_avg
}
}
});
}
});
},
}
});
HTML snippet:
<div class="property-card_price-book">
<span class="strikethrough-price">${{property.rate.low.total.original_nightly_avg}}</span>
<span class="price">${{property.rate.low.total.nightly_avg}}</span>
<div class="offer">
<figure>
<span></span>
<sep></sep>
<span></span>
</figure>
</div>
</div>

Vue 2 custom select2: why is #change not working while #input is working

I created a custom select2 input element for Vue 2.
My question is: why is
<select2 v-model="vacancy.staff_member_id" #input="update(vacancy)"></select2>
working, but
<select2 v-model="vacancy.staff_member_id" #change="update(vacancy)"></select2>
not?
Since normal <input> elements in Vue have a #change handler, it would be nice if my custom select2 input has the same.
Some information on my custom element:
The purpose of this element is to not render all <option> elements but only those needed, because we have many select2 inputs on one page and many options inside a select2 input, causing page load to become slow.
This solution makes it much faster.
Vue.component('select2', {
props: ['options', 'value', 'placeholder', 'config', 'disabled'],
template: '<select><slot></slot></select>',
data: function() {
return {
newValue: null
}
},
mounted: function () {
var vm = this;
$.fn.select2.amd.require([
'select2/data/array',
'select2/utils'
], function (ArrayData, Utils) {
function CustomData ($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
if (params.term && params.term !== '') {
// search for term
var results;
var termLC = params.term.toLowerCase();
var length = termLC.length;
if (length < 3) {
// if only one or two characters, search for words in string that start with it
// the string starts with the term, or the term is used directly after a space
results = _.filter(vm.options, function(option){
return option.text.substr(0,length).toLowerCase() === termLC ||
_.includes(option.text.toLowerCase(), ' '+termLC.substr(0,2));
});
}
if (length > 2 || results.length < 2) {
// if more than two characters, or the previous search give less then 2 results
// look anywhere in the texts
results = _.filter(vm.options, function(option){
return _.includes(option.text.toLowerCase(), termLC);
});
}
callback({results: results});
} else {
callback({results: vm.options}); // no search input -> return all options to scroll through
}
};
var config = {
// dataAdapter for displaying all options when opening the input
// and for filtering when the user starts typing
dataAdapter: CustomData,
// only the selected value, needed for un-opened display
// we are not using all options because that might become slow if we have many select2 inputs
data:_.filter(vm.options, function(option){return option.id === parseInt(vm.value);}),
placeholder:vm.placeholder
};
for (var attr in vm.config) {
config[attr] = vm.config[attr];
}
if (vm.disabled) {
config.disabled = vm.disabled;
}
if (vm.placeholder && vm.placeholder !== '') {
$(vm.$el).append('<option></option>');
}
$(vm.$el)
// init select2
.select2(config)
.val(vm.value)
.trigger('change')
// prevent dropdown to open when clicking the unselect-cross
.on("select2:unselecting", function (e) {
$(this).val('').trigger('change');
e.preventDefault();
})
// emit event on change.
.on('change', function () {
var newValue = $(this).val();
if (newValue !== null) {
Vue.nextTick(function(){
vm.$emit('input', newValue);
});
}
})
});
},
watch: {
value: function (value, value2) {
if (value === null) return;
var isChanged = false;
if (_.isArray(value)) {
if (value.length !== value2.length) {
isChanged = true;
} else {
for (var i=0; i<value.length; i++) {
if (value[i] !== value2[i]) {
isChanged = true;
}
}
}
} else {
if (value !== value2) {
isChanged = true;
}
}
if (isChanged) {
var selectOptions = $(this.$el).find('option');
var selectOptionsIds = _.map(selectOptions, 'value');
if (! _.includes(selectOptionsIds, value)) {
var missingOption = _.find(this.options, {id: value});
var missingText = _.find(this.options, function(opt){
return opt.id === parseInt(value);
}).text;
$(this.$el).append('<option value='+value+'>'+missingText+'</option>');
}
// update value only if there is a real change
// (without checking isSame, we enter a loop)
$(this.$el).val(value).trigger('change');
}
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
The reason is because you are listening to events on a component <select2> and not an actual DOM node. Events on components will refer to the custom events emitted from within, unless you use the .native modifier.
Custom events are different from native DOM events: they do not bubble up the DOM tree, and cannot be captured unless you use the .native modifier. From the docs:
Note that Vue’s event system is separate from the browser’s EventTarget API. Though they work similarly, $on and $emit are not aliases for addEventListener and dispatchEvent.
If you look into the code you posted, you will see this at the end of it:
Vue.nextTick(function(){
vm.$emit('input', newValue);
});
This code emits a custom event input in the VueJS event namespace, and is not a native DOM event. This event will be captured by v-on:input or #input on your <select2> VueJS component. Conversely, since no change event is emitted using vm.$emit, the binding v-on:change will never be fired and hence the non-action you have observed.
Terry pointed out the reason, but actually you can simply pass your update event to the child component as a prop. Check demo below.
Vue.component('select2', {
template: '<select #change="change"><option value="value1">Value 1</option><option value="value2">Value 2</option></select>',
props: [ 'change' ]
})
new Vue({
el: '#app',
methods: {
onChange() {
console.log('on change');
}
}
});
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
<div id="app">
<div>
<p>custom select</p>
<select2 :change="onChange"></select2>
</div>
<div>
<p>default select</p>
<select #change="onChange">
<option value="value1">Value 1</option>
<option value="value2">Value 2</option>
</select>
</div>
</div>
fiddle

SimpleCart.js search functionality

Have been using simpleCart for a while and it works great and i know it has a search function in it but it only seems to search certain elements my question is this, i would like to know if it can be set up to search within the contents of html files? as all the items are stored in Html pages for simple cataloging.
try this: JS
function filter(e){
search = e.value.toLowerCase();
console.log(e.value)
document.querySelectorAll('.item_name').forEach(function(row){
text = row.innerText.toLowerCase();
if(text.match(search)){
row.style.display="block"
} else {
row.style.display="none"
}
// need to count hidden items and if all instances of .kb-items are hidden, then hide .kb-item
var countHidden = document.querySelectorAll(".item_name[style='display: none;']").length;
console.log(countHidden);
})
}
function detectParent()
{
var collectionref=document.querySelectorAll(".simpleCart_shelfItem");
collectionref.forEach(group=>{
var itemcollection=group.getElementsByClassName("item_name");
var hidecounter=0;
for(var j=0;j<itemcollection.length;j++)
{
if(itemcollection[j].style.display==='none')
{
hidecounter++;
}
}
if(hidecounter===itemcollection.length)
{
group.style.display="none";
}else{
group.style.display="block";
}
});
}
And HTML:
<input type="text" onkeyup="filter(this);detectParent();"/>