Using angular functions ($q) in protractor - angularjs

I want to use the $q service of angular in my e2e tests. (I want to get the texts of a bunch of elements via getText() which returns a promise. After all promises are resolved, I want to test the list. So I want to use $q.all() etc.)
angular.injector(['myApp']).get('$q'); results in "ReferenceError: angular is not defined"
Installing angular via node and then var angular = require("angularjs"); results in "Error: Cannot find module 'angular'"
Also, inserting a browser.waitForAngular() does not help there.
Using the inject(function($q) {}) syntax has the same problem.
How can I use such angular functions in protractor?
edit:
Here's the very naive version of what I want to achieve
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
var title = container.element(by.tagName('h2'));
title.getText().then(function (text) {
var key = getSomeKey();
var entry = processEntry(text);
entries[key] = entry;
});
});
return entries;
};
That works in principle, at some point in time entries contains all data. However, I need to wait for that moment. What I would do is create and return a promise that gets resolved as soon as all getText promises are resolved.
e.g.
var deferred = $q.defer();
$q.all(getTextPromises).then(function () {
deferred.resolve(entries);
});
return deferred.promise;

From the looks of your code containers is a list of elementFinders? (i.e. var containers = [element(by.x), element(by.y), element(by.z)]):
Using q: (you need to add q as dependency in package.json first)
var q = require('q');
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
var deferred = q.defer();
var title = container.element(by.tagName('h2'));
title.getText().then(function (text) {
deferred.resolve(processEntry(text));
});
entries[getSomeKey()] = deferred.promise();
});
return q.all(entries);
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})
But protractor knows promise itself (and it's preferably that you don't mix protractor's promise with q promise unless you know what you're doing):
var collectEntries = function(containers) {
var entries = {};
containers.each(function (container) {
entries[getSomeKey()] = container.element(by.tagName('h2')).
getText().then(function (text) {
return processEntry(text);
});
});
return protractor.promise.fullyResolved(entries);
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})
If your containers are found using a single selector (i.e. var containers = element.all(by.xyz)), it's even easier:
var collectEntries = function(containers) {
return containers.reduce(function(entries, elem) {
return elem.getText().then(function(text) {
entries[getSomeKey()] = processEntry(text);
return entries;
});
}, {});
};
expect(collectEntries).toBe({key1: 'title1', key2: 'title2'})

Related

Angular 1.x and AG-Grid Components

I'm trying to upgrade our app from a really old version to the latest (v16). Since they have deprecated the old way of cell and header renders I'm trying to wrap my head around using scope and Angular compilation.
The ag-grid documentation states: You will then need to manage creating and destroying child scopes yourself inside the init() and destroy() methods.
angularCompileRows, angularCompileFilters and angularCompileHeaders are not supported within Components.
I tried to create a cell render like this:
function MyCellRenderer() {
}
MyCellRenderer.prototype.init = function (params) {
$scope.myMessage = 'Hi Scott';
var compiled = $compile('<p ng-bind="myMessage"></p>')($scope);
this.eGui = document.createElement('span');
this.eGui.innerHTML = compiled.html();
};
MyCellRenderer.prototype.getGui = function () {
return this.eGui;
};
However this doesn't work. Any thoughts?
I was able to get this to work, but I don't like that I need to wrap around a $timeout. I had to do this because a digest was already in progress:
function MyCellRendererSimple() {
}
MyCellRendererSimple.prototype.init = function (params) {
console.log('init Called');
this.eGui = document.createElement('span');
};
MyCellRendererSimple.prototype.getGui = function () {
var self = this;
$timeout(function() {
var compiled = $compile('<nice-checkbox checkbox-id="checkbox-2" ng-model="checkboxModal1"></nice-checkbox>')($scope);
$scope.$digest();
self.eGui.appendChild(compiled[0]);
});
return self.eGui;
};

How to set a variable in an AngularJS callback?

I have an AngularJS 1.5 directive:
var assetSearchService = function(proService) {
var assets = [];
var searchAssets = function(searchTerm){
proService.searchAssets(searchTerm).then(function(data){
assets = data.data;
});
};
return {
searchAssets, searchAssets,
assets: assets
};
};
When I try to use assetSearchService.assets in my controller after calling search, the data is not set in assetService.assets.
If I log the data after the searchAssets promise returns, I am getting data.
this.assets does not work so how do I do get the variable back from the callback?
Found an answer. Neither assets = newArray nor conact does not work because both return a new array and break the reference, rather than modifying the current one.
This works:
Array.prototype.push.apply(assets, data.data);
Here's a working example: https://jsfiddle.net/mbaranski/5k4bqo0z/
You can achieve more complex solution
var assetSearchService = function(proService) {
service = this;
service.assets = [];
service.searchAssets = function(searchTerm){
proService.searchAssets(searchTerm).then(function(data){
assets.push(data.data);
});
};
return service;
};

angularjs binding/scope issue for select list?

OK switching my code to angularjs and the angular 'way', not sure what I am doing wrong.
A select list is not getting updated when the model changes unless I call $apply, and I find myself calling apply a lot.
index.html has this:
<div id='rightcol' data-ng-include="'partials/rightSidebar.html'"
data-ng-controller="rightSidebarController">
</div>
and rightSidebar.html has this:
<select id='srcList' size='10'
data-ng-model="data.source"
data-ng-click='srcOnclick()'
data-ng-options="s.title for s in data.srcList | filter:{title:data.srcFilter} | orderBy:'title'"></select>
rightSidebarController.js has this:
$scope.data = {};
$scope.data.srcList = dataProvider.getSourceList();
$scope.data.source = dataProvider.getSource();
dataProvider is a service that makes an asynchronous database call (IndexedDB) to populate srcList, which is what gets returned in dataProvider.getSource().
Is it the asynchronous database call that forces me to call $apply, or should the controller be ignorant of that?
Is there a 'better' way to do this?
Edited to add service code.
Another controller calls dataProvider.refreshSourceList:
myDB.refreshSourceList = function() {
myDB.getRecords("source", function(recs) {
myDB.srcList = recs;
$rootScope.$broadcast('SrcListRefresh');
});
};
myDB.srcList is the field being bound by $scope.data.srcList = dataProvider.getSourceList();
myDB.getRecords:
myDB.getRecords = function(storeName, callback) {
var db = myDB.db;
var recList = [];
var trans = db.transaction([storeName], 'readonly');
var store = trans.objectStore(storeName);
var cursorRequest = store.openCursor();
cursorRequest.onerror = myDB.onerror;
cursorRequest.onsuccess = function(e) {
var cursor = cursorRequest.result || e.result;
if (cursor === false || cursor === undefined) {
if (callback !== undefined) {
$rootScope.$apply(function() {
callback(recList);
});
}
} else if (cursor.value !== null) {
recList.push(cursor.value);
cursor.continue();
}
};
cursorRequest.onerror = myDB.onerror;
};
Anything you do async needs to be wrapped in $scope.$apply(). This is because angular works in a similar fashion to a game loop, however instead of constantly running, it knows to end the loop when an action is taken, and $scope.$digest() is called.
If you are using IndexedDB, I would recommend creating an angular wrapper for it, like so:
(forgive my IndexedDB code, I'm not experience with it)
angular.module('app',[])
.factory('appdb', function($rootScope){
var db = indexedDB.open('appdb', 3);
return {
get : function(table, query, callback) {
var req = db.transaction([table])
.objectStore(table)
.get(query);
req.onsuccess(function(){
$rootScope.$apply(function(){
callback(req.result);
});
});
}
};
});
This way you can be sure that any data retrieve and set on a controller scope inside of callback will have $scope.$digest() called afterward.

Can't get waiting for function to return (with promises?) working in angular controller

I'm trying to get the following findTimelineEntries function inside an Angular controller executing after saveInterview finishes:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId}, function() {
$scope.findTimelineEntries();
});
};
The save action adds or edits data that also is part of the timeline entries and therefore I want the updated timeline entries to be shown.
First I tried changing it to this:
$scope.saveInterview = function() {
var functionReturned = $scope.interviewForm.$save({employeeId: $scope.employeeId});
if (functionReturned) {
$scope.findTimelineEntries();
}
};
Later to this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
$scope.saveInterview.done(function(result) {
$scope.findTimelineEntries();
});
And finaly I found some info about promises so I tried this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
var promise = $scope.saveInterview();
promise.done(function() {
$scope.findTimelineEntries();
});
But somehow the fact that it does work this way according to http://nurkiewicz.blogspot.nl/2013/03/promises-and-deferred-objects-in-jquery.html, doesn't mean that I can use the same method on those $scope.someFuntcion = function() functions :-S
Here is a sample using promises. First you'll need to include $q to your controller.
$scope.saveInterview = function() {
var d = $q.defer();
// do something that probably has a callback.
$scope.interviewForm.$save({employeeId: $scope.employeeId}).then(function(data) {
d.resolve(data); // assuming data is something you want to return. It could be true or anything you want.
});
return d.promise;
}

How to instantiate Ext views for testing in jasmine

I want to test Views in Ext4, but
Application.getView
returns constructor function, and I run out of ideas how to make it work.
describe("general", function() {
it("should have fields enabled by default", function(){
var view = Application.getView('BRM.view.business_rule.form_part.General'),
viewInstance = new view(),
items = viewInstance.items
;
_.each(items, function(item){
console.log(item);
debugger;
})
})
})
You have to use the create method:
var viewInstance = Application.getView('BRM.view.business_rule.form_part.General').create();
var items = viewInstance.items;
Alternatively you can also use Ext.create:
Ext.create('BRM.view.business_rule.form_part.General')

Resources