I'm testing my Backbone Marionette modules. Before each test I want to setup a module on the application, and in the teardown process want to remove it completely.
I found only how to stop a module, but it is not enough, it doesnt remove event listeners, module object on application etc. And besides invoking the module definition again if more test cases exist, it doesnt create a new module, just applies it to the existing.
var application = new Backbone.Marionette.Application();
beforeEach(function() {
application.module('MyModule', function() {
// module definition goes here
})
});
afterEach(function() {
// i want something like this
application.remove(MyModule)
});
// assume you know the name of the module
delete application['MyModule'];
// otherwise
var module = application.module('awesomeModule', function(){});
delete application[module.moduleName];
MyModuleYou could define it after and just start and stop when on before and after each methods.
var application = new Backbone.Marionette.Application();
application.module('MyModule', function() {
// module definition goes here
})
beforeEach(function() {
application .module('MyModule').start();
});
afterEach(function() {
application .module('MyModule').stop();
});
Related
I have some helper functions, which I need several times at providers (outisde $get) and at config phase. When I don't want to write it at global scope I see two solutions:
A own helper provider:
app.provider('helper', function() {
this.help = function() {
// helper functionality
}
this.$get = function() {};
});
app.config(function(helperProvider) {
helperProvider.help();
});
The helper as constant:
app.constant('help', function() {
// helper functionality
});
app.config(function(help) {
help();
});
I don't like the provider solution, because I always have to use the suffix provider when I want to use it and I need an unused $get method.
So I uses the constant solution at the moment.
But now I have a new problem: I have an additional helper which I needs at the helper. The angular.constant is not initialized with an injector.
When the other helper (let's name it metaHelper) is at an other module (in the exmaple helperModule), than I can use angular.injector:
app.constant('help', function() {
var metaHelper = angular.injector(['helperModule', 'ng']).get('metaHelper');
metaHelper();
});
That I do at the moment but it feels wrong. And what is, when I have at the future "metaHelpers" in the same module? What is the best practice for provider/config helper at angular?
Thanks
I'm trying to avoid global scope for the following problem, but cannot see a way to avoid it.
I have a singleton Javascript object called "Application". This is an AMD module.
I then have many "Modules" (not to be confused with AMD modules) which are just javascript objects that I'd like to register with the "Application" instance.
For example:
require(['Application'], function(app) {
var module = {
name: "theme-switcher",
start: function() { console.log("started") } }
app.registerModule(module)
}
The architecture I am going for, is i'd like for each "Module" on the page to register itself with the "Application" instance.
Here is the tricky part: Only once ALL modules have been registered with the application, do I then want the "Application" instance, to loop through those registered modules and call their "Start()" methods.
The way I thought to do this was to just add another require block at the bottom of the page like this:
<script type="text/javascript">
require(['Application'], function (app) {
// start all the registered modules running.
app.start(() => {
// this could be a call back function once all modules started..
});
});
Niavely thinking, that just because this require call was last, that it would allways be executed last. But actually, sometimes this gets fired BEFORE the require calls above - so the Application attempts to Start() all registered modules BEFORE the modules themselves have all registered themselves with the Application.
No matter how I think about this problem, I am often led back to the fact that I need to keep some state in global scope, Something like this:
Module 1:
var app = Application.Instance;
var moduleStart = function(){
require(['jquery'], function(jquery) {
// do module goodness here.
}};
app.registerModule({name: "theme-switcher", start: moduleStart })
// later on in page - some other widget
// Module 2
var app = Application.Instance;
var moduleStart = function(){
require(['jquery'], function(jquery) {
// do second module goodness here.
}};
app.registerModule({name: "foo", start: moduleStart })
And then at the bottom of the page,
var app = Application.Instance;
app.Start(); // loops through registered modules calling start() method.
Surely there must be a way to do this avoiding global scope?
The reason for me wanting to do this, is that I want the "Application" to manage the lifecycle for registered modules on the page - including starting / pausing / stopping them etc. I'd also like the Application to publish an event once ALL modules gave been started - as this is when I would typically stop displaying my "loading" animation, and actually display the DOM - as modules will often manipulate the DOM in their start() methods and I don't want the page to be visible before everything is started.
This will do it. If you make every object you are wanting an AMD module, which I think you should be doing if you have RequireJS in place anyway, then you'll just need an array of strings defining those AMD module names to pass as an argument to the app's init.
Application.js : -
define(function (require, exports, module) {
"use strict";
var modules = {};
var moduleNames = [];
var numberOfModules = 0;
var loadedModules = 0;
exports.init = function (dependencies) {
numberOfModules = dependencies.length;
for (var i = 0; i < numberOfModules; i++){
var name = dependencies[i];
moduleNames.push(name);
require([name], function (moduleRef) {
loadedModules++;
modules[name] = moduleRef;
if (numberOfModules === loadedModules) {
exports.start();
}
});
}
};
exports.start = function () {
// all modules available
// use modules.myModuleName to access the module.
modules.myModuleName.functionName();
// or if they all have start() function and it needs calling
for (var i = 0; i < moduleNames.length; i++) {
modules[moduleNames[i]].start();
}
};
});
USAGE Depending on how you are loading your app, assuming you have an Application reference somewhere, just call:
// names of modules RequireJS uses to require, can be changed for each page.
var dependencies = ['moduleOne', 'moduleTwo', 'myModuleName'];
app.init(dependencies);
CodePen of this code, slightly altered to work on one page... http://codepen.io/owenayres/pen/MyMJYa
I'm already some time in the development using AngularJS, and what I do write works, but know I've come to a point where I would like to run unit tests on my AngularJS code.
I have created a very simple service that will inject a stylesheet onto the page,
see the example code below:
var OfficeSuiteStylesheetInjectorModule = angular.module('OfficeSuiteStylesheetInjectorModule', []);
OfficeSuiteStylesheetInjectorModule.factory('stylesheetInjectorService', ['$q', function($q) {
// Returns the service itself.
return {
inject: function(id, uri) {
var deferred = $q.defer();
// Embed the stylesheet into the page, but only when it's non-existing.
if (!angular.element('#' + id).length) {
var link = StylesheetFactory.Create(id, uri);
{
link.onload = deferred.resolve;
angular.element('head').append(link);
}
return deferred.promise;
}
}
}
}]);
It's not a big service, it's just dependend on $q for promises so that I can run additional logic when the stylesheet has been embedded in the page.
Now, I'm using Jasmine (I'm quite new to this) for testing my JavaScript code and I would like to test this module.
I have a skeleton:
// Tests for the angular 'StylesheetInjectorService'.
describe('StylesheetInjectorService', function() {
var stylesheetInjectorService = {};
// This code is executed before every test.
beforeEach(function() {
// Tells jamine that the module we're working on is the 'OfficeSuiteStylesheetInjectorModule'.
angular.module('OfficeSuiteStylesheetInjectorModule');
});
// Ensures that it injects a stylesheet element into the page.
it('Should inject a stylesheet element into the page.', function() {
// How to test here that the stylesheet is injected?
});
});
});
How can I inject te service in the page and ensures that the stylesheet is loaded?
Edit: Loading service now works:
beforeEach(module('OfficeSuiteStylesheetInjectorModule'));
// This code is executed before every test.
beforeEach(function() {
// Inject the required dependencies into the page.
inject(function($injector) {
stylesheetInjectorService = $injector.get('stylesheetInjectorService');
});
});
The same question is still open however. How to test if a stylesheet was embedded in the page?
Any help is highly appreciated.
Kind regards
To write a spec for the attachment of a stylesheet to angular.element('head') I would change the logic a bit to attach it to $document.head.
If you dont want to do that, I would recommend that you change your service into a directive seeing as how injecting a script element, is manipulating the DOM. That way you would kill two birds with one stone, as you would need to inject $compile to test your directive (which would enable you to $compile a custom head element to boot). But this is slightly "over the top" for now.
Implementation:
if (!angular.element('#' + id).length) {
var link = StylesheetFactory.Create(id, uri);
link.onload = deferred.resolve;
$document.head.append(link);
return deferred.promise;
}
}
beforeEach:
/**
* Sorry, this was previously $location which was just
* such a silly mistake.
*/
var $timeout;
beforeEach(function () {
inject(function ($injector) {
$timeout = $injector.get('$timeout');
});
});
it:
it('attaches the stylesheet to $document.head', function () {
styleSheetInjectorService.inject('someId', '/path/to/some/stylesheet');
$timeout.flush(); // flush promises
expect(angular.element($document.head).lastChild[0].nodeName).to.eq('LINK');
});
Something along those lines should get you up and running. Bare in mind that the spec I wrote uses the chai#expect style assertions, and the mocha test framework. Edit the syntax to fit Jasmine if you mean to copy-paste.
I had this doubt a while ago.
To inject your controllers and services into your tests you need to use a tool called Angular Mocks. Here's some official reference about it.
I sugest you use it together with the Karma enviroment.
Here's a great Getting Started tutorial:
https://www.airpair.com/angularjs/posts/unit-testing-angularjs-applications
This other one is for the Ionic Framework, but can still aplly to your case
Hope it can help.
I'm using a hybrid version of mocha, ti-mocha, to build unit test for a Titanium SDK-based app. I'm completely new to BDD and mocha, so the API learning curve is quite steep. Here's my issue with a stripped-down test harness. I can access the index.js module, its methods and properties. However, I don't know how to access the named functions, in this case doClick(), within that module.
I'm using mocha + should.js. In the test harness below, context "index" passes, but context "doClick" fails. I'm so new to this API, that I'm not sure if I even framed the question properly. How do I access the functions within the module?
index-mocha.js
// creates the "mocha" global necessary to run a test suite anywhere in your app
var should = require('should');
module.exports = function(index) {
// create the test suite
describe('mochatest', function() {
context('index', function() {
it('index exists', function() {
should.exist(index);
});
it('index.open function', function() {
should(index.open).be.a.Function;
});
it('id = index', function() {
index.id.should.equal('index');
});
});
context('doClick', function() {
it('doClick exists', function() {
should.exist(index.doClick);
// return;
});
it('doClick is a function', function() {
should(index.doClick).be.a.Function;
});
});
});
var outputFile = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory, 'results.json');
outputFile.createFile();
mocha.setup({
reporter: 'ti-spec', // the reporter to use with your tests
outputFile: outputFile, // write results to the given Ti.Filesystem.File file
quiet: false // if true, suppress all console logging
});
// run the tests
mocha.run();
};
index.js
function doClick(e) {
alert($.label.text);
}
if(runTests){
require('ti-mocha');
$.index.addEventListener('open', function(){
require('index-mocha')($.index);
});
}
$.index.open();
Nice to see someone playing around with testing in Titanium :)
Just to clarify a thing, the variable $ refers to an instance of your current controller. Also, Alloy gives you references to view elements for which you have defined an id via this variable; This may be seen as a little sugar as all those views are accessible through $.getViews().
Therefore, all functions defined inside your controller file are accessible only from within that controller. If you want them to be accessible from the outside, the easiest and cleanest way is to exports them.
This can be done in two ways:
By directly add them to the controller object
$.doClick = doClick;
By using the exports variable
exports.doClick = doClick;
The result will be exactly the same as, during the compilation, Alloy will merge the exports variable (which is, initially, only an empty object) with your controller a.k.a $.
Then, just pass your controller through your require instead of the index view, to have access to both views and newly added listeners.
If I have a module like this:
define([
'app'
, 'text!index.html!strip'
, 'css!index'
],
function (App, source) {
var response = {};
App.newMethod = function (foo) {
console.log("foo ="+foo);
};
// return response object
return response;
}
);
I'm wondering how to add methods to a module that is used as a dependency in another module. Sure I can add methods to the object, but will these also update the App object when it is called from another module?
Question:
Is there a way to add methods to a module, which is loaded as a dependency and have these methods available on all modules, which require this dependency?
Short answer:
Yes. The module needs to be an object/instance (not a class) and it will work with requirejs.
Long answer:
When you require a module as a dependence for the first time Requirejs generates an object, and for the next times you requires the module Requirejs will return the object it generated the first time. So all the times you require a module you get always the same reference of the object.
With
define([], function () {
var app = {
//my methods.
};
return app;
});
and
define(['app'], function (app) {
app.newMethod = function (){
// ...
};
});
you can use app like this:
define(['app'], function (app) {
app.newMethod();
});
But injecting methods from one object to an other is a really bad practice. If you need something from an object just add it when creating the object, not by injection.
define([], function () {
var app = {
newMethod: function () {
// ...
},
// my methods.
};
return app;
});
For example if object A injects a new method that will be used in object B, but B is called when A is not loaded then there would be an error Object #<Object> has no method 'newMethod'