Can someone help me understand how WebDriverJS/Protractor works in this case?
function MyPageObject(buttonElementFinder) {
this.getButtonByIndex = function(index) {
return {
myButton: buttonElementFinder.get(index)
}
}
}
1. describe('My button', function() {
2.
3. it('should contain the text foo', function() {
4. var myElementFinder = element.all(by.css('.foo'));
5. var pageObject = new MyPageObject(myElementFinder);
6. var button = pageObject.getButtonByIndex(0);
7. expect(button.text()).toBe('foo');
8. });
9.
10. });
Does the WebDriverJS control flow have an action added to it on line 6 because of the .get method of ElementFinders?
I presume the expect also adds another item to the control flow too on line 7?
Edit: I have update the code to use element.all.
var myElementFinder = element.all(by.css('.foo'));
myElementFinder is a ElementArrayFinder and is simply an object. Nothing async is happening here.
var pageObject = new MyPageObject(myElementFinder);
Obvious.
var button = pageObject.getButtonByIndex(0);
This will return an ElementFinder from buttonElementFinder.get. Nothing async is happening here.
expect(button.text()).toBe('foo');
button.text() returns a promise from Webdriver.schedule, which in turn is using the control flow which is retrieved using webdriver.promise.controlFlow(), which exposes an execute function.
Related
I have a module with four functions that call one after the other. I am trying to follow the Revealing Module Pattern. One of the functions is public, the remaining are private. It goes like this:
publicMethod is called from another module
queryNames is called from publicMethod
execute(parameters, callback?, errback?) is called from queryNames
addNamesList is called as the callback? argument of execute
Several dijit/form/CheckBox's are created and the method querySegments is triggered onChange
querySegments needs to call a method of an object created in publicMethod.
The problem is in step 6, I can't reach the object created in step 1.
I have tried to use dojo hitch to define the callback? argument in step 3, but I can't get it to work. I tried putting this in its first argument, but even then I can't reach the required scope to call addNamesList.
Here is some code to demonstrate this issue.
define([
'dojo/dom',
'dijit/form/CheckBox',
'esri/layers/ArcGISDynamicMapServiceLayer',
'esri/tasks/query',
'esri/tasks/QueryTask',
'dojo/_base/lang'
],
function (
dom,
CheckBox,
ArcGISDynamicMapServiceLayer,
Query, QueryTask,
lang
) {
// ***************
// private methods
// ***************
// fetch names and call addNamesList to put the list in place
var queryNames = function (map, mapLayer) {
// new QueryTask(url, options?)
var queryTask = new QueryTask("url")
var query = new Query()
// execute(parameters, callback?, errback?)
// this callback passes an argument called featureSet
queryTask.execute(query, lang.hitch(map, "addNamesList", mapLayer), function(error) {console.log(error)})
}
// callback function of queryNames
var addNamesList = function (mapLayer, featureSet) {
console.log('addOplist')
var namesCount = featureSet.features.length
for (var i = 0; i <namesCount; i++) {
// work
var cbox = new CheckBox({
id: "cbox_" + i,
value: featureSet.features[i].attributes["someID"],
checked: false,
onChange: function (evt) {
querySegments(this.value, mapLayer)
}
})
cbox.placeAt("someDiv" + i, "first")
}
}
// triggered by the checkbox event
var querySegments = function (name, mapLayer) {
// build the query
var queryStatement = "someID = " + name
var layerDefinitions = [queryStatement]
// call a method of mapLayer
mapLayer.setLayerDefinitions(layerDefinitions)
}
// **************
// public methods
// **************
var publicMethod = function (map) {
var mapLayer = new ArcGISDynamicMapServiceLayer('restURL')
map.addLayer(mapServiceLayer)
queryNames(map, mapLayer)
return mapLayer
}
return {
publicMethod: publicMethod
}
}
)
You can see a more detailed explanation and a working example on this other (and more broad) question that I have put on Code Review.
I am new to JavaScript and I guess I still have a lot of issues with scoping, closures and callbacks.
I will deeply appreciate any input, including how to improve this question.
Edit
With this current implementation (with dojo hitch), no error is thrown. The method addNamesList is not called (nor errback, which I also don't understand why). I think this is because addNamesList is not on map's (hitch first argument) namespace. I tried to put this instead, but it makes no difference.
Before I decided to use hitch, the code looked like this:
var queryNames = function (map, mapLayer) {
...
queryTask.execute(query, addNamesList)
}
var addNamesList = function (featureSet) {
...
...
...
querySegments(this.value, mapLayer)
}
but then I couldn't reach mapLayer inside the method triggered by the check box event. It would throw Uncaught ReferenceError: mapLayer is not defined. That is why I tried to use hitch.
Javascript is asynchronous, so pretty much data coming from db, http requests or whatever is returned via callbacks. Here's what happens in your code:
public method calls queryNames
queryNames call addNamesList of map asynchronously and returns nothing
public method takes back control, meanwhile some stuff is going on with the addNamesList
mapLayer is returned untouched while some stuff is still going on in the background
So, to avoid this, you should return data from public method via callback, so you pass callback as the second parameter to the public method, then to the querySegments. Then, in the success callback of query, when you finally get the result ready, you do:
callback(mapLayer);
So, everything you should do is to pass this callback as deep as needed to the place where you have your mapLayer ready (so you've done with it everything you wanted), and then do a callback(mapLayer);.
This and this would probably explain better.
Best regards, Alexander
I have discover that a custom code is not syncronous as I thought.
I have this pseudo code:
ObjectA = function ()
{
var pointer;
var value =[];
this.set_pointer = function (p) {pointer = p;}
this.return_value = function () {return value[pointer];}
}
ObjectB = function ()
{
var SCOPE = this;
var OBJ = new ObjectA();
....
this.reset = function ()
{ OBJ.set_pointer(0);}
this.draw = function (what)
{
SCOPE.update();
OBJ.set_pointer(from);
OBJ.get_value();
// do somethings with Three.js
// draw some lines and some little pointclouds.
// do some things
// update two text elements
}
}
Main = new ObjectB();
Main.draw(7);
Main.reset();
ObjectA is using arraybuffers, dataviews and typedarrays.
ObjectB is using Three.js to draw some very symple 3D things.
The problem is inside 'draw'.
OBJ.get_value(); is using the pointer value 0 (zero) instead 7.
Abnormally (as I think) main.draw(7) is not executed first and later Main.reset(); It seems that Main.reset() is inmediatelly executed, so I have 0 (zero)
I'm not going to wait any DOM synchronism.
What can be the reason of this bechaviour?. Maybe the Three,js use ? The OOP style I'm using ?
Is there any way to check why is this happen?
Any idea would be appreciated
NOTE: Sorry for use the Three.js tag.
SOLVED
I have found the reason (or I think ) of a NO synchonism
Sometimes 'this' could be pointing to 'window' instead of the self instance of your object.
Sometimes, a bad use of this (when it is 'window') can raise an error you have not taken into account. Then a next line of code can be executed, and sometimes you can have the impression of an incorrect (not syncronous) operation.
So.... review the bad use of 'this'....
Somewhere a constructor is defined like
var Something = function() {
// do stuff
}
If I could insert the line
mydebug.Something = this;
at the beginning, I could store the most recently created instance of Something, which would help me debugging. I can't, but I get passed the Something (the function and its name) and can replace it by something else. I just don't know how to make the "something else" to behave like the original.
function intercept(someConstructor, someName) {
return function <<someName>> () { // 1
var result = <<create a new instance>>; // 2
mydebug[someName] = result;
return result;
}
}
which would get called as
intercept(Something, "Something")
There are at least two problems:
I don't know how to create a function having a name given by a variable.
I don't know how to create the instance.
For 1:
Depending on where you are storing that function you don't have to name it in your intercept implementation.
You can just do return function () { ... or you can do something similar to someObject[someName] = function()... and then return someObject[someName].
For 2:
If I understand correctly you should be able to just do var result = new someConstructor();, but I'm not 100% clear on your goal.
I have created this:
var where = function(){
sym.getSymbol("Man").getPosition()
}
console.log(where);
if (where()<=0){
var playMan = sym.getSymbol("Man").play();
} else {
var playMan = sym.getSymbol("Man").playReverse();
}
This is for Edge Animate hence all the syms. I am trying to access the timeline of symbol Man, then if it is at 0 play it. But it isnt working and the reason, I think, is that I have an incomplete understanding of how a var works. In my mind I am giving the variable 'where' the value of the timeline position of symbol 'Man'. In reality the console is just telling me I have a function there, not the value of the answer. I have run into this before and feel if I can crack it I will be a much better human being.
So if anyone can explain in baby-language what I am misunderstanding I would be grateful.
Thanks
S
var where = function () { ... };
and
function where() { ... }
are essentially synonymous here. So, where is a function. You are calling that function here:
if (where()<=0)
However, the function does not return anything. You need to return the value from it, not just call sym.getSymbol("Man").getPosition() inside it.
That, or don't make it a function:
var where = sym.getSymbol("Man").getPosition();
if (where <= 0) ...
The value will only be checked and assigned once in this case, instead of updated every time you call where().
Try
var where = function()
{
return sym.getSymbol("Man").getPosition();
};
Your code wasn't returning anything.
var where = function() {
return sym.getSymbol("Man").getPosition()
}
console.log(where);
if(where()<=0) {
var playMan = sym.getSymbol("Man").play();
} else {
var playMan = sym.getSymbol("Man").playReverse();
}
I have following code in my JavaScript code.
if (window.confirm('Are you sure?')) {
AdminData.actOnResult('delete');
}
I am writing test for this piece of code. How do I mock window.confirm method? I tried following code but it did not work.
window.confirm = function(arg) {
return true;
};
I can move the window.confirm method to another function and then I can mock that method. However I was wondering if there is a better solution.
Your own code works fine for me in IE. Just the following in the global scope should override it:
var confirm = function () { return true; }
EDIT
I've seen a few questions on SO in the past about trying to override confirm, mostly because they don't like it (and who would?). If you're trying to bypass it for this sort of reason, I suggest you look at changing your code to implement a callback-based replacement for confirm. Take a look at jQuery UI's modal confirm for a good example of this.
I am using Jasmine for unit testing and have mocked alert and confirm with the following
alert = function (alertString) {debug.log('ALERT:', alertString);};
var confirmValue = true; //set this before you expect the confirm statement to be shown
confirm = function (confirmString) {
debug.log('CONFIRM:', confirmString, confirmValue);
return confirmValue;
};
Then I can say:
describe("test", function () {
it('should test true confirm workflow', function () {
confirmValue = true; // or false if you like
//expect outcomes that would come from any confirms being called with true
});
});
It's not perfect, and if you have multiple confirms that could pop between setting that confirmValue, you could be in trouble. Perhaps then it would be good to setup a cue of expected confirm return values... tricky...
I'd think about implementing a wrapper around static methods on the window (or other) object. Then provide your wrapper to whatever uses the static method. Obviously this is easier if you are using a "class"-based implementation. Then, in order to mock the method, simply provide a different wrapper that returns the value that you want.
var windowWrapper = {
confirm: function(msg) { return confirm(msg); },
...
};
var mockWrapper = {
confirm: function(msg) { return true; },
...
}
var wrapper = windowWrapper;
if (test) {
wrapper = mockWrapper;
}
...
if (wrapper.confirm('Are you sure?')) {
AdminData.actOnResult('delete');
}