React-native socket.io emit / on actions not firing - react-native

I am working with a react-native application that involves socket.io. I have installed socket.ion successfully and imported it in my component like:
import React, { Component } from 'react';
import io from 'socket.io-client/dist/socket.io';
const connectionConfig = {
jsonp: false,
reconnection: true,
reconnectionDelay: 100,
reconnectionAttempts: 100000/// you need to explicitly tell it to use websockets
};
socket = io('http://192.168.1.100:3001', connectionConfig);
type Props = {};
export default class Socket extends Component<Props> {
constructor(){
super();
socket.emit("Button",'pressed')
socket.on('userid',(id)=>{
this.setState({userid:{id}});
Alert.alert(id);
})
}
And the code for my server side using express is:
io.on('connection', function(socket){
console.log(socket.id);
socket.on('Button',function(data){
console.log("ButtonPressed");
});
socket.emit('userid',socket.id);
})
What is strange, after like every 1.5s the server console logs a different socket.id when i ran the application on an android device. I assume the socket.io connects successfully but again disconnects in the 1.5s i mentioned such that the socket.emit and socket.on event don't get to be executed.I have tried many options provided but cannot get the right way to fix this. Please if you know a work-around i highly appreciate. Thank you.

I realised on android if you use the option transports: ['websocket'] and you are in the development mode, then first enable the debug remotely by shaking your phone and sockets will work fine for you. So basiclly you should have something like
import io from 'socket.io-client';
const connectionConfig = {
jsonp: false,
reconnection: true,
reconnectionDelay: 100,
reconnectionAttempts: 5000,
transports: ['websocket']/// you need to explicitly tell it to use websockets
};
socket = io('http://192.168.1.100:3001', connectionConfig);

Related

How do I mock server-side API calls in a Nextjs app?

I'm trying to figure out how to mock calls to the auth0 authentication backend when testing a next js app with React Testing Library. I'm using auth0/nextjs-auth0 to handle authentication. My intention is to use MSW to provide mocks for all API calls.
I followed this example in the nextjs docs next.js/examples/with-msw to set up mocks for both client and server API calls. All API calls generated by the auth0/nextjs-auth0 package ( /api/auth/login , /api/auth/callback , /api/auth/logout and /api/auth/me) received mock responses.
A mock response for /api/auth/me is shown below
import { rest } from 'msw';
export const handlers = [
// /api/auth/me
rest.get(/.*\/api\/auth\/me$/, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
user: { name: 'test', email: 'email#domain.com' },
}),
);
}),
];
The example setup works fine when I run the app in my browser. But when I run my test the mocks are not getting picked up.
An example test block looks like this
import React from 'react';
import {render , screen } from '#testing-library/react';
import Home from 'pages/index';
import App from 'pages/_app';
describe('Home', () => {
it('should render the loading screen', async () => {
render(<App Component={Home} />);
const loader = screen.getByTestId('loading-screen');
expect(loader).toBeInTheDocument();
});
});
I render the page inside the App component like this <App Component={Home} /> so that I will have access to the various contexts wrapping the pages.
I have spent about 2 days on this trying out various configurations and I still don't know what I might be doing wrong. Any and every help is appreciated.
This is probably resolved already for the author, but since I ran into the same issue and could not find useful documentation, this is how I solved it for end to end tests:
Overriding/configuring the API host.
The plan is to have the test runner start next.js as custom server and then having it respond to both the next.js, as API routes.
A requirements for this to work is to be able to specify the backend (host) the API is calling (via environment variables). Howerver, access to environment variables in Next.js is limited, I made this work using the publicRuntimeConfig setting in next.config.mjs. Within that file you can use runtime environment variables which then bind to the publicRuntimeConfig section of the configuration object.
/** #type {import('next').NextConfig} */
const nextConfig = {
(...)
publicRuntimeConfig: {
API_BASE_URL: process.env.API_BASE_URL,
API_BASE_PATH: process.env.API_BASE_PATH,
},
(...)
};
export default nextConfig;
Everywhere I reference the API, I use the publicRuntimeConfig to obtain these values, which gives me control over what exactly the (backend) is calling.
Allowing to control the hostname of the API at runtime allows me to change it to the local machines host and then intercept, and respond to the call with a fixture.
Configuring Playwright as the test runner.
My e2e test stack is based on Playwright, which has a playwright.config.ts file:
import type { PlaywrightTestConfig } from '#playwright/test';
const config: PlaywrightTestConfig = {
globalSetup: './playwright.setup.js',
testMatch: /.*\.e2e\.ts/,
};
export default config;
This calls another file playwright.setup.js which configures the actual tests and backend API mocks:
import {createServer} from 'http';
import {parse} from 'url';
import next from 'next';
import EndpointFixture from "./fixtures/endpoint.json";
// Config
const dev = process.env.NODE_ENV !== 'production';
const baseUrl = process?.env?.API_BASE_URL || 'localhost:3000';
// Context
const hostname = String(baseUrl.split(/:(?=\d)/)[0]).replace(/.+:\/\//, '');
const port = baseUrl.split(/:(?=\d)/)[1];
const app = next({dev, hostname, port});
const handle = app.getRequestHandler();
// Setup
export default async function playwrightSetup() {
const server = await createServer(async (request, response) => {
// Mock for a specific endpoint, responds with a fixture.
if(request.url.includes(`path/to/api/endpoint/${EndpointFixture[0].slug}`)) {
response.write(JSON.stringify(EndpointFixture[0]));
response.end();
return;
}
// Fallback for pai, notifies about missing mock.
else if(request.url.includes('path/to/api/')) {
console.log('(Backend) mock not implementeded', request.url);
return;
}
// Regular Next.js behaviour.
const parsedUrl = parse(request.url, true);
await handle(request, response, parsedUrl);
});
// Start listening on the configured port.
server.listen(port, (error) => {
console.error(error);
});
// Inject the hostname and port into the applications publicRuntimeConfig.
process.env.API_BASE_URL = `http://${hostname}:${port}`;
await app.prepare();
}
Using this kind of setup, the test runner should start a server which responds to both the routes defined by/in Next.js as well as the routes intentionally mocked (for the backend) allowing you to specify a fixture to respond with.
Final notes
Using the publicRuntimeConfig in combination with a custom Next.js servers allows you to have a relatively large amount of control about the calls that are being made on de backend, however, it does not necessarily intercept calls from the frontend, the existing frontend mocks might stil be necessary.

Vue js socket.io-client on connection method not executing

I am creating a chat application using socket.io along with vue js. My connection code looks like this:
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
import SocketIO from "socket.io-client"
const io=SocketIO('https://example.com:8443');
Vue.use(new VueSocketIO({
debug: true,
connection: io
}))
There is no error in connection, which means the connection is done I think. And then I added on connection evening like this:
io.on('connection', socket => {
console.log("hai");
})
But this function is not calling when connection done. So to check I changed the port number, then the connection fails, so connection is done properly I think.
By referring to some other code, I just added like
sockets: {
connect: function() {
console.log('socket connected')
}
}
also.But didn't work.I am new to vue.How can i solve this.Thanks in advance
My console showing the follows
Vue-Socket.io: Received socket.io-client instance
vue-socketio.js?5132:10 Vue-Socket.io: Vuex adapter enabled
vue-socketio.js?5132:10 Vue-Socket.io: Vuex socket mutations disabled
vue-socketio.js?5132:10 Vue-Socket.io: Vuex socket actions enabled
vue-socketio.js?5132:10 Vue-Socket.io: Vue-Socket.io plugin enabled

Close redis connection when using NestJS Queues

I'm trying to setup E2E tests in a NestJS project, however, jest output looks like this:
Jest did not exit one second after the test run has completed
After a lot of reading this is because there are some resources, not yet liberated, after some debugging it turns there's an open connection to redis created by ioredis which is used by bull which is used by NestJS to do task queue processing. The thing is that I don't have a reference to the connection in the test code, so how can I close it? I'm tearing down the Nest application in the afterAll jest's hook like this:
afterAll(async () => {
await app.close();
});
but it does nothing, the connection is still there and the jest error message persists. I know I can just add the --forceExit to the jest command but that is not solving anything, it's just hiding the problem under the rug.
This took me awhile to figure out. You need to close the module in the afterAll hook. I was able to find this from looking at the tests in the nestJS Bull repo.
describe('RedisTest', () => {
let module: TestingModule;
beforeAll(async () => {
module = Test.createTestingModule({
imports: [
BullModule.registerQueueAsync({
name: 'test2',
}),
],
});
});
afterAll(async () => {
await module.close();
});
});
https://github.com/nestjs/bull/blob/master/e2e/module.e2e-spec.ts
After struggling almost to getting depressed i found a solution that worked for me.
I'm using "#nestjs/bull": "^0.3.1", "bull": "^3.21.1".
because the queue from bull package uses redis, it keeps the connection open although the module & app are closed.
await moduleRef.close();
await app.close();
i realized that when using --detectOpenHandles while relying on leaked-handles library for more information, you will see something like this in the console:
tcp stream {
fd: 20,
readable: true,
writable: false,
address: {},
serverAddr: null
}
tcp handle leaked at one of:
at /media/user/somePartitionName/Workspace/Nest/project-
name/node_modules/ioredis/built/connectors/StandaloneConnector.js:58:45
tcp stream {
fd: 22,
readable: true,
writable: true,
address: { address: '127.0.0.1', family: 'IPv4', port: 34876 },
serverAddr: null
}
Solution
using beforEach() & afterEach()
in beforEach(), add this instruction to get an instance of your queue :
queue = moduleRef.get(getQueueToken("queuename"));
in afterEach(), close the queue like so: (also close your app & module for better practice)
await queue.close();
Note
using beforAll() & afterAll() doesn't work and the same problem occurs, at least from what i have tried, both beforEach() & afterEach() work combined.
You don't have to add queue.close() to every test, just close queues in their own service/provider using OnModuleDestroy Hook:
#Injectable()
export class ServicesConsumer implements OnModuleDestroy {
constructor(
#InjectQueue('services')
private readonly servicesQueue: Queue,
) { }
async onModuleDestroy() {
await this.servicesQueue.close();
}

WebSockets from Vue component to flask - can't connect to server

I am making my first steps with websockets in my application.
My frontend is using vue.js while my backend uses flask.
In my component I wrote this.
created() {
console.log('Starting connection to WebSocket Server');
// this.connection = new WebSocket('wss://echo.websocket.org');
this.connection = new WebSocket('wss://192.168.0.22:5000');
this.connection.onmessage = function (event) {
console.log(event);
};
this.connection.onopen = function (event) {
console.log(event);
console.log('Successfully connected to the echo websocket server...');
};
},
In my flask app.py besides other stuff I have this
app = Flask(__name__)
socketio = SocketIO(app)
CORS(app)
"""Socket.IO decorator to create a websocket event handler"""
#socketio.on('my event')
def handle_my_custom_event(json, methods=['GET', 'POST']):
print('received my event: ' + str(json))
socketio.emit('my response', json, callback=messageReceived)
def messageReceived(methods=['GET', 'POST']):
print('message was received!!!')
if __name__ == "__main__":
socketio.run(app, debug=True)
In my browser I get the error that firefox could not make a connection to wss://192.168.0.22:5050. I already tried the frontend with the websocket from a tutorial which is commented out now.
I am not sure, which url I should use for my own backend or what I have to add there.
Sorry if this is obvious but I am a complete beginnern.
Thanks in advance!
EDIT:
In chrome the error I receive is "WebSocket connection to 'wss://192.168.0.38:5000/' failed: WebSocket opening handshake timed out"
Also as I saw this error when trying out stuff, maybe this question could be relevant? vue socket.io connection attempt returning "No 'Access-Control-Allow-Origin' header is present" error even when origins have been set
so the part for the socket which i ended up using for the client/component:
import io from 'socket.io-client';
created() {
// test websocket connection
const socket = io.connect('http://192.168.0.38:5000');
// getting data from server
// eslint-disable-next-line
socket.on('connect', function () {
console.error('connected to webSocket');
//sending to server
socket.emit('my event', { data: 'I\'m connected!' });
});
// we have to use the arrow function to bind this in the function
// so that we can access Vue & its methods
socket.on('update_on_layouts', (data) => {
this.getAllLayouts();
console.log(data);
});
},
The Flask server code stayed as shown above. Additionally here is an example from my flask server to emit the update_on_layouts socketio.emit('update_on_layouts', 'success')

Dynamic address for socket.io-client

Intro
Most of you will probably ask "Why?", why am I doing this stack? The reason is that initially I created the project in Nuxtjs + expressjs. But my PM wanted me to not distribute my source code to our client so I decided to bundle my code up into a single .exe file with electron. I tried using pkg but I couldn't figure out how to compile everything exactly.
The problem
The problem I am having with socket.io-client is that I want to be able to move the exe file to a different machine, and have socket.io connect to the socket.io server on that machine dynamically. Changing machines would mean that the IP of the server will be different, so whenever the user opens the webpage for that server, the socket.io-client would connect to the proper server. It works when I build the app from my current machine but when moved to lets say a VM then this is the response I get when I access the page:
ServiceUnavailableError: Response timeout
at IncomingMessage.<anonymous> (C:\Users\LIANG-~1\AppData\Local\Temp\nscAD47.tmp\app\resources\app.asar\node_modules\connect-timeout\index.js:84:8)
at IncomingMessage.emit (events.js:182:13)
at IncomingMessage.EventEmitter.emit (domain.js:442:20)
at Timeout._onTimeout (C:\Users\LIANG-~1\AppData\Local\Temp\nscAD47.tmp\app\resources\app.asar\node_modules\connect-timeout\index.js:49:11)
at ontimeout (timers.js:425:11)
at tryOnTimeout (timers.js:289:5)
at listOnTimeout (timers.js:252:5)
at Timer.processTimers (timers.js:212:10)
To further elaborate on what I am trying to do, lets say I compile the code on my current machine with the private IP of 192.168.0.104 (this runs perfectly), I want to move the exe file to another machine with the private IP of 192.168.0.105 (Accessing the webpage from this server gives the above error).
Technology used
The technology that I am using is nuxt.js created with express template, socket.io, and vue-socket.io-extended.
What I have tried
I have tried checking for reconnect events or timeout events, when these events are triggered then I call socket.connect(process.env.WS_URL) which doesn't work. I believe that when I packaged the electron app, it makes the plugin data immutable. I couldn't figure out someway to change the URL to the address of the machine.
import Vue from 'vue'
import io from 'socket.io-client'
import VueSocketIO from 'vue-socket.io-extended'
export default ({ store }) => {
// process.env.WS_URL = 'http://192.168.0.12:3000'
const socket = io(process.env.WS_URL, { transports: 'websocket' })
socket.on('timeout', () => {
socket.connect(process.env.WS_URL)
})
Vue.use(VueSocketIO, io(process.env.WS_URL, { transports: 'websocket' }), { store })
}
What I have right now
I created a socket.io plugin for nuxtjs to implement into my app, the plugin looks like this:
import Vue from 'vue'
import io from 'socket.io-client'
import VueSocketIO from 'vue-socket.io-extended'
export default ({ store }) => {
// process.env.WS_URL = 'http://192.168.0.12:3000'
Vue.use(VueSocketIO, io(process.env.WS_URL, { transports: 'websocket' }), { store })
}
I expect the socket.io-client to connect to the correct private IP of where the exe file is located. But Request Timeout is received, even though when I output the process.env.WS_URL is actually the new address.
EDIT: After further testing, seems like the socket.io plugin is fixed, after the build process. So changing the process.env.WS_URL wouldn't have any effect. Is there a way to change the URL for socket.io even after nuxtjs finished building?
(I wanted to comment this but I can't)
Anyway, are you sure you the information in the edit is true?
I use Nuxt.js with Electron and also the vue-socket.io. I use an argument for calling the executable as the socket.io address:
This is specific for electron
global.sharedObject = { socketIOAddress: process.argv[1] }
and this is my nuxt plugin file for vue-socket.io:
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
import { remote } from 'electron'
export default ({ store }) => {
Vue.use(new VueSocketIO({
debug: true,
connection: remote.getGlobal('sharedObject').socketIOAddress
vuex: {
store,
actionPrefix: 'SOCKET_',
mutationPrefix: 'SOCKET_'
}
})
)
}
So while the code might not be exactly what you want Nuxt doesn't have to be build again for it. Maybe the environment variables didn't change right when you tried? You should try with a argument as well perhaps.
Also important: ssr: false in the nuxt config for the vue-socket.io plugin.
The solution I came up with is by using an environmental variable. First have the variable set as localhost:3000 or whatever your server's local address is. I would use electron-store to keep a config.json file of all my settings including the server IP. Before my server starts up, I read the config file and change the server address set previously. Then I use it as follows:
import Vue from 'vue'
import io from 'socket.io-client'
import VueSocketIO from 'vue-socket.io-extended'
export default ({ app, store }) => {
Vue.use(VueSocketIO, io(app.$env.WS_URL), { store })
}
NOTE: WS_URL is the environmental variable name.