2.0.0 • Published 12 months ago

cypress-mock-websocket-plugin v2.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
12 months ago

Cypress Websocket Plugin

Mock websocket requests in a cypress intercept style.

semantic-release: conventionalcommits GitHub Workflow Status npm

The plugin allows you to write a test like:

const request = { type: 'request', payload: 'PING' }
const response = { type: 'response', payload: 'PONG'}

cy.mockWebSocket('ws://cypress-websocket/ws')
  .registerSocketRequestResponse(request, response)
  .visit('/')
  .contains('Cypress Websocket Plugin')
  .get('button')
  .click()
  .get('pre')
  .contains('PONG')

Usage

The plugin is known to work with rxjs web sockets and GraphQL Apollo client. It's recommended that you can bypass the default web socket constructor, but you can also use the browser's native web socket implementation (see below).

Setup

Import this plugin by adding it to the commands.ts file:

// cypress/support/commands.ts
import 'cypress-mock-websocket-plugin';

Here's an example how it will work with rxjs:

const isRunningInCypress = () =>
  !!window.Cypress

const getWebSocketCtor = () =>
  isRunningInCypress()
    ? window.MockedWebSocket
    : window.WebSocket

const webSocket$ = webSocket({
  url: 'ws://cypress-websocket/ws',
  WebSocketCtor: getWebSocketCtor(),
})

In your Cypress tests, mock the web socket before you call visit:

cy.mockWebSocket('ws://cypress-websocket/ws')
  .visit('/')

Changing the default web socket constructor

If for some reasons you want to use a different WebSocketCtor name than the default window.MockedWebSocket from the example, you can now change the name of the constructor:

cy.mockWebSocket('ws://cypress-websocket/ws', { webSocketCtorName: 'MyWebSocket' })

Native Web Socket API

It's now possible to use the browser's native WebSocket API. Because Cypress uses WebSockets for internal communication, you have to explicitly turn on the option for native web sockets:

cy.mockWebSocket('ws://cypress-websocket/ws', { useNative: true })

Initial response


⚠️ BREAKING CHANGE

The initial response must be moved to the options object.


If you need to mock an initial response, you can do it like this:

const initialResponse = { type: 'response', payload: 'INITIAL' }
cy.mockWebSocket('ws://cypress-websocket/ws', { connectionResponseMessage: initialResponse })

Mocking interactions (request/response style)

Mocking with static request and response:

const request = { type: 'request', payload: 'PING' }
const response = { type: 'response', payload: 'PONG'}

cy.mockWebSocket('ws://cypress-websocket/ws')
  .registerSocketRequestResponse(request, response)

If you have any fields dynamically generated by the client, e.g. access tokens, you can simply skip those properties in the request object. The plugin will partially match the expected and actual request.

Example:

// provide this to the cy.registerSocketRequestResponse
const expectedRequest = { 
  type: 'request', 
  payload: {
    content: 'Some content to match'
  }
}

// and it will match when client sends the following request:
const actualRequest = {
  type: 'request',
  payload: {
    accessToken: 'abc12345',
    content: 'Some content to match'
  }
}

The plugin uses a library called ts-pattern in order to match the requests.

If you need more control to match the request, you can pass a request handler instead of using a request object:

cy.registerSocketRequestResponse((request) => {
  //implement your logic to match the request here and return the mocked response
  return {
    type: 'response',
    payload: 'your payload'
  }
})

Mocking server events

In order to mock server events, you can simply trigger a server event like this:

cy.mockWebSocket('ws://cypress-websocket/ws')
  .visit('/')
  .contains('Cypress Websocket Plugin')
  .triggerSocketEvent({type: 'event', payload: "First Event"})
  .get('pre').contains('First Event')

Or, if you need more control (e.g. you need to provide a context specified in a previous interaction):

const correlationId = 1234
cy.mockWebSocket('ws://cypress-websocket/ws')
  .visit('/')
  .triggerSocketEvent(() => { return { type: 'event', payload: correlationId }})
  .get('pre').contains(correlationId)

Known Issues

In React 18, if you're using strict mode the useEffect hook is called twice, which does not work with the current implementation. You either have to deactivate strict mode in your tests, or you have to initialize the web socket outside of useEffect

Dependencies

This plugin was built using the following libraries:

Contributions

Any contributions are welcome. Feel free to open a ticket or create a pull request.

2.0.0

12 months ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago