Jasmine: how to check that array contains an object? - jasmine

This check used to pass:
expect(array).toContain(value)
Array:
[
{"_t":"user","id":1073970419,"email":"email3#example.org","name":"Spectator"},
{"_t":"user","id":4464992042,"email":"email4#example.org","name":"Collaborator"},
{"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"}
]
Value:
{"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"}
But no longer passes. Whats the new way to write the same test?

The syntax you need is:
const obj = {"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"};
expect(array).toContain(jasmine.objectContaining(obj));
See fiddle: https://jsfiddle.net/bblackwo/4o5u5Lmo/16/

It won't contain that object (remember, two objects with the same properties are not the same object for the purposes of equality), so toContain will never pass.
You need to use another test, like toEqual or (if you only want to check for a subset of properties), toEqual combined with jasmine.objectContaining.
Here's the toEqual example from the Jasmine documentation on that page:
describe("The 'toEqual' matcher", function() {
it("works for simple literals and variables", function() {
var a = 12;
expect(a).toEqual(12);
});
it("should work for objects", function() {
var foo = {
a: 12,
b: 34
};
var bar = {
a: 12,
b: 34
};
expect(foo).toEqual(bar);
});
});
Note now foo equals bar.
Here's their example using jasmine.objectContaining:
describe("jasmine.objectContaining", function() {
var foo;
beforeEach(function() {
foo = {
a: 1,
b: 2,
bar: "baz"
};
});
it("matches objects with the expect key/value pairs", function() {
expect(foo).toEqual(jasmine.objectContaining({
bar: "baz"
}));
expect(foo).not.toEqual(jasmine.objectContaining({
c: 37
}));
});
// ...
});
Note how the object with several properties matches the partial object supplied to jasmine.objectContaining.

#T.J.Crowder already explained precisely the problem. Just to help you a tad more, if you want to adapt your example, you'd need something like this:
var userA = {"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"}
array =
[
{"_t":"user","id":1073970419,"email":"email3#example.org","name":"Spectator"},
{"_t":"user","id":4464992042,"email":"email4#example.org","name":"Collaborator"},
userA
]
expect(array).toContain(userA);

Related

Passing command line arguments to nightwatch.runner

I have a test setup that accepts dynamic values for predefined options (e.g: --foo="bar", --foo="car", etc) which works perfectly when run from the command line.
This works:
./node_modules/nightwatch/bin/nightwatch ./tests/test.js --env "production" --foo="bar"
However, I want to call the test from inside a node app, but can't quite seem to get the extra parameters passed into my test. My test initializes correctly, but then fails because I can't get any of the extra params.
try {
const nightwatch = require('nightwatch');
const argv = {
config: 'nightwatch.json',
test: 'test.js',
env: 'default',
foo: 'bar',
};
const done = function() {
console.log('nightwatch complete');
}; // your callback function
const settings = {};
nightwatch.runner(argv, done, settings);
} catch (error) {
console.log('Exception:' + error.message);
}
There seems to be absolutely no documentation for nightwatch.runner or nightwatch.cli so any help you can provide would be greatly appreciated.
It took a lot of poking around to see what gets set where, but finally I tried playing with the settings object and realized that I could override the globals param like so:
var settings = {
globals: {
foo: 'one',
bar: 'two'
}
};
This probably isn't the intended functionality of the settings object here, but it worked for me.
try {
const nightwatch = require('nightwatch');
const argv = {
config: 'nightwatch.json',
test: 'test.js',
env: 'default',
foo: 'bar',
};
const done = function() {
console.log('nightwatch complete');
}; // your callback function
var settings = {
globals: {
foo: 'one',
bar: 'two'
}
};
nightwatch.runner(argv, done, settings);
} catch (error) {
console.log('Exception:' + error.message);
}
If anyone who comes across this is interested, I posted a very simple version on github.

Rivetjs Global onchange event handler

I'm trying my best to get myself around rivets, but I'm needing to do something that should be simple and I'm not quite sure how to do it.
My case scenario its simple: I have this dataset, and whenever a change occurs either in 'rows' or in a 'row', I need to dispatch this changes right away to the server.
Looks like a common pattern to me, but I can not do it without cheating.
This is my solution so far:
var data = {
rows: [
{name: 'John', age: 30},
{name: 'Mary', age: 29}
]
}
var self = this
, valRoutine = rivets.binders['value'].routine;
rivets.binders['value'].routine = function(el, value) {
var res = valRoutine.call(this, el, value);
self.doServerStuff(this.model);
return res;
};
How to do event handling in rivets? There is no default events defined? I think that "onModel[add|change|del]Event" should be in the box, am I missing something? Probably am, but the docs dont help much and its not ease to find examples, so... =/
Can anyone help on this?
Ah, by the way, samples on how to put components to work, anyone? It was one of the major reason I came across rivets.
Thanks!!!
Ok, found a much better way:
rivets.binders['update'] = {
bind: function(el) {
var self = this
, field = this.keypath
;
this.callback = function() {
var page = self.observer.obj;
if (page.data.main.status == 'editing') {
var data = {row:{}};
data[page.key] = page.data.main.row[page.key];
data.row[field] = page.data.main.row[field];
page.exec('update', {
data: data
}, this);
}
};
$(el).on('change', this.callback);
},
unbind: function(el) {
$(el).off('change', this.callback);
},
routine: function(el, value) {
}
};
And then, just:
<input rv-update="my_field" rv-value="data.row.my_field">
I realised that if I catch the update the way I was, at every keystroke it would be calling the server.

Extending behaviors when extending views

The task is:
MyViewX uses BehaviorA and B
MyViewY extends MyViewX,
and uses all behaviors in MyViewX, plus BehaviorC
// The following code works
//
var MyViewX = Marionette.ItemView.extend({
behaviors: {
BehaviorA: {},
BehaviorB: {}
}
});
var MyViewY = MyViewX.extend({
behaviors: {
BehaviorA: {},
BehaviorB: {},
BehaviorC: {}
}
});
The question: how to make use of the behaviors definition in X, so that in Y I do not need to repeat A and B?
// this is not good. It overrides X. Y has only C
var MyViewY = MyX.extend({
behaviors: {
BehaviorC: {}
}
});
// maybe something like this?
// but how exactly to get X's behaviors here?
var MyViewY = MyViewX.extend({
behaviors: _.extend{
BEHAVIORS_OF_THE_SUPER_OF_THE_CURRENT_CLASS,
{ BehaviorC: {} }
}
});
// this gives no errors,
// but A and B do not behave, as if they are not used.
//
var MyViewY = MyViewX.extend({
behaviors: { BehaviorC: {} },
initialize: function () {
var b = this.behaviors;
this.behaviors = _.extend(
MyViewY.__super__.behaviors,
b
);
}
});
You can get MyViewX's behaviors by accessing it's prototype. Using your ideal solution the following should work:
var MyViewY = MyViewX.extend({
behaviors: _.extend({},
MyViewX.prototype.behaviors,
{ BehaviorC: {} }
);
});
The reason the last example doesn't work is because Marionette Behaviors are added to a view in the Marionette.View constructor which takes place before the Backbone.View constructor which calls initialize at the end.

Filter Parse.Object properties that should get saved to the server

I have this Parse.Object that I want to save to the server, but I'd like to whitelist the attributes of this object that get saved.
Parse.Object.extend('someObject', {
defaults: {
foo: 1,
bar: 2,
computedProperty: function() {
return this.get('foo') + this.get('bar')
}
},
get: function(attr) {
var value = Parse.Object.prototype.get.call(this, attr)
return _.isFunction(value) ? value.call(this) : value
}
})
As you can see, this object has a computed property among its attributes. I would like to filter out the computedProperty when I save this Parse.Object. Is that possible?
So, we've figured out a way to filter the list of attributes that get saved.
If you wanna do it, you have to override a private, undocumented method on the Parse.Object called _getSaveJSON, so the complete model above would be:
Parse.Object.extend('someObject', {
defaults: {
foo: 1,
bar: 2,
computedProperty: function() {
return 1+2
}
},
get: function(attr) {
var value = Parse.Object.prototype.get.call(this, attr)
return _.isFunction(value) ? value.call(this) : value
},
_getSaveJSON: function() {
var model = this
var json = _.clone(_.first(this._opSetQueue))
Parse._objectEach(json, function(op, key) {
json[key] = op.toJSON();
});
var whitelistedAttributes = ['foo', 'bar']
return _.pick(json, whitelistedAttributes)
}
})

Crossfilter query

Is it possible to filter a crossfilter dataset which has an array as the value?
For example, say I have the following dataset:
var data = [
{
bookname: "the joy of clojure",
authors: ["Michael Fogus", "Chris Houser"],
tags: ["clojure", "lisp"]
},
{
bookname: "Eloquent Ruby",
authors: ["Russ Olsen"],
tags: ["ruby"]
},
{
bookname: "Design Patterns in Ruby",
authors: ["Russ Olsen"],
tags: ["design patterns", "ruby"]
}
];
Is there an easy way to access the books which are tagged by an particular tag? And also the books which have a particular author? The way I understand how to use crossfilter so far has me doing something like this:
var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});
var tag = tags.group();
And then when I access the grouping (like so):
tag.all()
I get this:
[{key: ["clojure", "lisp"], value: 1},
{key: ["design patterns", "ruby"], value: 1},
{key: ["ruby"], value: 1}]
When I would rather have this:
[{key: "ruby", value: 2},
{key: "clojure", value: 1},
{key: "lisp", value: 1},
{key: "design patterns", value: 1}]
I've added comments to the code below. Big picture: use reduce function.
var data = ...
var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});
tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial).value()
Notice how I've used groupAll() instead of group() b/c we want our reduce functions (defined below) to operate on one group rather than 3 groups.
Now the reduce functions should look like this:
/*
v is the row in the dataset
p is {} for the first execution (passed from reduceInitial).
For every subsequent execution it is the value returned from reduceAdd of the prev row
*/
function reduceAdd(p, v) {
v.tags.forEach (function(val, idx) {
p[val] = (p[val] || 0) + 1; //increment counts
});
return p;
}
function reduceRemove(p, v) {
//omitted. not useful for demonstration
}
function reduceInitial() {
/* this is how our reduce function is seeded. similar to how inject or fold
works in functional languages. this map will contain the final counts
by the time we are done reducing our entire data set.*/
return {};
}
I've never used "crossfilter" (I'm assuming this is a JS library). Here are some pure JS methods though.
This...
data.filter(function(d) {
return d.authors.indexOf("Michael Fogus") !== -1;
})
returns this:
[{bookname:"the joy of clojure", authors:["Michael Fogus", "Chris Houser"], tags:["clojure", "lisp"]}]
This...
var res = {};
data.forEach(function(d) {
d.tags.forEach(function(tag) {
res.hasOwnProperty(tag) ? res[tag]++ : res[tag] = 1
});
})
returns this:
({clojure:1, lisp:1, ruby:2, 'design patterns':1})
To either of these, you can apply d3.entries to get your {key:"ruby", value: 2} format.

Resources