Reputation: 4102
I have a hard time trying to test a modal controller (created using Angular UI Bootstrap). I dumbed down the test code as much as I could but I am still getting an error. Here's the modal controller (part of it):
var controllersModule = angular.module('forge.geomanagement.controllers');
controllersModule.controller('EditGeofenceModalController', function ($timeout, $scope: , $modalInstance, forgeGeoTriggerService, $rootScope, geofence, triggerID) {
var searchAddressInput: HTMLInputElement;
//make a copy of geofence obj passed into modal
$scope.geofence = {
FriendlyName: geofence.FriendlyName,
Coords: angular.copy(geofence.Boundary),
GeoTags: angular.copy(geofence.GeoTags)
};
$scope.goefenceID = triggerID;
var gCLength = $scope.geofence.Coords.length;
//wrap it in timeout function to paint the map after its container is rendered
$timeout(function () {
$scope.geofenceMap = new google.maps.Map(document.getElementById('map_canvas'), $scope.mapOptions);
//autocomplete functionality
searchAddressInput = <HTMLInputElement>document.getElementById('pac-input');
$scope.autocomplete = new google.maps.places.Autocomplete(searchAddressInput, $scope.mapOptions);
$scope.autocomplete.bindTo('bounds', $scope.geofenceMap); //set autocomplete suggestion bounds to map's current viewport
//bind autocomplete to the map
google.maps.event.addListener($scope.autocomplete, 'place_changed', function () {
$scope.place = $scope.autocomplete.getPlace();
$scope.geofenceMap.panTo($scope.place.geometry.location);
$scope.geofenceMap.setZoom(12);
$scope.model.searchAddress = $scope.place.formatted_address;
$scope.$digest();
});
//GEOFENCE FUNCTIONALITY
forgeGeoTriggerService.GeofenceCreator($scope.geofenceMap, $scope.geofence.Coords);
//show geofence in edit mode
forgeGeoTriggerService.ShowGeofence($scope.geofenceMap, $scope.geofence.Coords);
$scope.$on("polygonPath.updated", function (event, geofenceCoords) {
$scope.$apply(function () {
$scope.geofence.Coords = geofenceCoords;
});
});
//clear geofence area btn
$scope.clearGeofenceArea = function () {
forgeGeoTriggerService.ClearGeofenceArea();
$scope.geofence.Coords.length = 0; // clear geofence array
};
}, 0);
$scope.cancel = function () {
$modalInstance.close()
};
$scope.saveGeofence = function () {
forgeGeoTriggerService.EditGeofence($scope.geofence, $scope.goefenceID)
.then(function (data) {
$scope.successMessage = 'Geofence Updated Successfully'
$rootScope.$broadcast('geotrigger.edited');
$timeout(function () {
$modalInstance.close();
}, 2000);
}, function (data) {
$scope.errorMessage = 'There was an error when updating geofence. Please try again.';
});
}
});
This is modal controller test
describe("forge.geomanagement.GeoApp", function () {
var scope, controller, modalInstance, timeout, forgeGeoTriggerService, window = {},
geofencemock, geofence, triggerID;
beforeEach(module('forge.geomanagement.GeoApp'));
describe("Controller: EditGeofenceModalController", function () {
beforeEach(inject(function ($controller, $rootScope, $timeout, _forgeGeoTriggerService_) {
scope = $rootScope.$new();
timeout = $timeout;
modalInstance = {
close: jasmine.createSpy('modalInstance.close'),
dismiss: jasmine.createSpy('modalInstance.dismiss'),
result: {
then: jasmine.createSpy('modalInstance.result.then')
}
}
geofencemock = {
FriendlyName: 'mock geofence',
Coords: [
{
"lat": 53.5598889724547,
"lng": -6.36953830718994
},
{
"lat": 53.463525599115,
"lng": -6.53707981109619
},
{
"lat": 53.3685818160803,
"lng": -6.46841526031494
},
{
"lat": 53.384966558115,
"lng": -5.75430393218994
},
{
"lat": 53.5598889724547,
"lng": -6.34756565093994
},
{
"lat": 53.5598889724547,
"lng": -6.36953830718994
}
],
GeoTags: ['tag1','tag2','tag3']
}
triggerIDmock = 1;
forgeGeoTriggerService = _forgeGeoTriggerService_;
controller = $controller("EditGeofenceModalController", {
$scope: scope,
$timeout: timeout,
$modalInstance: modalInstance,
forgeGeoTriggerService: forgeGeoTriggerService,
geofence: geofencemock,
triggerID: triggerIDmock
});
}));
it('2 is 2', function () {
expect(2).toBe(2);
})
it("geofence should be defined", function () {
expect(geofencemock).toBeDefined();
});
it("should contain reference to forgeGeoTriggerService", function () {
expect(forgeGeoTriggerService).not.toBeNull();
});
it("$modalInstance obj should be defined when modal is open", function () {
expect(modalInstance).toBeDefined();
});
it("cancel function should close edit geofence modal", function () {
scope.cancel();
expect(modalInstance.close).toHaveBeenCalled();
});
});
});
But when I try to run it I get the error: "Cannot read property length of undefined" that corresponds to $scope.geofence.Coords property - an array that is successfully copied over to modal from parent controller. As you can see, I also created a geofencemock object and tried to use it in a very simple test but it looks like it's not being picked up. I would really appreciate some input, cause I have already spent couple of hours trying to fix it or find a solution online, but to no avail.
Thanks.
Upvotes: 0
Views: 400
Reputation: 4102
Ok, I got it working. The error "Cannot read property 'lat' of undefined" was related not to the coordinates in geofence mock object but to the geofence.Center.lat property I was using in my controller with geofence.Center.lng to position the center of the map. Let me explain: we get the polygon details from the server, then we pass them into edit modal window (Angular UI Bootstrap):
forgeGeoTriggerService.GetGeoFence(geotriggerID)
.then(function (geofenceData) {
$scope.modalInstance = $modal.open({
windowClass: 'popup-geofence-modal',
templateUrl: TemplateUrlProvider.GetUrl('GeofenceModal'),
controller: 'EditGeofenceModalController',
resolve: {//pass geofenceData from server to geofence obj inside modal
geofence: function () {
return geofenceData;
},
triggerID: function () {
return geotriggerID
}
}
});
}, function (error) {
$scope.errorMessage = 'There was an error when trying to fetch geofencene details. Please try again later.';
});
Then in EditGeofenceModalController we make use of the geofence object passed from the parent controller above
'use strict';
var controllersModule = angular.module('forge.geomanagement.controllers');
controllersModule.controller('EditGeofenceModalController', function ($timeout, $scope, $modalInstance, forgeGeoTriggerService, $rootScope, geofence, triggerID) {
var searchAddressInput: HTMLInputElement;
//make a copy of geofence obj passed into modal
$scope.geofence = {
FriendlyName: geofence.FriendlyName,
Coords: angular.copy(geofence.Boundary),
GeoTags: angular.copy(geofence.GeoTags)
};
$scope.goefenceID = triggerID;
var gCLength = $scope.geofence.Coords.length;
//if first and last coords are the same - remove the last one
if ($scope.geofence.Coords[0].lat === $scope.geofence.Coords[gCLength - 1].lat
&& $scope.geofence.Coords[0].lng === $scope.geofence.Coords[gCLength - 1].lng) {
$scope.geofence.Coords.pop();
}
//!!!!!!!set the map center to geofence.Center
var geofenceCenter: google.maps.LatLng = new google.maps.LatLng(
geofence.Center.lat, geofence.Center.lng
);
Pay attention to the comment line with exclamation marks. This is where I set the center of the map. The geofence object returned from the server has a Center property - an obj with lat and lng properties. Once I changed the Coords to Boundary in my geofencemock obj in test as @rayners suggested, it was still missing the Center property. Setting it like that in the test file fixed the problem and my tests passed:
geofencemock = {
FriendlyName: 'mock geofence',
Boundary: [
{
"lat": 53.5598889724547,
"lng": -6.36953830718994
},
{
"lat": 53.463525599115,
"lng": -6.53707981109619
},
{
"lat": 53.3685818160803,
"lng": -6.46841526031494
},
{
"lat": 53.384966558115,
"lng": -5.75430393218994
},
{
"lat": 53.5598889724547,
"lng": -6.34756565093994
},
{
"lat": 53.5598889724547,
"lng": -6.36953830718994
}
],
GeoTags: ['tag1', 'tag2', 'tag3'],
Center: {
"lat": 53.46769593973309,
"lng": -6.2952017905716735
}
}
Upvotes: 0
Reputation: 539
You're setting $scope.geofence.Coords
from geofence.Boundary
:
$scope.geofence = {
FriendlyName: geofence.FriendlyName,
Coords: angular.copy(geofence.Boundary),
GeoTags: angular.copy(geofence.GeoTags)
};
But you're mocking geofence
with Coords
directly:
geofencemock = {
FriendlyName: 'mock geofence',
Coords: [
{
"lat": 53.5598889724547,
"lng": -6.36953830718994
},
Change the latter to be geofencemock.Boundary
and you should be fine.
Upvotes: 1