@keplog/cli 0.2.0
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/LICENSE +21 -0
- package/README.md +495 -0
- package/bin/keplog +2 -0
- package/dist/commands/delete.d.ts +3 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +158 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +131 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/issues.d.ts +3 -0
- package/dist/commands/issues.d.ts.map +1 -0
- package/dist/commands/issues.js +543 -0
- package/dist/commands/issues.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +104 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/releases.d.ts +3 -0
- package/dist/commands/releases.d.ts.map +1 -0
- package/dist/commands/releases.js +100 -0
- package/dist/commands/releases.js.map +1 -0
- package/dist/commands/upload.d.ts +3 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +76 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +57 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +155 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/uploader.d.ts +11 -0
- package/dist/lib/uploader.d.ts.map +1 -0
- package/dist/lib/uploader.js +171 -0
- package/dist/lib/uploader.js.map +1 -0
- package/jest.config.js +16 -0
- package/package.json +58 -0
- package/src/commands/delete.ts +186 -0
- package/src/commands/init.ts +137 -0
- package/src/commands/issues.ts +695 -0
- package/src/commands/list.ts +124 -0
- package/src/commands/releases.ts +122 -0
- package/src/commands/upload.ts +76 -0
- package/src/index.ts +31 -0
- package/src/lib/config.ts +138 -0
- package/src/lib/uploader.ts +168 -0
- package/tests/README.md +380 -0
- package/tests/config.test.ts +397 -0
- package/tests/uploader.test.ts +524 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
6
|
+
import { uploadSourceMaps } from '../src/lib/uploader';
|
|
7
|
+
|
|
8
|
+
// Mock ora to avoid spinner output during tests
|
|
9
|
+
jest.mock('ora', () => {
|
|
10
|
+
return jest.fn(() => ({
|
|
11
|
+
start: jest.fn().mockReturnThis(),
|
|
12
|
+
succeed: jest.fn().mockReturnThis(),
|
|
13
|
+
fail: jest.fn().mockReturnThis(),
|
|
14
|
+
info: jest.fn().mockReturnThis(),
|
|
15
|
+
}));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('uploadSourceMaps', () => {
|
|
19
|
+
let mock: MockAdapter;
|
|
20
|
+
let testDir: string;
|
|
21
|
+
let originalProcessExit: any;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// Create axios mock
|
|
25
|
+
mock = new MockAdapter(axios);
|
|
26
|
+
|
|
27
|
+
// Create temporary test directory
|
|
28
|
+
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'keplog-upload-test-'));
|
|
29
|
+
|
|
30
|
+
// Mock process.exit to prevent tests from actually exiting
|
|
31
|
+
originalProcessExit = process.exit;
|
|
32
|
+
process.exit = jest.fn() as any;
|
|
33
|
+
|
|
34
|
+
// Suppress console output during tests
|
|
35
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
36
|
+
jest.spyOn(console, 'error').mockImplementation();
|
|
37
|
+
jest.spyOn(console, 'warn').mockImplementation();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
// Restore mocks
|
|
42
|
+
mock.restore();
|
|
43
|
+
process.exit = originalProcessExit;
|
|
44
|
+
|
|
45
|
+
// Clean up test directory
|
|
46
|
+
if (fs.existsSync(testDir)) {
|
|
47
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Restore console
|
|
51
|
+
jest.restoreAllMocks();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('file discovery', () => {
|
|
55
|
+
it('should find source map files matching pattern', async () => {
|
|
56
|
+
// Create test .map files
|
|
57
|
+
const mapFiles = ['app.js.map', 'vendor.js.map', 'style.css.map'];
|
|
58
|
+
mapFiles.forEach(file => {
|
|
59
|
+
fs.writeFileSync(path.join(testDir, file), '{}', 'utf-8');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Mock successful upload
|
|
63
|
+
mock.onPost().reply(200, {
|
|
64
|
+
uploaded: mapFiles,
|
|
65
|
+
errors: [],
|
|
66
|
+
release: 'v1.0.0',
|
|
67
|
+
count: mapFiles.length,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await uploadSourceMaps({
|
|
71
|
+
release: 'v1.0.0',
|
|
72
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
73
|
+
projectId: 'test-project',
|
|
74
|
+
apiKey: 'test-key',
|
|
75
|
+
apiUrl: 'https://api.keplog.com',
|
|
76
|
+
verbose: false,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Should have called the API
|
|
80
|
+
expect(mock.history.post.length).toBe(1);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should find nested source map files with glob pattern', async () => {
|
|
84
|
+
// Create nested directory structure
|
|
85
|
+
const nestedDir = path.join(testDir, 'dist', 'js');
|
|
86
|
+
fs.mkdirSync(nestedDir, { recursive: true });
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(path.join(nestedDir, 'app.js.map'), '{}', 'utf-8');
|
|
89
|
+
fs.writeFileSync(path.join(testDir, 'dist', 'style.css.map'), '{}', 'utf-8');
|
|
90
|
+
|
|
91
|
+
mock.onPost().reply(200, {
|
|
92
|
+
uploaded: ['app.js.map', 'style.css.map'],
|
|
93
|
+
errors: [],
|
|
94
|
+
release: 'v1.0.0',
|
|
95
|
+
count: 2,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await uploadSourceMaps({
|
|
99
|
+
release: 'v1.0.0',
|
|
100
|
+
filePatterns: [path.join(testDir, '**/*.map')],
|
|
101
|
+
projectId: 'test-project',
|
|
102
|
+
apiKey: 'test-key',
|
|
103
|
+
apiUrl: 'https://api.keplog.com',
|
|
104
|
+
verbose: false,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(mock.history.post.length).toBe(1);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should handle multiple file patterns', async () => {
|
|
111
|
+
// Create files in different directories
|
|
112
|
+
fs.mkdirSync(path.join(testDir, 'dist'), { recursive: true });
|
|
113
|
+
fs.mkdirSync(path.join(testDir, 'build'), { recursive: true });
|
|
114
|
+
|
|
115
|
+
fs.writeFileSync(path.join(testDir, 'dist', 'app.js.map'), '{}', 'utf-8');
|
|
116
|
+
fs.writeFileSync(path.join(testDir, 'build', 'vendor.js.map'), '{}', 'utf-8');
|
|
117
|
+
|
|
118
|
+
mock.onPost().reply(200, {
|
|
119
|
+
uploaded: ['app.js.map', 'vendor.js.map'],
|
|
120
|
+
errors: [],
|
|
121
|
+
release: 'v1.0.0',
|
|
122
|
+
count: 2,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await uploadSourceMaps({
|
|
126
|
+
release: 'v1.0.0',
|
|
127
|
+
filePatterns: [
|
|
128
|
+
path.join(testDir, 'dist/*.map'),
|
|
129
|
+
path.join(testDir, 'build/*.map'),
|
|
130
|
+
],
|
|
131
|
+
projectId: 'test-project',
|
|
132
|
+
apiKey: 'test-key',
|
|
133
|
+
apiUrl: 'https://api.keplog.com',
|
|
134
|
+
verbose: false,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(mock.history.post.length).toBe(1);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should remove duplicate files from multiple patterns', async () => {
|
|
141
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{}', 'utf-8');
|
|
142
|
+
|
|
143
|
+
mock.onPost().reply(200, {
|
|
144
|
+
uploaded: ['app.js.map'],
|
|
145
|
+
errors: [],
|
|
146
|
+
release: 'v1.0.0',
|
|
147
|
+
count: 1,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await uploadSourceMaps({
|
|
151
|
+
release: 'v1.0.0',
|
|
152
|
+
// Same file matched by two patterns
|
|
153
|
+
filePatterns: [
|
|
154
|
+
path.join(testDir, '*.map'),
|
|
155
|
+
path.join(testDir, 'app.js.map'),
|
|
156
|
+
],
|
|
157
|
+
projectId: 'test-project',
|
|
158
|
+
apiKey: 'test-key',
|
|
159
|
+
apiUrl: 'https://api.keplog.com',
|
|
160
|
+
verbose: false,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Should only upload once
|
|
164
|
+
expect(mock.history.post.length).toBe(1);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should exit with error when no files found', async () => {
|
|
168
|
+
try {
|
|
169
|
+
await uploadSourceMaps({
|
|
170
|
+
release: 'v1.0.0',
|
|
171
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
172
|
+
projectId: 'test-project',
|
|
173
|
+
apiKey: 'test-key',
|
|
174
|
+
apiUrl: 'https://api.keplog.com',
|
|
175
|
+
verbose: false,
|
|
176
|
+
});
|
|
177
|
+
} catch (error) {
|
|
178
|
+
// May throw or exit
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should filter out non-.map files', async () => {
|
|
185
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{}', 'utf-8');
|
|
186
|
+
fs.writeFileSync(path.join(testDir, 'app.js'), 'code', 'utf-8');
|
|
187
|
+
|
|
188
|
+
mock.onPost().reply(200, {
|
|
189
|
+
uploaded: ['app.js.map'],
|
|
190
|
+
errors: [],
|
|
191
|
+
release: 'v1.0.0',
|
|
192
|
+
count: 1,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await uploadSourceMaps({
|
|
196
|
+
release: 'v1.0.0',
|
|
197
|
+
filePatterns: [path.join(testDir, '*')],
|
|
198
|
+
projectId: 'test-project',
|
|
199
|
+
apiKey: 'test-key',
|
|
200
|
+
apiUrl: 'https://api.keplog.com',
|
|
201
|
+
verbose: false,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(mock.history.post.length).toBe(1);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('API communication', () => {
|
|
209
|
+
beforeEach(() => {
|
|
210
|
+
// Create a test .map file for upload tests
|
|
211
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{"version":3}', 'utf-8');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should send correct API request', async () => {
|
|
215
|
+
mock.onPost().reply(config => {
|
|
216
|
+
// Verify URL and headers
|
|
217
|
+
expect(config.url).toContain('/api/v1/cli/projects/test-project/sourcemaps');
|
|
218
|
+
expect(config.headers?.['X-API-Key']).toBe('test-api-key');
|
|
219
|
+
|
|
220
|
+
return [200, {
|
|
221
|
+
uploaded: ['app.js.map'],
|
|
222
|
+
errors: [],
|
|
223
|
+
release: 'v1.0.0',
|
|
224
|
+
count: 1,
|
|
225
|
+
}];
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
await uploadSourceMaps({
|
|
229
|
+
release: 'v1.0.0',
|
|
230
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
231
|
+
projectId: 'test-project',
|
|
232
|
+
apiKey: 'test-api-key',
|
|
233
|
+
apiUrl: 'https://api.keplog.com',
|
|
234
|
+
verbose: false,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(mock.history.post.length).toBe(1);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should include release in form data', async () => {
|
|
241
|
+
mock.onPost().reply(200, {
|
|
242
|
+
uploaded: ['app.js.map'],
|
|
243
|
+
errors: [],
|
|
244
|
+
release: 'v2.0.0',
|
|
245
|
+
count: 1,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await uploadSourceMaps({
|
|
249
|
+
release: 'v2.0.0',
|
|
250
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
251
|
+
projectId: 'test-project',
|
|
252
|
+
apiKey: 'test-key',
|
|
253
|
+
apiUrl: 'https://api.keplog.com',
|
|
254
|
+
verbose: false,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Verify request was made
|
|
258
|
+
expect(mock.history.post.length).toBe(1);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should handle successful upload', async () => {
|
|
262
|
+
mock.onPost().reply(200, {
|
|
263
|
+
uploaded: ['app.js.map'],
|
|
264
|
+
errors: [],
|
|
265
|
+
release: 'v1.0.0',
|
|
266
|
+
count: 1,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
await expect(uploadSourceMaps({
|
|
270
|
+
release: 'v1.0.0',
|
|
271
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
272
|
+
projectId: 'test-project',
|
|
273
|
+
apiKey: 'test-key',
|
|
274
|
+
apiUrl: 'https://api.keplog.com',
|
|
275
|
+
verbose: false,
|
|
276
|
+
})).resolves.not.toThrow();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should exit with error when upload has errors', async () => {
|
|
280
|
+
mock.onPost().reply(200, {
|
|
281
|
+
uploaded: [],
|
|
282
|
+
errors: ['File too large', 'Invalid format'],
|
|
283
|
+
release: 'v1.0.0',
|
|
284
|
+
count: 0,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await uploadSourceMaps({
|
|
288
|
+
release: 'v1.0.0',
|
|
289
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
290
|
+
projectId: 'test-project',
|
|
291
|
+
apiKey: 'test-key',
|
|
292
|
+
apiUrl: 'https://api.keplog.com',
|
|
293
|
+
verbose: false,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('error handling', () => {
|
|
301
|
+
beforeEach(() => {
|
|
302
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{}', 'utf-8');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should handle 401 authentication error', async () => {
|
|
306
|
+
mock.onPost().reply(401, {
|
|
307
|
+
error: 'Invalid API key',
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
await expect(uploadSourceMaps({
|
|
311
|
+
release: 'v1.0.0',
|
|
312
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
313
|
+
projectId: 'test-project',
|
|
314
|
+
apiKey: 'invalid-key',
|
|
315
|
+
apiUrl: 'https://api.keplog.com',
|
|
316
|
+
verbose: false,
|
|
317
|
+
})).rejects.toThrow('Invalid API key');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should handle 404 project not found', async () => {
|
|
321
|
+
mock.onPost().reply(404, {
|
|
322
|
+
error: 'Project not found',
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
await expect(uploadSourceMaps({
|
|
326
|
+
release: 'v1.0.0',
|
|
327
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
328
|
+
projectId: 'nonexistent',
|
|
329
|
+
apiKey: 'test-key',
|
|
330
|
+
apiUrl: 'https://api.keplog.com',
|
|
331
|
+
verbose: false,
|
|
332
|
+
})).rejects.toThrow('Project not found');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should handle network errors', async () => {
|
|
336
|
+
mock.onPost().networkError();
|
|
337
|
+
|
|
338
|
+
await expect(uploadSourceMaps({
|
|
339
|
+
release: 'v1.0.0',
|
|
340
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
341
|
+
projectId: 'test-project',
|
|
342
|
+
apiKey: 'test-key',
|
|
343
|
+
apiUrl: 'https://api.keplog.com',
|
|
344
|
+
verbose: false,
|
|
345
|
+
})).rejects.toThrow();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should handle timeout errors', async () => {
|
|
349
|
+
mock.onPost().timeout();
|
|
350
|
+
|
|
351
|
+
await expect(uploadSourceMaps({
|
|
352
|
+
release: 'v1.0.0',
|
|
353
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
354
|
+
projectId: 'test-project',
|
|
355
|
+
apiKey: 'test-key',
|
|
356
|
+
apiUrl: 'https://api.keplog.com',
|
|
357
|
+
verbose: false,
|
|
358
|
+
})).rejects.toThrow();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should handle server connection errors', async () => {
|
|
362
|
+
mock.onPost().reply(() => {
|
|
363
|
+
const error: any = new Error('connect ECONNREFUSED');
|
|
364
|
+
error.code = 'ECONNREFUSED';
|
|
365
|
+
error.isAxiosError = true;
|
|
366
|
+
throw error;
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
await expect(uploadSourceMaps({
|
|
370
|
+
release: 'v1.0.0',
|
|
371
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
372
|
+
projectId: 'test-project',
|
|
373
|
+
apiKey: 'test-key',
|
|
374
|
+
apiUrl: 'https://api.keplog.com',
|
|
375
|
+
verbose: false,
|
|
376
|
+
})).rejects.toThrow();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should handle DNS resolution errors', async () => {
|
|
380
|
+
mock.onPost().reply(() => {
|
|
381
|
+
const error: any = new Error('getaddrinfo ENOTFOUND');
|
|
382
|
+
error.code = 'ENOTFOUND';
|
|
383
|
+
error.isAxiosError = true;
|
|
384
|
+
throw error;
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await expect(uploadSourceMaps({
|
|
388
|
+
release: 'v1.0.0',
|
|
389
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
390
|
+
projectId: 'test-project',
|
|
391
|
+
apiKey: 'test-key',
|
|
392
|
+
apiUrl: 'https://invalid-domain.keplog.com',
|
|
393
|
+
verbose: false,
|
|
394
|
+
})).rejects.toThrow();
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should handle invalid glob patterns', async () => {
|
|
398
|
+
await expect(uploadSourceMaps({
|
|
399
|
+
release: 'v1.0.0',
|
|
400
|
+
filePatterns: ['[invalid-pattern'],
|
|
401
|
+
projectId: 'test-project',
|
|
402
|
+
apiKey: 'test-key',
|
|
403
|
+
apiUrl: 'https://api.keplog.com',
|
|
404
|
+
verbose: false,
|
|
405
|
+
})).rejects.toThrow();
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe('verbose mode', () => {
|
|
410
|
+
beforeEach(() => {
|
|
411
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{}', 'utf-8');
|
|
412
|
+
fs.writeFileSync(path.join(testDir, 'vendor.js.map'), '{}', 'utf-8');
|
|
413
|
+
|
|
414
|
+
mock.onPost().reply(200, {
|
|
415
|
+
uploaded: ['app.js.map', 'vendor.js.map'],
|
|
416
|
+
errors: [],
|
|
417
|
+
release: 'v1.0.0',
|
|
418
|
+
count: 2,
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should display detailed information in verbose mode', async () => {
|
|
423
|
+
const consoleSpy = jest.spyOn(console, 'log');
|
|
424
|
+
|
|
425
|
+
await uploadSourceMaps({
|
|
426
|
+
release: 'v1.0.0',
|
|
427
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
428
|
+
projectId: 'test-project',
|
|
429
|
+
apiKey: 'test-key',
|
|
430
|
+
apiUrl: 'https://api.keplog.com',
|
|
431
|
+
verbose: true,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should not display extra information when verbose is false', async () => {
|
|
438
|
+
await uploadSourceMaps({
|
|
439
|
+
release: 'v1.0.0',
|
|
440
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
441
|
+
projectId: 'test-project',
|
|
442
|
+
apiKey: 'test-key',
|
|
443
|
+
apiUrl: 'https://api.keplog.com',
|
|
444
|
+
verbose: false,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Should still complete successfully
|
|
448
|
+
expect(mock.history.post.length).toBe(1);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('edge cases', () => {
|
|
453
|
+
it('should handle empty release string', async () => {
|
|
454
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{}', 'utf-8');
|
|
455
|
+
|
|
456
|
+
mock.onPost().reply(200, {
|
|
457
|
+
uploaded: ['app.js.map'],
|
|
458
|
+
errors: [],
|
|
459
|
+
release: '',
|
|
460
|
+
count: 1,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
await uploadSourceMaps({
|
|
464
|
+
release: '',
|
|
465
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
466
|
+
projectId: 'test-project',
|
|
467
|
+
apiKey: 'test-key',
|
|
468
|
+
apiUrl: 'https://api.keplog.com',
|
|
469
|
+
verbose: false,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
expect(mock.history.post.length).toBe(1);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should handle very large number of files', async () => {
|
|
476
|
+
// Create 100 test files
|
|
477
|
+
for (let i = 0; i < 100; i++) {
|
|
478
|
+
fs.writeFileSync(path.join(testDir, `file${i}.js.map`), '{}', 'utf-8');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const files = Array.from({ length: 100 }, (_, i) => `file${i}.js.map`);
|
|
482
|
+
mock.onPost().reply(200, {
|
|
483
|
+
uploaded: files,
|
|
484
|
+
errors: [],
|
|
485
|
+
release: 'v1.0.0',
|
|
486
|
+
count: 100,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
await uploadSourceMaps({
|
|
490
|
+
release: 'v1.0.0',
|
|
491
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
492
|
+
projectId: 'test-project',
|
|
493
|
+
apiKey: 'test-key',
|
|
494
|
+
apiUrl: 'https://api.keplog.com',
|
|
495
|
+
verbose: false,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
expect(mock.history.post.length).toBe(1);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('should handle custom API URL', async () => {
|
|
502
|
+
fs.writeFileSync(path.join(testDir, 'app.js.map'), '{}', 'utf-8');
|
|
503
|
+
|
|
504
|
+
mock.onPost('https://custom.keplog.io/api/v1/cli/projects/test-project/sourcemaps')
|
|
505
|
+
.reply(200, {
|
|
506
|
+
uploaded: ['app.js.map'],
|
|
507
|
+
errors: [],
|
|
508
|
+
release: 'v1.0.0',
|
|
509
|
+
count: 1,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
await uploadSourceMaps({
|
|
513
|
+
release: 'v1.0.0',
|
|
514
|
+
filePatterns: [path.join(testDir, '*.map')],
|
|
515
|
+
projectId: 'test-project',
|
|
516
|
+
apiKey: 'test-key',
|
|
517
|
+
apiUrl: 'https://custom.keplog.io',
|
|
518
|
+
verbose: false,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
expect(mock.history.post[0].url).toContain('custom.keplog.io');
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"moduleResolution": "node"
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|