ember-data-model-fragments icon indicating copy to clipboard operation
ember-data-model-fragments copied to clipboard

Integration test: Uncaught TypeError: store.createFragment is not a function

Open xamoom-pali opened this issue 9 years ago • 8 comments

I am trying to write an integration test for a component that manipulates a model called spot. Here is the spot model:

// models/spot.js

// imports...
// some validations rules...
const Validations = buildValidations({ ... });

export default Model.extend( Validations, {
  customMeta : fragmentArray('custom-meta'),

  name: attr("string"),

  location: fragment('location', {
    defaultValue: {
      lat: null,
      lon: null
    }
  }),

I am trying to create a spot record in such an integration test:

test('should render card displaying name, content, status correctly', function(assert) {
let spotMock;
const store = this.container.lookup('service:store');
 run(()=>{
    spotMock = store.createRecord('spot', {
      isMoving: true,
      name: 'spot-name',
  // no matter whether the following is present or absent, the error persists
  //     location: store.createFragment('location', {
  //       lat: 1,
  //       lon: 1
  //     })
    });
  });
 //...

store.createRecord() fails due to: Uncaught TypeError: store.createFragment is not a function

This is the place which calls store.createFragment():

// fragment.js

 // Creates a fragment and sets its owner to the given record
  function createFragment(store, declaredModelName, record, key, options, data) {
    var actualModelName = getActualFragmentType(declaredModelName, options, data);
    var fragment = store.createFragment(actualModelName); // error

    setFragmentOwner(fragment, record, key);
    setFragmentData(fragment, data);

    return fragment;
  }

Maybe a screenshot helps: createfragment

Apparently, createFragment() is not set up upon the store yet. Do I have to set it up anyhow for the purposes of integration testing? I could not find any hint about it in the docs, however, I guess I am missing something simple in here.

xamoom-pali avatar Aug 24 '16 08:08 xamoom-pali

@xamoom-pali Sorry for the delayed response. I just saw this issue (I missed the email :( ). I suspect what's happening is that because it's an integration test (not an acceptance test) that our initializers were not fired. The result of that is we did not reopen the store class and add our createFragment. I'm guessing you've figured out most of this already.

So what to do about it? In my production app's testsuite, I ended up writing a utility function (setupEmberData), that among other things, manually invokes ember-data-model-fragment's initializer to get things primed up. I call this utility function in my test setup. Here is an example:

// app/tests/helpers/setup-ember-data.js

// NOTE: This is really from the addon, but it's injected into the app space
import fragmentTransformInitializer from 'buttercup/initializers/model-fragments';

export function setupEmberData(owner) {
  fragmentTransformInitializer.initialize(owner);
}
// app/tests/integration/component-grid-system-tests.js
import { setupEmberData } from '../helpers/setup-ember-data';

moduleForComponent('grid-system', 'Integration: Grid System', {
  beforeEach() {
    let owner = Ember.getOwner(this);
    setupEmberData(owner);
  }
});

So yea, this isn't great at all. Integration tests with ember can be a little weird because the app is "half setup". But come to think of it, ember-data has a setupContainer function they invoke in an initializer, so I wonder if there is another trick to this that I'm not aware of.

workmanw avatar Aug 31 '16 12:08 workmanw

Yeap, I'm not crazy 😜 . Ember-test-helpers actually invoke ember-data's setupContainer initializer manually. See: ember-test-helpers/build-registry.js.

So unfortunately that means there isn't some alternative way for ember-data-model-fragments to make this work for you out of the box. You'll need to manually invoke the initializer.

workmanw avatar Aug 31 '16 13:08 workmanw

@xamoom-pali I'm going to close this issue. If you have any further questions or concerns let me know.

workmanw avatar Oct 28 '16 14:10 workmanw

I've similar issue. When I test serializer for a model which is composed with MF.fragments, I run into store is null at testing method normalizeUpdateRecordResponse.

test('it normalizeUpdateRecordResponse', function(assert) {
  assert.expect(0);
  Ember.run(() => {
    let store = this.container.lookup('service:store');
    let serializer = this.container.lookup('serializer:myModel');
    let data = Object.assign({}, myModelResponse);
    let record = this.subject(data);

    let snapshot = record._createSnapshot();
    let primaryModelClass = store.modelFor('myModel');
    let requestType = 'updateRecord';
    let id = myModelResponse.id;
    let payload = { data: data };

    try {
      let response = serializer.normalizeUpdateRecordResponse(store, primaryModelClass, payload, id, requestType);
    } catch (err) {}

  });
});

Please take a look thanks.

screen shot 2016-12-01 at 9 58 11 am screen shot 2016-12-01 at 9 58 21 am

ldong avatar Dec 01 '16 18:12 ldong

@ldong What kind of tests are you doing? It looks like integration, is that right? Can you please try manually invoking the initializer in your test setup as I mentioned in a prior comment.

import fragmentTransformInitializer from 'buttercup/initializers/model-fragments';

test('it normalizeUpdateRecordResponse', function(assert) {
  assert.expect(0);
  Ember.run(() => {
    fragmentTransformInitializer.initialize(this.container.owner);
    
    let store = this.container.lookup('service:store');
    // ....
  });
});

If that doesn't work, can you provide me with a stack trace.

workmanw avatar Dec 01 '16 20:12 workmanw

@workmanw I tried your workaround but also could not get it to work. Manually looking up the store using this.container.lookup('service:store') and then calling createFragment works even without manually initializing the fragmentTransformInitializer.

I'm guessing this.store does not point to the actual store somehow?

edit: Gah... it's this.store(). Don't mind me.

kmiyashiro avatar Jan 26 '17 22:01 kmiyashiro

So I was wondering why it's unique problem to ember-data-model-fragments. I think maybe we could actually do without the initializer if we just added those files into the app directory. Initializer dated back to before we had convert this to an ember CLI add-on.

I'm going to reopen it and see what can be done about this. Its definitely a gotcha.

workmanw avatar Jan 26 '17 22:01 workmanw

I ran into this issue and couldn't solve it using the advice above, so, for posterity's sake, here's what fixed it for me:

I got the "store.createFragment is not a function" error on an old Ember project that had been upgraded from Ember 2 to 3, and I have a new Ember project that started out on version 3, which did not have this issue. So I went through all test-setup-related files in both projects and compared them.

Then I made the following changes:

In /tests/test-helper.js, I changed this:

import resolver from './helpers/resolver';
import {
  setResolver,
} from '@ember/test-helpers';
import { start } from 'ember-cli-qunit';

setResolver(resolver);
start();

to that:

import Application from '../app';
import config from '../config/environment';
import { setApplication } from '@ember/test-helpers';
import { start } from 'ember-qunit';

setApplication(Application.create(config.APP));

start();

And in /config/environment.js, I added

if (environment === 'test') {
  ENV.APP.autoboot = false;
}

After those changes, I didn't need to do any manual setup to get the fragments to work in testing.

Hope this helps someone.

nino avatar Oct 18 '18 15:10 nino