@prairielearn/express-list-endpoints 1.0.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.
@@ -0,0 +1,638 @@
1
+ import { assert } from 'chai';
2
+ import express from 'express';
3
+
4
+ import listEndpoints, { type Endpoint } from '../src/index.js';
5
+
6
+ function assertResult(endpoints: Endpoint[]) {
7
+ assert.isArray(endpoints);
8
+ assert.lengthOf(endpoints, 2);
9
+
10
+ endpoints.forEach((endpoint) => {
11
+ assert.typeOf(endpoint, 'object');
12
+
13
+ assert.typeOf(endpoint.path, 'string');
14
+ assert.include(endpoint.path, '/');
15
+
16
+ assert.isArray(endpoint.methods);
17
+ endpoint.methods.forEach((method) => {
18
+ assert.typeOf(method, 'string');
19
+ assert.equal(method, method.toUpperCase());
20
+ assert.notEqual(method, '_ALL');
21
+ });
22
+
23
+ assert.isArray(endpoint.middlewares);
24
+ endpoint.middlewares.forEach((middleware) => {
25
+ assert.typeOf(middleware, 'string');
26
+ });
27
+ });
28
+ }
29
+
30
+ describe('listEndpoints', () => {
31
+ describe('when called with non configured app', () => {
32
+ let endpoints: Endpoint[];
33
+
34
+ before(() => {
35
+ const app = express();
36
+
37
+ endpoints = listEndpoints(app);
38
+ });
39
+
40
+ it('should return an empty array', () => {
41
+ assert.isArray(endpoints);
42
+ assert.lengthOf(endpoints, 0);
43
+ });
44
+ });
45
+
46
+ describe('when called over an app', () => {
47
+ let endpoints: Endpoint[];
48
+
49
+ before(() => {
50
+ const app = express();
51
+
52
+ app
53
+ .route('/')
54
+ .get((_req, res) => {
55
+ res.end();
56
+ })
57
+ .all((_req, res) => {
58
+ res.end();
59
+ })
60
+ .post((_req, res) => {
61
+ res.end();
62
+ });
63
+
64
+ app
65
+ .route('/testing')
66
+ .all((_req, res) => {
67
+ res.end();
68
+ })
69
+ .delete((_req, res) => {
70
+ res.end();
71
+ });
72
+
73
+ endpoints = listEndpoints(app);
74
+ });
75
+
76
+ it('should return an array of well formed objects', () => {
77
+ assertResult(endpoints);
78
+ });
79
+ });
80
+
81
+ describe('when called over a router', () => {
82
+ let endpoints: Endpoint[];
83
+
84
+ before(() => {
85
+ const router = express.Router();
86
+
87
+ router
88
+ .route('/')
89
+ .get((_req, res) => {
90
+ res.end();
91
+ })
92
+ .all((_req, res) => {
93
+ res.end();
94
+ })
95
+ .post((_req, res) => {
96
+ res.end();
97
+ });
98
+
99
+ router
100
+ .route('/testing')
101
+ .all((_req, res) => {
102
+ res.end();
103
+ })
104
+ .delete((_req, res) => {
105
+ res.end();
106
+ });
107
+
108
+ endpoints = listEndpoints(router);
109
+ });
110
+
111
+ it('should return an array of well formed objects', () => {
112
+ assertResult(endpoints);
113
+ });
114
+ });
115
+
116
+ describe('when called over an app with mounted routers', () => {
117
+ let endpoints: Endpoint[];
118
+
119
+ before(() => {
120
+ const app = express();
121
+ const router = express.Router();
122
+
123
+ app
124
+ .route('/testing')
125
+ .all((_req, res) => {
126
+ res.end();
127
+ })
128
+ .delete((_req, res) => {
129
+ res.end();
130
+ });
131
+
132
+ router
133
+ .route('/')
134
+ .get((_req, res) => {
135
+ res.end();
136
+ })
137
+ .all((_req, res) => {
138
+ res.end();
139
+ })
140
+ .post((_req, res) => {
141
+ res.end();
142
+ });
143
+
144
+ app.use('/router', router);
145
+
146
+ endpoints = listEndpoints(app);
147
+ });
148
+
149
+ it('should return an array of well formed objects', () => {
150
+ assertResult(endpoints);
151
+ });
152
+
153
+ describe('and some of the routers has the option `mergeParams`', () => {
154
+ let endpoints: Endpoint[];
155
+
156
+ before(() => {
157
+ const app = express();
158
+ const router = express.Router({ mergeParams: true });
159
+
160
+ router.get('/:id/friends', (_req, res) => {
161
+ res.end();
162
+ });
163
+
164
+ app.use('/router', router);
165
+
166
+ endpoints = listEndpoints(app);
167
+ });
168
+
169
+ it('should parse the endpoints correctly', () => {
170
+ assert.lengthOf(endpoints, 1);
171
+ assert.equal(endpoints[0].path, '/router/:id/friends');
172
+ });
173
+
174
+ describe('and also has a sub-router on the router', () => {
175
+ let endpoints: Endpoint[];
176
+
177
+ before(() => {
178
+ const app = express();
179
+ const router = express.Router({ mergeParams: true });
180
+ const subRouter = express.Router();
181
+
182
+ subRouter.get('/', (_req, res) => {
183
+ res.end();
184
+ });
185
+
186
+ app.use('/router', router);
187
+
188
+ router.use('/:postId/sub-router', subRouter);
189
+
190
+ endpoints = listEndpoints(app);
191
+ });
192
+
193
+ it('should parse the endpoints correctly', () => {
194
+ assert.lengthOf(endpoints, 1);
195
+ assert.equal(endpoints[0].path, '/router/:postId/sub-router');
196
+ });
197
+ });
198
+ });
199
+ });
200
+
201
+ describe('when the defined routes', () => {
202
+ describe('contains underscores', () => {
203
+ let endpoints: Endpoint[];
204
+
205
+ before(() => {
206
+ const router = express.Router();
207
+
208
+ router.get('/some_route', (_req, res) => {
209
+ res.end();
210
+ });
211
+
212
+ router.get('/some_other_router', (_req, res) => {
213
+ res.end();
214
+ });
215
+
216
+ router.get('/__last_route__', (_req, res) => {
217
+ res.end();
218
+ });
219
+
220
+ endpoints = listEndpoints(router);
221
+ });
222
+
223
+ it('should parse the endpoint correctly', () => {
224
+ assert.lengthOf(endpoints, 3);
225
+ assert.equal(endpoints[0].path, '/some_route');
226
+ assert.equal(endpoints[1].path, '/some_other_router');
227
+ assert.equal(endpoints[2].path, '/__last_route__');
228
+ });
229
+ });
230
+
231
+ describe('contains hyphens', () => {
232
+ let endpoints: Endpoint[];
233
+
234
+ before(() => {
235
+ const router = express.Router();
236
+
237
+ router.get('/some-route', (_req, res) => {
238
+ res.end();
239
+ });
240
+
241
+ router.get('/some-other-router', (_req, res) => {
242
+ res.end();
243
+ });
244
+
245
+ router.get('/--last-route--', (_req, res) => {
246
+ res.end();
247
+ });
248
+
249
+ endpoints = listEndpoints(router);
250
+ });
251
+
252
+ it('should parse the endpoint correctly', () => {
253
+ assert.lengthOf(endpoints, 3);
254
+ assert.equal(endpoints[0].path, '/some-route');
255
+ assert.equal(endpoints[1].path, '/some-other-router');
256
+ assert.equal(endpoints[2].path, '/--last-route--');
257
+ });
258
+ });
259
+
260
+ describe('contains dots', () => {
261
+ let endpoints: Endpoint[];
262
+
263
+ before(() => {
264
+ const router = express.Router();
265
+
266
+ router.get('/some.route', (_req, res) => {
267
+ res.end();
268
+ });
269
+
270
+ router.get('/some.other.router', (_req, res) => {
271
+ res.end();
272
+ });
273
+
274
+ router.get('/..last.route..', (_req, res) => {
275
+ res.end();
276
+ });
277
+
278
+ endpoints = listEndpoints(router);
279
+ });
280
+
281
+ it('should parse the endpoint correctly', () => {
282
+ assert.lengthOf(endpoints, 3);
283
+ assert.equal(endpoints[0].path, '/some.route');
284
+ assert.equal(endpoints[1].path, '/some.other.router');
285
+ assert.equal(endpoints[2].path, '/..last.route..');
286
+ });
287
+ });
288
+
289
+ describe('contains multiple different chars', () => {
290
+ let endpoints: Endpoint[];
291
+
292
+ before(() => {
293
+ const router = express.Router();
294
+
295
+ router.get('/s0m3_r.oute', (_req, res) => {
296
+ res.end();
297
+ });
298
+
299
+ router.get('/v1.0.0', (_req, res) => {
300
+ res.end();
301
+ });
302
+
303
+ router.get('/not_sure.what-1m.d01ng', (_req, res) => {
304
+ res.end();
305
+ });
306
+
307
+ endpoints = listEndpoints(router);
308
+ });
309
+
310
+ it('should parse the endpoint correctly', () => {
311
+ assert.lengthOf(endpoints, 3);
312
+ assert.equal(endpoints[0].path, '/s0m3_r.oute');
313
+ assert.equal(endpoints[1].path, '/v1.0.0');
314
+ assert.equal(endpoints[2].path, '/not_sure.what-1m.d01ng');
315
+ });
316
+ });
317
+ });
318
+
319
+ describe('when called over a mounted router with only root path', () => {
320
+ let endpoints: Endpoint[];
321
+
322
+ before(() => {
323
+ const app = express();
324
+ const router = express.Router();
325
+
326
+ router.get('/', (_req, res) => {
327
+ res.end();
328
+ });
329
+
330
+ app.use('/', router);
331
+
332
+ endpoints = listEndpoints(app);
333
+ });
334
+
335
+ it('should retrieve the list of endpoints and its methods', () => {
336
+ assert.lengthOf(endpoints, 1);
337
+ assert.equal(endpoints[0].path, '/');
338
+ assert.lengthOf(endpoints[0].methods, 1);
339
+ assert.equal(endpoints[0].methods[0], 'GET');
340
+ });
341
+ });
342
+
343
+ describe('when called over a multi-level base route', () => {
344
+ let endpoints: Endpoint[];
345
+
346
+ before(() => {
347
+ const app = express();
348
+ const router = express.Router();
349
+
350
+ router.get('/my/path', (_req, res) => {
351
+ res.end();
352
+ });
353
+
354
+ app.use('/multi/level', router);
355
+ app.use('/super/duper/multi/level', router);
356
+
357
+ endpoints = listEndpoints(app);
358
+ });
359
+
360
+ it('should retrieve the correct built path', () => {
361
+ assert.lengthOf(endpoints, 2);
362
+ assert.equal(endpoints[0].path, '/multi/level/my/path');
363
+ assert.equal(endpoints[1].path, '/super/duper/multi/level/my/path');
364
+ });
365
+
366
+ describe('with params', () => {
367
+ let endpoints: Endpoint[];
368
+
369
+ before(() => {
370
+ const app = express();
371
+ const router = express.Router();
372
+
373
+ router.get('/users/:id', (_req, res) => {
374
+ res.end();
375
+ });
376
+
377
+ router.get('/super/users/:id', (_req, res) => {
378
+ res.end();
379
+ });
380
+
381
+ app.use('/multi/:multiId/level/:levelId', router);
382
+
383
+ endpoints = listEndpoints(app);
384
+ });
385
+
386
+ it('should retrieve the correct built path', () => {
387
+ assert.lengthOf(endpoints, 2);
388
+ assert.equal(endpoints[0].path, '/multi/:multiId/level/:levelId/users/:id');
389
+ assert.equal(endpoints[1].path, '/multi/:multiId/level/:levelId/super/users/:id');
390
+ });
391
+ });
392
+
393
+ describe('with params in middle of the pattern', () => {
394
+ let endpoints: Endpoint[];
395
+
396
+ before(() => {
397
+ const app = express();
398
+ const router = express.Router();
399
+
400
+ router.get('/super/users/:id/friends', (_req, res) => {
401
+ res.end();
402
+ });
403
+
404
+ app.use('/multi/level', router);
405
+
406
+ endpoints = listEndpoints(app);
407
+ });
408
+
409
+ it('should retrieve the correct built path', () => {
410
+ assert.lengthOf(endpoints, 1);
411
+ assert.equal(endpoints[0].path, '/multi/level/super/users/:id/friends');
412
+ });
413
+ });
414
+ });
415
+
416
+ describe('when called over a route with params', () => {
417
+ let endpoints: Endpoint[];
418
+
419
+ before(() => {
420
+ const app = express();
421
+
422
+ app.get('/users/:id', (_req, res) => {
423
+ res.end();
424
+ });
425
+
426
+ endpoints = listEndpoints(app);
427
+ });
428
+
429
+ it('should retrieve the correct built path', () => {
430
+ assert.lengthOf(endpoints, 1);
431
+ assert.equal(endpoints[0].path, '/users/:id');
432
+ });
433
+ });
434
+
435
+ describe('when called over a route with params in middle of the pattern', () => {
436
+ let endpoints: Endpoint[];
437
+
438
+ before(() => {
439
+ const app = express();
440
+
441
+ app.get('/users/:id/friends', (_req, res) => {
442
+ res.end();
443
+ });
444
+
445
+ endpoints = listEndpoints(app);
446
+ });
447
+
448
+ it('should retrieve the correct built path', () => {
449
+ assert.lengthOf(endpoints, 1);
450
+ assert.equal(endpoints[0].path, '/users/:id/friends');
451
+ });
452
+ });
453
+
454
+ describe('when called over a route with multiple methods with "/" path defined', () => {
455
+ let endpoints: Endpoint[];
456
+
457
+ before(() => {
458
+ const router = express.Router();
459
+
460
+ router
461
+ .post('/test', (_req, res) => {
462
+ res.end();
463
+ })
464
+ .delete('/test', (_req, res) => {
465
+ res.end();
466
+ });
467
+
468
+ endpoints = listEndpoints(router);
469
+ });
470
+
471
+ it('should retrieve the correct built path', () => {
472
+ assert.lengthOf(endpoints, 1);
473
+ assert.equal(endpoints[0].path, '/test');
474
+ });
475
+
476
+ it('should retrieve the correct built methods', () => {
477
+ assert.lengthOf(endpoints[0].methods, 2);
478
+ assert.equal(endpoints[0].methods[0], 'POST');
479
+ assert.equal(endpoints[0].methods[1], 'DELETE');
480
+ });
481
+ });
482
+
483
+ describe('when called with middlewares', () => {
484
+ let endpoints: Endpoint[];
485
+
486
+ before(() => {
487
+ const router = express.Router();
488
+
489
+ const exampleMiddleware = () => {};
490
+
491
+ router.post('/test', [
492
+ exampleMiddleware,
493
+ () => {}, // Anonymous middleware
494
+ ]);
495
+
496
+ endpoints = listEndpoints(router);
497
+ });
498
+
499
+ it('should retrieve the correct built path', () => {
500
+ assert.lengthOf(endpoints, 1);
501
+ assert.equal(endpoints[0].path, '/test');
502
+ assert.lengthOf(endpoints[0].methods, 1);
503
+ assert.equal(endpoints[0].methods[0], 'POST');
504
+ });
505
+
506
+ it('should retrieve the correct middlewares', () => {
507
+ assert.lengthOf(endpoints[0].middlewares, 2);
508
+ assert.equal(endpoints[0].middlewares[0], 'exampleMiddleware');
509
+ assert.equal(endpoints[0].middlewares[1], 'anonymous');
510
+ });
511
+ });
512
+
513
+ describe('when called with an array of paths', () => {
514
+ let endpoints: Endpoint[];
515
+
516
+ before(() => {
517
+ const app = express();
518
+ const router = express.Router();
519
+
520
+ app.get(['/one', '/two'], (_req, res) => {
521
+ res.end();
522
+ });
523
+
524
+ router.get(['/one', '/two'], (_req, res) => {
525
+ res.end();
526
+ });
527
+
528
+ app.use(['/router', '/sub-path'], router);
529
+
530
+ endpoints = listEndpoints(app);
531
+ });
532
+
533
+ it('should list routes correctly', () => {
534
+ assert.lengthOf(endpoints, 4);
535
+ assert.equal(endpoints[0].path, '/one');
536
+ assert.equal(endpoints[0].methods[0], 'GET');
537
+ assert.equal(endpoints[1].path, '/two');
538
+ assert.equal(endpoints[1].methods[0], 'GET');
539
+ });
540
+ });
541
+
542
+ describe('when called with an app with a mounted sub-app', () => {
543
+ let endpoints: Endpoint[];
544
+
545
+ before(() => {
546
+ const app = express();
547
+ const subApp = express();
548
+
549
+ app.get('/', (_req, res) => {
550
+ res.end();
551
+ });
552
+
553
+ subApp.get('/', (_req, res) => {
554
+ res.end();
555
+ });
556
+
557
+ app.use('/sub-app', subApp);
558
+
559
+ endpoints = listEndpoints(app);
560
+ });
561
+
562
+ it('should list routes correctly', () => {
563
+ assert.lengthOf(endpoints, 2);
564
+ assert.equal(endpoints[0].path, '/');
565
+ assert.equal(endpoints[0].methods[0], 'GET');
566
+ assert.equal(endpoints[0].middlewares[0], 'anonymous');
567
+ assert.equal(endpoints[1].path, '/sub-app');
568
+ assert.lengthOf(endpoints[1].methods, 0);
569
+ assert.lengthOf(endpoints[1].middlewares, 0);
570
+ });
571
+ });
572
+
573
+ describe('when called with route params with regexp', () => {
574
+ let endpoints: Endpoint[];
575
+
576
+ before(() => {
577
+ const app = express();
578
+
579
+ app.get('/foo/:item_id(\\d+)/bar', (_req, res) => {
580
+ res.end();
581
+ });
582
+
583
+ endpoints = listEndpoints(app);
584
+ });
585
+
586
+ it('should list routes correctly', () => {
587
+ assert.lengthOf(endpoints, 1);
588
+ assert.equal(endpoints[0].path, '/foo/:item_id/bar');
589
+ assert.equal(endpoints[0].methods[0], 'GET');
590
+ assert.equal(endpoints[0].middlewares[0], 'anonymous');
591
+ });
592
+ });
593
+
594
+ describe('when called with a route with multiple params with regexp', () => {
595
+ let endpoints: Endpoint[];
596
+
597
+ before(() => {
598
+ const app = express();
599
+
600
+ app.get('/foo/bar/:baz_id(\\d+)/:biz_id(\\d+)', (_req, res) => {
601
+ res.end();
602
+ });
603
+
604
+ endpoints = listEndpoints(app);
605
+ });
606
+
607
+ it('should list routes correctly', () => {
608
+ assert.lengthOf(endpoints, 1);
609
+ assert.equal(endpoints[0].path, '/foo/bar/:baz_id/:biz_id');
610
+ assert.equal(endpoints[0].methods[0], 'GET');
611
+ assert.equal(endpoints[0].middlewares[0], 'anonymous');
612
+ });
613
+ });
614
+
615
+ describe('supports regexp validators for params in subapp', () => {
616
+ let endpoints: Endpoint[];
617
+
618
+ before(() => {
619
+ const app = express();
620
+ const subApp = express.Router();
621
+
622
+ subApp.get('/baz/:biz_id(\\d+)', (_req, res) => {
623
+ res.end();
624
+ });
625
+
626
+ app.use('/foo/bar', subApp);
627
+
628
+ endpoints = listEndpoints(app);
629
+ });
630
+
631
+ it('should list routes correctly', () => {
632
+ assert.lengthOf(endpoints, 1);
633
+ assert.equal(endpoints[0].path, '/foo/bar/baz/:biz_id');
634
+ assert.equal(endpoints[0].methods[0], 'GET');
635
+ assert.equal(endpoints[0].middlewares[0], 'anonymous');
636
+ });
637
+ });
638
+ });