Skip to content

Commit

Permalink
test(ws): add socket.io test
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Jan 31, 2024
1 parent 3e8c2c0 commit 8c261f2
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 4 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "3.3.0",
"engine.io-parser": "^5.2.1",
"express": "^4.17.3",
"express-rate-limit": "^6.3.0",
"follow-redirects": "^1.15.1",
Expand All @@ -140,6 +141,9 @@
"node-fetch": "2.6.7",
"rimraf": "^3.0.2",
"simple-git-hooks": "^2.7.0",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"socket.io-parser": "^4.2.4",
"superagent": "^6.1.0",
"supertest": "^6.1.6",
"ts-jest": "^27.1.1",
Expand Down
41 changes: 41 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

138 changes: 138 additions & 0 deletions test/modules/WebSocket/third-party/socket.io.browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { test, expect } from '../../../playwright.extend'
import { Server } from 'socket.io'
import type { io } from 'socket.io-client'
import type { Encoder, Decoder } from 'socket.io-parser'
import type { encodePacket, decodePacket } from 'engine.io-parser'
import { HttpServer } from '@open-draft/test-server/http'
import { DeferredPromise } from '@open-draft/deferred-promise'
import type { WebSocketInterceptor } from '../../../../src/interceptors/WebSocket'

declare global {
interface Window {
io: typeof io
encoder: Encoder
decoder: Decoder
encodePacket: typeof encodePacket
decodePacket: typeof decodePacket
interceptor: WebSocketInterceptor
}
}

const httpServer = new HttpServer()
const wsServer = new Server(httpServer['_http'], {
transports: ['websocket'],
})

test.beforeAll(async () => {
await httpServer.listen()
})

test.afterAll(async () => {
await httpServer.close()
})

test('intercepts and modifies data sent to socket.io server', async ({
loadExample,
page,
}) => {
await loadExample(require.resolve('./socket.io.runtime.js'), {
/**
* @note The WebSocket interceptor must be applied
* before "socket.io-client" is evaluated. SocketIO
* hoists the global WebSocket class so it cannot
* be patched by the interceptor.
*/
markup: `
<script src="https://cdn.socket.io/4.7.4/socket.io.min.js" integrity="sha384-Gr6Lu2Ajx28mzwyVR8CFkULdCU7kMlZ9UthllibdOSo6qAiN+yXNHqtgdTvFXMT4" crossorigin="anonymous" defer>
</script>
`,
})

const serverMessagePromise = new DeferredPromise<string>()
wsServer.on('connection', (socket) => {
socket.on('message', (data) => {
serverMessagePromise.resolve(data)
})
})

await page.evaluate((url) => {
const { io, interceptor, encoder, encodePacket, decodePacket, decoder } =
window

const decodeMessage = (
// @ts-expect-error
encodedEngineIoPacket
) => {
const decodedEngineIoPacket = decodePacket(encodedEngineIoPacket)

if (decodedEngineIoPacket.type !== 'message') {
return
}
const decodedSocketIoPacket = decoder['decodeString'](
decodedEngineIoPacket.data
)
/**
* @note You should reference "PacketType.EVENT"
* from "socket.io-parser" here but can't pass
* that through Playwright.
*/
if (decodedSocketIoPacket.type !== 2) {
return
}

return decodedSocketIoPacket.data.slice(1)
}

interceptor.on('connection', ({ client, server }) => {
const sendToSocketIo = (
// @ts-expect-error
data
) => {
encodePacket(
{
type: 'message', // 4
data: encoder.encode({
type: 2,
/**
* @noto Not sure if prepending "message"
* manually is correct. Either encoder doesn't
* do that though.
*/
data: ['message', data],
nsp: '/',
}),
},
true,
(encodedEngineIoPacket) => {
server.send(encodedEngineIoPacket)
}
)
}

server.connect()

client.on('message', (event) => {
const data = decodeMessage(event.data)

if (data?.[0] === 'hello') {
event.stopImmediatePropagation()
sendToSocketIo('mocked hello!')
}
})

client.on('message', (event) => {
server.send(event.data)
})
})

const ws = io(url, {
transports: ['websocket'],
})

ws.on('connect', () => {
ws.send('hello')
})
}, httpServer.http.address.href)

expect(await serverMessagePromise).toBe('mocked hello!')
})
12 changes: 12 additions & 0 deletions test/modules/WebSocket/third-party/socket.io.runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { WebSocketInterceptor } from '@mswjs/interceptors/WebSocket'
import { Encoder, Decoder } from 'socket.io-parser'
import { encodePacket, decodePacket } from 'engine.io-parser'

const interceptor = new WebSocketInterceptor()
interceptor.apply()
window.interceptor = interceptor

window.encodePacket = encodePacket
window.decodePacket = decodePacket
window.encoder = new Encoder()
window.decoder = new Decoder()
13 changes: 9 additions & 4 deletions test/playwright.extend.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { test as base, expect } from '@playwright/test'
import { Compilation, WebpackHttpServer } from 'webpack-http-server'
import {
Compilation,
CompilationOptions,
WebpackHttpServer,
} from 'webpack-http-server'
import {
BrowserXMLHttpRequestInit,
createBrowserXMLHttpRequest,
Expand All @@ -9,7 +13,7 @@ import {
import { getWebpackHttpServer } from './webpackHttpServer'

interface TestFixutures {
loadExample(entry: string): Promise<Compilation>
loadExample(entry: string, options?: CompilationOptions): Promise<Compilation>
webpackServer: WebpackHttpServer
callXMLHttpRequest: (
init: BrowserXMLHttpRequestInit
Expand All @@ -25,9 +29,10 @@ export const test = base.extend<TestFixutures>({
async loadExample({ webpackServer, page }, use) {
let compilation: Compilation | undefined

await use(async (entry) => {
await use(async (entry, options) => {
compilation = await webpackServer.compile(
Array.prototype.concat([], entry)
Array.prototype.concat([], entry),
options
)

page.on('pageerror', console.error)
Expand Down

0 comments on commit 8c261f2

Please sign in to comment.