Return value from nested promise with Webdriver - javascript

In my test code I wish to achieve the following:
it('Updates label text', function(done) {
page.testLabelText();
assert.equal(page.testLabelText().pageLabel, page.testLabelText().iFrameLabel);
done();
});
In my page object, here is testLabelText();:
page.testLabelText = function () {
var pageLabel = function () {
return driver.findElement(By.css('#element')).getText().then(function(text) {
return text;
});
};
var iFrameLabel = function () {
return driver.findElement(By.css('#element')).getText().then(function(text) {
return text;
});
};
return {
pageLabel: pageLabel(),
iFrameLabel: iFrameLabel()
};
};
But this returns 'Undefined' when printed to the console...I'm a newbie at javascript so though I've managed this in regular javascript, everything I've tried has failed with selenium WebdriverJS promises...

Your assert.equal() is comparing two distinct promise objects so that will never be true. To understand why, here's the step by step. What you need to do is to get the values out of both the promises after they are resolved and then compare the values.
page.testLabelText(); by itself just returns an object so calling it by itself with no assignment of the return value or referencing the return value does nothing.
page.testLabelText().pageLabel by itself is a promise.
page.testLabelText().iFrameLabel by itself is a promise.
And, they are different promise objects so your assert.equal() will not be true.
If you wanted to compare the two values from the promises, you'd have to do something like this:
var obj = page.testLabelText();
Promise.all(obj.pageLabel, obj.iFrameLabel).then(function(results) {
assert.equal(results[0], results[1]);
done();
});

The solution was to use an assertion library that could resolve promises in the tests, as this would be impossible using regular async asserts. In this case I used Chai as Promised.
Requiring the following:
chai = require('chai'),
chaiAsPromised = require("chai-as-promised"),
should = chai.should();
and including chai.use(chaiAsPromised); in mocha's before hook, I could then write
it('Updates label text', function() {
var label = FormsPage.testLabelText();
label.labelHeading.should.eventually.contain(label.userInput);
});
I found a blog post on this here

Related

How to mock a promise in Karma unit test called inside a function

I have the following function in my Angular controller and want to test, if the promise returns the expected result
function getName() {
var name = "";
nameService.getName().then(function (data) {
name = data.name;
});
return name;
}
How can I mock the promise call with fake data? I am not sure if I can use $httpBackend or $provide here? I tried this but it didn't work:
it("function getName should get the name from the nameService.getNameInfo function", function () {
var name = { name: "name1"};
spyOn(mockNameService, 'getNameInfo').and.callFake(function() {
return {
then: function(callback) {return callback(name);}
};
});
var result = myCtrl.getName();
expect(result).toEqual("name1");
});
Try using:
spyOn(mockNameService, 'getNameInfo').and.returns($q.when('dummyData'));
to mock your data.
Then you need to verify once it is resolved. So write:
expect(myCtrl.getName().then(function(name){
expect(name).toBe('dummyData');
}).toBeResolved();
The problem is not with your unit testing code, its an wrong implementation at all of the application code.
here:
function getName() {
var name = "";
nameService.getName().then(function (data) {
name = data.name;
});
return name; //it will return "" because the promise yet not resolved
}
In this code function getName will always return a empty string "" and it will never return the values you are assigning when the promise resolves name = data.name because promises are asynchronous, so by the time its resolves the function getName already returned the empty string to the caller!
So, in this case, refactoring the original code and fixed it there dhould be the first idea!
Actually your unit test codes identified the bug in your code, so its served already its original purpose, test case failing doesn't always meant that you have to fix it there, rather if your test cases are perfectly written against the logical expectation for all possible units/modules you need to think about why it is failing, looking at the actual code, and that is the actual purpose of writing unit test cases

Sinon stub pass through arguments

I want to be able to just return the arguments that a stub receives passed to a promise, is this possible:
Impl:
function(arg) {
return db.find(arg)
.then(result => {
return fnIWantToStub(result);
})
.then(nextResult => {
return done()
});
Test:
var stub = sinon.stub(fnIWantToStub);
stub.returns(PassedThruArgsThatReturnsAPromise);
Can this be done?
The docs state that you can return an arg at a given index.
stub.returnsArg(index);
I don't think there is a blanket return all args.
(UPDATE after question edit)
You could stub the db.find function and resolve it. I use mocha and chai.
const dbStub = sinon.stub(db, 'find);
const expected = {..};
dbStub.returns(Promise.resolve(someDataFixture));
const result = functionUnderTest(args);
// using chaiAsPromised
return expect(result).to.eventually.equal(expected);
Note you have to return to get Mocha to run through the promise. You can also use mocha's done callback. Although quite often I'll just use a Promise.
Checkout http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/

protractor custom expected condition fails with error

I'm trying to wait the browser with browser.wait with a custom ExpectedCondition like this
The FunctionReturningANumber returns only a number and the numberToCheck is the number to check the number for.
var conditionFn = function () {
return functionReturningANumber(param) === numberToCheck;
};
var condition = EC.and(conditionFn);
browser.wait(condition, 50000);
But if I execute this, I get the error: fn(...).then is not a function which basically says, that it expects an promise. I have looked up the documentation about ExpectedConditions, and the example for a custom one is like this:
// You can define your own expected condition, which is a function that
// takes no parameter and evaluates to a promise of a boolean.
var urlChanged = function() {
return browser.getCurrentUrl().then(function(url) {
return url === 'http://www.angularjs.org';
});
};
And I do not see how here a promise is created. I only see, that a boolean is returned, and the documentation says evaluates to a promise of a boolean which confuses me even more.
This above is for waiting a response from an API, this is caused, because the test triggers a backend process, which protractor then needs to wait for. If there is any better way of doing this, I would greatly appreciate a better way.
I am using protractor 3.1.1.
Any help really apprectiated.
Edit:
I found a way to solve this, for some reason the logical solution by #alecxe didn't work, even if it makes sense:
var numberFound = 0;
var done = false;
var check = function () {
numberFound = functionReturnungANumber(param);
if (numberFound != numberToCheck) {
setTimeout(check, 4000);
} else {
done = true;
}
};
check();
return done;
If I add this to the function and retrieve the return value in the test, which calls this function, and add a browser.wait(function () {
return done;
}); there it works.
It's not beautiful, but for some reason, its the only thing working.... for me at least.
It's just that you don't need to wrap your Expected Condition function into EC.and:
browser.wait(conditionFn, 5000);
Try this one.
browser.wait(conditionFn () {
return url === 'http://www.angularjs.org';
}, 8000);

getting ".then() is not a function" error when using AngularJS

This is my JS:
self.obj = {}
self.obj.accessErrors = function(data) {
var cerrorMessages = [];
for (prop in data) {
if (data.hasOwnProperty(prop)){
if (data[prop] != null && data[prop].constructor == Object) {
self.obj.fetch[accessErrors](data[prop]);
}
else {
cerrorMessages.push(data[prop]);
}
}
}
return cerrorMessages;
};
self.obj.fetch = {
X: function() {
// do stuff.
},
Y: function(callback) {
$http.get('/yposts/').then(
function(response) {
self.posts = response.data;
callback(self.posts);
},
function(response) {
self.posts = {};
self.obj.accessErrors(response.data).then(function(cerrrorMessages) {
callback(posts, cerrorMessages);
});
}
);
}
};
And I am getting an error pointing to this line:
self.obj.accessErrors(response.data).then(function(cerrrorMessages) {
The error says:
TypeError: self.obj.accessErrors(...).then is not a function
Any idea how to solve this?
self.obj.accessErrors(response.data) does not return a promise so therefore, you can't use promise methods on it.
If you want it to return a promise and you want that promise to reflect when all the fetch() operations are done and those operations are actually async, then you will have to make all your async code into using promises and you will have to combine them all using Promise.all() or the angular equivalent and convert from using callbacks in fetch to just using a promise. Right now, you have a mix which is difficult to program with.
The .then() construction is only needed when using Promise objects - essentially, instead of returning a value, the function returns an object that resolves to a value at some point in the future (which is then passed into the function that you pass to .then().
But you are right in that you need an asynchronous pattern to do this correctly, since fetch.Y is an asynchronous method. A good thing to do would be to create an array of promises at the beginning of your accessErrors function, like so:
var fetchPromises = [];
and then replace self.obj.fetch[accessErrors](data[prop]); with something that calls push on that array to add the promise that fetch returns to it.
Then, instead of returning accessErrors, return Promise.all(fetchPromises).
This will require some fairly significant modification to your code, however - namely, you will need to rewrite it so that it uses the Promise API instead of this callback by itself (which shouldn't be too difficult to do).

Using forEach to sequentially execute functions in Q

I'm attempting to run a series of functions based upon the Q API using their first strategy for sequences. This suggests the pattern:
var funcs = [foo, bar, baz, qux];
var result = Q(initialVal);
funcs.forEach(function (f) {
result = result.then(f);
});
return result;
What structure are each of the functions within the array meant to take? I am quite confused about when to use the return def.promise;. Is that simply always the last line? Will it frequently or always immediately follow def.resolve(someVar). Is something like this then ended structure?
function foo(f){
var def = Q.defer();
f++;
def.resolve(f);
return def.promise;
}
So that each subsequent function within the array will receive the newly calculated value of f: in this case, if var initialVal = 1; and four functions each incrementing f++, the returned result will be 4? How do I access that returned value? console.log(result) prints { state: 'pending' } .
What structure are each of the functions within the array meant to take?
Q.js allows promises to be created in several ways. For example :
function foo(value) {
var def = Q.defer();
def.resolve(value + 1);
return def.promise;
}
function foo(value) {
return Q(value + 1);
}
function foo(value) {
return Q.Promise(function(resolve, reject) {
resolve(value + 1);
});
}
Other Promise libs are similar, but not necessarily so flexible. Native js Promises must be constructed with the third of these approaches.
However, in the real world you will only rarely need to create your own Promise. You will typically be dealing with promise-returning lib methods that someone else has written. For example :
function foo(value) {
return lib.doSomethingAsync(value, and, other, params);
}
How do I access that returned value?
The code is easier to understand if member name "result" is replaced with "promise", and result.then(f) is rewritten with an anonymous function that calls f().
function performAsyncSequence() {
var promise = Q(initialVal);
funcs.forEach(function (f) {
promise = promise.then(function(previousResult) {
return f(previousResult);
});
});
return promise;
}
This is 100% equivalent to the code in the question, but now it should be clearer how the previous result is passed down the promise chain.
Accessing all previous promise results in the sequence is more complicated. The answers here discuss the subject comprehensively.

Categories