I’m writing jasmine tests for an ExtJS 4.1.1 client app. The server side code is Java. The client app doesn’t follow a strict MVC pattern; the models are separate but the view and controller code is co-mingled. The controllers don’t even inherit from Ext.app.controller. (Don’t blame me, I didn’t write it).
I’m able to test the Ext.app.Application object but keep getting null whenever I try to create a custom ExtJS layout object inside the Jasmine spec.
It's probably a basic error in my test ExtJS app or jasmine spec but I'm very new to ExtJS and can't pin point it.
Any help is really appreciated.
File structure:
/myapp (java code)
...
/webclient
/assets
/extjs-4.1.1
/myapp (same name as top level java code)
myapp.js
myapp-ui.js
/myapp-test
runspec.html
/lib
/jasmine-2.3.4
/specs
uispecs.js
/ui
/layouts
myappgrid.js
myapppanel.js
Here's the code for the ExtJS app (myapp-ui.js), Jasmine specs (uispecs.js), and spec runner (runspec.html)
myapp-ui.js
Ext.Loader.setConfig({
enabled : true,
paths : {
myapp : 'myapp'
}
});
var Application = null;
Ext.onReady(function() {
Application = Ext.create('Ext.app.Application', {
name: 'myappUI',
appFolder: 'myapp',
autoCreateViewport: true,
launch: function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.execute();
}
});
});
Here's the code for uispecs.js (Jasmine test spec)
describe("Basic Assumptions", function() {
var myLayout = null;
beforeEach( function() {
// This always returns null
myLayout = Ext.create('myappUI.ui.layouts.MyLayout');
});
// This test PASSES
it("myappUI is defined",function() {
expect('myappUI').toBeDefined();
});
// This test FAILS: myLayout is never created
it ('myLayout shouldn’t be null', function () {
expect ( myLayout != null).toBeTruthy();
});
});
Related
I'm new to Jasmine. I've written a simple code to execute in JS Fiddle which is working perfectly fine. But, when I include jasmine code, its not working. Am I missing anything here?
var app = angular.module('sortModule', [])
app.factory('sortFactory', function(){
var sortedColors = []
var shouldPush = true;
return {
sortColors: function(colorsArray){
var colorsOrder = [{color:'green'},{color:'yellow'},{color:'blue'},{color:'red'}]
for(color in colorsOrder) {
for(objColor in colorsArray) {
shouldPush = colorsOrder[color].color === colorsArray[objColor].color ? true : false
if(shouldPush) {sortedColors.push(colorsArray[objColor]);}
}
}
return sortedColors;
}
}
});
app.controller('sortController', function($scope,sortFactory){
$scope.colorsArray = [{id: '1',color: 'red',code : '#ff0000'},{id: '2',color: 'blue',code : '#0000ff'},{id: '3',color: 'red',code : '#ff0000'},{id: '4',color: 'yellow',code : '#ffff00'},{id: '5',color: 'green',code : '#00ff00'}];
$scope.sortedColors = sortFactory.sortColors($scope.colorsArray)
});
describe('colors', function () {
beforeEach(module('sortModule'));
it('can get the actual sorted ordered colors list', inject(function(sortFactory) {
expect(sortFactory).toBeDefined();
}));
});
var NOT_IMPLEMENTED = undefined;
// load jasmine htmlReporter
(function() {
var env = jasmine.getEnv();
env.addReporter(new jasmine.HtmlReporter());
env.execute();
}());
Actual working fiddle without Jasmine
http://jsfiddle.net/SyedNizamChennai/kjuemhua/15/
New fiddle when Jasmine is included
https://jsfiddle.net/SyedNizamChennai/5d4f0hdL/3/
Test code contains module function
beforeEach(module('sortModule'));
This is function from ngMock module, you need include it as external resource.
https://jsfiddle.net/SyedNizamChennai/5d4f0hdL/3/
EDIT:
working example is here:
http://jsbin.com/lenogujesi/1/edit?html,output
(i used jsbin to better show dependencies and their order)
some notes:
if is jasmine loaded, angular mock expose function mock and others as global (see https://docs.angularjs.org/api/ngMock#function), otherwise function is undefined
angular and angular mock should have same version
I'm using the single-page app template from https://github.com/volojs/create-template
I tried to make a simple example below.
Problem
ModuleA.js is being loaded twice, once directly from main.js and again from simulator.js which depends also on that module. This is causing two different references to an object (which I thought would only be one, like a singleton). I thought requirejs would not load the same module twice. Being (relatively) new to JavaScript I realize this may be naivete on my part. I'm trying to follow the template.
Here's a simplified version that demonstrates the problem:
www/index.html
<head>
...
<script data-main="app" src="lib/require.js"></script>
</head>
...
www/app.js
// For any third party dependencies, like jQuery, place them in the lib folder.
// Configure loading modules from the lib directory,
// except for 'app' ones, which are in a sibling
// directory.
requirejs.config({
baseUrl: 'lib',
paths: {
app: '../app'
}
});
// Start loading the main app file. Put all of
// your application logic in there.
requirejs(['app/main']);
www/app/main.js
define(function (require) {
var simulator = require('./simulator.js');
var ModuleA = require('./ModuleA.js');
ModuleA.init();
ModuleA.displayList();
ModuleA.update("joe", 99);
ModuleA.displayList();
simulator.start(); // should display the same list
});
www/app/ModuleA.js
define(function () {
var theList = {};
console.log("loading ModuleA");
return {
displayList: function () {
console.log(Object.keys(theList));
},
init : function () {
theList["fred"] = 10;
},
update : function (k, v) {
theList[k] = v;
}
}
});
www/app/simulator.js
define(["./ModuleA"], function (ModuleA) {
return {
start: function () {
ModuleA.displayList();
}
};
});
console output:
loading ModuleA
loading ModuleA
["fred"]
["fred", "joe"]
[]
The empty [] displayed on the last line is (likely) the second copy of the list due to the second loading of ModuleA.
The problem is the module references are not consistent. In main.js it requires ./ModuleA.js whereas in simulator.js it defines ./ModuleA (without the .js filetype).
Making those references identical corrects the behavior such that the module is only loaded once.
I guess I mixed the styles because of the many examples on the web. It kind of seems like a bug that it works this way, but maybe it's a feature?
If you want to share an instantiated singleton object using requireJS, you can do something like this for ModuleA:
define(function () {
console.log("loading ModuleA");
function myList(){
this.theList = {}
}
myList.prototype.displayList = function () {
console.log(Object.keys(this.theList));
}
myList.prototype.init = function () {
this.theList["fred"] = 10;
}
myList.prototype.update = function (k, v) {
this.theList[k] = v;
}
return new myList();
});
We built a widget app with backbone and require.js. It works cool with one app instance on page. Now we have a new requirement. We need to run multiple widget instances on same page. Each of app will have its own configurations.
The following test code is not working as we expected:
for(var i=0;i<3;i++){
require([ "app" ], function(app) {
var bootstrap = {};
jQueryPB(function() {
app.testData = i;
app.startup();
});
});
}
I wonder how could I instantiate multiple apps and set different configs to them?
Project structure is similar to:
/main.js
require.config({
....
});
/*code to create multiple instances*/
require(["app"], function(app){
var instance = new app(color:"yellow");
var instance2 = new app(color:"red");
instance.render();
instance2.render();
/*want to create multiple instances here to same page*/
})
/*above code is not working, TypeError: app is not a constructor*/
/app.js
define([ "jQueryPB", "backbone", "underscore", "models/app", "views/app" ], function($jpb,
Backbone, _, appModel, appView) {
var appInfo = new appModel();
var app = new appView({
model : appInfo
});
return app;
});
/models/app.js
/views/app.js
/view/bags.js
/view/bag (it references app by var app = require("app") , so that it can access app.color)
/collection/bags
/model/bag
I use r.js to compile all js into one
node r.js -o build.js optimize=none
After main.js is fully downloaded, it would start to initialize different app instances.
=================================================updated code
cool. I tried it with similar way:
main.js
require(["app"], function(app){
var instance = new app({
testData : 1
});
instance.testData = "1";
instance.startup();
})
app.js
define([ "jQueryPB", "backbone", "underscore", "models/app", "views/app" ], function($jpb,
Backbone, _, appModel, appView) {
return function app(color) {
var appInfo = new appModel();
var app = new appView({
model : appInfo
});
console.log(">>"+color.testData);
app.testData = color.testData;
return app;
};
});
a problem is in bag.js, it needs to access the custom variable in app. I use var app = require("app"); console.log(app.testData); But the output is undefined. Is there a different way to access app instance?
In addition, if require("app"), will it cause a problem if there are multiple app instances?
The problem is, require being an asynchronous function, does not execute immediately, and when it finally does execute, the value of i will have changed. Ultimately, you will end up with all your instances sharing the same value of i, as the loop will have completed before the first require callback gets executed. See here.
You can solve this by creating a closure around each iteration of your loop. This way the original value of i is retained within the scope of the require callback.
for (var i = 0; i < 3; i++) {
(function (i) {
require(["app"], function (app) {
// etc
});
})(i);
}
I have a angularjs application, which I generated with yeoman. In the karma.conf.js is a reference to test/mock/**/*.js. I have troubles to find out, how I use this folder. Currently I have a simple Service:
'use strict';
angular.module('tvcalApp')
.factory('Series', function ($resource) {
return $resource('/search/:search');
});
and a Test
'use strict';
var $httpBackend;
describe('Service: Series', function () {
// load the service's module
beforeEach(module('tvcalApp'));
// instantiate service
var Series;
beforeEach(inject(function (_Series_) {
Series = _Series_;
}));
beforeEach(inject(function ($injector) {
var url_get = '/search/The%20Simpsons';
var response_get = [{"seriesid": "71663"}];
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET(url_get).respond(response_get);
}));
it('should return a list if search for The Simpsons', function () {
var res = Series.query({search: 'The Simpsons'});
$httpBackend.flush();
expect(res[0].seriesid === 71663);
});
});
This is working. But I wonder If I could use the mock folder from the karma.conf.js for the mocking function. Is it possible to move the mock part into the mock folder and use it for all unit test?
I could not find any example or documentation for this folder. Can someone please point me to to an example or documentation how to use the mock folder.
Basically i have done something like this looking at angular-mocks.js:
Let's say may app is called ql. and i have a loginService that i want to mock:
mocks/servicesMock.js looks like this:
'use strict';
var ql = {};
ql.mock = {};
ql.mock.$loginServiceMockProvider = function() {
this.$get = function() {
var $service = {
login: function() { }
};
return $service;
};
};
angular.module('qlMock', ['ng']).provider({
$loginServiceMock: ql.mock.$loginServiceMockProvider
});
Then in my tests i can injeck $loginServiceMock:
'use strict';
describe('LoginController tests', function () {
// load the controller's module
beforeEach(module('ql'));
// load our mocks module
beforeEach(angular.mock.module('qlMock'));
var loginController,
loginServiceMock,
scope;
// Initialize the controller and a mock scope
// $loginSericeMock will be injected from serviceMocks.js file
beforeEach(inject(function ($controller, $rootScope, $loginServiceMock) {
scope = $rootScope.$new();
loginServiceMock = $loginServiceMock;
loginController = $controller('LoginController', {
$scope: scope,
loginService: loginServiceMock
});
}));
});
The example by #gerasalus is useful, but to answer the question:
mocks is just a folder to put your code in to keep your project organized and the code in tests short and to the point. By keeping all your mocks in one place, it is easier to reuse them in tests... copying them from one test to another would be bad practice from a DRY perspective.
So, for example, you might have a service called 'Foo'
app/service/foo.js
Then you might create a mock of that service, called 'FooMock'
test/mocks/service/foo.js
And then you would create tests and inject whatever mocks you need, as is shown in gerasulus's answer.
Just in case it matters, I use ASP.NET 3.5 with VB.NET. I have nested MasterPages and UpdatePanels with Partial PostBacks. I include Modernizr 1.7 with YepNopeJs/IE Shim in my head section. Right before the closing body tag, I include my jQuery 1.6, jQuery UI 1.8.12, and this script.js I'm trying to build.
I'm thinking of using something like:
SITE = {
PAGES : { ... },
VARS : { ... },
HELPERS : { ... },
PLUGINS : { ... },
init : function() { ... }
};
SITE.init();
UPDATE
Ok with Levi's advice, I came up with this solution:
var SFAIC = {}; // Global namespace
SFAIC.common = { ... }; // Shared properties
SFAIC.common.fn = { ... }; // Shared functions
SFAIC.plugin = {
qtip: $.fn.qtip,
validate: $.fn.validate,
validator: $.fn.validator
};
SFAIC.init = function() { ... }; // Global initializer
$(document).ready(function() { SFAIC.init(); });
Then each page would have its own object literal like:
SFAIC.Main = {}; // Main.aspx
SFAIC.Main.someSection = { ... }; // Some Section's properties
SFAIC.Main.someSection.fn = { ... }; // Some Section's functions
SFAIC.Main.anotherSection = { ... }; // Another Section's properties
SFAIC.Main.anotherSection.fn = { ... }; // Another Section's functions
SFAIC.Main.init = function() { ... }; // Main.aspx's intializer
$(document).ready(function() { SFAIC.Main.init(); });
I recommend that you make a new object for section and a new function for each page/item.
However, the more scripts you add in this way, the harder it gets to manage the whole in an editor. Netbeans has a feature that lets you jump to parts of the object and helps manage this.
Example:
var lib = {}; // your library
//maybe you like the plural name plugins better. That's fine.
lib.plugin = {
//define plugins here
};
//maybe you like the plural name helpers better. That's fine too.
lib.helper = {
//define your helpers here
cycle: function() {
//code for the cycle plug-in
}
};
lib.account = {
//you could stick code that is general to all account pages here
};
lib.account.overview = function() {
//you could stick code that is specific to the account overview page here
//maybe you'd use the cycle plug-in to show their latest posts.
lib.plugin.cycle();
};
lib.account = {
//you could stick code that is general to all account pages here
};
lib.account.overview = function() {
//you could stick code that is specific to the account overview page here
//maybe you'd use the cycle plug-in to show their latest posts.
lib.plugin.cycle();
};
Then on the Account Overview page you'd call lib.account.overview().
For Production:
Use a package like closure, uglify or the one I mention at the end to package all your code into one file and send that.
For Development:
I would recommend for structure you use a asynchronous javascript loader like
require.js.
This means you have lot's of modules and you specifically state the dependancies.
For example you would have one main.js
// main.js
require([
"jquery.js",
"jquery.ui.js",
...
], function() {
// if the correct location is "mysite.com/foo/" then url will be "foo"
var url = window.location.pathname.match(/\/(\w+)\//)[1] || "mainpage";
require(url + ".js", function(pageObj) {
// ...
});
});
// foo.js
define({
pageStuff: ...
});
I recommend you read through the requireJS docs to understand their structuring system. It's one of the best I've found.
When it comes to optimising all javascript into one file you just use their builder. This should be part of your project deploy system.