Meteor.js: Getting Started

· 19 Nov 2014 · Community

Meteor.js tutorial with meteor.js tests.

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:

$ 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

$ meteor create todos

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

$ 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.

$ 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.

$ 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:

$ 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:

$ 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.

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

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

$ 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.

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.

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.

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.

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.

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.

<head>
  <title>todo_app</title>
</head>

<body>
  <h1>Welcome to Meteor!</h1>

  {{> todos}}
</body>

<template name="todos">
  {{#each todos}}
    {{> todo}}
  {{/each}}
</template>

<template name="todo">
  <p>{{name}}</p>
</template>

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.

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.

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.

<template name="createTodo">
  <input id="newTodo" type="text">
  <button>Save</button>
</template>

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

<template name="todos">
  {{#each todos}}
    {{> todo}}
  {{/each}}
  {{> createTodo}}
</template>

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.

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.

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.

$ 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.

comments powered by Disqus
Newsletter

Occasional lightweight product and blog updates. Unsubscribe at any time.

© 2009-2017 Rendered Text. All rights reserved. Terms of Service, Privacy policy, Security.