Protractor console log - angularjs

I want to output the text of a div in my protractor test, so far I have:
console.log(ptor.findElement(protractor.By.id('view-container')).getText());
but this outputs
[object Object]
I tried "toString()" and same result.
Is there a way to output the text to the console?

getText and most other Protractor methods return promises. You want to put your console.log statement inside the promise resolution:
Using the new Protractor syntax:
element(by.id('view-container')).getText().then(function(text) {
console.log(text);
});

this is pretty old, but as a former n00b at protractor, I wished there was more documentation.
you could also use:
element(by.id('view-container')).getText().then(console.log);
or what I like to do for readability is put all the objects on a page in their own function, section, or file:
//top declaration of variables
var viewContainer = element(by.id('view-container')).getText();
.... //bunch of code
....
viewContainer.then(console.log);
That will take care of most of your garden-variety debugging needs.
For promises in general, you could try using protractor.promise.all()
let's say you have two things that are both promises:
var getTime = element(by.xpath(theTimeXpath)).getText();
var getPageTitle = element(by.xpath(thePageTitle)).getInnerHtml();
protractor.promise.all([getTime, getPageTitle]).then(function(theResultArray){
var timeText = result[0];
var pageTitleInnerHtml = result[1];
console.log(timeText); // outputs the actual text
console.log(pageTitleInnerHtml); //outputs the text of the Inner html
});
This second method is useful for when things begin to get more complex. personally, however, I find other ways around this. Although it's not bad, it's kind of funky for other developers having to read my code.

I would like to suggest a small improvement to other answers.
short answer : I like to use browser.sleep(0).then(..); where I need to push something to protractor's flow.
it is generic and easy to move around.
tl;dr
so using the above, you can easily add a function on browser (or ptor) something like:
browser.log = function( logger, level, msg ){
browser.sleep(0).then(function(){ logger[level](msg); });
}
or something a bit more sophisticated with apply - but that depends on your logger.
you can obviously enhance that a bit to have logger like api
var logger = browser.getLogger('name');
should be implemented like (lets assume log4js)
browser.getLogger = function( name ){
var logger = require('log4js').getLogger(name);
function logMe( level ) {
return function(msg ){
browser.sleep(0).then(function(){ logger[level](msg); });
}
}
return { info : logMe('info'), ... }
}
basically, the sky is the limit.
I am sure there's a way to make my code a lot shorter, the point is using the sleep method as basis.

You could always assert that the text you get is the text you expect:
expect(element(by.id('view-container')).getText()).toBe('desired-text');

you can try this one:
const textInfo = element(by.id('view-container'));
console.log('text: ', textInfo.getText());

When user wants to log the expected and actual result in protractor always use then method implementation.
verifyDisplayedText(locator: Locator, expectedText: string) {
const text = this.getText(locator);
try {
text.then(function(value){
if (value.trim() === expectedText) {
verifyLog("VERIFICATION: PASSED. Expected: '" + expectedText + "' Actual: '" + value+"'");
} else {
errorLog("VERIFICATION: FAILED. Expected: '" + expectedText + "' Actual: '" + value+"'");
}
});
expect(text).toBe(expectedText);
}catch (error1) {
errorLog("VERIFICATION: FAILED. Expected: '" + expectedText + "' Actual: '" + text+"'");
throw error1;
}
}

If you're in 2021, you will want to read this answer
According to protractors documentation, .getText() returns a promise. Promise is an Object is javascript. So this is what you're logging.
You need to get the value off the promise, by resolving it
The best way to handle a promise, as of 2021, is to use async/await keywords. This will make Protractor 'freeze' and wait, until the promise is resolved before running the next command
it('test case 1', async () => {
let text = await ptor.findElement(protractor.By.id('view-container')).getText();
console.log(text);
// or directly
console.log(await ptor.findElement(protractor.By.id('view-container')).getText(););
})
.then() can also be used, but using async/await will make your code a lot more readable and easier to debug.

Related

Protractor - Unable to access element due to fixed Top navigation bar

I'm facing the following issue in protractor with jasmine
Click/mouse hover not working because of fixed top navigation bar in my application. I need to click/perform mouse hover on a web page.
Unfortunately that element is displaying behind that fixed navigation bar. So scroll till element present & click by x & y coordinates are not working.
My dependencies are :
protractor version 5.2.2
node 8.9.3
selenium standalone 3.13
chrome driver-2.40
chromebrowser v67
OS- Windows 10
Thanks in advance
Try using prototype executeScript
Just try clicking that element from the browser console using id,name or xpath.
For example :
var el = element(by.module('header'));
var tag = browser.executeScript('return arguments[0].click()', el).then(function() {
expect(something).toMatch(something);
});
Another way, along the same lines as what Bharath Kumar S and knowing JeffC's caveat that this approach is cheating, I had a similar issue where the App-Header kept getting in my way of clicking, and I knew I was willing to never need it (so, for instance, to find other ways to navigate or log out and not check for stuff that was on it). I, therefore, did the following, which solved the problem. Note if you refresh the screen, you have to call it again. Also note I am using a number of functions from https://github.com/hetznercloud/protractor-test-helper, which do what you would expect from their names.
var removeAppHeaderIfAny = async function() {
//this function hides the app header
//it is useful to avoid having covers there when Protractor worries that something else will get the click
let found = false;
try {
found = await waitToBeDisplayed(by.className("app-header"), 2000);
} catch (e) {
let s: string = "" + e;
if (s.search("TimeoutError") != 0) flowLog("presumably fine, cover already removed: " + e);
found = false;
}
if (!found) return;
if (found) {
let coverElement = await element(by.className("app-header"));
browser.executeScript(
"arguments[0].style.visibility='hidden';",
coverElement
);
await waitToBeNotDisplayed(by.className("app-header"), 10000);
}
return;
//note after this is called you will not see the item, so you cannot click it
};
As I look at the code, it strikes me one can probably remove the if (found) and associated brackets at the end. But I pasted in something I know has been working, so I am not messing with that.
As indicated up front, I knew I was willing to forego use of the app-header, and it is a bit crude.

Why/how do the elements of this array change between the console.log calls?

I have an anuglarJS controller that calls an API via a service to return data. Ths issue is that sometimes, the data is not being updated in a directive that uses the data that is returned.
However, digging into this resulted in observing some very strange behavior. I added several console logs to debug what was happening, and discovered that the number of items in a property on the array is changing from one console call to the next.
The controller code is as follows:
init(){
this.ftService.getSitePromise(true).then((result: ng.IHttpPromiseCallbackArg<Site>) => {
let ctrl = this;
ctrl.isLoadingItems = true;
ctrl.hideSplash = true;
ctrl.siteReady = true;
ctrl.curSite = result.data;
ctrl.curSite.Items = [];
console.log("end of header site call");
ctrl.$timeout(function () {
console.log(ctrl.curSite.Items);
console.log("start get site items first call")
ctrl.ftService.getSitePromise(false).then((result: ng.IHttpPromiseCallbackArg<Site>) => {
console.log("return first call result.data.Items: " + result.data.Items.length);
ctrl.curSite.Items = result.data.Items;
ctrl.isLoadingItems = false;
console.log("return first call ctrl.curSite.Items: " + ctrl.curSite.Items.length);
console.log(ctrl);
console.log(ctrl.curSite);
console.log(ctrl.curSite.Items);
});
}, 200);
});
}
The console from this code executing, when the data isn't being shown as expected is as follows:
Any insight as to how this is occurring, and/or how I might correct it, would be greatly appreciated.
Edit: I didn't read the comments before posting. I didn't see your problem was solved. Hopefully this may help someone else in the future??
Why/how do the elements of this array change between the console.log calls?
Objects can change in console.log calls because the deeper nested properties are accessed lazily meaning that the console will only grab them when you click the little arrow to expand the object or if they are a shallow property.
You can change this behavior by cloning the object using Object.assign though you may need to clone the object deeply (which Object.assign({}, myObj) does not.
The stackoverflow snippet console won't show the correct results. Open your chrome dev tools to see the real result.
// OPEN THE DEVELOPER TOOLS CONSOLE
let myObj = {
shallowProp: 'some value',
arr: ['initial value']
};
// notice that when this logs, it seems like the change to myObj happens before the log but it does not
console.log(
'myObj initial log',
myObj
);
// using `Object.assign` clones the object so that when expanded in the console, the value is the initial value
console.log(
'myObj initial log with Object.assign',
Object.assign({}, myObj)
);
// when the value is actually changed
myObj.arr = ['new value'];
// the final state
console.log('myObj after change', myObj);
Conclusion: try cloning your object before logging it the console.

Adding a promise to browser.driver.wait not working in protractor

I am using node-resemble image comparison library to compare two images in a protractor test. Node-resemble returns a promise which resolves with comparison data. I want protractor to wait till the results are available.
I have tried using browser.driver.wait to achieve this, but somehow it is not working. Here's the code I am using for returning a promise to browser.driver.wait:
var compareImage = function(path1, path2) {
var deferred = protractor.promise.defer();
var img1 = fs.readFileSync(path1);
var img3 = fs.readFileSync(path2);
resemble(img1).compareTo(img3).onComplete(function(data) {
console.log(data);
deferred.fulfill(data);
});
return deferred.promise;
};
And this is how I am using it:
var prom = compareImage(path1, path2);
browser.driver.wait(prom, 3000).then(function(data) {
console.log(data);
expect(data.misMatchPercentage).toBe('0.00');
});
I have read the documentation and have tried to do it the way it is described, but somehow it is not working. None of the two console.log are logging the data to console. Here is what the documentation says:
Example: Suppose you have a function, startTestServer, that returns a promise for when a server is ready for requests. You can
block a WebDriver client on this promise with: var started =
startTestServer(); driver.wait(started, 5 * 1000, 'Server should start
within 5 seconds'); driver.get(getServerUrl());
It seems like there is something I am missing out. Any help is appreciated!
You have used browser.driver.wait.
Instead use browser.wait and see how it works

Angularjs Passing array between controllers

I have been through several tutorials and posts about this topic and still can't seem to figure out what is wrong with my code. To me it seems I am having scoping issues with the data within my service. My code is split up into separate files. Here is my code:
github link : https://github.com/StudentJoeyJMStudios/PetPinterest.git
//in dataService.js
var app = angular.module('se165PetPinterestWebApp');
app.service('SharedData', function ()
{
var categoryOfSelectedAnimals = [];
this.setCatOfSelAnimals = function(pulledCategoriesFromParse)
{
categoryOfSelectedAnimals = pulledCategoriesFromParse;
console.log('after assignment in set::' + categoryOfSelectedAnimals);
};
this.getCatOfSelAnimals = function()
{
console.log('in get::::' + categoryOfSelectedAnimals);
return categoryOfSelectedAnimals;
};
});
in my first controller to set the data in signup.js
app.controller('SignupCtrl',['$scope', 'SharedData', function ($scope, SharedData)
{
var Categories = Parse.Object.extend('Categories');
var query = new Parse.Query(Categories);
query.find({
success: function(results)
{
$scope.availableCategoriesOfAnimals = results;
SharedData.setCatOfSelAnimals(results);
},
error: function(error)
{
alert('Error: ' + error.code + ' ' + error.message);
}
});
};
}]);
Then in my other controller trying to get the data from the array within my service:
var app = angular.module('se165PetPinterestWebApp');
app.controller('CatSelCtrl', function ($scope, SharedData)
{
$scope.availableCategoriesOfAnimals = SharedData.getCatOfSelAnimals();
});
When I print the contents from the SharedData.getCatOfSelAnimals I get 0 every time. Please help. Thank you very much in advance.
EDIT: After playing around with a string a bit I am finding the changed string in the set function is not saved into the service and when I call my get function within my service the string is not changed from the set method. Please help, thank you in advance.
EDIT: So it looks like when I navigate to new page by using window.location.href = '../views/categorySelection.html'; in my signup.js it reloads my dataService.js which re-sets my variables back to nothing. Does anyone have any ideas as to how to fix this?
Edit
First: why you lose data
You need to setup routing properly. Right now you are not changing views but rather using window.location.href to load a new bootstrap file (dashboard.html), i.e. everything saved in memory will be lost. So you have been doing it right, sort of, but the moment you change to dashboard.html all data from Parse is lost.
You can solve this by configuring routes and use $location.url() to change URL. Read more about angular.route here: https://docs.angularjs.org/api/ngRoute/service/$route
The angular way
After looking at your code and running your app I think we need to take a step back. Angular is tricky to get used to but there is a lot of good tutorials. I think you might wanna read some of them to get a better grasp of how it works and how to setup and build your app.
Start here: http://www.airpair.com/angularjs
Boilerplate
Found this boilerplate for an Angular app using Parse. It might be something you could use. https://github.com/brandid/parse-angular-demo
Original
Or an even quicker way to empty $scope.availableCategoriesOfAnimals and then merge new data without breaking reference:
$scope.availableCategoriesOfAnimals.length = 0;
Array.prototype.push.apply($scope.availableCategoriesOfAnimals, pulledCategoriesFromParse);
You are breaking the reference on assignment. This is a JavaScript issue, not an angular one for that matter ;)
Try this in your set function:
categoryOfSelectedAnimals.length=0;
pulledCategoriesFromParse.forEach(function (e) {categoryOfSelectedAnimals.push(e)});
in stead of reassigning
edit: angular extend works on objects, not arrays, so replaced it with a bit of JS.

How can I let Angular show an error on a Angular error?

I've taken over an Angular application from another developer.
Now I've been playing around with it and making my first edits.
Question is: when I bind to non-existing elements (or make any other mistake) I don't see any error, which sounds good but isn't because I want to be notified when I do something wrong.
How can I make Angular show errors?
To began, I recommend not using the minified version of angular, as the unminified version allows more coherent and clear errors to be logged in the console.
Next, I think the best way to handle angular errors is to write a custom wrapper to better handle them. Here is an example of how you could write a wrapper.
The first step would be to write a function that will handle the error in a way that you want. This is how I current handle angular errors. Note: this could be modified in many different ways to make the error handling more customized.
function HandleAngularError(Exception, AppName){
try {
var AppName = (window.parent._.isEmpty(AppName) ? "Angular App Unspecified" : AppName) + " - ";
if (window.parent._.isUndefined(Exception)) {
console.log(strAppName + "error: exception undefined", "AngularJs");
} else {
console.log(strAppName + "error: " + Exception.toString() + " " + JSON.stringify(Exception), "AngularJs");
}
} catch (e) {
alert("Handle Angular Error: " + Exception.toString() + " " + JSON.stringify(Exception));
}
}
The next step is to include the error handling function in the any of the Modules in you project and rely on the $exceptionHandler to then pass angular errors into your custom wrapper like so:
angular.module("someApp",[], function(){
//setup stuff here
}).factory( '$exceptionHandler', function () {
return function (exception) {
HandleAngularError(exception, "someApp");
};
});
By default, AngularJS is forgiving, that is to say it prefers to show nothing rather than throwing an exception if you binded an undefined value.
From Angular doc :
In JavaScript, trying to evaluate undefined
properties generates ReferenceError or TypeError. In Angular,
expression evaluation is forgiving to undefined and null.
One way of displaying an error would be to decorate the $eval function, which is used to evaluate binded expressions.
You could try something like this :
app.run(function($rootScope) {
var origRootScope = $rootScope,
origEval = origProvider.$eval;
//Override rootScope's $eval with our own
origProvider.$eval = function(expression, locals) {
// Test the expression value and add the behavior you like
if(typeof expression === 'undefined') {
// Log some kind of error
console.log('Some kind of error')
}
// Call the original $eval function
var returnValue = origEval.apply(this, arguments);
return returnValue;
}
});
I haven't tried that code but you should be able to add custom logging to $eval this way.

Resources