Jasmine tests failing over EmberJS needs API - javascript

I'm working on unit testing an EmberJS project with Jasmine but I'm having trouble with Ember's needs API.
When I try to run the jasmine tests, they fail on creating a controller instance, if the controller in question has "needs" as well as an init function that calls
this._super()
I get this console error
"Cannot call method 'has' of null"
that when I tried to debug, brought me all the way into the bowels of Ember but I got nowhere with that.
Anyone have any idea what I'm doing wrong
Application.SearchPendingController = Ember.ObjectController.extend({
needs: ['searchResults', 'search'],
shouldDisable: false,
searchResultsController: null,
init: function () {
this._super();
this.set('searchResultsController', this.controllerFor('searchResults'));
this.get('controllers.search.content').reload();
this.get('controllers.searchResults').set('content', this.get('controllers.search.content.results'));
},
transitionToResults: function () {
console.log('yay');
}.observes('this.searchResultsController.content')
});
The jasmine tests throw an error when I try to create this controller
var searchPendingController = Application.SearchPendingController.create();
Anyone have any ideas about this?

When you create a controller, Ember.js checks the dependencies (needs) in the init method. Checking for dependencies assumes you have an Ember.js application, and this application's container is found in the container property of the controller. This all works great if Ember.js created the controller for you.
Your error is happening here, in the verifyDependencies function.
If you don't want Ember.js to create the controller for you and want to create it manually, (which is what you are doing here), you will need to manually set the controller's container property to the application's container.
Application.SearchPendingController.reopen({
container: Application.__container__
});
Unit testing controllers is tricky and requires that you dive into the internals of Ember.js. My advice, let Ember.js create the controllers for you, and use integration tests instead of unit testing them.

Even better is if your controller needs to access the other controller for calculating some property is to have the container create the controllers for you.
Application.SearchPendingController = Ember.ObjectController.extend({
needs: ['searchResults', 'search'],
Testing
var searchModel = something,
searchResultsModel = something,
searchPendingModel = something;
var container = Application.__container__,
searchController = container.lookup('controller:search'),
searchResultsController = container.lookup('controller:searchResults'),
searchPendingController = container.lookup('controller:searchPending'),
searchController.set('model', searchModel),
searchResultsController.set('model', searchResultsModel ),
searchPendingController.set('model', searchPendingModel );

Related

Angular Unit Testing: TypeError: cannot set properties of null (setting 'innerHtml') (Jasmine/Karma)

TypeError: cannot set properties of null (setting 'innerHtml')
I have created a simple angular service that initializes the inner html of a div tag in the main component of my angular project and is called in multiple components. When I run my tests I get the above karma error. I assume this is because the component is not created in the service.spec.ts file. I have checked and the class is defined in the main html file.
service.ts function:
onCLick(value: string): void {
document.querySelector('div.class').innerHtml = value;
}
service.spec.ts:
descirbe('ClickedService', () => {
let service: ClickedService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ClickedService);
});
to("#onClick should add to innerHtml", () => {
service.onClick('test value'); // error is here
});
});
welcome to the StackOverflow. To be honest, I wouldn't bother with fixing this unit test, because it looks like your approach to update the value is completely incorrect in the first place.
In Angular world, Service should provide you with values, and it can obtain them either from server via HTTP, or as a result of internal calculation. Then it's up to a Component, which is using this service, to deal with the value and display it if needed.
The reason why your test is not working is, that while creating the TestBed for the service, HTML is not expected and you are not providing any. Your querySelector within the service can't find the div you are looking for and you can't set innerHtml value to null.
If you really want to make this value and this logic available within the application, move it to a standalone component. You can then add it wherever you want, it will wrap the logic and it will prevent the repetition of the code. Don't use service with JS queries to update HTML, this will only lead to serious issues in the future.
Check this article about binding in angular to get better idea of what to do.

Angularjs simple meaning of module and Dependency

Hello I am New to angularjs
var app = angular.module('app', [])
app.controller('ProductController', ['$scope,$htttp', productController]);
Please correct me if i am wrong
This above two line is saying we have created the module name app
and we have controller called productController and have $scope, $http as a dependency.
App is starting point of our angularjs application.
Controller is basically for business logic.
what is the servieces ,Factory ?
what is Dependency Injection in simple words ?
I want to understand it more clearly as i study on angualar.js also. but unable to understand it clearly.
Please help to provide me some simple understanding on this
angular.module create module for application, and an application can one or more than one module. It's for modularity. so if you want to initialize core module. then you have to
var app=angular.module("core.module", [
/* Shared Modules */
"services",
"widgets",
"layout"])
So what it does it will initialize every module for application.so by using app object you can directly create common directive for all the modules and much more.
core.module is behaves like constructor and what you want to initialize will pass in "[]" during the call of constructor. it's up-to you.
You can call it as dependency.
In every module you can have different controllers but one thing always keep in mind whenever you add script file reference in html then do add child modules first then core otherwise it will give error. Module not defined.
Service and Factory pretty much they both are equivalent. Most important is to realize that both are singletons.
Factories are functions that return the object, while services are constructor functions of the object which are instantiated with the new keyword.
Services:
angular.module('app').service('abcservice', function() {
this.getdata= function() {};
});
Factory:
angular.module('app').factory('TestFactory', function() {
return {
someFunction: function() {}
};
});
when you want to use services and factory in our application then you have to pass it as argument/dependency in controller declaration.
app.controller(controllerId, ['$scope','abcservice','TestFactory']);
For much please refer Angular Tutorial

Creating AngularJS object that calls method on app load

I am wondering if there is a convention within AngularJS for creating an object that lives within the app module, but is not attached directly to the view in any way, but is called when the view has loaded and the app starts up. In particular, I am trying to write an object that dispatches messages to listening controllers when they come in from the server.
Currently, I have implemented this by creating a "Controller" that attaches to the view. It has a monitor() function that is called when the page loads, and then listens in a loop for any incoming messages. I call the monitor() function from within the loaded view, by setting the ng-controller like so:
<div ng-controller="MyController">
{{ monitor() }}
</div>
This doesn't feel like the right thing to do. This "Controller" isn't interacting with the view in any way, so my gut tells me I am violating principles of AngularJS. But I haven't been able to turn up an easy solution that is endorsed by the AngularJS doc.
I am looking for a way to create an object that lives within the AngularJS world (in other words, it can use dependency injection to get access to services, and it can use $scope.$broadcast to send messages to other listening controllers), but that doesn't need to attach itself to the view in any way.
Ideally, I am looking for a way to say, "Here Angular, on startup, create this object, and run this method on it." Is there a way to do this?
You may use this as a starting point:
declaration of your object.
AngularJS: Service vs provider vs factory
myApp.factory('MessageBus', function() {
return {
listeners: [],
init: function() {
// do whatever you need at startup
},
pushMessage: function(msg) {
angular.forEach(this.listeners, function(listener) {
listener(msg);
});
},
subscribe: function(onMessageCallback) {
this.listeners.push(onMessageCallback);
}
};
});
calling a method on angular appilcation start
https://docs.angularjs.org/api/ng/type/angular.Module#run
myApp.run(function(MessageBus) {
MessageBus.init();
});
using this object within controllers
https://docs.angularjs.org/guide/di
myApp.controller('MessageCtrl', function($scope, MessageBus) {
$scope.messagesToShow = [];
MessageBus.subscribe(function(message) {
$scope.messagesToShow.push(message);
});
$scope.submitMessage = function(id, text) {
MessageBus.pushMessage({
type: 'TEXTMESSAGE',
id: id,
payload: text
});
};
});
Note that this is something to start with and nothing for any production code. For example the controller doesn't unsubscribe after being destroyed - if the page changes - and so you leak memory.
Don't use $broadcast-events for this
1: they are slow
2: if this MessageBus has a specific concern, than in should be an own object with a meaningfull name and api. Otherwise your $rootScope will be flooded with thousends of different events for different concerns when your application grows. A service is always easier to document and you have a clean dependency on that specific service. Only using events on the $rootScope hides this dependency from every developer reading and hopefully understanding your codebase,
Yeah you approach is really smelly. This function will be called every time a $apply/$digest invokes.
Maybe move the function into the run callback on the module.
var app = angular.module("YourApp", [//dependencies]);
app.run(function($YourUIService){
$YourUIService.monitor();
});
The run will be invoked, when your angularjs-module has loaded every dependency and is ready to run.
Didn't find the doc for this :/

How to clear view model in Durandal

Is there a way to selectively tell Durandal to reinitialize a view model. I am aware of the singleton vs new instance approaches to initialize view models.
//singleton since a declared object is returned
define(function() {
return { prop1: 1, prop2: 2 }
});
//new instance since a constructor is returned
define(function() {
var ctor = function(){};
return ctor;
});
I generally don't like to declare view models as singletons, but I have to do it in a special case due to sub routing which requires me to pass data from a parent router to my child router. However, the singleton has other side effects, so I was wondering: Is there a way to selectively request a new instance of the view model even if it was initially declared as a singleton?
Not that I know of - this is more of a limitation of requirejs versus Durandal, though. Once require has loaded the module, so far as I know it will always return you the same version of that module. Unless there's a way to tell require to reload the module?
The only thing I could think of would be to "reset" the view model during the activate method. If you're changing routes and finding that the activate method isn't being called, it may be because Durandal thinks that your module is already active (in which case it won't reactivate). You can change this behavior by customising the areSameItem function for the router (see this question for an explanation).
Hope that helps.
I don't know if it helps in your case, but you could use the activate() method in your view model. For more information see http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.

AngularJS DI for custom functions

I've been working a lot lately with AngularJS and I really like the dependency injection stuff. I'm using it all over the place, but just in Angular components, like controllers and the like.
This always calls on the angular object:
angular.module('app').controller(/**/);
Now I have this function:
var custom = function(MyService) {
// do stuff
};
I've declared the Service this way:
angular.module('app').factory('MyService', function($rootScope) {
return {
show: function() {
// do stuff
};
},
hide: function() {
// do stuff
}
};
});
I now want to use this service in my custom function. Is there a way of manually calling the angular DI container? (I couldn't find anything in the docs...)
I know that this works for controllers not defined with the angular.module()... thing:
function Controller(MyService) {
MyService.hide(); // works
}
But how to use it outside of AngularJS components, in completely independent functions?
Or do I have to take a completely different path to achieve my goal?
Cheers and thanks in advanced,
Christian
angularjs is pretty neat in the fact that it exposes its own internal services to the user. The service you're looking for is $injector
What you want to use to call your custom function is $injector.invoke(myCustomFunction,thisForCustomFunction,{named:locals});
If you are by chance wanting to invoke this function outside of angular you'll have to get the applications injector.
var injector = angular.element(elementWithNgApp).injector();
Note:
The invocation time may be reduced though. As the injector must annotate (find what services you need) using several regular expressions. The reason this is not an issue throughout angular is because it is done once. Because services,factories,providers, are all instantiated (newed) singletons that provide closure with the services and whatever uses the services inside your providers
To prevent this extra step you can provide a $inject property on your function.. Something like this:
myfunction.$inject = ['ServiceA','ServiceB'];
function myfunction(a,b){
//a is ServiceA
//b is ServiceB
};

Categories

Resources