Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Ken Mayer

Sencha Touch BDD – Part 5 – Controller Testing

Ken Mayer
Saturday, May 18, 2013

Sencha Touch BDD

tl;dr

A multi-part series of articles on how to test Sencha Touch applications. It uses Jasmine for unit testing and Siesta for integration testing.

Part 5 – Controller Testing

Recap

Part 4 Introduced PhantomJS as an easy and faster alternative to headful Jasmine testing. Part 3 added jasmine-ajax so we can verify that stores and models react properly to back-end data. We also learned how to use stores to test views, without depending on a back-end server. In Part 2 I showed you how to unit test Sencha model classes in Jasmine. In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework.

It’s a control thing, but I will let you understand

Sencha Touch controllers usually live within the context of a single application object. Normally, this is handled for you when you invoke Ext.Application() in your app.js file. It creates a singleton object for you in the namespace of your application. For example, if you configured your application’s name to be ‘SenchaBdd’, then the application will be available as the .app attribute of the global SenchaBdd object, that is, SenchaBdd.app.

Unit testing should not have a running application, however. The point is that we are testing classes in isolation. There’s nothing isolated about an integrated, running, Javascript application. There is a relatively simple solution, however; You need to create you own “test” application object that you can then pass as a configuration option when you create controllers under test.

$ cat spec/javascripts/controller/MyControllerSpec.js
describe('SenchaBdd.controller.MyController', function() {
    var controller, app;
    beforeEach(function () {
        app = Ext.create('Ext.app.Application', {name: 'SenchaBdd'});
        controller = Ext.create('SenchaBdd.controller.MyController', { application: app });
        controller.launch();
    });

    afterEach(function() { app.destroy(); })

You may want to refactor the application creation and tear-down into a spec helper, to DRY out your tests.

Test behaviors, not events

It’s tempting to write a Jasmine test that tries to trigger an event in the DOM, then follow the event handling through the application. This is the road to hell. If you find yourself trying to simulate an event, please stop. That is what integration tests are better at doing. Controllers are classes like any other, and you should test methods in the same way. For example, let’s drive out a behavior where, when a user taps on the ‘Buy’ button our application sends a request to the back-end.

describe('SenchaBdd.controller.MyController', function () {
  var controller, app;
  beforeEach(function () {
    app = Ext.create('Ext.app.Application', {name: 'SenchaBdd'});
    controller = Ext.create('SenchaBdd.controller.MyController', { application: app });
    controller.launch();
  });

  afterEach(function () {
    app.destroy();
  });

  it('#newOrder', function () {
    var order = controller.newOrder();
    expect(order.$className).toEqual('SenchaBdd.model.MyModel');
    expect(order.phantom).toBeTruthy();
  });

  describe('#onBuy', function() {
    it('calls save on the order', function() {
      var myOrder = Ext.create('SenchaBdd.model.MyModel');
      spyOn(myOrder, 'save');
      spyOn(controller, 'newOrder').andCallFake(function() {
        return myOrder;
      });
      
      controller.onBuy()
      
      expect(myOrder.save).toHaveBeenCalled();
    })
  })
});

You might notice that I neither ‘tap’, nor do I test for a ‘POST’ ajax call. The former is better tested through integration tests. The latter is better tested in the model. All the controller need do assert that the model was saved. We trust external classes to function (because they’re tested, too, right?) Testing the #save method on the model follows the same process as testing stores, as I outlined in Part3. Another thing to note is that, under test, this controller does not have any views associated with it; Ext.ComponentQuery calls will return empty (undefined) results. This is to be expected in an isolated test, but may make for some head scratching when you first encounter it. If you must test something in the DOM, you should be writing an integration test anyway.

Ext.define('SenchaBdd.controller.MyController', {
  extend:   'Ext.app.Controller',
  config:   {
    views:   ['MyView'],
    refs:    {
      buyButton: 'myview #buyButton'
    },
    control: {
      buyButton: { tap: 'onBuy' }
    }
  },
  newOrder: function () {
    return Ext.create('SenchaBdd.model.MyModel');
  },
  onBuy:    function () {
    this.newOrder().save();
  }
});

As a personal preference, and make it easier to test and refactor, I have a #newOrder method that delegates to the model to create a new instance.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD – Part 4 – PhantomJS

Ken Mayer
Friday, May 10, 2013

Sencha Touch BDD

tl;dr

A multi-part series of articles on how to test Sencha Touch applications. It uses Jasmine for unit testing and Siesta for integration testing.

Part 4 – Headless testing using PhantomJS

Part 3 added jasmine-ajax so we can verify that stores and models react properly to back-end data. We also learned how to use stores to test views, without depending on a back-end server. In Part 2 I showed you how to unit test Sencha model classes in Jasmine. In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework.

I hear it’s all about the cloud these days

Not only do we use Test Driven Development, we test all the time (see also TATFT). In fact, continuous testing is a key component in a well-run agile practice. Pivotal developed ciborg (previously known as Lobot), to automatically deploy Jenkins servers in the cloud. Jasmine has a continuous integration target in its rake tasks, rake jasmine:ci. If you’ve never run it before, now is a good time to try it. You’ll notice that Jasmine launches an instance of the Firefox browser, runs the test suite, then reports the results. That’s great if you’ve got (1) Firefox and (2) a machine that has a display! When you run your CI tests in the cloud, the virtual machines may not be configured with a virtual display. Furthermore, the process is terribly slow.

In this installment, we’ll see how to mix in PhantomJS so we can run our Jasmine tests without the need for a visual browser. PhantomJS is orders of magnitude faster, too.

Do the install dance

  1. Add ‘jasmine-phantom’ to your Gemfile
index e6b5daf..612fb25 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,3 +2,4 @@ source "https://rubygems.org"
 
 gem "rake"
 gem "jasmine"
+gem "jasmine-phantom"

and modify your Rakefile to load the PhantomJS tasks:

--- a/Rakefile
+++ b/Rakefile
@@ -2,6 +2,7 @@
 begin
   require 'jasmine'
   load 'jasmine/tasks/jasmine.rake'
+  load 'jasmine-phantom/tasks.rake'
 rescue LoadError
   task :jasmine do

Finally, run bundle install

Now, when you run rake -T from the command line, you’ll see a new target:

$ rake -T jasmine
rake jasmine             # Run specs via server
rake jasmine:ci          # Run continuous integration tests
rake jasmine:phantom:ci  # Run jasmine specs using phantomjs and report the results

Run rake

Running it will the phantom:ci target will start the Jasmine continuous integration tests in PhantomJS instead of Firefox.

$ rake jasmine:phantom:ci 2>1 /dev/null
[2013-05-06 20:28:15] INFO  WEBrick 1.3.1
[2013-05-06 20:28:15] INFO  ruby 1.9.3 (2012-11-10) [x86_64-darwin12.2.0]
[2013-05-06 20:28:15] INFO  WEBrick::HTTPServer#start: pid=17061 port=60080
Waiting for jasmine server on 60080...

8 specs | 0 failing
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD – Part 3 – Testing Views and Mocking Stores

Ken Mayer
Sunday, May 5, 2013

Sencha Touch BDD

tl;dr

A multi-part series of articles on how to test Sencha Touch applications. It uses Jasmine for unit testing and Siesta for integration testing.

Part 3 – Testing Views and Mocking Stores

In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework. In Part 2 I showed you to unit test Sencha model classes in Jasmine.

I don’t normally test views, but when I do

There’s an old MVC mantra: “Fat models, skinny controllers and stupid views.” We don’t want complex logic in our views; that makes them hard to maintain. We don’t want any business logic in our controllers, either. That’s why we have models. Views should be so simple that they don’t require tests. There is a gray area with Sencha, however, where we found testing to be useful in our design process. It has to do with how views interact with stores.

Stores are essentially collections of models. They also encapsulate the persistence layer logic, separate from the business logic of the model. DataViews are a special class of Views in Sencha Touch that will consume a collection to generate a list-type view via a template.

Here are two goals for testing views and stores:

  • Does the view consume the right fields from the store, without hitting the back-end for data?
  • Does the store organize the data from the back-end, i.e. create the interface that is used by the view? Again, without hitting the back-end for data.

Small steps and iterate

Let’s define a very simple view via tests, we’ll make it work using an in-line store, then we’ll refactor the store into its own class. Next, we’ll refactor the storage class to use a remote back-end.

Ext.require('SenchaBdd.view.MyView');

describe('SenchaBdd.view.MyView', function () {
  it("has a list of colors", function () {
    var view = Ext.create('SenchaBdd.view.MyView', {
      renderTo: 'jasmine_content',
      store:    {
        fields: ['color'],
        data:   [
          {color: 'red'},
          {color: 'green'},
          {color: 'blue'}
        ]
      }
    });

    expect(Ext.DomQuery.select('.favorite-color').map(function (el) {
      return el.textContent
    }).join(', ')).toEqual('red, green, blue');

  });
});

Sencha does not come bundled with jQuery, so if you were expecting a DOM query like, $(“.favorite-color”), you might be surprised by the expectation. Ext has its own flavor of querying the DOM, using Ext.DomQuery.select.

Try implementing the view on your own to make the test pass. It should look remarkably similar to this:

Ext.define('SenchaBdd.view.MyView', {
  extend: 'Ext.dataview.DataView',
  xtype:  'myview',
  config: {
    itemTpl: '<div class="favorite-color">{color}</div>'
  }
});

In your application, you probably won’t hardwire a store into the view. In fact, you will probably embed this little view inside a larger container, like so (this adds another tab to the sample app):

--- a/app/view/Main.js
+++ b/app/view/Main.js
@@ -10,6 +10,11 @@ Ext.define('SenchaBdd.view.Main', {
 
         items: [
+            {
+              title: 'Favorites',
+              iconCls: 'star',
+              xtype: 'myview',
+              store: 'mystore',
+              styleHtmlContent: true
+            },
             {
                 title: 'Welcome',
                 iconCls: 'home',

Let’s create a store so our view will show us something:

Ext.define('SenchaBdd.store.MyStore', {
  extend: 'Ext.data.Store',
  config:    {
    storeId: 'mystore',
    fields: ['color'],
    data:   [
      {color: 'red'},
      {color: 'green'},
      {color: 'blue'}
    ]
  }
});

In order to see this view, we’ll need to add it to our app.js file:

--- a/app.js
+++ b/app.js
@@ -31,7 +31,11 @@ Ext.application({
     ],
 
     views: [
-        'Main'
+        'Main', 'MyView'
     ],

+    stores: [
+            'MyStore'
+    ],
 
     icon: {

Now, let’s go back and refactor our test so it uses MyStore instead of one that was hard-wired.

--- a/spec/javascripts/view/MyViewSpec.js
+++ b/spec/javascripts/view/MyViewSpec.js
@@ -2,17 +2,16 @@ Ext.require('SenchaBdd.view.MyView');
 
 describe('SenchaBdd.view.MyView', function () {
   it("has a list of colors", function () {
+    var store = Ext.create('SenchaBdd.store.MyStore', {
+      data:     [
+        {color: 'red'},
+        {color: 'green'},
+        {color: 'blue'}
+      ]
+    });
     var view = Ext.create('SenchaBdd.view.MyView', {
       renderTo: 'jasmine_content',
-      store:    {
-        fields: ['color'],
-        data:   [
-          {color: 'red'},
-          {color: 'green'},
-          {color: 'blue'}
-        ]
-      }
-
+      store:    store
     });
     expect(Ext.DomQuery.select('.favorite-color').map(function (el) {
       return el.textContent

All of our tests should remain green, but we’ve removed the “fake” store from our test.

Let’s get dynamic

Static data stores are boring. The fun starts when you start talking to a back-end API. Let’s say that we have a server that responds to an end point of ‘/colors.json’ with a list of favorite colors. We can even “fake” it by placing a file in the appropriate place. Even so, we don’t want our tests to make network calls to the back-end. That’s not appropriate for unit testing. We’ll use Jasmine’s AJAX mocking helper, jasmine-ajax. At the time of this writing, the 2.0 branch had not been merged into the main line, and we need the 2.0 branch in order to work with Ext.

cd spec/javascripts/helpers
curl -O 'https://raw.github.com/pivotal/jasmine-ajax/2_0/lib/mock-ajax.js'
git add ./mock-ajax.js

And we’ll create our first store spec in spec/javascripts/store/MyStoreSpec.js:

describe('SenchaBdd.store.MyStore', function () {
  var store;
  beforeEach(function () {
    jasmine.Ajax.useMock();
    clearAjaxRequests();
    store = Ext.create('SenchaBdd.store.MyStore')
  });

  it('calls out to the proper url', function () {
    store.load();
    var request = mostRecentAjaxRequest();
    expect(request.url).toEqual('/colors.json');
  });
});

Notice that I call jasmine.Ajax.useMock() and clearAjaxRequests() in the set up block. This is because I want to wait until the very last moment to turn on ajax mocking. The Ext class loader might still be trying to load a class (via xhr), and the mocker will prevent that from happening. I also clear all previous requests (in case there were any left over, to prevent test polution).

When you run Jasmine, you’ll get a test failure, “TypeError: Cannot read property ‘url’ of null” because we haven’t set up the proxy in the store, yet. Let’s do that.

$ cat app/store/MyStore.js
Ext.define('SenchaBdd.store.MyStore', {
  extend: 'Ext.data.Store',
  config: {
    autoLoad: true,
    storeId:  'mystore',
    fields:   ['color'],
    proxy:    {
      type:       'ajax',
      url:        '/colors.json'
    }
  }
});

Now, when you run the test suite, you’ll get a different error! This is because the default settings for the ajax proxy enables caching, paging, etc. Things we don’t need, so we have to turn them off:

    proxy:   {
      type:       'ajax',
      url:        '/colors.json',
      noCache:    false,
      pageParam:  false,
      startParam: false,
      limitParam: false
    }

Now our api test is green! You can even test this in the application by dropping a file, ‘colors.json’ into the public directory.

Let’s add one more test to finish things off.

  it('populates the collection', function () {
    store.load();
    var mockedRequest = mostRecentAjaxRequest();

    mockedRequest.response({
      status:       200,
      responseText: [
        {color: 'red'},
        {color: 'green'},
        {color: 'blue'}
      ]
    });

    expect(store.getCount()).toEqual(3);
    expect(store.getAt(0).get('color')).toEqual('red');
    expect(store.getAt(1).get('color')).toEqual('green');
    expect(store.getAt(2).get('color')).toEqual('blue');
  });

Every mocked ajax request has a response() method that you can use to inject your own response, synchronously, to your tests. This confirms that MyStore properly parses and arranges the received data so that it can be presented by the view.

Validate your mocks

We now have 2 tests that depend on a back-end to respond in a specified way. How do we keep these tests from drifting out of sync? Since we’ve mocked the store in our view test, we might never know if the back-end API changes!

You can wrap Jasmine expectation in functions so that they are reusable. Then we can mix this matcher into our view test to confirm that the store we use there is the same.

Let’s add this function to our SpecHelper.js file:

function myStoreDataIsValid(store) {
  expect(store.getCount()).toEqual(3);
  expect(store.getAt(0).get('color')).toEqual('red');
  expect(store.getAt(1).get('color')).toEqual('green');
  expect(store.getAt(2).get('color')).toEqual('blue');
}

And we’ll replace our existing tests with a single line:

  myStoreDataIsValid(store);

We’ll also modify the MyViewSpec.js:

--- a/spec/javascripts/view/MyViewSpec.js
+++ b/spec/javascripts/view/MyViewSpec.js
@@ -9,6 +9,7 @@ describe('SenchaBdd.view.MyView', function () {
         {color: 'blue'}
       ]
     });
+    myStoreDataIsValid(store);
     var view = Ext.create('SenchaBdd.view.MyView', {
       renderTo: 'jasmine_content',
       store:    store

Similarly, we can refactor our colors array into var:

--- a/spec/javascripts/helpers/SpecHelper.js
+++ b/spec/javascripts/helpers/SpecHelper.js
@@ -16,6 +16,15 @@ afterEach(function () {
     domEl.setAttribute('style', 'display:none;');
 });
 
+var colorsJSON;
+beforeEach(function() {
+  colorsJSON = [
+    {color: 'red'},
+    {color: 'green'},
+    {color: 'blue'}
+  ];
+});

And then use colorsJSON in our tests.

--- a/spec/javascripts/store/MyStoreSpec.js
+++ b/spec/javascripts/store/MyStoreSpec.js
@@ -18,11 +18,7 @@ describe('SenchaBdd.store.MyStore', function () {
 
     request.response({
       status:       200,
-      responseText: [
-        {color: 'red'},
-        {color: 'green'},
-        {color: 'blue'}
-      ]
+      responseText: colorsJSON
     });
     myStoreDataIsValid(store);
   });

and

--- a/spec/javascripts/view/MyViewSpec.js
+++ b/spec/javascripts/view/MyViewSpec.js
@@ -3,12 +3,9 @@ Ext.require('SenchaBdd.view.MyView');
 describe('SenchaBdd.view.MyView', function () {
   it("has a list of colors", function () {
     var store = Ext.create('SenchaBdd.store.MyStore', {
-      data:     [
-        {color: 'red'},
-        {color: 'green'},
-        {color: 'blue'}
-      ]
+      data:     colorsJSON
     });
+    myStoreDataIsValid(store);
     var view = Ext.create('SenchaBdd.view.MyView', {
       renderTo: 'jasmine_content',
       store:    store

What we’ve managed, so far

The toy app is coming along. We’ve test driven a DataView that consumes a back-end API. We’ve added the mock-ajax library so we can unit tests our stores in isolation. We’ve even seen a few techniques for keeping our mocks from getting out of sync (although, if you’ve been paying attention, I’ve still left a gaping hole, that needs to be plugged).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD Part 2

Ken Mayer
Friday, April 26, 2013

Sencha Touch BDD

tl;dr

A multi-part series of articles on how to test Sencha Touch applications. It uses Jasmine for unit testing and Siesta for integration testing.

Part 2 – Unit Testing Models

In Part 1 I showed you how to set up your Sencha Touch development environment to use the Jasmine JavaScript test framework. We’re going to take a bit of a breather from all the hard work we did last week. In this blog, I’m going to show you how to test simple models.

Let’s have some fun, shall we?

Test-Driven-Development starts with a test, of course. Let’s write one that just asserts that our model class exists:

$ cat spec/javascripts/model/MyModelSpec.js
describe('SenchaBdd.model.MyModel', function() {
  it('exists', function() {
    var model = Ext.create('SenchaBdd.model.MyModel');
    expect(model.$className).toEqual('SenchaBdd.model.MyModel');
  });
});

When we run our tests in the browser, Jasmine reports this:

  Error: [Ext.Loader] Failed loading synchronously via XHR: 'app/model/MyModel.js'; 
  please verify that the file exists. XHR status code: 404

Which is just Ext’s very formal way of saying, “No such class exists” because we haven’t written it, yet.

Let’s write one that makes the test pass:

$ cat app/model/MyModel.js
Ext.define('SenchaBdd.model.MyModel', {
  extend: 'Ext.data.Model'
});

We have proven that we can create a new class, and that it’s name is what we expect.

Attributes

Let’s assert that our model has some attributes:

$ cat spec/javascripts/model/MyModelSpec.js
it('has data', function () {
  var model = Ext.create('SenchaBdd.model.MyModel', {
    name: 'Test',
    email: 'test@example.com',
    favoriteColor: 'blue'
  });
  expect(model.get('name')).toEqual('Test');
  expect(model.get('email')).toEqual('test@example.com');
  expect(model.get('favoriteColor')).toEqual('blue');
});

Which of course, fails, until we add some fields to our model:

$ cat app/model/MyModel.js
Ext.define('SenchaBdd.model.MyModel', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      { name: 'name', type: 'string' },
      { name: 'email', type: 'string' },
      { name: 'favoriteColor' }
    ]
  }
});

Default values

Let’s say that our model has some default values

$ cat spec/javascripts/model/MyModelSpec.js
it('has default values', function() {
  var model = Ext.create('SenchaBdd.model.MyModel')
  expect(model.get('favoriteColor')).toEqual('yellow');
})

Reload the Jasmine runner in the browser and …

  SenchaBdd.model.MyModel has default values.
  Expected undefined to equal 'yellow'.

Which is easily resolved by adding it to the class:

$ cat app/model/MyModel.js
{ name: 'favoriteColor', defaultValue: 'yellow' }

Validations

One last simple piece, let’s assert that email is a required field.

$ cat spec/javascripts/model/MyModelSpec.js
it('requires an email address', function() {
  var model = Ext.create('SenchaBdd.model.MyModel');
  var errors = model.validate();
  expect(errors.isValid()).toBeFalsy();

  expect(errors.getByField('email')[0].getMessage()).toEqual('must be present');
})

The first expectation asserts that the model is not valid at all. It’s a gatekeeper test. The second test asserts that there’s validation error on the email field (and not some other field).

$ cat app/model/MyModel.js
  config: {
    ...
    validations: [
      { field: 'email', type: 'presence' }
    ]

Roundup

This is a pretty simple set of tests. If you are at all familiar with unit testing, you won’t find much new here. Testing for validations by inspecting on the Errors collection was a little tricky to suss out. Hopefully I’ve saved you a few frustrating moments digging through the source code. What’s also interesting, I think, is how the test report reads:

  SenchaBdd.model.MyModel
    exists
    has data
    has default values
    requires an email address

The test itself communicates something to the reader about the intention of the model. That’s a key concept to understand about TDD; the most important and expensive reader of the application is not the browser, it’s the person who reads and maintains it. It might be your successor or your team mate, or perhaps, yourself, six months from now, when you’ve forgotten everything about this particular patch of code.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Sencha Touch BDD Part 1

Ken Mayer
Wednesday, April 17, 2013

Sencha Touch BDD

tl;dr

A multi-part series of articles on how to test Sencha Touch applications. It uses Jasmine for unit testing and Siesta for integration testing.

Part 1 – Getting Started

In this article you will learn how to set up an application to Jasmine tests in your

Opinionated is a good thing

In my not-so-humble professional opinion, every modern web framework should provide a testing infrastructure with each newly generated application. I’m not concerned if it isn’t my preferred testing package. As long as there’s something. Testing is not an option, and the framework authors probably (hopefully?) test, so why not offer a serving suggestion for new projects? The worst that can happen is that you, the developer, disagree with the choice of framework. There’s a little extra bootstrap cost to replace one framework with another. That’s far less expensive than every new developer discovering a way to test.

Sencha Touch 2.1 has a generator built into its sencha command line tool, but it does not create a test structure as part of the template. This article is the first in a series of discoveries about how to test Sencha Touch applications. I am not claiming that this is the one true way to test. This is not necessarily the best way, either. It is, however, something that works. It installs easily on my development laptop. It gets you to your first passing test quickly. It saves you the cost of exploring all of the options and making these discoveries for yourself. You have plenty of other things to worry about.

But first, you need a web server

Once you’ve installed Sencha Touch and Sencha Command (3.1.0.256 when I wrote this), and you’ve generated the template application, you’ll need to serve the pages locally. Most projects will have some sort of app server already running, but it’s not strictly necessary for testing. When I need to serve pages on my own, I prefer pow. It is a zero-configuration server that can host as many applications as you please. I also like the powder ruby gem; it adds a nice command line interface to manage pow. If you are worried about adding a ruby dependency to your project, stop worrying. Sencha Touch uses the compass ruby gem to generate css files; so you already have a ruby dependency.

pow looks for a rack app, but in my sample app, I don’t have one. pow also looks for a directory named public from which it will server static files. The simplest thing that works is to create a symlink named public that points to the root directory of the project.

# Generate a new ST app
$ cd <touch toolkit directory>
$ sencha generate app senchaBdd ~/workspace/sencha-bdd

# Set up pow/powder
$ cd ~/workspace/sencha-bdd
$ ln -s . public
$ powder up
$ powder link

# Test the server
$ open http://sencha-bdd.dev

If all goes well, you should be able to open the application in any web browser at http://sencha-bdd.dev

Running sample app

Running sample app

Install Jasmine

Installing the stand-alone version of Jasmine will work, but it doesn’t scale to hundreds or thousands of specs. That’s why the Jasmine gem was created. I did some more research and found a way to test using the Jasmine gem.

  1. In the root directory of your project add rake and jasmine to your Gemfile
    $ cat Gemfile
    source "https://rubygems.org"
    
    group :development do
    	gem 'rake'
    	gem 'jasmine'
    end
  2. Run bundle install

  3. Run jasmine init

  4. Jasmine will install a basic set up, but there’s some cruft that you won’t need for a Sencha application.
    rm public/javascripts/Player.js
    rm public/javascripts/Song.js
    rm spec/javascripts/PlayerSpec.js
  5. Edit the src_files entry in spec/javascripts/support/jasmine.yml:
    src_files:
        - touch/sencha-touch-all-debug.js   # Load Sencha library
        - spec/app.js                   # Load our spec Ext.Application
        - app/**/*.js                   # Load source files
  6. Create this file in spec/app.js:
    Ext.Loader.setConfig({
        enabled: true,                  // Turn on Ext.Loader
        disableCaching: false           // Turn OFF cache BUSTING
    });
    
    Ext.Loader.setPath({
        'SenchaBdd': 'app'              // Set the path for all SenchaBdd.* classes
    });
    
    Ext.application({
        name: 'SenchaBdd'               // Create (but don't launch) an application
    });
  7. And this one in spec/javascripts/helpers/SpecHelper.js:
    
    Ext.require('Ext.data.Model');
    
    afterEach(function () {
        Ext.data.Model.cache = {};      // Clear any cached models
    });
    
    var domEl;
    beforeEach(function () {            // Reset the div with a new one.
        domEl = document.createElement('div');
        domEl.setAttribute('id', 'jasmine_content');
        var oldEl = document.getElementById('jasmine_content');
        oldEl.parentNode.replaceChild(domEl, oldEl);
    });
    
    afterEach(function () {             // Make the test runner look pretty
        domEl.setAttribute('style', 'display:none;');
    });

So, what’s going on here? Sencha Touch applications need Ext.Loader to manage class loading. You also need an Ext.Application, especially for controller tests. The modifications to jasmine.yml set up the proper load order, and the jasmine gem will find all of the source files underneath the app/ directory. The app.js is a customized version of your normal app.js that sets up the class loader and global namespace configuration. You should replace “SenchaBdd” with the real name of your application. Two things are happening in SpecHelper.js: First, by default Ext.data.Model caches every model created by the application in a global in-memory array. If you don’t clear it between tests, you can be surprised by test pollution. The second part is to set up and clear a space in the test runner for inserting DOM elements, usually for some sort of view testing.

Create a directory structure that matches your application’s

Your application’s directory structure should look something like this:

├── app
│   ├── controller
│   ├── model
│   ├── profile
│   ├── store
│   └── view

Modify the spec directory so that it mirrors the app/ directory:

├── spec
│   ├── app.js
│   └── javascripts
│       ├── model
│       ├── controller
│       ├── view
│       ├── store
│       ├── profile

Install Jasmine

You can get the stand alone version from http://github.com/pivotal/jasmine. Install it at the in the spec directory. I include the version number, so I can experiment with different versions, but that’s a matter of taste.

├── app
│   ├── controller
│   ├── model
│   ├── profile
│   ├── store
│   └── view
├── public -> .
├── resources/
├── spec/
│   ├── controller
│   ├── jasmine-1.3.1
│   ├── model
│   ├── profile
│   ├── store
│   └── view
└── touch

In order to get Jasmine going, you need a special html file named, by convention, SpecRunner. Add it to spec/ as well. It looks like this:

You will need to modify lines 18 and 22 with the name of your ST app (which can be found in app.json and app.js).

Write one passing test

Create a file, spec/javascripts/sanitySpec.js

describe("Sanity", function() {
  it("succeeds", function() {
    expect(true).toEqual(true);
  });
});


Now load the spec runner into a browser. In the case of this sample app, the url is http://sencha-bdd.dev/spec/SpecRunner.html

Now start the Jasmine spec server from the command line:

    bundle exec rake jasmine

And then open a browser window on http://localhost:8888

You should see the test results with one passing spec.

1 Passing Jasmine Spec

1 Passing Jasmine Spec

If you don’t see this, open up the browser’s developer console to look for clues.

Until next time

That’s it! You now have a complete JavaScript testing framework installed in your application. This is a good time to commit your changes. Celebrate in the glory of the green goodness. You’ve earned it.

Next time, I’ll show you how to test a model class.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Christian Niles

Pivotal Tracker for iOS 1.6.1 fixes Romaji and other text input issues: 失礼しました。

Christian Niles
Wednesday, March 6, 2013

We’ve just released version 1.6.1 of the Pivotal Tracker iOS app, which includes fixes for a number of text input problems.

Most importantly, it fixes Romaji input for Japanese users. While we don’t officially support non-English localizations, we try our best to allow Tracker to be used in any language. This release also fixes a similar bug that prevented auto-completion, spell-checking, and text shortcuts from working in text fields.

Dragging stories is also snappier in this release, because we’ve reduced the delay before beginning a drag. To recap, stories can be dragged to a new location by touching and holding a story for a short time with one finger. We include a momentary delay, otherwise stories would get moved while scrolling through story panels. Touching a story with two fingers will immediately begin dragging a story, which I find really convenient and useful on an iPad.

The new version can be downloaded from the App Store, and we welcome your feedback in the Pivotal Tracker community forum.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Christian Niles

Greatly Refined Story Editing in Pivotal Tracker for iOS 1.6

Christian Niles
Wednesday, February 27, 2013

Download Pivotal Tracker for iOS v1.6Today we’ve released Pivotal Tracker for iOS 1.6 to the App Store. With this release we’ve thoroughly refined the app to make updating stories easier and more enjoyable. While there are all kinds of refinements, we think you’ll be most excited about the following:

All text can now be edited inline

A story’s name, description, tasks, and comments are all editable inline, without transitioning to a new screen and losing context. While editing, text now wraps gracefully across multiple lines as you type.

For example, previous versions of the app provided a separate screen for creating a comment. In version 1.6 this now happens inline, right where the comment will be added. This allows you to continue to scan other comments or details about the story as you write.

Managing labels is easier

Finding and adding labels to a story is much improved, especially for larger projects. The label list now filters as you type, which helps avoid duplicate or mistyped labels. Adding a new label is also much more obvious and easy.

Moving Tasks doesn’t require a separate mode anymore

All tasks can now be moved using the standard move controls, without the need to go into “move” mode. This gets rid of a needless extra taps. To remove a task, simply swipe it and tap the “Delete” button.

Moving or Deleting a story are both more accessible

We’ve gotten rid of the separate “Action” screen, and replaced it with a pair of buttons to move or delete a story. You’ll find these at the very bottom of the story editor, rather than the old icon in the title bar.

We hope you love this release! Please don’t be shy about letting us know in our community about how we can continue to make the app better.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Jonathan Berger

FOWD Day 1: Tapworthy Mobile Design – Josh Clark

Jonathan Berger
Monday, May 14, 2012

Designing for Touch with Josh Clark

Day 1 of the Future of Web Design was a fantastic workshop on designing for mobile with Josh Clark aka @globalmoxie, fellow Brooklynite and author of Tapworthy, a sharp guy, and a great speaker. Hop below the fold for the full notes.

§1: Mobile Context

Intro

  • Early assumptions about designing for mobile may not have been right
  • JC wrote “TapWorthy: Designing great iPhone Apps”. More of a general touch-ui specific book than exclusively iPhone.
  • Q: “What’s your favorite app?” A: uhhhhh…they all drive me crazy? Maybe MindSnacks. Discuss w/ yr neighbor.
  • Software used to be very grey and dry, but it’s tough to get you to stop talking about your favorite app. It’s an engaging question. First the web, and now especially mobile, have made software much more human and lovable and fun.

Anthropology

  • We’re all anthropologists here.
  • Though we design monolithically, there’re lots of mobile cultures.

Myths

Myth 1: Mobile users are rushed and distracted

Mobile isn’t actually mobile anymore. It’s on the couch, in the kitchen, on the 3 hour layover. We have time. Mobile != Desktop Lite

  • Example: alibris (amazon competitor) left its main differentiating feature (rare book sales) off the mobile app.
  • 28% of US mobile web users mostly use the web on mobile
  • 25mm people only see the web via a phone
  • Making all content and features is a “civic responsibility”. Mmm…

Myth 2: Mobile == Less

  • Jacob Nielson recently advised a separate, feature-reduced site for mobile. JC does not agree.
  • Don’t confuse context with intent.

Myth 3: Complexity is a dirty word

  • Complexity is good. It’s powerful.
  • Mitigate confusion, not complexity.
  • Figure out what the user needs.
  • Example: “Do I need an umbrella”? app is perfect for JC. For his Weather-Channel-watching father in law, it’s condescending.
  • It’s not that ‘less is more’; it’s that ‘just enough is more’.
  • Original Facebook iOS app was desktop-lite; users were incensed. “No comments?! No photos?!” It’s since grown to include ~90% of the desktop feature set, and it the most-used FB interface.
  • Don’t manage complexity all at once; manage it through give-and-take.
  • Don’t confuse clarity and density
  • “Every screen should have a primary task”.

Myth 4: Extra taps and clicks are evil

  • We’re not on dial-up modems anymore. A lot of “minimize clicks” ethos had to do with mitigating the effects of latency.
  • “Every tap should deliver satisfaction”: more information, a smile, whatever.
  • “Progressive disclosure”: give a little bit at a time, as its asked for.
  • An interesting opportunity for phones is that there’s probably only one user; you can train a specific user. E.g., twitter was hiding some controls. To educate the user, they opened the screen w/ hidden controls visible, and then hid them after 250ms. Later, they turned off the animation once the user triggered the “Show more controls” button.
  • “Mobile == More“
  • How can mobile do more? It’s got cameras, microphones, gyroscopes.
  • Start with a basic site, use feature-sniffing javascript to discover what the client can do.

Myth 5: Gotta have a mobile website

  • You need a great mobile experience, but a mobile website? Maybe not.
  • There is no ‘mobile web’. Don’t silo by device.
  • if “www.mysite.com” redirects to “mobile.mysite.com”, you’re doing it wrong. URL == Uniform resource locator.
  • We only know device context, not user context.
  • Ideally, there should be One Web.
  • The mobile site shouldn’t have less stuff than desktop because there’s a smaller screen. It should have less stuff because most of the stuff on desktop is crap.
  • Luke W.’s whole “mobile first” point is that small-screen design constraints are a useful filter for everything.
  • Content runs the show. Your interface is a collection of apps plugging into the wellspring of content.

Myth 6: Mobile is about apps (or websites)

  • We tend to focus on a single container (e.g. apps or websites)
  • An app isn’t a strategy; it’s an app.
  • Your product isn’t an app, or a website, or a feed, etc. These are all containers. You’re product is your content.
  • We have to pull back from our obsession with presentation and think about content.
  • Civilians (not just geeks) are beginning to expect their content to be available on every device.
  • Some people have all the devices (laptop, tablet, phone), others only have one.
  • We’re also beginning to expect that this content is integrated: when I put down my Kindle and start reading on my phone, or I pause a Netflix movie on the TV and pick it up in bed on my iPad, it picks up where I left off. With iCloud, this is happening with the content we create.
  • We’re all iCloud developers now.
  • Every native app should be a web client. The browser’s a native app, btw.
  • Some thoughts on this stuff: http:futurefriend.ly

Myth 7: CMS and API are for database nerds

  • We all need to think about this stuff.
  • Structured content is going to allow us to design for multiple platforms.
  • “Metadata is the new art direction.” Ethan Resnick, @erd_ux 19-yo NYU student(!)
  • Ergo, we have to drive our design skills further down the stack.
  • Traditional editorial judgement often disappears when we go mobile, e.g. newspaper layout (reflects editorial judgement about what’s important) vs. many digital interfaces (often simply reverse-chronological order).
  • Let the robots do it: e.g., the Guardian parses InDesign .indd files to gauge importance and reflect that on the web.
  • Repurpose design content.
  • Create content and design strategies that aren’t tied to a particular container.

Summary

  • Mobile != rushed
  • Mobile != less
  • Complex != complicated
  • Tap quality > tap quantity
  • No such thing as ‘mobile web’
  • Focus for all platforms
  • Don’t think ‘app’; think ‘service’.
  • Metadata is the new art direction

Everywhereness is a design nightmare

  • It suggests infinite possibilities and therefore infinite priorities.
  • “Simplify before we suppress.” – Ethan Marcotte

Contexts we tend to design mobile against:

Context #1: I’m microtasking:

  • Capture lost time: waiting on line, when your dining companion leaves for the bathroom, etc.
  • identify lost tasks
  • Find the primary task for an app and make it available everywhere (e.g. todo apps should have an ‘add task’ icon on every screen).

Context #2: I’m local

  • Mobile is the primary device not because it’s always with you (though that’s important), but because it knows the most about you: sensors, personal data
  • change context, not content:
    • Shopper: shuffles your shopping list depending on where your geolocated in the store, e.g. produce aisle, dairy, etc.
    • Word Lens: point camera at a word in a foreign language, see translation.
    • IntoNow: foursquare for TV that listens (like Shazam) to identify season and episode.
  • Save input effort on these devices because they’re not that good at traditional input.
  • Table Drum: map table taps to drum kit sounds. WANT! Transforms your environment into an interface.
  • How can we use the superpowers of the device for input?

Context #3: I’m bored

  • It’s not so much “i’m bored” as “i have attention to spend”.
  • Software isn’t just for work anymore: fart-apps might be frivolous, but they represents a shift from software-as-business-tool to software-as-distraction and entertainment for civilians (not just geeks).
  • Exploration is a common theme for escape apps (reading, games, etc).
  • Work apps have a lot of untapped potential for exploration; quantified life apps are video games for narcissists.
  • Don’t just optimize for the fastest interaction; give them a chance to slow down and explore.

How do we use these?

CHART: usage of device (mobile, tablet, desktop) vs hour-of-day usage of device (mobile, tablet, desktop) vs hour-of-day.

  • iPad use explodes after 6pm or so; it’s the return of the evening newspaper.
  • Phone is mobile, tablet is portable.
  • iPhone: very active browsers, more willing to buy, skew older, wealthier, more educated, have more sex than other mobile users.
  • NB: eBay == 25% of US mobile commerce
  • Ads are a good proxy for how companies think of themselves and their customers
  • Apple ads for Facetime: Louis Armstrong, parent seeing child, grandparents seeing graduate. Sentimental.
  • When people get these devices for the first time, they’re surprised at how emotionally attached they are
  • Verizon droid ad: sci-fi alien cyborg. It’s about the technology.
  • Android skews techy, users customize their phones more (wallpaper, ringtones), skews more to straight communication. Apps are tools, not media. Skews younger, less affluent.
  • Windows mobile: formerly MS + Symbian were 80% of market. Slaughtered by iPhone.
  • Metro interface is helping MSFT come back. Very interesting UI. Aiming for a specific user (young, mobile couple, very plugged into social media). Incorporates personalization.
  • No clear winner right now.
  • 46% of US adults have smartphones. There’re still a lot of people on feature phones.
  • 75% of US adults use text message. Teens do about 8 texts / waking hour(!).

§2: Finger-Friendly Design

What do you wish your phone could do?

Brainstorm a quick list of apps to work on.

  • Collate tour recommendations into an offline touring app
  • Allow me to make calendar appointments / decisions and communicate them via email in a non-shitty way
  • Turn objects on the table into a drum machine
  • capture 3D panoramas
  • capture panoramic workspaces, eg capture my whiteboards and foamcores in virtual space and let me view windows into them

Group suggestions

  • Family intercom app so I don’t have to shout to my kids acorss the house
  • Find the nearest open burger king; the Junk App aka Junkfinder
  • dictophone that translates into mini notes for music
  • universal remote for home
  • on-the go IDE
  • customize your phone
  • home automation app
  • integrated augmented reality app
  • organize work by client or project on the go
  • touring recommendations
  • calendar & comms on a single screen
  • aug-reality whiteboard

Winner: integrated home automation (coffee, lights)

Consider the physical affordances of the device

giant swiss army knife

  • Huge physical load, but also huge cognitive load
  • clarity trumps density
  • handheld => industrial design. how do your pixels feel? In one hand?
    • one hand, tapping with thumb
    • two-handed, tapping with thumbs
    • hold with one hand, tap with the other

Thumbs are awesome

  • one-handed, tapping w/ thumb is the most common

Thumb Tapping Hot Zone

  • Top-vs-bottom is the main axis of emphasis; don’t worry about left vs right.
  • In iOS, edit buttons tend to go top-right; available, but not risking accidental taps

Occlusion

  • Traditional physical design tends towards control at bottom, content at the top.
  • How to keep fingers from occluding content?

Placing Controls

  • It’s a bad idea to stack controls on touch devices, especially on the bottom of the screen. Lots of opportunity for mis-taps.
  • Primary system controls are good at the bottom.
  • Tabbing works a little better at the top for Android, bottom for iOS.
  • Position:fixed is really poorly implemented on mobile browsers, especially for Android. It’s hard to get position:fixed to work right.
  • Apps like LinkedIn overwrite native scrolling in javascript to do things like pinning controls to the bottom.
  • Ad Age uses a “MENU” button on the top right, which is an anchor link to the full-screen nav located at the bottom. HTML 1.0. Quick, clean, DRY, universally implemented.

Placing Navigation

  • iPhone: screen bottom
  • Android: screen top (don’t compete w/ primary hardware buttons)
  • Native web: page bottom

Tablets

  • Avoid putting controls on top-middle of screen.
  • Put controls in the top-corners.
  • UNLESS you need to preview the effects in the main window above, in which case controls need to be on the bottom.

Targets

  • Apple suggests 44pt is the minimum target size for tap targets.

INTERLUDE: Pixels vs points

  • Now that screen density varies widely, measure in points: 1/96th of an inch.
    Ceci n'est pas un pixel

  • Android: “Density-independent pixel (dp)”

  • iOS: Point (not to be confused w/ typography points)
  • CSS: “Pixel”. D’oh!
iPhone
  • Originally 320×480.
  • Retina doubled it to 640×960. How to deal w/ legacy screens?
  • Introduce points: Retina: 2px == 1pt.
  • Other displays: 1px == 1pt.
  • iOS knows that image@2x.png (100×100) is the retina version of image.png (50×50).
  • Design in vector, beware half-pixel divisions.

Back to Pixels vs Points

  • On the web, its a little uglier: <img width='50' height ='50' src='image@2x.png'>. Now we’re shoving hi-res images down the pipe even for screens that can’t handle it.
  • Responsive image serving is a hard problem w/in the context of HTML.
  • grigsby responsive images
  • argument for changing HTML
  • CSS
    .className { background-image: url(image.png); /*50 px square */ }
    @media only screen and (-webkit-min-device-pixel-ratio: 2)
    .className { background-image: url(image@2x.png); /*100 px square */ }
    .className { background-size: 50%; /*50 px square */ }

Targets!

  • Mobile touch is not only an interface for the hand, but of the hand.
  • 44pts (~7mm) is a useful touch target (size of the fingertip)
  • 44px is close enough on the web.
  • e.g. iPhone keyboard is 44pt high.
  • You can squeeze the other dimension as low as 29pt and it’ll still work, so 44×29 or 29×44 are ok.
  • iOS uses 44pt heights all over the place (key size, row height, top and bottom nav); it becomes a regular element of the graphic system, almost like a grid. Home icons are 88pts.
  • Android standardizes on 48dp (~10mm), because there’s enough variability in the hardware that they need a little margin for error.
  • The closer the buttons are together, the larger they have to be.
  • Hardware detection area accounts for occlusion (the target is a little below the visual), so smaller targets where you’re really concentrating and trying to be precise are actually harder to hit!
  • Touch targets tend to be larger than the visual area implies.
  • If you do have to take tap-risks and jam things together at the bottom of the screen, do the extra work and rebuild the bottom nav so the touch area isn’t that huge.
  • It’s ok / desirable to make your tap targets larger than they appear.
  • Don’t just make it easy to read; make it easy to touch. Clarity trumps density.
  • Don’t just design in Photoshop; put it on the app and test.
  • Glance test: put it on the phone, hold at arms length, and see if it makes sense.
  • Bite-size content (e.g. a weather app) should eschew scrolling when possible. Single-screen makes the app feel more solid, almost like a physical object.

Review

  • You’re designing a physical device.
  • Where do fingers and thumbs sit? (hot zones).
  • Varied rules for phones (iOS: controls at bottom. Android: controls at top).
  • Tablet: push to edges and corners.
  • Big fat touch targets: at least 44pt.
  • Progressive disclosure: clarity trumps density.

Building: Layout

Design the main screen

  • Featured content
  • Primary Tasks
  • Secondary Tasks
  • Navigation to other views

Remember

  • Make use of the sensors
  • Create room for exploration
  • Capture lost time

Design w/ team!

§3: Navigation

  • Remember Choose Your Own Adventure books? Tangled navigation paths.
  • Paths should not cross.
  • Paths should be predictable and unique.
  • It may seem that multiple paths give more convenience, but they’re harder to model mentally.

3 models:

Flat Pages:

  • Good for casual browsing, focused content, or discovery.
  • Good for variable screens, custom content.
  • Easy to swipe between pages.
  • Very little interface chrome; good for saving space, but not a lot of inference.
  • It’s flexible: typically let ppl change order, add cards.
  • Downside: can’t jump to a specific screen.
  • Limited to 20 (practically, ~10) cards; past that, there’s no more room for the navigation!
  • Works best for homogenous content; when the cards are different, it’s tough to remember which screen is which.
  • Works best for no-scroll screens. It feels like a card, like something physical. We’re not that good at scrolling in two directions.

Tab Bar

  • Very familiar; this is where people start to design by default.
  • Easily allows for heterogeneous content.
  • Constant advertisement / reminder for what the app can do.
  • iPhone is limited to 5 tabs; add more and you’ll automatically get the “More” tab.
  • DO NOT use the ‘More’ as a junk drawer.
  • Have the discipline to leave things at 5 tabs on iOS; if you need more, go to Tree Structure.
  • If you’re on Android, drop to 4 tabs.
  • Android Ice Cream Sandwich introduces the Action Bar: show one tab, with 1 or more tools (depending on screen size and orientation).
  • Order: righties will have the leftmost tab in prime tab position, vice versa for lefties. New convention is to have the prime action being the center. How to draw attention? Make it a little bigger (a la old instagram or new foursquare).
  • Path: interesting vertical tab structure with invoked tabs, but a little weird and tough to discover.

Toolbar vs Tab bar

  • Toolbar: usually light background. Act locally (on the content on the individual screen).
  • Tab bar: usually dark background. Think globally. (change to show whatever tools you need to affect that content.)
  • Don’t show both at once.

Tab Bar Pro’s:

  • One tap access to all main features.
  • Clearly labeled menu advertises features.
  • It’s always there.

Tab Bar Cons:

  • Limited to four (five?) buttons.
  • Committing a lot of screen space.

Tree Structures

  • Scales to a ton of content.
  • Familiar mental model.
  • Direct interaction w/ content: the word or picture you want to use; it removes abstractions.
  • Nested folders, a lot like column view in Finder.
  • Allows longer and more customizable menu options.
  • Often a list.
  • But sometimes you’ll see springboards (facebook) or galleries (photo albums).
  • Not much room for a way out or ‘back to home’. Leads to a lot of ‘back back back’ garbage taps. It’s not easy to switch branches.
  • Work-around: combine w/ tab bar.
  • It would be nice to have a gesture to go back. “Swipe across the top” is becoming the norm.
  • As designers, we need to protect users from actions that will do them harm. Gesture Jiujitsu can help, e.g. using swipes (easy to do, hard to do accidentally) for undo or Return To Home.
  • One of the great things about touch is that it removes mediation; you deal directly w/ what you want.
  • Cons:
    • Main categories available only from top.
    • Inefficient to switch branches.
    • No standard for returning to top level.

Popovers

  • iPad is a monster in the tablet space (although Kindle Fire is coming on strong in the US).
  • It’s too big a screen to be switching pages all the time. Often the phone’s “card-flipping” model doesn’t work as well.
  • Long-hold is the right-click of gestural interfaces.
  • Pop-overs are ok for quick interactions.
  • Use popovers to act on content.
  • Use popovers for quick peaks.
  • Avoid popovers for exploration or navigation.

None of the above: new interfaces

  • Design the personality of your app at the outset.
  • A personality will emerge no matter what; if you don’t design it, you’ll be at its mercy.
  • Simple things like backgrounds can change the personality greatly, even while sticking to the same design patterns.
  • Emotion is a design element.
  • Q: do you like skeumorphism? At worst, it can be kitschy, but at best it can show you how to use the app. What does your metaphor propose? and what does it promise? If it looks like a book, I should be able to turn the page (I’m looking at you, iPad contacts). Be true to your metaphor when you go skeuomorphic.
  • Skeumorphism often takes a strong point of view, which can be good or bad.

Metaphorically speaking

The Dark Art of Aping Real Objects

  • Some clones are the same size: Guitar tuner, apple remote. Sounds and animation can enhance that illusion. You’re absorbing all the industrial design thought that went into the original, though you may lose some ergonomic affordances.

  • Minitures (Chess, GarageBand’s piano) are a bit different. The interface loses its ergonomics.

Voice capture app is eye candy, but not functional.

  • Sometimes it’s just eye-candy. (E.g. voice capture app w/ its microphone).
  • Lots of shelves these days. Its just a dressed-up tree structure, but we love collections. It’s fine for things that belong on a shelf (e.g. books), but you’ll lose emotional resonance when the objects can’t really be on shelves and the metaphors break.

  • When contemplating metaphors:

    • Is this a problem that can be solved with built-in, native tools?
    • Are you being too clever? Is the metaphor complicating the mental model?
    • Is your metaphor appropriate to the device? Phone OS’s are card-based; bringing windows in gets weird.
    • Do you have more interface than you need?
    • Don’t be different to be different, be different to be better. Different means I have to learn something new.
    • Creative interfaces can be joyful. See: BeBot (robot synth app).
    • Ask: am I going too far? Am I going far enough?

Gimmicky presentation

  • “The sin of pridefully obvious presentation”: Ed Tufte.
  • The NYT app quacks a lot like a newspaper. Why so boring?
  • Apple asked NYT to build a demo for the iPad, just before the iPad was announced. Three young designers / developers were brought in: two weeks to do it, no contact with home base, no cell phones. “You’ll be demo’ing to Steve, make us proud.” Eep!
  • After 2 days, they built a Deck-based version. Realized it was wrong; went back to what became “NYT Editors Choice”. “Strong if conservative first effort”.
  • Sometimes you can impress most by doing it quietly.
  • NYT App: “Yawn. It looks like the NYT”. Flipboard: “HOLY CRAP! It looks like the NYT!!!”
  • Old conventions aren’t necessarily old-fashioned.
  • Feature the content, not the interface.

Gestures

Multitouch with Two Hands

  • Uzu app for iPad: multitouch manipulation of particle generators.
  • The utility of keyboard shortcuts comes from being able to do them w/o looking. Gestures provide a similar opportunity.
  • Gestures that begin at the edge should be OS-level gestures: android, WebOS, Meego all did this. Apple used to, but recently has been breaking edge gestures by taking them over.

  • Buttons require cognitive and physical effort.

  • Gestures ~= keyboard shortcuts.
  • Use entires screen as a controls.
  • Standards are emerging: tap, swipe, pinch/spread, long tap.
  • Model content as physical objects.
  • Explore multitouch gestures.
  • Follow the toddlers; they’re better at this than we are. They haven’t been spoiled by 20 years of desktop interactions.
  • Make a 3 yr old your beta tester. She won’t understand the content of the app, but she can use the interface and navigate.
  • “NUI”: Natural User Interface

Haptics

  • Shake is a powerful gesture, but can be gimmicky. It takes the focus off the app, and onto the device.
  • Younger people are more inclined to rotate the phone; older folks stay with portrait.
  • Landscape tends to be more engaging: both hands are occupied, and the aspect ration is closer to our biological field of view.

Conclusion

  • The more backward-compatible (accessible) your app is, the more forward-compatible (future-proof) it’ll be.
  • Mobile’s a great wedge, because it’s got everyone in a panic right now. We’ve been building websites the wrong way for 15 years.
  • We’ve got the most exciting job in the world!

Tweets

  • When designing for mobile, don’t confuse context with intent. – Josh Clark
  • clarity trumps density
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Introducing Android IntelliJ Starter and Android CI

Joe Moore
Tuesday, August 9, 2011

We have been doing quite a bit of Android development over the last year and a half at Pivotal Labs. Over time we have compiled a set of go-to tools, and libraries, and configuration settings that help make our development process as productive as possible. We are excited to publish two open source projects, each with the goal of helping new Android development projects hit the ground running: Android IntelliJ Starter and Android CI.

Android IntelliJ Starter

Android IntelliJ Starter (github project here) is a “template” IntelliJ 10.5 project created to bootstrap Android development in IntelliJ. Our goal: start test-driving your new Android project within minutes, not hours (or days) using the Robolectric framework for testing and Robojuice framework for dependency-injection. In addition to the starter application and unit tests many other supporting libraries are provided, including C2DM push notification libraries with a stubbed-out, documented C2DM implementation class.

Android IntelliJ Starter represents hard earned configuration knowledge as well: getting all these tools to work seamlessly in IntelliJ and on the command line using ant is no small feat. We’ve even provided instructions on how to remove the extra tools and libraries — configuration by deletion.

Android CI

Android CI (github project here) is intended to bootstrap Android continuous integration using Jenkins-CI (formerly Hudson). This project is a stripped-down version of Jenkins’ configuration directory, which is ~/.jenkins by default.

Android CI ships with one preconfigured job: running tests and building .apks for Android IntelliJ Starter. If your project starts as a clone or fork of Android IntelliJ Starter then Android CI’s configuration will work well for you with only a few simple changes.

Help Us Improve

At Pivotal Labs we are committed to making Android development as productive as possible. We will add more functionality to both projects over time and we encourage others to fork, enhance, send us pull requests, and to use the Issues tab on each Github project’s page to notify us of problems so we can fix them promptly.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Fast Android Debugging with IntelliJ

Joe Moore
Friday, July 15, 2011

Sure, you can launch your Android app in IntelliJ’s debugger, but that’s slow. IntelliJ allows you to dynamically attach the debugger to a running device using the “Attach debugger to Android process” button. That’s fast!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (781)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (21)
  • cucumber (20)
  • design (19)
  • jasmine (19)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • tracker ecosystem (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to mobile Feed
  1. 1
  2. 2
  3. 3
  4. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >