refactor: update TypeScript configuration and test utilities for improved type safety
- Modify tsconfig.json to relax strict type checking for gradual migration - Update test files to use more flexible type checking and mocking - Add type-safe mock and test utility functions - Improve error handling and type inference in test suites - Export Tool interface and tools list for better testing support
This commit is contained in:
@@ -54,8 +54,8 @@ interface MockWebSocketConstructor extends jest.Mock<MockWebSocketInstance> {
|
||||
}
|
||||
|
||||
// Mock the entire hass module
|
||||
jest.mock('../../src/hass/index.js', () => ({
|
||||
get_hass: jest.fn()
|
||||
// // jest.mock('../../src/hass/index.js', () => ({
|
||||
get_hass: mock()
|
||||
}));
|
||||
|
||||
describe('Home Assistant API', () => {
|
||||
@@ -66,11 +66,11 @@ describe('Home Assistant API', () => {
|
||||
beforeEach(() => {
|
||||
hass = new HassInstanceImpl('http://localhost:8123', 'test_token');
|
||||
mockWs = {
|
||||
send: jest.fn(),
|
||||
close: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
send: mock(),
|
||||
close: mock(),
|
||||
addEventListener: mock(),
|
||||
removeEventListener: mock(),
|
||||
dispatchEvent: mock(),
|
||||
onopen: null,
|
||||
onclose: null,
|
||||
onmessage: null,
|
||||
@@ -84,7 +84,7 @@ describe('Home Assistant API', () => {
|
||||
} as MockWebSocketInstance;
|
||||
|
||||
// Create a mock WebSocket constructor
|
||||
MockWebSocket = jest.fn().mockImplementation(() => mockWs) as MockWebSocketConstructor;
|
||||
MockWebSocket = mock().mockImplementation(() => mockWs) as MockWebSocketConstructor;
|
||||
MockWebSocket.CONNECTING = 0;
|
||||
MockWebSocket.OPEN = 1;
|
||||
MockWebSocket.CLOSING = 2;
|
||||
@@ -96,7 +96,7 @@ describe('Home Assistant API', () => {
|
||||
});
|
||||
|
||||
describe('State Management', () => {
|
||||
it('should fetch all states', async () => {
|
||||
test('should fetch all states', async () => {
|
||||
const mockStates: HomeAssistant.Entity[] = [
|
||||
{
|
||||
entity_id: 'light.living_room',
|
||||
@@ -108,7 +108,7 @@ describe('Home Assistant API', () => {
|
||||
}
|
||||
];
|
||||
|
||||
global.fetch = jest.fn().mockResolvedValueOnce({
|
||||
global.fetch = mock().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockStates)
|
||||
});
|
||||
@@ -121,7 +121,7 @@ describe('Home Assistant API', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch single state', async () => {
|
||||
test('should fetch single state', async () => {
|
||||
const mockState: HomeAssistant.Entity = {
|
||||
entity_id: 'light.living_room',
|
||||
state: 'on',
|
||||
@@ -131,7 +131,7 @@ describe('Home Assistant API', () => {
|
||||
context: { id: '123', parent_id: null, user_id: null }
|
||||
};
|
||||
|
||||
global.fetch = jest.fn().mockResolvedValueOnce({
|
||||
global.fetch = mock().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockState)
|
||||
});
|
||||
@@ -144,16 +144,16 @@ describe('Home Assistant API', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle state fetch errors', async () => {
|
||||
global.fetch = jest.fn().mockRejectedValueOnce(new Error('Failed to fetch states'));
|
||||
test('should handle state fetch errors', async () => {
|
||||
global.fetch = mock().mockRejectedValueOnce(new Error('Failed to fetch states'));
|
||||
|
||||
await expect(hass.fetchStates()).rejects.toThrow('Failed to fetch states');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Service Calls', () => {
|
||||
it('should call service', async () => {
|
||||
global.fetch = jest.fn().mockResolvedValueOnce({
|
||||
test('should call service', async () => {
|
||||
global.fetch = mock().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({})
|
||||
});
|
||||
@@ -175,8 +175,8 @@ describe('Home Assistant API', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle service call errors', async () => {
|
||||
global.fetch = jest.fn().mockRejectedValueOnce(new Error('Service call failed'));
|
||||
test('should handle service call errors', async () => {
|
||||
global.fetch = mock().mockRejectedValueOnce(new Error('Service call failed'));
|
||||
|
||||
await expect(
|
||||
hass.callService('invalid_domain', 'invalid_service', {})
|
||||
@@ -185,8 +185,8 @@ describe('Home Assistant API', () => {
|
||||
});
|
||||
|
||||
describe('Event Subscription', () => {
|
||||
it('should subscribe to events', async () => {
|
||||
const callback = jest.fn();
|
||||
test('should subscribe to events', async () => {
|
||||
const callback = mock();
|
||||
await hass.subscribeEvents(callback, 'state_changed');
|
||||
|
||||
expect(MockWebSocket).toHaveBeenCalledWith(
|
||||
@@ -194,8 +194,8 @@ describe('Home Assistant API', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle subscription errors', async () => {
|
||||
const callback = jest.fn();
|
||||
test('should handle subscription errors', async () => {
|
||||
const callback = mock();
|
||||
MockWebSocket.mockImplementation(() => {
|
||||
throw new Error('WebSocket connection failed');
|
||||
});
|
||||
@@ -207,14 +207,14 @@ describe('Home Assistant API', () => {
|
||||
});
|
||||
|
||||
describe('WebSocket connection', () => {
|
||||
it('should connect to WebSocket endpoint', async () => {
|
||||
test('should connect to WebSocket endpoint', async () => {
|
||||
await hass.subscribeEvents(() => { });
|
||||
expect(MockWebSocket).toHaveBeenCalledWith(
|
||||
'ws://localhost:8123/api/websocket'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle connection errors', async () => {
|
||||
test('should handle connection errors', async () => {
|
||||
MockWebSocket.mockImplementation(() => {
|
||||
throw new Error('Connection failed');
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ jest.unstable_mockModule('@digital-alchemy/core', () => ({
|
||||
bootstrap: async () => mockInstance,
|
||||
services: {}
|
||||
})),
|
||||
TServiceParams: jest.fn()
|
||||
TServiceParams: mock()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@digital-alchemy/hass', () => ({
|
||||
@@ -78,7 +78,7 @@ describe('Home Assistant Connection', () => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
it('should return a Home Assistant instance with services', async () => {
|
||||
test('should return a Home Assistant instance with services', async () => {
|
||||
const { get_hass } = await import('../../src/hass/index.js');
|
||||
const hass = await get_hass();
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('Home Assistant Connection', () => {
|
||||
expect(typeof hass.services.climate.set_temperature).toBe('function');
|
||||
});
|
||||
|
||||
it('should reuse the same instance on subsequent calls', async () => {
|
||||
test('should reuse the same instance on subsequent calls', async () => {
|
||||
const { get_hass } = await import('../../src/hass/index.js');
|
||||
const firstInstance = await get_hass();
|
||||
const secondInstance = await get_hass();
|
||||
|
||||
@@ -44,19 +44,19 @@ const mockWebSocket: WebSocketMock = {
|
||||
close: jest.fn<WebSocketCloseHandler>(),
|
||||
readyState: 1,
|
||||
OPEN: 1,
|
||||
removeAllListeners: jest.fn()
|
||||
removeAllListeners: mock()
|
||||
};
|
||||
|
||||
jest.mock('ws', () => ({
|
||||
WebSocket: jest.fn().mockImplementation(() => mockWebSocket)
|
||||
// // jest.mock('ws', () => ({
|
||||
WebSocket: mock().mockImplementation(() => mockWebSocket)
|
||||
}));
|
||||
|
||||
// Mock fetch globally
|
||||
const mockFetch = jest.fn() as jest.MockedFunction<typeof fetch>;
|
||||
const mockFetch = mock() as jest.MockedFunction<typeof fetch>;
|
||||
global.fetch = mockFetch;
|
||||
|
||||
// Mock get_hass
|
||||
jest.mock('../../src/hass/index.js', () => {
|
||||
// // jest.mock('../../src/hass/index.js', () => {
|
||||
let instance: TestHassInstance | null = null;
|
||||
const actual = jest.requireActual<typeof import('../../src/hass/index.js')>('../../src/hass/index.js');
|
||||
return {
|
||||
@@ -85,12 +85,12 @@ describe('Home Assistant Integration', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should create a WebSocket client with the provided URL and token', () => {
|
||||
test('should create a WebSocket client with the provided URL and token', () => {
|
||||
expect(client).toBeInstanceOf(EventEmitter);
|
||||
expect(jest.mocked(WebSocket)).toHaveBeenCalledWith(mockUrl);
|
||||
expect(// // jest.mocked(WebSocket)).toHaveBeenCalledWith(mockUrl);
|
||||
});
|
||||
|
||||
it('should connect and authenticate successfully', async () => {
|
||||
test('should connect and authenticate successfully', async () => {
|
||||
const connectPromise = client.connect();
|
||||
|
||||
// Get and call the open callback
|
||||
@@ -114,7 +114,7 @@ describe('Home Assistant Integration', () => {
|
||||
await connectPromise;
|
||||
});
|
||||
|
||||
it('should handle authentication failure', async () => {
|
||||
test('should handle authentication failure', async () => {
|
||||
const connectPromise = client.connect();
|
||||
|
||||
// Get and call the open callback
|
||||
@@ -130,7 +130,7 @@ describe('Home Assistant Integration', () => {
|
||||
await expect(connectPromise).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should handle connection errors', async () => {
|
||||
test('should handle connection errors', async () => {
|
||||
const connectPromise = client.connect();
|
||||
|
||||
// Get and call the error callback
|
||||
@@ -141,7 +141,7 @@ describe('Home Assistant Integration', () => {
|
||||
await expect(connectPromise).rejects.toThrow('Connection failed');
|
||||
});
|
||||
|
||||
it('should handle message parsing errors', async () => {
|
||||
test('should handle message parsing errors', async () => {
|
||||
const connectPromise = client.connect();
|
||||
|
||||
// Get and call the open callback
|
||||
@@ -198,12 +198,12 @@ describe('Home Assistant Integration', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create instance with correct properties', () => {
|
||||
test('should create instance with correct properties', () => {
|
||||
expect(instance['baseUrl']).toBe(mockBaseUrl);
|
||||
expect(instance['token']).toBe(mockToken);
|
||||
});
|
||||
|
||||
it('should fetch states', async () => {
|
||||
test('should fetch states', async () => {
|
||||
const states = await instance.fetchStates();
|
||||
expect(states).toEqual([mockState]);
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
@@ -216,7 +216,7 @@ describe('Home Assistant Integration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch single state', async () => {
|
||||
test('should fetch single state', async () => {
|
||||
const state = await instance.fetchState('light.test');
|
||||
expect(state).toEqual(mockState);
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
@@ -229,7 +229,7 @@ describe('Home Assistant Integration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should call service', async () => {
|
||||
test('should call service', async () => {
|
||||
await instance.callService('light', 'turn_on', { entity_id: 'light.test' });
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
`${mockBaseUrl}/api/services/light/turn_on`,
|
||||
@@ -244,17 +244,17 @@ describe('Home Assistant Integration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle fetch errors', async () => {
|
||||
test('should handle fetch errors', async () => {
|
||||
mockFetch.mockRejectedValueOnce(new Error('Network error'));
|
||||
await expect(instance.fetchStates()).rejects.toThrow('Network error');
|
||||
});
|
||||
|
||||
it('should handle invalid JSON responses', async () => {
|
||||
test('should handle invalid JSON responses', async () => {
|
||||
mockFetch.mockResolvedValueOnce(new Response('invalid json'));
|
||||
await expect(instance.fetchStates()).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should handle non-200 responses', async () => {
|
||||
test('should handle non-200 responses', async () => {
|
||||
mockFetch.mockResolvedValueOnce(new Response('Error', { status: 500 }));
|
||||
await expect(instance.fetchStates()).rejects.toThrow();
|
||||
});
|
||||
@@ -263,15 +263,15 @@ describe('Home Assistant Integration', () => {
|
||||
let eventCallback: (event: HassEvent) => void;
|
||||
|
||||
beforeEach(() => {
|
||||
eventCallback = jest.fn();
|
||||
eventCallback = mock();
|
||||
});
|
||||
|
||||
it('should subscribe to events', async () => {
|
||||
test('should subscribe to events', async () => {
|
||||
const subscriptionId = await instance.subscribeEvents(eventCallback);
|
||||
expect(typeof subscriptionId).toBe('number');
|
||||
});
|
||||
|
||||
it('should unsubscribe from events', async () => {
|
||||
test('should unsubscribe from events', async () => {
|
||||
const subscriptionId = await instance.subscribeEvents(eventCallback);
|
||||
await instance.unsubscribeEvents(subscriptionId);
|
||||
});
|
||||
@@ -309,19 +309,19 @@ describe('Home Assistant Integration', () => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
it('should create instance with default configuration', async () => {
|
||||
test('should create instance with default configuration', async () => {
|
||||
const instance = await get_hass() as TestHassInstance;
|
||||
expect(instance._baseUrl).toBe('http://localhost:8123');
|
||||
expect(instance._token).toBe('test_token');
|
||||
});
|
||||
|
||||
it('should reuse existing instance', async () => {
|
||||
test('should reuse existing instance', async () => {
|
||||
const instance1 = await get_hass();
|
||||
const instance2 = await get_hass();
|
||||
expect(instance1).toBe(instance2);
|
||||
});
|
||||
|
||||
it('should use custom configuration', async () => {
|
||||
test('should use custom configuration', async () => {
|
||||
process.env.HASS_HOST = 'https://hass.example.com';
|
||||
process.env.HASS_TOKEN = 'prod_token';
|
||||
const instance = await get_hass() as TestHassInstance;
|
||||
|
||||
Reference in New Issue
Block a user