Package Exports
- @exodus/replay
- @exodus/replay/fetch
- @exodus/replay/websocket
Readme
Record/inspect/replay fetch and WebSocket sessions
For offline testing
These implementations are not specific to @exodus/test and can be used separately
Zero-dependencies, bundleable
Replayers can run in V8 / JavaScriptCore / Hermes even with no network API implemented at all
import { fetchRecorder, fetchReplayer, WebSocketRecorder, WebSocketReplayer, prettyJSON } from '@exodus/replay'Generated logs are human-readable
fetchRecorder(log[, { fetch }])
Returns a fetch implementation that appends all requests to the provided log array
log is expected to be an array and is mutated by appending new requests
If a base fetch implementation is not passed, global fetch is used as the base
That can be overriden, e.g. fetchRecorder(log, { fetch: require('node-fetch') })
fetchReplayer(log)
Returns a fetch implementation that replies with requests from the provided log array
Requests are served by "first matching" order and are discarded from an internal log clone
log is expected to be an array and is not mutated
WebSocketRecorder(log[, { WebSocket }])
Returns a WebSocket implementation that appends all sessions to the provided log array
log is expected to be an array and is mutated by appending new sessions
If a base WebSocket implementation is not passed, global WebSocket is used as the base
That can be overriden, e.g. WebSocketRecorder(log, { WebSocket: require('ws') })
WebSocketReplayer(log[, { interval = 0 }])
Returns a WebSocket implementation that replies with sessions from the provided log array
Sessions are served by "first matching" order and are discarded from an internal log clone
log is expected to be an array and is not mutated
Optionally, interval can be used to control replay speed:
interval = 0for immediate event firing without delays (default)interval = Infinityfor event timing matching the original recordinginterval = numberfor delay between events ofMath.min(number, recorededDelay)
prettyJSON(data, { width = 120 })
Use it to pretty-print logs: prettyJSON(log)
Like JSON.stringify(), but with pretty-printing for readability and ease if inspection
The output is parse-able with JSON.parse()
For simplicity, fitting into width is not guaranteed, but the output is stable between runs
Samples
fetch recording
{
"request": {
"resource": "https://jsonplaceholder.typicode.com/users/2",
"options": { "headers": [["accept", "application/json"]] }
},
"status": 200,
"statusText": "OK",
"ok": true,
"headers": [
...
["connection", "keep-alive"],
["content-encoding", "br"],
["content-type", "application/json; charset=utf-8"],
["date", "Sat, 03 Aug 2024 16:57:51 GMT"],
...
],
"url": "https://jsonplaceholder.typicode.com/users/2",
"redirected": false,
"type": "basic",
"bodyType": "json",
"body": {
"id": 2,
"name": "Ervin Howell",
"username": "Antonette",
"email": "Shanna@melissa.tv",
"address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
"geo": { "lat": "-43.9509", "lng": "-34.4618" }
},
"phone": "010-692-6593 x09125",
"website": "anastasia.net",
"company": {
"name": "Deckow-Crist",
"catchPhrase": "Proactive didactic contingency",
"bs": "synergize scalable supply-chains"
}
}
}WebSocket recording
{
"url": "wss://javascript.info/article/websocket/demo/hello",
"log": [
{ "type": "get readyState", "at": 24, "value": 0 },
{ "type": "open", "at": 426 },
{ "type": "get readyState", "at": 426, "value": 1 },
{ "type": "get bufferedAmount", "at": 427, "value": 0 },
{ "type": "send()", "at": 427, "data": "Hi there" },
{ "type": "get bufferedAmount", "at": 427, "value": 8 },
{ "type": "message", "at": 543, "data": "Hello from server, there!" },
{ "type": "close()", "at": 543 },
{ "type": "get readyState", "at": 543, "value": 2 },
{ "type": "close", "at": 661, "code": 1005, "reason": "", "wasClean": true },
{ "type": "get readyState", "at": 661, "value": 3 }
]
}