2
votes

I've run into some strange behavior here and wanted to see if anybody knew why it occurs. I've written a simple Ember QUnit test and want to share some data between each test, just to reduce clutter.

Test

import Ember from 'ember'
import { moduleFor, test } from 'ember-qunit';

let create = Ember.Object.create;

let shared = create({});
shared.stardardData1 = create({ id: 1 });
shared.stardardData2 = create({ id: 2 });

moduleFor('controller:foo', 'description', {
    beforeEach() { ... }
    afterEach() { ... }
}

test('should do things', function () {
    let myGroup = [shared.standardData1, shared.standardData2];
}

A couple things here:

  • I'm getting an error this._initProperties is not a function
    • The error goes away when I remove the shared stuff
    • The error persists if I add the shared stuff between the moduleFor and the test
  • I want to be able to share variables defined in either the beforeEach or globally
  • I've tried doing something like let a = 1 in the beforeEach, but can't seem to reference them in the test itself

I noticed that the QUnit docs say that they've eliminated globals. Could that be playing a role in this? https://qunitjs.com/upgrade-guide-2.x/

PS: It would be nice to have something that set up the module just once instead of every time

Edit

Here's the stacktrace:

Promise rejected before should do things: this._initProperties is not a function
Source:     

    TypeError: this._initProperties is not a function
        at create (http://localhost:7357/assets/vendor.js:46461:14)
        at Object.beforeEach (http://localhost:7357/assets/tests.js:185:20)
        at http://localhost:7357/assets/test-support.js:6586:31
        at tryCatch (http://localhost:7357/assets/vendor.js:61631:14)
        at invokeCallback (http://localhost:7357/assets/vendor.js:61646:15)
        at publish (http://localhost:7357/assets/vendor.js:61614:9)
        at http://localhost:7357/assets/vendor.js:41408:7
        at invoke (http://localhost:7357/assets/vendor.js:11120:16)
        at Object.flush (http://localhost:7357/assets/vendor.js:11184:11)
        at Object.flush (http://localhost:7357/assets/vendor.js:10992:17)
    

Actually I think this might not have to do with the globals. I've also added (was not included before) let create = Ember.Object.create just to save some typing (and wrapped the above objects with the call create(object). Getting rid of that and using the long form seems to get rid of this error though ...

1
They've eliminated globals means that they don't put their variables on window anymore, you should still be able to use shared data like this, I've done it many times before. You can also create another file in your test directory and store data inside that can be shared across multiple tests. Can you show how the function _initProperties is used and where its defined?nem035
That's the question isn't it? It isn't mine and I'm having a tough time tracking it down. I'm guessing that it's buried deep in either the Ember or QUnit source codeshane
Can you show the error stacktrace?nem035
Also you should remove everything until the error doesn't happen, then slowly add line per line to narrow done the culpritnem035

1 Answers

2
votes

The last part of your question is the actual problem.

When you do

let create = Ember.Object.create

You extract the function create from the Ember.Object and create a new reference to it, as a plain function.

As per JS binding rules, when you call a function as a plain function, this inside of it binds to the global object (or undefined in strict mode). On the other hand, when you call a function as a method on an object (i.e. calling it directly on Ember.Object) would bind this to that object.

That's why this is undefined within create and hence this._initProperties is not a function.

Here's a demo of what is happening:

// an object with a method
var obj = {
  getThis: function() {
    return this;
  }
};

// an extracted reference to the method
var extractedGetThis = obj.getThis;

// this binds to the object
console.log(
  obj.getThis() === obj // true
);

// this binds globally
console.log(
  extractedGetThis() === window // true
);