I have an angularjs service which calculates a products price based on discounts, quantity, etc. I'm trying to write a jasmine test to call this service, passing in test data. I get an error that the app is missing it's dependencies. I don't want to have to load Ui router, shouldn't mocks take care of that?
Error: [$injector:nomod] Module 'ui.router' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Here is my Jasmine SpecRunner.html. The Web project I am testing is in a different project than my Jasmine test project.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Jasmine Spec Runner v2.0.0</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css">
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>
<script src="http://localhost:54411/Scripts/vendor/angular.js"></script>
<script src="http://localhost:54411/Scripts/vendor/angular-mocks.js"></script>
<script src="http://localhost:54411/Scripts/app.js"></script>
<!-- include source files here... -->
<script src="http://localhost:54411/Scripts/services/productPriceCalculatorSvc.js"></script>
<!-- include spec files here... -->
<script src="spec/ProductPriceCalculatorSpec.js"></script>
</head>
<body>
</body>
</html>
The spec file:
describe("Product Price Calculator service test", function () {
describe("when I call product price calculator.calculateCustomerDiscPrice", function () {
var sut;
beforeEach(function() {
module('quoteMasterApp');
inject(function(productPriceCalculatorSvc) {
sut = productPriceCalculatorSvc;
});
});
it('can calculate customer discount price', function() {
productPriceCalculatorSvc.calculateCustomerDiscPrice(null, null);
});
});
});
Here is my service declaration.
myApp.service("productPriceCalculatorSvc", [
function() {
return {
calculateCustomerDiscPrice: function(product, conversionRate) {
// calculations occur here
});
}
}
}
])
You need to tell the framework how to find your service.
Something like:
describe("ProductPriceCalculator", function () {
var productPriceCalculatorSvc;
beforeEach(function() {
module('productPriceCalculatorSvcFactory');
});
beforeEach(inject(function ($injector) {
productPriceCalculatorSvc = $injector.get('productPriceCalculatorSvcFactory');
}));
it('can calculate customer discount price', function () {
productPriceCalculatorSvc.calculateCustomerDiscPrice();
});
});
See more here: https://docs.angularjs.org/api/auto/service/$injector
I have the same case : test a controller that calls a service.
Here is what I have in my decribe
describe('Controller Test', function () {
var scope, ctrl;
// load your controllers including myController
beforeEach(module('mycontrollers'));
// load your services (do not forget to include them in your spec runner !)
// there should be myService defined in this module
beforeEach(module('myservices'));
// It seems a good practice to inject nothing in "it"
// Everything is injected in beforeEach
// here the controller uses theService and set some value in the scope
beforeEach(inject(function($rootScope, $controller, ReportProductService) {
scope = $rootScope.$new();
ctrl = $controller('myController', {
$scope: scope,
theService : myService
});
}));
it('should work', function () {
//verify that the controller is there
expect(ctrl).toBeDefined();
// do test
expect(scope.someExpectedValueSetByController).toBeDefined();
});
I can do a JSFiddle if you want
I can also make a better answer as my service is using $http
Please comment if you don't agree or find a better way to do it
Related
I am trying to instantiate a Google Places Autocomplete input within an Angular 2 component. I use this code to do it:
loadGoogle() {
let autocomplete = new google.maps.places.Autocomplete((this.ref.nativeElement), { types: ['geocode'] });
let that = this
//add event listener to google autocomplete and capture address input
google.maps.event.addListener(autocomplete, 'place_changed', function() {
let place = autocomplete.getPlace();
that.place = place;
that.placesearch = jQuery('#pac-input').val();
});
autocomplete.addListener()
}
Normally, I believe, I would use the callback function provided by the Google API to ensure that it is loaded before this function runs, but I do not have access to it within a component's scope. I am able to load the autocomplete input 90% of the time, but on slower connections I sometimes error out with
google is not defined
Has anyone figured out how to ensure the Google API is loaded within a component before instantiating.
Not sure whether this will help, but I just use a simple script tag in my index.html to load Google API and I never get any error. I believe you do the same as well. I post my codes here, hope it helps.
Note: I use Webpack to load other scripts, except for Google Map API.
<html>
<head>
<base href="/">
<title>Let's Go Holiday</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Google Map -->
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<your-key>&libraries=places"></script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
And then in your component:
...
declare var google: any;
export class SearchBoxComponent implements OnInit {
ngOnInit() {
// Initialize the search box and autocomplete
let searchBox: any = document.getElementById('search-box');
let options = {
types: [
// return only geocoding results, rather than business results.
'geocode',
],
componentRestrictions: { country: 'my' }
};
var autocomplete = new google.maps.places.Autocomplete(searchBox, options);
// Add listener to the place changed event
autocomplete.addListener('place_changed', () => {
let place = autocomplete.getPlace();
let lat = place.geometry.location.lat();
let lng = place.geometry.location.lng();
let address = place.formatted_address;
this.placeChanged(lat, lng, address);
});
}
...
}
I used it the same way as explained above but as per google page speed i was getting this suggestion,
Remove render-blocking JavaScript:
http://maps.googleapis.com/…es=geometry,places®ion=IN&language=en
So i changed my implementation,
<body>
<app-root></app-root>
<script src="http://maps.googleapis.com/maps/api/js?client=xxxxx2&libraries=geometry,places®ion=IN&language=en" async></script>
</body>
/* Now in my component.ts */
triggerGoogleBasedFn(){
let _this = this;
let interval = setInterval(() => {
if(window['google']){
_this.getPlaces();
clearInterval(interval);
}
},300)
}
You can do one more thing, emit events once the value(google) is received,& trigger your google task
inside them.
I have a use case where I have a component (like a database) that I would like to expose all the information as bindable properties. However, only a few of those properties will be need by any particular client who uses it. There could be 1000's of entries in the database. How can I figure out which ones are actually needed by the client.
For example:
Polymer('database,
{
observer : {
name : function(oldVal, newVal) { onDataChanged('name', newVal);},
addr : function(oldVal, newVal) { onDataChanged('addr', newVal);},
tel.main : function(oldVal, newVal) { onDataChanged('tel.main',
etc....
}
});
In this case I would like to dynamically create observe handlers only for the data bindings that are actually needed on the fly.
If you are willing to have your clients extend your component to specify the desired database fields then you can dynamically create observers only for the fields they specify.
Example
Component
<link rel="import" href="../../webcomponents/bower_components/polymer/polymer.html">
<polymer-element name=demo-dynamicproperties >
<template>
<h2>dynamic properties</h2>
See the console for changes
</template>
<script>
Polymer({
// Validate that it is an attribute that is allowed
// For the example we will allow anything starting with validitem
isValidAttribute: function(name) {
return (name.match('^validitem'));
},
// Get attributes
created: function() {
for (name in this.publish) {
console.log("Trying: "+name);
// Verify that it is one of the dynamic attributes
if (this.isValidAttribute(name)) {
console.log("Accepting: "+name);
this[name]="Pull from DB";
// References:
// https://www.polymer-project.org/0.5/docs/polymer/node_bind.html
// https://github.com/Polymer/NodeBind/blob/master/tests/tests.js
// https://github.com/polymer/observe-js
var observer = new PathObserver(this,name);
observer.open(makeHandler(this,name));
}
}
/************* TEST **********************************/
// Verify that dynamic updates worked by updating
this.job('update_validitem1', function() {
this.validitem1="Updated after 10 seconds";
}, 10000);
/************ End Test ******************************/
}
});
// Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
function makeHandler(element, property) {
function handler(newval,oldval) {
console.log("element" + element,"property:" + property,"from:"+oldval,"to:"+newval);
}
return handler;
}
</script>
</polymer-element>
Usage
<!DOCTYPE HTML>
<html lang="en">
<head>
<script src="../../webcomponents/bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="import" href="../../webcomponents/bower_components/polymer/polymer.html">
<link rel="import" href="demo-dynamicproperties.html">
<title>demo-dynamicproperties test page</title>
</head>
<body>
<polymer-element name=demo-usedb extends="demo-dynamicproperties" attributes="validitem1 validitem2 invaliditem" noscript>
</polymer-element>
<h1>Demo</h1>
<template is="auto-binding">
<h2>Dynamic tag</h2>
<demo-usedb validitem1="{{item1choice2}}" item2="setthis"></demo-usedb>
<h2>Input</h2>
<input type="text" value="{{item1choice2}}">
<h3>Produces</h3>
{{item1choice2}}
</template>
</body>
</html>
It looks like the answer to the questions is that it cannot be done. There does not appear to be any hooks or events that a component can use to get notified when properties are bound (or attempted to be bound) to it. I filed a bug/enhancement request here
https://github.com/Polymer/polymer/issues/1303
to request that this feature be supported in the future.
Just started with Angular and trying to make this work.
I would like to display the name in the <p></p>, but it shows {{ name }}.
ASPX:
<html ng-app="myApp">
<head runat="server">
<script src="Assets/Vendor/angularjs-v1.2.28.js"></script>
<script src="App/app.js"></script>
<script src="App/controllers.js"></script>
</head>
<body ng-controller="myCtrl">
<p>{{ name }}</p>
</body>
</html>
app.js:
var app = angular.module('myApp','myCtrl');
controllers.js:
var controller = {};
controller.myCtrl = function ($scope) {
$scope.name = "abcd";
}
EDIT: I have changed the order of loading the script files and updated this query.
This is the error I see in console - Uncaught Error: [$injector:modulerr]
You've not correctly put the order of the scripts. Rather put app.js in front of controller.js. Now you're getting the error: var app is not defined.
[Addition]
Furthermore, you're trying to inject myCtrl which is no object. So changing var controller to var myCtrl would probably work.
Better would be to use something as described here: https://docs.angularjs.org/guide/controller
app.controller('myCtrl', ['$scope', function($scope) {
$scope.name = "abcd";
}]);
Few things here:
1 - myCtrl is not a dependent module. So, you don't need it when defining the myApp module:
angular.module('myApp',[]);
2 - Order of scripts, as described by #Highmastdon.
3 - When defining the controller, you can use the myApp module:
angular.module('myApp').controller('myCtrl', function($scope) {
$scope.name = 'abcd';
});
I registered my project and generated a browser key at https://code.google.com/apis/console/.
Now how do I use that key when using the Google Script Loader?
I've been doing this, which works (with or without the key parameter), but even after several weeks the API console shows no requests:
<script src=//www.google.com/jsapi?key=my_key"></script>
<script>
google.load('maps', '3.10', { other_params : 'libraries=places&sensor=false', callback : ... })
</script>
The key is useless for the jsapi, you must append it to other_params:
<script>
google.load('maps', '3', {
other_params: 'key=my_key',
callback: function(){}
});
</script>
When using charts/loader you have to do something like this:
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {
'packages':['geochart'],
// Note: you will need to get a mapsApiKey for your project.
// See: https://developers.google.com/chart/interactive/docs/basic_load_libs#load-settings
'mapsApiKey': 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY'
});
google.charts.setOnLoadCallback(drawRegionsMap);
...
</script>
Note the mapsApiKey property.
As described in https://developers.google.com/chart/interactive/docs/gallery/geochart
This is something I have been trying to figure out, but I am not sure exactly how to do it. I have a flex application that logs into facebook, but after that I can't access any of the facebook api. Right now I am using this HTML to log in:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<!-- Include support librarys first -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<script type="text/javascript" src="http://connect.facebook.net/en_US/all.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
//This example uses the javascript sdk to login before embedding the swf
var APP_ID = "[My App ID Here]";
var REDIRECT_URI = "http://apps.facebook.com/isotesthoskins/";
var PERMS = "publish_stream,offline_access"; //comma separated list of extended permissions
function init() {
FB.init({appId:APP_ID, status: true, cookie: true});
FB.getLoginStatus(handleLoginStatus);
}
function handleLoginStatus(response) {
if (response.session) { //Show the SWF
//A 'name' attribute with the same value as the 'id' is REQUIRED for Chrome/Mozilla browsers
swfobject.embedSWF("isotest.swf", "flashContent", "760", "500", "9.0", null, null, null, {name:"flashContent"});
} else { //ask the user to login
var params = window.location.toString().slice(window.location.toString().indexOf('?'));
top.location = 'https://graph.facebook.com/oauth/authorize?client_id='+APP_ID+'&scope='+PERMS+'&redirect_uri='+REDIRECT_URI+params;
}
}
$(init);
</script>
And everything logs in fine, but when I try this in the application after I am logged in, nothing happens.
Facebook.api("/me", function(response){
changeText.text = response.name;
});
I don't need to init because it was done by the javascript login, right? I might be wrong about that though.
Looks like you are calling the API using the Flex SDK.
That is not going to work, as the token is not shared between JS and Flex.
You should login on the Flex side or thunk into the JS to make the call.