express-generator-typescript 3.0.3 → 3.1.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/README.md +7 -3
- package/bin/cli.js +1 -20
- package/lib/template/README.md +4 -3
- package/lib/template/package.json +8 -6
- package/lib/template/src/common/classes/route-errors.ts +4 -8
- package/lib/template/src/common/constants/HttpStatusCodes.ts +2 -7
- package/lib/template/src/common/constants/{Paths.ts → Paths/Paths.ts} +16 -9
- package/lib/template/src/common/constants/Paths/helpers.ts +29 -0
- package/lib/template/src/common/constants/Paths/index.ts +1 -0
- package/lib/template/src/common/constants/env.constants.ts +42 -0
- package/lib/template/src/common/utils/{date-utils.ts → date.utils.ts} +10 -12
- package/lib/template/src/entities/User/module.ts +77 -28
- package/lib/template/src/entities/User/types.ts +1 -1
- package/lib/template/src/entities/common/{types.ts → entity-base-types.ts} +1 -1
- package/lib/template/src/main.ts +3 -4
- package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +0 -1
- package/lib/template/src/public/scripts/users.js +20 -11
- package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +0 -1
- package/lib/template/src/public/stylesheets/users.css +24 -1
- package/lib/template/src/repos/MockOrm.ts +2 -2
- package/lib/template/src/repos/UserRepo.ts +20 -27
- package/lib/template/src/repos/common/database.json +3 -3
- package/lib/template/src/{routes/apiRouter.ts → routers/api.router.ts} +3 -11
- package/lib/template/src/{routes/UserRoutes.ts → routers/user.router.ts} +38 -47
- package/lib/template/src/server.ts +22 -11
- package/lib/template/src/services/UserService.ts +9 -14
- package/lib/template/src/views/users.html +8 -0
- package/lib/template/tests/support/utils.ts +23 -0
- package/lib/template/tests/users.test.ts +182 -140
- package/package.json +6 -5
- package/lib/template/src/common/constants/env-inv.ts +0 -32
- package/lib/template/src/entities/User/_internal/validators.ts +0 -47
- package/lib/template/src/routes/common/express-types.ts +0 -9
- package/lib/template/src/routes/common/parseReq.ts +0 -23
- package/lib/template/tests/common/comparators.ts +0 -38
- package/lib/template/tests/common/supertest-types.ts +0 -11
- /package/lib/template/src/common/{types/utility-types.ts → utils/validators.ts} +0 -0
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { getISOString } from '@src/common/utils/date-utils';
|
|
4
|
-
import type { UserEntity } from '@src/entities/User';
|
|
1
|
+
import { type UserEntity } from '@src/entities/User';
|
|
5
2
|
|
|
6
3
|
import orm from './MockOrm';
|
|
7
4
|
|
|
@@ -15,9 +12,7 @@ import orm from './MockOrm';
|
|
|
15
12
|
async function persists(id: string): Promise<boolean> {
|
|
16
13
|
const db = await orm.openDb();
|
|
17
14
|
for (const user of db.users) {
|
|
18
|
-
if (user.id === id)
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
15
|
+
if (user.id === id) return true;
|
|
21
16
|
}
|
|
22
17
|
return false;
|
|
23
18
|
}
|
|
@@ -27,13 +22,13 @@ async function persists(id: string): Promise<boolean> {
|
|
|
27
22
|
*/
|
|
28
23
|
async function getAll(): Promise<UserEntity[]> {
|
|
29
24
|
const db = await orm.openDb();
|
|
30
|
-
return db.users;
|
|
25
|
+
return db.users.sort((a, b) => a.id.localeCompare(b.id));
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
/**
|
|
34
29
|
* Add one user.
|
|
35
30
|
*/
|
|
36
|
-
async function
|
|
31
|
+
async function addOne(user: UserEntity): Promise<void> {
|
|
37
32
|
const db = await orm.openDb();
|
|
38
33
|
db.users.push(user);
|
|
39
34
|
return orm.saveDb(db);
|
|
@@ -42,7 +37,7 @@ async function add(user: UserEntity): Promise<void> {
|
|
|
42
37
|
/**
|
|
43
38
|
* Update a user.
|
|
44
39
|
*/
|
|
45
|
-
async function
|
|
40
|
+
async function updateOne(user: UserEntity): Promise<void> {
|
|
46
41
|
const db = await orm.openDb();
|
|
47
42
|
for (let i = 0; i < db.users.length; i++) {
|
|
48
43
|
if (db.users[i].id === user.id) {
|
|
@@ -60,7 +55,7 @@ async function update(user: UserEntity): Promise<void> {
|
|
|
60
55
|
/**
|
|
61
56
|
* Delete one user.
|
|
62
57
|
*/
|
|
63
|
-
async function
|
|
58
|
+
async function deleteOne(id: string): Promise<void> {
|
|
64
59
|
const db = await orm.openDb();
|
|
65
60
|
for (let i = 0; i < db.users.length; i++) {
|
|
66
61
|
if (db.users[i].id === id) {
|
|
@@ -73,9 +68,9 @@ async function delete_(id: string): Promise<void> {
|
|
|
73
68
|
// ============================ Unit-tests Only ============================ //
|
|
74
69
|
|
|
75
70
|
/**
|
|
76
|
-
* @testOnly
|
|
77
|
-
*
|
|
78
71
|
* Delete every user record.
|
|
72
|
+
*
|
|
73
|
+
* @testOnly
|
|
79
74
|
*/
|
|
80
75
|
async function deleteAllUsers(): Promise<void> {
|
|
81
76
|
const db = await orm.openDb();
|
|
@@ -84,34 +79,32 @@ async function deleteAllUsers(): Promise<void> {
|
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
/**
|
|
87
|
-
* @testOnly
|
|
88
|
-
*
|
|
89
82
|
* Insert copies of `users` with fresh ids; the inputs are not modified.
|
|
83
|
+
*
|
|
84
|
+
* @testOnly
|
|
90
85
|
*/
|
|
91
86
|
async function insertMultiple(
|
|
92
87
|
users: UserEntity[] | readonly UserEntity[],
|
|
93
88
|
): Promise<UserEntity[]> {
|
|
94
89
|
const db = await orm.openDb();
|
|
95
|
-
|
|
96
|
-
...user,
|
|
97
|
-
id: jetid(),
|
|
98
|
-
created: getISOString(),
|
|
99
|
-
}));
|
|
100
|
-
db.users = [...db.users, ...inserted];
|
|
90
|
+
db.users = [...db.users, ...users];
|
|
101
91
|
await orm.saveDb(db);
|
|
102
|
-
return
|
|
92
|
+
return [...users];
|
|
103
93
|
}
|
|
104
94
|
|
|
105
95
|
// ========================================================================= //
|
|
106
96
|
// EXPORT //
|
|
107
97
|
// ========================================================================= //
|
|
108
98
|
|
|
99
|
+
export const UserRepoTestOnly = {
|
|
100
|
+
deleteAllUsers,
|
|
101
|
+
insertMultiple,
|
|
102
|
+
} as const;
|
|
103
|
+
|
|
109
104
|
export default {
|
|
110
105
|
persists,
|
|
111
106
|
getAll,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
deleteAllUsers,
|
|
116
|
-
insertMultiple,
|
|
107
|
+
addOne,
|
|
108
|
+
updateOne,
|
|
109
|
+
deleteOne,
|
|
117
110
|
} as const;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"users": [
|
|
3
3
|
{
|
|
4
|
-
"id": "
|
|
4
|
+
"id": "01a12230-a18b-721e-bf21-0ee836fa989d",
|
|
5
5
|
"name": "Sean Maxwell",
|
|
6
6
|
"email": "smaxwell@example.com",
|
|
7
7
|
"created": "2024-03-22T05:14:36.252Z"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
|
-
"id": "
|
|
10
|
+
"id": "01a12230-a18c-7473-b1f0-845016f412d3",
|
|
11
11
|
"name": "John Smith",
|
|
12
12
|
"email": "john.smith@example.com",
|
|
13
13
|
"created": "2024-03-22T05:20:55.079Z"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
|
-
"id": "
|
|
16
|
+
"id": "01a12230-a18c-7473-b1f1-88ed3ea1d5ae",
|
|
17
17
|
"name": "Gordan Freeman",
|
|
18
18
|
"email": "nova@prospect.com",
|
|
19
19
|
"created": "2024-03-22T05:42:18.895Z"
|
|
@@ -2,7 +2,7 @@ import { Router } from 'express';
|
|
|
2
2
|
|
|
3
3
|
import Paths from '@src/common/constants/Paths';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import userRouter from './user.router';
|
|
6
6
|
|
|
7
7
|
// ========================================================================= //
|
|
8
8
|
// EXEC //
|
|
@@ -10,16 +10,8 @@ import UserRoutes from './UserRoutes';
|
|
|
10
10
|
|
|
11
11
|
const apiRouter = Router();
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
const userRouter = Router();
|
|
16
|
-
|
|
17
|
-
userRouter.get(Paths.Users.Get._, UserRoutes.getAll);
|
|
18
|
-
userRouter.post(Paths.Users.Add._, UserRoutes.add);
|
|
19
|
-
userRouter.put(Paths.Users.Update._, UserRoutes.update);
|
|
20
|
-
userRouter.delete(Paths.Users.Delete._, UserRoutes.delete);
|
|
21
|
-
|
|
22
|
-
apiRouter.use(Paths.Users._, userRouter);
|
|
13
|
+
// Add more routers as your app grows (e.g. `postsRouter`)
|
|
14
|
+
apiRouter.use(Paths.Users.PATH, userRouter);
|
|
23
15
|
|
|
24
16
|
// ========================================================================= //
|
|
25
17
|
// EXPORT //
|
|
@@ -1,74 +1,65 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import validate from 'express-zod-safe';
|
|
3
|
+
|
|
1
4
|
import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
|
|
5
|
+
import Paths from '@src/common/constants/Paths';
|
|
2
6
|
import User from '@src/entities/User';
|
|
3
7
|
import UserService from '@src/services/UserService';
|
|
4
8
|
|
|
5
|
-
import type { Req, Res } from './common/express-types';
|
|
6
|
-
import parseReq from './common/parseReq';
|
|
7
|
-
|
|
8
9
|
// ========================================================================= //
|
|
9
|
-
//
|
|
10
|
+
// CONSTANTS //
|
|
10
11
|
// ========================================================================= //
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// Shorthand for the path strings
|
|
14
|
+
const P = {
|
|
15
|
+
GET: Paths.Users.Get.PATH,
|
|
16
|
+
ADD: Paths.Users.Add.PATH,
|
|
17
|
+
UPDATE: Paths.Users.Update.PATH,
|
|
18
|
+
DELETE: Paths.Users.Delete.PATH,
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
// Middlware `validators`
|
|
22
|
+
const V = {
|
|
23
|
+
add: validate({ body: { user: User.Schemas.Input } }),
|
|
24
|
+
update: validate({ body: { user: User.Schemas.Main } }),
|
|
25
|
+
delete: validate({ params: { id: User.Schemas.Id } }),
|
|
16
26
|
} as const;
|
|
17
27
|
|
|
18
28
|
// ========================================================================= //
|
|
19
|
-
//
|
|
29
|
+
// EXEC //
|
|
20
30
|
// ========================================================================= //
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*/
|
|
27
|
-
async function getAll(_: Req, res: Res) {
|
|
32
|
+
const router = Router();
|
|
33
|
+
|
|
34
|
+
// Get all users
|
|
35
|
+
router.get(P.GET, async (_, res) => {
|
|
28
36
|
const users = await UserService.getAll();
|
|
29
37
|
res.status(HttpStatusCodes.OK).json({ users });
|
|
30
|
-
}
|
|
38
|
+
});
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* @route POST /api/users/add
|
|
36
|
-
*/
|
|
37
|
-
async function add(req: Req, res: Res) {
|
|
38
|
-
const { user } = reqValidators.add(req.body);
|
|
40
|
+
// Add one user
|
|
41
|
+
router.post(P.ADD, V.add, async (req, res) => {
|
|
42
|
+
const { user } = req.body;
|
|
39
43
|
const created = await UserService.addOne(user);
|
|
40
44
|
res.status(HttpStatusCodes.CREATED).json({ user: created });
|
|
41
|
-
}
|
|
45
|
+
});
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* @route PUT /api/users/update
|
|
47
|
-
*/
|
|
48
|
-
async function update(req: Req, res: Res) {
|
|
49
|
-
const { user } = reqValidators.update(req.body);
|
|
47
|
+
// Update one user
|
|
48
|
+
router.put(P.UPDATE, V.update, async (req, res) => {
|
|
49
|
+
const { user } = req.body;
|
|
50
50
|
await UserService.updateOne(user);
|
|
51
51
|
res.status(HttpStatusCodes.OK).end();
|
|
52
|
-
}
|
|
52
|
+
});
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*/
|
|
59
|
-
async function delete_(req: Req, res: Res) {
|
|
60
|
-
const { id } = reqValidators.delete(req.query);
|
|
61
|
-
await UserService.delete(id);
|
|
54
|
+
// Delete one user
|
|
55
|
+
router.delete(P.DELETE, V.delete, async (req, res) => {
|
|
56
|
+
const { id } = req.params;
|
|
57
|
+
await UserService.deleteOne(id);
|
|
62
58
|
res.status(HttpStatusCodes.OK).end();
|
|
63
|
-
}
|
|
59
|
+
});
|
|
64
60
|
|
|
65
61
|
// ========================================================================= //
|
|
66
62
|
// EXPORT //
|
|
67
63
|
// ========================================================================= //
|
|
68
64
|
|
|
69
|
-
export default
|
|
70
|
-
getAll,
|
|
71
|
-
add,
|
|
72
|
-
update,
|
|
73
|
-
delete: delete_,
|
|
74
|
-
} as const;
|
|
65
|
+
export default router;
|
|
@@ -8,12 +8,12 @@ import logger from 'jet-logger';
|
|
|
8
8
|
import morgan from 'morgan';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
|
|
11
|
+
import { RouteError } from '@src/common/classes/route-errors';
|
|
11
12
|
import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
|
|
12
13
|
import Paths from '@src/common/constants/Paths';
|
|
13
|
-
import
|
|
14
|
-
import BaseRouter from '@src/routes/apiRouter';
|
|
14
|
+
import BaseRouter from '@src/routers/api.router';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { Env, NodeEnvs } from './common/constants/env.constants';
|
|
17
17
|
|
|
18
18
|
// ========================================================================= //
|
|
19
19
|
// EXEC //
|
|
@@ -28,17 +28,17 @@ app.use(express.json());
|
|
|
28
28
|
app.use(express.urlencoded({ extended: true }));
|
|
29
29
|
|
|
30
30
|
// Show routes called in console during development
|
|
31
|
-
if (
|
|
31
|
+
if (Env.NODE_ENV === NodeEnvs.DEVELOPMENT) {
|
|
32
32
|
app.use(morgan('dev'));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Security
|
|
36
|
-
if (
|
|
36
|
+
if (Env.NODE_ENV === NodeEnvs.PRODUCTION) {
|
|
37
37
|
app.use(helmet());
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// Add APIs, must be after middleware
|
|
41
|
-
app.use(Paths.
|
|
41
|
+
app.use(Paths.PATH, BaseRouter);
|
|
42
42
|
|
|
43
43
|
// Add error handler. `_next` must stay: Express only treats 4-arg middleware
|
|
44
44
|
// as an error handler.
|
|
@@ -48,7 +48,20 @@ app.use((err: Error, _: Request, res: Response, _next: NextFunction) => {
|
|
|
48
48
|
.status(err.status)
|
|
49
49
|
.json({ error: err.message, errors: err.errors });
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
// Body-parser errors are client errors; don't expose the rejected body.
|
|
52
|
+
if ('type' in err) {
|
|
53
|
+
if (err.type === 'entity.parse.failed') {
|
|
54
|
+
return res
|
|
55
|
+
.status(HttpStatusCodes.BAD_REQUEST)
|
|
56
|
+
.json({ error: 'Invalid JSON' });
|
|
57
|
+
}
|
|
58
|
+
if (err.type === 'entity.too.large') {
|
|
59
|
+
return res
|
|
60
|
+
.status(HttpStatusCodes.PAYLOAD_TOO_LARGE)
|
|
61
|
+
.json({ error: 'Request body too large' });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (Env.NODE_ENV !== NodeEnvs.TEST) {
|
|
52
65
|
logger.err(err, true);
|
|
53
66
|
}
|
|
54
67
|
return res
|
|
@@ -58,15 +71,13 @@ app.use((err: Error, _: Request, res: Response, _next: NextFunction) => {
|
|
|
58
71
|
|
|
59
72
|
// =========================== Front-end Content =========================== //
|
|
60
73
|
|
|
61
|
-
// Views directory (html)
|
|
74
|
+
// Views/HTML directory (html)
|
|
62
75
|
const viewsDir = path.join(import.meta.dirname, 'views');
|
|
63
|
-
|
|
64
|
-
// Set static directory (js and css).
|
|
65
76
|
const staticDir = path.join(import.meta.dirname, 'public');
|
|
66
77
|
|
|
67
78
|
// Refresh the browser on changes. Imported lazily because livereload is a
|
|
68
79
|
// dev dependency and isn't installed in production.
|
|
69
|
-
if (
|
|
80
|
+
if (Env.NODE_ENV === NodeEnvs.DEVELOPMENT) {
|
|
70
81
|
const { setupLiveReload } = await import('@src/common/utils/dev-only');
|
|
71
82
|
setupLiveReload(app, [staticDir, viewsDir]);
|
|
72
83
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { RouteError } from '@src/common/classes/route-errors';
|
|
1
|
+
import { NotFoundError } from '@src/common/classes/route-errors';
|
|
3
2
|
import User, { type UserEntity, type UserInput } from '@src/entities/User';
|
|
4
3
|
import UserRepo from '@src/repos/UserRepo';
|
|
5
4
|
|
|
@@ -7,7 +6,7 @@ import UserRepo from '@src/repos/UserRepo';
|
|
|
7
6
|
// CONSTANTS //
|
|
8
7
|
// ========================================================================= //
|
|
9
8
|
|
|
10
|
-
const
|
|
9
|
+
const UserServiceErrors = {
|
|
11
10
|
USER_NOT_FOUND: 'User not found',
|
|
12
11
|
} as const;
|
|
13
12
|
|
|
@@ -27,7 +26,7 @@ function getAll(): Promise<UserEntity[]> {
|
|
|
27
26
|
*/
|
|
28
27
|
async function addOne(input: UserInput): Promise<UserEntity> {
|
|
29
28
|
const user = User.create(input);
|
|
30
|
-
await UserRepo.
|
|
29
|
+
await UserRepo.addOne(user);
|
|
31
30
|
return user;
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -36,10 +35,8 @@ async function addOne(input: UserInput): Promise<UserEntity> {
|
|
|
36
35
|
*/
|
|
37
36
|
async function updateOne(user: UserEntity): Promise<void> {
|
|
38
37
|
const persists = await UserRepo.persists(user.id);
|
|
39
|
-
if (!persists)
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
return UserRepo.update(user);
|
|
38
|
+
if (!persists) throw new NotFoundError(UserServiceErrors.USER_NOT_FOUND);
|
|
39
|
+
return UserRepo.updateOne(user);
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
/**
|
|
@@ -47,10 +44,8 @@ async function updateOne(user: UserEntity): Promise<void> {
|
|
|
47
44
|
*/
|
|
48
45
|
async function deleteOne(id: string): Promise<void> {
|
|
49
46
|
const persists = await UserRepo.persists(id);
|
|
50
|
-
if (!persists)
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return UserRepo.delete(id);
|
|
47
|
+
if (!persists) throw new NotFoundError(UserServiceErrors.USER_NOT_FOUND);
|
|
48
|
+
return UserRepo.deleteOne(id);
|
|
54
49
|
}
|
|
55
50
|
|
|
56
51
|
// ========================================================================= //
|
|
@@ -58,9 +53,9 @@ async function deleteOne(id: string): Promise<void> {
|
|
|
58
53
|
// ========================================================================= //
|
|
59
54
|
|
|
60
55
|
export default {
|
|
61
|
-
Errors,
|
|
56
|
+
Errors: UserServiceErrors,
|
|
62
57
|
getAll,
|
|
63
58
|
addOne,
|
|
64
59
|
updateOne,
|
|
65
|
-
|
|
60
|
+
deleteOne,
|
|
66
61
|
} as const;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<!-- Head -->
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
7
|
<title>Users</title>
|
|
7
8
|
<link rel="stylesheet" type="text/css" href="stylesheets/users.css" />
|
|
8
9
|
|
|
@@ -50,6 +51,13 @@
|
|
|
50
51
|
<div id="all-users-anchor"></div>
|
|
51
52
|
</div>
|
|
52
53
|
|
|
54
|
+
<!-- Error Display -->
|
|
55
|
+
<aside class="users-column errors-column" aria-labelledby="errors-heading">
|
|
56
|
+
<div id="errors-heading" class="column-header">Error Messages:</div>
|
|
57
|
+
<pre id="error-display" role="status" aria-live="polite">
|
|
58
|
+
No errors to display.</pre>
|
|
59
|
+
</aside>
|
|
60
|
+
|
|
53
61
|
<script src="/scripts/HttpClient.js"></script>
|
|
54
62
|
<script src="/scripts/renderUsers.js"></script>
|
|
55
63
|
<script src="/scripts/users.js"></script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Response } from 'supertest';
|
|
2
|
+
|
|
3
|
+
// ========================================================================= //
|
|
4
|
+
// TYPES //
|
|
5
|
+
// ========================================================================= //
|
|
6
|
+
|
|
7
|
+
export type TypedRes<T> = Omit<Response, 'body'> & { body: T };
|
|
8
|
+
export type TypedErrRes<T = 'string'> = TypedRes<{ error: T }>;
|
|
9
|
+
|
|
10
|
+
interface ValidationError {
|
|
11
|
+
errors: { message: string }[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ========================================================================= //
|
|
15
|
+
// HELPERS //
|
|
16
|
+
// ========================================================================= //
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get the error message from a superTest Response object.
|
|
20
|
+
*/
|
|
21
|
+
export function extractErrMsg(res: TypedRes<ValidationError[]>) {
|
|
22
|
+
return res.body[0].errors[0].message;
|
|
23
|
+
}
|