How to stub javascript dependencies (jquery) in jasmine? - javascript

I have some legacy code on my hands which was written with/relies upon the following stack:
jquery 1.8.1
jquery lazyload 1.8.0
d3 v2
Before I change anything in the code, I figured I'd write tests for it, so I can make nothing brakes :).
I chose the jasmine test framework because I'm familiar with rspec
I'm running into some issues, because the code I want to write tests for relies on jquery to define some "constants" e.g:
var WIDTH = $(document).width();
I guess there is no way around stubbing.
Should I include jquery in jasmine and try to spec the document?
Or not include jquery in jasmine and stub $?
I fear I might be going down the wrong direction and would much appreciate some guidance (code snippets much appreciated). Thanks for helping a noob out!

I would include jQuery and mock the functions that it calls. In your example, I would do.
spyOn($.fn, 'width').andReturn(300); //Return a value that you expect to be used
Jasmine spies have a property calls that is an array of all the calls and one thing that I have done is examine the calls entries and you can check the calling object. That being a jQuery object it has the property selector which you can expect to be equal to document
expect($.fn.width.calls[0].object.selector).toEqual(document);
Though remember you are trying to test the expected behavior of the code, not that each step of the code is completed as it is written. Trying to test that certain lines exists will prevent you from easily refactoring.

Related

Jasmine spy "expect(xxx).notToHaveBeenCalled()

With great thanks to those that so swiftly rescued me. The essence of the problem was that I failed to realize I should be looking in documentation for "matchers", and looking in "expect" was not going to solve my problem.
I'm working with Jasmine 3.0 (and very new to it!) and trying to verify that a spy sees zero calls to a target under particular conditions. I thought I might use the count() behavior for this, but I'm struggling to work out what syntax I should use.
I've tried
spyOn(target, 'action').and.callThrough();
target.triggeringAction();
expect(target.action).count().toBe(0);
But Jasmine reports TypeError: expect(...).count is not a function.
I don't see any expect(...).toNotHaveBeenCalled() and I'm not sure where to look next.
You could use expect(target.action).toHaveBeenCalledTimes(0);
This could work as well expect(target.action).not.toHaveBeenCalled();

Is there a way to tell Google Closure Compiler to *NOT* inline my local functions?

Here's what I'm looking for:
I want to use the wonderful features of SIMPLE mode minification while disabling just one specific feature (disable local function inline).
UPDATE: The answer is NO, it's not possible given my setup. But for me there is a workaround given I am using Grails.
As #Chad has explained below, "This violates core assumptions of the compiler". See my UPDATE3 below for more info.
IN QUESTION FORM:
I'm using CompilationLevel.SIMPLE_OPTIMIZATIONS which does everything I want, except that it's inlining my local functions.
Is there any way around this? For example, is there a setting I can place in my JS files to tell Google Closure not to inline my local functions?
It would be cool to have some directives at the top of my javascript file such as:
// This is a JS comment...
// google.closure.compiler = [inlineLocalFunctions: false]
I'm developing a Grails app and using the Grails asset-pipeline plugin, which uses Google Closure Compiler (hereafter, Compiler). The plugin supports the different minification levels that Compiler supports via the Grails config grails.assets.minifyOptions. This allows for 'SIMPLE', 'ADVANCED', 'WHITESPACE_ONLY'.
AssetCompiler.groovy (asset-pipeline plugin) calls ClosureCompilerProcessor.process()
That eventually assigns SIMPLE_OPTIMIZATIONS on the CompilerOptions object. And by doing so, CompilerOptions.inlineLocalFunctions = true as a byproduct (this is hard coded behavior in Compiler). If I were to use WHITESPACE_ONLY the result would be inlineLocalFunctions=false.
So by using Asset Pipeline's 'SIMPLE' setting, local functions are being inlined and that is causing me trouble. Example: ExtJS ext-all-debug.js which uses lots of local functions.
SO post Is it possible to make Google Closure compiler *not* inline certain functions? provides some help. I can use its window['dontBlowMeAway'] = dontBlowMeAway trick to keep my functions from inlining. However I have LOTS of functions and I'm not about to manually do this for each one; nor would I want to write a script to do it for me. Creating a JS model and trying to identity local functions doesn't sound safe, fun nor fast.
The previous SO post directs the reader to https://developers.google.com/closure/compiler/docs/api-tutorial3#removal, where the window['bla'] trick is explained, and it works.
Wow thanks for reading this long.
Help? :-)
UPDATE1:
Okay. While spending all the effort in writing this question, I may have a trick that could work. Grails uses Groovy. Groovy makes method call interception easy using its MetaClass API.
I'm going to try intercepting the call to:
com.google.javascript.jscomp.Compiler.compile(
List<T1> externs, List<T2> inputs, CompilerOptions options)
My intercepting method will look like:
options.inlineLocalFunctions=false
// Then delegate call to the real compile() method
It's bed time so I'll have to try this later. Even so, it would be nice to solve this without a hack.
UPDATE2:
The response in a similar post (Is it possible to make Google Closure compiler *not* inline certain functions?) doesn't resolve my problem because of the large quantity of functions I need inlined. I've already explained this point.
Take the ExtJS file I cited above as an example of why the above similar SO post doesn't resolve my problem. Look at the raw code for ext-all-debug.js. Find the byAttribute() function. Then keep looking for the string "byAttribute" and you'll see that it is part of strings that are being defined. I am not familiar with this code, but I'm supposing that these string-based values of byAttribute are later being passed to JS's eval() function for execution. Compiler does not alter these values of byAttribute when it's part of a string. Once function byAttribute is inlined, attempts to call the function is no longer possible.
UPDATE3: I attempted two strategies to resolve this problem and both proved unsuccessful. However, I successfully implemented a workaround. My failed attempts:
Use Groovy method interception (Meta Object Protocol, aka MOP) to intercept com.google.javascript.jscomp.Compiler.compile().
Fork the closure-compiler.jar (make my own custom copy) and modify com.google.javascript.jscomp.applySafeCompilationOptions() by setting options.setInlineFunctions(Reach.NONE); instead of LOCAL.
Method interception doesn't work because Compiler.compile() is a Java class which is invoked by a Groovy class marked as #CompileStatic. That means Groovy's MOP is not used when process() calls Google's Compiler.compile(). Even ClosureCompilerProcessor.translateMinifyOptions() (Groovy code) can't be intercepted because the class is #CompileStatic. The only method that can be intercepted is ClosureCompilerProcessor.process().
Forking Google's closure-compiler.jar was my last ugly resort. But just like #Chad said below, simply inserting options.setInlineFunctions(Reach.NONE) in the right place didn't resurrect my inline JS functions names. I tried toggling other options such as setRemoveDeadCode=false to no avail. I realized what Chad said was right. I would end up flipping settings around and probably destroying how the minification works.
My solution: I pre-compressed ext-all-debug.js with UglifyJS and added them to my project. I could have named the files ext-all-debug.min.js to do it more cleanly but I didn't. Below are the settings I placed in my Grails Config.groovy:
grails.assets.minifyOptions = [
optimizationLevel: 'SIMPLE' // WHITESPACE_ONLY, SIMPLE or ADVANCED
]
grails.assets.minifyOptions.excludes = [
'**ext-all-debug.js',
'**ext-theme-neptune.js'
]
Done. Problem solved.
Keywords: minify, minification, uglify, UglifyJS, UglifyJS2
In this case, you would either need to make a custom build of the compiler or use the Java API.
However - disabling inlining is not enough to make this safe. Renaming and dead code elimination will also cause problems. This violates core assumptions of the compiler. This local function is ONLY referenced from within strings.
This code is only safe for the WHITESPACE_ONLY mode of the compiler.
Use the function constructor
var fnc = new Function("param1", "param2", "alert(param1+param2);");
Closure will leave the String literals alone.
See https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function

BreezeJS RequireJS support unstable (knockout example)

As I'm trying to compile my project with breeze and requirejs for production, I'm running into multiple issues. Unrolling this, I tried starting fresh with the Knockout RequireJS example.
After I installed MVS2013Pro I was able to run the project, but not without issues:
window.breeze is defined so it has been leaked to global scope (this
should not happen with RequireJS)
Hitting F5 Refresh will crash with
Uncaught Error: Can't find breeze and/or Q (breeze savequeuing)
Uncaught TypeError: undefined is not a function (breeze dataservice)
With these errors I'm not even trying to establish my grunt requirejs (r optimizer) task. As these are the root of my problems.
Note: I'm not searching for a knockout solution, my stack actually is: MongoDataService+Breeze+SaveQueueing+RequireJS+AngularJS+Socket.IO+Cordova
window.breeze
You're right that breeze sneaks back into the browser way down at the bottom of breeze.debug.js:
if (global.window) {
global.window.breeze = breeze;
}
We don't know why we put that in the code base. No one remembers. Coming at the end as it does, it clearly isn't needed by Breeze itself.
I commented it out in preliminary testing and that seems to do the trick. The window.breeze becomes undefined in the Todo-KO-Require sample; it remains defined (properly) in samples that do not use require.js.
We don't think this would be a breaking change for anyone ... and the remedy is simple enough: define it yourself at the top of your app.
We'll probably remove these lines for the next release unless we discover a reason to do otherwise.
F5 fail?
I can't repro this at all. I suspect your are conflating your discovery that breeze is inadvertently in the global namespace with your efforts to write an Angular app that uses breeze.savequeuing.js.
Why do I think this? Because the error you describe arises when trying to use breeze.savequeuing.js in an Angular app which is explicitly disallowed in the comments (line #14) of v.1.0.4:
Depends on Breeze (which it patches) and Q.js (not for use in Angular ... yet)
You no doubt omitted Q.js from your required libraries and it all goes to custard from there.
I'm personally allergic to the "save-when-user-changes-anything" user experience that made save queuing necessary. I'm almost sorry I wrote it. But there it is.
The apparently dependence on Q.js is the reason it isn't available for Angular apps as written. It's actually pretty easy to make it Angular-ready now because we recently (quietly) made accessible the component that Breeze is using for promises
breeze.Q // returns the promises component currently used by Breeze
Therefore, you could change the one reference to Q on line #104 to
var deferredSave = breeze.Q.defer();
This should work when using breeze v.1.4.14 and later. I haven't fully tested it yet.
This change (or something like it) likely will find its way into breeze.savequeuing.js v.1.1.0 (as I just pushed a non-release version of that).

Timing issues with overriding js functions across files

In my rails project, I've written a javascript function to override the default behaviour of an object in another defined in another file. The line that did this looked something like:
window.someObject.methodToOverride = function...
Initially this would get me a cannot set property 'methodToOverride' of undefined error, which I'm assuming was related to the a timing issue (with someObject not being setup yet). I was able to resolve the issue by chucking it in a jQuery $(document).ready function, but while that works, it seems a bit hacky to me.
Is there a better way to do this?
This sounds like you're looking for require.js. What it does is let you set up your code modularly, defining which module depends on which. In your case, once it's set up, by putting a wrapper such as
require(["someObject"], function (someObject) {
someObject.methodToOverride = function...
}
will make it so when you got to this function, require.js would dynamically load your someObject file, and when it was fully loaded, pass someObject as a parameter into the function you provided. It can work with much more complicated examples, with any level and any number of dependencies, loading them no more than once on an as-needed basis.
You can find a lot more information on require.js all over SO, e.g.:
simple example for using require.js
Require.js nested requires

The 'describe' keyword in javascript

So I am a newbie in javascript and i had been going through some one else's code and I found this..
describe('deviceready', function() {
it('should report that it fired', function() {
spyOn(app, 'report');
app.deviceready();
expect(app.report).toHaveBeenCalledWith('deviceready');
});
});
What I don't understand is:
What exactly does the describe keyword do?
info:
- Its a phonegap application
- We are using the spine.js and jQuery libraries
Describe is a function in the Jasmine testing framework. It simply describes the suite of test cases enumerated by the "it" functions.
Also used in the mochajs framework.
Describe is not part of Javascript, it is a function defined in the library you used (namely Jasmine)
According to the Jasmine Documentation
The describe function is for grouping related specs, typically each test file has one at the top level. The string parameter is for naming the collection of specs, and will be concatenated with specs to make a spec's full name.
The "describe" block is used to group tests together in jest.
Have a look at the following link. Go to the scoping section, You shall understand why and how it is been used.
https://jestjs.io/docs/setup-teardown
jest also has describe function.
https://jestjs.io/docs/api#describename-fn

Categories

Resources