elero-usb-transmitter-client 1.0.6 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,182 @@
1
+
2
+ import { UsbTransmitterClient } from '../src/UsbTransmitterClient'
3
+ import { ControlCommand, EasyCommand, InfoData } from '../src/domain/enums'
4
+ import { SerialPort } from 'serialport'
5
+ import { BYTE_HEADER } from '../src/domain/constants'
6
+
7
+ // Mock entire serialport module
8
+ jest.mock('serialport')
9
+
10
+ describe('UsbTransmitterClient (Mocked)', () => {
11
+ let client: UsbTransmitterClient
12
+ let mockSerialPortInstance: any
13
+
14
+ beforeEach(() => {
15
+ // Reset mocks
16
+ jest.clearAllMocks()
17
+
18
+ // Setup mock instance
19
+ mockSerialPortInstance = {
20
+ isOpen: false,
21
+ open: jest.fn((cb) => {
22
+ mockSerialPortInstance.isOpen = true
23
+ if (cb) cb(null)
24
+ }),
25
+ close: jest.fn((cb) => {
26
+ mockSerialPortInstance.isOpen = false
27
+ if (cb) cb(null)
28
+ }),
29
+ write: jest.fn((data, cb) => {
30
+ if (cb) cb(null)
31
+ }),
32
+ flush: jest.fn((cb) => {
33
+ if (cb) cb(null)
34
+ }),
35
+ once: jest.fn(),
36
+ read: jest.fn(),
37
+ pipe: jest.fn(),
38
+ on: jest.fn(),
39
+ removeListener: jest.fn()
40
+ }
41
+
42
+ // When new SerialPort() is called, return our mock instance
43
+ ; (SerialPort as unknown as jest.Mock).mockImplementation(() => mockSerialPortInstance)
44
+
45
+ client = new UsbTransmitterClient('/dev/ttyMOCKED')
46
+ })
47
+
48
+ test('open() should open the serial port', async () => {
49
+ await client.open()
50
+ expect(mockSerialPortInstance.open).toHaveBeenCalled()
51
+ expect(mockSerialPortInstance.flush).toHaveBeenCalled()
52
+ })
53
+
54
+ test('close() should close the serial port', async () => {
55
+ await client.close()
56
+ expect(mockSerialPortInstance.close).toHaveBeenCalled()
57
+ })
58
+
59
+ test('checkChannels() calls correct command and parses response', async () => {
60
+ await client.open()
61
+
62
+ // Simulate "readable" event and data read
63
+ mockSerialPortInstance.once.mockImplementation((event: string, cb: Function) => {
64
+ if (event === 'readable') {
65
+ // Trigger the callback immediately to simulate data ready
66
+ cb()
67
+ }
68
+ })
69
+
70
+ // Mock response for check channels (Head, Len, Cmd, Byte3...CS)
71
+ // Response length check is 6 bytes.
72
+ // Byte 3 is bitmap of active channels (1-8). let's say ch 1 and 2 are active (binary 00000011 = 3)
73
+ const responseBuffer = Buffer.from([
74
+ BYTE_HEADER, // 0xAA
75
+ 0x04, // Length
76
+ EasyCommand.EASY_CHECK,
77
+ 0x00, // High channels (starts at 9)
78
+ 0x03, // Low channels (starts at 1, so 1 & 2)
79
+ 0x00 // Checksum (ignored for now in mock, or we calculate it if logic is strict)
80
+ ])
81
+ // Fix checksum if logic requires it: 256 - sum
82
+ const sum = responseBuffer[0] + responseBuffer[1] + responseBuffer[2] + responseBuffer[3] + responseBuffer[4]
83
+ responseBuffer[5] = (256 - (sum % 256)) % 256
84
+
85
+ mockSerialPortInstance.read.mockReturnValue(responseBuffer)
86
+
87
+ const channels = await client.checkChannels()
88
+ expect(channels).toEqual(expect.arrayContaining([1, 2]))
89
+ expect(mockSerialPortInstance.write).toHaveBeenCalledWith(
90
+ expect.arrayContaining([BYTE_HEADER, 0x02, EasyCommand.EASY_CHECK]),
91
+ expect.any(Function)
92
+ )
93
+ })
94
+
95
+ test('sendControlCommand() sends correct bytes', async () => {
96
+ await client.open()
97
+
98
+ mockSerialPortInstance.once.mockImplementation((event: string, cb: Function) => {
99
+ if (event === 'readable') cb()
100
+ })
101
+
102
+ // Response for Info (length 7)
103
+ // 0: AA, 1: 05, 2: EASY_SEND, 3: high, 4: low, 5: status, 6: CS
104
+ const responseBuffer = Buffer.from([
105
+ BYTE_HEADER,
106
+ 0x05,
107
+ EasyCommand.EASY_SEND,
108
+ 0x00,
109
+ 0x01, // Channel 1 bit mask
110
+ InfoData.INFO_MOVING_DOWN,
111
+ 0x00
112
+ ])
113
+ const sum = responseBuffer.slice(0, 6).reduce((a, b) => a + b, 0)
114
+ responseBuffer[6] = (256 - (sum % 256)) % 256
115
+
116
+ mockSerialPortInstance.read.mockReturnValue(responseBuffer)
117
+
118
+ const response = await client.sendControlCommand(1, ControlCommand.down)
119
+
120
+ expect(response.status).toBe(InfoData.INFO_MOVING_DOWN)
121
+
122
+ // Verify write arguments
123
+ // Data: [AA, 05, EASY_SEND, high, low, cmd, CS]
124
+ // Channel 1 -> low=1, high=0
125
+ expect(mockSerialPortInstance.write).toHaveBeenCalledWith(
126
+ expect.arrayContaining([
127
+ BYTE_HEADER,
128
+ 0x05,
129
+ EasyCommand.EASY_SEND,
130
+ 0,
131
+ 1,
132
+ ControlCommand.down
133
+ ]),
134
+ expect.any(Function)
135
+ )
136
+ })
137
+ test('getInfo() should handle fragmented packets (reproduction fix)', async () => {
138
+ await client.open()
139
+
140
+ let readableCallback: Function | null = null;
141
+ mockSerialPortInstance.on.mockImplementation((event: string, cb: Function) => {
142
+ if (event === 'readable') {
143
+ readableCallback = cb
144
+ }
145
+ })
146
+
147
+ let readCallCount = 0
148
+ mockSerialPortInstance.read.mockImplementation((len: number) => {
149
+ readCallCount++
150
+ if (readCallCount === 1) {
151
+ return null // Not enough data yet
152
+ }
153
+ // Return dummy response buffer for getInfo call
154
+ const responseBuffer = Buffer.from([
155
+ BYTE_HEADER,
156
+ 0x05,
157
+ EasyCommand.EASY_SEND,
158
+ 0x00,
159
+ 0x01,
160
+ InfoData.INFO_MOVING_DOWN,
161
+ 0x00
162
+ ])
163
+ const sum = responseBuffer.slice(0, 6).reduce((a, b) => a + b, 0)
164
+ responseBuffer[6] = (256 - (sum % 256)) % 256
165
+ return responseBuffer
166
+ })
167
+
168
+ const infoPromise = client.getInfo(1)
169
+
170
+ // Wait a tick to ensure tryRead() ran once and failed
171
+ await new Promise(r => process.nextTick(r))
172
+
173
+ // Now trigger readable event again (simulation of second packet arriving)
174
+ if (readableCallback) {
175
+ (readableCallback as Function)()
176
+ }
177
+
178
+ const response = await infoPromise
179
+ expect(response.status).toBe(InfoData.INFO_MOVING_DOWN)
180
+ expect(readCallCount).toBeGreaterThanOrEqual(2)
181
+ })
182
+ })
@@ -1,31 +0,0 @@
1
- import { UsbTransmitterClient } from "../src/UsbTransmitterClient"
2
- import { ControlCommand, EasyCommand, InfoData } from "../src/domain/enums"
3
-
4
- jest.setTimeout(200000)
5
-
6
- const client = new UsbTransmitterClient('/dev/ttyUSB0')
7
-
8
- const aktiveChannelsForTest = [1, 2]
9
-
10
- test('checkChannels', async () => {
11
- await client.open()
12
- const channels = await client.checkChannels()
13
- expect(channels).toEqual(aktiveChannelsForTest)
14
- await client.close()
15
- })
16
-
17
- test('getInfo', async () => {
18
- await client.open()
19
- const response = await client.getInfo(1)
20
- console.log(response)
21
- expect(response.command).toEqual(EasyCommand.EASY_ACK)
22
- await client.close()
23
- })
24
-
25
- test('sendControlCommand', async () => {
26
- await client.open()
27
- const response = await client.sendControlCommand(1, ControlCommand.down)
28
- console.log(response)
29
- expect(response.status).toEqual(InfoData.INFO_MOVING_DOWN)
30
- await client.close()
31
- })