1
votes

I am trying to test a component handling newsletter-subscriptions. I want to test whether the submitForm function saved successfully by setting a class of a hidden input accordingly after the save happened or did not happend so that I can assert this in an integration test.

Here is my code:

newsletter.subscription.js:

import Ember from 'ember';

export default Ember.Component.extend({
  store: Ember.inject.service(),
  displayNotifications: Ember.inject.service(),

  didInsertElement() {
    this.set('model', this.get('store').createRecord('subscription'));
  },
  actions: {
    submitForm() {
      this.get('model').save().then(
        (json) => {
          this.get('displayNotifications').success({
            key: 'subscription.success',
          });
          this.set('model', this.get('store').createRecord('subscription'));
          this.set('subscribed', true);
        },
        (reason) => {
          this.set('subscribed', 'error');
        });
     }
   }
});

Here is the code of the newsletter-subscription.hbs:

<div class="ui container row newsletter-form">
  <div class="ui inverted segment">
    <form class="ui form" {{action "submitForm" on="submit"}}>
      <div class="ui stackable grid">
        <div class="eleven wide right aligned column">
          {{input type='string' value=model.email  placeholder=(t 'subscription.email')}}
        </div>
        <div class="five wide inverted left aligned column">
          <button type="submit" class="ui fluid secondary button {{unless model.email 'disabled'}}">
            {{t 'button.newsletter'}}
          </button>
        </div>
        <input type='hidden' id="debug" class="{{subscribed}}" />
      </div>
    </form>
  </div>
</div>

And here is my test: newsletter-subscription-test.js

 moduleForComponent('newsletter-subscription', 'Integration | Component | newsletter-subscription', {
   integration: true
 });


 test('newsletter-subscription: submitting newsletter-subscription sets subscribed to true', function(assert){

    this.render(hbs`{{newsletter-subscription subscribed='notSubscribed'}}`);

    assert.equal(this.$('#debug').hasClass("notSubscribed"), true, 'before click, debug element has class notSubscribed');

    this.$('input[name=subscription-email]').val("[email protected]");
    this.$('button[type=submit]').click();

    return wait().then( () => {
      assert.equal(this.$('#debug').hasClass("subscribed"), true, "after callback of click ran, debug element has class subscribed");
    });

 });

The first assertion works. But then I keep getting an error:

✘ Assertion Failed: You can only unload a record which is not inFlight.

From breakpoint analysis, I found that this happens when this.get('model').save() is called. My tries to resolve the issue remained ineffective so far (i.e. mocking server response using mirage). Is there an issue with the Ember run loop in this context that I somehow have to take care of or something different?

Thank you for your input!

1

1 Answers

0
votes

I ran into this issue today; the core issue seems to be that since the app isn't running in integration and unit tests, saving records can't actually work. There are two ways around it:

  1. (preferred) Stub out save() on the models you pass into your components, using Sinon. For example,

    const record = new Ember.Object({ save: Sinon.spy(() => Ember.RSVP.resolve()) };
    this.render(hbs`
      {{your-component record=record}}
    `);
    
  2. If the above isn't an option -- in my case, because the record is being created within your component, and you don't want to extract that creation to a passed-in method (which would be the cleaner option), you can override store's behavior to make saves a no-op, by overriding store.scheduleSave (as described by Kelly Sutton, here):

    this.inject.service('store');
    
    this.store.scheduleSave = function(context, resolver) {
      resolver.resolve(context);
    };
    

There are certainly other ways around this, including of course just switching to an acceptance test, but the above two are probably the quickest fixes.