* #708 apply strictier linting rules and fix simple eslint bugs * #708 fix eslint errors relate to promise * #708 fix eslint import/no-extraneous-dependencies * #708 fix eslint errors of react-hook * #708 enable eslint check for typescript --------- Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
@@ -1,211 +1,210 @@
|
||||
import { fetchWebSocketTicket } from "@/websocket/api/fetchWebSocketTicket";
|
||||
import { createWebSocketConnection } from "@/websocket/connection/createConnection";
|
||||
import { WS_INBOUND_EVENTS } from "@/websocket/protocols";
|
||||
import { waitFor } from "@testing-library/dom";
|
||||
import { setupWebsocket } from "./utils/setupWebsocket";
|
||||
import { fetchWebSocketTicket } from '@/websocket/api/fetchWebSocketTicket'
|
||||
import { createWebSocketConnection } from '@/websocket/connection/createConnection'
|
||||
import { WS_INBOUND_EVENTS } from '@/websocket/protocols'
|
||||
import { waitFor } from '@testing-library/dom'
|
||||
import { setupWebsocket } from './utils/setupWebsocket'
|
||||
|
||||
jest.mock("@/websocket/api/fetchWebSocketTicket");
|
||||
jest.mock('@/websocket/api/fetchWebSocketTicket')
|
||||
|
||||
describe("createWebSocketConnection", () => {
|
||||
let mockWebSocket: jest.Mock;
|
||||
let cleanup: () => void;
|
||||
let webSocketInstances: any[] = [];
|
||||
describe('createWebSocketConnection', () => {
|
||||
let mockWebSocket: jest.Mock
|
||||
let cleanup: () => void
|
||||
let webSocketInstances: any[] = []
|
||||
|
||||
const mockTicket = {
|
||||
value: "test-ticket-123",
|
||||
clientAddress: "127.0.0.1",
|
||||
generatedOn: "2025-01-12T10:00:00Z",
|
||||
validUntil: "2025-01-12T11:00:00Z",
|
||||
username: "testuser",
|
||||
};
|
||||
value: 'test-ticket-123',
|
||||
clientAddress: '127.0.0.1',
|
||||
generatedOn: '2025-01-12T10:00:00Z',
|
||||
validUntil: '2025-01-12T11:00:00Z',
|
||||
username: 'testuser'
|
||||
}
|
||||
|
||||
/** ---------- Helpers ---------- */
|
||||
|
||||
const getWs = () => webSocketInstances[0];
|
||||
const getWs = () => webSocketInstances[0]
|
||||
|
||||
const triggerEvent = (ws: any, event: string, payload?: any) => {
|
||||
ws._listeners[event]?.[0]?.(payload);
|
||||
};
|
||||
ws._listeners[event]?.[0]?.(payload)
|
||||
}
|
||||
|
||||
const createAndOpenConnection = async () => {
|
||||
const mockCallbacks = {
|
||||
onMessage: jest.fn(),
|
||||
onClose: jest.fn(),
|
||||
onError: jest.fn(),
|
||||
};
|
||||
const promise = createWebSocketConnection(mockCallbacks);
|
||||
onError: jest.fn()
|
||||
}
|
||||
const promise = createWebSocketConnection(mockCallbacks)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(webSocketInstances.length).toBe(1);
|
||||
});
|
||||
expect(webSocketInstances.length).toBe(1)
|
||||
})
|
||||
|
||||
triggerEvent(getWs(), WS_INBOUND_EVENTS.CONNECTION_OPENED);
|
||||
const socket = await promise;
|
||||
triggerEvent(getWs(), WS_INBOUND_EVENTS.CONNECTION_OPENED)
|
||||
const socket = await promise
|
||||
|
||||
return { socket, ws: getWs(), promise, mockCallbacks };
|
||||
};
|
||||
return { socket, ws: getWs(), promise, mockCallbacks }
|
||||
}
|
||||
|
||||
/** ---------- Setup ---------- */
|
||||
|
||||
beforeEach(() => {
|
||||
({ webSocketInstances, mockWebSocket, cleanup } = setupWebsocket());
|
||||
window.WEBSOCKET_URL = "wss://calendar.example.com";
|
||||
|
||||
(fetchWebSocketTicket as jest.Mock).mockResolvedValue(mockTicket);
|
||||
});
|
||||
;({ webSocketInstances, mockWebSocket, cleanup } = setupWebsocket())
|
||||
window.WEBSOCKET_URL = 'wss://calendar.example.com'
|
||||
;(fetchWebSocketTicket as jest.Mock).mockResolvedValue(mockTicket)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
delete window.WEBSOCKET_URL;
|
||||
delete window.CALENDAR_BASE_URL;
|
||||
cleanup();
|
||||
});
|
||||
jest.clearAllMocks()
|
||||
delete window.WEBSOCKET_URL
|
||||
delete window.CALENDAR_BASE_URL
|
||||
cleanup()
|
||||
})
|
||||
|
||||
/** ---------- Tests ---------- */
|
||||
|
||||
it("throws when WEBSOCKET_URL is not defined", async () => {
|
||||
delete window.WEBSOCKET_URL;
|
||||
it('throws when WEBSOCKET_URL is not defined', async () => {
|
||||
delete window.WEBSOCKET_URL
|
||||
const mockCallbacks = {
|
||||
onMessage: jest.fn(),
|
||||
};
|
||||
onMessage: jest.fn()
|
||||
}
|
||||
|
||||
await expect(createWebSocketConnection(mockCallbacks)).rejects.toThrow(
|
||||
"WEBSOCKET_URL is not defined"
|
||||
);
|
||||
});
|
||||
'WEBSOCKET_URL is not defined'
|
||||
)
|
||||
})
|
||||
|
||||
it("fetches WebSocket ticket", async () => {
|
||||
await createAndOpenConnection();
|
||||
expect(fetchWebSocketTicket).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('fetches WebSocket ticket', async () => {
|
||||
await createAndOpenConnection()
|
||||
expect(fetchWebSocketTicket).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("creates WebSocket with correct URL and ticket", async () => {
|
||||
await createAndOpenConnection();
|
||||
it('creates WebSocket with correct URL and ticket', async () => {
|
||||
await createAndOpenConnection()
|
||||
|
||||
expect(mockWebSocket).toHaveBeenCalledWith(
|
||||
"wss://calendar.example.com/ws?ticket=test-ticket-123"
|
||||
);
|
||||
});
|
||||
'wss://calendar.example.com/ws?ticket=test-ticket-123'
|
||||
)
|
||||
})
|
||||
|
||||
it("creates WebSocket with correct URL and ticket without the WEBSOCKET_URL", async () => {
|
||||
delete window.WEBSOCKET_URL;
|
||||
window.CALENDAR_BASE_URL = "https://calendar.example.com";
|
||||
await createAndOpenConnection();
|
||||
it('creates WebSocket with correct URL and ticket without the WEBSOCKET_URL', async () => {
|
||||
delete window.WEBSOCKET_URL
|
||||
window.CALENDAR_BASE_URL = 'https://calendar.example.com'
|
||||
await createAndOpenConnection()
|
||||
|
||||
expect(mockWebSocket).toHaveBeenCalledWith(
|
||||
"wss://calendar.example.com/ws?ticket=test-ticket-123"
|
||||
);
|
||||
});
|
||||
'wss://calendar.example.com/ws?ticket=test-ticket-123'
|
||||
)
|
||||
})
|
||||
|
||||
it("resolves with socket when connection opens", async () => {
|
||||
const { socket, ws } = await createAndOpenConnection();
|
||||
expect(socket).toBe(ws);
|
||||
});
|
||||
it('resolves with socket when connection opens', async () => {
|
||||
const { socket, ws } = await createAndOpenConnection()
|
||||
expect(socket).toBe(ws)
|
||||
})
|
||||
|
||||
it("rejects when connection fails", async () => {
|
||||
it('rejects when connection fails', async () => {
|
||||
const mockCallbacks = {
|
||||
onMessage: jest.fn(),
|
||||
};
|
||||
const promise = createWebSocketConnection(mockCallbacks);
|
||||
onMessage: jest.fn()
|
||||
}
|
||||
const promise = createWebSocketConnection(mockCallbacks)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(webSocketInstances.length).toBe(1);
|
||||
});
|
||||
expect(webSocketInstances.length).toBe(1)
|
||||
})
|
||||
|
||||
triggerEvent(getWs(), WS_INBOUND_EVENTS.ERROR, new Event("error"));
|
||||
triggerEvent(getWs(), WS_INBOUND_EVENTS.ERROR, new Event('error'))
|
||||
|
||||
await expect(promise).rejects.toThrow("WebSocket connection failed");
|
||||
});
|
||||
await expect(promise).rejects.toThrow('WebSocket connection failed')
|
||||
})
|
||||
|
||||
it("attaches message event listener", async () => {
|
||||
const { ws } = await createAndOpenConnection();
|
||||
it('attaches message event listener', async () => {
|
||||
const { ws } = await createAndOpenConnection()
|
||||
|
||||
expect(ws._listeners[WS_INBOUND_EVENTS.MESSAGE]).toBeDefined();
|
||||
expect(ws._listeners[WS_INBOUND_EVENTS.MESSAGE].length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(ws._listeners[WS_INBOUND_EVENTS.MESSAGE]).toBeDefined()
|
||||
expect(ws._listeners[WS_INBOUND_EVENTS.MESSAGE].length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it("attaches close event listener", async () => {
|
||||
const { ws } = await createAndOpenConnection();
|
||||
expect(ws._listeners[WS_INBOUND_EVENTS.CONNECTION_CLOSED]).toBeDefined();
|
||||
});
|
||||
it('attaches close event listener', async () => {
|
||||
const { ws } = await createAndOpenConnection()
|
||||
expect(ws._listeners[WS_INBOUND_EVENTS.CONNECTION_CLOSED]).toBeDefined()
|
||||
})
|
||||
|
||||
it("handles invalid JSON messages", async () => {
|
||||
const errorSpy = jest.spyOn(console, "error").mockImplementation();
|
||||
it('handles invalid JSON messages', async () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation()
|
||||
|
||||
const { ws } = await createAndOpenConnection();
|
||||
const { ws } = await createAndOpenConnection()
|
||||
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.MESSAGE, { data: "invalid json" });
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.MESSAGE, { data: 'invalid json' })
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
"Failed to parse WebSocket message:",
|
||||
'Failed to parse WebSocket message:',
|
||||
expect.any(Error)
|
||||
);
|
||||
)
|
||||
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
errorSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("rejects on timeout", async () => {
|
||||
jest.useFakeTimers();
|
||||
it('rejects on timeout', async () => {
|
||||
jest.useFakeTimers()
|
||||
const mockCallbacks = {
|
||||
onMessage: jest.fn(),
|
||||
};
|
||||
const promise = createWebSocketConnection(mockCallbacks);
|
||||
onMessage: jest.fn()
|
||||
}
|
||||
const promise = createWebSocketConnection(mockCallbacks)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(webSocketInstances.length).toBe(1);
|
||||
});
|
||||
expect(webSocketInstances.length).toBe(1)
|
||||
})
|
||||
|
||||
jest.advanceTimersByTime(10000);
|
||||
jest.advanceTimersByTime(10000)
|
||||
|
||||
await expect(promise).rejects.toThrow("WebSocket connection timed out");
|
||||
await expect(promise).rejects.toThrow('WebSocket connection timed out')
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it("calls onMessage callback when message received", async () => {
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection();
|
||||
it('calls onMessage callback when message received', async () => {
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection()
|
||||
|
||||
const testMessage = { type: "test", payload: "data" };
|
||||
const testMessage = { type: 'test', payload: 'data' }
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.MESSAGE, {
|
||||
data: JSON.stringify(testMessage),
|
||||
});
|
||||
data: JSON.stringify(testMessage)
|
||||
})
|
||||
|
||||
expect(mockCallbacks.onMessage).toHaveBeenCalledWith(testMessage);
|
||||
});
|
||||
expect(mockCallbacks.onMessage).toHaveBeenCalledWith(testMessage)
|
||||
})
|
||||
|
||||
it("does not call onMessage when JSON parsing fails", async () => {
|
||||
const errorSpy = jest.spyOn(console, "error").mockImplementation();
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection();
|
||||
it('does not call onMessage when JSON parsing fails', async () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation()
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection()
|
||||
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.MESSAGE, { data: "invalid json" });
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.MESSAGE, { data: 'invalid json' })
|
||||
|
||||
expect(mockCallbacks.onMessage).not.toHaveBeenCalled();
|
||||
expect(mockCallbacks.onMessage).not.toHaveBeenCalled()
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
"Failed to parse WebSocket message:",
|
||||
'Failed to parse WebSocket message:',
|
||||
expect.any(Error)
|
||||
);
|
||||
)
|
||||
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
errorSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("calls onClose callback when connection closes", async () => {
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection();
|
||||
it('calls onClose callback when connection closes', async () => {
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection()
|
||||
|
||||
const closeEvent = new CloseEvent("close", {
|
||||
const closeEvent = new CloseEvent('close', {
|
||||
code: 1000,
|
||||
reason: "Normal closure",
|
||||
});
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.CONNECTION_CLOSED, closeEvent);
|
||||
reason: 'Normal closure'
|
||||
})
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.CONNECTION_CLOSED, closeEvent)
|
||||
|
||||
expect(mockCallbacks.onClose).toHaveBeenCalledWith(closeEvent);
|
||||
});
|
||||
expect(mockCallbacks.onClose).toHaveBeenCalledWith(closeEvent)
|
||||
})
|
||||
|
||||
it("calls onError callback when error occurs", async () => {
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection();
|
||||
it('calls onError callback when error occurs', async () => {
|
||||
const { ws, mockCallbacks } = await createAndOpenConnection()
|
||||
|
||||
const errorEvent = new Event("error");
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.ERROR, errorEvent);
|
||||
const errorEvent = new Event('error')
|
||||
triggerEvent(ws, WS_INBOUND_EVENTS.ERROR, errorEvent)
|
||||
|
||||
expect(mockCallbacks.onError).toHaveBeenCalledWith(errorEvent);
|
||||
});
|
||||
});
|
||||
expect(mockCallbacks.onError).toHaveBeenCalledWith(errorEvent)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user