npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@open-draft/test-server

v0.7.1

Published

HTTP/HTTPS testing server for your tests.

Downloads

2,682

Readme

Test Server

Spawns HTTP/HTTPS/WS/WSS local servers for testing.

Motivation

I happened to do a lot of network testing. In my test suites, I often need to perform requests against actual HTTP/WebSocket servers, but need the flexibility to model whatever scenarios I want, including on a per-test basis. Traditional server packages likes express or fastify are great but too verbose for this particular use case.

So I've built this utility. I've been using this library for years to help me perfect the test suite for Mock Service Worker. But why build it at all?

  • Compact. I can spawn actual servers in tests while keeping the test setup to the minimum.
  • Practical. I can configure test servers first, spawn later. This adds a ton of reusability while also keeping my tests clean. I can also use the test servers as disposables, which means they will automatically clean after themselves even if I establish a server within a particular test.
  • Universal. This library gives you the means to create test HTTP and WebSocket servers to test the network to your heart's content.

It's also a bit silly that in 2024 some server libraries don't return a connection promise and don't give you any adequate means to get the server's actual URL. Those two are quite essential when testing the network.

This library is built on top of Hono, and you should definitely check it out! For once, it's an incredible environment-agnostic server library. But also, because you can attach existing Hono middleware to your test serves to send them to the moon.

Install

npm install @open-draft/test-server

Usage

HTTP server

import { createTestHttpServer } from '@open-draft/test-server/http'

const server = createTestHttpServer({
  defineRoutes(router) {
    router.get('/numbers', () => {
      return Response.json([1, 2, 3])
    })
  },
})

beforeAll(async () => {
  await server.listen()
})

afterAll(async () => {
  await server.close()
})

test('fetches all the numbers', async () => {
  const response = await fetch(server.http.url('/numbers'))
  expect(response.status).toBe(200)
  await expect(response.json()).resolves.toEqual([1, 2, 3])
})

WebSocket server

import { createTestHttpServer } from '@open-draft/test-server/http'
import { createTestWebSocketServer } from '@open-draft/test-server/ws'

const server = createTestHttpServer()
const wss = createTestWebSocketServer({
  // Attach the test WebSocket server to an existing HTTP server.
  server,
  pathname: '/ws',
})

// Running the HTTP server will establish the WebSocket middleware as well.
await server.listen()

console.log('WebSocket server is running at:', wss.ws.url())

wss.ws.on('connect', (socket) => {
  console.log('new connection:', socket.id)
})

As disposable utilities

Both the createTestHttpServer() and createTestWebSocketServer() utilities are disposable, which means they can be created within function closures (e.g. your test cases) and will be automatically cleaned up once that closure is garbage-collected.

// my.test.js
import { createTestHttpServer } from '@open-draft/test-server/http'

it('fetches a resource by id', async () => {
  await using server = createTestHttpServer({
    defineRoutes(router) {
      router.get('/resource/:id', () => {
        return Response.json({ a: 1 })
      })
    }
  })

  await fetch(server.http.url('/resource/abc-123'))
})

In the test case above, the test HTTP server will automatically be closed once the test case is done!

The "rooms" you can create in the test HTTP server are also disposable, which allows you to append additional behaviors to an already existing test server instance.

const server = createTestHttpServer()

beforeAll(async () => await server.listen())
afterAll(async () => await server.listen())

it('handles errors when fetching a resource', async () => {
  await using room = server.http.createRoom({
    defineRoutes(router) {
      router.get('/resource', () => {
        return new Response(null, { status: 404 })
      })
    }
  })

  await fetch(room.url('/resource'))
})

Note that the request references the /resource pathname nested under the room (room.url()).

API

createTestHttpServer([options])

Creates a test HTTP server instance. The server instance is not running until you call server.listen()

  • options (optional)
    • protocols, (optional) Array<'http' | 'htts', the list of protocols to spawn corresponding test servers. For example, providing ['http', 'https'] will spawn two test servers (HTTP and HTTPS) with the same route handlers.
    • defineRoutes, (optional) (router: Hono) => void, a function describing the initial route handlers.
const server = createTestHttpServer({
  protocols: ['http', 'https'],
  defineRoutes(router) {
    router.get('/user', () => {
      return Response.json(userFixture)
    })
  },
})

TestHttpServer

TestHttpServer.listen([options])

  • options (optional)
    • hostname, (optional) string, the exact hostname to use for this server.

TestHttpServer.close()

Closes the HTTP server, aborting any pending requests.

TestHttpServer.http.url([pathname])

  • pathname (optional) string, a pathname to rebase against the server URL.
const server = createTestHttpServer()
await server.listen()

server.http.url() // "http://localhost:57834"
server.http.url('/resource') // "http://localhost:57834/resource"

TestHttpServer.http.createRoom([options])

Create a new room on this server. Room is a set of route handlers scoped under a random base path.

  • options (optional)
    • defineRoutes, (optional) (router: Hono) => void, a function describing the routes for this room. Note that all room routes are scoped to the random room base path.
const server = createTestHttpServer()
await server.listen()

const room = server.http.creaetRoom({
  defineRouter(router) {
    router.get('/resource', () => new Response())
  },
})
room.url() // "http://localhost:57834/48e34ef7-13d0-4b6b-862f-4a29475c3396"

await fetch(room.url('/resource'))

Creating rooms with their own route handlers allows you to reuse the same running server instance while imbuing it with different functionality on a per-test basis.

createTestWebSocketServer(options)

  • options
    • server, TestHttpServer, a reference to the server instance created by calling the createTestHttpServer() utility.
    • pathname, string, a pathname of the given HTTP server where the WebSocket connection must be established.

TestWebSocketServer.ws.url()

TestWebSocketServer.wss.url()

Returns an absolute URL to this WebSocket connection.

Note that the .url() method on the WebSocket server does not accept a pathname. The WebSocket protocol does not define the concept of sub-resources, so there is no point in referencing nested paths that point at nothing.

TestWebSocketServer.on(type, listener)

Adds an event listener to the underlying WebSocket connection.

const wss = createTestWebSocketServer({
  server,
  pathname: '/ws',
})

wss.ws.on('connection', (socket) => {
  // Handle any incoming WebSocket client connections
  // to this test WebSocket server under HTTP.
})

TestWebSocketServer.once(type, listener)

Adds a one-time event listener to this WebSocket connection.

TestWebSocketServer.off(type, listener)

Removes the event listener for the given event type from this WebSocket connection.

TestWebSocketServer.close()

Closes the WebSocket server, closing any active clients.

[!IMPORTANT] Note that closing the WebSocket server does not close the parent HTTP server. Closing the parent HTTP server, however, will automatically close any attached WebSocket servers.