How to fix 'service injection' for EmberJS integration test? - ember.js

I have a tasks-table component that uses 'current-user' service. I inject the service into the component as displayed below
import { inject as service } from '#ember/service';
export default Component.extend({
currentUser: service(),
showBatchAction: computed('currentUser.user.directRoles.#each.title', function() {
return this.get('currentUser.user.directRoles').toArray().some((role) => {
return (role.id == 13) || (role.id == 15)
});
}),
});
The code works fine however, the integration test fails.
Test:
module('Integration | Component | tasks-table export csv button', function(hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function() {
run(() => {
this.owner.unregister('service:current-user');
});
this.owner.register('serivce:current-user', Service.extend({
user: EmberObject.create({
first_name: 'Bob',
last_name: 'Newby',
role: 'client',
directRoles: {title: 'employee', id: 1}
})
}));
});
test('it renders', async function(assert) {
var component = this.subject({
authManager: stubMyService.create()
});
await render(hbs `{{tasks-table}}`);
assert.equal(this.$('.export-csv').text().trim(), 'Export as CSV');
});
});
The error I'm getting is: TypeError: undefined is not an object (evaluating 'this.get('currentUser.user.directRoles').toArray')

directRoles is not an array? How come you loop through (some method) over an object. By saying object.toArray() does not work.

Related

How to over ride a function of component in integration test in ember Qunit testing

I'm writing my first question here sorry for any ambiguity.
I write an integration test for update-pw component which simple render update-pw and then fill input field with fillIn and then click save button which trigger the action savePW in update-pw.js. I only pass email(for whom we want to change password) and new password.
savePW() function further has a function call self.store.updateSingleUserPw(email, newPw) which is written in service store.js.
updateSingleUserPw(email, newPw) returns a promise after server process on API call. On basis of fulfillment or rejection of promise I show a modal.
I just want to make that promise fulfill or rejected in my test instead of server response for promise.
// integration/component/update-pw-test.js
import { module, test } from 'qunit';
import EmberObject from '#ember/object';
import { setupRenderingTest } from 'ember-qunit';
import { render, fillIn, click } from '#ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import Service from '#ember/service';
module('Integration | Component | update-pw', function(hooks) {
setupRenderingTest(hooks);
const store = Service.extend({
savePW() {
self.store.updateSingleUserPw(email, newPw, function() {
console.log('this is function overriding', email, newPw);
return true;
})
.then(function() {
// Reset controller fields
self.set('password', '');
self.set('updateModal', false);
swal({
title: 'Das hat geklappt',
type: 'success'
});
}, function() {
self.set('updateModal', false);
swal({
title: 'problems with setting new pw.',
type: 'error'
});
})
.finally(function() {
self.set('changingPassword', false);
});
}
});
test('it renders', async function(assert) {
this.application.register('service:store', store);
this.application.inject.service('store', { as: 'store' });
assert.expect(2);
this.set('updateModal', true);
this.set('testing', true);
let currentUpdateAdmin = EmberObject.create({
username: 'steinauer',
email: 'lala#test.at'
});
this.set('currentUpdateAdmin', currentUpdateAdmin);
await render(hbs`{{update-pw updateModal=updateModal currentUpdateAdmin=currentUpdateAdmin testing=testing store=store}}`);
assert.equal(this.element.querySelector('h4').textContent.trim(), 'set new PW for steinauer');
await fillIn('#password', 'test123456');
await click('.save-button');
// Template block usage:
await render(hbs`
{{#update-pw}}
template block text
{{/update-pw}}
`);
// assert.equal(this.element.textContent.trim(), 'what is this');
});
});
// components/update-pw.js
import Component from '#ember/component';
export default Component.extend({
changingPassword: false,
actions: {
savePW() {
let self = this;
if (!self.get('currentUpdateAdmin.email'))
return;
let newPw = self.get('password');
let email = self.get('currentUpdateAdmin.email');
self.set('changingPassword', true);
if (!email)
return;
self.store.updateSingleUserPw(email, newPw)
.then(function() {
// Reset controller fields
self.set('password', '');
self.set('updateModal', false);
swal({
title: 'Das hat geklappt',
type: 'success'
});
}, function() {
self.set('updateModal', false);
swal({
title: 'problems with setting new pw',
type: 'error'
});
})
.finally(function() {
self.set('changingPassword', false);
});
}
}
});
function in Service/store.js :
updateSingleUserPw(email, newPw) {
let headers = this.get('headers');
return new Promise(function(resolve, reject) {
$.ajax({
type: 'POST',
url: ENV.api + '/accounts/updateSingleUserPw',
data: {
email: email,
pwNew: newPw
},
headers,
dataType: 'json'
}).then(function(success) {
if (success) {
resolve(newPw);
} else {
reject('password change failed');
}
}, function(xhr, status, error) {
reject(error);
});
});
}
Before trying to override function I got only rejected promise modal but after the try of overriding the function i'm getting:
Promise rejected during "it renders": Cannot read property register of undefined.
thanks for your question 🎉
Firstly can I thank you for providing your code samples, I would not have been able to solve your question had you not provided so much! I have actually simplified some of the things that you are trying to do and I think by simplifying things I have come to the solution.
Firstly I have renamed the Service that you keep using to be called password-store. Usually when an Ember developer sees a Service named store they tend to think of an ember-data store which I'm assuming you're not actually using here by the functionality that you are expecting.
I generated a very simple mock store that just had one function in it:
// app/services/password-store.js
import Service from '#ember/service';
export default Service.extend({
updateSingleUserPw(email, password) {
// TODO: do something with email & password
return Promise.resolve();
}
});
This just returns a promise so that it won't break any of the other code samples. I then updated your update-pw component to use the new password store:
// app/components/update-pw.js
import Component from '#ember/component';
import { inject as service } from '#ember/service';
function swal() {
// noop - not sure where this comes from
}
export default Component.extend({
passwordStore: service(),
changingPassword: false,
actions: {
savePW() {
if (!this.get('currentUpdateAdmin.email'))
return;
let newPw = this.get('password');
let email = this.get('currentUpdateAdmin.email');
this.set('changingPassword', true);
if (!email)
return;
this.passwordStore.updateSingleUserPw(email, newPw)
.then(() => {
// Reset controller fields
this.set('password', '');
this.set('updateModal', false);
swal({
title: 'Das hat geklappt',
type: 'success'
});
}, () => {
this.set('updateModal', false);
swal({
title: 'problems with setting new pw',
type: 'error'
});
})
.finally(() => {
this.set('changingPassword', false);
});
}
}
});
I also added a swal() function because I didn't quite know where that came from in your example. It seemed to be missing so I just ignored it.
Now lastly I have setup a template so that the test will actually pass:
// app/templates/components/update-pw.hbs
<h4>set new PW for steinauer</h4>
{{input id="password" value=password}}
<button type="button" name="button" class="save-button" {{action 'savePW'}}></button>
Now with the application fully setup here is the full example of a test that will do exactly what you were hoping to do:
// tests/integration/components/update-pw-test.js
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, fillIn, click } from '#ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import StoreService from 'your-app-name/services/password-store';
module('Integration | Component | update-pw', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
const passwordStore = StoreService.extend({
updateSingleUserPw(email, newPw) {
console.log('updateSingleUserPw override!!');
assert.equal(newPw, 'test123456');
return Promise.resolve();
}
});
this.owner.register('service:password-store', passwordStore);
assert.expect(2);
this.set('updateModal', true);
this.set('testing', true);
let currentUpdateAdmin = {
username: 'steinauer',
email: 'lala#test.at'
};
this.set('currentUpdateAdmin', currentUpdateAdmin);
await render(hbs`{{update-pw updateModal=updateModal currentUpdateAdmin=currentUpdateAdmin testing=testing store=store}}`);
assert.equal(this.element.querySelector('h4').textContent.trim(), 'set new PW for steinauer');
await fillIn('#password', 'test123456');
await click('.save-button');
// Template block usage:
await render(hbs`
{{#update-pw}}
template block text
{{/update-pw}}
`);
});
});
The first thing that you might notice is that we are not using this.application.register or this.application.inject. I can't remember exactly if this is how it used to be done a long time ago but this is not available for a few years in Ember.
What we end up doing is we import the StoreService from your-app-name/services/password-store (replacing your-app-name with whatever your modulePrefix is) and then we extend it while overriding the updateSingleUserPw() function. In your example it looked like you were trying to override a function called savePW() but that is actually the action name from the component and it might have been slightly confusing you.
I hope that helps, I have tested the example locally and it works perfectly well! You may also notice I added an assertion inside the service, this is quite a useful pattern to make sure that the service receives the right arguments from the component 👍

Qunit serializer test is returning “Cannot read property ‘push’ of null”

Ember serializer test below is failing with “Cannot read property ‘push’ of null”.
I am using Pretender mock server library. The test is failing when I'm calling a store.findRecord()
Note how there are no relationships in the assignment model/serializer, which is why it's confusing that it's throwing the following error:
Click here to see the error that's getting returned
assignment serializer:
import DS from 'ember-data';
const { JSONAPISerializer } = DS;
export default JSONAPISerializer.extend({
attrs: {
autoPassReviewerNote: 'autoPassReviewerNote',
dateOfCreation: 'date_of_creation',
displayType: 'displayType',
lastUpdate: 'last_update',
moduleItem: 'moduleItem',
submissionType: 'submissionType'
}
});
assignment model:
import DS from 'ember-data';
const {
Model,
attr
} = DS;
export default Model.extend({
title: attr('string'),
submissionType: attr('string'),
description: attr('string'),
completed: attr('boolean'),
displayType: attr('string'),
dateOfCreation: attr('string'),
lastUpdate: attr('string'),
autopass: attr('boolean'),
moduleItem: attr('object')
});
serializer test (which is failing):
import { moduleForModel, test } from 'ember-qunit';
import Pretender from 'pretender';
import Ember from 'ember';
const {
run
} = Ember;
var server;
moduleForModel('assignment', 'Unit | Serializer | assignment', {
needs: [
'serializer:assignment'
],
beforeEach: function() {
server = new Pretender(function() {
// eslint-disable-next-line ember/use-ember-get-and-set
this.get('/assignments/:id', function() {
data: {
type: 'assignment',
id: 98,
attributes: {
title: 'dfgdfg',
submissionType: 'dfgdf',
displayType: 'dfgdfg',
date_of_creation: 'sdfgsdfg',
last_update: 'fgdgd'
}
}
};
return [ 200, { 'Content-Type': 'application/json' }, JSON.stringify(response) ];
});
});
},
afterEach: function() {
server.shutdown();
}
});
test('testing assignment serializer', function(assert) {
var checkAttrSerialization = (assignment) => {
assert.equal(assignment, true);
}
let store = this.store();
run(() => {
return store.findRecord('assignment', 98).then((assignment) => checkAttrSerialization(assignment));
});
});

Ember-data: Saving parent to child via a select box

I've went over a lot of examples both here on SO and in some guides/blogs. Nothing seems to work.
I have a customer that hasMany loads
currently the code is:
route
export default Ember.Route.extend({
setupController: function(controller, model) {
controller.setProperties(model);
},
model: function() {
return Ember.RSVP.hash({
content: this.store.createRecord('truck-load'),
customerList: this.store.findAll('customer'),
equipmentList: this.store.findAll('equipment-list')
});
},
resetController(controller, isExisting) {
if (isExisting) {
var model = controller.get('model');
if (model.get('isNew')) {
model.destroyRecord();
}
}
}
});
select box in the template - materialize add on for ember-cli
{{md-select content=customerList
value=model.customer
label="Customer"
prompt="Please Choose a Customer..."
optionLabelPath='content.name'
optionValuePath='content.id'}}
Current controller - I've tried this many ways
export default Ember.Controller.extend({
actions: {
save() {
var truckload = this.get('model');
this.get('model.customer').then((customer) => {
truckload.set('customer', customer);
truckload.save().then((load) => {
this.get('notify').success('Truck Load Created');
this.transitionToRoute('truck-loads.show', load.id);
});
});
JSON for my JSON-API server running Elixir/Phoenix
Parameters: %{"data" => %{"attributes" => %{"pro_number" => "423432", "special" => nil, "status" => nil},
"relationships" => %{"customer" => %{"data" => nil},
"equipment_list" => %{"data" => nil}}} }
customer (and equipment-list) are both coming over nil.
This fixed it.
1) Settings the drop down result as a controller property
2) Accessing this to lookup the model and set it.
selectedCustomer: null,
selectedEquipment: null,
actions: {
save() {
var truckload = this.get('model');
var customer_id = this.get('selectedCustomer');
var equipment_id = this.get('selectedEquipment')
this.store.findRecord('customer', customer_id).then((customer) => {
truckload.set('customer', customer);
this.store.findRecord('equipmentList',equipment_id).then((equipment) => {
truckload.set('equipmentList', equipment);
truckload.save().then((load) => {
this.get('notify').success('Truck Load Created');
this.transitionToRoute('truck-loads.show', load.id);
});
});
});
return false;
},
I doubt this is the best way to do it - but - it DOES work.

Ember.data 1.13 JSONAPI belongsTo async pass to component

I use JSONAPI as the adapter.
Model:
App.Report = DS.Model.extend({
'name': DS.attr('string'),
'description': DS.attr(),
'placemark': DS.belongsTo('placemark', {
'async': true
})
});
App.Placemark = DS.Model.extend({
'geometry': DS.attr(),
'description': DS.attr(),
'reports': DS.hasMany('report', {
'async': true
})
});
Route:
App.ReportRoute = Ember.Route.extend({
'model': function (params) {
return this.store.find('report', params.report_id);
},
'renderTemplate': function () {
this.render('report', {
'into': 'application'
});
}
});
App.PlacemarkRoute = Ember.Route.extend({
'model': function (params) {
return this.store.find('placemark', params.element_id);
},
'renderTemplate': function () {
this.render('placemark', {
'into': 'application'
});
}
});
i use a component for embeddeding a map canvas where the data (placemark) are come from the Report:
{{map-canvas placemark=model.placemark}}
App.MapCanvasComponent = Ember.Component.extend({
'availablePlacemark': Ember.computed('placemark', function() {
return this.get('placemark.geometry');
});
I found placemark is unable to retrieve and 'undefined' returned.
I tries using:
return this.get('placemark').then(function(placemark) {
return placemark.get('geometry');
});
it did not work.
now, I am thinking if it is a JSONAPI adapter issue or async problem from view to a components.
i am using ember 1.13
Hope someone could help
thanks

Ember- integration test case on action

In ember controller
action:function(){
a:function(){
....
this.set('b',true);
}
}
I just want to write a test case for this
test('a - function test case', function(assert) {
var controller= this.subject();
controller._action().a();
assert(controller.get(b),true);
});
but this not working I'm getting undefined error.
any other way to pass this test case?
Looking to your code, I believe you're trying to use ember actions, if so you have to use actions: { ... } instead of action: function() { ... }.
And to trigger an action you use the send method.
This is an example on how to test an action in ember-cli:
app/controllers/index
import Ember from 'ember';
export default Ember.Controller.extend({
value: null,
actions: {
changeValue: function() {
this.set('value', true);
}
}
});
tests/unit/controllers/index-test.js
import {
moduleFor,
test
} from 'ember-qunit';
moduleFor('controller:index', {});
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(!controller.get('value'));
controller.send('changeValue');
assert.ok(controller.get('value'));
});
This was working for me
test('it exists', function(assert) {
var controller = this.subject();
assert.ok(!controller.get('value'));
Ember.run(function(){
controller.send('changeValue');
assert.ok(controller.get('value'));
});
});