Category Archives: software development

Deploying with Ember.js: a story

What is this all about?

This is the story of how we changed our Ember.js application deployment one night while no one was looking. This is not a strictly technical story, and the details may not matter; what is important is the journey we took and the steps you can follow to get your app deploying however you want.

Table of Contents

  1. What is this all about?
  2. Some Background (feel free to skip)
  3. Our Goals (the whole point of this)
  4. Our Journey
    1. Starts with a plan on a whiteboard
    2. Leads to Ember-cli-deploy
  5. The End
  6. Afterwords

Some Background (feel free to skip)

Our application – Ilios – is an installed solution and the code is in two parts. The API and management code is written in PHP and is installed by medical schools all over the world. Each installation is a little bit different, but they all require and consume our Ember.js frontend application. We don’t ship the frontend with the API codebase, it is provide separately and updated far more frequently. In the past we have shipped an index.html file with links to our AWS Cloudfront distribution containing the assets. This way the assets are stored in a CDN where they can be loaded quickly by users and easily kept up to date by our team without needing to understand each individual installation.

Our Goals (the whole point of this)

In order to take advantage of http/2 push and service workers we need to change the way the frontend is deployed. Our goal is to ship a single archive file containing the frontend assets including the app code, service workers, images, styles etc…

This archive must be tagged in such a way that we can have many versions in the wild at the same time with a default active version. Because the code for our API does change it must be possible to activate versions in such a way that v23 is the default frontend for API v1.1 and v34 is the default frontend for API v1.3.

Once the archive is downloaded and extracted by the API server it must be simple to parse and customize the files so that scripts and styles can be sent to the browser using http/2 push LINK headers and service workers can be placed at the root of our domain (where they must be do be effective).

Because we are constantly adding new features and fixing bugs we need to be able to release versions continuously.

TLDR

We want to ship a magical box to our customers and we want to ship it all the time.

Our Journey

Starts with a plan on a whiteboard

Distribute

  1. Build the app
  2. Archive the build artifacts
  3. Tag the archive with a version which combines both a unique ID for the code as well as the API version it works with
  4. Upload the archive to an S3 bucket
  5. Mark the most recent version so it is easy to find

Consume

  1. Download and extract the archive at
    • a) the most recent version
    • b) a specific version from the past
    • c) dev build we want to test
  2. Parse the the index file to extract configuration and assets
  3. Serve a modified index.html file to users

Leads to Ember-cli-deploy

This isn’t a surprise ember-cli-deploy has both the builtin tools and a plugin ecosystem to make deploying Ember.js apps very manageable. All we need to do is assemble a list of plugins that can meet our needs. As is the case with many ember addons, our journey really starts with Ember Observer where we find there is a whole category dedicated to plugins for ember-cli-deploy.

Some investigation and perusal of the deploy docs and a few minutes in the #ec-deploy channel in the Ember Slack Team will lead us to some standard choices and well supported solutions.

  • ember-cli-deploy is the foundation we can build on
  • ember-cli-deploy-build builds our app
  • ember-cli-deploy-revision-data can tag and activate versions
  • ember-cli-deploy-s3 uploads our assets and makes them public in an S3 bucket

A search for ‘archive’ in Ember Observer leads us to:

  • ember-cli-deploy-archive which takes our build assets and outputs a single tar archive

These combined knock out several of our needs.

Further investigation of ember-cli-deploy-s3 leads us to it’s companion ember-cli-deploy-s3-index which not only uploads to S3, it also manages versioning and activation.

And while looking around we also stumble upon ember-cli-deploy-json-config which conveniently parses our messy index.html file and outputs nice human and machine readable JSON that will be way easier for our API server to consume.

Looks like we don’t have to write any code at all! Just install and configure some plugins just the way we want and in the end just need to run a few instal commands:

ember install ember-cli-deploy
ember install ember-cli-deploy-archive
ember install ember-cli-deploy-build
ember install ember-cli-deploy-display-revisions
ember install ember-cli-deploy-json-config
ember install ember-cli-deploy-revision-data
ember install ember-cli-deploy-s3-index

Which helpfully creates a config/deploy.js file for us with some useful defaults:

module.exports = function(deployTarget) {
  var ENV = {
    build: {}
    // include other plugin configuration that applies to all deploy targets here
  };

  if (deployTarget === 'development') {
    ENV.build.environment = 'development';
    // configure other plugins for development deploy target here
  }

  if (deployTarget === 'staging') {
    ENV.build.environment = 'production';
    // configure other plugins for staging deploy target here
  }

  if (deployTarget === 'production') {
    ENV.build.environment = 'production';
    // configure other plugins for production deploy target here
  }

  // Note: if you need to build some configuration asynchronously, you can return
  // a promise that resolves with the ENV object instead of returning the
  // ENV object synchronously.
  return ENV;
};

We just need to configure our AWS info and we will something working. Note: I removed targets here as well as comments for brevity.

module.exports = function(deployTarget) {
  var ENV = {
    build: {}
    's3-index': {
      accessKeyId: '<our-access-key>',
      secretAccessKey: '<our-secret>',
      bucket: '<our-bucket-name>',
      region: '<our-bucket-region>'
    },
  };

  return ENV;
};

Next step is to try it out:

ember deploy development  --verbose
...
...
Pipeline complete

And when we got look in our S3 bucket low and behold there is an build.tar:HASH file in there! We’re really getting somewhere.

That version is nice, but remember we really need to know what API versions this build is compatible with. Oh, I see there is a prefix option we can use.

's3-index': {
  ...
  prefix: 'v1.22'
},

Deploy again and we now have a bucket with

build.tar:HASH (from our first deploy)
v1.22/
  build.tar:NEWHASH (from our second deploy)

That is exactly what the doctor ordered. Check that off our list and lets download that archive and see what it has. Hmmm… it has an index.html file, but no index.json file. Looking back at the --verbose output we can see why

+- didBuild
|  |
|  +- archive
- saving tarball of tmp/deploy-dist to tmp/deploy-archive/build.tar
- tarball ok
|  |
|  +- json-config
- generating `tmp/deploy-dist/index.json` from `tmp/deploy-dist/index.html`
- generated: `tmp/deploy-dist/index.json`
- added `index.json` to `context.distFiles`

The build.tar file is getting generated before the index.json file has been created. Since both of these plugins are doing their work inside the didBuild hook we just need to swap the order they run in. We can find out how to do that in the docs at http://ember-cli-deploy.com/docs/v1.0.x/configuration/#advanced-plugin-configuration

Looks like we just add a pipeline object to our deploy config like:

pipeline: {
  runOrder: {
    'archive': { after: 'json-config' },
  },
},

and… done! running ember deploy development again gives us exactly the build.tar we’re looking for.

The End

Seriously. That’s how the story ends. No brave journey into the darkness of code comments to figure out just how this is supposed to work. It just works. TM. It’s so simple we can just add it as a command at the end of our CI process and never think about it again.

Afterwords

I’m 100% head over heals in love with ember-cli-deploy. I find it incredibly freeing to be able to tinker with our deployment process to find just the right setup without needing to design and script each pice every time. The plugin ecosystem gives me the building blocks I can use to assemble our pipeline in whatever way makes sense so we can test new ideas and strategies fast and with very little friction.

I hope this was a useful overview of one deployment journey. Want to share your own? Tell me about a mistake I made or shower me with praise? Leave a comment here or find me at @iam_jrjohnson.

Improving Summon/EZproxy/Shibboleth Login Experience With Custom JavaScript

If you use Summon’s custom auth banner/VPN banner with Shibboleth and EZproxy, you may have noticed an unfortunate side effect. If you do a search, and then click the banner to log in, you will be redirected to the start page rather than back to your search results.

Fixing this means tapping into their app with custom JavaScript. The front end of Summon is built on AngularJS 1.2.27 and ships with jQuery.  Here’s how we’re currently doing it:

(function () {
  var waitingForInitialLoad = true;

  var fixBannerLink = function () {
    var links = $('.vpnBanner.customAuthBanner div a');
    if (waitingForInitialLoad && links.length === 0) {
      setTimeout(fixBannerLink, 100);
      return;
    } else {
      waitingForInitialLoad = false;
      window.addEventListener('hashchange', fixBannerLink);
    }
    
    links.each(function() {
      if (/^Off the UCSF network/.test(this.text)) {
        $(this).attr('ng-href', 'https://ucsf.idm.oclc.org/login?qurl=' + encodeURIComponent(location.href));
        $(this).attr('href', 'https://ucsf.idm.oclc.org/login?qurl=' + encodeURIComponent(location.href));
      }
    });
  }

  $().ready(fixBannerLink);
}());

Most embarrassing hack: The /^Off the UCSF network/ regular expression is of course matching the text of the link. You’ll need to update that to match the text in your own link. (Or find a less embarrassing hack and submit it as a pull request in the GitHub repository.)

Also, our EZproxy URL prefix is hard-coded in there a few times. Change to your own, and make sure you use the qurl URL parameter rather than url.

If you have any questions, feel free to open an issue in the GitHub repository.

Rails and Ember without rails-api

This is a follow up to a post I wrote a couple months ago, “Ember and Rails Scaffolding for Simple Crud”. In that post, I gave an overview for how to generate simple CRUD scaffolding for an Ember app using Rails purely as an api through the rails-api gem.

Here’s the thing… if you take the api-only approach, you by design give up the ability to write standard Rails views. This can be a good thing, of course, which is why the gem was integrated into Rails 5. If all you want is an api, you don’t want the extra weight of everything else that comes with Rails, and you always can add various gems back in as needed. But for now, you may want to preserve the ability to write a full MVC rails app while still providing an API for Ember or other single page javascript frameworks.

Fortunately, this isn’t especially difficult. The Rails side will get a little more verbose, at least the way I’m writing it, but all you need to do is ensure that your app responds to both html and json – and of course be particularly careful to make sure that you don’t mix view logic with backend logic.

So, here we go…

First, create a full rails app with basic CRUD for the User model in the previous tutorial. I’m not going to repeat the steps here since they won’t change much. The only difference here is that instead of doing this with the rails-api gem and command, you’ll now do this with traditional rails. You will still need to create serializers, add rack/cors, allow access to various HTTP actions in the Rails app, and so forth. This is all available through the previous tutorial, with one change – you don’t need to install the rails-api gem, and wherever it says “rails-api generate…”, instead just use “rails generate…”.

You should now have a fully functional rails app for CRUD operations on a User that also provides json formatting as an api. The main difference between the api for a traditional Rails app and the rails-api generated app is that the traditional rails app responds by default as html, whereas rails-api responds as json. To get a json response from the traditional rails app, you will need to append “.json” to the url – in other words, to get the list of users rendered as json rather than displayed as html, you’d need to request:

http://localhost:3000/users.json

whereas the rails-api version doesn’t require this extension, as a rails-api app by default returns json (and wouldn’t normally respond as html at all).

On the Ember side, we need to instruct the adapter to specifically request json from the Rails app, as this is no longer the default Rails response.

To accomplish this, we will modify the Ember adapter in app/user/adapter.js

import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  host: 'http://localhost:3000',
  buildURL: function(record, suffix) {
        var s = this._super(record, suffix);
        return s + ".json";
    }
});

As you can see, this will append “.json” to all the requests send from Ember to Rails – even post, put, and delete requests , so you’ll need to explicitly handle the json format in any Rails controllers you intend to make available to Ember. As a result, we’ll need to modify the update and create methods in the Rails controller to specifically respond with json for Ember.

There is, inevitably, one more wrinkle – although Rails does respond by default to the “.json” extension, Ember expects a slightly different formatting, so you’ll need to make a few tweaks to get it working with Ember. Here’s the full controller code:

class UsersController &lt; ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
    
    #render json: @users
    
    respond_to do |format|
      format.html
      format.json { render json: @users }
    end
  end

  # GET /users/1
  # GET /users/1.json
  def show
    respond_to do |format|
      format.html
      format.json { render json: @user }
    end
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end

  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name)
    end
end

You may notice some additional code in create and update. This is because we need to respond as json for Ember, which we configured to submit all requests with the .json extension (even POST and PUT requests).

At this point, you can bring up both a Rails app on port 3000 and an Ember app on port 4200 and use both a standard Rails view and the Ember client for CRUD operations on your User model.

This does require some extra overhead, but it does keep open the possibility of writing a traditional Rails app while providing an API for not just Ember but any other app that might want to consume a Rails API.

Ember and Rails Scaffolding for Simple CRUD

Like many developers who use Rails, I haven’t thought about scaffolding for a while, but I found myself wanting something like it when I started looking into Ember as a front end for Rails. As with Rails, my guess is that I won’t use Ember scaffolding for long, but I wanted a way to get that initial Ember application up and running, with Rails as a backend.

It turns out that there are easy to use scaffolding generators for an Ember front end and a Rails API backend, with just a few minor gotchas you need to be aware of when integrating the two.

For this tutorial, we’re going to do the simplest thing possible for a crud app. We will create a single model (User), with one field (name), and we will create a web app with Create, Retrieve, Update, and Delete operations. Ember will supply the front end, Rails will handle the back end.

One quick note – while there are ways to integrate Rails with Ember in the same application, this tutorial will build the Ember frontend out as a completely separate, stand-alone app from the Rails backend. This way, the front end can be integrated with any backend that provides the API that Rails provides.

Here we go…

Part 1 – Create an Ember front end

We’ll create a client side, front end application that provides an interface for CRUD operations.

Step 0 – Install ember and ember-cli.

This will also require installing node, npm. I’m pretty sure you’ll need git as well. I’m not going to cover all this (or the process for installing Ruby or Rails). There are plenty of guides on the web to getting all this installed. Just make sure you can execute the following command

ember -v

I’m using ember 1.13.8, node 0.12.6, and npm 2.13.4 (on Mac OS X).

Step 1 – Create an ember app

ember new ember-crud

Step 2 – Install the ember-cli-scaffold package

cd into ember-crud and run

ember install ember-cli-scaffold

(for more information, see https://www.npmjs.com/package/ember-cli-scaffold)

Step 3 – Generate scaffolding for a Model (User) with one field (Name)

ember generate scaffold user name:string --pod

The “pod” option creates a different organizational structure for your files than the standard ember defaults. I prefer it and will be using it here, but the differences are very minimal.

Step 4 – Verify that your app is working

ember serve

and go to http://localhost:4200/users

You should see a very Rails-ish interface with full CRUD for a user with a single input field, name. Go ahead and create, edit, delete a few to verify that it is all working.

The ember CRUD app is using a local dev storage provided by mirage. In the next step, we’ll swap this out for a Rails backend.

Part 2 – Provide a Rails API for CRUD operations

Ember is already providing the view, so rather than creating a full blown rails app, we will limit the Rails app to providing an API for persistence.

Step 0: Install Ruby and Rails

As with Ember, there are lots of resources on the web for getting Ruby and Rails installed. Make sure that you can run

ruby -v

(I’m using 2.1.0)

and

rails -v

(I’m using Rails 4.2.0. You will need this version or later for Rails to use the rails-api gem, which I believe will be included in Rails 5).

Step 1 – Create a rails API only application

Install the rails-api gem

gem install rails-api

And generate a new Rails application

rails-api new rails-crud

Step 2 – Create an API scaffold for CRUD operations for a User

cd into rails-crud and type

rails-api g scaffold user name:string

While I promised not to go into a lot of detail here, you may notice that no view tier is created, and if you look at the user controller, you’ll see that it is set up for rendering json, not html.

Step 3 – Seed with a bit of data

Technically, you don’t need to do this step, since you’ll populate from your Ember app, but it can help to verify everything is working properly on the Rails side before integrating with your Ember app.

in db/seeds.rb, create something like this

user1 = User.create(name: 'user1')
user2 = User.create(name: 'user2')

and run

rake db:migrate
rake db:seeds

Step 4 – Check out the Rails API

run:

rails server

go to localhost:3000/users

and you should see a json representation of the two users you created in the seeds.rb file.

Part 3 – Use the Rails API service as a backend for the Ember application

This is relatively straightforward, though there are a few wrinkles.

Step 1 – Modify the way Rails is rendering JSON for a list of objects

Take another look at the json returned from http://localhost:300/users

[{"id":1,"name":"user1","created_at":"2015-10-21T22:17:32.778Z","updated_at":"2015-10-21T22:17:32.778Z"},{"id":2,"name":"user2","created_at":"2015-10-21T22:17:32.783Z","updated_at":"2015-10-21T22:17:32.783Z"}]

You may notice that Rails has flattened this into a single array. Ember, by default, expects a slightly different formatting where the json array of User objects is stored in a hash with the model type as a key.

One approach to this problem is to use a serializer to establish the format for JSON output from the rails api.

Add the serializer to your Gemfile

gem 'active_model_serializers', '~>; 0.8.3'

and run

bundle update

and create a new serializer for the user model

rails g serializer user

This will create a user_serializer.rb file in app/serializers.

class UserSerializer < ActiveModel::Serializer
  embed :ids, embed_in_root: true
  attributes :id, :name
end

This code will format users the way Ember expects it at the defaults, and will include only the id and name that are expected by the Ember model we created earlier (the various Rails additions like created_at or updated_at will not be serialized and sent to Ember as JSON).

NOTE:

I recently tried this with ember-cli 2.11.0, and it looks like the formatting for JSON may have changed since I wrote this. To get this working, I had to create a new file named json_api.rb in the initializers folder containing the following code (per this tutorial from EmberIgniter).

ActiveSupport.on_load(:action_controller) do
  require 'active_model_serializers/register_jsonapi_renderer'
end

ActiveModelSerializers.config.adapter = :json_api

Once you’ve made these changes, reload http://localhost:3000/users Or go to one of the individual users http://localhost:3000/users/1 You should see the following change to the json representation

{"users":[{"id":1,"name":"user1"},{"id":2,"name":"user2"},{"id":3,"name":"user1"},{"id":4,"name":"user2"}]}

Step 2 – Tell Rails to allow Ember to use the API

For security reasons, Rails by default won’t allow an application running on a different port to access the API. To solve this, add the following to your Gemfile

gem 'rack-cors', :require => 'rack/cors'

And add the following configuration to your Rails config/application.rb file

class Application < Rails::Application

config.middleware.use Rack::Cors do
   allow do
     origins '*'
     resource '*', headers: :any, methods: [:get, :post, :put, :delete, :options]
   end
end

and run

bundle update

and restart the rails server

This is the minimal configuration to get this example working – for more information on how to allow cross-origin JSON properly check out the rack-cors documentation at https://github.com/cyu/rack-cors

Step 3 – Point Ember at the Rails API

In your Ember application, open the application.js file in app/user/adapter.js (if you didn’t use the –pod structure, this will be in app/adapters instead). You should see a line

namespace: 'api'

change this to

host: ‘http://localhost:3000’

You will also need to disable mirage so that it won’t intercept Ember communications with the rails app. In the config directory of the ember-crud application, open the environments.js file and add

if (environment === 'development') {
   ENV['ember-cli-mirage'] = {
   enabled: false
};
…

Step 4 – Turn off Ember’s cli security policy

Like Rails, ember comes with configuration defaults to protect against cross domain security problems. To get this example running quickly, you can remove this line from package.json in your Ember app.

“ember-cli-content-security-policy”: “0.4.0”,

As with other config options in this tutorial, this is something you’ll want to read about and understand rather than just disabling.

Step 5 – Verify that the changes to the Ember UI are directed to and properly handled by the Rails application

Restart ember by typing

ember serve

(you may need to stop and restart the server if it is still running), and navigate to

http://localhost:4200/users

You should see a list of the users you created in the Rails database seed.

Try adding, editing, or deleting a few users. You can verify the changes at the Rails back end by rendering the list of Users in the database as JSON by going to

localhost:3000/users

Step 6 – Fix the duplicates problem

You may have noticed that when you create a new record, two new items are added to your list – but if you look at the rails service, only one record was persisted and one of the records in the ember list has a null id. If you refresh the page, the record with the null id will disappear.

I’ve been looking around for a real solution to this. If you just want to make the null id record to disappear, you can hack it in the index route:

model: function() {
   return this.store.find('user', {id: true});
}

Postscript

I wrote this as part of my notes on getting up and running on Ember and Rails. I’ve found that if I don’t do these write ups when I’m learning something (figuring I’ll do it later when I understand it all better), there’s a good chance I’ll never do it at all. However, I figured it would be a good idea to run it by a colleague here at the UCSF CKM, Jon Johnson, who has some Ember experience. He said no problem posting his reply:


There are a couple of things you might do a little bit differently, but they aren’t wrong. I’m not sure if its a drop in but https://github.com/fotinakis/jsonapi-serializers looks like a better serializer to use in rails. Active record will work and continue to be supported, but if I were going at this from scratch I would start with that.

For Ember you might want to setup the adapter globally to talk to rails instead of just for the user model. You can do that with `ember g adapter application` It looks like that paragraph might be stuck between these two things as you reference application.js being in the user pod.

Instead of killing mirage in development you could also restart the server in production mode. Thats what we do to talk to the real API. Something like `ember s –env=production` will not go through mirage at all. I’m not sure if that is easier or harder than your way.

Testing in Ember.js, Part 3: mock data with ember-cli-mirage

When Last We Left Our Heroes…

The goal of automated testing is to find problems before your users do. Good tests do this by preventing bad code from being merged. A great continuous integration (CI) setup can catch problems in beta browsers and libraries in time to report them to their authors or fix your code before a release happens. By the end of this three part series you will have a great CI setup. Tests will automatically run against any browser you support and any future version of your dependencies.

In Part One, we covered using Sauce Labs and Travis CI to create your test matrix.

In Part Two, we covered testing our application against many versions of Ember.

In Part Three, we will write some acceptance tests and use the awesome ember-cli-mirage addon to provide controlled mock data to our tests and development environment.

Getting Set Up

Part Three picks up right where we left off with a working ember-cli project and build configuration. This is not a tutorial on Test Driven Development so we’re going to start out with a working example and then test it.

First setup our ember-data models:

$ cd testing-sandbox
$ ember g model fruit title:string color:belongsTo
$ cat app/models/fruit.js
import DS from 'ember-data';
export default DS.Model.extend({
  title: DS.attr('string'),
  color: DS.belongsTo('color', {async: true})
});

$ ember g model color title:string fruits:hasMany
$ cat app/models/color.js 
import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  fruits: DS.hasMany('fruit', {async: true})
});

Then a simple route:

$ ember g route colors --path='/colors'

Modify app/routes/colors.js to get all the colors:

import Ember from 'ember';

export default Ember.Route.extend({
    model(){
      return this.store.find('color');
    }
});

Setup a template to list the colors and their fruits app/templates/colors.hbs

<ul>
  {{#each model as |color|}}
    <li>{{color.title}}
      <ul>
        {{#each color.fruits as |fruit|}}
          <li>{{fruit.title}}</li>
        {{/each}}
      </ul>
    </li>
  {{/each}}
</ul>

Setup ember-cli-mirage

Let’s install the ember-cli-mirage addon.

$ ember install ember-cli-mirage

Now we need to configure our basic API routes. Mirage creates a basic configuration file for you at app/mirage/config.js. We just need to add a few lines for our new models:

export default function() {
  this.get('/colors');
  this.get('/colors/:id');
  this.get('/fruits');
  this.get('/fruits/:id');
};

For each of our models we need a factory so our tests can create new data. As ember-cli-mirage matures, generators for your factories will be added. For now, you have to make them on your own. We need one for fruit and one for color.

Create the file app/mirage/factories/fruit.js:

import Mirage from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  title: (i) => `fruit ${i}`,
  color: null
});

…and the file app/mirage/factories/color.js:

import Mirage from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  title: (i) => `color ${i}`,
  fruits: []
});

Wow. Thats was some serious setup; take heart that we’re done now and we can finally write a test.

Finally a test!

$ember g acceptance-test colors

Add some fixture data and a test to your new file at tests/acceptance/colors-test.js:

test('visiting /colors', function(assert) {
  //turn on logging so we can see what mirage is doing
  server.logging = true;
  
  //make our first color, its gets an id of one
  server.create('color', {
    //fill this color with some fruits (they don't exist yet, thats next)
    fruits: [1,2,3,4,5]
  });
  //now lets create a bunch of fruits and link them to our color
  server.createList('fruit', 5, {
    color: 1
  });
  //want another color? - just add it.
  server.create('color', {
    fruits: [6,7]
  });
  server.createList('fruit', 2, {
    color: 2
  });
  visit('/colors');

  andThen(function() {
    assert.equal(currentURL(), '/colors');
    //this is a stupid test, but hey its a tutorial, what did you expect?
    assert.equal(find('li').length, 9);
  });
});

Yup, I wrote that test for you. This isn’t a lesson on Test Driven Development. If you want that watch “Test Driven Development By Example”. The important part here is that we create fresh testing data with every test using server from ember-cli-mirage. You can be in complete control of what is passed to your application so you can check for any condition.

Final Thoughts

We’re just about out of time and we covered a lot. You still have some test writing to do and I wish there was an addon to do that for you. Until then you can take solace in the knowledge that your testing infrastructure is a foundation you can build your reputation on.

Until next time, Internet friends: If you liked it or hated it let me know @iam_jrjohnson.

Testing in Ember.js, Part 1

[Update] 10/28/17 – Thanks to @DanstonEvans corrected the browser string for firefox on SauceLabs.

The Big Picture

The goal of automated testing is to find problems before your users do. Good tests do this by preventing bad code from being merged. A great continuous integration (CI) setup can catch problems in beta browsers and libraries in time to report them to their authors or fix your code before a release happens. By the end of this three part series you will have a great CI setup. Tests will automatically run against any browser you support and any future version of your dependencies.

Requirements for this guide are Ember.js > 1.10 and Ember CLI > 0.2.3. It may be entirely possible to do this without Ember CLI, but I wouldn’t know how.

In Part One, we will cover using Sauce Labs and Travis CI to create your test matrix.

Getting Set Up

If you’ve never used Ember CLI before, you should follow their instructions to install all dependencies.

Now let’s create a new sandbox to play in:

$ ember new testing-sandbox
$ cd testing-sandbox
$ ember test --server

Congrats! You now have a brand new Ember.js app and running tests in both PhantomJS and Chrome. Go ahead and leave that console window open and create a new one. Tests will keep running in the original window and track all the changes we make.

$ cd testing-sandbox
$ ember g acceptance-test welcome-page

Your test console should now record a failure indicating:

✘ UnrecognizedURLError: /welcome-page

Open testing-sandbox/tests/acceptance/welcome-page-test.js in your favorite editor and make it look like this:

import Ember from 'ember';
import {
  module,
  test
} from 'qunit';
import startApp from 'testing-sandbox/tests/helpers/start-app';

var application;

module('Acceptance: WelcomePage', {
  beforeEach: function() {
    application = startApp();
  },

  afterEach: function() {
    Ember.run(application, 'destroy');
  }
});

test('we should be welcoming', function(assert) {
  visit('/');

  andThen(function() {
    assert.equal(currentURL(), '/');
    var title = find('#title');
    assert.equal(title.text(), 'Welcome to Ember.js');
  });
});

Save that and all of your tests should pass. We are ready to get started with multi-browser testing.

Test Multiple Browsers in the Cloud

Sauce Labs is a service for running your tests against a huge variety of browsers. We’re going to abstract a lot of the complexity of using Sauce Labs by taking advantage of the excellent ember-cli-sauce addon. First, you will need Sauce Labs credentials. You can start a free trial or, if your project is open source, you can sign up for Open Sauce. When you are done, take note of your user name and access key. You will need them later.

Let’s install the addon:

$ember install ember-cli-sauce

Now we can add additional browsers to our testem.json file. Testem calls these launchers:

  $ ember sauce --browser='firefox' --platform='Windows 7'
  $ ember sauce --browser='internet explorer' --version=11 --platform='Windows 8.1'

Lets run some tests!

First we have to export our sauce credentials as environment variables.

$export SAUCE_USERNAME="YOUR_USERNAME"
$export SAUCE_ACCESS_KEY="YOUR_ACCESS_KEY"

Then we fire up a proxy tunnel so Sauce Labs browsers can get to our local Ember.js server.

$ember start-sauce-connect

Then we launch the actual tests.

$ember test --launch='SL_firefox_public_Windows_7,SL_internet_explorer_11_Windows_8_1'

You should see something like:

ok 1 Firefox 37.0 - Acceptance: WelcomePage: we should be welcoming
ok 2 Firefox 37.0 - JSHint - acceptance: acceptance/welcome-page-test.js should pass jshint
ok 3 Firefox 37.0 - JSHint - .: app.js should pass jshint
ok 4 Firefox 37.0 - JSHint - helpers: helpers/resolver.js should pass jshint
ok 5 Firefox 37.0 - JSHint - helpers: helpers/start-app.js should pass jshint
ok 6 Firefox 37.0 - JSHint - .: router.js should pass jshint
ok 7 Firefox 37.0 - JSHint - .: test-helper.js should pass jshint
ok 8 IE 11.0 - Acceptance: WelcomePage: we should be welcoming
ok 9 IE 11.0 - JSHint - acceptance: acceptance/welcome-page-test.js should pass jshint
ok 10 IE 11.0 - JSHint - .: app.js should pass jshint
ok 11 IE 11.0 - JSHint - helpers: helpers/resolver.js should pass jshint
ok 12 IE 11.0 - JSHint - helpers: helpers/start-app.js should pass jshint
ok 13 IE 11.0 - JSHint - .: router.js should pass jshint
ok 14 IE 11.0 - JSHint - .: test-helper.js should pass jshint

1..14
# tests 14
# pass  14
# fail  0

# ok

Wasn’t that awesome? You just tested your code in two browsers. You can add anything you want to testem.json. Go nuts!

When you are done testing remember to kill the tunnel we opened.

$ember stop-sauce-connect

Making It Automatic with Travis CI

The last piece of this puzzle is to use Travis CI to run these tests for you every time you commit code. Update your .travis.yml file to run Sauce Labs tests. You will need to tell Travis CI what your Sauce Labs credentials are in the env section:

---
language: node_js
node_js:
  - "0.12"

sudo: false

env:
  global:
    #set these here becuase they get pulled out by testem saucie
    - SAUCE_USERNAME="YOUR_USER_NAME"
    - SAUCE_ACCESS_KEY="YOUR_ACCESS_KEY"

cache:
  directories:
    - node_modules

before_install:
  - "npm config set spin false"
  - "npm install -g npm@^2"

install:
  - npm install -g bower
  - npm install
  - bower install

before_script:
  # Create a sauce tunnel
  - ember start-sauce-connect

script:
  - ember test --launch='SL_firefox_Windows_7,SL_internet_explorer_11_Windows_8_1' --port=8080

after_script:
  # Destroy the sauce tunnel
  - ember stop-sauce-connect

You are well on your way to being a cross-browser testing hero! In my next post I will take you through using the ember-try addon to test your code against upcoming Ember.js versions.

If you have questions or see a mistake, you can use the comments here or tweet @iam_jrjohnson.

Ember CLI, Heroku, and You

Warning, this is old information and way more work than you need to do. The solution we are using now is the excellent heroku-buildpack-ember-cli.

A disclaimer: This is not for use in production. Doing this for a production app would be a bad decision.

The problem: Developers on the Ilios Project need to be able to share their changes with other team members. While it is possible to deploy a static Ember CLI app nearly anywhere, we want to include our mock API so everyone is looking at the same data.

The solution: Use Heroku to host an Ember CLI application running its built in Express server.

Continue reading Ember CLI, Heroku, and You

Introducing Amalgamatic

“Search!” by Jeffrey Beall licensed CC BY-ND 2.0

Academic libraries offer many resources, but users cannot be expected to search in, say, a half dozen different interfaces to find what they’re looking for. So academic libraries typically offer federated search.

Sometimes, a solution is purchased. Many libraries, for example, use 360 Search.

Here at UCSF, we are among the libraries that have built our own federated search. Twice.

Continue reading Introducing Amalgamatic