How can we use jasmine, jasmine.ConsoleReporter, require.js and backbone together - requirejs

I'm struggling to get jasmine along with it's ConsoleReporter working within a backbone application using require.js. I have seen Check Backbone/requireJs project with Jasmine but that hardcodes the libraries (which is something that I'd prefer to avoid).
In my backbone application I have created test function (I'd prefer to keep it there to test interactions between models):
test = function () {
require(['js/test/run'], function () {});
}
and run.js (I get the console.log "should" fine, but don't get anything to do with the failed test):
define(["jasmine", "jasmineConsoleReporter"],
function (jasmine, ConsoleReporter) {
describe('hello', function () {
it('should be true', function () {
console.log('should');
expect(true).toEqual(true);
});
});
jasmine.getEnv().addReporter(new ConsoleReporter(console.log));
jasmine.getEnv().execute();
//return tests;
}
);
The shim for jasmine and jasmineConsoleReporter are:
jasmine: {
exports: "jasmine"
},
jasmineConsoleReporter: {
deps: ['jasmine'],
exports: "getJasmineRequireObj"
}
And the source for jasmineConsoleReporter can be found at https://github.com/pivotal/jasmine/blob/master/src/console/console.js
I'm guessing that the console reporter isn't being constructed correctly because I get the 'should' in the console and nothing else.

Try my setup:
https://github.com/wojciechszela/jasmine-requirejs-jscover
Adding backbone to it (or any other lib) should be easy.

Related

Mock WebAPI interface using ts-mockito

I'm writing a unit test for a class which uses browser WebAPI interface.
I use ts-mockito to mock the interface (a WebGL2RenderingContext in my case).
When I run the test, Node throws ReferenceError: WebGL2RenderingContext is not defined
which is understandable, because the test is run under NodeJS environment, not browser, so the class/interface doesn't exist.
Is there any way to make NodeJS environment aware of the WebAPI interfaces, so that it's possible to be mocked?
NOTE: Since it's a unit test, it should NOT be run on a real browser.
jsdom seems to be a possible solution, but I have no idea how to mock it with ts-mockito.
The following snippet illustrate what I'm trying to do:
import { mock, instance, verify } from 'ts-mockito'
// ========== CLASS ==========
class DummyClass {
dummyMethod() : void {}
}
class TestedClass {
static testDummy(dummy : DummyClass) : void {
dummy.dummyMethod();
}
static testGlCtx(glCtx : WebGL2RenderingContext) : void {
glCtx.flush();
}
}
// ========== TEST ==========
describe('DummyClass', () => {
// This test passed successfully
it('works fine', () => {
const mockDummy = mock(DummyClass);
TestedClass.testDummy( instance(mockDummy) );
verify( mockDummy.dummyMethod() ).once();
});
});
describe('WebGL interface', () => {
it('works fine', () => {
// This line failed with 'ReferenceError: WebGL2RenderingContext is not defined'
const mockGLCtx = mock(WebGL2RenderingContext);
TestedClass.testGlCtx( instance(mockGLCtx) );
verify( mockGLCtx.flush() ).once();
});
});
Run using mocha with the command mocha --require ts-node/register 'test.ts'.
There are two solutions: For common DOM APIs, and for generic mocking.
For common DOM APIs
As detailed in this StackOverflow answer, jsdom can be used to bring DOM APIs into NodeJS runtime environment.
Run npm install --save-dev jsdom global-jsdom
and change Mocha's command to
mocha --require ts-node/register --require global-jsdom/register 'test.ts'
NOTE: global-jsdom is the newer & updated version of jsdom-global.
This solution works for common DOM APIs (such as HTMLElement, SVGElement, File),
but it doesn't work for more specialized APIs (WebGL, Crypto, audio & video).
For generic interface mocking
Turns out ts-mockito has a way to mock interfaces, including DOM & any browser Web APIs.
So the above test code can be changed to:
describe('WebGL interface', () => {
it('works fine', () => {
const mockGLCtx = mock<WebGL2RenderingContext>();
TestedClass.testGlCtx( instance(mockGLCtx) );
verify( mockGLCtx.flush() ).once();
});
});
and the test will run successfully.

Can't figure out where to put require.config when using TypeScript, RequireJs, and Jasmine

I've been following the pattern for setting up TypeScript, RequireJS, and Jasmine that Steve Fenton describes here:
https://www.stevefenton.co.uk/Content/Blog/Date/201407/Blog/Combining-TypeScript-Jasmine-And-AMD-With-RequireJS/
That pattern as really worked well and truly unblocked me (yay!), but I'm now at the point where I need to customize some settings for RequireJS but I can't seem to figure out where to put my require.config call. Everywhere I've tried has caused breaks and regressions. Here are the two approaches that seem most logical/promising
In SpecRunner.cshtml
<script data-main="/Scripts/TypeScript/RequireJsConfig" src="/Scripts/require.js"></script>
In RequireJsConfig.ts
require.config({
baseUrl: "../Scripts",
paths: {
jquery: "../jquery-2.1.3"
}
});
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Attempt 1: When I try it this way I immediately get this error
//
// JavaScript runtime error: Object doesn't support property or method 'config'
//
import TestLoader = require("Tests/TestLoader");
TestLoader.Run();
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Attempt 2: When I try it this way, everything builds and runs without errors, but
// Jasmine doesn't find any of the tests. All I get is "No specs found" even
// though I see the breakpoints on my "it" statements getting hit.
//
require(["Tests/TestLoader"], (testLoader) => {
testLoader.Run();
});
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
jasmine.getEnv().execute();
In TestLoader.ts
import GuidHelperTests = require("Tests/T3/Helpers/GuidHelperTests");
import ObjectHelperTests = require("Tests/T3/Helpers/ObjectHelperTests");
class TestLoader {
public static Run: () => void = () => {
GuidHelperTests.Run();
ObjectHelperTests.Run();
}
}
export var Run = () => TestLoader.Run();
In GuidHelperTests.ts
import T3 = require("T3/T3Lib");
export var Run = () => {
describe("GuidHelper tests", () => {
it("GUID validator validates good GUID", () => {
// etc. ...
My guess is that Attempt 2 doesn't work because of some kind of sequencing issue where the test discovery process is happening before modules are loaded, or something like that. I'm just not versed enough in RequireJS to know what my options are here.
I prefer to keep my configuration away from my application - you can pre-register the configuration like this, and it will be picked up by RequireJS when it loads. No need to add it to your first file.
<script>
var require = {
baseUrl: "../Scripts",
paths: {
jquery: "../jquery-2.1.3"
}
};
</script>
<script data-main="/Scripts/TypeScript/RequireJsConfig" src="/Scripts/require.js"></script>

Can Blanket.js work with Jasmine tests if the tests themselves are loaded with RequireJS?

We've been using Jasmine and RequireJS successfully together for unit testing, and are now looking to add code coverage, and I've been investigating Blanket.js for that purpose. I know that it nominally supports Jasmine and RequireJS, and I'm able to successfully use the "jasmine-requirejs" runner on GitHub, but this runner is using a slightly different approach than our model -- namely, it loads the test specs using a script tag in runner.html, whereas our approach has been to load the specs through RequireJS, like the following (which is the callback for a requirejs call in our runner):
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.TrivialReporter();
var jUnitReporter = new jasmine.JUnitXmlReporter('../JasmineTests/');
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.addReporter(jUnitReporter);
jasmineEnv.specFilter = function (spec) {
return htmlReporter.specFilter(spec);
};
var specs = [];
specs.push('spec/models/MyModel');
specs.push('spec/views/MyModelView');
$(function () {
require(specs, function () {
jasmineEnv.execute();
});
});
This approach works fine for simply doing unit testing, if I don't have blanket or jasmine-blanket as dependencies for the function above. If I add them (with require.config paths and shim), I can verify that they're successfully fetched, but all that appears to happen is that I get jasmine-blanket's overload of jasmine.getEnv().execute, which simply prints "waiting for blanket..." to the console. Nothing is triggering the tests themselves to be run anymore.
I do know that in our approach there's no way to provide the usual data-cover attributes, since RequireJS is doing the script loading rather than script tags, but I would have expected in this case that Blanket would at least calculate coverage for everything, not nothing. Is there a non-attribute-based way to specify the coverage pattern, and is there something else I need to do to trigger the actual test execution once jasmine-blanket is in the mix? Can Blanket be made to work with RequireJS loading the test specs?
I have gotten this working by requiring blanket-jasmine then setting the options
require.config({
paths: {
'jasmine': '...',
'jasmine-html': '...',
'blanket-jasmine': '...',
},
shim: {
'jasmine': {
exports: 'jasmine'
},
'jasmine-html': {
exports: 'jasmine',
deps: ['jasmine']
},
'blanket-jasmine': {
exports: 'blanket',
deps: ['jasmine']
}
}
});
require([
'blanket-jasmine',
'jasmine-html',
], function (blanket, jasmine) {
blanket.options('filter', '...'); // data-cover-only
blanket.options('branchTracking', true); // one of the data-cover-flags
require(['myspec'], function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 250;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function (spec) {
return htmlReporter.specFilter(spec);
};
jasmineEnv.addReporter(new jasmine.BlanketReporter());
jasmineEnv.currentRunner().execute();
});
});
The key lines are the addition of the BlanketReporter and the currentRunner execute. Blanket jasmine adapter overrides jasmine.execute with a no-op that just logs a line, because it needs to halt the execution until it is ready to begin after it has instrumented the code.
Typically the BlanketReport and currentRunner execute would be done by the blanket jasmine adapter but if you load blanket-jasmine itself in require, the event for starting blanket test runner will not get fired as subscribes to the window.load event (which by the point blanket-jasmine is loaded has already fired) therefore we need to add the report and execute the "currentRunner" as it would usually execute itself.
This should probably be raised as a bug, but for now this workaround works well.

RequireJS plugin, load files on demand

I have RequireJS implemented fine, and a Grunt based build process which is optimizing the all the JS files app into one file via r.js which is also working fine. All my app files are concatenated into one big JS file for efficient production deployment.
Now I'm having the following requirements:
I need to write a plugin for requirejs, that will not load(not include the file) into the optimized file in the build process, but will required on demand:
Meaning in my code I'll have:
var myObj = require("myplugIn!jsFile");
So in the end when this line runs, it will runs in 2 options:
on build process, the file is not included in the optimized file
The application is running, it will be request the file on demand.
I wrote the following plugin, but is not working:
define(function () {
"use strict";
return {
load : function (name, req, onload, config) {
// we go inside here we are running the application not in build process
if (!config.isBuild) {
req([name], function () {
onload(arguments[0]);
});
}
}
};
});
What I'm missing here.
In your build configuration you can exclude files that you don't want to bundle. They will still be loaded on demand when needed. You may also do something like this:
define(function (){
// module code...
if (condition){
require(['mymodule'], function () {
// execute when mymodule has loaded.
});
}
}):
This way mymodule will be loaded only if condition is met. And only once, if you use same module dependency elsewhere it will return loaded module.
It was more simpler that I though, if helps someone, I'm posting the solution, I create a plugin , that in build process return nothing and in run time, returns the required file, hope helps someone.
define(function () {
"use strict";
return {
load : function (name, req, onload, config) {
if (config.isBuild) {
onload(null);
} else {
req([name], function () {
onload(arguments[0]);
});
}
}
};
});

Crossroads and requirejs

I'm having trouble getting a route to match using crossroads with requirejs. Well, it probably has nothing to do with requirejs, just thought I'd mention it.
This is what my code looks like:
require.config({
shim: {
/* use shims to define dependencies for modules. e.g.,
'jquery.colorize': ['jquery'],
'jquery.scroll': ['jquery'],
*/
'crossroads': ['signals', 'can']
},
paths: {
"jquery": "http://code.jquery.com/jquery-1.8.2",
"can": "/scripts/can/amd/can",
"can.fixture": "/scripts/can/amd/can/util/fixture",
"signals": "/scripts/signals/signals",
"crossroads": "/scripts/crossroads/crossroads"
}
});
require(['jquery', 'crossroads', 'controllers/project'], function ($, crossroads, projectController) {
var projectRoute = crossroads.addRoute('projects', function () {
$(document).ready(function () {
console.log('projects ready');
$.when(ProjectModel.findAll()).then(function (projectResponse) {
var projects = new SortList(projectResponse);
console.log('doc ready projects=', projects);
new ProjectsControl('#projects', {
projects: projects,
defaultSort: 'priority'
});
});
});
})
console.log('matched: ', projectRoute.match(window.location.href));
});
The url that it's trying to match is http://localhost:34382/projects and the output of console.log is "matched: false"
Any suggestions?
I figured out that I need to change what I'm matching on. I've changed this line
console.log('matched: ', projectRoute.match(window.location.href));
to this
console.log('matched: ', projectRoute.match(window.location.pathname + window.location.search));
and now it works.
Also realized that you have to explicitly call crossroads.parse() somewhere for it to work (or am I missing something?)
so at the end of my require function I call this and my route is found
crossroads.parse(window.location.pathname + window.location.search);
Also, in case anyone is wondering, I'm using CanJS as the client-side MVC framework and ASP.NET MVC4 as the server-side.

Resources