miragejs / discuss

Ask questions, get help, and share what you've built with Mirage
MIT License
2 stars 0 forks source link

WebSockets Support in Mirage #23

Open seansellek opened 6 years ago

seansellek commented 6 years ago

WebSockets Support in Mirage

Summary

Our team has been working on integrating web sockets mocking with Mirage. We set out to find something that worked for us. We've built this and have been using it to augment our tests and our development environment. It's worked well for us, and has even allowed us to provide proper test coverage for bugs being reported in the field and has allowed us to simulate complex situations in development, helping us catch bugs we would have otherwise had a lot of trouble seeing. We'd like to propose it as something that could be integrated with Mirage itself.

Motivation

WebSockets are a critical part of many application's infrastructure, and without the ability to simulate them in development and control them in test, Mirage cannot fulfill it's mission of being a client-side mock of your server.

Detailed design

I am proposing only one addition to the api:

Server.ws(url)

Returns: a mock-socket Server Creates a new mock socket at the specified, fully-resolved url. This method should be called in mirage/config.js to setup the socket endpoint. With no further action taken, this will prevent Mirage from yelling at us when attempts are made to connect to this endpoint.

In order to send messages down (or listen for messages from the client), you can then use the returned Server, which is documented over at mock-socket.

Examples: This will send a singular WebSocket message whenever the client connects to the socket.

// in mirage/config.js
this.ws('ws://localhost:4200/socket').on('connection', (ws) => {
  ws.send({ data });
});

If you want to manually send messages or otherwise control a WebSocket from your tests, you can easily access and control that WebSocket like you would any other endpoint:

// in mirage/config.js
this.ws('ws://localhost:4200/socket');
// in a test file
test('displays text when receiving websocket message', function(assert) {
  this.server.ws('ws://localhost:4200/socket').send({ message: "Hello World!" });
  assert.dom('[data-test-socket-message]').hasText("Hello World!");
});

For Development, you may wish to define custom web socket behavior to better mimic your server. Mirage's scenarios seem to be the best place for this to live.

// in mirage/config.js
this.ws('ws://localhost:4200/socket');
// in mirage/scenarios/default.js
const socket = this.ws('ws://localhost:4200/socket');
setInterval(() => {
  socket.send({ message: "Hello World!"});
}, 5000);

Special Considerations

How we teach this

Drawbacks

Alternatives

samselikoff commented 6 years ago

I love this. Thank you for putting the time and effort in to prototype/build it, and also to write this issue up.

I agree that WS is way less constrained than normal REST backends, so it makes sense to start with a flexible primitive. We can build sugar on top of it from there, as common use cases arise.

In the short-term, if (for example) folks want to send serialized payloads of their models to the frontend, they can do so via

let ws = server.ws('ws://localhost:4200/socket');

setInterval(() => {
  let post = server.schema.posts.first();
  ws.send( server.serializerOrRegistry.serialize(post) );
}, 5000);

(We can come up with a better alias for serialize to give folks access to the json payload).

So, there is no other kind of "method" to connect to a ws (similar to get, post, etc.), right? If so, this.ws feels like the right api.

Regarding socket.io, I think this.ws would be the API whether or not is was supported, so we can start with mock-socket, and later add a socket.io layer that users can switch if they are using socket.io. But we can tackle that at a future time. I am a bit bummed mock-socket doesn't work with it, but I agree it's a solid library and one I've used on past projects.

If you'd like to kick of a PR I would love to get this into Mirage!

MrCuriosity commented 5 years ago

which version can we play with this feature? :)

crixx commented 5 years ago

@samselikoff @seansellek what's missing here? How can we give you guys a hand?

saygun commented 5 years ago

I might also try to help if needed. This feature would be great.

samselikoff commented 5 years ago

I think an API we're going to be adding soon will help with this, it's on a branch and it's something like server.handleRequest() which mimics sending a request to the server and generating a response. Then you could use this to respond with the payloads for given websocket events (e.g. serialized models).

It's worth noting that you can use something like mock socket today with Mirage, I've done this in past projects by wiring things up in mirage/config.js

sukima commented 5 years ago

If this were implemented I think it would help to expose a small API on window that way in development one could open the console and simulate server -> client events.

Abhinav1801 commented 5 years ago

@samselikoff @seansellek Hello guys, Do we have this feature (websocket support in ember-mirage) in any if the mirage versions yet? If not, when can we expect to play with this?

samselikoff commented 5 years ago

@Abhinav1801 You can definitely do this today, using a combination of something like mock-socket and Mirage's ORM & serializers. If you put something together and hit some walls I can help out more specifically.

samselikoff commented 4 years ago

FYI: Transferred this to our Discuss repo, our new home for more open-ended conversations about Mirage!

If things become more concrete + actionable we can create a tracking issue in the main repo.

thafryer commented 4 years ago

@samselikoff Has there been any more traction here? Is there a working example or does anyone have something started? If not, I can work on contributing something for this.