Testing angularjs es6 factories with jasmine - angularjs

I have the following file: classAModel.js with the following code:
class classAModel{
constructor(model) {
'ngInject';
if (!model) return {};
this.id = model.id;
this.name = model.name;
this.displayName = model.displayName;
}
}
export default classAModel;
This code is defined as a factory in another file: module.js:
import classAModelfrom './classAModel'
module.factory('ClassAModel', ClassAModel);
This code works perfectly when not in a testing context. It works using Webpack to create a bundle that is loaded and runs. So far so good. Now, the question is how do I test this class. Before I changed my code to es6 style, it used be a function and it worked. The test first loads the bundle, but when I try to inject the factory (again, same as before), I get an error: Unknown provider: modelProvider <- model <- classAModel. I can understand why he thinks there is a problem, but I can't understand how to fix it.
Moreover, I'm wondering if this is the correct way to work with the factory rather then create a factory method inside the class, that gets the model, and then create my object.
Thanks

Based on the information you've provided, here's a simple test case for testing your factory. Hope this is what you're looking for.
import classAModel from './classAModel'
let classAInstance;
describe('classAModel', function() {
beforeEach(function() {
modelInstance = new Model();
classAInstance = new classAModel(modelInstance);
});
it('should have id provided by model', () => {
expect(classAInstance.id).toBe(modelInstance.id);
});
});

Related

Angular JS $injector - get dependencies

I need to get the instances of my Services/Factory in my Angular JS application. For that reason I would want to use $injector so as to get instances and not depend on DI. I tried to create a wrapper method over $injector which is a seperate js file so that any other modules can call this helper method and get necessary instances. I know this is not straight forward but still wanted to try.
For this my code looks like
//helper.js file
export default function returnInstances(service) {
const app = angular.module('moduleName');
let instance;
app.run(() => {
injector = angular.injector(['ng', 'moduleName']);
instance = injector.get(service);
});
return instance;
}
// some other file
const instance = returnInstances('serviceName');
As expected this does not work. So I was wondering if anything like this is possible.
const helper = {
init: (injector) => this.injector = injector;
get: (name) => this.injector.get(name);
}
export default helper;
And in file where you bootstrap your module:
app.run((['$injector'], $injector) => helper.init($injector));
angular.bootstrap(...
And then u can use it.

How do I hook an AngularJS directive into two different projects?

I am searching for a tutorial on specifically this, but don't seem to find it, so maybe its something I missed:
If I declare a module, saved as myproject.js
var myProject = angular.module('myProject',[]);
Later I add a directive, saved as domchanger.directive.js
myProject.directive('domchanger', function (){});
But now, I have a new project, saved as newproject.js
var newProject = angular.module('newProject', []);
But if I try to add domchanger, its hooked into myProject.
surely I shouldn't have to save a whole version with the myProject changed to newProject?
I'm sure I've seen somehow where I would somehow do something to the directive to make it generic, so it can be used on any module. Most online documentation seems to skip this step, or not even get to it.
What do I need to do, to allow domchanger.directive.js to be plug and play for newProject?
EDIT: as per request for simplified non working code
var myProject = angular.module('myProject', []);
myProject.controller('projectControl', ['$scope', function ($scope) {
$scope.Test = "hello";
}]);
myProject.directive('projectDirective', function () {
});
var newProject = angular.module('newProject', ['projectControl',
'projectDirective']);
This gives me a
Failed to instantiate module newProject due to:
Error: $injector:nomod
Module Unavailable
If that helps..
If this is what your "domchanger.directive.js" look like...
var app = angular.module("myDirective", []);
app.directive("testDirective", function() {
//...
});
app.controller('testController',function($scope){
//...
});
this plug n'play (injectable module) can be called in your projects like...
var app = angular.module("myProject", ["myDirective"]);
or
var app = angular.module("newProject", ["myDirective"]);
Hope this is what you are looking for...

Unit Testing Component in AngularJS 1.5 and Typescript

Working on converting some old broken AngularJS unit tests to TypeScript and came across this error:
Error: [$injector:unpr] Unknown provider: myComponentDirectiveProvider <- myComponentDirective
In every example of AngularJS unit testing I've seen, they have a main module with everything on it, and they inject that main module into the unit test with angular.mock.module('theWholeStinkingApp'). That is nice for a little tutorial, but I'm dealing with a very large application with hundreds of components and dozens of services. Not to mention filters and other directives. It doesn't work to just inject the whole app into a unit test. Nor do I think it's a good idea to start up the whole app just to unit test a component (kind of defeats the purpose of unit testing when you have to include everything you own in every test).
For testing services I can just create my module in the test in a beforeEach like this, so I'm mocking dependencies, but injecting the real service I want to test:
angular.mock.module(($provide) => {
$provide.service('firstDependency', FirstDependencyMock);
$provide.service('secondDependency', SecondDependencyMock);
$provide.service('serviceIWantToTest', ServiceIWantToTest);
});
I can't figure out how to do this and inject a Component. Any help would be appreciated.
Please keep in mind, I do not want to have to angular.mock.module('theWholeApp') to get this working. I just want to create a mock module and attach my component to it.
Here's a slimmed down version of what I'm doing.
Component looks something like this
angular.module('theWholeApp', [])
.component('myComponent', {
controller: MyComponentController, // class is defined elsewhere
templateUrl: 'path/to/my/template.html'
)
... // 100+ other components;
Here's the test:
describe('MyComponent', () => {
beforeEach(() => {
// angular.mock.module('theWholeApp'); This is not an option.
// Creating a mock module with just the dependencies I need for this test
angular.mock.module(($provide) => {
$provide.service('firstDependency', FirstDependencyMock);
$provide.service('secondDependency', SecondDependencyMock);
});
// Tried adding this to register the controller, but it doesn't help.
angular.mock.module(($controllerProvider) => {
$controllerProvider.register('MyComponentController', MyComponentController);
});
angular.mock.inject(($injector) => {
$rootScope = $injector.get('$rootScope');
$componentController = $injector.get('$componentController');
firstDependency= $injector.get('firstDependency');
secondDependency= $injector.get('secondDependency');
$scope = $rootScope.$new();
});
});
describe('doSomething', () => {
it('exists', () => {
controller = $componentController('myComponent', {$scope});
expect(controller.doSomething).toBeDefined();
});
});
});
Obviously, this isn't the live code, just a representation. Hopefully I got all the made up names right. The point is, I want to create a mock module and add my component to it so a call to $componentController('myComponent', {$scope}) works.
Thanks!
Alright, the answer I came up with is deceptively simple, but I've never seen it done anywhere. If anyone can tell me why it's a bad idea and suggest something better, I'm all for it.
The answer is to just create the module you want then immediately add it to the test.
describe('MyComponent', () => {
// Create a new module with the things you need.
// Putting it outside the "beforeEach" because it only needs to be created once.
angular.module('myComponentTestModule', [])
.component('myComponent', {
controller: MyComponentController, // class is defined elsewhere
templateUrl: 'path/to/my/template.html'
)
.service('firstDependency', FirstDependencyMock)
.service('secondDependency', SecondDependencyMock);
beforeEach(() => {
// Add the new module to the test suite
angular.mock.module('myComponentTestModule');
angular.mock.inject(($injector) => {
$rootScope = $injector.get('$rootScope');
$componentController = $injector.get('$componentController');
firstDependency= $injector.get('firstDependency');
secondDependency= $injector.get('secondDependency');
$scope = $rootScope.$new();
});
});
describe('doSomething', () => {
it('exists', () => {
controller = $componentController('myComponent', {$scope});
expect(controller.doSomething).toBeDefined();
});
});
});

ES6 Angular1 service syntax, how to create a service and use it?

I know there are similar questions, but somehow they all have different code and i cant seem to find something that answers my specific problem, im sorry.
Im following several exmaples, and i got here:
This is my soon-to-be map service (maps.service.js):
import angular from 'angular'
import angularConfig from '../angularConfig/angularConfig'
export default class Maps {
initializeMap (element, options){
return new Promise(function(resolve){
return resolve(new google.maps.Map(document.getElementById(element), options))
})
}
}
Maps.$inject = ['$http']
angular.module(Maps, [])
.service('maps.service', Maps)
You can see its a simple class with a method inside.
At the bottom based on several examples i placed a call to angular.service, gave it a name and used the class i created. I think this is wrong, but i dont know why. I also dont know if thats the correct way to inject a service i need from angular into it.
Then, in order to consume this service i have a small controller (catalog.controller.js):
// import mapService from '../../common/maps/maps.service'
class catalogController {
constructor($scope, mapsService) {
this.$scope = $scope
}
$onInit () {
// map init
mapService.initializeMap(param1, param2)
.then(() => {
console.log('it worked!!')
})
}
}
catalogController.$inject = ['$scope']
export default catalogController;
The problem is no matter how i write the code in the service, i always end up with an error when i try to use it, when i inspect mapService to see what i has inside, the function is not there.
I tried importing it and passing it as a parameter to the constructor, i tried with different ways i found to create the service, but i cant make it work, everyone seems to be using something different.
Both this files are inside a folder named 'catalog', outside this folder i have this file:
import angular from 'angular'
import catalog from './catalog/catalog'
let componentModule = angular.module('app.components', [
catalog
])
.name
export default componentModule
which is in turn used by another file. This whole structure works, as i have things working already with controllers and views. My only problem is how to create and use a service.
Any help will be appreciated, as i have already spent many days on this!
I can provide more details if needed.
Thank you :)
According to DI, do some changes:
Maps Service:
import angular from 'angular'
import angularConfig from '../angularConfig/angularConfig'
export default class Maps {
constructor($http){}
initializeMap (element, options){
return new Promise(function(resolve){
return resolve(new google.maps.Map(document.getElementById(element), options))
})
}
}
Maps.$inject = ['$http']
angular.module(Maps, [])
.service('maps.service', Maps)
catalogController
class catalogController {
constructor($scope, mapsService) {}
$onInit () {
// map init
this.mapService.initializeMap(param1, param2)
.then(() => {
console.log('it worked!!')
})
}
}
catalogController.$inject = ['$scope', 'mapsService']
export default catalogController;
PS: Here is a good example: https://github.com/FountainJS/generator-fountain-angular1

Checking Backbone view method called with Jasmine

I have a Backbone application I'm working on where all the functionality is namespaced under App. I have a view called App.LoginView, and I want to test it with Jasmine, but I'm struggling to get it to work.
This is the code for the test:
describe("Test the views", function () {
'use strict';
beforeEach(function () {
// Create DOM element
$('body').append('<div class="content"></div>');
});
afterEach(function () {
$('div.content').remove();
});
// Test the LoginView object was created
it("tests that the LoginView object exists", function () {
// Check the view definition exists
expect(App.LoginView).toBeDefined();
// Spy on the prototype
spyOn(App, 'LoginView').andCallThrough();
// Create the view
this.LoginView = new App.LoginView();
// Check the view exists
expect(this.LoginView).toBeDefined();
expect(this.LoginView.initialize).toBeDefined();
expect(this.LoginView.template).toBeDefined();
expect(this.LoginView.tagName).toBeDefined();
expect(this.LoginView.render).toBeDefined();
// Remove it
this.LoginView.remove();
});
});
It throws the following error:
TypeError: 'undefined' is not a function (evaluating 'spyOn(App, 'LoginView').andCallThrough()')
I'm using the grunt-contrib-jasmine to run the tests and jasmine-jquery to add support for jQuery. I have used Jasmine a little bit in the past, but I'm struggling to see where I've gone awry here.
Found a solution in the end. To get it working, I spied on the initialize method of the prototype and changed the syntax for the call through as this changed in Jasmine 2.
spyOn(App.LoginView.prototype, 'initialize').and.callThrough();
I also dynamically inserted a basic template into the DOM using jasmine-jquery as otherwise Underscore complained about the missing template:
$('body').append('<script type="text/template" id="login-template"><div class="content-padded"></div></script>');

Resources