24 Nov 2021 · Semaphore News

    Meteor.js: Getting Started

    8 min read
    Contents

    MeteorJS stands out among the recent big Javascript frameworks with its radical view on creating web applications. It seamlessly connects the client side code with a Node + MongoDB based backend and promises unbelievably fast application development.

    As an introduction to Meteor.js, I set out to build a simple but properly tested web application and share what I learned.

    Installing the Meteor.js framework

    Meteor has a one line install process based on a script from its website:

    sh
    $ curl https://install.meteor.com | /bin/sh
    

    The above command installs all the needed dependencies along with a MongoDB database. After this you are ready to start writing applications.

    Creating an example Todo application

    sh
    $ meteor create todos
    

    Right after the above command finishes you can launch a working web application with the following:

    sh
    $ cd todos
    $ meteor --port 3000
    => Meteor server running on: http://localhost:3000/
    

    Meteor.js tutorial initial screen.

    Unit tests

    Meteor works with various testing frameworks and test runners. One of the most popular test runners is Velocity that supports Javascript test frameworks like Jasmine and Mocha.

    Jasmine — my favorite Javascript test environment — is easy to set up with Meteor and Velocity.

    sh
    $ meteor add sanjo:jasmine
    

    In Meteor you should put all your tests in a tests directory. Before you start writing your own tests, first create a directory structure like the following one, as required by sanjo:jasmine.

    sh
    $ mkdir -p tests/jasmine/server/unit
    $ mkdir -p tests/jasmine/client/integration
    

    Velocity also offers a nice HTML reporter that overlays your application and shows a red/gray circle in the upper right corner of the application.

    Meteor.js velocity tests.

    When you click on the circle it will list all the applications test statuses.

    Meteor.js test run.

    You can install it with the following command:

    sh
    $ meteor add velocity:html-reporter
    

    Unfortunatelly, in the moment of writing this blog post, there is no standard way to run Velocity tests from the command line. A Node package velocity-cli is trying to change that, at least until there is no meteor test command.

    Integration and acceptance tests

    For integration tests you can use Nightwatch which will run Selenium-based browser tests on your application. To install it for your application, run:

    sh
    $ meteor add clinical:nightwatch
    

    Unfortunately, the setup for the rest of the framework is a little painful. First you need to install npm and node, and java runtime.

    sh
    $ sudo apt-get install nodejs
    $ sudo apt-get install openjdk-7
    

    After which you should execute the following installation steps for nightwatch.

    sh
    $ ln -s .meteor/local/build/programs/server/assets/packages/clinical_nightwatch/launch_nightwatch_from_app_root.sh run_nightwatch.sh
    $ sudo chmod +x run_nightwatch.sh
    $ sudo ./run_nightwatch.sh
    

    Your integration tests should be placed inside tests/nightwatch directory.

    Writing a todo application

    To demonstrate the power and expressiveness MeteorJS, I will walk you through the design process of a simple Todo application. This application will have only two features. It will show all todos a user saved in the database, and it will allow us two add more todos.

    The server and the client in a MeteorJS application are interconnected, and as a matter of fact, can be even defined in the same file. Let’s create a file named todos.js in the projects root and initialize a Mongo model that can store todos for our application.

    javascript
    Todos = new Mongo.Collection("todos")
    

    In order to handle and check the validity of newly created todos, a class that represents a Todo can come quite handy. Right under the definition of the Todos collection we can create a Todo class.

    javascript
    function Todo(name) {
      this.name = name;
    }
    
    Todo.prototype = {
      valid: function() {
        return this.name && this.name != "";
      },
    
      save: function() {
        Todos.insert({name: this.name});
      }
    };
    

    As a good TDD practitioner we should also create a matching test using Jasmine. We can define a test inside the tests/jasmine/server/unit/todo_spec.js file. At the moment we’re still curious and learning, hence implementation first.

    javascript
    describe("Todo", function() {
    
      beforeEach(function() {
        this.name = "wash dishes"
        this.todo = new Todo(this.name);
      });
    
      it("accepts name", function() {
        expect(this.todo.name).toEqual(this.name);
      });
    
      describe("#save", function() {
    
        beforeEach(function() {
          spyOn(Todos, "insert");
    
          this.todo.save();
        });
    
        it("inserts into the database", function() {
          expect(Todos.insert).toHaveBeenCalled();
        });
      });
    
    });
    

    Filling the database on startup

    On application startup we can invoke various actions. For example we could prefill our database with Todos if there is no todo defined.

    Put the following in the todos.js file.

    javascript
    function prefillTodos() {
      if(Todos.find().count() !== 0) return;
    
      var todos = ["Create a Todo", "Learn MeteorJS"];
    
      todos.forEach(function(todo) { new Todo(todo).save(); });
    }
    
    if (Meteor.isServer) Meteor.startup(prefillTodos);
    

    And its tests in the tests/jasmine/server/unit/prefill_todos_spec.js file.

    javascript
    describe("prefillTodos", function() {
    
      beforeEach(function() {
        spyOn(Todos, "insert");
        spyOn(Todos, "find").and.returnValue({count: function() { return 0; }})
    
        prefillTodos();
      });
    
      it("initializes the database with default todos", function() {
        expect(Todos.insert).toHaveBeenCalledWith({name: "Create a Todo"});
        expect(Todos.insert).toHaveBeenCalledWith({name: "Learn MeteorJS"});
      });
    
    });
    

    Templates

    MeteorJS uses templates to display information to the user. To create a HTML view for the todos, first create a todos.html in the project root and create a todo view like the following.

    html

    Welcome to Meteor!

    
    
      {{> todos}}
    
    
    
      {{#each todos}}
        {{> todo}}
      {{/each}}

    {{name}}

    
    
    

    This file contains regular HTML notation, spiced up with some curly braces. These curly braces are Handlebars-like constructs and allow us to dynamically insert data in our Html views.

    Each template can receive arbitrary JSON data that can be used to parameterize the output of that template. To invoke the rendering of a template MeteorJS uses the {{> templateName}} notation.

    Each of these templates can have a helper or an event handler.

    Helpers are handy methods that we can invoke inside the definition of our templates. Event handlers let us react to various Javascript events. The following code segment shows how to set up a helper method for our todos template.

    Append the following snippet to the todos.js file.

    javascript
    function setUpTodoTemplates() {
      Template.todos.helpers({
        todos: function () {
          return Todos.find();
        }
      });
    }
    
    if (Meteor.isClient) setUpTodoTemplates();
    

    Testing templates

    In MeteorJS there is a custom of testing templates with Jasmine. We should start with testing our todos template in the tests/jasmine/client/integration/todos_template_spec.js file.

    javascript
    describe("TodosTemplate", function() {
    
      beforeEach(function() {
        var div = document.createElement("div");
        var data = [{name: "test1"}, {name: "test2"}];
    
        var comp = Blaze.renderWithData(Template.todos, data);
    
        Blaze.insert(comp, div);
      });
    
      it("contains all the passed todos", function() {
        expect($(div).children().length()).toEqual(2)
      });
    
    });
    

    Creating new todos

    Combining the above knowledge, we can easily extend our todo application with a form that creates new todos.

    We should start with a template in our todos.html file.

    html
    
      
      
    
    

    Now, insert a template render invocation inside our todos template.

    
      {{#each todos}}
        {{> todo}}
      {{/each}}
      {{> createTodo}}
    
    

    To handle button clicks from out createTodo template, we should add an event handler inside the setUpTodoTemplates. The following code snippet registers an event handler that will create a new Todo.

    Put the snippet inside the setUpTodoTemplates in the todos.js file.

    javascript
    Template.createTodo.events({
      'click button': function() {
        var input = $("#newTodo");
        var name = input.val();
    
        input.val("")
    
        new Todo(name).save();
      }
    });
    

    An acceptance scenario for the above action could be written as the following inside a tests/nightwatch/creatingTodos.js file.

    javascript
    module.exports = {
      "Adding a Todo" : function (client) {
        client
          .url("http://localhost:3000")
          .waitForElementVisible("body", 1000)
          .pause(10000)
          .assert.visible("input[type=text]")
          .assert.visible("button")
          .setValue("input[type=text]", "Test Todo")
          .click("button")
          .assert.containsText("body", "Test Todo")
          .end();
      }
    };
    

    To run this end-to-end test run the following.

    sh
    $ sudo bash run_nightwatch.sh
    

    Final screenshot and source code

    Meteor.js finished todo application.

    You can clone the source code for the whole todo application from my Github repository.

    Summary

    Meteor is an exciting new technology that allows us to create eventful application with ease. The Meteor platform seems to be quite stable, however the testing libraries seem to be lagging behind and there is a lack of good documentation.

    I encountered several stumbling blocks while working with tests, when it was hard to find any information on the issue. For example incompatibilities between Nightwach and the version of Firefox I was using, running integration tests for the client side sometimes unexpectedly failed with an error or exception. Fortunately each of the test libraries are under heavy development and I am expecting high level of test integration in the near future.

    Finally, here is a list of articles that helped me understand Meteor better.

    MeteorJS Homepage
    Bulletproof MeteorJS
    Setting up selenium and nightwatch

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar
    Writen by:
    Chief Architect at Semaphore. A decade of experience in dev productivity, helping close to 50,000 organizations with operational excellence.