Skip to content

Commit 7a0651e

Browse files
Merge pull request #4 from recursivefunk/jra/tests
sensor tests
2 parents 901873b + a010efa commit 7a0651e

File tree

2 files changed

+64
-24
lines changed

2 files changed

+64
-24
lines changed

src/lib/sensor.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const hueMotionPollingInterval = env.num('HUE_MOTION_POLLING_INTERVAL', 2) * 100
1313
* accordingly. Return an EventEmitter which will be the channel over which the client is notifed of state changes.
1414
*/
1515
export default class Sensor extends EventEmitter {
16-
constructor ({ bridgeIp, sensorId, username }) {
16+
constructor ({ bridgeIp, sensorId, username, setIntervalFn }) {
1717
super();
1818
EventEmitter.call(this);
1919
this._sensorId = sensorId;
@@ -23,18 +23,20 @@ export default class Sensor extends EventEmitter {
2323
this._user = this._bridge.user(this._username);
2424
this._lastKnownState = null;
2525
this._lastMotionStop = null;
26+
this._setInterval = setIntervalFn || setInterval;
2627
}
2728

2829
async monitor () {
2930
const tempSensor = await this._user.getSensor(this._sensorId);
3031
this._lastKnownState = await tempSensor.state.presence;
3132

32-
setInterval(async () => {
33+
this._setInterval(async () => {
3334
let sensor;
3435
try {
3536
sensor = await this._user.getSensor(this._sensorId);
3637
} catch (err) {
3738
this.emit('error', err);
39+
return;
3840
}
3941
const updatedState = sensor.state.presence;
4042

test/lib/sensor.test.js

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ jest.mock('good-env', () => ({
77
if (key === 'HUE_MOTION_STOP_BUFFER') return 90;
88
if (key === 'HUE_MOTION_POLLING_INTERVAL') return 2;
99
return defaultValue;
10+
}),
11+
get: jest.fn().mockImplementation((key) => {
12+
if (key === 'NODE_ENV') return 'TEST';
13+
return undefined;
1014
})
1115
}));
1216

@@ -23,9 +27,15 @@ const mockBridge = jest.fn().mockReturnValue({
2327
user: mockUser
2428
});
2529

26-
jest.mock('jshue', () => ({
27-
bridge: mockBridge
28-
}));
30+
const mockBridgeFn = jest.fn().mockReturnValue({
31+
user: mockUser
32+
});
33+
34+
jest.mock('jshue', () => {
35+
return jest.fn().mockReturnValue({
36+
bridge: mockBridgeFn
37+
});
38+
});
2939

3040
// Mock log
3141
jest.mock('../../src/lib/log.js', () => ({
@@ -36,24 +46,46 @@ jest.mock('../../src/lib/log.js', () => ({
3646
import env from 'good-env';
3747
import jsHue from 'jshue';
3848
import log from '../../src/lib/log.js';
39-
import Sensor from '../../src/lib/sensor.js';
4049

41-
describe.skip('sensor.js', () => {
50+
describe('sensor.js', () => {
4251
let sensor;
52+
let Sensor;
4353
const mockConfig = {
4454
bridgeIp: '192.168.1.100',
4555
sensorId: '123',
4656
username: 'testuser'
4757
};
48-
49-
beforeEach(() => {
58+
59+
// Synchronous setInterval mock for testing
60+
function createManualInterval() {
61+
let callback;
62+
return {
63+
setInterval: (fn, interval) => {
64+
callback = fn;
65+
return 1;
66+
},
67+
tick: async () => {
68+
if (callback) await callback();
69+
}
70+
};
71+
}
72+
73+
let manualInterval;
74+
75+
// Helper to flush all pending promises
76+
async function flushPromises() {
77+
for (let i = 0; i < 5; i++) {
78+
await Promise.resolve();
79+
await new Promise(r => setImmediate(r));
80+
}
81+
}
82+
83+
beforeEach(async () => {
5084
jest.clearAllMocks();
51-
jest.useFakeTimers();
52-
sensor = new Sensor(mockConfig);
53-
});
54-
55-
afterEach(() => {
56-
jest.useRealTimers();
85+
jest.resetModules();
86+
Sensor = (await import('../../src/lib/sensor.js')).default;
87+
manualInterval = createManualInterval();
88+
sensor = new Sensor({ ...mockConfig, setIntervalFn: manualInterval.setInterval });
5789
});
5890

5991
describe('constructor', () => {
@@ -66,20 +98,21 @@ describe.skip('sensor.js', () => {
6698
});
6799

68100
it('should set up bridge and user correctly', () => {
69-
expect(mockBridge).toHaveBeenCalledWith('192.168.1.100');
101+
expect(mockBridgeFn).toHaveBeenCalledWith('192.168.1.100');
70102
expect(mockUser).toHaveBeenCalledWith('testuser');
71103
});
72104
});
73105

74106
describe('monitor', () => {
75107
it('should start monitoring the sensor', async () => {
76108
const monitorPromise = sensor.monitor();
77-
expect(sensor._lastKnownState).toBe(false);
78109
await monitorPromise;
110+
expect(sensor._lastKnownState).toBe(false);
79111
expect(mockGetSensor).toHaveBeenCalledWith('123');
80112
});
81113

82114
it('should emit motion_start when presence changes to true', async () => {
115+
jest.setTimeout(10000);
83116
const motionStartSpy = jest.fn();
84117
sensor.on('motion_start', motionStartSpy);
85118

@@ -94,8 +127,8 @@ describe.skip('sensor.js', () => {
94127
});
95128

96129
await sensor.monitor();
97-
jest.advanceTimersByTime(2000); // Advance past polling interval
98-
130+
await manualInterval.tick();
131+
await flushPromises();
99132
expect(motionStartSpy).toHaveBeenCalled();
100133
});
101134

@@ -114,21 +147,26 @@ describe.skip('sensor.js', () => {
114147
});
115148

116149
await sensor.monitor();
117-
jest.advanceTimersByTime(2000); // Advance past polling interval
118-
jest.advanceTimersByTime(90000); // Advance past buffer period
119-
150+
await manualInterval.tick(); // First interval: presence changes to false
151+
// Simulate time passage for buffer
152+
sensor._lastMotionStop = Date.now() - 90000;
153+
await manualInterval.tick(); // Second interval: should emit motion_stop
154+
await flushPromises();
120155
expect(motionStopSpy).toHaveBeenCalled();
121156
});
122157

123158
it('should emit error when sensor polling fails', async () => {
124159
const errorSpy = jest.fn();
125160
sensor.on('error', errorSpy);
126161

162+
mockGetSensor.mockResolvedValueOnce({
163+
state: { presence: false }
164+
});
127165
mockGetSensor.mockRejectedValueOnce(new Error('Test error'));
128166

129167
await sensor.monitor();
130-
jest.advanceTimersByTime(2000); // Advance past polling interval
131-
168+
await manualInterval.tick();
169+
await flushPromises();
132170
expect(errorSpy).toHaveBeenCalledWith(expect.any(Error));
133171
});
134172
});

0 commit comments

Comments
 (0)