How to write my code to test a websocket event? - javascript

I am trying to test that data gets added to my database after a websocket event has fired.
In the application I am working with there is already a working example of this.
it('some test name', function (done) {
this.timeout(12000)
var socket = io.connect('http://localhost:3000', {
'transports': [
'websocket',
'flashsocket',
'jsonp-polling',
'xhr-polling',
'htmlfile'
]
})
socket.emit('some-room-event', {
participant: 'p1',
room: 12,
active: true
})
setTimeout(function () {
app.service('rooms').get(12)
.then(function (room) {
assert(room.active === true)
socket.disconnect()
done()
}).catch(function (err) {
socket.disconnect()
done(err)
})
}, 11000)
})
I am coming from a ruby background and slotting into the project so I am quite new. It feels a bit like the use of a timeout is a code smell and it just feels wrong. You don't want to increase the duration it takes to run your tests by an arbitrary wait time.
I have read a lot of articles around this, but it's pretty confusing. Is there a better way to structure this code and potentially get rid of the setTimeout?
I am using feathersjs, mocha and assert.

It looks like the event that is being tested is just a normal websocket event that does something to the rooms service.
An arbitrary timeout in the test is definitely not the best way to solve the issue that the websocket event does not acknowledge when it has completed whatever it was supposed to do. What I think you could do is to listen to a Feathers service event when the room is updated or patched (although it is a little odd that the socket event isn't just using the official socket.emit('rooms::update', data) to update the room state):
it('some test name', function (done) {
var socket = io.connect('http://localhost:3000', {
'transports': [
'websocket',
'flashsocket',
'jsonp-polling',
'xhr-polling',
'htmlfile'
]
});
app.service('rooms').once('updated', function(room) {
assert(room.active);
done();
});
socket.emit('some-room-event', {
participant: 'p1',
room: 12,
active: true
});
});

Related

How do I edit PubNub Message and Add reaction to individual message

I have been working with PubNub, I tried storage and playback to update ( edit ) message, but failed.
Kindly make me understand with code example, how to do it.
Can you provide some examples of your code?
You can find the docs for storage and playback at: https://www.pubnub.com/docs/web-javascript/api-reference-storage-and-playback
1st thing you need to do is create a PubNub Client:
var pubnub = new PubNub({
subscribeKey: "mySubscribeKey",
publishKey: "myPublishKey",
cipherKey: "myCipherKey",
authKey: "myAuthKey",
logVerbosity: true,
uuid: "myUniqueUUID",
ssl: true,
});
Then you can make a history call to a specific channel you want messages for:
pubnub.history(
{
channel: 'channel ID you want history for',
count: 100, // how many items to fetch
stringifiedTimeToken: true, // false is the default
},
function (status, response) {
// handle status, response
}
);
After that you can just load the JSON into your UI
Docs on editing a message: https://www.pubnub.com/docs/web-javascript/message-update-delete
To edit a message
Using the Interleaving pattern, you publish new versions of the same message to the same channel just like normal publishes. The subsequent messages must use the same message_id as the original message...
Let me know if this helps!
Mathew
thanks I found the solution: just using pubnub.addMessageAction() and pubnub.removeMessageAction() like this:
pubnub.addMessageAction(
{
channel: 'channel1'
messageTimetoken: '15610547826970040',
action: {
type: 'reaction',
value: 'smiley_face',
},
},
function(status, response) {
});

RxJS: Observable.webSocket() get access to onopen, onclose…

const ws = Observable.webSocket('ws://…');
ws.subscribe(
message => console.log(message),
error => console.log(error),
() => {},
);
I want to observe my WebSocket connection with RxJS. Reacting to onmessage events by subscribing to the observable works like a charm. But how can I access the onopen event of the WebSocket? And is it possible to trigger the WebSocket .close() method? RxJS is pretty new to me and I did research for hours, but maybe I just don't know the right terms. Thanks in advance.
Looking at the sourcecode of the Websocket there is a config object WebSocketSubjectConfig which contains observables which you can link to different events. You should be able to pass a NextObserver typed object to config value openObserver like so:
const openEvents = new Subject<Event>();
const ws = Observable.webSocket({
url: 'ws://…',
openObserver: openEvents
});
openEvents
.do(evt => console.log('got open event: ' + evt))
.merge(ws.do(msg => console.log('got message: ' + msg))
.subscribe();
The link of #Mark van Straten to the file is dead. The updated link is here. I also wanted to highlight the usage as suggested in the docs. A in my opinion better copy and paste solution to play around with:
import { webSocket } from "rxjs/webSocket";
webSocket({
url: "wss://echo.websocket.org",
openObserver: {
next: () => {
console.log("connection ok");
},
},
closeObserver: {
next(closeEvent) {
// ...
}
},
}).subscribe();

How to Observe a Custom Event using RXJS in Angular 2?

I have a third party library that I am intending to integrate with RxJS. This is a messaging library called Tiger Text. According to them I can listen to an event called messages and when the stream has a message I can use it to further utilize it. The code snippet for the same is as follows:-
var client = new TigerConnect.Client({ defaultOrganizationId: 'some-org-id' })
client.signIn('user#mail.com', 's3cr3t', { udid: 'unique-device-id' }).then(function (session) {
onSignedIn(session)
})
function onSignedIn(session) {
console.log('Signed in as', session.user.displayName)
client.messages.sendToUser(
'someone#mail.com',
'hello!'
).then(function (message) {
console.log('sent', message.body, 'to', message.recipient.displayName)
})
client.events.connect()
client.on('message', function (message) {
console.log(
'message event',
message.sender.displayName,
'to',
message.recipient.displayName,
':',
message.body
)
})
}
Now please have a look at the place where you have the below mentioned piece of code.
client.on('message', function (message) {
console.log(
'message event',
message.sender.displayName,
'to',
message.recipient.displayName,
':',
message.body
)
})
I wanted to know how to use RxJS so as to create an observable out of this piece of code so as to subscribe to the stream and whenever we have a change I take the new data and process it as I wish.
Please Advice.
For this use-cases you typically don't need to write a custom Observable and you can use just Observable.create(). Then it depends on whether you want to write a cold or a hot observable.
For cold Observables you create the producer of values when subscribing and close it when unsubscribing:
Observable.create(obs => {
var client = new TigerConnect.Client({ defaultOrganizationId: 'some-org-id' });
client.signIn('user#mail.com', 's3cr3t', { udid: 'unique-device-id' }).then(function (session) {
onSignedIn(session);
});
client.on('message', function (message) {
obs.next(...);
});
return () => {
client.close(); // or whatever...
};
});
Or if you want to write a hot Observable the producer will exist independently on any subscriptions and just add/remove the listener:
var client = new TigerConnect.Client({ defaultOrganizationId: 'some-org-id' });
client.signIn('user#mail.com', 's3cr3t', { udid: 'unique-device-id' }).then(function (session) {
onSignedIn(session);
});
Observable.create(obs => {
let listener = client.on('message', function (message) {
obs.next(...);
});
() => {
// remove the event listener somehow
listener.remove();
};
});
Sometimes you can see this solved by using a Subject but this is usually more complicated than using Observable.create() because then you need to handle the creation and tear down logic yourself and also Subjects have internal state.
Here's a very similar question as yours:
Subscribe to a stream with RxJS and twitter-stream-api module
Articles on the topics related to your question by the lead developer of RxJS:
https://medium.com/#benlesh/hot-vs-cold-observables-f8094ed53339
https://medium.com/#benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
https://medium.com/#benlesh/rxjs-dont-unsubscribe-6753ed4fda87
https://medium.com/#benlesh/learning-observable-by-building-observable-d5da57405d87
You can use fromEventPattern to create an observable from a custom event:
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEventPattern';
const messages = Observable.fromEventPattern(
handler => client.on('message', handler),
handler => client.off('message', handler)
);
messages.subscribe(message => console.log(message));
You pass to fromEventPattern functions that add and remove the event handler using the custom API's add and remove mechanism. You've not included it in your question, but I've assumed the API you're using implements an off method.

How to use Sinon.js FakeXMLHttpRequest with superagent?

I am trying to test one of the React Flux actions that makes a request to the server.
// AppActions.js
fetchMovies(date) {
this.dispatch(date);
request
.get('/api/movies')
.query(date)
.end((err, res) => {
if (!res.ok) {
this.actions.fetchMoviesFail(res.body);
} else {
this.actions.fetchMoviesSuccess(res.body);
}
});
}
In my Flux store tests I have something like the following:
// AppStore-test.js
it ('should successfully handle fetchMovies', () => {
var callback = sinon.spy();
var date = {
startDate: moment('2015-04-01').format('YYYY-MM-DD'),
endDate: moment('2015-04-15').format('YYYY-MM-DD')
};
AppActions.fetchMovies(date, callback);
requests[0].respond(200, { 'Content-Type': 'application/json' },
'[{ "id": 12, "comment": "Hey there" }]');
expect(callback.calledWith([{id: 12, comment: "Hey there"}])).to.be.ok;
});
This obviously doesn't work because fetchMovies only takes one argument - date. It's my first time using sinon.js, so perhaps I am missing something really obvious?
How do I fake this asynchronous request and make it either succeed or fail, because right now no matter what I do, it never resolves the .end() promise.
You should use nock for this.
Nock intercepts all HTTP requests going from your code and then respons according to how you've set it up.
This way you can keep your mocks and spys down to a minimum and treat request as just another implementation detail.
The nock scope you create responds to certain assertions, so you can expect that it was done, faulty or similar.
An example;
var nockScope = nock('http://localhost:8000').get('/api/movies').reply(200, 'OK')
expect(nockScope.isDone()).to.be.ok()

jasmine: Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL

I have an angular service called requestNotificationChannel:
app.factory("requestNotificationChannel", function($rootScope) {
var _DELETE_MESSAGE_ = "_DELETE_MESSAGE_";
function deleteMessage(id, index) {
$rootScope.$broadcast(_DELETE_MESSAGE_, { id: id, index: index });
};
return {
deleteMessage: deleteMessage
};
});
I am trying to unit test this service using jasmine:
"use strict";
describe("Request Notification Channel", function() {
var requestNotificationChannel, rootScope, scope;
beforeEach(function(_requestNotificationChannel_) {
module("messageAppModule");
inject(function($injector, _requestNotificationChannel_) {
rootScope = $injector.get("$rootScope");
scope = rootScope.$new();
requestNotificationChannel = _requestNotificationChannel_;
})
spyOn(rootScope, '$broadcast');
});
it("should broadcast delete message notification", function(done) {
requestNotificationChannel.deleteMessage(1, 4);
expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
done();
});
});
I read about the Asynchronous Support in Jasmine, but as I am rather new to unit testing with javascript couldn't make it work.
I am receiving an error :
Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL
and my test is taking too long to execute (about 5s).
Can somebody help me providing working example of my code with some explanation?
Having an argument in your it function (done in the code below) will cause Jasmine to attempt an async call.
//this block signature will trigger async behavior.
it("should work", function(done){
//...
});
//this block signature will run synchronously
it("should work", function(){
//...
});
It doesn't make a difference what the done argument is named, its existence is all that matters. I ran into this issue from too much copy/pasta.
The Jasmine Asynchronous Support docs note that argument (named done above) is a callback that can be called to let Jasmine know when an asynchronous function is complete. If you never call it, Jasmine will never know your test is done and will eventually timeout.
Even for async tests, there is a timeout that goes off in this cases, You can work around this error by increasing the value for the limit timeout to evaluate an async Jasmine callback
describe('Helper', function () {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000000;
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
it('Template advance', function(doneFn) {
$.ajax({
url: 'public/your-end-point.mock.json',
dataType: 'json',
success: function (data, response) {
// Here your expected using data
expect(1).toBe(1)
doneFn();
},
error: function (data, response) {
// Here your expected using data
expect(1).toBe(1)
doneFn();
}
});
});
});
Source: http://jasmine.github.io/2.0/introduction.html#section-42
This error can also be caused by leaving out inject when initializing a service/factory or whatever. For example, it can be thrown by doing this:
var service;
beforeEach(function(_TestService_) {
service = _TestService_;
});
To fix it just wrap the function with inject to properly retrieve the service:
var service;
beforeEach(inject(function(_TestService_) {
service = _TestService_;
}));
import { fakeAsync, ComponentFixture, TestBed } from '#angular/core/testing';
use fakeAsync
beforeEach(fakeAsync (() => {
//your code
}));
describe('Intilalize', () => {
it('should have a defined component', fakeAsync(() => {
createComponent();
expect(_AddComponent.ngOnInit).toBeDefined();
}));
});
You can use karma-jasmine plugin to set the default time out interval globally.
Add this config in karma.conf.js
module.exports = function(config) {
config.set({
client: {
jasmine: {
timeoutInterval: 10000
}
}
})
}
This error started out of the blue for me, on a test that had always worked. I couldn't find any suggestions that helped until I noticed my Macbook was running sluggishly. I noticed the CPU was pegged by another process, which I killed. The Jasmine async error disappeared and my tests are fine once again.
Don't ask me why, I don't know. But in my circumstance it seemed to be a lack of system resources at fault.
This is more of an observation than an answer, but it may help others who were as frustrated as I was.
I kept getting this error from two tests in my suite. I thought I had simply broken the tests with the refactoring I was doing, so after backing out changes didn't work, I reverted to earlier code, twice (two revisions back) thinking it'd get rid of the error. Doing so changed nothing. I chased my tail all day yesterday, and part of this morning without resolving the issue.
I got frustrated and checked out the code onto a laptop this morning. Ran the entire test suite (about 180 tests), no errors. So the errors were never in the code or tests. Went back to my dev box and rebooted it to clear anything in memory that might have been causing the issue. No change, same errors on the same two tests. So I deleted the directory from my machine, and checked it back out. Voila! No errors.
No idea what caused it, or how to fix it, but deleting the working directory and checking it back out fixed whatever it was.
Hope this helps someone.
You also get this error when expecting something in the beforeAll function!
describe('...', function () {
beforeAll(function () {
...
expect(element(by.css('[id="title"]')).isDisplayed()).toBe(true);
});
it('should successfully ...', function () {
}
}
Don't use done, just leave the function call empty.
It looks like the test is waiting for some callback that never comes. It's likely because the test is not executed with asynchronous behavior.
First, see if just using fakeAsync in your "it" scenario:
it('should do something', fakeAsync(() => {
You can also use flush() to wait for the microTask queue to finish or tick() to wait a specified amount of time.
In my case, this error was caused by improper use of "fixture.detectChanges()" It seems this method is an event listener (async) which will only respond a callback when changes are detected. If no changes are detected it will not invoke the callback, resulting in a timeout error. Hope this helps :)
Works after removing the scope reference and the function arguments:
"use strict";
describe("Request Notification Channel", function() {
var requestNotificationChannel, rootScope;
beforeEach(function() {
module("messageAppModule");
inject(function($injector, _requestNotificationChannel_) {
rootScope = $injector.get("$rootScope");
requestNotificationChannel = _requestNotificationChannel_;
})
spyOn(rootScope, "$broadcast");
});
it("should broadcast delete message notification with provided params", function() {
requestNotificationChannel.deleteMessage(1, 4);
expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4} );
});
});
What I did was: Added/Updated the following code:
framework: 'jasmine',
jasmineNodeOpts:
{
// Jasmine default timeout
defaultTimeoutInterval: 60000,
expectationResultHandler(passed, assertion)
{
// do something
},
}
As noted by #mastablasta, but also to add that if you call the 'done' argument or rather name it completed you just call the callback completed() in your test when it's done.
// this block signature will trigger async behavior.
it("should work", function(done){
// do stuff and then call done...
done();
});
// this block signature will run synchronously
it("should work", function(){
//...
});
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
Keeping this in the block solved my issue.
it('', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Instead of
beforeEach(() => {..
use
beforeEach(fakeAsync(() => {..
In my case, a timeout was cause because of a failed injection of a service with providedIn: 'root'. It's not clear why injection failed, nor why there was no early error if there is apparently no instance of provider available.
I was able to work around it by manually providing a value:
TestBed.configureTestingModule({
declarations: [
// ...
],
imports: [
// ...
],
providers: [
// ...
{ provide: MyService, useValue: { /* ... */ } },
]
}).compileComponents();
I have caught the same error because I used the setTimeout function in the component. Example:
ngOnInit(): void {
this.changeState();
}
private changeState(): void {
setTimeout(() => this.state = StateEnum.IN_PROGRESS, 10000);
}
When I changed the timeout from 10000ms to 0 or less than 5000ms (DEFAULT_TIMEOUT_INTERVAL), all tests were passed.
In my case, I was not returning the value from the spy method, hence facing error,
mainMethod(args): Observable<something>{
return nestedMethod().pipe();
}
Your Test should like below,
it('your test case', (done: DoneFn) => {
const testData = {}; // Your data
spyOn(service, 'nestedMethod').and.returnValue(of(testData));
const obxValue = service.mainMethod('your args');
obxValue.pipe(first()).subscribe((data) => {
expect(data).not.toBeUndefined();
done();
});
});
If you have an argument (done) in the it function try to remove it as well it's call within the function itself:
it("should broadcast delete message notification", function(/*done -> YOU SHOULD REMOVE IT */) {
requestNotificationChannel.deleteMessage(1, 4);
expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
// done(); -> YOU SHOULD REMOVE IT
});

Categories

Resources