Jasmine tests in Livescript: "it" keyword conflict - livescript

I'm trying to port some Jasmine tests from javascript to LiveScript. One of Jasmine's functions is it. Here's an example:
(function(){
describe("The selling page", function() {
// bla bla
it("should display cards.", function() {
expect(selectedCards.count()).toEqual(1);
});
});
So, I tried this in LiveScript:
do !->
describe "the selling page " !->
it "should display cards" !->
expect (selectedCards.count()).toEqual("")
Which compiles to this:
// Generated by LiveScript 1.3.1
(function(){
describe("the selling page ", function(it){ // <--- note the "it"
it("should display cards", function(){
expect(browser.getTitle().toEqual(""));
});
});
})();
You notice that the second function takes the "it" as an argument. I didn't find the livescript doc about this but I understand it's a feature (if I replace the it function by something else it's all fine. That makes me confident I'm not partially applying a function).
However, I need the it function and I don't want it as an argument. How can I write this snippet then ? Thanks !

You should use (...) in describe to avoid adding extra params
Like that:
do !->
describe "the selling page", (...) !->
it "should display cards" !->
expect (selectedCards.count()).toEqual("")

If you don't mind aliasing it for something else, you can also do this:
that = it
do !->
describe "the selling page " !->
that "should display cards" !->
expect selectedCards.count! .to-equal ''
When it is not in a function, it is treated as global it.

Related

How do unit test the rules of vuetify v-text field with jest?

For an vuetify v-text field which is having rules element for validation purpose. May I know how to do unit testing for those rules part using Jest unit test.
You need to call the validation method from your tests and then verify that the output is expected.
In your case, you'll mount the component you're testing, then call wrapper.vm.nameRules('value to test'). Save what returned from that function in a variable, then validate that the response is what you expected with an expect statement: expect(response).toEqual(true).
Been fighting with this a little bit; here is one solution, albeit somewhat undesirable...
Solution: Targeting the component validation rules directly, and supplying values and asserting the outcome.
Caveats: I don't fully like this approach as it requires accessing the rules by index, but it does get the job done.
If you wanted to play with this locally, I've included the code blocks below for ease of copy paste.
SomeComponent.vue
<template>
<h1>Hello</h1>
</template>
<script>
export default {
name: "SomeComponent",
data() {
return {
myRules: [
(value) => value > 5 || "It's less than 5, error",
(value) => value < 100 || "It's more than 100, error",
],
};
},
};
</script>
sometest.spec.js
import SomeComponent from "#/components/SomeComponent.vue"
describe("Sample tests", () => {
it('should execute rules', () => {
var rules = SomeComponent.data().myRules;
expect(rules[0](-1)).toBe("It's less than 5, error")
expect(rules[1](-1)).toBe(true)
})
});

Testing directives with template URL

I'm trying to test an angularJS directive that uses a templateURL. For the life of me I can't get the compiler to actually load the templateURL, even when it has been put into the templateCache. I realize karma preprocesses all the template contents and creates modules for each that preloads the templateCache, but I expected that this would have been equivalent.
Here is a http://jsfiddle.net/devshorts/cxN22/2/ that demonstrates whats going on.
angular.module("app", [])
.directive("test", function(){
return {
templateUrl:"some.html",
replace:true
}
});
//--- SPECS -------------------------
describe("template url test", function() {
var element, scope, manualCompiledElement;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller, $compile, $templateCache){
$templateCache.put("some.html", "<div>hello</div>");
scope = $rootScope.$new();
element = $compile(angular.element('<test></test>'))(scope);
manualCompiledElement = $compile(angular.element($templateCache.get('some.html')))(scope);
scope.$digest();
}));
it("has hello", function() {
expect(element.text()).toContain('hello');
});
it("template cache has contents", function(){
expect(manualCompiledElement.text()).toContain('hello');
});
});
What am I missing?
I realise you no longer necessarily need to know, but it looks to me like there are two problems contributing to this.
The first was pointed out by #Words-Like-Jared. You are defining the directive as restricted to attributes (default) but using it as an element. So you need restrict: 'E'.
The second problem is that your template is never actually retrieved and your compile/link never completes. The request for the contents of the template within the directive are asynchronous so a digest on the root scope is needed to resolve the promise and return them, similar to this answer for another question.
When you perform your manual compilation, the results are not asynchronous and the template is retrieved immediately. Actually the compilation in your manual compilation doesn't do a lot as you are compiling the contents of your template, which doesn't have any directives in.
Now at the end of your beforeEach where you use
$scope.$digest()
you are digesting on the current scope and its children. When you use $rootScope.$digest() or $scope.$apply() you will perform a digest across all scopes. So changing this to
$rootScope.$digest()
// or
$scope.$root.$digest()
in the line after your compilation means both of your tests will now pass. I have updated the fiddle here.
Here is a variation of the solution in coffeescript
expect = chai.expect;
app = angular.module('TM_App')
app.directive "test", ()->
templateUrl:"some.html",
replace :true
describe '| testing | templateUrl',->
element = null
beforeEach ->
module('TM_App')
beforeEach ->
inject ($compile,$rootScope, $templateCache)->
$templateCache.put "some.html", "<div>hello {{name}}</div>"
scope = $rootScope.$new();
element = $compile('<test/>')(scope);
scope.name = 'John'
scope.$digest()
it "has hello", ()->
expect(element.text() ).to.equal 'hello John'
expect(element[0].outerHTML).to.equal '<div class="ng-binding">hello John</div>'

Testing model binding in Backbone JS with Jasmine

I have a view that contains a model. The view listens for an event from the model and will perform an action once the event is triggered. Below is my code
window.Category = Backbone.Model.extend({})
window.notesDialog = Backbone.View.extend({
initialize: function() {
this.model.bind("notesFetched", this.showNotes, this);
},
showNotes: function(notes) {
//do stuffs here
}
})
I want to test this using Jasmine and below is my test (which doesn't work)
it("should show notes", function() {
var category = new Category;
var notes_dialog = new NotesDialog({model: category})
spyOn(notes_dialog, "showNotes");
category.trigger("notesFetched", "[]");
expect(notes_dialog.showNotes).toHaveBeenCalledWith("[]");
})
Does anyone know why the above test doesn't work? The error I get is "Expected spy showNotes to have been called with [ '[]' ] but it was never called."
I was doing something similar where I had a view, but I couldn't get the spy to work properly unless I added it to the prototype, and before I created the instance of the view.
Here's what eventually worked for me:
view.js
view = Backbone.View.extend({
initialize: function(){
this.collection.bind("change", this.onChange, this);
},
...
onChange: function(){
console.log("Called...");
}
});
jasmine_spec.js
describe("Test Event", function(){
it("Should spy on change event", function(){
var spy = spyOn(view.prototype, 'onChange').andCallThrough()
var v = new view( {collection: some_collection });
// Trigger the change event
some_collection.set();
expect(spy).toHaveBeenCalled()
});
});
I would test initially with the toHaveBeenCalled() expectation and change to the toHaveBeenCalledWith() after you get that working...
Update 5/6/2013: Changed update() to set()
Try to amend your existing test code as follows:
it("should show notes", function() {
var category = new Category;
spyOn(NotesDialog.prototype, "showNotes");
var notes_dialog = new NotesDialog({model: category})
category.trigger("notesFetched", "[]");
expect(notes_dialog.showNotes).toHaveBeenCalledWith("[]");
})
In your original code, the instance of the method you are calling is one defined in the bind closure, whereas the one you are spying on is in the notes_dialog instance. By moving the spy to the prototype, you are replacing it before the bind takes place, and therefore the bind closure encapsulates the spy, not the original method.
Using a spy means to replace the function you spying on. So in your case you replace the bind function with the spy, so the internal logic of the original spy will not call anymore. And thats the right way to go cause you dont wanna test that Backbones bind is work but that you have called bind with the specific paramaters "notesFetched", this.showNotes, this.
So how to test this. As you know every spy has the toHaveBeenCalledWith(arguments) method. In your case it should looks like this:
expect(category.bind).toHaveBeenCalledWith("notesFetched", category. showNotes, showNotes)
So how to test that trigger the "notesFetched" on the model will call your showNotes function.
Every spy saves the all parameters he was called with. You can access the last one with mostRecentCall.args.
category.bind.mostRecentCall.args[1].call(category.bind.mostRecentCall.args[2], "[]");
expect(notes_dialog.showNotes).toHaveBeenCalledWith("[]");
mostRecentCall.args[1] is the the second argument in your bind call (this.showNotes). mostRecentCall.args[2] is the the third argument in your bind call (this).
As we have test that bind was called with your public method showNotes, you can also call the your public method showNotes directly, but sometimes the passed arguments can access from outside so you will use the shown way.
Your code looks fine, except do you have the test wrapped in a describe function, as well as an it function?
describe("show notes", function(){
it("should show notes", function(){
// ... everything you already have here
});
});
Total guess at this point, but since you're not showing the describe function that's all I can think it would be. You must have a describe block for the tests to work, if you don't have one.
You are pretty close ;)
spyOn replaces the function with your spy and returns you the spy.
So if you do:
var dialog_spy = spyOn(notes_dialog, "showNotes");
category.trigger("notesFetched", "[]");
expect(dialog_spy).toHaveBeenCalledWith("[]");
should work just fine!

How to programatically add and use elements (dialog box in this case)

So My first though was, that adding more, and more HTML elements is not a way to go, and I come up with this solution
var Jaxi = {
CurrentLocation: '/',
showLoginDialog: function () {
dojo.place('<div data-dojo-type="dijit.Dialog" style="width:600px;" id="loginDialog"><div id="dialog-content"></div><a href="javascript:Jaxi.CloseDialog()">Close</div>', dojo.body())
dojo.xhrGet({
url: "/Account/SingIn?ReturnUrl=" + Jaxi.CurrentLocation,
load: function (result) {
dojo.byId("dialog-content").innerHTML = result;
}
});
dojo.ready(function () {
dijit.byId("loginDialog").show();
});
},
CloseDialog: function () {
dijit.byId("loginDialog").hide();
dojo.destroy("loginDialog");
}
};
It's working.. To some degree at least. Dialog open, but no styles are appiled. But moreover I can't close dialog.
Question Is how to make it working ?
After you have placed the div in your body, Dojo needs to parse the HTML to "notice" the new widget. When it notices the data-dojo-type attribute it says "Hey, here's a widget, I need to make this into a beautiful Dialog".
showLoginDialog: function () {
dojo.place('<div data-dojo-type="dijit.Dialog" ....</div>', dojo.body());
dojo.parser.parse();
....
Of course, you also have to make sure your body tag has class="claro" (or any other theme you want to use).
That being said, I personally think this is a little messy way to make a dialog box. You are sort of mixing declarative with programmatic. I'm not sure what you mean by "My first though was, that adding more, and more HTML elements is not a way to go", but in my own opinion mixing HTML inside your javascript makes the code difficult to read. You may want to take a look at this sitepen article if you want a clean way to separate HTML and Javascript.

Using dijit.InlineEditBox with dijit.form.Select

I'm trying to use a dijit.form.Select as the editor for my dijit.InlineEditBox. Two problems / unexpected behavior seem to occur:
Inconsistently, the InLineEditBox doesn't have the initial value set as selected
Consistently, after selecting a choice, the value that should be hidden is shown instead of the label.
The width isn't set to 130px
Here's working code: http://jsfiddle.net/mimercha/Vuet8/7/
The jist
<span dojoType="dijit.InlineEditBox" editor="dijit.form.Select"
editorParams="{
options: [
{label:'None',value:'none'},
{label:'Student',value:'stu'},
{label:'Professor',value:'prof',selected:true},
],
style:'width:1000px;',
}"
editorStyle="width: 1000px;"
>
</span>
Any help is greatly appreciated! Thanks!
Okay, after a few MORE hours struggling with the mess that is dijit.InlineEditBox, I think I have the solution to the remaining issue (#2).
EDIT: My first solution to #2 is still flawed; the implementation at http://jsfiddle.net/kfranqueiro/Vuet8/10/ will never return the actual internal value when get('value') is called.
EDIT #2: I've revamped the solution so that value still retains the real (hidden) value, keeping displayedValue separate. See if this works better:
http://jsfiddle.net/kfranqueiro/Vuet8/13/
First, to recap for those who weren't on IRC:
Issue #1 was happening due to value not being properly set as a top-level property of the InlineEditBox itself; it didn't pick it up properly from the wrapped widget.
Issue #3 was happening due to some pretty crazy logic that InlineEditBox executes to try to resolve styles. Turns out though that InlineEditBox makes setting width particularly easy by also exposing it as a top-level numeric attribute. (Though IINM you can also specify a percentage as a string e.g. "50%")
Now, issue #2...that was the killer. The problem is, while InlineEditBox seems to have some logic to account for widgets that have a displayedValue attribute, that logic is sometimes wrong (it expects a displayedValue property to actually exist on the widget, which isn't necessarily the case), and other times missing entirely (when the InlineEditBox initializes). I've worked around those as best I could in my own dojo.declared extensions to InlineEditBox and the internal widget it uses, _InlineEditor - since generally it's a good idea to leave the original distribution untouched.
It's not pretty (neither is the underlying code I dug through to understand and come up with this), but it seems to be doing its job.
But man, this was rather interesting. And potentially pertinent to my interests as well, as we have used this widget in our UIs as well, and will be using it more in the future.
Let me know if anything backfires.
hm...
<span dojoType="dijit.InlineEditBox" editor="dijit.form.Select"
editorParams="{
options: [
{label:'None',value:'none'},
{label:'Student',value:'stu'},
{label:'Professor',value:'prof',selected:true},**<<<<** and this comma is for?
],
style:'width:1000px;',**<<<<** and this comma is for?
}"
editorStyle="width: 1000px;"
>
</span>
Also, when using dijit.form.Select, selected value is not attr "selected" but value.
And if you enter prof inside <span ...blah > prof </span> than your proper selected option will be selected ;)
Dijit select checks for VALUE, not attr.
This may be fixed in recent Dojo - see http://bugs.dojotoolkit.org/ticket/15141 - but using 1.7.3 I found this worked:
In my app directory, at the same level as dojo, dijit and dojox, I created a file InlineSelectBox.js which extends InlineEditBox with code to set the HTML on the associated domNode from the value of the Dijit, and which wires up that code to the onChange() event:
define(["dijit/InlineEditBox",
"dijit/form/Select",
"dojo/on",
"dojo/_base/declare",
"dojo/_base/array"
],
function(InlineEditBox, Select, on, declare, array){
return declare(InlineEditBox, {
_setLabel: function() {
array.some(this.editorParams.options, function(option, i){
if (option.value == this.value) {
this.domNode.innerHTML = option.label;
return true;
}
return false;
}, this);
},
postMixInProperties: function(){
this.inherited(arguments);
this.connect(this, "onChange", "_setLabel");
},
postCreate: function(){
this.inherited(arguments);
this._setLabel();
}
});
});
Then, in my view script:
require(["dojo/ready",
"app/InlineSelectBox",
"dijit/form/Select"
],
function(ready, InlineSelectBox, Select){
ready(function(){
// Add code to set the options array
var options = [];
// Add code to set the initial value
var initialValue = '';
var inlineSelect = new InlineSelectBox({
editor: Select,
editorParams: {options: options},
autoSave: true,
value: initialValue
}, "domNodeToAttachTo");
});
});
I was dealing with this situation a few months ago, and not finding a resolution i made my own algorithm.
I put a div with an event on Onclick that build programatically a Filtering Select on that div with the store i want to use.
function create(id,value){
var name = dojo.byId(id).innerHTML;
dojo.byId(id).parentNode.innerHTML = '<div id="select"></div>';
new dijit.form.FilteringSelect({
store: store,
autoComplete: true,
invalidMessage:"Invalid Selection",
style: "width: 80px;",
onBlur: function(){ },
onChange: function(){ },
required: true,
value: value,
disabled: false,
searchAttr: "name",
id: "status"+id,
name: "status"
},"select");
dijit.byId('status'+id).focus();
}
I used the onBlur event to destroy the widget and the onchange to save by xhr the new value.
The focus is below because the onBlur was not working properly.
note: the function is not complete.