I want to send errors in the websocket after successful connection inside tests.
I have a file which will start a websocket server and inside another method there is another websocket that will be connected to another websocket server.
client -> Main file server -> Another server
Client is the test which will connect to the main file's websocket server. I created another websocket server inside beforeEach block of the test with predefined port that will be used from main file to connect to it.
My test file looks like -
describe("some test", async () =>{
beforeEach(async () => {
await new Promise(resolve => {
anotherServer = new WebSocketServer({port:8000, path: '/ws'});
anotherServer.on('connection', function connection(ws) {
ws.on('message', async(data) => {
ws.send(data);
});
ws.on('error',(err) => {
ws.close();
})
ws.on('close',async() =>{
ws.close();
})
ws.send("Connected");
});
resolve(true);
})
})
it("handle error in another server socket",async ()=> {
await main.start(3000);
wb = new Websocket(uri,options);
await new Promise(resolve => { wb.onopen = resolve});
// here I want to send error event in the another server's socket and assert on the logs which are present in the main code.
})
});
My main file has all the event listners for error event and close events. I want to test both of them by sending error and close event in the socket from another server.
Related
I am trying to integrate socket.io with strapi. But unfortunately I have been unable to do so without any proper tutorial or documentation covering this aspect.
I followed along with the only resource I found online which is:
https://medium.com/strapi/strapi-socket-io-a9c856e915a6
But I think the article is outdated. I can't seem to run the code mentioned in it without running into tonnes of errors.
Below is my attempt to implement it and I have been trying to connect it through a chrome websocket plugin smart websocket client But I am not getting any response when I try to run the server.
I'm totally in the dark. Any help will be appreciated
module.exports = ()=> {
// import socket io
var io = require('socket.io')(strapi.server)
console.log(strapi.server) //undefined
// listen for user connection
io.on('connect', socket => {
socket.send('Hello!');
console.log("idit")
// or with emit() and custom event names
socket.emit('greetings', 'Hey!', { 'ms': 'jane' }, Buffer.from([4, 3, 3, 1]));
// handle the event sent with socket.send()
socket.on('message', (data) => {
console.log(data);
});
// handle the event sent with socket.emit()
socket.on('salutations', (elem1, elem2, elem3) => {
console.log(elem1, elem2, elem3);
});
});
};
So I found the solution. Yay. I'll put it here just in case anybody needs it.
boostrap.js
module.exports = async () => {
process.nextTick(() =>{
var io = require('socket.io')(strapi.server);
io.on('connection', async function(socket) {
console.log(`a user connected`)
// send message on user connection
socket.emit('hello', JSON.stringify({message: await strapi.services.profile.update({"posted_by"})}));
// listen for user diconnect
socket.on('disconnect', () =>{
console.log('a user disconnected')
});
});
strapi.io = io; // register socket io inside strapi main object to use it globally anywhere
})
};
Found this at: https://github.com/strapi/strapi/issues/5869#issuecomment-619508153_
Apparently, socket.server is not available when the server starts. So you have to make use of process.nextTick that waits for the socket.server to initialize.
I'll also add a few questions that I faced when setting this up.
How do i connect from an external client like nuxt,vue or react?
You just have to connect through "http://localhost:1337" that is my usual address for strapi.
I am using nuxt as my client side and this is how set up my socketio on the client side
I first installed nuxt-socket-io through npm
Edited the nuxt.config file as per it's documention
modules:[
...
'nuxt-socket-io',
...
],
io: {
// module options
sockets: [
{
name: 'main',
url: 'http://localhost:1337',
},
],
},
And then i finally added a listener in one of my pages.
created() {
this.socket = this.$nuxtSocket({})
this.socket.on('hello', (msg, cb) => {
console.log('SOCKET HI')
console.log(msg)
})
},
And it works.
A clean way to integrate third-party services into Strapi is to use hooks. They are loaded once during the server boot. In this case, we will create a local hook.
The following example has worked with strapi#3.6.
Create a hook for socket.io at ./hooks/socket.io/index.js
module.exports = strapi => {
return {
async initialize() {
const ioServer = require('socket.io')(strapi.server, {
cors: {
origin: process.env['FRONT_APP_URL'],
methods: ['GET', 'POST'],
/* ...other cors options */
}
})
ioServer.on('connection', function(socket) {
socket.emit('hello', `Welcome ${socket.id}`)
})
/* HANDLE CLIENT SOCKET LOGIC HERE */
// store the server.io instance to global var to use elsewhere
strapi.services.ioServer = ioServer
},
}
}
Enable the new hook in order for Strapi to load it - ./config/hook.js
module.exports = {
settings: {
'socket.io': {
enabled: true,
},
},
};
That's done. You can access the websocket server inside ./config/functions/bootstrap.js or models' lifecycle hooks.
// ./api/employee/models/employee.js
module.exports = {
lifecycles: {
async afterUpdate(result, params, data) {
strapi.services.ioServer.emit('update:employee', result)
},
},
};
For those who are looking the answer using Strapi version 4
var io = require("socket.io")(strapi.server.httpServer)
I am trying to use mock-sockets with Cypress, setting up the mock in the onBeforeLoad hook for cy.visit() in my beforeEach block. I can get one test to work but when the mock setup runs on the next beforeEach I get an error that A mock server is already listening on this url.
code under test:
(called from my React app's componentDidiMount)
subscribeToSettings(url: string): W3CWebSocket {
let settingsSubscription = new W3CWebSocket(url);
settingsSubscription.onopen = () => console.log('WebSocket Client Connected (settings)');
settingsSubscription.onclose = () => console.log('WebSocket Client Disconnected (settings)');
settingsSubscription.onmessage = (message: MessageEvent) => this.handleSettingsMessage(message);
return settingsSubscription;
}
/**
* Handler for websocket settings messages, which updates the local settings values.
* #param message the websocket message
*/
handleSettingsMessage(message: MessageEvent) {
const updatedValues = JSON.parse(message.data);
console.log('A message was received on the settings channel.', updatedValues);
this.props.updateSettingsFromBackend(updatedValues);
}
cypress tests
import { Server } from 'mock-socket'
import { defaultSettingsState } from "../../src/reducers/settings.reducer";
import { _createSettingsApiPutPayload } from "../../src/actions/settings.actions";
describe('mock socket method 1', () => {
let mockSocket;
let mockServer;
beforeEach(() => {
cy.visit('/', {
onBeforeLoad(win: Window): void {
// #ts-ignore
cy.stub(win, 'WebSocket', url => {
mockServer = new Server(url)
mockServer.on('connection', socket => {
console.log('mock socket connected');
mockSocket = socket;
});
mockSocket = new WebSocket(url);
return mockSocket
});
},
});
});
afterEach(() => {
mockSocket.close()
mockServer.stop()
});
it('gets a message', () => {
cy.contains('SETTINGS').click()
const object = _createSettingsApiPutPayload(defaultSettingsState)
mockSocket.send(JSON.stringify(object));
cy.contains('Motion threshold')
});
it('gets another message', () => {
cy.contains('SETTINGS').click()
const object = _createSettingsApiPutPayload(defaultSettingsState)
mockSocket.send(JSON.stringify(object));
cy.contains('Motion threshold')
});
});
Here are the logs from my console:
WebSocket Client Connected (settings)
mock socket connected at url ws://localhost:8702/PM_Settings
A message was received on the settings channel. {…}
mock socket connected at url ws://localhost:3000/sockjs-node/949/mhuyekl3/websocket
The development server has disconnected.
Refresh the page if necessary.
Uncaught Error: A mock server is already listening on this url
I wonder if it has to do with that second call which is for some mystery url.
(Note: calling cy.contains('SETTINGS').click() at the end of beforeEach somehow doesn't work, even in that first test. Even when I have my app set to start on the settings page (instead of having to click to it from inside the tests), clicking on SETTINGS from beforeEach still doesn't work even though we're already there. So that's kind of weird)
These cypress logs may also be helpful:
It only worked for me, when I moved server stopping into WebSocket stub:
cy.stub(window, 'WebSocket', url => {
if (mockServer) {
mockServer.stop();
}
mockServer = new Server(url);
mockServer.on('connection', socket => {
mockSocket = socket;
});
mockSocket = new WebSocket(url);
return mockSocket;
});
Im probably wrong, but I guess afterEach or mockServer.stop(); is async thats why mock server fails to stop before new init
I am new to web-sockets. I was reading this article on medium and in a part of the client code, the code disconnects the socket every time a new message is emitted.
Why is it so? Does this have some design advantage, is this how it is done?
Why not leave the socket connected for more message streams?
-----EDIT------
Here is the code that I am talking about. The code is from this blog
newMessageReceived() {
const observable = new Observable<{ user: String, message: String}>(observer => {
this.socket.on('new message', (data) => {
observer.next(data);
});
return () => {
// Why is the coder disconnecting the socket here?
this.socket.disconnect();
};
});
return observable;
}
Do we always have to use socket.emit() inside a socket.on() like that:
io.sockets.on('connection', function (socket) {
console.log('User connected !');
retrieveDictionnary((dictionnary) =>{
socket.emit('dictionnarySend', dictionnary);
}
}
I want to create on my client side a function which ask information to the server when I click on a button:
translateServer(parameter, control){
this.socket.emit('translate', [parameter,control]);
}
But it seems that it's not working, the server never receive this message.
Thank you !
The pattern you are using above is the recommended way of interacting with a socket (ie acquiring a socket instance when the 'connection' event fires, and then calling emit() from that socket instance, etc).
If I understand your client-side requirements correctly, you are wanting to send data to the server via web sockets - are you sure the socket that you have established a web socket connection between the client and server?
For instance, if you add the following to your client-side code, you should see a success message in your console:
const socket = io.connect('YOUR SERVER ADDRESS');
socket.on('connect', () => {
console.log('connected to server!');
// [UPDATE]
// This assumes you have a <button> element on your page. When
// clicked, a message will be sent to the server via sockets
document.querySelector('button').addEventListener('click', (event) => {
// Prevent button click reloading page
event.preventDefault();
// Send message to server via socket
socket.emit('MESSAGE_ID', 'test message from client' + new Date());
});
});
Update
This shows your original server code, expanded with the detail needed to receive and print data sent from client via sockets:
io.sockets.on('connection', function (socket) {
console.log('User connected !');
// Register a server handler for any messages from client on MESSAGE_ID channel
socket.on('MESSAGE_ID', (message) => {
// Print the message received from client in console
console.log('message from client', message);
})
retrieveDictionnary((dictionnary) =>{
socket.emit('dictionnarySend', dictionnary);
}
}
I haven't received any event notifications and am wondering if I am missing something.
I followed the instructions from the Fabric Composer website to define the BasicEvent in my cto model and added the code for creating and emitting events in a transaction and updated the network. I created a separate eventListener.js program that subscribes to the events using the businessNetworkConnection using the code sample from the website.
After I start my eventListener.js app it seems to be listening (after receiving the connected status message in console, nothing else happens...it doesn't go back to the normal prompt line.)
I then execute the transaction that should emit the event and it runs successfully but no event is received in the other terminal window where the eventlistener.js is running.
Here is the key part of the eventListener.js program:
businessNetworkConnection.connect(connectionProfile, businessNetworkIdentifier, participantId, participantPwd)
.then((result) => {
businessNetworkDefinition = result;
console.log('Connected: BusinessNetworkDefinition obtained=' + businessNetworkDefinition.getIdentifier());
});
businessNetworkConnection.on('event', (event) => {
// event: { "$class": "org.namespace.BasicEvent", "eventId": "0000-0000-0000-000000#0" }
console.log(event);
});
Is the businessNetworkConnection.on('event', (event) ... command supposed to cause the program to appear to hang while its listening?
If so, is there something else that could be done to troubleshoot where the problem is?
I'm using v0.6 HLF on local Docker.
I think your issue is that you don't wait for the businessNetworkDefinition to be connected before you register your listener. Remember that the then block is executed asynchronously and your following businessNetworkConnection.on code will execute immediately.
You should add a second then block to register your listener after the businessNetworkDefinition has been connected.
e.g.
return adminConnection.connect('hlfv1', 'admin', 'adminpw')
.then(() => {
return BusinessNetworkDefinition.fromDirectory(path.resolve(__dirname, '..'));
})
.then((businessNetworkDefinition) => {
return adminConnection.deploy(businessNetworkDefinition);
// return true;
})
.then(() => {
businessNetworkConnection = new BusinessNetworkConnection();
return businessNetworkConnection.connect('hlfv1', 'my-network', 'admin', 'adminpw');
})
.then(() => {
businessNetworkConnection.on('event', (event) => {
console.log( '****** received the event ' + JSON.stringify(businessNetworkConnection.getBusinessNetwork().getSerializer().toJSON(event)));
});
});
I wrote a sample code for a basic sample application developed by Hyperledger team. You can check out about from this code snippet: SampleEventListener