chai spy passes to.be.spy assertion, but throws error on .called() "is not a spy or a call to a spy" - chai

I am attempting to use chai spies in my unit tests. I am using karma, mocha, chai and sinon.
I was originally trying to use chai spies to verify a callback in my angular app was called when provided. But to solve the error I have boiled my test case down to what I think is a pretty simple one.
I have the below unit test
describe('spy tests:', function() {
it('should be spy', function() {
var spy = chai.spy();
expect(spy).to.be.spy;
});
it('should have been called', function() {
var spy = chai.spy();
spy();
expect(spy).to.have.been.called();
});
}
The first "should be spy" test passes, which as far as I can reason means that a spy is in fact being created. However the second test fails with the below error:
TypeError: { [Function]
toString: { [Function: toString] bind: { [Function: bind] bind: [Circular] } },
reset: { [Function] bind: { [Function: bind] bind: [Circular] } },
__spy: { calls: [ [] ], called: true, name: undefined },
bind: { [Function: bind] bind: [Circular] } } is not a spy or a call to a spy!
This is particularly frustrating as I just verified it is a spy in the previous "should be spy" assertion.
Below are my frameworks, as I am including them in my karma.conf.js:
frameworks: ['chai-as-promised', 'chai-things', 'chai-spies', 'sinon-chai', 'chai', 'mocha']
To make matters more frustrating the below assertion does pass:
expect(spy.__spy.called).to.be.true;
I am happy to provide any other info needed. Thanks!

I'm not a chai spies expert, but I was having similar issues getting chai spy to work.
I just tested the function, and there appears to be a missing ")" bracket at the end; Maybe it's a copy and paste error?
describe('spy tests:', function() {
it('should be spy', function() {
var spy = chai.spy();
expect(spy).to.be.spy;
});
it('should have been called', function() {
var spy = chai.spy();
spy();
expect(spy).to.have.been.called();
});
});
If that's not the issue, then I was able to create a successful chai spy test by overwriting my function variable with spy.
function functionA() {
}
function thatCallsFunctionA() {
functionA();
}
describe('Function thatCallsFunctionA', function() {
it('Should call functionA', function() {
var functionA = spy(functionA);
thatCallsFunctionA();
expect(functionA).to.have.been.called();
});
})

Related

Async lifecycle function in Vue Test Utils

When I tried to test the component which has mounted method like this:
mounted(){
this.search()
}
methods:{
async search(){
try{
await axios.something
console.log("not executed only when shallowMount")
}catch{}
}
}
I checked it returned Promise<pending> without await.
I wrote the test like this:
wrapper = await shallowMount(Component, {
localVue
});
await wrapper.vm.search()// this works perfectly
However, only the shallowMount apparently skips awaited function while the next line works perfectly.
I have no idea about this behavior.
How can I fix it?
Edit:
I also use Mirage.js for mocking response.
function deviceServer() {
return createServer({
environment: "test",
serializers: {
device: deviceListSerializer()
},
models: {
device: Model
},
fixtures: {
devices: devices
},
routes() {
this.namespace = "/api/v1/";
this.resource("device");
},
seeds(server) {
server.loadFixtures("devices");
}
});
}
shallowMount is not returning Promise and that's why there is nothing to await. To await promises in tests try to use flush-promises library. With flushPromises your test will look like this:
import flushPromises from 'flush-promises'
wrapper = shallowMount(Component, {
localVue
});
await flushPromises(); // we wait until all promises in created() hook are resolved
// expect...

Slack integration only works on onPrepare, but not in onComplete, afterLaunch?

I am hooking my protractor tests with Slack, the idea is to send a message on a slack room if the test started and once it's done.
However, I can only successfully send messages to slack during onPrepare. But not in afterLanch or even in onComplete.
I tried to do a simple console.log in the afterLaunch and onComplete so I know it works. The only thing that's confusing me is why it does not send the message in slack AFTER the test is done.
If there is a better way of doing this, please tell me your ideas. Right now, this is the best i've got.
Please see my code:
let SpecReporter = require('jasmine-spec-reporter').SpecReporter;
var webRep = require('jasmine-slack-reporter');
var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');
var SlackWebhook = require('slack-webhook');
var slack = new SlackWebhook('my-webhook-url');
exports.config = {
capabilities: {
'browserName' : 'chrome',
},
seleniumAddress: 'http://localhost:4446/wd/hub',
specs: ['./smoke-test/loginAccountType.js'],
onPrepare: function () {
browser.ignoreSynchronization = true,
slack.send({
text: "Test is starting",
channel: '#test-report'
}),
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: true
}
})),
jasmine.getEnv().addReporter(
new Jasmine2HtmlReporter({
savePath: './reports',
takeScreenshotsOnlyOnFailures: true,
})
);
},
afterLaunch: function () {
slack.send({
text: "Test is Done",
channel: '#test-report'
})
},
jasmineNodeOpts: {
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 100000,
print: function() {},
},
};
It seems like your slack.send() is async?
Try to return promise from your afterLanch/onComplete - in this case protractor will wait for this promise to resolve before killing node.js process.
I never worked with slack-webhook package. But what i see in documentation - .send() is returning promise. Try to return it from your afterLaunch/onComplete:
afterLaunch: function () {
return slack.send({
text: "Test is Done",
channel: '#test-report'
})
},
In addition to #Xotabu4 his answer.
According to the docs the send is async, so do a return;
But also catch the error, just to check what fails, see below.
return slack.send('some text')
.then(function(res) {
// succesful request
})
.catch(function(err) {
// handle request error
})
Hope it helps

RTCPeerConnection.createAnswer callback returns undefined object in mozilla for WebRTC chat

Following is my code to answer the incoming call:
var pc = connection.pc;
pc.setRemoteDescription(sdp,function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer,function() {
// code for sending the answer
})
})
})
The above code works fine for chrome, but when i run the same in mozilla, the answer obtained from pc.createAnswer callback is undefined. As a result of which it gives me following error:
TypeError: Argument 1 of RTCPeerConnection.setLocalDescription is not
an object.
The problem is you're not checking errors, specifically: not passing in the required error callbacks.
setRemoteDescription and setRemoteDescription require either three arguments (legacy callback style) or one (promises), but you're passing in two. Same for createAnswer minus one.
The browser's JS bindings end up picking the wrong overload, returning you a promise which you're not checking either, effectively swallowing errors.
Either add the necessary error callbacks:
var pc = connection.pc;
pc.setRemoteDescription(sdp, function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer, function() {
// code for sending the answer
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
Or use the modern promise API:
var pc = connection.pc;
pc.setRemoteDescription(sdp)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
// code for sending the answer
})
.catch(e => console.error(e));
The promise API is available natively in Firefox, or through adapter.js in Chrome. See fiddle.
And always check for errors. ;)

HandleInvalidRoute not working with Durandal

Here's my shell.js vm:
var vm = {
router: router,
auth: auth,
viewAttached: function () {
},
activate: function () {
router.useConvention();
router.handleInvalidRoute = function (route, params) {
debugger;
toastr.info('No Route Found: ' + route);
};
router.map([
{ url: 'error', moduleId: 'viewmodels/error', name: 'Error', visible: false }
]);
router.mapAuto();
if (auth.isAuthenticated)
//return router.activate('folder/2');
return router.activate('home');
else {
return router.activate('home');
}
}
};
return vm;
});
When I navigate to an invalid route (/folders, for example), the debugger in my handleInvalidRoute block isn't hit, and I get a scripterror from require.js:
GET http://appname.com/App/viewmodels/folders.js 404 (Not Found)
require.js:33 Uncaught Error: Script error
http://requirejs.org/docs/errors.html#scripterror require.js:8 J
require.js:8 j.onScriptError
That's all I have to work with. Any idea what's going on?
This has been answered by #EisenbergEffect in the Durandal newsgroup https://groups.google.com/forum/#!topic/durandaljs/eZrIcgn3aU8.
It is because you called mapAuto which always attempts to map urls to
modules, whether or not they actually exist. Effectively,
handleInvalidRoute will never be called.

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.