Search code examples
ember.jsember-qunit

Rendered component test fails after Ember 1.10 upgrade


I'm unit testing a component, in particular the rendered form. I'm approaching this pretty much as described in the Ember Guides.

In particular, the component has three computed properties which show different classes on the rendered elements depending on the backing model. I'm tweaking the properties in Ember.run() blocks and then looking at the rendered component again.

What's interesting here is that the computed properties seem not to be re-computing even through I'm touching the attribute they observe. Later tests which don't test rendering - just the return from the component - do pass.

Here's my test code:

moduleForComponent('wizard-tab', "Component - WizardTab", {
  setup: function () {
    this.tab = this.subject({ step: 2, stepCompleted: 1, tab: tabs.all()[1] });
  }
});

test('#render', function () {
  let tab = this.tab;
  ok(this.$().find('span.wizard-tab-detail').length, "Active tab has a detail span"); // Passes
  // Note that both of the additional states observe stepCompleted
  // so I need to touch that to get them to recalculate
  Ember.run( function () {
    tab.set('stepCompleted', 2);
    tab.set('tab', WizardTab.all()[4]);
  });

  ok(this.$().find('span.wizard-tab-icon-disabled').length, "Future tabs have a disabled class"); // Fails

  Ember.run( function () {
    tab.set('stepCompleted', 3);
    tab.set('tab', WizardTab.all()[1]);
  });

  ok(this.$().find('span.wizard-tab-icon-done').length, "Inactive tabs have a done class"); // Fails
});

The first assertion passes, the next two fail. Using console.log statements I've validated that the set()s are working, but the property calculated from them is returning the wrong result.

Here's one of the computed property definitions:

  disabled: function() {
    return this.get('tab.stepNumber') > (this.get('stepCompleted') + 1);
  }.property('stepCompleted')

(I literally get false for 5 > 2 when I put in console.log checks on that comparison.) Is there something I'm missing that would prevent that from updating when I check subsequent renders of the component?

This is ember CLI 0.2.0, Ember 1.10.0 and ember-cli-qunit 0.3.8.

ETA: Probably relevant: this test passes on Ember 1.8 and ember-cli-qunit 0.3.1. It's the update to Ember CLI 0.2.0 and accompanying Ember and ember-cli-qunit updates which cause the failure.

(ETA: note from kiwiupover's comment below that this section below isn't relevant to the problem; the guides may not show the best current way to do this.)

Note that the guides use a similar pattern:

test('changing colors', function() {

  // 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.
  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');
  });

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

I tried wrapping the second and third assertions in andThen() but that raised errors - andThen() was undefined.


Solution

  • I got this working by starting a new branch off development (our default branch) and re-running the update. Here are the differences between my original pass and what worked:

    • More component updates, I think just because some time has passed since my first attempt. ember-resolver, loader.js, ember-cli-app-version and ember-cli-dependency-checker had all moved up. I don't know if any of those mattered, but they did change.
    • The key part, I think, was isolating the three tests in separate test blocks and also updating the subject in an Ember.run() block for each test that used different attribute values from the setup component.

    Here's what the three tests looked like when they passed:

    moduleForComponent('wizard-tab', "Component - WizardTab", {
      setup: function () {
        this.tab = this.subject({ step: 2, stepCompleted: 1, tab: WizardTab.all()[1] });
      }
    });
    
    test('Rendered active tabs have a detail span', function () {
      let tab = this.tab;
      ok(this.$().find('span.wizard-tab-detail').length, "Active tab has a detail span");
    });
    
    test('Rendered future tabs have a disabled class', function () {
      let tab = this.tab;
      Ember.run( function () {
        tab.set('step', 2);
        tab.set('stepCompleted', 2);
        tab.set('tab', WizardTab.all()[4]);
      });
      ok(this.$().find('span.wizard-tab-icon-disabled').length, "Future tabs have a disabled class");
    });
    
    test('Rendered inactive tabs have a done class', function () {
      let tab = this.tab;
      Ember.run( function () {
        tab.set('step', 2);
        tab.set('stepCompleted', 3);
        tab.set('tab', WizardTab.all()[1]);
      });
      ok(this.$().find('span.wizard-tab-icon-done').length, "Inactive tabs have a done class");
    });
    

    I believe that last change - moving from one test with some Ember.run() blocks to three - is what really did it. I used some {{log value}} lines in the template to look at which values were being sent to the template, and it was using the subject from the setup block all three times until I added the Ember.run() blocks.