Waiting for Ionic Loading dialogs with Protractor - protractor

There are similar questions (linked below) but none solves this problem. I'm writing Protractor tests for an Ionic Project. I need to execute tests at times when an Ionic Loading dialog appears and disappears.
I've created a repo with the bare bones of the app and the tests that need to be made. Solve this and you solve the problem (I describe the problem below): https://github.com/TmanTman/StackoverflowQ. Just adapt the path to your Chrome for your system in conf.js.
To simulate an asynchronous Ionic Loading dialog I just add this to the controller in a blank Ionic project:
$interval( function() {
$ionicLoading.show({
template: 'Async ionicLoading',
duration: 5000
});
}, 5000 , 1);
})
I need to get protractor to wait for the dialog to appear, do some tests, wait for the dialog to disappear, and then do some more tests. My latest attempt in my test file is:
it('should only test when ionicLoading appears', function() {
browser.wait(function(){
return element(by.css('.loading-container.visible.active')).isPresent();
}, 10000);
var ionicLoadingText = element(by.css('.loading-container.visible.active')).getText();
expect(ionicLoadingText).toEqual('Async IonicLoading');
})
it('should only test once ionicLoading disappears', function() {
browser.wait(function() {
var deferred = protractor.promise.defer();
var q = element(by.css('.loading-container.visible.active')).isPresent()
q.then( function (isPresent) {
deferred.fulfill(!isPresent);
});
return deferred.promise;
});
expect(1).toEqual(1);
})
I'm trying to avoid using synchronous sleep function, as my code is highly asynchronous. I've tried countless variations but I can't get it to work. Links I've used for info includes:
Protractor blocking wait
Asynchronous Testing with Protractor's ControlFlow
Protractor wait for isElementPresent and click on waited element fails

The problem is two-fold:
1) From what I can deduce, the duration property of the $ionicLoading method is implemented with a timeout function. Protractor does not work well with $timeout. So instead of using the duration property, the $ionicLoading dialog can be hidden with a $interval call (adapting the code from the question):
$interval( function() {
$ionicLoading.show({
template: 'Async IonicLoading'
});
$interval( function() {
$ionicLoading.hide();
}, 5000, 1)
}, 5000 , 1);
2) The code to detect the asynchronous change is the following:
it('should only test when ionicLoading appears', function() {
browser.wait(function() {
var deferred = protractor.promise.defer();
var q = element(by.css('.loading-container.visible.active')).isPresent()
q.then( function (isPresent) {
deferred.fulfill(isPresent);
});
return deferred.promise;
}, 10000);
var ionicLoadingText = element(by.css('.loading-container.visible.active')).getText();
expect(ionicLoadingText).toEqual('Async IonicLoading');
})
it('should only test once ionicLoading disappears', function() {
browser.wait(function() {
var deferred = protractor.promise.defer();
var q = element(by.css('.loading-container.visible.active')).isPresent()
q.then( function (isPresent) {
deferred.fulfill(!isPresent);
});
return deferred.promise;
}, 10000);
expect(1).toEqual(1);
})
Then both tests pass.

Related

Protractor : In a particular page, none of the protractor actions are working

My protractor script is working fine until a page where reveal.js package is used. I am not sure if that is the reason it causes the scripts to fail, but otherwise the code base is same as the other pages where my scripts works fine.
Note: I tried most of the protractor actions (click, highlight, waitForElement, toContain, etc), none of them worked. I could only click the links by inserting jQuery in my script.
CODE:
let HighlightElement = function (el) {
return browser.executeScript("arguments[0].setAttribute('style', arguments[1]);", el.getWebElement(), "color: Red; border: 1px solid red;").
then(function (resp) {
browser.sleep(2000);
return el;
}, function (err) { });
}
let waitUntilElementPresent = function (visibilityOfObject, maxWaitTime) {
var EC = protractor.ExpectedConditions;
browser.manage().timeouts().implicitlyWait(2);
browser.wait(function () {
browser.manage().timeouts().implicitlyWait(3);
return visibilityOfObject.isDisplayed()
.then(
function (isDisplayed) {
browser.wait(EC.visibilityOf(visibilityOfObject), maxWaitTime, "Element taking more time to load");
browser.manage().timeouts().implicitlyWait(3);
return isDisplayed;
},
function (error) {
return false;
});
}, 100000);
}
ACTUAL CODE:
var homepage = new homePageObj();
utilities.waitUntilElementPresent(homepage.waitScreenText); //here the script is failing. It is just a simple script and it used to work in other pages but it doesn’t work only in some of the pages
utilities.HighlightElement(homepage.waitScreenText);
utilities.HighlightElement(homepage.startButton);
homepage.startButton.click();
Error:
Failed: Wait timed out after 120062ms
Using below jQuery I am able to click, but we need to give wait time explicitly for each and every java script that we use, which is time consuming. I am beginner in automation, Kindly help me with a solution.
browser.executeScript("document.getElementsByClassName('cc-button')[0].click()");
My code started running after proving "browser.waitForAngularEnabled(false)" at the start of the script.

In protractor, I want the code to handle based on if OTP triggers and if not, I can login to the home page or any page and cont do the work

I am new to coding and as well as to protractor.
In protractor, I want the code to handle based on if OTP triggers go and retrieve OTP and if not, login to the home page or any page and continue to do the actions in the home page. I was trying to do an if else check with
I tried as like below
browser.getcurrentUrl().toEqual().then function()
{
statements;
},
I don't think it works. Can someone help?
below is my code snippet.
Basically i was trying to check the url, if it contains specific texts in it, I dont want anything to perform further execution want to exit out of execution. If the url doesnt contain anything specified I want to proceed with further execution.
The if condition is working fine. but not the else part.
var HomePages = require('../Pages/HomePage.js');
var EC = protractor.ExpectedConditions;
describe(‘Check_url function’, function() {
browser.wait(EC.urlContains(’some url’),2000).then(result => {
if (result) {
console.log('Sorry!!!!!!!, Encountered PassCode Authentication Process.
Execution cant be proceed further');
} else {
HomePages.profile();
browser.driver.sleep(300);
}
});
});
//////////////////////////
HomePages.js -
'use strict';
module.exports = {
Homepage: {
usrname: element(by.className('profile-name')),
usricon: element(by.css('[title="profile"]')),
Cli_id: element(by.css('[title=“Client ID"]'))
},
profile: function() {
this.click_Profile();
},
click_Profile: function() {
var angular3 = this.Homepage;
angular3.usricon.click();
},

Screenshot is not attached to report but available in 'allure-results' folder or failure Specs screenshot

In Allure Reporters [with Protractor], The screenshot which is being taken is only at the end of the test. Due to which the screenshot which is showing in the Allure Reports are of login page only and not of the application once user logged in.
Here is the code, I am using to generate the Allure Reports.
allureReporterSetup: function() {
const AllureReporter = require('jasmine-allure-reporter');
jasmine.getEnv().addReporter(new AllureReporter({
resultsDir: 'allure-results'
}));
jasmine.getEnv().afterEach(function (done) {
browser.takeScreenshot().then(function (png) {
allure.createAttachment('Screenshot', function () {
return new Buffer(png, 'base64')
}, 'image/png')();
done();
})
});
},
This may help you
onPrepare() {
var originalAddExpectationResult = jasmine.Spec.prototype.addExpectationResult;
jasmine.Spec.prototype.addExpectationResult = function () {
if (!arguments[0]) {
browser.takeScreenshot().then(function (png) {
allure.createAttachment('Screenshot', function () {
return new Buffer(png, 'base64')
}, 'image/png')();
})
}
return originalAddExpectationResult.apply(this, arguments);
};
var AllureReporter = require('jasmine-allure-reporter');
jasmine.getEnv().addReporter(new AllureReporter({
resultsDir: 'allure-results',
}));
}
As I understand Protractor is a tool for testing Angular JS web sites. It's quite like the Selenium so it is not anyhow connected with your issue.
Your issue is connected with a testing framework that you use.
What happens now is your screenshot is probably taken at the end of a test (after scenario/after test) or even after test suite.
What you need is to take the screenshot right after test step or even right after a test failure. I guess after your test failure some teardown actions are performed. The thing is you need to take a screenshot before those actions. This way it will be taken in time.

Tracking url of external site launched in cordova-inappbrowser-plugin

I'm currently building an ionic app which is to be a wrapper for an external web application. What I want to do is to be able to track the url being redirected to when the user changes location in the external web app.
In my main controller I have the following code.
app.controller('MainCtrl', function ($rootScope) {
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
// Now safe to use the Codova API
var url = "https://external-site/";
var target = "_self";
var options = "location=no";
var ref = cordova.InAppBrowser.open(url, target, options);
ref.addEventListener('loadstart', function () {
console.log("loadstart");
});
}
});
When the page loads I don't get the event listener to fire or when the user changes locations in the external site. I have tried pointing the target to _system and _blank which makes no difference for me.
Can anybody help me?
Thanks in advance.
It's my experience that all the events not always fires on all platforms. Try subscribing to all the events and print some debug info. Then test on different devices (iOS, android) and see what events are fired.
$rootScope.$on('$cordovaInAppBrowser:loadstart', function(e, event){console.log('start')};
$rootScope.$on('$cordovaInAppBrowser:loadstop', function(e, event){console.log('stop')});
$rootScope.$on('$cordovaInAppBrowser:loaderror', function(e, event){console.log('err')});
$rootScope.$on('$cordovaInAppBrowser:exit', function(e, event){console.log('exit')});
btw: I'm using ngCordova here...
very strange.. all I did was update ionic, run 'ionic start test blank' add the plugin modify app.js to this
angular.module('starter', ['ionic'])
.run(function ($ionicPlatform) {
$ionicPlatform.ready(function () {
if (window.cordova && window.cordova.plugins.Keyboard) {
var inAppBrowserRef;
var target = "_self";
var options = "location=no";
inAppBrowserRef = cordova.InAppBrowser.open('https://onesignal.com/', target, options);
inAppBrowserRef.addEventListener('loadstart', function () { console.log('start') });
inAppBrowserRef.addEventListener('loadstop', function () { console.log('stop') });
inAppBrowserRef.addEventListener('loaderror', function () { console.log('err') });
}
});
})
and then run 'ionic run android' and all events fires perf.

How to get karma to clear the dom after each test?

The component I'm testing does some changes to the dom in an it() test, however it remains there in the next it() test, and this breaks my test. Is there a way to reset the DOM each it() test?
For none JQuery users:
afterEach(function () {
document.body.innerHTML = '';
});
Above removes all the <script> tags which are added by default, but that does not matter, because they have alreay been executed once (and Karma does never refresh the browser, at least not till all tests finish).
Hence you can also use beforeEach(...) (but I like to think of it as a dining table, you don't clean up before you start, you clean up after yourself).
I wrote a little DomCleaner that keeps track of events boud during your tests and cleans the body on cleanup (requireJS but you can change the code to your needs):
define(function () {
'use strict';
var installed = false,
documentAddListener,
documentListeners,
windowAddListener,
windowListeners;
return {
install: function () {
if (installed) {
throw new Error('Trying to install document cleaner, but its already installed!');
}
installed = true;
documentAddListener = document.addEventListener;
windowAddListener = window.addEventListener;
spyOn(document, 'addEventListener');
spyOn(window, 'addEventListener');
documentListeners = [];
windowListeners = [];
document.addEventListener.and.callFake(function () {
documentListeners.push(arguments);
documentAddListener.apply(null, arguments);
});
window.addEventListener.and.callFake(function () {
documentListeners.push(arguments);
windowAddListener.apply(null, arguments);
});
},
cleanup: function () {
if (!installed) {
throw new Error('Trying to cleanup document, but cleaner is not installed!');
}
installed = false;
documentListeners.forEach(function (listener) {
document.removeEventListener.apply(null, listener);
});
windowListeners.forEach(function (listener) {
window.removeEventListener.apply(null, listener);
});
documentListeners = [];
windowListeners = [];
document.body.innerHTML = '';
},
};
});
use it like this (in your first describe):
beforeEach(function () {
domCleaner.install();
});
afterEach(function () {
domCleaner.cleanup();
});
If using Jasmine - Use the beforeEach function to run a command before each test. In that function, you can clear the DOM. An example using jQuery:
describe("A test suite", function() {
beforeEach(function () {
$('body').empty();
});
});
I had to do this manually in each test method.
Just use DOM operations that remove stuff from the html using javascript.
stuff like
document.body.innerHTML = '';