Open chrismllr opened 3 years ago
Sorry, I don't personally have any suggestions. It would be really nice to have an official way to do this. AFAIK, Mirage doesn't have a built-in solution for dealing with web sockets but discussions have been had on a proposed API https://github.com/miragejs/discuss/issues/23.
Feel free to share a use case here and any ideas you have for a good API and I'd be happy to put some work into it. I've been hesitant to add anything to this library so it may be good to use this project as a proving ground for a solution and then see if we can get something official into Mirage itself.
@jneurock Thanks for the response!
I attempted to go so far as to using graphql-subscriptions
and subscriptions-transport-ws
libraries in my mirage server, but that felt pretty heavy handed.
For the time being, I opted to create a test helper method to add the mocked server to the mirage server context, being sure to use the same wsURL
im using to set up the Apollo WebsocketLink:
// Note-- this is the only lib that worked, mock-socket did not work properly
import { Server } from 'mock-socket-with-protocol';
import type { TestContext } from 'ember-test-helpers';
import Config from 'app/config/environment';
import { assert } from '@ember/debug';
let operationMap: Record<string, string> = {};
export function buildSubscriptionMessage(
operationName: string,
payload: Record<string, any>
): string {
return JSON.stringify({
type: 'data',
payload,
id: operationMap[operationName],
});
}
export function setupWebsocketServer(hooks: NestedHooks): void {
hooks.beforeEach(function (this: TestContext) {
assert(
'[setupWebsocketServer] You must place `setupWebsocketServer` after `setupMirage`.',
this.server
);
this.server.ws = new Server(Config.apollo.wsURL);
this.server.ws.on('connection', (socket: WebSocket) => {
socket.on('message', (data: string) => {
const msg = JSON.parse(data);
if (!msg.payload?.operationName) {
return;
}
operationMap[msg.payload.operationName] = msg.id;
});
});
});
hooks.afterEach(function (this: TestContext) {
operationMap = {};
this.server.ws.stop();
});
}
The utility function buildSubscriptionMessage
is used to mock-send messages "from the backend":
// send example message
describe('a test suite!', function(hooks) {
setupMirage(hooks)
setupWebsocketServer(hooks)
test('a test', function(this: TestContext, assert) {
const done = assert.async();
this.server.ws.send(
buildSubscriptionMessage(
'SubOperationName',
{
id: '123',
moreData: 'Data'
}
)
);
// use a mocked handler somewhere to call done();
assert.verifySteps();
});
});
This is obviously Very pared down; subscriptions-ws-transport
has a very thorough API for building the messages that are delivered to the UI -- this naive buildSubscriptionMessage
will surely need to be fleshed out more to get more types of messages covered but for now, it seems to be getting the job done simply without re-building too much backend logic 🤷
Edit: Updated the solution based on how it has evolved for us!
Super delayed response from my side...
Very cool. It seems this is largely an issue with Mirage itself lacking WebSocket support. There's some interest in refactoring Mirage and decoupling it from Pretender to enable usage of other server mocking libraries: https://github.com/miragejs/miragejs/issues/1013. It would be awesome to just support mocking subscriptions out-of-the-box.
Any suggestions on how to handle mocking Subscriptions? I'm thinking through a way to do it using the
graphql-subscriptions
in tandem withmock-socket
libraries, but it might be a little heavy.