I am trying to build up a WebSocket with oak (not the native deno one).
The following code is how I build the server.
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onclose = () => { console.log('Disconnected from the client!');};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket) => {
console.log('new websocket, ws: ',ws);
const uid = globalThis.crypto.randomUUID();
sockets.set(uid,ws);
console.log('socket: ',sockets);
for await (const ev of ws){
console.log('ev: ', ev);
}
}
export const wsRoutes = new Router()
.get('/ws', runWS);
But in the for loop (at the end), for ws it says Type 'WebSocket' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.. What's the deal with this and how to fix it?
The error message is providing you with useful information: the WebSocket is not AsyncIterable, which means that it cannot be used with a for await...of loop.
Here is the type documentation for WebSocket in Deno. It is (for the most part) the same as the WHATWG standard WebSocket that is documented on MDN.
If your intention is to respond to incoming message events, you'll need to attach an event listener:
webSocket.addEventListener("message", (messageEvent) => {
// Do something in response to each message event
});
Additional:
Here's an observation based on the code you've shown, but not in response to your question:
It's probably more ergonomic to store the sockets as the keys of your map, and the associated state data in the values. (This is the inverse of what you've shown). Here's an example of why:
import {
Router,
type RouterMiddleware,
} from "https://deno.land/x/oak#v10.6.0/mod.ts";
// You seem to want to log data to the console.
// This function will help you easily log only certain properties of objects:
/**
* Functional implementation of the type utility
* [`Pick<Type, Keys>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)
*/
function pick<T, K extends keyof T>(
obj: T,
keys: readonly K[],
): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) result[key] = obj[key];
return result;
}
type SocketData = { id: string };
const socketMap = new Map<WebSocket, SocketData>();
// Do something when a connection is opened
function handleOpen(ev: Event, ws: WebSocket) {
const socketData: SocketData = { id: window.crypto.randomUUID() };
socketMap.set(ws, socketData);
console.log({
event: pick(ev, ["type"]),
socketData,
});
}
// Do something when an error occurs
function handleError(ev: Event, ws: WebSocket) {
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a connection is closed
function handleClose(ev: CloseEvent, ws: WebSocket) {
ev.code; // number
ev.reason; // string
ev.wasClean; // boolean
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type", "code", "reason", "wasClean"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a message is received
// Change `unknown` to the type of message payloads used in your application.
// (for example, JSON messages are `string`)
function handleMessage(ev: MessageEvent<unknown>, ws: WebSocket) {
ev.data; // unknown
ev.lastEventId; // string
ev.ports; // readonly MessagePort[]
const socketData = socketMap.get(ws);
if (socketData) {
socketData.id; // string
}
console.log({
event: pick(ev, ["type", "data", "lastEventId", "ports"]),
socketData,
});
}
const webSocketMiddleware: RouterMiddleware<"/ws"> = async (ctx, next) => {
const ws = ctx.upgrade();
ws.addEventListener("open", (ev) => handleOpen(ev, ws));
ws.addEventListener("error", (ev) => handleError(ev, ws));
ws.addEventListener("close", (ev) => handleClose(ev, ws));
ws.addEventListener("message", (ev) => handleMessage(ev, ws));
await next();
};
export const router = new Router();
router.get("/ws", webSocketMiddleware);
This is my updated code. It avoids the problem entirely
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
interface BroadcastObj{
name: string,
mssg: string
}
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
if(!ctx.isUpgradable){
ctx.throw(501);
}
const uid = globalThis.crypto.randomUUID();
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onmessage = (m) => {
let mssg = m.data as string;
if(typeof(mssg) === 'string'){
chatMessage(JSON.parse(mssg));
}
};
ws.onerror = (e) => {console.log('error occured: ', e);};
ws.onclose = () => { chatDisconnect(uid);};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket, uid: string) => {
await sockets.set(uid,ws);
}
const chatMessage = async (msg: BroadcastObj) => {
await sockets.forEach((ws: WebSocket) => {
ws.send(JSON.stringify(msg));
});
}
const chatDisconnect = async (uid: string) => {
await sockets.delete(uid);
}
export const wsRoutes = new Router()
.get('/ws', runWS);
I have this function
public pick(config?: FilePickerConfig): Promise<FilePickerResult> {
return new Promise<FilePickerResult>(resolve => {
this.pickWithCallbacks(resolve, resolve, config);
});
}
I want to test if the call to this.pickWithCallbacks has as first and second parameter the resolve parameter of the function.
Is there a way to do this in jest or jasmine? I have tried to spy on window, 'Promise' but it does not work.
Edit: It is not a depulicate of Spying on a constructor using Jasmine because that is what I have tried and did not work.
I have tried this:
const dummyResolve = () => { };
const promiseSpy = spyOn(window, 'Promise').and.callFake((dummyResolve)=>{});
const pickWithCallbacksSpy = spyOn(sut, 'pickWithCallbacks');
sut.pick();
expect(pickWithCallbacksSpy).toHaveBeenCalledWith(dummyResolve, dummyResolve, undefined);
So finally I just left the Promise do his thing and I captured the resolve callback
test('on success should call pickWithCallbacks with the resolve function of a promise', (done) => {
const cordovaExecSpy = spyOn(sut, 'pickWithCallbacks');
const dummyReturn = {};
sut.pick().then(obtained => {
expect(obtained).toBe(dummyReturn);
done();
});
const capturedOnSucess = cordovaExecSpy.calls.mostRecent().args[0];
capturedOnSucess(dummyReturn);
});
test('on Error should call pickWithCallbacks with the resolve function of a promise', (done) => {
const cordovaExecSpy = spyOn(sut, 'pickWithCallbacks');
const dummyReturn = {};
sut.pick().then(obtained => {
expect(obtained).toBe(dummyReturn);
done();
});
const capturedOnError = cordovaExecSpy.calls.mostRecent().args[1];
capturedOnError(dummyReturn);
});
Given a myknex.js:
export default { return require('knex')({client:'mysql',connection:{//conn}})}
and I want to write a unit test for the following function:
async function get({ userId }) {
return await myknex('users')
.where({ id: userId })
.returning('*');
}
The unit test looks like:
const sinon = require('sinon');
const sandbox = sinon.createSandbox();
const { myknex } = require('./myknex');
it('no record', async () => {
sandbox
.stub(myknex('users'), 'executeQuery').resolves([]);
const result = await myrepo.get({ userId: 1 });
const expectedResult = [];
expect(result).to.deep.equal(expectedResult);
});
I got an error message about:
TypeError: Cannot stub non-existent own property executeQuery
How can I mock the chained myknex calls?
Since you have myknex.js export a function of knex, we need to use proxyquire to mock this in test file.
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const proxyquire = require('proxyquire'); // include proxyquire
const expectedResult = [];
const knexQuery = {
where: sinon.stub().returnsThis(), // because we want to call `returning` afterwards
returning: sinon.stub().resolves(expectedResult) // resolve it as promise
}
const myknexStub = sinon.stub().returns(knexQuery);
const myrepo = proxyquire('./src', { './myknex': myknexStub }); // your source file and stub myknex here
describe(('Test My Module'), () => {
it('no record', async () => {
const result = await myrepo.get({ userId: 1 });
// many options to unit test the function
expect(myknexStub.calledWith('users')).to.be.ok;
expect(knexQuery.where.calledWith({ id: 1 })).to.be.ok;
expect(knexQuery.returning.calledWith('*')).to.be.ok;
expect(knexQuery.returning.calledAfter(knexQuery.where)).to.be.ok;
expect(result).to.deep.equal(expectedResult);
});
});
Hope it helps
I am trying to make two ajax call one after the other , i.e with the result of the first call data , i am making the second call. I am trying to use thunk , but it is not happening , i am getting errors.
actions.js
const fetchedStateNameSucess = (json) => {
return {
type: 'FETCH_STATE_NAME_SUCCESS',
json
}
}
const fetchProvidersDetailsSuccess = (providersData) => {
return {
type: 'FETCH_PROVIDER_DETAILS_SUCCESS',
providersData
}
}
export const fetchProvidersDetails = (providerId) => {
return (dispatch) => {
const providerUrl = `http://someUrl`;
const stateClientCountUrl = `http://someUrl/${state}`;
fetch(providerUrl)
.then(response => response.json())
.then(json => dispatch(fetchProvidersDetailsSuccess(json)))
.then((stateValue = json[0].stateVal)
fetch(stateClientCountUrl)
dispatch(fetchedStateNameSucess(response)));
};
}
In the above call , fetch(providerUrl) , i am getting response where i am getting the stateval , how to use that in making the second call to fetch(stateClientCountUrl) which takes stateval as a parameter.
As Miguel said you can do your second query in .then() clause as well as you can use async/await syntax, something like this:
export const fetchProvidersDetails = providerId => {
return async dispatch => {
const providerUrl = `http://someUrl`;
try {
const response = await fetch(providerUrl);
const json = await response.json();
dispatch(fetchProvidersDetailsSuccess(json))
const stateClientCountUrl = `http://someUrl/${json[0].stateVal}`;
const response2 = await fetch(stateClientCountUrl);
const json2 = await response2.json();
dispatch(fetchedStateNameSucess(json2));
} catch (error) {
console.log('Error', error);
}
}
If you want to use values from the first call response for the second fetch call, you need to do the second fetch after the first one has succeeded, more or less like this:
export const fetchProvidersDetails = (providerId) => {
return (dispatch) => {
const providerUrl = `http://someUrl`;
const stateClientCountUrl = `http://someUrl/${state}`;
fetch(providerUrl)
.then(response => response.json())
.then(json => {
dispatch(fetchProvidersDetailsSuccess(json));
const stateValue = json[0].stateVal;
fetch(stateClientCountUrl)
.then(response => dispatch(fetchProvidersDetailsSuccess(response)));
})
}
Don't forget to add error handling there, both for HTTP status codes and for JavaScript errors (by adding corresponding catch clauses).
I am trying to write a minimum working example of chai-as-promised in order to understand how it is working when testing functions that return a promise.
I have the following function:
simple.test = async (input) => {
return input;
};
and the following test function:
chai.use(sinonChai);
chai.use(chaiAsPromised);
const { expect } = chai;
const should = chai.should();
describe('#Test', () => {
it('test', () => {
expect(simple.test(1)).should.eventually.equal(1);
});
});
However, testing this results in the test not passing, but in a very long error, which is pasted here: https://pastebin.com/fppecStx
Question: Is there something wrong about the code, or what seems to be the problem here?
First: Your mixing expect and should. If you want to use should for assertion, you don't need expect.
Second: To tell mocha that a test is async you have to either call done, return a Promise or use async/await.
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const sinonChai = require('sinon-chai');
const should = chai.should();
chai.use(sinonChai);
chai.use(chaiAsPromised);
// Function to test
const simple = {
test: async (input) => {
return input;
}
}
// Test
describe('#Test', () => {
it('test', () => {
return simple.test(1).should.eventually.equal(1);
});
});