Testing Backbone Model's trigger method with Jasmine - testing

I got a weird error when testing the trigger method of my Backbone model. Below is my code:
Category = Backbone.Model.extend({
fetchNotes: function() {
this.trigger("notesFetchedEvent");
}
})
describe("Category", function() {
it("should fetch notes", function() {
var category = new Category;
spyOn(category, "trigger");
category.fetchNotes();
expect(category.trigger).wasCalledWith("notesFetchedEvent");
})
})
The error I got was "Expected spy trigger to have been called with [ 'notesFetchedEvent' ] but was called with ...jibberish...". Does anyone know how to fix this? Thanks.

I've found that often the best way to test event triggering is to register a spy as one of the listeners on the event instead of spying on the trigger method directly. This would look something like this:
describe("Category", function() {
it("should fetch notes", function() {
var category = new Category();
var spy = jasmine.createSpy('event');
category.on('notesFetchedEvent', spy);
category.fetchNotes();
expect(spy).toHaveBeenCalled();
});
});

Related

Update data without refresh the page with Vue and axios

I have a page with 2 tabs (Questions and Data) made on Vue and Axios.
In first Tab, I fill the form and submit it - Save button v-on:click="save".
save: function() {
axios({
method: 'patch',
url: url,
data: this.data
})
.then(function (response) {
this.data = response.data;
}
In the second Tab(Data) I have the list of saved data:
mounted() {
axios
.get('/api/recommended-products/?patient_uuid=' + '{{patient.uuid}}')
.then(response => (this.data= response.data.results))
}
Now when I change answers in Questions Tab my list in Data Tab should change automatically. It changes if I refresh the page - mounted() works.
I tried to create updateList() function:
updateList: function() {
axios
.get('/api/recommended-products/?patient_uuid=' + '{{patient.uuid}}')
.then(response => (this.data= response.data.results))
}
and added it to save() function like:
save: function() {
axios({
method: 'patch',
url: url,
data: this.data
})
.then(function (response) {
this.data = response.data;
this.updateList();
}
The problem is that this way works other second time (sometime works sometimes not). So I just added location.reload(); to save() but I don't like this approach. Is it possible to update Data list without refreshing the page? What am I doing wrong with updateList() function?
In my opinion here you should use vuex and its getters.
You would then have only one request in all the application and the data would be automatically refreshed once updated in the state.
You can then access the data using a computed property which will be automatically re-rendered when the state is updated.
Here is an example using multiple tabs : https://codesandbox.io/s/vuex-axios-demo-forked-m0cqe4?file=/src/App.vue
In this example, i'm loading posts information through the JsonPlaceHolder API.
Every time the form is re send (using a function). The action of the store is called, then the state is updated which trigger the getter to re-render the data.
Note: i'm loading the first post into the mounted with a default value of 1 here.
save: function() {
axios({
method: 'patch',
url: url,
data: this.data
})
.then(function (response) {
this.data = […this.data, …response.data]
}
You have re rendered issue I think can can you try above solution
I think this might be helpful. Try to implement something like following.
async function() {
try{
await axios.post() // or any request
//action if success
//another action if success
...
} catch(error) {
//do something with error.
console.log(error)
}

How to run method inside a response function using Vue.js

I'm trying to run a method when I get success response from API, but the method dont run. I made a quick example here to show.
The test() function should be executed after i get the response, since its calling another API endpoint. Here is the vue.js code.
var app = new Vue({
el: "#contents",
data: {
id: null,
details: [],
},
methods: {
fetchProductDetails: function(){
let vue = this;
axios.post("/api/get-details", {
id : id
})
.then(function (response) {
vue.details = response.data;
this.test();
})
.catch(function (error) {});
},
test: function () {
console.log(app.details);
}
},
mounted: function(){
this.fetchProductDetails();
},
});
You should run vue.test() instead of this.test(), just like you use vue.details = response.data instead of this.details = response.data.
When using an unnamed function in .then(), this no longer refers to your vue application, but to the unnamed function. You could use ES6 arrow function syntax in order to avoid having to set this to a specific variable, as arrow functions use their parent's scope for this instead of setting this to refer to themselves:
axios.post("/api/get-details", { id: this.id })
.then(response => {
this.details = response.data;
this.test();
})
.catch(error => { console.log(error)});
Arrow functions (and ES6 in general) are not supported by IE11 however. so you'd need to use Babel to compile it back to something ES5 JavaScript if you need to support older browsers.

How do I use the vue-resource plugin to delete an object on demand?

Using the vue-resource plugin, it has an example like this:
new Vue({
ready: function() {
var resource = this.$resource('someItem{/id}');
// delete item
resource.delete({id: 1}).then(function (response) {
// handle success
}, function (response) {
// handle error
});
}
})
I am somewhat confused that this is under the ready property since it suggests it runs and deletes something as soon as the component loads. How do I actually use that resource.delete function from clicking an element? For example, I have this:
<div #click="deleteReward(reward)" class="glyphicon glyphicon-trash pull-right"></div>
which is currently calling this:
deleteReward(reward) {
this.rewards.$remove(reward);
this.$http.delete('api/reward/' + reward.id).then(function (response) {
console.log('deleted ' + reward.name);
});
},
But my understanding is that I should somehow be able to call resource.delete instead and not have to specify the URL and method explicitly. Am I wrong in that assumption?
What I want is to do something like #click="reward.delete()" and have it know to call the resource.delete automatically and have resource.delete accept the reward object as a parameter.
You could try something like this:
new Vue({
data: {
resource: null
},
ready: function() {
this.resource = this.$resource('api/reward{/id}')
},
methods: {
deleteReward(reward) {
this.resource.delete({id:reward.id}).then(function (response) {
console.log('deleted ' + reward.name)
})
}
}
})
And then:
<div #click="deleteReward(reward)"></div>

Why store load event don't work?

I have a simple example :
var store = Ext.create('Ext.data.ArrayStore', {
})
store.load();
store.on('load', function() {
alert('test');
})
Why is it not work ?
You're performing the load() before you add the listener. Put the store.load() after the on() call.
Edit: Yes, I tested it.

Sinon JS "Attempted to wrap ajax which is already wrapped"

I got the above error message when I ran my test. Below is my code (I'm using Backbone JS and Jasmine for testing). Does anyone know why this happens?
$(function() {
describe("Category", function() {
beforeEach(function() {
category = new Category;
sinon.spy(jQuery, "ajax");
}
it("should fetch notes", function() {
category.set({code: 123});
category.fetchNotes();
expect(category.trigger).toHaveBeenCalled();
}
})
}
You have to remove the spy after every test. Take a look at the example from the sinon docs:
{
setUp: function () {
sinon.spy(jQuery, "ajax");
},
tearDown: function () {
jQuery.ajax.restore(); // Unwraps the spy
},
"test should inspect jQuery.getJSON's usage of jQuery.ajax": function () {
jQuery.getJSON("/some/resource");
assert(jQuery.ajax.calledOnce);
assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);
assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);
}
}
So in your jasmine test should look like this:
$(function() {
describe("Category", function() {
beforeEach(function() {
category = new Category;
sinon.spy(jQuery, "ajax");
}
afterEach(function () {
jQuery.ajax.restore();
});
it("should fetch notes", function() {
category.set({code: 123});
category.fetchNotes();
expect(category.trigger).toHaveBeenCalled();
}
})
}
What you need in the very beginning is:
before ->
sandbox = sinon.sandbox.create()
afterEach ->
sandbox.restore()
Then call something like:
windowSpy = sandbox.spy windowService, 'scroll'
Please notice that I use coffee script.