@cli4ai/gmail 1.0.7 → 1.0.9
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.
- package/cli4ai.json +260 -27
- package/package.json +1 -3
- package/lib/api.test.ts +0 -287
- package/lib/api.ts +0 -299
- package/lib/attachments.ts +0 -199
- package/lib/drafts.ts +0 -434
- package/lib/labels.ts +0 -198
- package/lib/messages.ts +0 -310
- package/lib/send.ts +0 -331
- package/lib/threads.ts +0 -164
package/cli4ai.json
CHANGED
|
@@ -1,38 +1,271 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gmail",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Gmail CLI tool for messages, threads, and drafts",
|
|
5
5
|
"author": "cliforai",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"entry": "run.ts",
|
|
8
8
|
"runtime": "bun",
|
|
9
|
-
"keywords": [
|
|
9
|
+
"keywords": [
|
|
10
|
+
"gmail",
|
|
11
|
+
"email",
|
|
12
|
+
"google",
|
|
13
|
+
"mail"
|
|
14
|
+
],
|
|
10
15
|
"commands": {
|
|
11
|
-
"inbox": {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
16
|
+
"inbox": {
|
|
17
|
+
"description": "Recent inbox messages",
|
|
18
|
+
"args": [
|
|
19
|
+
{
|
|
20
|
+
"name": "limit",
|
|
21
|
+
"required": false
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"unread": {
|
|
26
|
+
"description": "Unread messages only",
|
|
27
|
+
"args": [
|
|
28
|
+
{
|
|
29
|
+
"name": "limit",
|
|
30
|
+
"required": false
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"search": {
|
|
35
|
+
"description": "Search with Gmail syntax",
|
|
36
|
+
"args": [
|
|
37
|
+
{
|
|
38
|
+
"name": "query",
|
|
39
|
+
"required": true
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "limit",
|
|
43
|
+
"required": false
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"read": {
|
|
48
|
+
"description": "Read full message",
|
|
49
|
+
"args": [
|
|
50
|
+
{
|
|
51
|
+
"name": "id",
|
|
52
|
+
"required": true
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"archive": {
|
|
57
|
+
"description": "Archive message",
|
|
58
|
+
"args": [
|
|
59
|
+
{
|
|
60
|
+
"name": "id",
|
|
61
|
+
"required": true
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"trash": {
|
|
66
|
+
"description": "Move to trash",
|
|
67
|
+
"args": [
|
|
68
|
+
{
|
|
69
|
+
"name": "id",
|
|
70
|
+
"required": true
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"untrash": {
|
|
75
|
+
"description": "Remove from trash",
|
|
76
|
+
"args": [
|
|
77
|
+
{
|
|
78
|
+
"name": "id",
|
|
79
|
+
"required": true
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"star": {
|
|
84
|
+
"description": "Star message",
|
|
85
|
+
"args": [
|
|
86
|
+
{
|
|
87
|
+
"name": "id",
|
|
88
|
+
"required": true
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"unstar": {
|
|
93
|
+
"description": "Unstar message",
|
|
94
|
+
"args": [
|
|
95
|
+
{
|
|
96
|
+
"name": "id",
|
|
97
|
+
"required": true
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
"markread": {
|
|
102
|
+
"description": "Mark as read",
|
|
103
|
+
"args": [
|
|
104
|
+
{
|
|
105
|
+
"name": "id",
|
|
106
|
+
"required": true
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
"markunread": {
|
|
111
|
+
"description": "Mark as unread",
|
|
112
|
+
"args": [
|
|
113
|
+
{
|
|
114
|
+
"name": "id",
|
|
115
|
+
"required": true
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"thread": {
|
|
120
|
+
"description": "Full conversation thread",
|
|
121
|
+
"args": [
|
|
122
|
+
{
|
|
123
|
+
"name": "id",
|
|
124
|
+
"required": true
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"threads": {
|
|
129
|
+
"description": "List recent threads",
|
|
130
|
+
"args": [
|
|
131
|
+
{
|
|
132
|
+
"name": "limit",
|
|
133
|
+
"required": false
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
"send": {
|
|
138
|
+
"description": "Send new email",
|
|
139
|
+
"args": [
|
|
140
|
+
{
|
|
141
|
+
"name": "to",
|
|
142
|
+
"required": true
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "subject",
|
|
146
|
+
"required": true
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "body",
|
|
150
|
+
"required": true
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
"reply": {
|
|
155
|
+
"description": "Reply to message",
|
|
156
|
+
"args": [
|
|
157
|
+
{
|
|
158
|
+
"name": "id",
|
|
159
|
+
"required": true
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "body",
|
|
163
|
+
"required": true
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
"replyall": {
|
|
168
|
+
"description": "Reply all",
|
|
169
|
+
"args": [
|
|
170
|
+
{
|
|
171
|
+
"name": "id",
|
|
172
|
+
"required": true
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"name": "body",
|
|
176
|
+
"required": true
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
"forward": {
|
|
181
|
+
"description": "Forward message",
|
|
182
|
+
"args": [
|
|
183
|
+
{
|
|
184
|
+
"name": "id",
|
|
185
|
+
"required": true
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "to",
|
|
189
|
+
"required": true
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
"draft": {
|
|
194
|
+
"description": "Create draft",
|
|
195
|
+
"args": [
|
|
196
|
+
{
|
|
197
|
+
"name": "to",
|
|
198
|
+
"required": true
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "subject",
|
|
202
|
+
"required": true
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"name": "body",
|
|
206
|
+
"required": true
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
"labels": {
|
|
211
|
+
"description": "List all labels"
|
|
212
|
+
},
|
|
213
|
+
"label": {
|
|
214
|
+
"description": "Add label to message",
|
|
215
|
+
"args": [
|
|
216
|
+
{
|
|
217
|
+
"name": "id",
|
|
218
|
+
"required": true
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "label",
|
|
222
|
+
"required": true
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
"unlabel": {
|
|
227
|
+
"description": "Remove label from message",
|
|
228
|
+
"args": [
|
|
229
|
+
{
|
|
230
|
+
"name": "id",
|
|
231
|
+
"required": true
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "label",
|
|
235
|
+
"required": true
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
"attachments": {
|
|
240
|
+
"description": "List attachments in message",
|
|
241
|
+
"args": [
|
|
242
|
+
{
|
|
243
|
+
"name": "id",
|
|
244
|
+
"required": true
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
"download": {
|
|
249
|
+
"description": "Download attachment",
|
|
250
|
+
"args": [
|
|
251
|
+
{
|
|
252
|
+
"name": "id",
|
|
253
|
+
"required": true
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
"drafts": {
|
|
258
|
+
"description": "List all drafts"
|
|
259
|
+
},
|
|
260
|
+
"draft-send": {
|
|
261
|
+
"description": "Send draft",
|
|
262
|
+
"args": [
|
|
263
|
+
{
|
|
264
|
+
"name": "id",
|
|
265
|
+
"required": true
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
}
|
|
36
269
|
},
|
|
37
270
|
"dependencies": {
|
|
38
271
|
"googleapis": "^144.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cli4ai/gmail",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Gmail CLI tool for messages, threads, and drafts",
|
|
5
5
|
"author": "cliforai",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"url": "https://github.com/cliforai/packages/issues"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@cli4ai/lib": "^1.0.2",
|
|
32
31
|
"googleapis": "^144.0.0",
|
|
33
32
|
"google-auth-library": "^9.0.0",
|
|
34
33
|
"commander": "^14.0.0"
|
|
@@ -36,7 +35,6 @@
|
|
|
36
35
|
"files": [
|
|
37
36
|
"run.ts",
|
|
38
37
|
"cli4ai.json",
|
|
39
|
-
"lib/**/*",
|
|
40
38
|
"README.md",
|
|
41
39
|
"LICENSE"
|
|
42
40
|
],
|
package/lib/api.test.ts
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, mock, beforeEach, afterEach, spyOn } from 'bun:test';
|
|
2
|
-
import {
|
|
3
|
-
formatDate,
|
|
4
|
-
decodeBase64Url,
|
|
5
|
-
encodeBase64Url,
|
|
6
|
-
extractHeaders,
|
|
7
|
-
extractBody,
|
|
8
|
-
cleanBody,
|
|
9
|
-
output,
|
|
10
|
-
type MessagePayload,
|
|
11
|
-
type EmailHeaders
|
|
12
|
-
} from './api';
|
|
13
|
-
|
|
14
|
-
describe('gmail/lib/api', () => {
|
|
15
|
-
describe('formatDate', () => {
|
|
16
|
-
test('should format timestamp to readable date', () => {
|
|
17
|
-
// 1704067200000 = 2024-01-01 00:00:00 UTC
|
|
18
|
-
const result = formatDate('1704067200000');
|
|
19
|
-
expect(result).toBe('2024-01-01 00:00');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('should handle timestamp as number', () => {
|
|
23
|
-
const result = formatDate(1704067200000);
|
|
24
|
-
expect(result).toBe('2024-01-01 00:00');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('should return null for undefined', () => {
|
|
28
|
-
expect(formatDate(undefined)).toBeNull();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('should return null for null', () => {
|
|
32
|
-
expect(formatDate(null)).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('decodeBase64Url', () => {
|
|
37
|
-
test('should decode base64url encoded string', () => {
|
|
38
|
-
// 'Hello World' in base64url
|
|
39
|
-
const encoded = 'SGVsbG8gV29ybGQ';
|
|
40
|
-
const result = decodeBase64Url(encoded);
|
|
41
|
-
expect(result).toBe('Hello World');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('should handle URL-safe characters', () => {
|
|
45
|
-
// Contains - and _ instead of + and /
|
|
46
|
-
const encoded = 'PDw_Pz4-'; // <<??>>
|
|
47
|
-
const result = decodeBase64Url(encoded);
|
|
48
|
-
expect(result).toBe('<<??>>');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('should handle missing padding', () => {
|
|
52
|
-
// Without = padding
|
|
53
|
-
const encoded = 'SGVsbG8';
|
|
54
|
-
const result = decodeBase64Url(encoded);
|
|
55
|
-
expect(result).toBe('Hello');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('should return empty string for undefined', () => {
|
|
59
|
-
expect(decodeBase64Url(undefined)).toBe('');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('should return empty string for null', () => {
|
|
63
|
-
expect(decodeBase64Url(null)).toBe('');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('should return empty string for empty string', () => {
|
|
67
|
-
expect(decodeBase64Url('')).toBe('');
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('encodeBase64Url', () => {
|
|
72
|
-
test('should encode string to base64url', () => {
|
|
73
|
-
const result = encodeBase64Url('Hello World');
|
|
74
|
-
expect(result).toBe('SGVsbG8gV29ybGQ');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('should replace + with -', () => {
|
|
78
|
-
// A string that would produce + in standard base64
|
|
79
|
-
const result = encodeBase64Url('<<??>>');
|
|
80
|
-
expect(result).not.toContain('+');
|
|
81
|
-
expect(result).toContain('-');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('should replace / with _', () => {
|
|
85
|
-
// A string that would produce / in standard base64
|
|
86
|
-
const result = encodeBase64Url('<<??>>');
|
|
87
|
-
expect(result).not.toContain('/');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('should remove padding', () => {
|
|
91
|
-
const result = encodeBase64Url('Hello');
|
|
92
|
-
expect(result).not.toContain('=');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('should be reversible', () => {
|
|
96
|
-
const original = 'Test message with special chars: éà';
|
|
97
|
-
const encoded = encodeBase64Url(original);
|
|
98
|
-
const decoded = decodeBase64Url(encoded);
|
|
99
|
-
expect(decoded).toBe(original);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('extractHeaders', () => {
|
|
104
|
-
test('should extract email headers', () => {
|
|
105
|
-
const headers = [
|
|
106
|
-
{ name: 'From', value: 'sender@example.com' },
|
|
107
|
-
{ name: 'To', value: 'recipient@example.com' },
|
|
108
|
-
{ name: 'Subject', value: 'Test Subject' },
|
|
109
|
-
{ name: 'Date', value: '2024-01-01' }
|
|
110
|
-
];
|
|
111
|
-
const result = extractHeaders(headers);
|
|
112
|
-
expect(result.from).toBe('sender@example.com');
|
|
113
|
-
expect(result.to).toBe('recipient@example.com');
|
|
114
|
-
expect(result.subject).toBe('Test Subject');
|
|
115
|
-
expect(result.date).toBe('2024-01-01');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('should handle Message-ID header', () => {
|
|
119
|
-
const headers = [
|
|
120
|
-
{ name: 'Message-ID', value: '<123@example.com>' }
|
|
121
|
-
];
|
|
122
|
-
const result = extractHeaders(headers);
|
|
123
|
-
expect(result.message_id).toBe('<123@example.com>');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('should handle Reply-To header', () => {
|
|
127
|
-
const headers = [
|
|
128
|
-
{ name: 'Reply-To', value: 'reply@example.com' }
|
|
129
|
-
];
|
|
130
|
-
const result = extractHeaders(headers);
|
|
131
|
-
expect(result.reply_to).toBe('reply@example.com');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test('should ignore unknown headers', () => {
|
|
135
|
-
const headers = [
|
|
136
|
-
{ name: 'X-Custom-Header', value: 'custom value' },
|
|
137
|
-
{ name: 'From', value: 'sender@example.com' }
|
|
138
|
-
];
|
|
139
|
-
const result = extractHeaders(headers);
|
|
140
|
-
expect(result.from).toBe('sender@example.com');
|
|
141
|
-
expect(Object.keys(result)).not.toContain('x_custom_header');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('should return empty object for undefined headers', () => {
|
|
145
|
-
const result = extractHeaders(undefined);
|
|
146
|
-
expect(result).toEqual({});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('should return empty object for empty array', () => {
|
|
150
|
-
const result = extractHeaders([]);
|
|
151
|
-
expect(result).toEqual({});
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
describe('extractBody', () => {
|
|
156
|
-
test('should extract body from simple message', () => {
|
|
157
|
-
const payload: MessagePayload = {
|
|
158
|
-
body: { data: 'SGVsbG8gV29ybGQ' } // 'Hello World'
|
|
159
|
-
};
|
|
160
|
-
const result = extractBody(payload);
|
|
161
|
-
expect(result).toBe('Hello World');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test('should prefer text/plain from multipart', () => {
|
|
165
|
-
const payload: MessagePayload = {
|
|
166
|
-
parts: [
|
|
167
|
-
{ mimeType: 'text/html', body: { data: 'PGh0bWw-SGVsbG88L2h0bWw-' } },
|
|
168
|
-
{ mimeType: 'text/plain', body: { data: 'SGVsbG8' } }
|
|
169
|
-
]
|
|
170
|
-
};
|
|
171
|
-
const result = extractBody(payload);
|
|
172
|
-
expect(result).toBe('Hello');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('should fall back to text/html if no text/plain', () => {
|
|
176
|
-
const payload: MessagePayload = {
|
|
177
|
-
parts: [
|
|
178
|
-
{ mimeType: 'text/html', body: { data: 'PGI-SGVsbG88L2I-' } } // <b>Hello</b>
|
|
179
|
-
]
|
|
180
|
-
};
|
|
181
|
-
const result = extractBody(payload);
|
|
182
|
-
// HTML tags should be stripped
|
|
183
|
-
expect(result).toContain('Hello');
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test('should handle nested parts', () => {
|
|
187
|
-
const payload: MessagePayload = {
|
|
188
|
-
parts: [
|
|
189
|
-
{
|
|
190
|
-
mimeType: 'multipart/alternative',
|
|
191
|
-
parts: [
|
|
192
|
-
{ mimeType: 'text/plain', body: { data: 'SGVsbG8' } }
|
|
193
|
-
]
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
};
|
|
197
|
-
const result = extractBody(payload);
|
|
198
|
-
expect(result).toBe('Hello');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test('should return empty string for undefined payload', () => {
|
|
202
|
-
expect(extractBody(undefined)).toBe('');
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test('should return empty string for empty payload', () => {
|
|
206
|
-
expect(extractBody({})).toBe('');
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('cleanBody', () => {
|
|
211
|
-
test('should remove excessive whitespace', () => {
|
|
212
|
-
const body = 'Hello World';
|
|
213
|
-
const result = cleanBody(body);
|
|
214
|
-
expect(result).toBe('Hello World');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test('should normalize line breaks', () => {
|
|
218
|
-
const body = 'Hello\r\nWorld';
|
|
219
|
-
const result = cleanBody(body);
|
|
220
|
-
expect(result).toBe('Hello\nWorld');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test('should collapse multiple newlines', () => {
|
|
224
|
-
const body = 'Hello\n\n\n\nWorld';
|
|
225
|
-
const result = cleanBody(body);
|
|
226
|
-
expect(result).toBe('Hello\n\nWorld');
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
test('should trim whitespace', () => {
|
|
230
|
-
const body = ' Hello World ';
|
|
231
|
-
const result = cleanBody(body);
|
|
232
|
-
expect(result).toBe('Hello World');
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test('should truncate to maxLength', () => {
|
|
236
|
-
const body = 'Hello World This is a long message';
|
|
237
|
-
const result = cleanBody(body, 10);
|
|
238
|
-
expect(result).toBe('Hello Worl...');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
test('should not truncate when maxLength is null', () => {
|
|
242
|
-
const body = 'Hello World';
|
|
243
|
-
const result = cleanBody(body, null);
|
|
244
|
-
expect(result).toBe('Hello World');
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
test('should return empty string for empty input', () => {
|
|
248
|
-
expect(cleanBody('')).toBe('');
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe('output', () => {
|
|
253
|
-
let consoleSpy: ReturnType<typeof spyOn>;
|
|
254
|
-
let logs: string[];
|
|
255
|
-
|
|
256
|
-
beforeEach(() => {
|
|
257
|
-
logs = [];
|
|
258
|
-
consoleSpy = spyOn(console, 'log').mockImplementation((msg: string) => {
|
|
259
|
-
logs.push(msg);
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
afterEach(() => {
|
|
264
|
-
consoleSpy.mockRestore();
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test('should output JSON by default', () => {
|
|
268
|
-
output({ test: 'data' });
|
|
269
|
-
expect(logs[0]).toContain('"test": "data"');
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
test('should output raw JSON when raw option is true', () => {
|
|
273
|
-
output({ test: 'data' }, { raw: true });
|
|
274
|
-
expect(logs[0]).toContain('"test": "data"');
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test('should output compact format for message arrays', () => {
|
|
278
|
-
const messages = [
|
|
279
|
-
{ date: '2024-01-01', from: 'sender@test.com', subject: 'Test Subject' }
|
|
280
|
-
];
|
|
281
|
-
output(messages, { compact: true });
|
|
282
|
-
expect(logs[0]).toContain('[2024-01-01]');
|
|
283
|
-
expect(logs[0]).toContain('sender@test.com');
|
|
284
|
-
expect(logs[0]).toContain('Test Subject');
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
});
|