Jasmine AJAX spy failure - javascript

Ok these tests were passing a few bit ago. I have made no changes to which version of jasmine I'm using but... can anyone see obvious syntax errors here?
describe("ajax return", function() {
beforeEach(function() {
ajaxSpy = spyOn($, "ajax")
})
describe("on success", function() {
beforeEach(async function() {
ajaxSpy.and.callFake(function(e) {
e.success({"success":true, "remove":{1:"test"},"assign_prestock":"test2"})
})
await catalogDOM.syncAvailability(null)
})
it("should call", function() {
...
})
})
})
When running, I'm getting this error:
1_catalogDOM_spec.js:518 Uncaught (in promise) TypeError: e.success is not a function
UPDATE code for catalogDOM.syncAvailability
catalogDOM.syncAvailability: function(item_name_id) {
return new Promise(function(resolve, reject) {
$.ajax({
type:"POST",
url: "/retrieve-options-availability",
dataType:"json",
contentType:"application/json",
data: JSON.stringify(params)
})
.done(function(response, status_string, jqxhr) {
if (response["success"] == true) {
resolve()
} else {
reject(response["message"])
}
})
.fail(function(jqxhr, error_string, exception_object){
reject("Error loading availability. Refresh & try again or contact us if this persists.")
})
}

Try doing this to debug:
ajaxSpy.and.callFake(function(e) {
// add console.log here !!
console.log('e: ', e);
e.success({"success":true, "remove":{1:"test"},"assign_prestock":"test2"})
})
Apparently, .success is not a function anymore and you can look at the value of e there. I am thinking e is the argument for what's provided in $.ajax(/* e is here */);.
Looking at the documentation here: https://api.jquery.com/jquery.ajax/, I think we need to mock a done function.
Try this:
ajaxSpy.and.callFake(function (e) {
return {
done: function () {
return {
"success":true,
"remove":{1:"test"},
"assign_prestock":"test2",
// edit - create a fail function that's empty here
fail: function() {
// leave empty
}
};
}
};
});
Edit
Instead of doing a spy on ajaxSpy, try spying on catalogDOM directly. Something like this:
spyOn(catalogDOM, 'syncAvailability').and.resolveTo({/* Mock value here */ });
Or
spyOn(catalogDOM, 'syncAvailability').and.returnValue(Promise.resolve({ /* Mock value here */ });
And then you don't have to await it.

Related

ignored children tests in Frisby JS with jasmine-node

I use Frisy with jasmine-node to test an Meteor API.
I want to test the remove of a discussion in a chat App. For this, I need to create a new discussion in chat and add a message in the discussion.
I noticed that my test fail if I put it after the second .then() method. It fails also after the third .then(). However, it works correctly after the first .then() method.
An example code with explicit failed test expect(false).toBe(true);:
var frisby = require('frisby');
describe("chat update", function() {
it("message added", function(done) {
frisby.post('http://localhost:3000/api/chat', {
name: "remove"
})
.then(function (res) {
let id = res._body.id;
expect(false).toBe(true); // (1) it fails the test
frisby.post('http://localhost:3000/api/chat/'+id+'/addmessage',
{
auteur: "Samuel",
message: "My message"
}
)
.then(function (res) {
expect(false).toBe(true); // (2) Without (1) it's ignored by frisby
frisby.post('http://localhost:3000/api/chat/remove',
{id: id}
)
.then(function (res) {
expect(false).toBe(true); // (3) Without (1) it's ignored by frisby
})
});
})
.done(done);
})
});
If I run the test, it will fail thanks to expect(false).toBe(true); // (1) it fails the test line.
If I remove this line, the test will run and jasmine valid it as right.
Do you know a way to don't ignore the (2) and (3) tests ?
Finaly, I found the solution.
It's because I forgot to return all frisby action (except the first one) like in the following code:
var frisby = require('frisby');
describe("chat update", function() {
it("message added", function(done) {
frisby.post('http://localhost:3000/api/chat', {
name: "remove"
})
.then(function (res) {
let id = res._body.id;
return frisby.post('http://localhost:3000/api/chat/'+id+'/addmessage',
{
auteur: "Samuel",
message: "My message"
}
)
.then(function (res) {
return frisby.post('http://localhost:3000/api/chat/remove',
{id: id}
)
.then(function (res) {
expect(false).toBe(true); // Will fail the test
})
});
})
.done(done);
})
});
You may notice the return operators before frisby.post().
I hope it will help !

Jasmine, React & AJAX: Unit testing function within a function

Here's the code I would like to test. Specifically, I want to spy on a utility called Linkvalidation.validate to make sure that it is called when handleSave() is called.
This code lives in a component called the CondensedFormModal:
handleSave() {
LinkValidation.validate(this.state.url)
.then((response) => {
if (response.success) {
this.setState({
message: ''
});
}
})
.fail((error) => {
if (error && error.message && error.message.match(/invalid internal link/)) {
this.setState({
message: 'The URL is an internal link. Please use a public link.'
});
} else {
this.setState({
message: 'The URL is invalid.'
});
}
});
Here is the LinkValidation.validate utility I'm using in the handleSave function above:
define([
'module/api-call'
], function(
ApiCall
) {
'use strict';
// Calls validation API
function validate(link) {
var deferred = $.Deferred();
ApiCall.apiCall(
'/url/check',
{ link: link },
'POST',
function(data) {
if (data.success === true) {
deferred.resolve(data);
} else {
// This link is not valid
deferred.reject(data);
}
},
function() {
deferred.reject();
}
);
return deferred;
}
return {
validate: validate
};
});
Here is my test file--
Import statement:
import { validate } from 'modules/link-validation.js';
Test:
describe('when the URL is an internal link', () => {
it('displays a unique error message', (done) => {
let modal = shallowInstance(<CondensedFormModal />);
modal.setState({
url: 'https://internal.example.com'
});
let x = jasmine.createSpy('validate').and.returnValue({
message: "invalid internal link",
success: false,
url: 'https://example.com'
});
modal.handleSave();
_.defer(() => {
expect(x).toHaveBeenCalled();
done();
});
});
});
When I run this test, it consistently fails with the message "Expected spy validate to have been called."
After looking at the Jasmine docs (https://jasmine.github.io/2.1/introduction) and various other Stack Overflow questions (Unit test with spy is failing. Says spy was never called , Jasmine test case error 'Spy to have been called' , etc.) I'm unable to make this work. I've also tried callFake and callThrough instead of returnValue.
Any ideas on how to spy on LinkValidation.validate to assure that it was called?
This line:
let x = jasmine.createSpy('validate')
creates new spy function (it doesn't spy on existing validate function) and handleSave function is not aware of it. So it's not called at all.
You have to set spy on function that is actually called in your component. Since your CondensedFormModal uses LinkValidation module (which I assume is imported in component file) you have to set spy on validate function from imported module which is actually used by component. So I'd suggest something like this:
In CondensedFormModal constructor set LinkValidation as component property to make it easily accessible in tests:
this.LinkValidation = LinkValidation;
In handleSave use validate function like this:
this.LinkValidation.validate(this.state.url);
And finally in test set spy on component validate method:
describe('when the URL is an internal link', () => {
it('displays a unique error message', (done) => {
let modal = shallowInstance(<CondensedFormModal />);
...
spyOn(modal.LinkValidation, 'validate').and.returnValue({
message: "invalid internal link",
success: false,
url: 'https://dash.vagrant.local.rf29.net/shopping/new'
});
modal.handleSave();
_.defer(() => {
expect(modal.LinkValidation.validate).toHaveBeenCalled();
done();
});
});
});

Benchmarking asynchronous vs synchronous code that throws an error

I have two functions; one is async (testAsync) and one is sync (testSync). I'm trying to use them with benchmark.js. Which one is faster and by how much. They should both throw an error.
I'm confused how I'm supposed to a) setup an asynchronous test b) make sure the test accounts throw an error for each function.
Here's what I got:
import Benchmark from 'benchmark'
var suite = new Benchmark.Suite;
// add tests
suite.add('query validation test sync', function(deferred) {
try {
testSync({'name': 'reggi'});
} catch (e) {
deferred.resolve();
}
})
.add('query validation test async', function(deferred) {
testAsync({'name': 'reggi'}, {})
.then(deferred.resolve)
.catch(deferred.resolve);
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('error', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
// run async
.run({ 'async': true });
You should specify defer: true as option the test function:
suite.add('query validation test sync', function() {
try {
testSync({'name': 'reggi'});
} catch (e) {
// it's sync code, you don't need use defer here
}
})
.add('query validation test async', {
defer: true,
fn: function(deferred) {
testAsync({'name': 'reggi'}, {})
.then(deferred.resolve)
.catch(deferred.resolve);
}
})
...
Check example on the benchmark.js site (in the middle of section)

Angular custom method on $resource with .then() function causing error

I have this in my Angular service:
return $resource(BASE + '/cases/:id',
{id: '#id'}, {
status: {method: 'GET', params: {status: '#status'}}
});
When using the method added to the $resource definition along with the promise's .then() function, I'm getting an error:
Cases.status({status: 'pending'})
.then(function(res) {
console.log(res);
$scope.cases.pending = res.data.cases;
})
.then(function() {
$scope.tabbed.pending = true;
});
After the above snippet is run, the error I get is:
TypeError: undefined is not a function on this line: .then(function(res) {
Can I not use these functions as I usually do when I'm using an extra method defined on the $resource?
I think you need to use $promise of $resource object which will call success function when actual promise gets resolved & then you could proceed with the promise chain.
CODE
Cases.status({status: 'pending'})
.$promise
.then(function(res) {
console.log(res);
$scope.cases.pending = res.data.cases;
})
.then(function(cases) {
$scope.tabbed.pending = true;
});
You have to use $promise in order to access the promise is created on that call, like this:
Cases.get({id: 123}).$promise
.then(function (success) {
//do something
}).then(function (error) {
//do something else
})
Or you can send both functions as callbacks:
Cases.get({id: 123}, function (success) {
//do something
}, function (error) {
//do something else
});
Tip
You don't have to add a new method to that $resource to send a GET to the same url, you can just have your $resource be plain:
//... code
return $resource(BASE + '/cases'});
and when you pass the parameters to it (if you are passing them as in the example) it will match the keys according to the object so you can just say:
Cases.get({status: 'something'}).$promise
.then(function(success){
//... code
})
.then(function(err){
//... code
});
Cases.get({id: 123}).$promise
.then(function(success){
//... code
})
.then(function(err){
//... code
});

Calling a method in one JS module and returning data to another

Trying to master the modular programming pattern in JS. I'm having difficulties calling a method in one module -- which returns true or false depending on conditions -- from another module and using the data returned.
This is a cut down version of my first module:
var stAuthentication = (function() {
return {
someMethod: function() {
///
},
isAuthenticated: function() {
var authenticated;
ajax_isAuthenticated = $.ajax({
url: root+'/assets/scripts/php/app/isAuthenticated',
type: 'POST',
dataType: 'text'
});
ajax_isAuthenticated.done(function(response) {
authenticated = response;
console.log("Ajax request returned "+authenticated);
});
ajax_isAuthenticated.fail(function(response) {
console.log("Ajax request (ajax_isAuthenticated) failed.");
});
if (authenticated == 0) {
return false;
} else {
return true;
}
},
someOtherMethod: function() {
///
}
}
})();
As you can see, the method in question is isAuthenticated(). This basically sends an AJAX request to the server where it checks if the user is logged in and returns 1 or 0. I have tested the response from this and it works as it should. My issue is with the next module, where I'll be calling the method in the first example to determine if the user is logged in before running a task in the second module. Here it is:
var stReadLater = (function() {
var elements, quantity;
return {
someMethod: function() {
//
},
saveItem: function() {
if (stAuthentication.isAuthenticated()) {
console.log("Signed in");
// user is signed in so use this feature
} else {
console.log("Not signed in");
// ask user to sign in before using this feature
}
},
someOtherMethod: function() {
//
}
}
})();
The actually location of the issue is this few lines I think, from the first module:
if (authenticated == 0) {
return false;
} else {
return true;
}
First of all, I noticed that it was always returning false. After more playing around, I noticed that authenticated was undefined which led me to move var authenticated around the scope but no luck. I also tried this.authenticated without luck, too.
I know I'm fairly close but I have tried so many different variations I've totally lost it. What is the correct way of doing this?
========================
THE FIX
I was checking the authenticated variable before AJAX had set it. Thanks to #willma for the suggestions. This is what I did if anybody else comes across this:
isAuthenticated: function() {
var deferred = $.Deferred();
ajax_isAuthenticated = $.ajax({
url: root+'/assets/scripts/php/app/isAuthenticated',
type: 'POST',
dataType: 'text'
});
ajax_isAuthenticated.done(function(response) {
deferred.resolve(response)
});
return deferred.promise();
}
and then in the second module:
saveItem: function() {
$.when(stAuthentication.isAuthenticated()).then(function(response) {
if (response == 0) {
console.log("Not signed in");
} else {
console.log("Is signed in");
}
});
}
You're mixing up synchronous and asynchronous code. isAuthenticated will always return false because the function will always return before either of the done or fail callbacks can be called.
Your entire function is basically doing this:
var authenticated;
if (authenticated == 0) { // This is the case because undefined == 0 -> true
return false;
} else {
return true;
}
You have two solutions. Either the stReadLater object can pass a callback function like this:
function done() {
console.log("Signed in");
}
function fail() {
console.log("Not signed in");
}
stReadLater.saveItem = function() {
stAuthentication.isAuthenticated(done, fail);
}
Then is your authentication module:
var stAuthentication.isAuthenticated = function(done, fail) {
ajax_isAuthenticated = $.ajax({
url: root+'/assets/scripts/php/app/isAuthenticated',
type: 'POST',
dataType: 'text'
});
ajax_isAuthenticated.done(done);
ajax_isAuthenticated.fail(fail);
}
Or you can use promises. I find promises more elegant. It's worth reading about them

Categories