Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSockets Support in Mirage #23

Open
seansellek opened this issue Mar 2, 2018 · 10 comments
Open

WebSockets Support in Mirage #23

seansellek opened this issue Mar 2, 2018 · 10 comments

Comments

@seansellek
Copy link

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

  • the .ws method also stores a reference to each WebSocket mock server defined, unique by URL. This is to avoid multiple calls to .ws with the same url causing collisions.
  • Mirage's .shutdown must now also clean up any mock socket servers.

How we teach this

  • There is very little to teach, since we are mostly exposing the socket server itself (In this case, mock-socket, but in the future that could be an actual web socket server) for the user to manipulate as necessary.
  • We approached this with a "less is more" mentality, since there are infinite ways any given application could use WebSockets, and there exists no standards like REST that we can cater to like the rest of mirage does.

Drawbacks

  • The mock-socket library itself does not support Socket.io. It claims to, but we were not able to get that working, it seems it diverged from Socket.io long ago, and there is no test coverage for it's socket.io mocking. My team decided that being able to properly test web sockets was more important to us than what Socket.io provided, and the path to least resistance was to tear out Socket.io and move foreward with vanilla web sockets.
  • The dependency on mock-socket, complete with piping through it's api so directly, might prove problematic, if that library falls behind or breaks for any reason it would heavily affect Mirage users.

Alternatives

  • We could provide more shorthands and conventions for common WebSocket usages, but when my team sat down to think about this, we realized that WebSockets were so much more versatile and open ended than RESTful http. Exposing the socket itself to send and receive messages from provided both the simplest experience and most control.

  • We could find another tool other than mock-socket if we feel there is anything lacking from it. However there do not seem to be many out there. We would probably need to build our own implementation should there be any concern surrounding mock-socket.

@samselikoff
Copy link
Contributor

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
Copy link

which version can we play with this feature?
:)

@crixx
Copy link

crixx commented Dec 4, 2018

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

@saygun
Copy link

saygun commented Dec 4, 2018

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

@samselikoff
Copy link
Contributor

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
Copy link

sukima commented Dec 14, 2018

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
Copy link

@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
Copy link
Contributor

@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 samselikoff transferred this issue from miragejs/ember-cli-mirage Jan 30, 2020
@samselikoff
Copy link
Contributor

samselikoff commented Jan 30, 2020

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
Copy link

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants