From ddc1de83947d799c528ff520a0d5863b35dabe11 Mon Sep 17 00:00:00 2001 From: naporitan Date: Mon, 1 Jul 2024 17:05:45 +0900 Subject: [PATCH] fix(client): Add Query Parameter Support to WebSocket Client in `hono/client` (#3066) * chore(client): fix url with query parameter websocket client * chore(client): remove log --- src/client/client.test.ts | 74 +++++++++++++++++++++++++++++++++++++++ src/client/client.ts | 8 +++-- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/client/client.test.ts b/src/client/client.test.ts index 8cb8060b..e01931a1 100644 --- a/src/client/client.test.ts +++ b/src/client/client.test.ts @@ -1044,6 +1044,80 @@ describe('WebSocket URL Protocol Translation', () => { }) }) +describe('WebSocket URL Protocol Translation with Query Parameters', () => { + const app = new Hono() + const route = app.get( + '/', + upgradeWebSocket((c) => ({ + onMessage(event, ws) { + ws.send('Hello from server!') + }, + onClose: () => { + console.log('Connection closed') + }, + })) + ) + + type AppType = typeof route + + const server = setupServer() + const webSocketMock = vi.fn() + + beforeAll(() => server.listen()) + beforeEach(() => { + vi.stubGlobal('WebSocket', webSocketMock) + }) + afterEach(() => { + vi.clearAllMocks() + server.resetHandlers() + }) + afterAll(() => server.close()) + + it('Translates HTTP to ws and includes query parameters', async () => { + const client = hc('http://localhost') + client.index.$ws({ + query: { + id: '123', + type: 'test', + }, + }) + expect(webSocketMock).toHaveBeenCalledWith('ws://localhost/index?id=123&type=test') + }) + + it('Translates HTTPS to wss and includes query parameters', async () => { + const client = hc('https://localhost') + client.index.$ws({ + query: { + id: '456', + type: 'secure', + }, + }) + expect(webSocketMock).toHaveBeenCalledWith('wss://localhost/index?id=456&type=secure') + }) + + it('Keeps ws unchanged and includes query parameters', async () => { + const client = hc('ws://localhost') + client.index.$ws({ + query: { + id: '789', + type: 'plain', + }, + }) + expect(webSocketMock).toHaveBeenCalledWith('ws://localhost/index?id=789&type=plain') + }) + + it('Keeps wss unchanged and includes query parameters', async () => { + const client = hc('wss://localhost') + client.index.$ws({ + query: { + id: '1011', + type: 'secure', + }, + }) + expect(webSocketMock).toHaveBeenCalledWith('wss://localhost/index?id=1011&type=secure') + }) +}) + describe('Client can be console.log in react native', () => { it('Returns a function name with function.name.toString', async () => { const client = hc('http://localhost') diff --git a/src/client/client.ts b/src/client/client.ts index 28f1bee1..aa329265 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -178,12 +178,16 @@ export const hc = >( return new URL(url) } if (method === 'ws') { - const targetUrl = replaceUrlProtocol( + const webSocketUrl = replaceUrlProtocol( opts.args[0] && opts.args[0].param ? replaceUrlParam(url, opts.args[0].param) : url, 'ws' ) + const targetUrl = new URL(webSocketUrl) + for (const key in opts.args[0]?.query) { + targetUrl.searchParams.set(key, opts.args[0].query[key]) + } - return new WebSocket(targetUrl) + return new WebSocket(targetUrl.toString()) } const req = new ClientRequestImpl(url, method)