Why evaluate() in Protractor? - selenium

When I run the below snippet, I got the following output. But I'm still unclear why and when evaluate() has to used ....
browser.get('https://weather.com/en-IN');
$$("input[data-ng-change='goSearch()']").evaluate('placeholderText').then(function(value) {
console.log(value);
});

evaluate() is rarely used, but has a unique purpose - it gives you an access to the scope of the current element you are working with. This is usually needed when a value you are looking for is not exposed in the HTML as an attribute or element's text.
For example, when you have a repeater over an array of objects and you need to access some object property that is not in the HTML:
element.all(by.repeater("address in addresses")).filter(function (elm) {
return elm.evaluate("address.zipCode").then(function (zipCode) {
return zipCode === "10801";
});
});

Related

Cypress Get Attribute value and store in Variable

I want to get the Attribute value and store in a variable how we can achieve this in cypress
In my case I want to get the complete class value and store it in variable.
This code just give me the attribute class value but how I can store the fetch value in variable
cy.get('div[class*="ui-growl-item-container ui-state-highlight ui-corner-all ui-shadow ui-growl-message"]').invoke('attr', 'class')
I was trying to compare the style of one element with another to make sure they were equal. Here's the code that seems to work for me.
cy.get('.searchable-group-selector-card-image')
.eq(4)
.invoke('attr', 'style')
.then(($style1) => {
const style1 = $style1
})
A good way to solve this kind of scenario is to use the alias mechanism. One could leverage this functionality to enqueue multiple elements and then check all of them together by chaining the results. I've recently come to a case in an SPA where the assertion had to happen between elements that were spread across different angular routes (call them different pages).
In your use case, this would like:
cy.get('.searchable-group-selector-card-image')
.eq(4)
.invoke('attr', 'style')
.as('style_1')
cy.get('.another-element')
.invoke('attr', 'style')
.as('style_2')
// later on for example you could do
cy.get('#style_1').then(style_1 => {
cy.get('#style_2').then(style_2 => {
// Both values are available and any kind of assertion can be performed
expect(style_1).to.include(style_2)
});
});
This is described in Variables and Aliases section of the Cypress Documentation.
Here is how I got the value of for attribute in a label tag which had text "Eat" inside.
cy.contains('Eat').then(($label) => {
const id = $label.attr('for');
}
Most important thing is to get the selector right, so it exactly finds the value you are looking for. In this case you already found it. By using then() gives you the ability to store it in a variable.
cy.get('div[class*="ui-growl-item-container ui-state-highlight ui-corner-all ui-shadow ui-growl-message"]').invoke('attr', 'class')
.then($growl-message => {
const message = $growl-message.text()
//do the checks with the variable message. For example:
cy.contains(message)
})
Note that the scope of the variable is within the curly brackets. Thus using the variable has to be within those curly brackets.

bind dynamically to this vuejs

Hey guys I have the following function its working ok but I think it could be better.
methods: {
onFileChange(e, filedName) {
console.log(e.target.files);
console.log(filedName);
const file = e.target.files[0];
const fileToCheck=document.getElementById(filedName);
console.log(fileToCheck);
if(filedName=='thumbnail1'){
if(fileToCheck.value!=''){
this.thumbnail1 = fileToCheck;
this.thumbnail1Url= URL.createObjectURL(file);
} else {
this.thumbnail1=null;
this.thumbnail1Url=null;
}
}
if(filedName=='thumbnail2'){
if(fileToCheck.value!=''){
console.log(fileToCheck);
this.thumbnail2=fileToCheck;
this.thumbnail2Url = URL.createObjectURL(file);
} else {this.thumbnail2=fileToCheck; this.thumbnail2Url=null;}
}
},
Instead of checking the value for
if(fieldName == "something"){
this.something = URL.createObjectURL(file)
}
I would simply pass in a string of the fieldName and bind to it dynamically by just typing this.fieldName (filedName could equal thumbnail1 or thumbnail2 or chicken for all I care I just want to be able to pass in the name of the data atrribute and bind to it that way) but when ever I do this it doesn't work. Any help here would be great.
It's not completely clear to me what you want to accomplish, but I think you're asking about creating a dynamic data property for a view component. If that's the case, there are a couple of things to consider.
First, the example you cite, this.fieldName is not correct JavaScript syntax if fieldName is a string that contains a property name. The correct version is this[fieldName].
Note, though, that you can't simply define a new data property for a Vue component by setting it to a value. That's a limitation of JavaScript that's described in the Vue documentation. If data[fieldName] is an existing property that's defined in the component's data object, then you'll be okay. Even if you don't know the value of the property, you can initialize it, for example, with a value of null and then update the value in your method. Otherwise, you'll need to add the property to an existing non-root-level property as the documentation explains.

How to hook into a change in the model in a widget

I'm trying to respond to a change in one of the properties of the model in a widget. To be clear, when the value of the property changes, I want to run some code to react to the change. In a parent widget I have a date picker which changes the date in the model.
I cannot get the custom setter to be called _setParentPropertyAttr...
If I include this in my widget
<span data-dojo-type="dojox/mvc/Output" data-dojo-props="value: at(rel:, 'ParentProperty')">
It works nicely. Changing the date picker outputs the current value to the page. So I can supply the value property to the output widget when the date changes in the model. But what I need to do (I think) is supply a custom property with the date property in the model when the date picker changes the value.
I realise this question is a bit vague but I can't provide the code as it's proprietary.
I've tried to break the problem down by setting a property manually within my widget as:
myProperty:0,
...
constructor
...
_setMyPropertyAttr: function(value):
{
console.log("setting myproperty");
}
....
this.set('myProperty', 5);
....
but that isn't working either.
If you set a property within a widget does that not call the custom setter?
I'm struggling a bit because there aren't so many dojo examples out there any help is much appreciated.
You can bind an event to be called when a widget's property is set/update or you can even use watch to do that.
But this only works using the set function, using someWidget.someProperty = 5; wont work.
Let me show you how dojo do it. The basic about the magic setters and getters is explained here.
_set: function(/*String*/ name, /*anything*/ value){
// summary:
// Helper function to set new value for specified property, and call handlers
// registered with watch() if the value has changed.
var oldValue = this[name];
this[name] = value;
if(this._created && !isEqual(oldValue, value)){
if(this._watchCallbacks){
this._watchCallbacks(name, oldValue, value);
}
this.emit("attrmodified-" + name, {
detail: {
prevValue: oldValue,
newValue: value
}
});
}
}
This peace of code is from dijit/_WidgetBase, the _set function is what dojo calls after a set is called, and is where it finally set the property value this[name] = value; and as you can see, it emit an event that will be called attrmodified-propertyName and also call a watchCallbacks.
For example, if in some place, we have this:
on(someWidget, 'attrmodified-myproperty', function(){
console.log(someWidget.get('myProperty'));
});
and then we use:
someWidget.set('myProperty', 'Hello World!');
The event will be triggered. Note that someWidget.myProperty = 'Hello World!' wont trigger the event registration. Also note that if in our widget we define the magic setter:
_setMyPropertyAttr: function(value) {
//do something here with value
// do more or less with other logic
//but some where within this function we need to cal "_set"
this._set('myProperty', value);
}
without calling _set, the magic wont happen.
As i said at the beginning, we can also use watch:
someWidget.watch('myProperty', function(){
console.log(someWidget.get('myProperty'));
});
Note that we can register to this events or the watch function within the same widget.
Also, as a plus, the magic setter can be triggered when creating the widget with just passing the property name in the constructor object param, this work for the declarative syntax too, for example:
var someWidget = new MyWidget({
'myProperty': 'Hello World!!'
});
and
<div data-dojo-type="MyWidget" data-dojo-props="myProperty: 'Hello World!!'"></div>
both will triggered a call to the _setMyPropertyAttr if exist, or dojo will use the magic setter in the case it doesn't exist.
Hope it helps
Consider using custom setter on your widget, where you can add your custom logic.
Example of definition of custom setter on your widget:
_setOpenAttr: function(/*Boolean*/ open){
// execute some custom logic here
this._set("open", open);
}
Example of setting a property on your widget:
widgetRef.set('open', true);
Alternatively you can could consider using dojo/store/Observable.
dojo/store/Observable store wrapper that adds support for notification of data changes.
You can read more about it on the followign link:
https://dojotoolkit.org/reference-guide/1.10/dojo/store/Observable.html
If figured out the problem. If I set a watch on the model I can then check if indiviual properties have changed in the watch function. I knew it would be something simple!

Iterating elements using NightWatchJS

How do i click a button returned by elements command in night watch
client.elements('xpath', ".//a[#class='abcd')]", function (allButtons){
console.log('Element value is '+element)
allButtons.value.forEach(function (element) {
this.elementIdClick(element, function(res){});
}
}
While running i am getting an error as
Element value is [object Object]
TypeError: Object #<Object> has no method 'elementIdClick'
So how do i get each element from the element list returned by client.elements
I realized the parameters for elementIdClick is wrong, i updated the code as
client.elements('xpath', ".//a[#class='abcd')]", function (allButtons){
allButtons.value.forEach(function (element) {
console.log('Element value is '+element)
this.elementIdClick(this.elementIdAttribute(allButtons.value[element].ELEMENT, 'id'), function(res){});
Now the error is
Element value is [object Object]
TypeError: Cannot read property 'ELEMENT' of undefined
So again back to original question. How do i get individual elements from a list of webelements using nightwatchJS
The following worked for me:
function iter(elems) {
elems.value.forEach(function(element) {
client.elementIdClick(element.ELEMENT)
})
};
client.elements('css selector', 'button.my-button.to-iterate', iter);
Each element is a JSON object of the form { ELEMENT: string } (so, has no method itself.)
this in forEach does not point to the element, nor client: you need to invoke client.elementIdClick() or will get a TypeError.
Hope it helps.
I used the following strategy to iterate over DOM elements using Nightwatch:
// Executing a function in the application context.
client.execute(function () {
// Get elements by CSS selector.
var elements = document.querySelectorAll('.elements');
// Iterate over them.
[].forEach.call(elements, function (element) {
// Manipulate each element.
element.click();
});
});
That snippet is inside a test of course.
If you use jQuery or something similar you can use that too.
I think the error is getting generated by your console.log() statement.
From the elements() command, allButtons.value will be an array of several objects. To access key pairs in that array, you need need to specify where in the array and then reference the object: allButtons.value[index].ELEMENT
Because you gave your .forEach() loop only one arg, it's interpreting that as the index for the array, and in my code sample below I replaced your local variable element with index for clarity. There is also no need to use the .elementIdAttribute() function; the number returned by allButtons.value[0].ELEMENT will work as the id.
client.elements('xpath', ".//a[#class='abcd')]", function (allButtons){
allButtons.value.forEach(function (index) {
console.log('Element value is '+index.ELEMENT)
client.elementIdClick(index.ELEMENT);}})
Hope that helps.

KoLite asyncCommand accessing element data

So I'm displaying a observable array in my view, and I want to be able to remove an element from that list using asyncCommand. However, I'm not sure how I should be getting that element. Is there a way of accessing or passing the selected element into the asyncCommand method?
Thanks for the input
addGroupCmd = ko.asyncCommand({
execute: function (data, complete) {
//access your observable here with the data object
//EX. var demo = data.id();
},
canExecute: function (isExecuting) {
return !isExecuting && isEditing();
}
}),
Ok, so I figured it out with it little bit of google's help. All you have to do is pass in the data parameter and ko.lite will figure out what object your talking about. pretty nice, not really sure how it works, but it does.