Archive for the ‘JavaScript’ Category



Fui parte de JSDayEs 2016

Este año tuve la suerte de que envié una propuesta el JSDayEs y la aceptaron. Sobre nuestra experiencia desarrollando con ES6, el nuevo standard. Dejo aquí el video de la charla dentro de la playlist de videos del evento.

Olvidé decir algunas cosas durante la charla, la más importante fue que con ES6 ya desparece la necesidad de usar el patrón IIFE que siempre resulta complejo de entender. Lo puse luego en las slides pero no lo dije durante la charla, estuve un poquito espeso.

En un evento con tanto nivel técnico, y yo que no me considero ningún gurú de JavaScript, el listón estaba muy alto. Sobretodo después de la apertura de Enrique Amodeo que ya con tantos años de experiencia a sus espaldas y tanto conocimiento, las ve venir de lejos. La charla de Enrique fue de las que más me aportó. Como para verla de nuevo tranquilamente. El viernes por la tarde estuve muy poquito en el open space porque estuvimos grabando parte del episodio 2 de NMNL por fuera del google campus. Así que llegué ya practicamente para las cañas y la cena. Estuvo genial el networking y la cena. Conocí gente super interesante (no diré nombres para no olvidarme a nadie) y lo pasamos estupendamente. La organización del evento fue brillante, recuerdo especialmente la apertura con Alex Fernandez corriendo por el recinto, saltando por encima de la gente y la producción de video que se curraron. Todo muy muy currado. El sitio por supuesto estupendo. Y en el desayuno y comida del sábado hubo opcion vegana para comer, que esto muy pocas veces pasa y termino comiendo pan y papas fritas con suerte. Muchos pequeños detalles que hicieron del evento uno grande. Algunas charlas como iban a ser grabadas preferí perdermelas para charlar con gente que estaba en la zona de descanso. A menudo cuando las charlas van a ser grabadas y se que no va a faltar público y que no van a ser interactivas, prefiero verlas grabadas en casa y aprovechar el evento para hablar con la gente. Y ahora que las he visto todas en vídeo pues debo decir que me han gustado todas. Estupendo que la duración de las charlas fuera cortita. Como posible punto de mejora se me ocurre quizás haber dejado alguna hora sin charla, sino para puro networking. Gracias a JSDay he establecido nuevas relaciones profesionales con personas muy afines en cuanto a valores y principios. Me permitió conocer más a gente del equipo de Kairós DS que ahora somos partners y debo decir que conocernos allí ayudó a tomar la decisión de asociarnos. O séa que no puedo pedirle más al evento. Simplemente que se repita. Agradecer y felicitar a la organización un pedazo de evento con un curro impresionante y una ejecución brillante.

During Socrates Conference 2015 we decided that it's the right time to jump in ES6 to developer a green field project that our customer is starting. Given that ES6 is already the stable and latest version of JavaScript, it does not make sense to start a new project with ES5, an already old version of the language. With the kind of functional style that we use when coding in JavaScript, the changes are not too big anyway as we are not going to use classes anyway. But we can leverage from block scope with "let" and "const" avoiding the use of "var" from now on. Configuring the tools has taken more time than I thought as the tooling and the ecosystem is changing really fast. You read a recipe from a blog post which is 6 months old and it turns out that most of the stuff described is no longer working. Some npm packages don't work anymore or the behavior at some point is very different from a previous version... apparently things are even more obscure on Windows which is the platform we have to use for this project.

As an example, I installed karma via npm the latest version. But when running gulp from the command line it stopped working with no error message at all, just a line break and back to the prompt. I commented all the lines in the gulpfile.js and then uncommented lines one by one executing gulp each time to discover that "require('karma')" was the reason. So I got into the node repl and type this myself:

var k = require('karma')

The result was the same, node repl exited silently getting me back to the command line prompt. I couldn't find a single way to catch the error although I tried try-catch, signal capturing, domains... and none of that worked. Then I started downgrading the version of the karma package until it worked for me. Version 0.13.3 works but 0.13.4 doesn't. It must be a very specific problem on my machine but I couldn't find any other solution. Eventually we are not using karma for now, we are using jasmine stand alone version and mocha.

This is the simplest gulpfile I was able to get working:

  1. var gulp = require('gulp');
  2. var sourcemaps = require("gulp-sourcemaps");
  3. var mainBowerFiles = require('main-bower-files');
  4. var browserify = require('browserify');
  5. var vinylSource = require('vinyl-source-stream');
  6. var glob = require('glob');
  7. var vinylBuffer = require('vinyl-buffer');
  8. var babel = require('babelify');
  9.  
  10. var globalNamespace = 'mynamespace';
  11. var launcher = './Scripts/src/main.js';
  12. var sourceFiles = 'Scripts/src/*.js';
  13. var specFiles = 'Scripts/spec/*.js';
  14. var libFiles = 'Scripts/dist/vendor/*.js';
  15. var distFolder = "Scripts/dist";
  16. var allFiles = [libFiles, sourceFiles, specFiles];
  17.  
  18. gulp.task('package-src', function () {
  19. var filenames = glob.sync(sourceFiles); // generate array of filenames
  20. return browserify({
  21. entries: filenames,
  22. debug: true
  23. })
  24. .require(launcher, {expose: globalNamespace}) // publish
  25. .on('error', function (err) {
  26. console.error(err); this.emit('end');
  27. })
  28. .transform(babelify)
  29. .bundle()
  30. .pipe(vinylSource('all.js')) // generated output file
  31. .pipe(vinylBuffer()) // required for sourcemaps
  32. .pipe(sourcemaps.init())
  33. .pipe(sourcemaps.write("."))
  34. .pipe(gulp.dest(distFolder));
  35. });
  36.  
  37. gulp.task('package-tests', function (done) {
  38. var filenames = glob.sync(specFiles);
  39. return browserify({
  40. entries: filenames,
  41. debug: true
  42. })
  43. .on('error', function (err) {
  44. console.error(err);
  45. this.emit('end');
  46. })
  47. .transform(babelify)
  48. .bundle()
  49. .pipe(vinylSource('specs.js'))
  50. .pipe(vinylBuffer())
  51. .pipe(sourcemaps.init())
  52. .pipe(sourcemaps.write("."))
  53. .pipe(gulp.dest(distFolder));
  54. });
  55.  
  56. gulp.task('package-vendor', function () {
  57. return gulp.src(mainBowerFiles({filter: '**/*.js'}))
  58. .pipe(gulp.dest(distFolder + '/vendor/'));
  59. });
  60.  
  61. gulp.task('default', [
  62. 'package-src',
  63. 'package-vendor',
  64. 'package-tests'
  65. ]);

The generated package is "all.js" which I include in the html page. The application's entry point is on main.js with exposes a function called startApp.

App starts up at the bottom of the html page:

  1. <script>
  2. var mynamespace = require('mynamespace'); // require function is provided by browserify, no need to include require.js
  3. mynamespace.startApp();
  4. </script>

main.js:

  1. import viewModel from "./viewModel";
  2. import restClient from "./restClient";
  3.  
  4. export function startApp() {
  5. let rc = restClient();
  6. let vm = viewModel(rc);
  7. ko.applyBindings(vm);
  8. };
  9.  

In order to run the tests the most simple choice was Jasmine stand alone, including the generated "specs.js" file in the SpecRunner.html page. As the tests include the production code, the generated file "specs.js" already include all the production code.

tests:

  1. import restClient from "../src/restClient";
  2. import viewModel from "../src/viewModel";
  3.  
  4. describe("make an vehicle order", ()=> {
  5. ...
  6.  

The next step was to include "watchify" in order to rebundle everytime a file is saved.

gulpfile.js:

  1. var gulp = require('gulp');
  2. var sourcemaps = require("gulp-sourcemaps");
  3. var mainBowerFiles = require('main-bower-files');
  4. var browserify = require('browserify');
  5. var vinylSource = require('vinyl-source-stream');
  6. var glob = require('glob');
  7. var vinylBuffer = require('vinyl-buffer');
  8. var watchify = require('watchify');
  9. var babelify = require('babelify');
  10.  
  11. var launcher = './Scripts/src/main.js';
  12. var globalNamespace = 'mynamespace';
  13. var sourceFiles = 'Scripts/src/**/*.js';
  14. var sourceBundle = 'all.js';
  15. var specFiles = 'Scripts/spec/**/*.js';
  16. var specBundle = 'specs.js';
  17. var libFiles = 'Scripts/dist/vendor/*.js';
  18. var distFolder = 'Scripts/dist';
  19. var allFiles = [libFiles, sourceFiles, specFiles];
  20.  
  21. gulp.task('package-src-dev', function() {
  22. bundleWatchify(sourceFiles, sourceBundle);
  23. });
  24.  
  25. gulp.task('package-src', function() {
  26. bundle(sourceFiles, sourceBundle);
  27. });
  28.  
  29. gulp.task('test-dev', function () {
  30. bundleWatchify(specFiles, specBundle);
  31. });
  32.  
  33. gulp.task('package-vendor', function () {
  34. return gulp.src(mainBowerFiles({filter: '**/*.js'}))
  35. .pipe(gulp.dest(distFolder + '/vendor/'));
  36. });
  37.  
  38. gulp.task('default', ['package-src', 'package-vendor', 'test-dev']);
  39. gulp.task('package-dist', ['package-src', 'package-vendor']);
  40.  
  41. function bundleWatchify(sources, output) {
  42. var watchified = watchify(doBrowserify(sources))
  43. .on('update', function (filenames) {
  44. console.log('rebuilding -> ', filenames[0]);
  45. rebundle(watchified, output);
  46. });
  47. return rebundle(watchified, output);
  48. }
  49.  
  50.  
  51. function bundle(sources, output) {
  52. return rebundle(doBrowserify(sources), output);
  53. }
  54.  
  55. function doBrowserify(sources) {
  56. var filenames = glob.sync(sources);
  57. var browserified = browserify({
  58. entries: filenames,
  59. debug: true
  60. });
  61. return browserified;
  62. }
  63.  
  64. function rebundle(b, output) {
  65. return b
  66. .require(launcher, { expose: globalNamespace })
  67. .on('error', function (err) {
  68. console.error(err);
  69. this.emit('end');
  70. })
  71. .transform(babelify)
  72. .bundle()
  73. .pipe(vinylSource(output))
  74. .pipe(vinylBuffer()) // required for sourcemaps
  75. .pipe(sourcemaps.init())
  76. .pipe(sourcemaps.write("."))
  77. .pipe(gulp.dest(distFolder));
  78. }
  79.  

This post has been written on September 14th 2015, if you try to use any of the snippets posted a few months later they probably won't work for you. Versions used:

package.json:

  1. {
  2. "name": "VehicleOrders",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "gulpfile.js",
  6. "devDependencies": {
  7. "babel": "5.8.23",
  8. "babelify": "6.3.0",
  9. "browserify": "11.0.1",
  10. "glob": "5.0.14",
  11. "gulp": "3.9.0",
  12. "gulp-babel": "5.2.1",
  13. "gulp-concat": "2.4.1",
  14. "gulp-sourcemaps": "1.5.2",
  15. "main-bower-files": "2.8.0",
  16. "vinyl-buffer": "1.0.0",
  17. "vinyl-source-stream": "1.1.0",
  18. "vinyl-transform": "1.0.0",
  19. "watchify": "3.4.0"
  20. },
  21. "scripts": {
  22. "test": "echo \"Error: no test specified\" && exit 1"
  23. },
  24. "author": "AIDA",
  25. "license": "ISC"
  26. }
JavaScript for testers workshop at LTGW2014

JavaScript for testers workshop

Yesterday, Jenny Mulholland (from Softwire) and I & ran our workshop on JavaScript for Testers at the London Tester Gathering Workshops 2014, at Skills Matter.

We had a nice group of participants with a wide range of kills and knowledge and we covered (roughly) multiple aspects of testing JavaScript apps. I had fun and we were busy during the workshop which is a good sign. Please if you were in the workshop give us some feedback using the comments in this post apart from the form that SkillsMatter will send to you, it will be very appreciated and will help us improve.

These are some of the resources we used:

Big thank you to Jenny for her invaluable help and support before and during the workshop. And to Tony Bruce and SkillsMatter for organizing this great two days of networking and workshops. It's fantastic to visit London from time to time and meet so many fascinating people.

Understanding JavaScript Errors

There is no magic in programming languages. The fact that they are formal languages makes them deterministic. For every behaviour there is a reason. For every error there is a logical cause.

Before googling for an odd error message, try to understand what the interpreter means. This is the fastest way to solve the problem.

I often see developers wondering why a certain test throws an error, whilst reading several times the production code, without really understand what's the meaning of the error. Wrong approach, one must know what the interpreter is saying before searching for the cause. It may be an error in the test itself!

The same error message may be expressed in a different manner by different browsers and other JavaScript interpreters like Node. This fact emphasizes the need for understanding what are they talking about.

In order to understand JavaScript error messages one must understand
what's the meaning of "undefined". Undefined is a special value.

  1. var x; // We are declaring a varible, whose value is undefined

In this case we say that variable "x" is undefined, is the type of the variable:

  1. typeof(x); // prints out the string "undefined" (as a string!).

The value of a variable is undefined until it's assigned a concrete
value:

  1. var x = 5; // x is a Number
  2. var x = function(){}; // x is a Function
  3. var x = {}; // x is an Object
  4. var x = true; // x is a boolean
  5. var x = undefined; // x is undefined

Now let's review errors containing the word "undefined" in the message:

TypeError: undefined is not a function (Google Chrome)

The interpreter says that something used as a function, is not a function actually, it's an undefined variable. Why does the interpreter assumes it's a function? Because there are braces after a name. When there is an open parenthesis right after a name, the interpreter determines that name is a function according to the grammar of the language. It's exactly like the question mark at the end of a sentence in English, we assume the sentence must be a question because of the mark at the end. The writer of the function was probably expecting the variable to be a function at that point but given some defect, it's not. This is how to reproduce the error:

  1. var x; // x is undefined.
  2. x(); // and... undefined is not a function, is just undefined!
  3.  

TypeError: Cannot read property 'length' of undefined (Google Chrome)

The error says there is a property named "length". A property is a member of an object. The interpreter assumes that "length" must be a property of an object because there is a dot preceding it. Properties are accessed with a dot:

  1. var obj = {propA: 123, propB: 456}; // an object with 2 properties
  2. var val1 = obj.propA; // reading property (123)
  3. var val2 = obj.propB; // reading property (456)

The error message says that the variable at the left of the "length" property is undefined, that is, the object is not actually an object, it's actually the undefined value. This is the way to reproduce the error:

  1. var obj; // obj is undefined
  2. obj.length; // trying to create or reference a property in an undefined value, raises an error.

If the "property" is accessed to set it a value, the error message will be slightly different:

  1. obj.length = 777; // TypeError: Cannot set property 'length' of undefined

TypeError: Cannot call method 'trigger' of undefined (Google Chrome)

This one is similar to the previous. It says "of undefined" expresses the sense of belonging. It must be related to an object. This time it's a method, not a property. It assumes that it must be a method because there are braces after "trigger". The error means that the value of the variable at the left hand side of the dot, is undefined:

  1. var obj;
  2. obj.trigger(); // trying to reference a method in an undefined value, raises error.

Node.js produces slightly different messages for the same errors:

  1. var x; // variable x is undefined
  2. x(); // TypeError: Property 'x' of object #<Object> is not a functio

In JavaScript everything happens within a certain environment. When the variable is defined out of any function its scope is global. In the case of the browser, the global environment is the "window" object. In the case of node, it's the "global" object:

  1. var x = 5;
  2. window.x; // 5 in the browser, error in node
  3. global.x; // 5 in node, error in browse

Node makes this fact explicit in the error messages.

TypeError: Property 'x' of object #<Object> is not a function (Node.js)

As there is not dot preceeding the "x", we know it's talking about
the "global" object. As we are using braces after "x", node assumes
it must be a function. Notice that it doesn't reveal the value of "x", which is undefined, it just says that "x" is not a function. So it would raise the same error if "x" was a number.

At the time of this writing, Firefox behaves a bit similar with regards to the value of the variable:

TypeError: x is not a function (Mozilla Firefox)

  1. var x;
  2. x(); // TypeError: x is not a function
  3.  

ReferenceError: x is not defined

I find this message a bit unfortunate. I would prefer "x is not declared" or "x does not exists". Because "not defined" seems to be the same as "undefined" but it's not. This error happens when the variable has not been declared but it's value is being accessed:

  1. var x = 10 + someNameNotDeclaredEver; // ReferenceErro

 

 

 

I am starting to work on a new project for a new customer, I'll join the team for a couple of days or perhaps weeks. They have been spiking and eventually have decided they want to develop the front-end using Marionette.js a library on top of Backbone.js together with Require.js. I've done a spike to show them how I'd structure the files, the app and the different kinds of tests.

The app is dead simple. The is a form with two fields, "description" and "url". When the user clicks on the button below, a new link is added underneath. So it's to add links to a panel.

Versions used in this working spike:

  • Jasmine.js - v2.0
  • Require.js - v2.1.14
  • Marionette.js - v2.0.1

The repo is here:  https://bitbucket.org/carlosble/marionette-testing-sample/overview

Structure:

  • bower_components - dependencies
  • node_modules - dependencies
  • index.html - launches the app
  • uiTests.html - launches Jasmine tests against the DOM, using Require.js
  • actionsTests.html - launches Jasmine tests to test business logic
  • js - this is the production code
    - app.js
    - views.js
    - models.js
    - actions.js
  • spec - these are the tests
    - viewsSpec.js - interact with the DOM
    - actionsSpec.js - no interaction with the DOM,  works on the business logic

With this structure the business logic don't have to depend on Marionette,  although it depend on Backbone models and collections.

Resources that help:

 

Teaching TDD

TDD class at the IronHack 2nd edition (picture by @JackLondon84)

Last week I was the teacher in the second edition of the IronHack, well for the last two days of the week. This time I've joined the group on their second week rather than the fifth. So I've been the one introducing unit tests. It's been quite challenging as it had to be using JavaScript. Many new concepts at the same time. Really hard for the participants but eventually absolutely all of them understood the benefits of unit tests and even the TDD cycle. They got the point with TDD. Fortunately,  my friend Fernando Moreno (who was a participant in the previous edition), was helping me out with the class. He was solving doubts and helping with technical problems and that made a huge difference. Being two people answering questions, was the key for the group to progress with steady pace. I am so grateful to Fernando, a great teacher assistant.

The group has a great potential, this training course is going to be a remarkable milestone in the life of many participants. It's been a pleasure meeting the folks.

And... you know what? pretty much all the participants from the first edition have a job already. And I know that some of them are really interesting positions. This is great news, I had the chance to talk with four of them and they are very enthusiastic about their new careers. This fact says a lot about the IronHack project and I am glad for being part of it. The more I know the project and the people behind it, the more I like it.

On Thursday evening Marta Fonda (from edition one) came up to give a talk on personal branding after my class, it was a nice surprise and we had a great time with beers afterwards.

On Friday I organized a small coding dojo in the same venue and I had the chance to pair with Sergio Revilla and Vishal  Shahdadpuri, from the first and second editions. We had a great time, I could appreciate Sergio's evolution which is impressive and we learned from each other. Pasku came out with a nice solution in Ruby. We ended up with a nice solution in JavaScript, also using a functional style.

I will back in the IronHack this summer in Barcelona. Check out their new courses on iOS development, in Madrid & Barcelona (summer and autumn).

Mock objects on JavaScript

Many frameworks use the word "mock" for what in reality are "spies". But that's OK, the work "mock" in English covers actually all the possible test double types...

However according to G. Meszaros, a mock object is a specific type of test double that is configured with expectations before exercising the system under test. During the execution the mock compares the calls being made, to its expectations failing if they don't match.

The two frameworks I use for test doubles in JavaScript are Jasmine and Sinon.js. And I also use some helpers of my own. None of these two frameworks provide actual mock objects. Sinon has a "mock" function that let us configure expectations before exercising the system under test. It behaves like a mock if we are working with functions only. Nonetheless if we are working with objects (OOP), then what Sinon provides is not a mock object because a call to any other method in the object other than the expected, will not fail the test. And it should as unexpected calls should make the test fail.

The solution to have real mock objects with Sinon is just a little helper:

  1.  
  2. //------ HELPER FUNCTIONS
  3. function unexpectedCall(functName)
  4. {
  5. return function(){
  6. throw new Error('Function ' + functName + ' was not expected but invoked');
  7. };
  8. }
  9. function inocuousCall(){
  10. return function(){};
  11. }
  12. function replaceAll(obj){
  13. return {
  14. methodsWith: function(fn){
  15. for (var propName in obj)
  16. if (typeof(obj[propName]) == 'function')
  17. obj[propName] = fn(propName);
  18. }
  19. }
  20. }
  21. function stubThe(obj){
  22. replaceAll(obj).methodsWith(inocuousCall);
  23. }
  24. function mockThe(obj){
  25. replaceAll(obj).methodsWith(unexpectedCall);
  26. }
  27.  
  28. //------ THE TEST
  29.  
  30. it("stores the user when adding a new one", function(){
  31. // the DOC (depended-on component)
  32. var repo = userRepository();
  33. // the SUT (system under test) and dependency injection
  34. var service = userService(repo);
  35. // patching the DOC, making it a mock
  36. mockThe(repo);
  37. var mock = sinon.mock(repo);
  38. // configuring expectation:
  39. mock.expects("store").once();
  40.  
  41. // exercising the SUT
  42. service.add(new User({email: 'test@test.com'}));
  43.  
  44. // verifying configured expectations
  45. mock.verify();
  46. });
  47.  

In languages like Java or C#, test doubles frameworks create a replacement for all the methods in the target class/interface. In JavaScript however, frameworks replace single functions. For this reason it's possible to have the same object acting as the SUT and the double in a single test. But the fact it's possible doesn't mean it's right. Whenever you find yourself in a test using a double for some method and then invoking another method in the same object, think carefully whether the design is appropriate. That smell is usually pointing out that you should extract a new class and create a dependency between the old and the new, so that one acts as the double and the other as the SUT.

Case study: Audience Response

Wow, AgileTestingDays 2013 it's being awesome! I gave my talk today, a practical "live coding" session. Last week I created a real-time application to communicate with the audience so that when I am speaking they can tell me if they are understanding or if they want me to repeat ...
So we started off using this app on my session. Interestingly enough the session was about building the tool again. From Cucumber specifications, all the way down to the GUI and the hub (server).

You can find the actual code of the application here and more importantly, the process I followed to write it, because I checked-in the code on every green test so by looking at the diffs, you'll figure out how code evolved.
Unfortunately the wifi didn't work well so I couldn't really take advantage of the app. Next time I'll bring my own wireless router to create our private LAN.

In order to prepare the session, I rewrote part of the app again myself. In here you can find this second iteration, again with a test committed at a time. By looking at the commits you can follow part of what I did during my session. You can take the code and practice yourself from this particular commit, comparing your moves with mine ones to see what we can learn.

Find the business specifications of the app here and the step definitions (glue) in here.

Now, the session didn't go bad, but it didn't go as well as I'd like. I did quite bad with the timing.I would have needed 90 minutes rather than 45 to illustrate the process properly. When I was preparing the talk, I wrote the code on my own and it didn't take much time, but presenting it's a different story, I've learned that I need about twice as much time.

2013-10-31 01.24.06But I am satisfied because several people understood the process and got value from it. Next time I run this session, it will go much better. And you know what? I've been approved by The Code Cop! Look at this picture.

 

I'll be happy if you were attending the talk and can give me some feedback in the form of a comment in this blog. As a reward, one person out of the people commenting will be randomly selected and I will run a free 90 minutes session for her/his company (remotely, videoconferencing), doing this same exercise properly with Q&A session.

James Lyndsay and Bart Knaack from The Test Lab have used an instance of the app for testing purposes and people have found several defects. I am happy for that because I didn't do any exploratory testing or even took care of network failures or latency problems. Thanks for that guys!

This exercise will be part of my upcoming book on Agile JavaScript for Rich Internet Applications. I expect to have the book done in 2014.

If you want to have a look at the sample deployed app on Heroku, use these urls:
Load a page in the browser with this url (as a speaker).
Then load the page as the audience in other window.
Then just interact.

Thank you very much for all your support, I really appreciated you invested your time on my talk. If are there questions please let me know of find me tomorrow in the conference to catch up or hang out 🙂

WebSockets and Android

I didn't know that the default browser installed on Android is called Webview. I haven't created any native Android app ever. But I wanted my web app to run on Android using Websockets. Unfortunately, websockets are not implemented in Android Webview.

In modern browsers, HTML5 provides a websocket API:

  1.  
  2. ws = new WebSocket('ws://' + host + ':'+ port);
  3. ws.onmessage = function(msg){
  4. var data = JSON.parse(msg.data);
  5. ws.send(JSON.stringify({message: 'OK cool!'}));
  6. };
  7.  

You don't need to include any JavaScript file in order for this to be valid code in modern browsers (when I saw it for the first time I was like.... "yeah cool but what library are you using?). Well you'd need a Websocket server. I've been trying ws for Node. It works very well with express.js and is very simple:

  1.  
  2. var WebSocketServer = require('ws').Server;
  3. var wss = new WebSocketServer({server: server}); // server is express' server instance.
  4. var clients = [];
  5.  
  6. wss.on('connection', function(ws) {
  7. console.log('connected');
  8. clients.push(ws);
  9. ws.on('close', function(){
  10. console.log('client closing');
  11. });
  12. ws.on('message', function(msg){
  13. console.log('message received');
  14. for (var i = 0, len = clients.length; i < len; i++)
  15. try {
  16. // do not use try-catch here, do it properly
  17. if (clients[i])
  18. clients[i].send(msg);
  19. }
  20. catch(e){
  21. clients[i] = null;
  22. }
  23. });
  24. });
  25.  

The code above is broadcasting a message to all connected clients.

It's very nice but it doesn't work on Android!

Workarounds:

  • Install Firefox or Chrome for Android, they support Websockets
  • Use a library that supports fall back to XHR long polling like Socket.io
  • Use Apache Cordova (phonegap) to create an Android app
  • Use a Flash implementation of websockets

If you use Apache Cordova you need to "provide" the websockets funcionality into webview using Java code. There are several plugins for that:

I haven't used any of those. The disadvantage is that users have to install an application. I just want them to use my web app without installing anything so I've discarded this option.

The flash implementation has some security constraints imposed by flash. I haven't dug into the subject much but I believe I need to open port 843 on the server and I can't do that on some PAAS providers. I can't confirm this, I might be wrong. The other thing is that browser needs the flash plugin. Take a look at this implementation (web-socket-js).

Eventually I've come back to Socket.io. Although people say it's outdated and it's got a poor websockets RFC implementation, it works very well for basic stuff and falls back to XHR long polling automatically:

  1.  
  2. var io = require('socket.io').listen(server); // server is express' server instance
  3. io.configure(function () {
  4. io.set("transports", ["websocket", "xhr-polling"]); // support both
  5. io.set("polling duration", 10);
  6. });
  7.  
  8. io.sockets.on('connection', function(socket){
  9. socket.on("sendMessage", function(msg){
  10. io.sockets.emit("messageReceived", msg); // broadcasting
  11. });
  12. });
  13.  
  14.  

On modern browsers implementing websockets, it will use them. On Android it will just use long polling.

To finish off, just remember that your mobile device and your desktop pc are two separate machines. If your client side js is trying to connect to "localhost" as the websockets server, it's not going to work from your mobile. I know it's absolutely obvious but I wasted more time than I expected not realizing that I had the wrong url in the client side js. It was written "localhost" rather than "192.168.0.100" for my tests using the Local Area Network. It was one of those silly mistakes that steal one's time more than they should 🙁

Fluent API for test doubles

There are several good frameworks out there to create test doubles with JavaScript. Jasmine  and Sinon provide test spies and stubs. JsMockito looks good too. However, creating your own API for test stubs is very easy. I've created mine as an exercise. It's a very naive implementation but it works. See the code:

  1.  
  2. describe("a fluent API to create a test stub", function(){
  3. it("stubs out a method", function(){
  4. // the fluent API:
  5. function when(stub){
  6. return {
  7. thenReturn:function(arg1){
  8. stub.configureOutput(arg1);
  9. }
  10. };
  11. }
  12. function stub(actual){
  13. var expectedArgs,
  14. configuredOutput,
  15. isConfigured = false;
  16. actual.configureOutput = function(output){
  17. configuredOutput = output;
  18. isConfigured = true;
  19. }
  20. actual.someMethod = function(){
  21. if (isConfigured){
  22. if (expectedArgs[0] == arguments[0])
  23. return configuredOutput;
  24. return;
  25. }
  26. else
  27. expectedArgs = arguments;
  28. return actual;
  29. };
  30. return actual;
  31. }
  32.  
  33. // the test confirming it works:
  34. var actualObject = {someMethod: function(a){return 1+a;}};
  35. var theStub = stub(actualObject);
  36.  
  37. when(theStub.someMethod(2)).thenReturn(5);
  38. expect(theStub.someMethod(2)).toBe(5);
  39. });
  40. });
  41.  

The implementation is not generalized to support any method or any number of parameters. I just wanted to play with the idea that I can invoke the stubbed method in the "when" statement and it doesn't execute anything apparently. It only executes the stubbed behavior once it's been configured.

It's very simple but it didn't come up to my mind when I implemented pyDoubles (now Doublex), which might have made the API even better.