Controller Tests using Jasmine and Typescript - angularjs

I am getting the following error
Expected 0 to be 4.
...anggen/app/products/products.list.controller.spec.js:22:35
Could anyone tell me why the httpbackend is not returning the response? or is it something silly that I missed? Spent a lot of time on getting this to work and am stuck..
Please look at the test case below. The test in question is it("should create controller"... test
My controller code is as follows...
module app.productList {
'use strict';
export interface IProductListModel {
title: string;
showImage: Boolean;
products: app.domain.IProduct[];
toggleImage(): void;
addNumbers(firstNumber:number, secondNumber:number): number;
}
export class ProductListCtrl implements IProductListModel {
title:string;
showImage:boolean;
products:app.domain.IProduct[];
static $inject = ["dataAccessService"]; // for minification
constructor(private dataAccessService:app.productService.DataAccessService) {
this.title = "Product List";
this.showImage = false;
this.products = [];
dataAccessService.getProductResource().query((data:app.domain.IProduct[]) => {
this.products = data;
});
}
public toggleImage():void {
this.showImage = !this.showImage;
}
public addNumbers(firstNumber:number, secondNumber:number) {
return firstNumber + secondNumber;
}
}
angular.module("app.products").controller("ProductListCtrl", ProductListCtrl)
}
My service code is as follows...
module app.productService {
export interface IDataAccessService {
getProductResource(): ng.resource.IResourceClass<IProductResource>;
getProductResourceDetails(): ng.resource.IResourceClass<IProductResource>;
}
export interface IProductResource extends ng.resource.IResource<app.domain.IProduct> {
}
export class DataAccessService
implements IDataAccessService {
// for minification.
static $inject = ["$resource"];
constructor(private $resource: ng.resource.IResourceService) {
}
getProductResource(): ng.resource.IResourceClass<IProductResource> {
//return this.$resource("/api/products/:productId");
return this.$resource("http://localhost:9998/generic/products");
}
}
angular
.module("app.products")
.service("dataAccessService", DataAccessService);
}
My Test code is as follows...
describe("Product List Controller", function () {
var controller: app.productList.ProductListCtrl;
var dataAccessService: app.productService.DataAccessService;
var $httpBackend : ng.IHttpBackendService;
beforeEach(function () {
angular.mock.module("productManagement");
});
beforeEach(inject(function (
$rootScope: ng.IRootScopeService,
_$httpBackend_: ng.IHttpBackendService,
_dataAccessService_: app.productService.DataAccessService) {
dataAccessService=_dataAccessService_;
$httpBackend=_$httpBackend_;
}));
it("should create controller", () => {
$httpBackend.expectGET("http://localhost:9998/generic/products").respond([
{"id": "1", "name": "Pizza Vegetaria", "price": 5 },
{"id": "2", "name": "Pizza Salami", "price": 5.5 },
{"id": "3", "name": "Pizza Thunfisch", "price": 6 },
{"id": "4", "name": "Aktueller Flyer", "price": 0 }
]);
controller = new app.productList.ProductListCtrl(dataAccessService);
expect(controller).not.toBeNull();
var result = controller.products;
expect(result.length).toBe(4);
$httpBackend.flush();
});
});

Related

Angular 2+ services to angularJs

I m trying to convert Angular 2+ service and use it in angularJs project.
app/users.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class UsersService {
private users: any = [
{ id: 1, name: 'john' },
{ id: 2, name: 'jane' }
];
constructor() { }
getUsers() {
return this.users;
}
}
then I tranform it into js with rollup
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('#angular/core'), require('#angular/upgrade/static'), require('#angular/platform-browser')) :
typeof define === 'function' && define.amd ? define(['exports', '#angular/core', '#angular/upgrade/static', '#angular/platform-browser'], factory) :
(factory((global.mymodule = {}),global.ng.core,global._static,global.platformBrowser));
}(this, (function (exports,core,_static,platformBrowser) { 'use strict';
var UsersService = /** #class */ (function () {
function UsersService() {
this.users = [
{ id: 1, name: 'john' },
{ id: 2, name: 'jane' }
];
}
UsersService.prototype.getUsers = function () {
return this.users;
};
UsersService.decorators = [
{ type: core.Injectable },
];
/** #nocollapse */
UsersService.ctorParameters = function () { return []; };
return UsersService;
}());
var MyModule = /** #class */ (function () {
function MyModule(upgrade) {
this.upgrade = upgrade;
}
MyModule.decorators = [
{ type: core.NgModule, args: [{
imports: [platformBrowser.BrowserModule, _static.UpgradeModule],
declarations: [],
providers: [UsersService],
exports: []
},] },
];
/** #nocollapse */
MyModule.ctorParameters = function () { return [
{ type: _static.UpgradeModule, },
]; };
return MyModule;
}());
exports.UsersService = UsersService;
exports.MyModule = MyModule;
Object.defineProperty(exports, '__esModule', { value: true });
})));
I add downgradeInjectable to convert angular2+ service into angularJs
angular.module('users.service', [])
.factory('usersServices', downgradeInjectable(usersServices));
next I try to load it in angularJs
(function() {
'use strict';
angular.module(
'ng1.module', [
'usersServices'
]);
})();
I m missing something because it doesn't work :/
I can't find ressource to do it, all people a trying to convert ng1 service to ng2 but not the reverse.
Someone can help ?
Thank you
Instead of importing 'usersServices' (the service factory) in the module 'ng1.module', you should import the actual module with 'usersServices' in it instead:
(function() {
'use strict';
angular.module(
'ng1.module', [
'users.service'
]);
})();

Example of injecting services in Angular 1.5 components

Can anyone give an example on using services with Angular 1.5 components?
I'm trying to inject a service in an Angular 1.5 component, but it doesn't work.
I have a login component like so:
class Login {
constructor($scope, $reactive, $state, myService) {
console.log(myService.somevariable); //doesn't work
}
}
// create a module
export default angular.module(name, [
angularMeteor
]).component(name, {
templateUrl: 'imports/ui/components/${name}/${name}.html',
controllerAs: name,
controller: Login
});
My service looks like this:
angular.module(name).service("myService", function () {
this.somevariable = 'somevalue';
});
I just cant seem to be able to get the service injected in the component.What am I doing wrong?
SOLUTION:
With sebenalern's help, I got it working.
I needed a service to validate an email address using a regular expression. I did it like this:
import angular from 'angular';
import angularMeteor from 'angular-meteor';
class Validator {
validateEmail(email) {
var re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
}
const name = 'validator';
// create a module
export default angular.module(name, [
angularMeteor
])
.service("Validator", Validator);
I then injected the service like so:
import {name as Validator} from '../../../api/services/validator'
class Login {
constructor($scope, $reactive, $state, Validator) {
'ngInject';
this.$state = $state;
$reactive(this).attach($scope);
this.Validator = Validator;
}
login() {
if(this.Validator.validateEmail(this.credentials.email)) {
// email is valid.
}
}
}
const name = 'login';
export default angular.module(name, [
angularMeteor,
Validator
]).component(name, {
templateUrl: `imports/ui/components/${name}/${name}.html`,
controllerAs: name,
controller:Login
})
Hope this helps :)
So one problem I see is you should be using the keyword this inside the constructor
this.$scope = $scope;
Another thing it is probably easier to stay away from classes and use functions:
class Login {
constructor($scope, $reactive, $state, myService) {
console.log(myService.somevariable); //doesn't work
}
}
Becomes:
angular
.module('name')
.service('myService', myService);
function myService () {
this.somevariable = 'somevalue';
}
To me it seems a lot cleaner. Also another thing about ES6 classes is
ES6 Classes are not hoisted, which will break your code if you rely on hoisting
For more info see link.
Now here is the working code I came up with:
First we declare the module:
angular.module('name', []);
Next we register our service and then create the service definition:
angular
.module('name')
.service('myService', myService);
function myService () {
this.somevariable = 'somevalue';
}
Next we do the same procedure for our controller and also we inject $scope and our service into it.
angular
.module('name')
.controller('Login', Login);
function Login($scope, myService) {
$scope.someVar = myService.somevariable;
}
Last I registered our component:
angular
.module('name')
.component('my-html', {
templateUrl: 'my-html.html',
controller: Login
});
And that is it on the javascript side.
Here is my html code:
<!DOCTYPE html>
<html lang="en-us" ng-app='name'>
<head>
<script src="//code.angularjs.org/1.5.0-rc.1/angular.js"></script>
<script src="controller.js"></script>
</head>
<body >
<h ng-controller="Login">{{ someVar }}</h>
</body>
</html>
I hope this helps!!
Here is how I am doing it and it works well. It works fine with classes. I assume you are using TypeScript.
class AdminHomeService {
consignment: IConsignment;
get: () => IConsignment;
constructor() {
this.consignment = new Consignment();
this.consignment.id = 10;
this.consignment.customer = "Customer3";
this.consignment.customerList = [{ id: 1, name: "Customer1" }, { id: 2, name: "Customer2" }, { id: 3, name: "Customer3" }];
this.consignment.shipperList = [{ key: "1", value: "Shipper1" }, { key: "2", value: "Shipper2" }, { key: "3", value: "Shipper3" }];
this.consignment.consigneeList = [{ key: "1", value: "Consignee1" }, { key: "2", value: "Consignee2" }, { key: "3", value: "Consignee3" }];
this.consignment.billingList = [{ key: "1", value: "Billing1" }, { key: "2", value: "Billing2" }, { key: "3", value: "Billing3" }];
this.consignment.carrierList = [{ key: "1", value: "Carrier1" }, { key: "2", value: "Carrier2" }, { key: "3", value: "Carrier3" }];
this.get = () => {
return this.consignment;
}
}
}
class AdminHomeComponentController {
consignment: IConsignment;
selectedCustomer: any;
static $inject = ["adminHomeService"];
constructor(private adminHomeService: AdminHomeService) {
this.consignment = new Consignment();
this.consignment = this.adminHomeService.get();
this.selectedCustomer = {};
this.selectedCustomer.selected = { "name": this.consignment.customer };
}
customerAddClick(): void {
}
}
class AdminHomeComponent implements ng.IComponentOptions {
bindings: any;
controller: any;
templateUrl: string;
$routeConfig: angular.RouteDefinition[];
constructor() {
this.bindings = {
textBinding: "#",
dataBinding: "<",
functionBinding: "&"
};
this.controller = AdminHomeComponentController;
this.templateUrl = "templates/admin.home.html";
//this.$routeConfig = [
// { path: "/admin", name: "AdminHome", component: "adminHome", useAsDefault: true }
//];
}
}
angular.module("adminHome", [])
.component("adminHome", new AdminHomeComponent())
.service("adminHomeService", AdminHomeService);
This post helped me a lot: http://almerosteyn.com/2016/02/angular15-component-typescript

AngularJS Testing with Mocha and Sinon - Mocking Firebase Dependencies

I am trying to skip going to Firebase for my data in my tests, and return some simple results instead. I do not want to test that the Firebase code works, but that my factories work in returning data.
I have the following factories:
// Get Firebase Reference
angular.module('MyApp').factory('FireBaseData', function(FIREBASE) {
var FireBaseReference = new Firebase(FIREBASE.URL); // FIREBASE.URL = xxx.firebaseio.com
return FireBaseReference;
})
// Get the Firebase Array of Team Records
angular.module('MyApp').factory('AllTeams', ["FireBaseData", "$firebaseArray",
function(FireBaseData, $firebaseArray) {
return $firebaseArray(FireBaseData.child('Teams'));
}
]);
I have created mocks that replace the individual functions, and my tests will use these.
'use strict';
var $MockFirebaseArray = function(ArrayWithData) {
return ArrayWithData;
};
var $MockFirebaseObject = function(ObjectWithData) {
return ObjectWithData;
};
var MockFirebaseData = function() {
return {
child: function(StringValue) {
return "";
}
};
};
Tests with the mocks:
'use strict';
describe('Firebase Mocks', function() {
var TestArray = [
{ 'aString': 'alpha', 'aNumber': 1, 'aBoolean': false },
{ 'aString': 'bravo', 'aNumber': 2, 'aBoolean': true },
{ 'aString': 'charlie', 'aNumber': 3, 'aBoolean': true },
{ 'aString': 'delta', 'aNumber': 4, 'aBoolean': true },
{ 'aString': 'echo', 'aNumber': 5 }
];
describe('MockFirebaseData', function() {
var TestFirebase = MockFirebaseData();
it('should return empty text ("") from FireBaseData', function() {
assert.equal('', TestFirebase.child('SomeNode'));
});
});
describe('$MockFirebaseArray', function() {
it('should have the data array passed', function() {
var TestData = $MockFirebaseArray(TestArray);
assert.equal(TestArray.length, TestData.length);
});
});
describe('$MockFirebaseObject', function() {
it('should have the data object passed', function() {
var TestData = $MockFirebaseObject(TestArray[0]);
assert.equal(TestArray[0].length, TestData.length);
assert.deepEqual(TestArray[0], TestData);
});
});
});
This shows that the Mocks are working to return data, which is what I want to stay away from actually accessing Firebase. Now, when I try to use my factory in a test, I am getting errors.
Test the Factory:
describe('Teams Module', function() {
beforeEach(module('MyApp')); // Load My Application
describe('AllTeams Array', function() {
// Create Test Data
var TeamData = [
{ "Key": 1, "Name":"Team 1", "Logo": "Team1.jpg" },
{ "Key": 3, "Name":"Team 3", "Logo": "Team3.jpg" },
{ "Key": 2, "Name":"Team 2", "Logo": "Team2.jpg" },
];
beforeEach(function () {
module(function($provide) {
var MockData = MockFirebaseData();
$provide.value('FireBaseData', MockData);
$provide.value('$firebaseArray', $MockFirebaseArray(TeamData));
});
});
it('can get an instance of AllTeams factory', inject(function(AllTeams) {
assert.isDefined(AllTeams);
}));
});
});
Error returned:
PhantomJS 1.9.8 (Windows 7 0.0.0)
Teams Module
AllTeams Array
can get an instance of AllTeams factory FAILED
TypeError: '[object Object],[object Object],[object Object]' is not a function (evaluating '$firebaseArray(FireBaseData.child('Teams'))')
at app/Team.js:9
Instead of:
$provide.value('$firebaseArray', $MockFirebaseArray(TeamData));
try this:
$provide.value('$firebaseArray', $MockFirebaseArray);
I believe this is what you were intending to do in the first place. When injected, your factory will then be able to call $firebaseArray as a function.

How can I inject 3rd party javascript files as objects into a factory

I have a 3rdparty.JS file at app\scripts\vendor\3rdparty.JS
angular.module('clientApp').factory('myFactory', function () {
// HOW can I get the 3rdparty.JS file here in form of an object?
// Public API here
return {
};
});
How can I now access the 3rdparty library in form of an object to access its methods?
Me too like using linq in angular apps. Those linq operations are time savers. Fortunately we have a plain javascript library equivalent to this.
linq.js in http://linqjs.codeplex.com/ is awesome and stable.
If someone can port this to bower package in github. That would be much helpful.
However, I tried this below and worked for me,
Full Content in the Plunker - http://plnkr.co/edit/Zy1W3G?p=info
// Add service to your module
//linq.svc.js
(function() {
'use strict';
angular
.module('mymodule')
.factory('$linq', linqService);
function linqService() {
return {
getEnumerable: Enumerable
};
function Enumerable() {
var Enumerable = function(getEnumerator) {
this.GetEnumerator = getEnumerator;
}
...... < Paste the remaining content here >
......
......
// out to global
return Enumerable;
}
}
})();
//Example Usage:
//mycontroller.js
(function() {
'use strict';
angular.module('mymodule')
.controller('mycontroller', MyController);
MyController.$inject = [
'$linq'
];
function MyController(
$linq
) {
var jsonArray = [{
"user": {
"id": 100,
"screen_name": "d_linq"
},
"text": "to objects"
}, {
"user": {
"id": 130,
"screen_name": "c_bill"
},
"text": "g"
}, {
"user": {
"id": 155,
"screen_name": "b_mskk"
},
"text": "kabushiki kaisha"
}, {
"user": {
"id": 301,
"screen_name": "a_xbox"
},
"text": "halo reach"
}]
var queryResult = $linq.getEnumerable().From(jsonArray)
.Where(function(x) {
return x.user.id < 200
})
.OrderBy(function(x) {
return x.user.screen_name
})
.Select(function(x) {
return x.user.screen_name + ':' + x.text
})
.ToArray();
console.log("queryResult==>", queryResult);
}
})();
Example Output:
queryResult==> ["b_mskk:kabushiki kaisha", "c_bill:g", "d_linq:to objects"]

Karma testing with .success() getting 'undefined' is not an object'

I'm trying to write a unit test to see if the 'getStudents()' provider function in my controller gets called if some properties are appropriately set. Notice the .success() callback:
$scope.update = function update() {
// omitted, just doing some checking...
// finally
else if (key.length === 3 || $scope.students.length === 0) {
StudentsProvider.getStudents($scope.keyword, $scope.selectedFilters).success(function(data) {
$scope.students = data;
});
}
};
My karma unit test looks like this:
describe("Students: Controllers", function () {
var $scope;
var ctrl;
beforeEach(module('studentsApp'));
describe("SearchCtrl", function () {
// Mock the provider
var mockStudentsProvider = {
getStudents: function getStudents() {
return [
{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3", MobilePhone: "7777777"
}]
}
];
}
};
var StudentsProvider;
beforeEach(inject(function ($controller, $rootScope) {
$scope = $rootScope.$new();
ctrl = $controller('SearchCtrl', { $scope: $scope, StudentsProvider: mockStudentsProvider});
StudentsProvider = mockStudentsProvider;
}));
describe("Update", function () {
beforeEach(function () {
spyOn(StudentsProvider, 'getStudents');
});
it("should always call the provider with 3 letters", function () {
$scope.keyword = "axe";
$scope.update();
expect(StudentsProvider.getStudents).toHaveBeenCalled();
expect(StudentsProvider.getStudents).toHaveBeenCalledWith("axe", "");
});
});
});
});
When I run this, I get the following error:
TypeError: 'undefined' is not an object (evaluating 'StudentsProvider.getStudents($scope.keyword, $scope.selectedFilters).success')
and it's probably because I'm not mocking the .success() callback. How would I do that? Thanks in advance!
Replace this:
var mockStudentsProvider = {
getStudents: function getStudents() {
return [{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3",
MobilePhone: "7777777"
}]
}];
}
};
with this:
var mockStudentsProvider = {
getStudents: function getStudents() {
var retVal = [{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3",
MobilePhone: "7777777"
}]
}];
return {
success: function(fn) {
fn(retVal)
};
}
}
};
And replace this:
spyOn(StudentsProvider, 'getStudents');
with this:
spyOn(StudentsProvider, 'getStudents').andCallThrough();
When you do not use andCallThrough() or andCallFake() jasmine prevents execution of the method and returns null. Inside your update method you are calling null.success. This will fail. (http://jasmine.github.io/1.3/introduction.html)
In your mock method you need to change the return format--the real http method returns an object where success refers to a function which takes an input a callback function.
In your case, the callback function is:
function(data) {
$scope.students = data;
}

Resources