I can't get jasmine testing working smoothly on nodejs. What I mean is that there are situations when the test just stops in the middle of the run, does not notify anything, but silently fails in error. One example I got a few moments ago is this:
In the validater.validateIDs. There is a callback "isOkFunc" that get sent to another modules function to a custom mongoose.find-function
validateIDs = function (data, dbFunc) {
data = data || selfData;
var
isOkFunc = (function () {
var ret = {};
ret.thisData = void 0;
ret.setData = function (data) {
ret.thisData = (data === undefined) ? true : data;
};
return ret;
})(),
findData = {
playerID: data.playerID,
gameID: data.gameID,
turnID: data.turnID,
objectID: data.objectID
};
dbFunc.find(findData, isOkFunc.setData);
return isOkFunc.thisData;
};
The callback get called in the custom find-function:
this.find = function (data, cb) {
models.Order.find(data,function (err, orders) {
if (err) {
console.log("error in getting orders");
cb(void 0);
} else {
console.log(orders);
cb(orders);
}
});
};
Now since I forgot to point the thisData to ret.thisData, it errored, since the variable did not exist. But the problem is that jasmine doesn't notify anything about this, it just silently fails and stops execution. How should this be done for better debugging? Or more like how it should be for ANY realistic debugging?
The actual part in spec-file that caused the problem was this:
isOk = validater.validateIDs(data, orderReceive);
After stripping few unrelated descibe-sections, here is the whole spec-file:
"use strict";
/* This is for making jasmine testing work in windows with netbeans. If this
* causes trouble for you, then try to find a better solution. I need this in
* windows enviroment */
if(process.platform.match(/^win/)) {
require("jasmine-node");
};
var uuid = require('node-uuid'),
orders = require("../../databases/modules/orders.js"),
orderReceive = orders.ordersAtDatabase("../testDB"),
data, UUID = uuid.v4();;
/* Counts the amount of finished-class received. When it reaches given point.
* Executes all functions. This is supposed to be executed last to remove database entries
*
* Without this we can not test the database ID validation fully, since we need
* to have data in database for this */
var toExecuteLast = (function toExecuteLast() {
var funcs = [], ret = {}, i,
count = 0,
max = 5;
ret.add = function(func) {
funcs.push(func);
};
ret.finished = function() {
count++;
};
ret.do = function() {
console.log("gogogo");
if(count >= max) {
console.log("gogogo2222");
for(i = 0; i < funcs.length; i++) {
funcs[i]();
}
clearInterval(toExecuteLast.do);
}
};
return ret;
})();
beforeEach(function() {
data =
{
"playerID": 1,
"gameID": 1,
"turnID": 1,
"objectID": UUID,
"type": "unit",
"action": "move",
"exec": {
"x": 10,
"y": 10
}
};
});
describe("Testing data validation from data, that would come from frontend", function() {
var validater;
validater = orders.orderValidation();
describe(". User is sending fishy data: ", function(){
it(". Test that everything works fine", function() {
expect(helper).not.toThrow();
});
it(". Something fishy with playerID", function() {
data.playerID = "kjdf ss";
expect(helper).toThrow(new Error("playerID was not an integer"));
data.playerID = 12322.33;
expect(helper).toThrow();
});
it(". Something fishy with gameID", function() {
data.gameID = "kjfkj";
expect(helper).toThrow(new Error("gameID was not an integer"));
});
it(". Something fishy with turnID", function() {
data.turnID = "| rm -r";
expect(helper).toThrow(new Error("turnID was not an integer"));
});
it(". Something fishy with objectID", function() {
data.objectID = "| rm -r";
expect(helper).toThrow(new Error("objectID was not an UUIDv4"));
});
it(". Something fishy with type", function() {
data.type = "| rm -r";
expect(helper).toThrow();
});
it(". Something fishy with action", function() {
data.action = "| rm -r";
expect(helper).toThrow(new Error("Action contains illegal character only a-z,A-Z,0-9,_ are allowed"));
});
it(". Something fishy with exec", function() {
data.exec = "| rm -r";
expect(helper).toThrow(new Error("Action was move, but exec.x was not an integer"));
});
toExecuteLast.finished();
});
describe(". Do a database check if the IDs that are given (player, game, turn, object) are correct? ", function(){
/* Remeber that the playerID is fetched from inner data / cookies / auth
* so it is supposed to be correct. We need it to be correct in the
* database too, so we can test the game, turn and object ID validations */
it(". Everything ok with ID validation?", function() {
var isOk;
isOk = validater.validateIDs(data, orderReceive);
waitsFor(function() {
return isOk !== 'undefined' ? true : false;
});
runs(function() {
expect(isOk).not.toBeTruthy();
isOk = false;
data.gameID = 10;
isOk = validater.validateIDs(data, orderReceive);
waitsFor(function() {
return (isOk !== 'undefined') ? true : false;
}, "", 1000);
runs(function() {
// THIS doesn't seem to work, why?
expect(true).toBeTruthy();
toExecuteLast.finished();
});
});
});
toExecuteLast.finished();
});
function helper() {
validater.validateRequest(data);
}
});
setInterval(toExecuteLast.do, 400);
orderReceive.showAll();
//orderReceive.removeAll();
Related
I have this grid that i am creating using knockoutjs, it works perfectly at first, now i am using a window.location.hash to run another query, it works too and query returns the correct amount of data however when i insert it within the observableArray (which gets inserted correctly as well), the grid doesn't update the data and shows the old data... I'm using removeAll() function on the observableArray as well before inserting new set of data but it wont update my grid, i suspect there is something wrong with the DOM?
I should mention when i reload the page (since the page's url keeps the hash for query) my grid shows the data and works perfectly. for some reason it needs to reload the page and doesn't work without,
Here is my JS:
if (!ilia) var ilia = {};
ilia.models = function () {
var self = this;
this.pageCount = ko.observable(0);
//this is the observableArray that i am talking about ++++++++
this.items = ko.observableArray();
var $pagination = null;
var paginationConfig = {
startPage: 1,
totalPages: 20,
onPageClick: function (evt, page) {
self.generateHash({ pageNum: page });
self.getData();
}
}
var hashDefault = {
pageNum: 1,
pageSize: 20,
catId: null,
search: ""
}
this.dataModel = function (_id, _name, _desc, _thumb, _ext) {
var that = this;
this.Id = ko.observable(_id);
this.Name = ko.observable(_name);
this.Desc = ko.observable(_desc);
this.Url = '/site/ModelDetail?id=' + _id;
var b64 = "data:image/" + _ext + ";base64, ";
this.thumb = ko.observable(b64 + _thumb);
}
this.generateHash = function (opt) {
//debugger;
var props = $.extend(hashDefault, opt);
var jso = JSON.stringify(props);
var hash = window.location.hash;
var newHash = window.location.href.replace(hash, "") + "#" + jso;
window.location.href = newHash;
return jso;
}
this.parseHash = function () {
var hash = window.location.hash.replace("#", "");
var data = JSON.parse(hash);
if (data)
data = $.extend(hashDefault, data);
else
data = hashDefault;
return data;
}
var _cntrl = function () {
var _hdnCatName = null;
this.hdnCatName = function () {
if (_hdnCatName == null)
_hdnCatName = $("hdnCatName");
return _hdnCatName();
};
var _grid = null;
this.grid = function () {
if (_grid == null || !_grid)
_grid = $("#grid");
return _grid;
}
this.rowTemplate = function () {
return $($("#rowTemplate").html());
}
}
this.createPagnation = function (pageCount, pageNum) {
$pagination = $('#pagination-models');
if ($pagination && $pagination.length > 0)
if (paginationConfig.totalPages == pageCount) return;
var currentPage = $pagination.twbsPagination('getCurrentPage');
var opts = $.extend(paginationConfig, {
startPage: pageNum > pageCount ? pageCount : pageNum,
totalPages: pageCount,
onPageClick: self.pageChange
});
$pagination.twbsPagination('destroy');
$pagination.twbsPagination(opts);
}
this.pageChange = function (evt, page) {
var hash = self.parseHash();
if (hash.pageNum != page) {
self.generateHash({ pageNum: page });
self.getData();
}
}
this.getData = function () {
var _hash = self.parseHash();
inputObj = {
pageNum: _hash.pageNum,
pageSize: _hash.pageSize,
categoryId: _hash.catId
}
//debugger;
//console.log(_hash);
if (inputObj.categoryId == null) {
ilia.business.models.getAll(inputObj, function (d) {
//debugger;
if (d && d.IsSuccessfull) {
self.pageCount(d.PageCount);
self.items.removeAll();
_.each(d.Result, function (item) {
self.items.push(new self.dataModel(item.ID, item.Name, item.Description, item.Thumb, item.Extention));
});
if (self.pageCount() > 0)
self.createPagnation(self.pageCount(), inputObj.pageNum);
}
});
}
else {
ilia.business.models.getAllByCatId(inputObj, function (d) {
if (d && d.IsSuccessfull) {
self.pageCount(d.PageCount);
self.items.removeAll();
console.log(self.items());
_.each(d.Result, function (item) {
self.items.push(new self.dataModel(item.ID, item.Name, item.Description, item.Thumb, item.Extention));
});
// initializing the paginator
if (self.pageCount() > 0)
self.createPagnation(self.pageCount(), inputObj.pageNum);
//console.log(d.Result);
}
});
}
}
this.cntrl = new _cntrl();
};
And Initialize:
ilia.models.inst = new ilia.models();
$(document).ready(function () {
if (!window.location.hash) {
ilia.models.inst.generateHash();
$(window).on('hashchange', function () {
ilia.models.inst.getData();
});
}
else {
var obj = ilia.models.inst.parseHash();
ilia.models.inst.generateHash(obj);
$(window).on('hashchange', function () {
ilia.models.inst.getData();
});
}
ko.applyBindings(ilia.models.inst, document.getElementById("grid_area"));
//ilia.models.inst.getData();
});
Would perhaps be useful to see the HTML binding here as well.
Are there any console errors? Are you sure the new data received isn't the old data, due to some server-side caching etc?
Anyhow, if not any of those:
Are you using deferred updates? If the array size doesn't change, I've seen KO not being able to track the properties of a nested viewmodel, meaning that if the array size haven't changed then it may very well be that it ignores notifying subscribers. You could solve that with
self.items.removeAll();
ko.tasks.runEarly();
//here's the loop
If the solution above doesn't work, could perhaps observable.valueHasMutated() be of use? https://forums.asp.net/t/2056128.aspx?What+is+the+use+of+valueHasMutated+in+Knockout+js
I am building an application in node.js with socket.io where I want people to be able to vote on questions and have a timer included which will give them a time limit for answering questions. The issue that I am running into (which I will highlight below) is that every time someone connects to the page, it will log the tick from the stopwatch object another time. Even after people leave, it will continue to log it one time for every time someone connected. Here is my code for the project thus far.
Stopwatch.js:
function Stopwatch() {
if (false === (this instanceof Stopwatch)) {
return new Stopwatch();
}
this.hour = 3600000;
this.minute = 60000;
this.second = 1000;
this.time = this.hour;
this.interval = undefined;
events.EventEmitter.call(this);
_.bindAll(this, 'start', 'stop', 'reset', 'onTick');
};
util.inherits(Stopwatch, events.EventEmitter);
Stopwatch.prototype.start = function() {
console.log("Starting the Timer!");
this.interval = setInterval(this.onTick, this.second);
this.emit('start');
};
Stopwatch.prototype.onTick = function() {
var remainder = this.time,
numhours,
numMinutes,
numSeconds,
output = "";
if (this.time === 0) {
this.stop();
return;
}
numHours = String(parseInt(remainder / this.hour, 10));
remainder -= this.hour * numHours;
numMinutes = String(parseInt(remainder / this.minute, 10));
remainder -= this.minute * numMinutes;
numSeconds = String(parseInt(remainder / this.second, 10));
output = _.map([numHours, numMinutes, numSeconds], function(str) {
if (str.length === 1) {
str = "0" + str;
}
return str;
}).join(":");
this.emit('tick', output);
this.time -= this.second;
}
module.exports = Stopwatch;
}
socket.js:
var Stopwatch = require('../models/stopwatch');
var people = {};
var stopwatch = Stopwatch();
module.exports = function (io) {
io.on('connection', function (socket) {
//console.log("Someone joined...");
//socket.on('join', function () {
if (Object.keys(people).length === 0) {
console.log('host joined');
people[socket.id] = {"name": "host", "image": "fake picture", "host": true};
} else {
console.log("Someone else joined");
people[socket.id] = {"name": "person", "image": "other picture", "host": false};
}
console.log(people);
//});
socket.on('startTimer', function() {
stopwatch.start();
});
socket.on('disconnect', function() {
delete people[socket.id];
console.log("someone left");
console.log(people);
});
stopwatch.on('tick', function(time) {
console.log(socket.id + '---stopwatch tick!' + time);
socket.emit('timer', { countdown: time });
});
});
};
The javascript on the client:
var socket;
$(document).ready(function() {
socket = io();
socket.on('timer', function (data) {
$('#timer').html(data.countdown);
});
$('#start-timer').click(function() {
socket.emit('startTimer');
});
});
Thanks in advance! I can't figure out why it still logs each user and the time, even after they disconnect from the server.
What's happening is that every time a new client opens a connection to the server, you're assigning an event listener to the stopwatch and never remove it, this causes the behaviour you're experiencing. You must remove your eventListener when the socket disconnects as stated in the node event emitter class.
You should end up with something like this:
io.on('connection', function (socket) {
//console.log("Someone joined...");
//socket.on('join', function () {
if (Object.keys(people).length === 0) {
console.log('host joined');
people[socket.id] = {"name": "host", "image": "fake picture", "host": true};
} else {
console.log("Someone else joined");
people[socket.id] = {"name": "person", "image": "other picture", "host": false};
}
console.log(people);
socket.on('startTimer', function() {
stopwatch.start();
});
var _tickListener = function(time) {
console.log(socket.id + '---stopwatch tick!' + time);
socket.emit('timer', { countdown: time });
};
socket.on('disconnect', function() {
delete people[socket.id];
stopwatch.removeListener('tick', _tickListener);
console.log("someone left");
console.log(people);
});
stopwatch.on('tick', _tickListener);
});
I have managed to use Strophe MAM to get the archived messages into the RAWInput, and display the last message(but only the last one). How do i display all the messages from the RAWInput?? But not just the last one?
And how do i extract who the message is from?
I have limited the messages to the last 5.
connection.mam.query("test3#macbook-pro.local", {
"with": "test4#macbook-pro.local","before": '',"max":"5",
onMessage: function(message) {
console.log( $(message).text());
},
onComplete: function(response) {
console.log("Got all the messages");
}
});
You can get all the messages using the `strophe.mam.js plugin
Here is my working code:
// Retrives the messages between two particular users.
var archive = [];
var q = {
onMessage: function(message) {
try {
var id = message.querySelector('result').getAttribute('id');
var fwd = message.querySelector('forwarded');
var d = fwd.querySelector('delay').getAttribute('stamp');
var msg = fwd.querySelector('message');
var msg_data = {
id:id,
with: Strophe.getBareJidFromJid(msg.getAttribute('to')),
timestamp: (new Date(d)),
timestamp_orig: d,
from: Strophe.getBareJidFromJid(msg.getAttribute('from')),
to: Strophe.getBareJidFromJid(msg.getAttribute('to')),
type: msg.getAttribute('type'),
body: msg.getAttribute('body'),
message: Strophe.getText(msg.getElementsByTagName('body')[0])
};
archive.val(archive.val() + msg_data.from + ":" + msg_data.message + "\n" + msg_data.to + ":" + msg_data.message + "\n");
archive.scrollTop(archive[0].scrollHeight - archive.height());
console.log('xmpp.history.message',msg_data.message);
} catch(err){
if(typeof(err) == 'TypeError'){
try {
console.log(err.stack)
} catch(err2){
console.log(err,err2);
}
}
}
return true;
},
onComplete: function(response) {
console.log('xmpp.history.end',{query:q, response:response});
}
};
$(document).ready(function)(){
archive = $("#archive-messages");
archive.val("");
$("#to-jid").change(function() {
$("#archive-messages").val("");
var to = {"with": $(this).val()};
$.extend(q, to, before, max);
// It takes around 800ms to auto login. So after this timeout we have to retrieve the messages of particular User.
setTimeout(function(){
connection.mam.query(Strophe.getBareJidFromJid(connection.jid), q);
}, 1000);
});
});
connection.mam.query("Your_Id", {
"with": "partner_id","before": '',"max":'',
onMessage: function(message) {
console.log("Message from ", $(message).find("forwarded message").attr("from"),
": ", $(message).find("forwarded message body").text());
return true;
},
onComplete: function(response) {
console.log("Got all the messages");
}
});
This will fetch all History for a user. If you want to limit, then provide max value.
Don't download strophe.mam.js(https://github.com/metajack/strophejs-plugins/tree/master/mam) from github. Its not working. Please copy below strophe.mam.js file.
**strophe.mam.js**
/* XEP-0313: Message Archive Management
* Copyright (C) 2012 Kim Alvefur
*
* This file is MIT/X11 licensed. Please see the
* LICENSE.txt file in the source package for more information.
*
* TODO:
* Get RSM from the reply
* Clean remove onMessage handler afterwards
* queryid?
*
*/
Strophe.addConnectionPlugin('mam', {
_c: null,
_p: [ "with", "start", "end" ],
init: function (conn) {
this._c = conn;
Strophe.addNamespace('MAM', 'urn:xmpp:mam:0');
},
query: function (jid, options) {
var _p = this._p;
var attr = {
type:"set",
id:jid
};
var mamAttr = {xmlns: Strophe.NS.MAM};
var iq = $iq(attr).c("query", mamAttr).c('x',{xmlns:'jabber:x:data'});
iq.c('field',{var:"FORM_TYPE"}).c('value').t("urn:xmpp:mam:0").up().up();
for (i = 0; i < this._p.length; i++) {
var pn = _p[i];
var p = options[pn];
delete options[pn];
if (!!p) {
var f
iq.c('field',{var:pn}).c('value').t(p).up().up();
}
}
iq.up();
var onMessage = options["onMessage"];
delete options['onMessage'];
var onComplete = options["onComplete"];
delete options['onComplete'];
iq.cnode(new Strophe.RSM(options).toXML());
this._c.addHandler(onMessage, Strophe.NS.MAM, "message", null);
return this._c.sendIQ(iq, onComplete);
}
});
The easiest way to retrieve all the messages is to include this line at the end of the onMessage() function:
return true;
I think the reason is that if a handler does not return true, it will be destroyed after the first time it is called.
I have been trying to pass a variable to _F.search in this but no luck, can someone please help?
(function(window, $) {
// 'use strict';
//setInterval(function () {alert("Hello")}, 3000);
var StreamTable = function(container, opts, data) {
return new _StreamTable(container, opts, data);
};
_F.search = function(text){
alert("search " + text);
var q = $.trim(text), count = 0;
// alert(this.last_search_text);
if (q == this.last_search_text) return;
this.last_search_text = q;
if(q.length == 0 ){
this.render(0);
}else{
this.searchInData(q);
this.render(0);
}
this.current_page = 0;
this.renderPagination(this.pageCount(), this.current_page);
this.execCallbacks('pagination');
};
}
I tried
onclick ="window.search(5)"
from a click button but still did not work.
The search function should be declared like this:
window.search = function(text){
This will attach the search function to the window object. This will allow you to attach it to the onclick event, like you have in your example:
onclick="window.search(5)"
I hope that somebody can help me.
I want to redeclare js function by extension.
For example, there is the basic js function on website:
function foo(){
..something here..
}
i want to redeclare it by own function with the same name. how it will be easiest to do?
edit 1. i'll try to explain better.
there is a native code in website:
Notifier = {
debug: false,
init: function (options) {
curNotifier = extend({
q_events: [],
q_shown: [],
q_closed: [],
q_max: 3,
q_idle_max: 5,
done_events: {},
addQueues: curNotifier.addQueues || {},
recvClbks: curNotifier.recvClbks || {},
error_timeout: 1,
sound: new Sound('mp3/bb1'),
sound_im: new Sound('mp3/bb2')
}, options);
if (!this.initFrameTransport() && !this.initFlashTransport(options)) {
return false;
}
this.initIdleMan();
if (!(curNotifier.cont = ge('notifiers_wrap'))) {
bodyNode.insertBefore(curNotifier.cont = ce('div', {id: 'notifiers_wrap', className: 'fixed'}), ge('page_wrap'));
}
},
destroy: function () {
Notifier.hideAllEvents();
curNotifier.idle_manager.stop();
curNotifier = {};
re('notifiers_wrap');
re('queue_transport_wrap');
},
reinit: function () {
ajax.post('notifier.php?act=a_get_params', {}, {
onDone: function (options) {
if (options) {
curNotifier.error_timeout = 1;
this.init(options);
} else {
curNotifier.error_timeout = curNotifier.error_timeout || 1;
setTimeout(this.reinit.bind(this), curNotifier.error_timeout * 1000);
if (curNotifier.error_timeout < 256) {
curNotifier.error_timeout *= 2;
}
}
}.bind(this),
onFail: function () {
curNotifier.error_timeout = curNotifier.error_timeout || 1;
setTimeout(this.reinit.bind(this), curNotifier.error_timeout * 1000);
if (curNotifier.error_timeout < 256) {
curNotifier.error_timeout *= 2;
}
return true;
}.bind(this)
});
}
}
and function Sound
function Sound(filename) {
var audioObjSupport = false, audioTagSupport = false, self = this, ext;
if (!filename) throw 'Undefined filename';
try {
var audioObj = ce('audio');
audioObjSupport = !!(audioObj.canPlayType);
if (('no' != audioObj.canPlayType('audio/mpeg')) && ('' != audioObj.canPlayType('audio/mpeg')))
ext = '.mp3?1';
else if (('no' != audioObj.canPlayType('audio/ogg; codecs="vorbis"')) && ('' != audioObj.canPlayType('audio/ogg; codecs="vorbis"')))
ext = '.ogg?1';
else
audioObjSupport = false;
} catch (e) {}
// audioObjSupport = false;
if (audioObjSupport) {
audioObj.src = filename + ext;
var ended = false;
audioObj.addEventListener('ended', function(){ended = true;}, true);
audioObj.load();
this.playSound = function() {
if (ended) {
audioObj.load();
}
audioObj.play();
ended = false;
};
this.pauseSound = function() {
audioObj.pause();
};
} else {
cur.__sound_guid = cur.__sound_guid || 0;
var wrap = ge('flash_sounds_wrap') || utilsNode.appendChild(ce('span', {id: 'flash_sounds_wrap'})),
guid = 'flash_sound_' + (cur.__sound_guid++);
var opts = {
url: '/swf/audio_lite.swf?4',
id: guid
}
var params = {
swliveconnect: 'true',
allowscriptaccess: 'always',
wmode: 'opaque'
}
if (renderFlash(wrap, opts, params, {})) {
var swfObj = browser.msie ? window[guid] : document[guid],
inited = false,
checkLoadInt = setInterval(function () {
if (swfObj && swfObj.paused) {
try {
swfObj.setVolume(1);
swfObj.loadAudio(filename + ext);
swfObj.pauseAudio();
} catch (e) {debugLog(e);}
}
inited = true;
clearInterval(checkLoadInt);
}, 300);
self.playSound = function() {
if (!inited) return;
swfObj.playAudio(0);
};
self.pauseSound = function() {
if (!inited) return;
swfObj.pauseAudio();
};
}
}
}
Sound.prototype = {
play: function() {
try {this.playSound();} catch(e){}
},
pause: function() {
try {this.pauseSound();} catch(e){}
}
};
when i try to add injection with redeclaration function Sound it doesn't work.
if i create my own function, for example, xSound and сall it this way:
cur.sound = new xSound('mp3/bb1');
it's working.
You can do it like this, for example:
foo = function(args) {
// method body...
}
JavaScript is a programming language where functions are first-class citizens so you can manipulate them like other types.
UPDATE:
Make sure that this piece of code actually does the redefinition and not the first definition. (thanks to #jmort253)
function foo(){
// ..something else here..
}
Remember that an extension's Content Script code and the webpage code run in different execution contexts.
So if you want to redefine a function that exists in the webpage context, you'll have to inject your code into the webpage. Take a look at this answer by Rob W for different methods of doing that:
Insert code into the page context using a content script