@urugus/slack-cli 0.2.11 → 0.2.13

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 (97) hide show
  1. package/README.md +4 -0
  2. package/dist/commands/history-display.d.ts +5 -1
  3. package/dist/commands/history-display.d.ts.map +1 -1
  4. package/dist/commands/history-display.js +3 -3
  5. package/dist/commands/history-display.js.map +1 -1
  6. package/dist/commands/history.d.ts.map +1 -1
  7. package/dist/commands/history.js +28 -11
  8. package/dist/commands/history.js.map +1 -1
  9. package/dist/types/commands.d.ts +1 -0
  10. package/dist/types/commands.d.ts.map +1 -1
  11. package/dist/utils/slack-api-client.d.ts +1 -0
  12. package/dist/utils/slack-api-client.d.ts.map +1 -1
  13. package/dist/utils/slack-api-client.js +3 -0
  14. package/dist/utils/slack-api-client.js.map +1 -1
  15. package/dist/utils/slack-operations/message-operations.d.ts +1 -0
  16. package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
  17. package/dist/utils/slack-operations/message-operations.js +21 -0
  18. package/dist/utils/slack-operations/message-operations.js.map +1 -1
  19. package/package.json +5 -2
  20. package/.claude/settings.local.json +0 -75
  21. package/.github/dependabot.yml +0 -18
  22. package/.github/workflows/ci.yml +0 -70
  23. package/.github/workflows/pr-validation.yml +0 -41
  24. package/.prettierignore +0 -11
  25. package/.prettierrc +0 -10
  26. package/CHANGELOG.md +0 -61
  27. package/CLAUDE.md +0 -16
  28. package/eslint.config.js +0 -38
  29. package/src/commands/channels.ts +0 -50
  30. package/src/commands/config-subcommands.ts +0 -63
  31. package/src/commands/config.ts +0 -50
  32. package/src/commands/history-display.ts +0 -19
  33. package/src/commands/history-validators.ts +0 -46
  34. package/src/commands/history.ts +0 -61
  35. package/src/commands/scheduled.ts +0 -71
  36. package/src/commands/send.ts +0 -69
  37. package/src/commands/unread.ts +0 -122
  38. package/src/index.ts +0 -27
  39. package/src/types/commands.ts +0 -58
  40. package/src/types/config.ts +0 -20
  41. package/src/utils/channel-formatter.ts +0 -45
  42. package/src/utils/channel-resolver.ts +0 -82
  43. package/src/utils/client-factory.ts +0 -10
  44. package/src/utils/command-wrapper.ts +0 -27
  45. package/src/utils/config/config-file-manager.ts +0 -56
  46. package/src/utils/config/profile-manager.ts +0 -79
  47. package/src/utils/config/token-crypto-service.ts +0 -80
  48. package/src/utils/config-helper.ts +0 -21
  49. package/src/utils/constants.ts +0 -78
  50. package/src/utils/date-utils.ts +0 -8
  51. package/src/utils/error-utils.ts +0 -6
  52. package/src/utils/errors.ts +0 -33
  53. package/src/utils/format-utils.ts +0 -9
  54. package/src/utils/formatters/base-formatter.ts +0 -34
  55. package/src/utils/formatters/channel-formatters.ts +0 -71
  56. package/src/utils/formatters/channels-list-formatters.ts +0 -55
  57. package/src/utils/formatters/history-formatters.ts +0 -123
  58. package/src/utils/formatters/message-formatters.ts +0 -85
  59. package/src/utils/mention-utils.ts +0 -47
  60. package/src/utils/option-parsers.ts +0 -100
  61. package/src/utils/profile-config.ts +0 -161
  62. package/src/utils/schedule-utils.ts +0 -41
  63. package/src/utils/slack-api-client.ts +0 -135
  64. package/src/utils/slack-operations/base-client.ts +0 -30
  65. package/src/utils/slack-operations/channel-operations.ts +0 -161
  66. package/src/utils/slack-operations/index.ts +0 -3
  67. package/src/utils/slack-operations/message-operations.ts +0 -176
  68. package/src/utils/slack-patterns.ts +0 -9
  69. package/src/utils/token-utils.ts +0 -17
  70. package/src/utils/validators.ts +0 -263
  71. package/tests/commands/channels.test.ts +0 -250
  72. package/tests/commands/config.test.ts +0 -158
  73. package/tests/commands/history.test.ts +0 -403
  74. package/tests/commands/scheduled.test.ts +0 -131
  75. package/tests/commands/send.test.ts +0 -414
  76. package/tests/commands/unread.test.ts +0 -492
  77. package/tests/index.test.ts +0 -40
  78. package/tests/test-utils.ts +0 -28
  79. package/tests/utils/channel-resolver.test.ts +0 -161
  80. package/tests/utils/config/config-file-manager.test.ts +0 -118
  81. package/tests/utils/config/profile-manager.test.ts +0 -266
  82. package/tests/utils/config/token-crypto-service.test.ts +0 -98
  83. package/tests/utils/config.test.ts +0 -400
  84. package/tests/utils/date-utils.test.ts +0 -30
  85. package/tests/utils/error-utils.test.ts +0 -34
  86. package/tests/utils/format-utils.test.ts +0 -61
  87. package/tests/utils/mention-utils.test.ts +0 -100
  88. package/tests/utils/option-parsers.test.ts +0 -173
  89. package/tests/utils/profile-config.test.ts +0 -282
  90. package/tests/utils/schedule-utils.test.ts +0 -63
  91. package/tests/utils/slack-api-client.test.ts +0 -313
  92. package/tests/utils/slack-operations/channel-operations.test.ts +0 -248
  93. package/tests/utils/slack-operations/message-operations.test.ts +0 -163
  94. package/tests/utils/token-utils.test.ts +0 -33
  95. package/tests/utils/validators.test.ts +0 -307
  96. package/tsconfig.json +0 -22
  97. package/vitest.config.ts +0 -27
@@ -1,492 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { setupUnreadCommand } from '../../src/commands/unread';
3
- import { SlackApiClient } from '../../src/utils/slack-api-client';
4
- import { ProfileConfigManager } from '../../src/utils/profile-config';
5
- import { setupMockConsole, createTestProgram, restoreMocks } from '../test-utils';
6
- import { ERROR_MESSAGES } from '../../src/utils/constants';
7
- import chalk from 'chalk';
8
-
9
- vi.mock('../../src/utils/slack-api-client');
10
- vi.mock('../../src/utils/profile-config');
11
-
12
- describe('unread command', () => {
13
- let program: any;
14
- let mockSlackClient: SlackApiClient;
15
- let mockConfigManager: ProfileConfigManager;
16
- let mockConsole: any;
17
-
18
- beforeEach(() => {
19
- vi.clearAllMocks();
20
-
21
- mockConfigManager = new ProfileConfigManager();
22
- vi.mocked(ProfileConfigManager).mockReturnValue(mockConfigManager);
23
-
24
- mockSlackClient = new SlackApiClient('test-token');
25
- vi.mocked(SlackApiClient).mockReturnValue(mockSlackClient);
26
-
27
- mockConsole = setupMockConsole();
28
- program = createTestProgram();
29
- program.addCommand(setupUnreadCommand());
30
- });
31
-
32
- afterEach(() => {
33
- restoreMocks();
34
- });
35
-
36
- const mockChannelsWithUnread = [
37
- {
38
- id: 'C123',
39
- name: 'general',
40
- is_channel: true,
41
- is_member: true,
42
- is_archived: false,
43
- unread_count: 5,
44
- unread_count_display: 5,
45
- last_read: '1705286300.000000',
46
- },
47
- {
48
- id: 'C456',
49
- name: 'random',
50
- is_channel: true,
51
- is_member: true,
52
- is_archived: false,
53
- unread_count: 2,
54
- unread_count_display: 2,
55
- last_read: '1705286400.000000',
56
- },
57
- {
58
- id: 'C789',
59
- name: 'dev',
60
- is_channel: true,
61
- is_member: true,
62
- is_archived: false,
63
- unread_count: 0,
64
- unread_count_display: 0,
65
- last_read: '1705286500.000000',
66
- },
67
- ];
68
-
69
- const mockUnreadMessages = [
70
- {
71
- ts: '1705286400.000001',
72
- user: 'U123',
73
- text: 'Hello world',
74
- type: 'message',
75
- },
76
- {
77
- ts: '1705286500.000002',
78
- user: 'U456',
79
- text: 'Test message',
80
- type: 'message',
81
- },
82
- ];
83
-
84
- describe('basic functionality', () => {
85
- it('should display unread counts in table format by default', async () => {
86
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
87
- token: 'test-token',
88
- updatedAt: new Date().toISOString()
89
- });
90
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
91
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
92
- );
93
-
94
- await program.parseAsync(['node', 'slack-cli', 'unread']);
95
-
96
- expect(mockSlackClient.listUnreadChannels).toHaveBeenCalled();
97
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('Channel'));
98
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('Unread'));
99
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('#general'));
100
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('5'));
101
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('#random'));
102
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('2'));
103
- });
104
-
105
- it('should display only count when --count-only is specified', async () => {
106
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
107
- token: 'test-token',
108
- updatedAt: new Date().toISOString()
109
- });
110
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
111
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
112
- );
113
-
114
- await program.parseAsync(['node', 'slack-cli', 'unread', '--count-only']);
115
-
116
- expect(mockConsole.logSpy).toHaveBeenCalledWith('#general: 5');
117
- expect(mockConsole.logSpy).toHaveBeenCalledWith('#random: 2');
118
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.bold('Total: 7 unread messages'));
119
- });
120
-
121
- it('should display in JSON format when specified', async () => {
122
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
123
- token: 'test-token',
124
- updatedAt: new Date().toISOString()
125
- });
126
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
127
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
128
- );
129
-
130
- await program.parseAsync(['node', 'slack-cli', 'unread', '--format', 'json']);
131
-
132
- const expectedOutput = [
133
- { channel: '#general', channelId: 'C123', unreadCount: 5 },
134
- { channel: '#random', channelId: 'C456', unreadCount: 2 },
135
- ];
136
- expect(mockConsole.logSpy).toHaveBeenCalledWith(JSON.stringify(expectedOutput, null, 2));
137
- });
138
-
139
- it('should display in simple format when specified', async () => {
140
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
141
- token: 'test-token',
142
- updatedAt: new Date().toISOString()
143
- });
144
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
145
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
146
- );
147
-
148
- await program.parseAsync(['node', 'slack-cli', 'unread', '--format', 'simple']);
149
-
150
- expect(mockConsole.logSpy).toHaveBeenCalledWith('#general (5)');
151
- expect(mockConsole.logSpy).toHaveBeenCalledWith('#random (2)');
152
- });
153
- });
154
-
155
- describe('channel filtering', () => {
156
- it('should filter by specific channel when --channel is specified', async () => {
157
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
158
- token: 'test-token',
159
- updatedAt: new Date().toISOString()
160
- });
161
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
162
- channel: mockChannelsWithUnread[0],
163
- messages: mockUnreadMessages,
164
- users: new Map([
165
- ['U123', 'john.doe'],
166
- ['U456', 'jane.smith']
167
- ])
168
- });
169
-
170
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general']);
171
-
172
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
173
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.bold(`#general: 5 unread messages`));
174
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('Hello world'));
175
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('Test message'));
176
- });
177
-
178
- it('should display channel unread in JSON format when specified', async () => {
179
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
180
- token: 'test-token',
181
- updatedAt: new Date().toISOString()
182
- });
183
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
184
- channel: mockChannelsWithUnread[0],
185
- messages: mockUnreadMessages,
186
- users: new Map([
187
- ['U123', 'john.doe'],
188
- ['U456', 'jane.smith']
189
- ])
190
- });
191
-
192
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general', '--format', 'json']);
193
-
194
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
195
-
196
- const logCall = mockConsole.logSpy.mock.calls[0][0];
197
- const parsed = JSON.parse(logCall);
198
-
199
- expect(parsed).toEqual({
200
- channel: '#general',
201
- channelId: 'C123',
202
- unreadCount: 5,
203
- messages: [
204
- {
205
- timestamp: expect.any(String),
206
- author: 'john.doe',
207
- text: 'Hello world'
208
- },
209
- {
210
- timestamp: expect.any(String),
211
- author: 'jane.smith',
212
- text: 'Test message'
213
- }
214
- ]
215
- });
216
- });
217
-
218
- it('should display channel unread in simple format when specified', async () => {
219
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
220
- token: 'test-token',
221
- updatedAt: new Date().toISOString()
222
- });
223
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
224
- channel: mockChannelsWithUnread[0],
225
- messages: mockUnreadMessages,
226
- users: new Map([
227
- ['U123', 'john.doe'],
228
- ['U456', 'jane.smith']
229
- ])
230
- });
231
-
232
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general', '--format', 'simple']);
233
-
234
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
235
- expect(mockConsole.logSpy).toHaveBeenCalledWith('#general (5)');
236
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringMatching(/^\[.*\] john\.doe: Hello world$/));
237
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringMatching(/^\[.*\] jane\.smith: Test message$/));
238
- });
239
-
240
- it('should show only count for channel when --count-only is specified', async () => {
241
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
242
- token: 'test-token',
243
- updatedAt: new Date().toISOString()
244
- });
245
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
246
- channel: mockChannelsWithUnread[0],
247
- messages: mockUnreadMessages,
248
- users: new Map([
249
- ['U123', 'john.doe'],
250
- ['U456', 'jane.smith']
251
- ])
252
- });
253
-
254
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general', '--count-only']);
255
-
256
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
257
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.bold(`#general: 5 unread messages`));
258
- expect(mockConsole.logSpy).not.toHaveBeenCalledWith(expect.stringContaining('Hello world'));
259
- expect(mockConsole.logSpy).not.toHaveBeenCalledWith(expect.stringContaining('Test message'));
260
- });
261
-
262
- it('should show count only in JSON format when both --count-only and --format json are specified', async () => {
263
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
264
- token: 'test-token',
265
- updatedAt: new Date().toISOString()
266
- });
267
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
268
- channel: mockChannelsWithUnread[0],
269
- messages: mockUnreadMessages,
270
- users: new Map([
271
- ['U123', 'john.doe'],
272
- ['U456', 'jane.smith']
273
- ])
274
- });
275
-
276
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general', '--count-only', '--format', 'json']);
277
-
278
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
279
-
280
- const logCall = mockConsole.logSpy.mock.calls[0][0];
281
- const parsed = JSON.parse(logCall);
282
-
283
- expect(parsed).toEqual({
284
- channel: '#general',
285
- channelId: 'C123',
286
- unreadCount: 5
287
- });
288
- });
289
- });
290
-
291
- describe('limit option', () => {
292
- it('should limit the number of channels displayed', async () => {
293
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
294
- token: 'test-token',
295
- updatedAt: new Date().toISOString()
296
- });
297
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
298
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
299
- );
300
-
301
- await program.parseAsync(['node', 'slack-cli', 'unread', '--limit', '1']);
302
-
303
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('#general'));
304
- expect(mockConsole.logSpy).not.toHaveBeenCalledWith(expect.stringContaining('#random'));
305
- });
306
- });
307
-
308
- describe('error handling', () => {
309
- it('should display message when no unread messages found', async () => {
310
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
311
- token: 'test-token',
312
- updatedAt: new Date().toISOString()
313
- });
314
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue([]);
315
-
316
- await program.parseAsync(['node', 'slack-cli', 'unread']);
317
-
318
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.green('✓ No unread messages'));
319
- });
320
-
321
- it('should handle channel not found error', async () => {
322
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
323
- token: 'test-token',
324
- updatedAt: new Date().toISOString()
325
- });
326
- vi.mocked(mockSlackClient.getChannelUnread).mockRejectedValue(
327
- new Error('channel_not_found')
328
- );
329
-
330
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'nonexistent']);
331
-
332
- expect(mockConsole.errorSpy).toHaveBeenCalledWith(expect.stringContaining('Error:'), expect.any(String));
333
- expect(mockConsole.exitSpy).toHaveBeenCalledWith(1);
334
- });
335
-
336
- it('should handle missing configuration', async () => {
337
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue(null);
338
-
339
- await program.parseAsync(['node', 'slack-cli', 'unread']);
340
-
341
- expect(mockConsole.errorSpy).toHaveBeenCalledWith(expect.stringContaining('Error:'), expect.any(String));
342
- expect(mockConsole.exitSpy).toHaveBeenCalledWith(1);
343
- });
344
- });
345
-
346
- describe('profile option', () => {
347
- it('should use specified profile', async () => {
348
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
349
- token: 'work-token',
350
- updatedAt: new Date().toISOString()
351
- });
352
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue([]);
353
-
354
- await program.parseAsync(['node', 'slack-cli', 'unread', '--profile', 'work']);
355
-
356
- expect(mockConfigManager.getConfig).toHaveBeenCalledWith('work');
357
- expect(SlackApiClient).toHaveBeenCalledWith('work-token');
358
- });
359
- });
360
-
361
- describe('rate limiting', () => {
362
- it('should handle rate limit errors gracefully', async () => {
363
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
364
- token: 'test-token',
365
- updatedAt: new Date().toISOString()
366
- });
367
-
368
- const rateLimitError = new Error('A rate limit was exceeded (url: conversations.info, retry-after: 10)');
369
- vi.mocked(mockSlackClient.listUnreadChannels).mockRejectedValue(rateLimitError);
370
-
371
- await program.parseAsync(['node', 'slack-cli', 'unread']);
372
-
373
- expect(mockConsole.errorSpy).toHaveBeenCalledWith(expect.stringContaining('Error:'), expect.stringContaining('rate limit'));
374
- expect(mockConsole.exitSpy).toHaveBeenCalledWith(1);
375
- });
376
-
377
- it('should not retry indefinitely on rate limit errors', async () => {
378
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
379
- token: 'test-token',
380
- updatedAt: new Date().toISOString()
381
- });
382
-
383
- const rateLimitError = new Error('A rate limit was exceeded (url: conversations.info, retry-after: 10)');
384
- vi.mocked(mockSlackClient.listUnreadChannels).mockRejectedValue(rateLimitError);
385
-
386
- await program.parseAsync(['node', 'slack-cli', 'unread']);
387
-
388
- expect(mockSlackClient.listUnreadChannels).toHaveBeenCalledTimes(1);
389
- });
390
- });
391
-
392
- describe('last_read timestamp handling', () => {
393
- it('should fetch messages after last_read timestamp even when unread_count is 0', async () => {
394
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
395
- token: 'test-token',
396
- updatedAt: new Date().toISOString()
397
- });
398
-
399
- const channelWithLastRead = {
400
- id: 'C08JFKGJPPE',
401
- name: 'dev_kiban_jira',
402
- is_channel: true,
403
- is_member: true,
404
- is_archived: false,
405
- unread_count: 0,
406
- unread_count_display: 0,
407
- last_read: '1750646034.663209',
408
- is_private: false,
409
- created: 1742353688
410
- };
411
-
412
- const unreadMessage = {
413
- ts: '1750646072.447069',
414
- user: 'U5F87BSGP',
415
- text: '@Suguru Sakashita / 阪下 駿 transitioned ES-4359 ArgumentError: \'発行者\' is not a valid field_name in Clip',
416
- type: 'message',
417
- };
418
-
419
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
420
- channel: { ...channelWithLastRead, unread_count: 1, unread_count_display: 1 },
421
- messages: [unreadMessage],
422
- users: new Map([['U5F87BSGP', 'jira-bot']])
423
- });
424
-
425
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'dev_kiban_jira']);
426
-
427
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('dev_kiban_jira');
428
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.bold('#dev_kiban_jira: 1 unread messages'));
429
- expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('transitioned ES-4359'));
430
- });
431
- });
432
-
433
- describe('mark-read functionality', () => {
434
- it('should mark messages as read when --mark-read is specified', async () => {
435
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
436
- token: 'test-token',
437
- updatedAt: new Date().toISOString()
438
- });
439
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
440
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
441
- );
442
- vi.mocked(mockSlackClient.markAsRead).mockResolvedValue(undefined);
443
-
444
- await program.parseAsync(['node', 'slack-cli', 'unread', '--mark-read']);
445
-
446
- expect(mockSlackClient.listUnreadChannels).toHaveBeenCalled();
447
- expect(mockSlackClient.markAsRead).toHaveBeenCalledWith('C123');
448
- expect(mockSlackClient.markAsRead).toHaveBeenCalledWith('C456');
449
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.green('✓ Marked all messages as read'));
450
- });
451
-
452
- it('should mark messages as read for specific channel when --channel and --mark-read are specified', async () => {
453
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
454
- token: 'test-token',
455
- updatedAt: new Date().toISOString()
456
- });
457
- vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
458
- channel: mockChannelsWithUnread[0],
459
- messages: mockUnreadMessages,
460
- users: new Map([
461
- ['U123', 'john.doe'],
462
- ['U456', 'jane.smith']
463
- ])
464
- });
465
- vi.mocked(mockSlackClient.markAsRead).mockResolvedValue(undefined);
466
-
467
- await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general', '--mark-read']);
468
-
469
- expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
470
- expect(mockSlackClient.markAsRead).toHaveBeenCalledWith('C123');
471
- expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.green('✓ Marked messages in #general as read'));
472
- });
473
-
474
- it('should handle mark as read errors gracefully', async () => {
475
- vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
476
- token: 'test-token',
477
- updatedAt: new Date().toISOString()
478
- });
479
- vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
480
- mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
481
- );
482
- vi.mocked(mockSlackClient.markAsRead).mockRejectedValue(
483
- new Error('channel_not_found')
484
- );
485
-
486
- await program.parseAsync(['node', 'slack-cli', 'unread', '--mark-read']);
487
-
488
- expect(mockConsole.errorSpy).toHaveBeenCalledWith(expect.stringContaining('Error:'), expect.any(String));
489
- expect(mockConsole.exitSpy).toHaveBeenCalledWith(1);
490
- });
491
- });
492
- });
@@ -1,40 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { execSync } from 'child_process';
3
- import { readFileSync } from 'fs';
4
- import { join } from 'path';
5
-
6
- describe('slack-cli version', () => {
7
- it('should display the correct version from package.json', { timeout: 20000 }, () => {
8
- // Read the expected version from package.json
9
- const packageJson = JSON.parse(
10
- readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')
11
- );
12
- const expectedVersion = packageJson.version;
13
-
14
- // Execute the CLI command to get version using ts-node
15
- const output = execSync('npx ts-node src/index.ts --version', {
16
- encoding: 'utf-8',
17
- cwd: join(__dirname, '..')
18
- }).trim();
19
-
20
- // The output should contain the version from package.json
21
- expect(output).toBe(expectedVersion);
22
- });
23
-
24
- it('should display version with -V flag', { timeout: 20000 }, () => {
25
- // Read the expected version from package.json
26
- const packageJson = JSON.parse(
27
- readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')
28
- );
29
- const expectedVersion = packageJson.version;
30
-
31
- // Execute the CLI command with -V flag using ts-node
32
- const output = execSync('npx ts-node src/index.ts -V', {
33
- encoding: 'utf-8',
34
- cwd: join(__dirname, '..')
35
- }).trim();
36
-
37
- // The output should contain the version from package.json
38
- expect(output).toBe(expectedVersion);
39
- });
40
- });
@@ -1,28 +0,0 @@
1
- import { vi } from 'vitest';
2
- import { Command } from 'commander';
3
-
4
- export interface MockConsole {
5
- logSpy: any;
6
- errorSpy: any;
7
- exitSpy: any;
8
- }
9
-
10
- export function setupMockConsole(): MockConsole {
11
- const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
12
- const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
13
- const exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);
14
-
15
- return { logSpy, errorSpy, exitSpy };
16
- }
17
-
18
- export function createTestProgram(): Command {
19
- const program = new Command();
20
- program.exitOverride((err) => {
21
- throw new Error('process.exit');
22
- });
23
- return program;
24
- }
25
-
26
- export function restoreMocks(): void {
27
- vi.restoreAllMocks();
28
- }