agent-messenger 2.18.0 → 2.19.1

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.
Files changed (79) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bunfig.toml +1 -0
  3. package/dist/package.json +1 -1
  4. package/dist/src/platforms/discordbot/client.d.ts +1 -0
  5. package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
  6. package/dist/src/platforms/discordbot/client.js +3 -0
  7. package/dist/src/platforms/discordbot/client.js.map +1 -1
  8. package/dist/src/platforms/discordbot/commands/message.d.ts +1 -0
  9. package/dist/src/platforms/discordbot/commands/message.d.ts.map +1 -1
  10. package/dist/src/platforms/discordbot/commands/message.js +2 -0
  11. package/dist/src/platforms/discordbot/commands/message.js.map +1 -1
  12. package/dist/src/platforms/kakaotalk/client.d.ts +4 -2
  13. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  14. package/dist/src/platforms/kakaotalk/client.js +67 -3
  15. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  16. package/dist/src/platforms/kakaotalk/commands/message.d.ts.map +1 -1
  17. package/dist/src/platforms/kakaotalk/commands/message.js +23 -1
  18. package/dist/src/platforms/kakaotalk/commands/message.js.map +1 -1
  19. package/dist/src/platforms/kakaotalk/index.d.ts +1 -1
  20. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  21. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  22. package/dist/src/platforms/kakaotalk/protocol/session.d.ts +2 -1
  23. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  24. package/dist/src/platforms/kakaotalk/protocol/session.js +15 -0
  25. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  26. package/dist/src/platforms/kakaotalk/types.d.ts +18 -0
  27. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  28. package/dist/src/platforms/kakaotalk/types.js +1 -0
  29. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  30. package/dist/src/shared/chromium/browsers.js +1 -1
  31. package/dist/src/shared/chromium/browsers.js.map +1 -1
  32. package/docs/content/docs/cli/discordbot.mdx +6 -0
  33. package/docs/content/docs/sdk/discordbot.mdx +4 -0
  34. package/package.json +1 -1
  35. package/skills/agent-channeltalk/SKILL.md +1 -1
  36. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  37. package/skills/agent-discord/SKILL.md +1 -1
  38. package/skills/agent-discordbot/SKILL.md +9 -1
  39. package/skills/agent-instagram/SKILL.md +1 -1
  40. package/skills/agent-kakaotalk/SKILL.md +24 -1
  41. package/skills/agent-line/SKILL.md +1 -1
  42. package/skills/agent-slack/SKILL.md +1 -1
  43. package/skills/agent-slackbot/SKILL.md +1 -1
  44. package/skills/agent-teams/SKILL.md +1 -1
  45. package/skills/agent-telegram/SKILL.md +1 -1
  46. package/skills/agent-telegrambot/SKILL.md +1 -1
  47. package/skills/agent-webex/SKILL.md +1 -1
  48. package/skills/agent-wechatbot/SKILL.md +1 -1
  49. package/skills/agent-whatsapp/SKILL.md +1 -1
  50. package/skills/agent-whatsappbot/SKILL.md +1 -1
  51. package/src/platforms/discord/commands/dm.test.ts +28 -20
  52. package/src/platforms/discord/commands/reaction.test.ts +12 -7
  53. package/src/platforms/discordbot/client.test.ts +17 -0
  54. package/src/platforms/discordbot/client.ts +9 -2
  55. package/src/platforms/discordbot/commands/message.test.ts +28 -11
  56. package/src/platforms/discordbot/commands/message.ts +4 -2
  57. package/src/platforms/instagram/commands/auth.test.ts +11 -9
  58. package/src/platforms/instagram/commands/chat.test.ts +8 -6
  59. package/src/platforms/instagram/commands/message.test.ts +8 -6
  60. package/src/platforms/kakaotalk/client.test.ts +96 -0
  61. package/src/platforms/kakaotalk/client.ts +82 -3
  62. package/src/platforms/kakaotalk/commands/message.test.ts +42 -0
  63. package/src/platforms/kakaotalk/commands/message.ts +33 -2
  64. package/src/platforms/kakaotalk/index.ts +2 -0
  65. package/src/platforms/kakaotalk/protocol/session.ts +15 -1
  66. package/src/platforms/kakaotalk/types.ts +24 -0
  67. package/src/platforms/webex/commands/auth.test.ts +30 -14
  68. package/src/platforms/webex/commands/member.test.ts +30 -34
  69. package/src/platforms/webex/commands/message.test.ts +37 -48
  70. package/src/platforms/webex/commands/snapshot.test.ts +26 -36
  71. package/src/platforms/webex/commands/space.test.ts +32 -38
  72. package/src/platforms/webex/commands/whoami.test.ts +10 -22
  73. package/src/platforms/webex/credential-manager.test.ts +3 -0
  74. package/src/platforms/whatsapp/commands/auth.test.ts +14 -20
  75. package/src/platforms/whatsapp/commands/chat.test.ts +17 -24
  76. package/src/platforms/whatsapp/commands/message.test.ts +31 -41
  77. package/src/shared/chromium/browsers.ts +1 -1
  78. package/src/test-setup.ts +5 -0
  79. package/tsconfig.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, expect, spyOn, it } from 'bun:test'
2
2
 
3
- import * as clientModule from '../client'
3
+ import { WebexClient } from '../client'
4
4
  import { WebexError } from '../types'
5
5
  import { whoamiCommand } from './whoami'
6
6
 
@@ -17,27 +17,23 @@ const mockUser = {
17
17
  created: '2024-01-01T00:00:00.000Z',
18
18
  }
19
19
 
20
- const makeFakeClient = () => ({
21
- login: async function (this: unknown) {
22
- return this
23
- },
24
- testAuth: async () => mockUser,
25
- })
26
-
27
- let webexClientSpy: ReturnType<typeof spyOn>
20
+ let loginSpy: ReturnType<typeof spyOn>
21
+ let testAuthSpy: ReturnType<typeof spyOn>
28
22
  let consoleLogSpy: ReturnType<typeof spyOn>
29
23
  let processExitSpy: ReturnType<typeof spyOn>
30
24
 
31
25
  beforeEach(() => {
32
- webexClientSpy = spyOn(clientModule, 'WebexClient').mockImplementation(
33
- makeFakeClient as unknown as typeof clientModule.WebexClient,
34
- )
26
+ loginSpy = spyOn(WebexClient.prototype, 'login').mockImplementation(async function (this: WebexClient) {
27
+ return this
28
+ })
29
+ testAuthSpy = spyOn(WebexClient.prototype, 'testAuth').mockResolvedValue(mockUser)
35
30
  consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
36
31
  processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => undefined as never)
37
32
  })
38
33
 
39
34
  afterEach(() => {
40
- webexClientSpy?.mockRestore()
35
+ loginSpy?.mockRestore()
36
+ testAuthSpy?.mockRestore()
41
37
  consoleLogSpy?.mockRestore()
42
38
  processExitSpy?.mockRestore()
43
39
  })
@@ -102,15 +98,7 @@ it('whoami outputs pretty-printed JSON when --pretty flag is passed', async () =
102
98
 
103
99
  it('whoami exits with code 1 when not authenticated', async () => {
104
100
  // given: no credentials
105
- webexClientSpy.mockImplementation(
106
- () =>
107
- ({
108
- login: async () => {
109
- throw new WebexError('No Webex credentials found.', 'no_credentials')
110
- },
111
- testAuth: async () => mockUser,
112
- }) as unknown as clientModule.WebexClient,
113
- )
101
+ loginSpy.mockRejectedValue(new WebexError('No Webex credentials found.', 'no_credentials'))
114
102
 
115
103
  // when: running whoami
116
104
  await whoamiCommand.parseAsync([], { from: 'user' })
@@ -8,6 +8,7 @@ import { WebexCredentialManager } from './credential-manager'
8
8
  describe('WebexCredentialManager', () => {
9
9
  let tempDir: string
10
10
  let credManager: WebexCredentialManager
11
+ const realFetch = globalThis.fetch
11
12
 
12
13
  beforeEach(async () => {
13
14
  tempDir = await mkdtemp(join(tmpdir(), 'webex-cred-test-'))
@@ -16,6 +17,8 @@ describe('WebexCredentialManager', () => {
16
17
 
17
18
  afterEach(async () => {
18
19
  await rm(tempDir, { recursive: true, force: true })
20
+ // Guarantee fetch restoration even if a test throws before its own restore line.
21
+ globalThis.fetch = realFetch
19
22
  })
20
23
 
21
24
  it('loadConfig returns null when no file exists', async () => {
@@ -2,12 +2,6 @@ import { afterEach, beforeEach, describe, expect, mock, spyOn, it } from 'bun:te
2
2
 
3
3
  const originalConsoleLog = console.log
4
4
 
5
- mock.module('@/shared/utils/error-handler', () => ({
6
- handleError: (err: Error) => {
7
- throw err
8
- },
9
- }))
10
-
11
5
  const mockGetAccount = mock(() => Promise.resolve(null))
12
6
  const mockListAccounts = mock(() => Promise.resolve([]))
13
7
  const mockSetCurrent = mock(() => Promise.resolve(false))
@@ -46,6 +40,7 @@ import { authCommand } from './auth'
46
40
 
47
41
  describe('auth commands', () => {
48
42
  let consoleLogSpy: ReturnType<typeof mock>
43
+ let consoleErrorSpy: ReturnType<typeof spyOn>
49
44
  let processExitSpy: ReturnType<typeof spyOn>
50
45
 
51
46
  beforeEach(() => {
@@ -73,14 +68,14 @@ describe('auth commands', () => {
73
68
 
74
69
  consoleLogSpy = mock((..._args: unknown[]) => {})
75
70
  console.log = consoleLogSpy
76
- processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
77
- throw new Error(`process.exit(${_code})`)
78
- })
71
+ consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
72
+ processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => undefined as never)
79
73
  processExitSpy.mockClear()
80
74
  })
81
75
 
82
76
  afterEach(() => {
83
77
  console.log = originalConsoleLog
78
+ consoleErrorSpy.mockRestore()
84
79
  processExitSpy.mockRestore()
85
80
  })
86
81
 
@@ -153,7 +148,7 @@ describe('auth commands', () => {
153
148
  it('outputs error and exits when account not found', async () => {
154
149
  mockSetCurrent.mockImplementation(() => Promise.resolve(false))
155
150
 
156
- await expect(authCommand.parseAsync(['use', 'nonexistent'], { from: 'user' })).rejects.toThrow('process.exit(1)')
151
+ await authCommand.parseAsync(['use', 'nonexistent'], { from: 'user' })
157
152
 
158
153
  expect(processExitSpy).toHaveBeenCalledWith(1)
159
154
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
@@ -185,7 +180,7 @@ describe('auth commands', () => {
185
180
  it('outputs error and exits when no account configured', async () => {
186
181
  mockGetAccount.mockImplementation(() => Promise.resolve(null))
187
182
 
188
- await expect(authCommand.parseAsync(['status'], { from: 'user' })).rejects.toThrow('process.exit(1)')
183
+ await authCommand.parseAsync(['status'], { from: 'user' })
189
184
 
190
185
  expect(processExitSpy).toHaveBeenCalledWith(1)
191
186
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
@@ -217,9 +212,7 @@ describe('auth commands', () => {
217
212
  it('outputs error for specific missing account', async () => {
218
213
  mockGetAccount.mockImplementation(() => Promise.resolve(null))
219
214
 
220
- await expect(authCommand.parseAsync(['status', '--account', 'missing-id'], { from: 'user' })).rejects.toThrow(
221
- 'process.exit(1)',
222
- )
215
+ await authCommand.parseAsync(['status', '--account', 'missing-id'], { from: 'user' })
223
216
 
224
217
  expect(processExitSpy).toHaveBeenCalledWith(1)
225
218
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
@@ -241,8 +234,9 @@ describe('auth commands', () => {
241
234
  )
242
235
  mockRemoveAccount.mockImplementation(() => Promise.resolve(true))
243
236
 
244
- await expect(authCommand.parseAsync(['logout'], { from: 'user' })).rejects.toThrow('process.exit(0)')
237
+ await authCommand.parseAsync(['logout'], { from: 'user' })
245
238
 
239
+ expect(processExitSpy).toHaveBeenCalledWith(0)
246
240
  expect(mockRemoveAccount).toHaveBeenCalledWith('plus-12025551234')
247
241
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
248
242
  expect(output.success).toBe(true)
@@ -253,7 +247,7 @@ describe('auth commands', () => {
253
247
  it('outputs error and exits when no account configured', async () => {
254
248
  mockGetAccount.mockImplementation(() => Promise.resolve(null))
255
249
 
256
- await expect(authCommand.parseAsync(['logout'], { from: 'user' })).rejects.toThrow('process.exit(1)')
250
+ await authCommand.parseAsync(['logout'], { from: 'user' })
257
251
 
258
252
  expect(processExitSpy).toHaveBeenCalledWith(1)
259
253
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
@@ -273,8 +267,9 @@ describe('auth commands', () => {
273
267
  mockConnect.mockImplementation(() => Promise.reject(new Error('Connection failed')))
274
268
  mockRemoveAccount.mockImplementation(() => Promise.resolve(true))
275
269
 
276
- await expect(authCommand.parseAsync(['logout'], { from: 'user' })).rejects.toThrow('process.exit(0)')
270
+ await authCommand.parseAsync(['logout'], { from: 'user' })
277
271
 
272
+ expect(processExitSpy).toHaveBeenCalledWith(0)
278
273
  expect(mockRemoveAccount).toHaveBeenCalledWith('plus-12025551234')
279
274
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
280
275
  expect(output.success).toBe(true)
@@ -295,10 +290,9 @@ describe('auth commands', () => {
295
290
  })
296
291
  mockRemoveAccount.mockImplementation(() => Promise.resolve(true))
297
292
 
298
- await expect(
299
- authCommand.parseAsync(['logout', '--account', 'plus-19995551234'], { from: 'user' }),
300
- ).rejects.toThrow('process.exit(0)')
293
+ await authCommand.parseAsync(['logout', '--account', 'plus-19995551234'], { from: 'user' })
301
294
 
295
+ expect(processExitSpy).toHaveBeenCalledWith(0)
302
296
  expect(mockRemoveAccount).toHaveBeenCalledWith('plus-19995551234')
303
297
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
304
298
  expect(output.success).toBe(true)
@@ -2,12 +2,6 @@ import { afterEach, beforeEach, describe, expect, mock, spyOn, it } from 'bun:te
2
2
 
3
3
  const originalConsoleLog = console.log
4
4
 
5
- mock.module('@/shared/utils/error-handler', () => ({
6
- handleError: (err: Error) => {
7
- throw err
8
- },
9
- }))
10
-
11
5
  const mockGetAccount = mock(() =>
12
6
  Promise.resolve({
13
7
  account_id: 'plus-12025551234',
@@ -69,6 +63,7 @@ import { chatCommand } from './chat'
69
63
 
70
64
  describe('chat commands', () => {
71
65
  let consoleLogSpy: ReturnType<typeof mock>
66
+ let consoleErrorSpy: ReturnType<typeof spyOn>
72
67
  let processExitSpy: ReturnType<typeof spyOn>
73
68
 
74
69
  beforeEach(() => {
@@ -118,21 +113,22 @@ describe('chat commands', () => {
118
113
 
119
114
  consoleLogSpy = mock((..._args: unknown[]) => {})
120
115
  console.log = consoleLogSpy
121
- processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
122
- throw new Error(`process.exit(${_code})`)
123
- })
116
+ consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
117
+ processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => undefined as never)
124
118
  processExitSpy.mockClear()
125
119
  })
126
120
 
127
121
  afterEach(() => {
128
122
  console.log = originalConsoleLog
123
+ consoleErrorSpy.mockRestore()
129
124
  processExitSpy.mockRestore()
130
125
  })
131
126
 
132
127
  describe('list', () => {
133
128
  it('lists chats with default limit', async () => {
134
- await expect(chatCommand.parseAsync(['list'], { from: 'user' })).rejects.toThrow('process.exit(0)')
129
+ await chatCommand.parseAsync(['list'], { from: 'user' })
135
130
 
131
+ expect(processExitSpy).toHaveBeenCalledWith(0)
136
132
  expect(mockListChats).toHaveBeenCalledWith(20)
137
133
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
138
134
  expect(output).toHaveLength(1)
@@ -141,25 +137,23 @@ describe('chat commands', () => {
141
137
  })
142
138
 
143
139
  it('respects --limit option', async () => {
144
- await expect(chatCommand.parseAsync(['list', '--limit', '5'], { from: 'user' })).rejects.toThrow(
145
- 'process.exit(0)',
146
- )
140
+ await chatCommand.parseAsync(['list', '--limit', '5'], { from: 'user' })
147
141
 
142
+ expect(processExitSpy).toHaveBeenCalledWith(0)
148
143
  expect(mockListChats).toHaveBeenCalledWith(5)
149
144
  })
150
145
 
151
146
  it('passes account option to credential manager', async () => {
152
- await expect(chatCommand.parseAsync(['list', '--account', 'my-account'], { from: 'user' })).rejects.toThrow(
153
- 'process.exit(0)',
154
- )
147
+ await chatCommand.parseAsync(['list', '--account', 'my-account'], { from: 'user' })
155
148
 
149
+ expect(processExitSpy).toHaveBeenCalledWith(0)
156
150
  expect(mockGetAccount).toHaveBeenCalledWith('my-account')
157
151
  })
158
152
 
159
153
  it('exits with error when no account configured', async () => {
160
154
  mockGetAccount.mockImplementation(() => Promise.resolve(null))
161
155
 
162
- await expect(chatCommand.parseAsync(['list'], { from: 'user' })).rejects.toThrow('process.exit(1)')
156
+ await chatCommand.parseAsync(['list'], { from: 'user' })
163
157
 
164
158
  expect(processExitSpy).toHaveBeenCalledWith(1)
165
159
  })
@@ -167,8 +161,9 @@ describe('chat commands', () => {
167
161
 
168
162
  describe('search', () => {
169
163
  it('searches chats by query', async () => {
170
- await expect(chatCommand.parseAsync(['search', 'Bob'], { from: 'user' })).rejects.toThrow('process.exit(0)')
164
+ await chatCommand.parseAsync(['search', 'Bob'], { from: 'user' })
171
165
 
166
+ expect(processExitSpy).toHaveBeenCalledWith(0)
172
167
  expect(mockSearchChats).toHaveBeenCalledWith('Bob', 20)
173
168
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
174
169
  expect(output).toHaveLength(1)
@@ -177,18 +172,16 @@ describe('chat commands', () => {
177
172
  })
178
173
 
179
174
  it('respects --limit option', async () => {
180
- await expect(chatCommand.parseAsync(['search', 'Alice', '--limit', '3'], { from: 'user' })).rejects.toThrow(
181
- 'process.exit(0)',
182
- )
175
+ await chatCommand.parseAsync(['search', 'Alice', '--limit', '3'], { from: 'user' })
183
176
 
177
+ expect(processExitSpy).toHaveBeenCalledWith(0)
184
178
  expect(mockSearchChats).toHaveBeenCalledWith('Alice', 3)
185
179
  })
186
180
 
187
181
  it('passes account option to credential manager', async () => {
188
- await expect(
189
- chatCommand.parseAsync(['search', 'test', '--account', 'my-account'], { from: 'user' }),
190
- ).rejects.toThrow('process.exit(0)')
182
+ await chatCommand.parseAsync(['search', 'test', '--account', 'my-account'], { from: 'user' })
191
183
 
184
+ expect(processExitSpy).toHaveBeenCalledWith(0)
192
185
  expect(mockGetAccount).toHaveBeenCalledWith('my-account')
193
186
  })
194
187
  })
@@ -2,12 +2,6 @@ import { afterEach, beforeEach, describe, expect, mock, spyOn, it } from 'bun:te
2
2
 
3
3
  const originalConsoleLog = console.log
4
4
 
5
- mock.module('@/shared/utils/error-handler', () => ({
6
- handleError: (err: Error) => {
7
- throw err
8
- },
9
- }))
10
-
11
5
  const mockGetAccount = mock(() =>
12
6
  Promise.resolve({
13
7
  account_id: 'plus-12025551234',
@@ -69,6 +63,7 @@ import { messageCommand } from './message'
69
63
 
70
64
  describe('message commands', () => {
71
65
  let consoleLogSpy: ReturnType<typeof mock>
66
+ let consoleErrorSpy: ReturnType<typeof spyOn>
72
67
  let processExitSpy: ReturnType<typeof spyOn>
73
68
 
74
69
  beforeEach(() => {
@@ -118,23 +113,22 @@ describe('message commands', () => {
118
113
 
119
114
  consoleLogSpy = mock((..._args: unknown[]) => {})
120
115
  console.log = consoleLogSpy
121
- processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
122
- throw new Error(`process.exit(${_code})`)
123
- })
116
+ consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
117
+ processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => undefined as never)
124
118
  processExitSpy.mockClear()
125
119
  })
126
120
 
127
121
  afterEach(() => {
128
122
  console.log = originalConsoleLog
123
+ consoleErrorSpy.mockRestore()
129
124
  processExitSpy.mockRestore()
130
125
  })
131
126
 
132
127
  describe('list', () => {
133
128
  it('fetches messages for a chat', async () => {
134
- await expect(messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net'], { from: 'user' })).rejects.toThrow(
135
- 'process.exit(0)',
136
- )
129
+ await messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net'], { from: 'user' })
137
130
 
131
+ expect(processExitSpy).toHaveBeenCalledWith(0)
138
132
  expect(mockGetMessages).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 25)
139
133
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
140
134
  expect(output).toHaveLength(1)
@@ -143,27 +137,25 @@ describe('message commands', () => {
143
137
  })
144
138
 
145
139
  it('respects --limit option', async () => {
146
- await expect(
147
- messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net', '--limit', '10'], { from: 'user' }),
148
- ).rejects.toThrow('process.exit(0)')
140
+ await messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net', '--limit', '10'], { from: 'user' })
149
141
 
142
+ expect(processExitSpy).toHaveBeenCalledWith(0)
150
143
  expect(mockGetMessages).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 10)
151
144
  })
152
145
 
153
146
  it('passes account option to credential manager', async () => {
154
- await expect(
155
- messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net', '--account', 'my-account'], { from: 'user' }),
156
- ).rejects.toThrow('process.exit(0)')
147
+ await messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net', '--account', 'my-account'], {
148
+ from: 'user',
149
+ })
157
150
 
151
+ expect(processExitSpy).toHaveBeenCalledWith(0)
158
152
  expect(mockGetAccount).toHaveBeenCalledWith('my-account')
159
153
  })
160
154
 
161
155
  it('exits with error when no account configured', async () => {
162
156
  mockGetAccount.mockImplementation(() => Promise.resolve(null))
163
157
 
164
- await expect(messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net'], { from: 'user' })).rejects.toThrow(
165
- 'process.exit(1)',
166
- )
158
+ await messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net'], { from: 'user' })
167
159
 
168
160
  expect(processExitSpy).toHaveBeenCalledWith(1)
169
161
  })
@@ -171,10 +163,9 @@ describe('message commands', () => {
171
163
 
172
164
  describe('send', () => {
173
165
  it('sends a message to a chat', async () => {
174
- await expect(
175
- messageCommand.parseAsync(['send', '12025551234@s.whatsapp.net', 'Hello world'], { from: 'user' }),
176
- ).rejects.toThrow('process.exit(0)')
166
+ await messageCommand.parseAsync(['send', '12025551234@s.whatsapp.net', 'Hello world'], { from: 'user' })
177
167
 
168
+ expect(processExitSpy).toHaveBeenCalledWith(0)
178
169
  expect(mockSendMessage).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'Hello world')
179
170
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
180
171
  expect(output.id).toBe('msg-2')
@@ -182,22 +173,20 @@ describe('message commands', () => {
182
173
  })
183
174
 
184
175
  it('passes account option to credential manager', async () => {
185
- await expect(
186
- messageCommand.parseAsync(['send', '12025551234@s.whatsapp.net', 'Hi', '--account', 'my-account'], {
187
- from: 'user',
188
- }),
189
- ).rejects.toThrow('process.exit(0)')
176
+ await messageCommand.parseAsync(['send', '12025551234@s.whatsapp.net', 'Hi', '--account', 'my-account'], {
177
+ from: 'user',
178
+ })
190
179
 
180
+ expect(processExitSpy).toHaveBeenCalledWith(0)
191
181
  expect(mockGetAccount).toHaveBeenCalledWith('my-account')
192
182
  })
193
183
  })
194
184
 
195
185
  describe('react', () => {
196
186
  it('sends a reaction to a message', async () => {
197
- await expect(
198
- messageCommand.parseAsync(['react', '12025551234@s.whatsapp.net', 'msg-1', '👍'], { from: 'user' }),
199
- ).rejects.toThrow('process.exit(0)')
187
+ await messageCommand.parseAsync(['react', '12025551234@s.whatsapp.net', 'msg-1', '👍'], { from: 'user' })
200
188
 
189
+ expect(processExitSpy).toHaveBeenCalledWith(0)
201
190
  expect(mockSendReaction).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'msg-1', '👍', undefined)
202
191
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
203
192
  expect(output.success).toBe(true)
@@ -207,22 +196,23 @@ describe('message commands', () => {
207
196
  })
208
197
 
209
198
  it('passes --from-me flag to sendReaction', async () => {
210
- await expect(
211
- messageCommand.parseAsync(['react', '12025551234@s.whatsapp.net', 'msg-1', '❤️', '--from-me'], {
212
- from: 'user',
213
- }),
214
- ).rejects.toThrow('process.exit(0)')
199
+ await messageCommand.parseAsync(['react', '12025551234@s.whatsapp.net', 'msg-1', '❤️', '--from-me'], {
200
+ from: 'user',
201
+ })
215
202
 
203
+ expect(processExitSpy).toHaveBeenCalledWith(0)
216
204
  expect(mockSendReaction).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'msg-1', '❤️', true)
217
205
  })
218
206
 
219
207
  it('passes account option to credential manager', async () => {
220
- await expect(
221
- messageCommand.parseAsync(['react', '12025551234@s.whatsapp.net', 'msg-1', '👍', '--account', 'my-account'], {
208
+ await messageCommand.parseAsync(
209
+ ['react', '12025551234@s.whatsapp.net', 'msg-1', '👍', '--account', 'my-account'],
210
+ {
222
211
  from: 'user',
223
- }),
224
- ).rejects.toThrow('process.exit(0)')
212
+ },
213
+ )
225
214
 
215
+ expect(processExitSpy).toHaveBeenCalledWith(0)
226
216
  expect(mockGetAccount).toHaveBeenCalledWith('my-account')
227
217
  })
228
218
  })
@@ -75,7 +75,7 @@ export function discoverBrowserProfileDirs(browserBase: string): string[] {
75
75
  dirs.push(join(browserBase, 'Default'))
76
76
  if (!existsSync(browserBase)) return dirs
77
77
  try {
78
- const entries = readdirSync(browserBase, { withFileTypes: true })
78
+ const entries = readdirSync(browserBase, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))
79
79
  for (const entry of entries) {
80
80
  if (!entry.isDirectory()) continue
81
81
  if (!/^Profile \d+$/i.test(entry.name)) continue
@@ -0,0 +1,5 @@
1
+ import { afterEach, mock } from 'bun:test'
2
+
3
+ afterEach(() => {
4
+ mock.restore()
5
+ })
package/tsconfig.json CHANGED
@@ -38,5 +38,5 @@
38
38
  "noFallthroughCasesInSwitch": true
39
39
  },
40
40
  "include": ["src/**/*", "tests/**/*"],
41
- "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*-test.ts"]
41
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*-test.ts", "src/test-setup.ts"]
42
42
  }