Testing Components Edit Page


Unit testing methods and computed properties follows previous patterns shown in Unit Testing Basics because Ember.Component extends Ember.Object.

Setup

Create the component to test using ember generate component pretty-color: This Ember component:

app/components/pretty-color.js
1
2
3
4
5
6
7
8
9
10
import layout from '../templates/components/pretty-color';

export default Ember.Component.extend({
  layout: layout,
  classNames: ['pretty-color'],
  attributeBindings: ['style'],
  style: function() {
    return 'color: ' + this.get('name') + ';';
  }.property('name')
});

... with its accompanying Handlebars template:

app/templates/components/pretty-color.hbs
1
Pretty Color: {{name}}

... can be unit tested using the moduleForComponent helper. This helper will find the component by name (pretty-color) and its template (if available).

tests/unit/components/pretty-color-test.js
1
2
3
4
moduleForComponent('pretty-color', {
  // specify the other units that are required for this test
  // needs: ['component:foo', 'helper:bar']
});

Now each test following the moduleForComponent call has a subject() function, which aliases the create method on the component factory.

We can test to make sure that changing the component's color property updates the rendered HTML:

tests/unit/components/pretty-color-test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
test('changing colors', function(assert) {

  // this.subject() is available because we used moduleForComponent
  var component = this.subject();

  // we wrap this with Ember.run because it is an async function
  Ember.run(function() {
    component.set('name','red');
  });

  // first call to $() renders the component.
  assert.equal(this.$().attr('style'), 'color: red;');

  // another async function, so we need to wrap it with Ember.run
  Ember.run(function() {
    component.set('name', 'green');
  });

  assert.equal(this.$().attr('style'), 'color: green;');
});

We might also test this component to ensure the template is being rendered properly.

tests/unit/components/pretty-color-test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test('template is rendered with the color name', function(assert) {

  // this.subject() is available because we used moduleForComponent
  var component = this.subject();

  // first call to $() renders the component.
  assert.equal($.trim(this.$().text()), 'Pretty Color:');

  // we wrap this with Ember.run because it is an async function
  Ember.run(function() {
    component.set('name', 'green');
  });

  assert.equal($.trim(this.$().text()), 'Pretty Color: green');
});

Interacting with Components in the DOM

Ember Components are a great way to create powerful, interactive, self-contained custom HTML elements. Because of this, it is important to test the component's methods and the user's interaction with the component.

Let's create a very simple component that simply sets its own title when clicked. Run ember generate component my-foo and open the component file:

app/components/my-foo.js
1
2
3
4
5
6
7
8
9
10
11
12
import layout from '../templates/components/my-foo';

export default Ember.Component.extend({
  layout: layout,
  title:'Hello World',

  actions: {
    updateTitle: function() {
      this.set('title', 'Hello Ember World');
    }
  }
});

Whose template is:

app/templates/components/my-foo.hbs
1
2
3
4
<h2>{{title}}</h2>
<button {{action "updateTitle"}}>
    Update Title
</button>

We would use jQuery triggers to interact with the rendered component and test its behavior:

tests/unit/components/my-foo-test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
moduleForComponent('my-foo', 'MyFooComponent');

test('clicking link updates the title', function(assert) {
  var component = this.subject();

  // assert default state
  assert.equal(this.$().find('h2').text(), 'Hello World');

  // perform click action
  this.$().find('button').click();

  assert.equal(this.$().find('h2').text(), 'Hello Ember World');
});

sendAction validation in components

Components often utilize sendAction, which is a way to interact with the Ember application. Here's a simple component that sends the action internalAction when a button is clicked:

app/components/my-other-foo.js
1
2
3
4
5
6
7
8
9
10
11
import layout from '../templates/components/my-other-foo';

export default Ember.Component.extend({
  layout: layout,

  actions: {
    doSomething: function() {
      this.sendAction('internalAction');
    }
  }
});

The button can be found in the template:

app/templates/components/my-other-foo.hbs
1
2
3
<button {{action "doSomething"}}>
    Do Something
</button>

In our test, we will create a test double (dummy object) that receives the action being sent by the component.

tests/unit/components/my-other-foo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
test('trigger external action when button is clicked', function(assert) {
  assert.expect(1);

  // component instance
  var component = this.subject();

  // render it
  this.$();

  var targetObject = {
    externalAction: function() {
      // we have the assertion here which will be
      // called when the action is triggered
      assert.ok(true, 'external Action was called!');
    }
  };

  // setup a fake external action to be called when
  // button is clicked
  component.set('internalAction', 'externalAction');

  // set the targetObject to our dummy object (this
  // is where sendAction will send its action to)
  component.set('targetObject', targetObject);

  // click the button
  this.$().find('button').click();
});