I'm working on building up unit tests for our Ember applications. The current set of tests I'm targeting are our Models. I've got a set of tests that work really well for Models based on Ember Data, but seem to fail when based on Ember.Object. Here are two examples:
App.Person = DS.Model.extend({
First: DS.attr("string"),
Last: DS.attr("string")
});
App.Person2 = Ember.Object.extend({
First: null,
Last: null
});
And the test which passes for DS.Model:
it('has a valid attribute: First', function() {
var property = App.Person.metaForProperty('First');
expect( property.type ).to.eql('string');
expect( property.isAttribute ).to.eql(true);
});
Then, when using the same structure for Ember.Object:
it('has a valid attribute: First', function() {
var property = App.Person2.metaForProperty('First');
});
I get the following error:
Error: Assertion Failed: metaForProperty() could not find a computed property with key 'First'.
at new Error (native)
at Error.Ember.Error (http://0.0.0.0:3385/app/js/components/ember/ember.js:844:19)
at Object.Ember.assert (http://0.0.0.0:3385/app/js/components/ember/ember.js:73:11)
at Function.Mixin.create.metaForProperty (http://0.0.0.0:3385/app/js/components/ember/ember.js:13247:11)
at Context.<anonymous> (http://0.0.0.0:3385/tests/model-person-test.js:6:35)
at invoke (http://0.0.0.0:3385/tests/bower_components/ember-mocha-adapter/adapter.js:60:8)
at Context.suite.on.context.it.context.specify.method (http://0.0.0.0:3385/tests/bower_components/ember-mocha-adapter/adapter.js:102:13)
at Test.require.register.Runnable.run (http://0.0.0.0:3385/tests/assets/mocha.js:4200:15)
at Runner.require.register.Runner.runTest (http://0.0.0.0:3385/tests/assets/mocha.js:4591:10)
at http://0.0.0.0:3385/tests/assets/mocha.js:4637:12
Can anyone offer insight as to what might be going wrong?
Yeah, First/Last aren't computed properties on Person2, they are just properties.
This would work
App.Person2 = Ember.Object.extend({
First: null,
Last: null,
Blah: function(){
}.property('First')
});
var j = App.Person2.metaForProperty('Blah');
console.log(j);
When you do DS.attr() Ember Data is actually injecting a computed property right there, see: Ember Data attr Source
Here's an updated set of assertions that work for me. It looks like because it's only using Ember.Object, the extra goodies from DS.Model simply aren't there.
it('has a valid attribute: First', function() {
var person = App.Person.create({
id: 1,
First: 'Andy'
});
expect( App.Person.proto().hasOwnProperty('First') ).to.eql(true);
expect(typeof person.get('First')).to.eql('string');
expect(person.get('First')).to.eql('Andy');
});
Related
I'm trying to change a controller's property from a component as follows(JSBIN example http://jsbin.com/gevuhu):
App.CategoryManagerController = Ember.Controller.extend({
selectedCategory: null,
});
App.BlogPostComponent = Ember.Component.extend({
needs: ['categoryManager'],
selectedCategory: Ember.computed.alias('controllers.categoryManager.selectedCategory'),
actions:{
selectedCategory: function (){
this.set('selectedCategory',1);
}
}
});
but getting the error Property set failed: object in path "controllers.categoryManager" could not be found or was destroyed.
Is it that we cannot use "needs" in components ?
Ember Components are completely isolated from surrounding context including controllers (see here). That's the bad news. The good news is that if you pass selectedCategory into the component, it will become 2-way bound, so any change to it in the component will be known by your controller.
So, your controller could be something like:
App.ApplicationController = Ember.ObjectController.extend({
needs: ['categoryManager'],
selectedCategory: Ember.computed.alias('controllers.categoryManager.selectedCategory'),
selectedCategoryChanged: function(){
alert("NEW CATEGORY: " + this.get('selectedCategory'));
}.observes('selectedCategory')
});
and then in your application template, you can say
{{ blog-post selectedCategory=selectedCategory }}
See a working example here
In later version like 2.2. We'll be writing this as:
App.ApplicationController = Ember.Controller.extend({
categoryManager: Ember.inject.controller("categoryManager")
});
and now, categoryManager will now have the controller named categoryManager.
I have these models in an ember-cli app:
var PuzzleRound = DS.Model.extend({
year: DS.attr('number')
});
var Puzzle = DS.Model.extend({
puzzleRounds: DS.hasMany('puzzleRound', {async: true})
});
And here's my test from tests/unit/models/puzzle-test.js:
import {
moduleForModel,
test
} from 'ember-qunit';
import PuzzleRound from 'weather-roulette/models/puzzle-round';
moduleForModel('puzzle', 'Puzzle', {
// Specify the other units that are required for this test.
needs: ['model:puzzleRound']
});
test('it exists', function() {
var model = this.subject();
// var store = this.store();
ok(!!model);
});
I get this error when running ember test:
Attempting to register an unknown factory: `model:puzzleRound`
I'm using ember-cli 0.1.1, Ember.js 1.7.0, Ember Data 1.0.0-beta.11. Does anyone have anything I can try to fix this?
I just tried out this code with ember-cli 0.0.44 and I got the same error that you did.
I renamed both references to the puzzleRound model path to puzzle-round and then your test passed for me. So:
DS.Model.extend({
puzzleRounds: DS.hasMany('puzzle-round', {async: true})
});
and
moduleForModel('puzzle', 'Puzzle', {
needs: ['model:puzzle-round']
});
I knew that the hyphenated style was preferred over the camelCase style, but I'm not sure when this became mandatory. This requirement may be specific to ember-cli or ember-qunit.
I was looking for a solution similar to this one for awhile, and did not see any mention of my solution so I thought I would post here anyways. It's quite simple really: make sure that the controller you're referencing is actually there.
// my-ember-application/tests/unit/controllers/index/bar-test.js
moduleFor('controller:index/bar', 'My Bar Test', {
beforeEach() { .. }
});
test('it exists', function (assert) {
assert.ok(true);
});
This code would reference a controller at this location:
my-ember-application/app/controllers/index/bar.js
I'm trying to load the current user into the data store but am having some difficulty. The server uses PassportJS and visiting /api/users/me returns a JSON object similar to this:
{"user":{"_id":"53a4d9c4b18d19631d766980","email":"ashtonwar#gmail.com",
"last_name":"War","first_name":"Ashton","location":"Reading, England",
"birthday":"12/24/1993","gender":"male","fb_id":"615454635195582","__v":0}}
My store is just defined by App.store = DS.Store.create();
The controller to retrieve the current user is:
App.UsersCurrentController = Ember.ObjectController.extend({
content: null,
retrieveCurrentUser: function() {
var controller = this;
Ember.$.getJSON('api/users/me', function(data) {
App.store.createRecord('user', data.user);
var currentUser = App.store.find(data.user._id);
controller.set('content', currentUser);
});
}.call()
});
It is called by my application controller:
App.ApplicationController = Ember.Controller.extend({
needs: "UsersCurrent",
user: Ember.computed.alias("controllers.UsersCurrent")
});
I suspect the line App.store.createRecord('user', data.user); is causing issues but I don't have any idea how to fix it.
The console logs TypeError: this.container is undefined while the Ember debugger shows every promise is fulfilled and the users.current controller has no content. Thankyou for any help you can provide.
Are you defining the store on the App namespace, because Ember Data doesn't do that by default. Either way, you're failing to define the type you want to find after you create the record.
var currentUser = controller.store.find('user', data.user._id);
createRecord returns the record, so there is no point in finding it afterward
var currentUser = controller.store.createRecord('user', data.user);
Also in your example, you are trying to call the function immediately on the type, and not on the instance. You should add that as a method to run on init.
App.UsersCurrentController = Ember.ObjectController.extend({
retrieveCurrentUser: function() {
console.log('hello')
var controller = this;
Ember.$.getJSON('api/users/me', function(data) {
var user = controller.store.createRecord('user', data.user);
controller.set('model', user);
});
}.on('init')
});
http://emberjs.jsbin.com/OxIDiVU/693/edit
I have a simple form in Ember which submits to a server and returns a response. On a failed response, I want to re-focus on the input field. Is there a way to access the field in my controller using the value binding?
Here's my jsbin as an example:
http://jsbin.com/umodir/1/edit
http://jsbin.com/efatut/2/edit
Your server should return something that sets a property on your controller that you can observe. I made it pretty simple and called it "error".
var App = Ember.Application.create();
App.ApplicationController = Ember.ObjectController.extend({
error: false,
submit: function() {
alert('I want to set a focus on the email input field now...');
this.set('error', true);
}
});
App.ApplicationView = Ember.View.Extend({
focusEmail: function() {
if (this.get('controller.error')) {
this.$('input[type=text]').focus();
}
}.observes('controller.error')
});
If you wanted to get really fancy you could use an Ember.Component like {{eb-input focuson="error"}} that would automatically focus when the controller's "error" property changed.
I have a model:
app.ObjectOne = Em.Object.extend({
id: null,
count: null
});
And another model, which computed property 'SomeProperty' I want to depend on property 'count' from ObjectOne
app.ObjectTwo = Em.Object.extend({
id: null,
someProperty: function(){
return count+5;
}.property('app.SomeObjectController.count')
});
So, can I do it that way?
Or is there any other way to do it;
The main idea is that data of ObjectOne model comes after ObjectTwo, so I what to recompute property and rerender view
If I understand well, the behavior you want is exactly what Ember.js can bring to you.
First, here is a very basic template:
<script type="text/x-handlebars">
Here, the template is updated each time this property is updated (equivalent to bound property)
{{App.objectTwo.someProperty}}
</script>
Here is the javascript. I don't know if you really want to use an ObjectController here, but you mention it, so I have use it. An ObjectController's act as a proxy around it's content property. That means here that someObjectController.get('count') will try to get the count property from the controller itself, and if it does not exist, then the controller retrieve the count property from its content.
App = Ember.Application.create();
App.someObjectController = Ember.ObjectController.create();
App.ObjectOne = Em.Object.extend({
id: null,
count: null
});
App.objectOne = App.ObjectOne.create({
id: 1,
count: 42
});
App.someObjectController.set('content', App.objectOne);
App.ObjectTwo = Ember.Object.extend({
id: null,
someProperty: function(){
return App.someObjectController.get('count')+5;
}.property('App.someObjectController.count')
});
App.objectTwo = App.ObjectTwo.create({
id: 1
});
//in 3 seconds the count property is set to 0. You can see the template is updated
Ember.run.later(function(){
App.objectOne.set('count', 0);
},3000);
Here is the working jsfiddle: http://jsfiddle.net/Sly7/M3727/4/