finding it difficult to test AngularJS UI Grid with protractor - angularjs

I am pretty much new to protractor and testing AngularJS is my first time. I have to test a table which is populated with Angular grid. These values don't have any unique id's to identify them. After doing some research, i found we need to use gridTestUtils.spec.js. I have tried this in my spec file as below:
var gridTestUtils = require('./gridTestUtils.spec.js');
describe('get row count of grid', function(){
it('get the row count of grid1', function(){
gridTestUtils.expectRowCount('grid1',8);
});
});
This is the only spec file and there is no pageobject file. But this code doesn't seem to run. it says pageObject is not defined. Can you please let me know how to proceed from here and i am not an expert. please answer in simple and detailed manner as it will be easy for me to understand.
Thank you for your help.
Thanks,
Mallesh

gridTestUtils definitely feels like it may be overkill. Have you thought about testing the page with lower level calls to protractor directly? It's not a lot of code.
describe('get row count of grid', function () {
before(function () {
browser.get('http://localhost:9000/orwhatever');
});
it('get the row count of grid1', function(){
expect(element.all(by.repeater('row in rows')).count()).to.eventually.equal(8);
});
});
Replace the url with the url of your app, and replace the repeater string with the same string that is in ng-repeat in your DOM. If you're not using a repeater, just use the $$() function to grab all elements by css-selector instead.

Related

AngularFire - Get a firebaseObject for each item in a firebaseArray ng-repeat

I have an ng-repeat which is connected to a firebaseArray which is an index of a user's doc Ids.
I want to display a list of their docs with additional info for each one, e.g. it's title and description.
My firebase structure is:
+users
- userId1
-theirDocs
-docId1: true
-docId2: true
+docs
-docId1
- tile
- description
- url
-docId2
- etc etc
I've tried everything and still can't get this to work, it seems a pretty common use case. I've tried using a function that calls a firebaseObject each time. I've tried using a separate controller. I've tried a directive, I've even tried using firebase.utils library (I think out of date now).
Can anyone recommend the best way to do this using AngularFire? Thanks in advance!
It would be better to see a code snippet as to fully understand what you've tried first of all, as it's difficult to determine where the issues lie. That being said, as long as your rules are not preventing this, you may need to wait for the data to download using $loaded function beforehand.
EXAMPLE
This example is clearly available by following this link, yet here is an example for further clarity:
var yourFirebaseReference = firebase.database().ref("REPLACE WITH YOUR FIREBASE BRANCH HERE");
var data = $firebaseArray(yourFirebaseReference);
data.$loaded().then(function(x) {
console.log(data) // Example, you can do what you want with your 'data' now as the data has loaded
x === data; // true
}).catch(function(error) {
console.log("Error:", error);
});

how to force angular to re-bind/update even there is no change in the model

Ok -- so you might be thinking why would you want this but I am trying to render some HTML using ng-html-bind like so (in HAML):
#my-visualization-panel{'ng-bind-html' => 'htmlSource'}
the htmlSource has some html which renders a visualization using c3.js visualization library. The htmlSource looks something like this
<script>
var MY_DATA = localStorage.getItem('MY_DATA');
c3.generate({
data: {
columns: MY_DATA
}
});
</script>
So the problem is that I update the visualization by re-setting localStorage['MY_DATA']. However, while the data that MY_DATA refers to might change, the actual htmlSource does not, so the view fails to update.
Is there a way to force the view to update even if the model, ostensibly, does not?
you can use the apply method of the $scope object:
$scope.apply();
if you are still getting the digest in progress errors, you can also make use of the $timeout object that will run the function in the next digest cycle:
$timeout(function() {
//code
});
as per your latest comment in this answer it seems to be that you are looking for the $scope.watch method. You can add a watcher in order to listen when something changes.
I would recommend changing the HTML anyway. For example
<script>
var MY_DATA = localStorage.getItem('MY_DATA');// 2482486284968248968 (hash of my_data, guid, or serial number)
c3.generate({
data: {
columns: MY_DATA
}
});
</script>
This will allow your script to operate as you expect, directly, without having to do big sweeping updates or hacks. Even if you "force a refresh", if the HTML doesn't change, Angular will not re-execute your script.
Another possibility is to call localStorage.getItem('MY_DATA') in your angular controllers / directives, instead of indirectly hoping Angular will run it for you via HTML updates. That seems to be the kind of control you're looking for.
I have spent around 2-3 hours to resolve this issue. Finally I found solution
$scope.$apply();

AngularJS + Hoodie: How to do a correct data flow?

I would like to combine AngularJS and Hoodie, but I am not sure how to do it the correct way.
Let's say I have a basic AngularJS controller
app.controller("c", function($scope) {
$scope.table = [];
$scope.onAdd = function(newEntry) {
$scope.table.push(newEntry);
};
});
After clicking on e.g.
<button ng-click="onAdd('test');">add</button>
Everything works as expected. But now I want to use Hoodie. Currently I am trying this:
app.controller("c", function($scope) {
$scope.table = [];
$scope.onAdd = function(newEntry) {
$scope.table.push(newEntry);
};
hoodie.store.on('entries:add', $scope.onAdd);
});
and anywhere: hoodie.store.add("entries", tmpObject);
The onAdd() gets called and is inserted into the table array (verified in the developer console), but my html table (via ng-repeat="entry in table") is not updated.
First question: Do you know why? What do I think wrong here?
Second question: What would be a good way to combine Hoodie and AngularJS? Would it be better to $watch on the table and insert items into it and store it via Hoodie after getting a change or the other way around and add a new item to the hoodie.store and then on("entries:add") do some Angular stuff?
Thanks in advance!
Heyho =)
First: If you update data-structure via an 'external' event(in this case hoodie), you need to trigger the Digest-Cycle e.g. $rootScope.$apply();
Second: There is a hoodie-angular-plugin that is written by Elmar and me. That solved the most of your basic tasks. That would be the easiest way in this case.

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

beforeEach (to prevent test pollution) not working as expected

I've been doing the AngularJS tutorial and I came into a weird issue with beforeEach for one of the end to end tests. For context, I am currently in the Experiments section at step 3. The test code is here:
'use strict';
/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html');
});
it('should filter the phone list as user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
});
it('should display current filter value within an element with id "status"', function() {
var statusElement = element(by.id('status'));
expect(statusElement.getText()).toMatch(/Current filter:\s*$/);
element(by.model('query')).sendKeys('nexus');
expect(statusElement.getText()).toMatch(/Current filter: nexus\s*$/);
});
});
The beforeEach block should reload the page before each spec execution, but it seems it is not, since when I run this using protractor, the last text inserted into the query element in the first spec ('motorola') still exists when the second spec is executed, causing that second spec to fail.
Now, when I move the beforeEach to the outer describe block, all the specs pass successfully. Any ideas?
glepretre is correct. As structured in the above example your tests look like this
describe
describe
before
it
it
Because of this nesting the before runs only once, before the first it. Looking at what each it block is testing, I think they are both meant to be testing the Phone List View, in which case the correct nesting is as follows:
describe
describe
before
it
it
However, if the index page is not specific to the Phone List View but contains the whole app, then it should probably be loaded in the top-level describe block, making this the most correct approach:
describe
before
describe
it
it

Resources