In Protractor, every action on an UI element is a promise. A
promise is something that happens in the future. It is easy to mix present with
the future, for example:
it('test_promise', function () {
browser.get('http://localhost:8080/examples/test.html')
.then(function() {
var btnEle=element(by.id("b1"));
for(var i=0; i<5; i++){
console.log("clicking at "+i+" times");
btnEle.click().then(function(){
console.log("clicked at "+i+" times");
})
}
});
})
The output from the above code is:
clicking at 0 timesclicking at 1 timesclicking at 2 timesclicking at 3 timesclicking at 4 timesclicked at 5 timesclicked at 5 timesclicked at 5 timesclicked at 5 timesclicked at 5 times
What happens is the click()
action is put into the promise queue and scheduled to happen in the future,
while the for loop finishes in the present. If we
want click() to record the clicking times
correctly, we have to use another variable to record the times:
it('test_promise2', function () { browser.get('http://localhost:8080/examples/test.html') .then(function() { var btnEle=element(by.id("b1")); var j=0; for(var i=0; i<5; i++){ btnEle.click().then(function(){ j++; console.log("clicked at "+j+" times"); }) } }); })
This trick can be used to get values from an array of
promises:
it('test_getAllElements', function () { browser.get('http://localhost:8080/examples/test.html') .then(function() { var messages=[]; var j=0; var defer = protractor.promise.defer(); element.all(by.xpath("//body")).then(function(eles) { for (var i = 0; i < eles.length; i++) { util.getNonInputText(eles[i]).then(function (text) { if (util.isDefined(text) && text.length > 0) { console.log("message is:" + text); messages.push(text); } j++; if (j === eles.length) { console.log("fulfilled"); defer.fulfill(messages); } }); } }); defer.promise.then(function(result){ console.log(result); }); }); })
Even something as innocent as getting the ID of an element
is a promise, to get the ID, you have to get it in then():
Util.prototype.getId = function(element){ return element.getAttribute("id").then(function(id){ return id; }); };
In my tests, on my occasions, I have to calculate IDs based
on known elements, so I have to write the logic like this:
var ele=element(by.name("abc")); util.getId(ele).then(function(id){ var anotherEle=element(by.id(id+"_btn")); anotherEle.click().then(function(){ ... }) })
This is certainly less intuitive than getting the id directly:
var ele=element(by.name("abc"));var id=util.getId(ele); var anotherEle=element(by.id(id+"_btn"));
I wanted to find a way to make writing async code as
intuitive as writing sync code, this led me to Generator.Here
is some code that shows the basics of Generator:
function* f() {
console.log('before a');
yield 'a';
console.log('before b');
yield 'b';
console.log('before c');
yield 'c';
console.log('before d');
return 'd';
}
var g = f();
console.log("1");
console.log(g.next());
console.log("2");
console.log(g.next());
console.log("3");
console.log(g.next());
console.log("4");
console.log(g.next());
console.log('before a');
yield 'a';
console.log('before b');
yield 'b';
console.log('before c');
yield 'c';
console.log('before d');
return 'd';
}
var g = f();
console.log("1");
console.log(g.next());
console.log("2");
console.log(g.next());
console.log("3");
console.log(g.next());
console.log("4");
console.log(g.next());
The output is:
1
before a
{ value: 'a', done:
false }
2
before b
{ value: 'b', done:
false }
3
before c
{ value: 'c', done:
false }
4
before d
{ value: 'd', done: true
}
function* f(), * makes function f a generator
function. The code inside the generator function won’t get executed until next() is called; and execution inside
the generator function will stop at yield until next() is called again.
This feature is useful in making async code into sync. Say,
one line of code creates a promise, the next line of code won’t get executed
until the promise is executed. Library co (https://github.com/tj/co
) does exactly that.
With co,
I
can write code in a more intuitive way. For example, I can now get the id of an
element:
co(function*() {
var ele = element(by.name("abc"));
var id=yield util.getId(ele);
var anotherEle=element(by.id(id+"_btn"));
});
var ele = element(by.name("abc"));
var id=yield util.getId(ele);
var anotherEle=element(by.id(id+"_btn"));
});
The resulting code is much more structured and readable:
And by the way, in Webstorm, you need to configure javascript language in order for Webstorm to support yield syntax:
No comments:
Post a Comment