Stale Element Reference in Protractor although conditional wait is added - javascript

Have problem under beforeEach function in my test class.
Click on the usersTab sometimes works fine, and sometimes returns StaleElementReferenceException.
Have tried protractor.ExpectedConditions like presenceOf or visibilityOf or elementToBeClickable but none worked in 100%.
I think that is caused by asynchronous and sometimes browser firstly want to click and afterwards wait - is it possible ?
Any idea how to handle it ?
var OnePage = require('../pages/one_page.js');
var SecondPage = require('../pages/second_page.js');
describe('Test', function () {
var onePage;
var secondPage;
var EC = protractor.ExpectedConditions;
beforeEach(function () {
browser.ignoreSynchronization = true;
onePage = new OnePage();
browser.wait(EC.presenceOf(onaPage.userLogin), 5000);
onePage.setUser('login#login');
onePage.setPassword('password');
onePage.login();
secondPage = new SecondPage();
browser.wait(EC.visibilityOf(secondPage.usersTab), 10000);
usersPage.usersTab.click();
});
I am using jasmine:2 and protractor:2.2.0 .

When I first started to build my Test Suite I ran into a similar issue and it took me a while to track down a solution. When you initialize the Page Object using a standard var in your beforeEach function it can hold on to an old instance when the 2nd or higher test is run. No idea how long it typically takes or what really triggered the hiccup in my tests, it was always random for me. The only way I could solve this was to use the this keyword in my beforeEach function like the example below. When the beforeEach function is executed the instance is correctly renewed in each test.
NOTE: In my conf.js file under onPrepare I have the following function setup to tell Protractor if the page that follows is an Angular page or not:
global.isAngularSite = function(flag) {
browser.ignoreSynchronization = !flag;
};
var OnePage = require('../pages/one_page.js');
var SecondPage = require('../pages/second_page.js');
describe('Test', function () {
beforeEach(function () {
isAngularSite(true);
this.onePage = new OnePage();
this.secondPage = new SecondPage();
this.ec = protractor.ExpectedConditions;
browser.wait(this.ec.presenceOf(this.onePage.userLogin), 5000);
this.onePage.setUser('login#login');
this.onePage.setPassword('password');
this.onePage.login();
browser.wait(this.ec.visibilityOf(this.secondPage.usersTab), 10000);
usersPage.usersTab.click();
});
it('', function() {
});
});

Try chaining the actions with each other so that they execute serially once the first one is completed resolving its promise. Here's how -
beforeEach(function () {
browser.ignoreSynchronization = true;
onePage = new OnePage();
browser.wait(EC.presenceOf(onaPage.userLogin), 5000).then(function(){
onePage.setUser('login#login');
onePage.setPassword('password');
onePage.login();
});
secondPage = new SecondPage();
browser.wait(EC.visibilityOf(usersPage.usersTab), 10000).then(function(){
usersPage.usersTab.click().then(function(){
//If you want to verify something after click, do it here.
browser.sleep(500);
});
});
});
If you want the second page actions to be done only after first one, then chain them together.
browser.wait(EC.presenceOf(onaPage.userLogin), 5000).then(function(){
//Actions of OnePage
}).then(function(){
//Actions of second page
secondPage = new SecondPage();
browser.wait(EC.visibilityOf(usersPage.usersTab), 10000).then(function(){
usersPage.usersTab.click().then(function(){
//If you want to verify something after click, do it here.
browser.sleep(500);
});
});
});
Hope this helps.

Related

Explain the function Middleware

Can anybody give me tutorial how this function is working i am not able to understand how this function is working. How this.go function actually working. Any Help would be appreciated. Thanks
var Middleware = function() {};
Middleware.prototype.use = function(fn) {
var self = this;
this.go = (function(stack) {
return function(next) {
stack.call(self, function() {
fn.call(self, next.bind(self));
});
}.bind(this);
})(this.go);
};
Middleware.prototype.go = function(next) {
next();
};
USAGE
var middleware = new Middleware();
middleware.use(function(next) {
var self = this;
setTimeout(function() {
self.hook1 = true;
console.log('a')
next();
}, 10);
});
middleware.use(function(next) {
var self = this;
setTimeout(function() {
self.hook2 = true;
console.log('b')
next();
}, 10);
});
var start = new Date();
middleware.go(function() {
console.log(this.hook1); // true
console.log(this.hook2); // true
console.log(new Date() - start); // around 20
});
The purpose of this code seems to be to build up a sequence of functions, which will then be run one after the other.
The go method's job is to run some code. If you've never called use, then go will run the specified code immediately, with no complications.
The use method lets you insert additional steps to be run. When you call use, the original go method is replaced with a new method. That new method will call the old go method, followed by your custom method. If use is called multiple times, this builds up a sequence of functions. The functions will be called in the order they were passed in to use. The next function starts running when the previous function calls next().
A lot of the complication of the code comes from making sure that the value of this stays what it's expected to be. If that wasn't a requirement, then use could be rewritten like this:
Middleware.prototype.use = function(fn) {
const oldGo = this.go;
this.go = next => oldGo(() => fn(next))
}

Runtime unit testing in JavaScript

I've been testing JavaScript code using unit testing frameworks like jasmine and Qunit. But all these testing framework works only at load time, but I want to initiate the test cases at run time, for instance I want to test an object's value on a button click like below test case in Jasmine,
function btnClick() {
var temp++;
describe("Test Suite Inside Button Click", function () {
it("To test true value", function () {
expect(temp).not.toEqual(-1);
});
});
};
How to run the test cases dynamically ?
Here is how you do it.
Invoke the jasmineEnv at run time and run the test
Note that I'm clearing out the reporter div just to clean up the
output- which may not to necessary in your case.
My setTimeout is only to load the div onto the page
See it in action here
var testFunc = function() {
//this is optional- I'm just clearing the reporter out to run it on fiddle.
$('.jasmine_html-reporter').remove();
var jasmineEnv = jasmine.getEnv();
describe('test', function() {
it('sample test', function() {
console.log('test ran');
expect(true).toBe(true);
});
});
jasmineEnv.execute()
}
setTimeout(function() {
$('#myElement').click(testFunc);
}, 0);

How to test code inside $(window).on("load", function() {}); in Jasmine

I have a javascript below, which appends a DIV on page load and hides it after 3 sec.
var testObj = {
initialize: function() {
var that = this;
$(window).on("load", function() {
(function ($) { //Append Div
$('body').append("<div>TEST</div>");
})(jQuery);
that.hideAppendedDiv();
});
},
hideAppendedDiv: function() { //Hide appended Div after 3s
setTimeout(function(){
$("div").hide();
}, 3000);
}
};
//call Initialize method
testObj.initialize();
How to write Jasmine test cases for the methods in the code.
I'm guessing that you don't really want to test a Javascript function such as $(window).on('load')... , but that your own function hideAppendedDiv() get's called from $(window).on('load'). Furthermore, you want to make sure that the function hideAppendedDiv() works as well.
IMO, you need two expects.
Somewhere in your setup beforeEach function:
beforeEach(function () {
spyOn(testObj , 'hideAppendedDiv').and.callThrough();
});
Expectations
it('expects hideAppendedDiv() to have been called', function () {
// make the call to the initialize function
testObj.initialize ();
// Check internal function
expect(testObj.hideAppendedDiv).toHaveBeenCalled();
});
it('expects hideAppendedDiv() to hide div', function () {
// make the call to the hideAppendedDiv function
testObj.hideAppendedDiv();
// Check behavior
expect(... check the div ...)
});
Edit
Just to be clear, Jasmine executes all the expects in order. Now, if you have two functions fn_1(), and fn_2() and you want to test that they were called in order you can setup yet another spi function that returns a specific value, or a sequential and incremental set of values every time it is called.
beforeEach(function () {
spyOn(testObj , 'fn_1').and.returnValues(1, 2, 3);
spyOn(testObj , 'fn_2').and.returnValues(4, 5, 6);
});
The first time fn_1 is called it will return 1, respectively fn_2 will return 4.
That is just one of the ways, but you have to get creative when testing.
Now if you want to test that a function was called after x amount of time here is a post that already explains it.
You don't need to test the window load event, if you move the append code out of the anonymous function call and pass it into the event handler instead you can test the functionality in exactly the same way you would anything else and your code will be better structured.

Jasmine testing methods inside .done or .then

Wondering if anyone can help me - I'm trying to test my js using Jasmine (1.3) and I can't figure out the best way to test any method calls inside a .then or a .done method.
example code to explain:
Backbone.View.extend({
myMethod: function () {
this.something.done(function () {
this.doSomethingElse();
}.bind(this));
}
})
I'd like to write a test that check that this.doSomethingElse was called.
I was looking around at jasmine.async and a waitsFor/runs set up but I'm not sure how it fits into external code i.e. I'm not going to call done() inside my actual code to get my test working. Also if I mock out the done method on this.something then I'm not longer testing the actual implementation, right?
I'm just missing how things fit together. If anyone could point me in the right direction I'd really appreciate it!
Update: based on feedback below I've now tried the following
Hey, thanks for the answer - I think maybe I don't have the last part correct - have tried 2 different ways, both initial pass but then fail after a second or 2.
it('calls doSomethingElse on done',function () {
var mockDeferred = $.Deferred();
myView.something = mockDeferred;
spyOn(myView,'doSomethingElse');
mockDeferred.resolve();
waitsFor(function () {
expect(myView.doSomethingElse).toHaveBeenCalled();
});
});
And also:
it('calls doSomethingElse on done',function () {
var mockDeferred = $.Deferred(),
someTrigger = false;
myView.something = mockDeferred;
spyOn(myView,'doSomethingElse');
runs(function () {
mockDeferred.resolve();
someTrigger = true;
});
waitsFor(function () {
someTrigger = true;
});
runs(function () {
expect(myView.doSomethingElse).toHaveBeenCalled();
});
});
In both instances the test will pass originally but then timeout to a failure after a second or 2.
Am I missing something?
To test the example function you described, I would do the following within your test:
Create a new deferred object (I'll call it mockDeferred)
Pass mockDeferred into your code under test so that it is now this.something in your example
Spy on the doSomethingElse function
Call myMethod()
Call resolve() on mockDeferred
Assert that doSomethingElse was called
Edit based on OP's Update:
I don't see anywhere in either of your examples where you are calling myView.myMethod() within your test; make sure you do that. I whipped up an example that you can reference here.
As an aside, I'm surprised the second example you tried passes initially. Maybe because you have some code outside of a runs() block?
Related problem
Spying on a method inside .then and expecting .toHaveBeenCalled fails
Solution:
run test inside fakeAsync and run tick() before the expect
Service:
getFirebaseDoc() {
this.db.firestore.doc('some-doc').get()
.then(this.getFirebaseDocThen)
.catch(this.getFirebaseDocCatch);
}
Unit testing:
it('should call getFirebaseDocThen', fakeAsync(() => { // note `fakeAsync`
spyOn(service, 'getFirebaseDocThen');
spyOn(service.db.firestore, 'doc').and.returnValue({
get: (): any => {
return new Promise((resolve: any, reject: any): any => {
return resolve({ exists: true });
});
},
});
service.getFirebaseDoc();
tick(); // note `tick()`
expect(service.getFirebaseDocThen).toHaveBeenCalled();
}));

Testing if a Jasmine Test Fails

I'm trying to write a plugin for Jasmine that allows you to return a promise from a spec and will pass or fail that spec depending on whether or not the promise is fulfilled or rejected.
Of course, I want to write tests to make sure that my plugin works correctly, and to be thorough, I need to make sure that tests fail when the promise is rejected... so how do I make a test pass when I need to make sure that a test "successfully fails"?
After a conversation with the developers who work on Jasmine, we've come up with this:
var FAILED = 'failed'
var PASSED = 'passed'
describe('My Test Suite', function () {
var env
beforeEach(function () {
// Create a secondary Jasmine environment to run your sub-specs in
env = new jasmine.Env()
})
it('should work synchronously', function () {
var spec
// use the methods on `env` rather than the global ones for sub-specs
// (describe, it, expect, beforeEach, etc)
env.describe('faux suite', function () {
spec = env.it('faux test', function (done) {
env.expect(true).toBe(true)
})
})
// this will fire off the specs in the secondary environment
env.execute()
// put your expectations here if the sub-spec is synchronous
// `spec.result` has the status information we need
expect(spec.result.status).toBe(FAILED)
})
// don't forget the `done` argument for asynchronous specs
it('should work asynchronously', function (done) {
var spec
// use the methods on `env` rather than the global ones.
env.describe('faux suite', function () {
// `it` returns a spec object that we can use later
spec = env.it('faux test', function (done) {
Promise.reject("FAIL").then(done)
})
})
// this allows us to run code after we know the spec has finished
env.addReporter({jasmineDone: function() {
// put your expectations in here if the sub-spec is asynchronous
// `spec.result` has the status information we need
expect(spec.result.status).toBe(FAILED)
// this is how Jasmine knows you've completed something asynchronous
// you need to add it as an argument to the main `it` call above
done()
}})
// this will fire off the specs in the secondary environment
env.execute()
})
})
Going off Joe's answer, I moved the fake test context into a single function. Since the code under test is making use of jasmine expectations, I load the inner Env into jasmine.currentEnv_ and call it explicitly with jasmine.currentEnv_.expect(). Note that currentEnv_ is an internal variable set by jasmine itself, so I can't guarantee that this won't be broken in a future jasmine version.
function internalTest(testFunc) {
var outerEnvironment = jasmine.currentEnv_;
var env = new jasmine.Env();
jasmine.currentEnv_ = env;
var spec;
env.describe("fake suite", function () {
spec = env.it("fake test", function () {
func();
});
});
env.execute();
jasmine.currentEnv_ = outerEnvironment;
return spec.result;
}
Then each test looks like
it("does something", function () {
//Arrange
//Act
var result = internalTest(function () {
//Perform action
});
//Assert
expect(result.status).toBe("failed"); //Or "success"
expect(result.failedExpectations.length).toBe(1);
expect(result.failedExpectations[0].message).toBe("My expected error message");
});

Categories

Resources