express-generator-typescript 2.8.1 → 2.9.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.
Files changed (71) hide show
  1. package/README.md +6 -7
  2. package/bin/cli.js +91 -32
  3. package/lib/express-generator-typescript.js +125 -151
  4. package/lib/{project-files → template}/.prettierignore +2 -4
  5. package/lib/template/README.md +50 -0
  6. package/lib/template/bs-config.js +8 -0
  7. package/lib/{project-files → template}/config/.env.development +4 -6
  8. package/lib/{project-files → template}/config/.env.production +4 -5
  9. package/lib/{project-files → template}/config/.env.test +4 -5
  10. package/lib/{project-files → template}/eslint.config.ts +4 -0
  11. package/lib/template/gitignore +10 -0
  12. package/lib/{project-files → template}/package.json +31 -35
  13. package/lib/{project-files → template}/src/common/constants/HttpStatusCodes.ts +7 -8
  14. package/lib/template/src/common/constants/Paths.ts +22 -0
  15. package/lib/template/src/common/constants/env.ts +28 -0
  16. package/lib/template/src/common/types/utility-types.ts +5 -0
  17. package/lib/template/src/common/utils/date-utils.ts +29 -0
  18. package/lib/template/src/common/utils/route-errors.ts +37 -0
  19. package/lib/template/src/main.ts +24 -0
  20. package/lib/template/src/models/User.model.ts +95 -0
  21. package/lib/template/src/models/common/types.ts +10 -0
  22. package/lib/template/src/public/scripts/http.js +25 -0
  23. package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +4229 -0
  24. package/lib/template/src/public/scripts/renderUsers.js +90 -0
  25. package/lib/template/src/public/scripts/users.js +142 -0
  26. package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +11798 -0
  27. package/lib/template/src/repos/MockOrm.ts +71 -0
  28. package/lib/{project-files → template}/src/repos/UserRepo.ts +21 -35
  29. package/lib/template/src/repos/common/database.json +28 -0
  30. package/lib/{project-files → template}/src/routes/UserRoutes.ts +15 -18
  31. package/lib/template/src/routes/apiRouter.ts +28 -0
  32. package/lib/template/src/routes/common/express-types.ts +9 -0
  33. package/lib/template/src/routes/common/parseReq.ts +23 -0
  34. package/lib/{project-files → template}/src/server.ts +23 -18
  35. package/lib/template/src/services/UserService.ts +66 -0
  36. package/lib/{project-files → template}/src/views/users.html +13 -6
  37. package/lib/{project-files → template}/tests/common/comparators.ts +6 -6
  38. package/lib/template/tests/common/supertest-types.ts +11 -0
  39. package/lib/template/tests/frontend.test.ts +33 -0
  40. package/lib/template/tests/support/agent.ts +23 -0
  41. package/lib/template/tests/users.test.ts +205 -0
  42. package/lib/{project-files → template}/tsconfig.json +1 -3
  43. package/lib/{project-files → template}/tsconfig.prod.json +3 -9
  44. package/lib/{project-files → template}/vitest.config.mts +2 -0
  45. package/package.json +69 -52
  46. package/lib/cli.js +0 -33
  47. package/lib/project-files/README.md +0 -39
  48. package/lib/project-files/gitignore +0 -4
  49. package/lib/project-files/src/common/constants/Paths.ts +0 -15
  50. package/lib/project-files/src/common/constants/env.ts +0 -28
  51. package/lib/project-files/src/common/types/structure-utils.ts +0 -2
  52. package/lib/project-files/src/common/utils/number-utils.ts +0 -10
  53. package/lib/project-files/src/common/utils/route-errors.ts +0 -35
  54. package/lib/project-files/src/common/utils/validators.ts +0 -14
  55. package/lib/project-files/src/main.ts +0 -24
  56. package/lib/project-files/src/models/User.model.ts +0 -72
  57. package/lib/project-files/src/models/common/types.ts +0 -4
  58. package/lib/project-files/src/public/stylesheets/lib/bootstrap.min.css +0 -6
  59. package/lib/project-files/src/repos/MockOrm.ts +0 -63
  60. package/lib/project-files/src/repos/common/database.json +0 -1
  61. package/lib/project-files/src/repos/common/database.test.json +0 -1
  62. package/lib/project-files/src/routes/apiRouter.ts +0 -28
  63. package/lib/project-files/src/routes/common/express-types.ts +0 -8
  64. package/lib/project-files/src/routes/common/parseReq.ts +0 -19
  65. package/lib/project-files/src/services/UserService.ts +0 -64
  66. package/lib/project-files/tests/common/error-utils.ts +0 -25
  67. package/lib/project-files/tests/common/supertest-types.ts +0 -15
  68. package/lib/project-files/tests/support/agent.ts +0 -23
  69. package/lib/project-files/tests/users.test.ts +0 -131
  70. /package/lib/{project-files → template}/.prettierrc.json +0 -0
  71. /package/lib/{project-files → template}/src/public/stylesheets/users.css +0 -0
@@ -0,0 +1,71 @@
1
+ import fs from 'fs/promises';
2
+ import jsonfile from 'jsonfile';
3
+ import path from 'path';
4
+
5
+ import EnvVars, { NodeEnvs } from '@src/common/constants/env';
6
+ import { IUser } from '@src/models/User.model';
7
+
8
+ // ========================================================================= //
9
+ // CONSTANTS //
10
+ // ========================================================================= //
11
+
12
+ const DATABASE_FILE_PATH = path.join(
13
+ __dirname,
14
+ 'common',
15
+ EnvVars.NodeEnv === NodeEnvs.TEST ? 'database.test.json' : 'database.json',
16
+ );
17
+
18
+ // ========================================================================= //
19
+ // TYPES //
20
+ // ========================================================================= //
21
+
22
+ type Database = {
23
+ users: IUser[];
24
+ };
25
+
26
+ // ========================================================================= //
27
+ // FUNCTIONS //
28
+ // ========================================================================= //
29
+
30
+ // NOTE: Every write is a read-modify-write of one JSON file with no locking,
31
+ // so concurrent requests can overwrite each other. Swap this module for a
32
+ // real database before relying on it.
33
+
34
+ /**
35
+ * Fetch the json from the file. A missing file is an empty database.
36
+ */
37
+ async function openDb(): Promise<Database> {
38
+ let db: Partial<Database>;
39
+ try {
40
+ db = (await jsonfile.readFile(DATABASE_FILE_PATH)) as Partial<Database>;
41
+ } catch (err) {
42
+ if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
43
+ db = {};
44
+ }
45
+ return { users: db.users ?? [] };
46
+ }
47
+
48
+ /**
49
+ * Update the file, creating its folder if needed.
50
+ */
51
+ async function saveDb(db: Database): Promise<void> {
52
+ await fs.mkdir(path.dirname(DATABASE_FILE_PATH), { recursive: true });
53
+ return jsonfile.writeFile(DATABASE_FILE_PATH, db, { spaces: 2 });
54
+ }
55
+
56
+ /**
57
+ * Empty the database
58
+ */
59
+ function cleanDb(): Promise<void> {
60
+ return saveDb({ users: [] });
61
+ }
62
+
63
+ // ========================================================================= //
64
+ // EXPORT //
65
+ // ========================================================================= //
66
+
67
+ export default {
68
+ openDb,
69
+ saveDb,
70
+ cleanDb,
71
+ } as const;
@@ -1,29 +1,18 @@
1
- import { getRandomInt } from '@src/common/utils/number-utils';
1
+ import jetid from 'jet-id';
2
+
3
+ import { getISOString } from '@src/common/utils/date-utils';
2
4
  import { IUser } from '@src/models/User.model';
3
5
 
4
6
  import orm from './MockOrm';
5
7
 
6
- /******************************************************************************
7
- Functions
8
- ******************************************************************************/
9
-
10
- /**
11
- * Get one user.
12
- */
13
- async function getOne(email: string): Promise<IUser | null> {
14
- const db = await orm.openDb();
15
- for (const user of db.users) {
16
- if (user.email === email) {
17
- return user;
18
- }
19
- }
20
- return null;
21
- }
8
+ // ========================================================================= //
9
+ // FUNCTIONS //
10
+ // ========================================================================= //
22
11
 
23
12
  /**
24
13
  * See if a user with the given id exists.
25
14
  */
26
- async function persists(id: number): Promise<boolean> {
15
+ async function persists(id: string): Promise<boolean> {
27
16
  const db = await orm.openDb();
28
17
  for (const user of db.users) {
29
18
  if (user.id === id) {
@@ -46,7 +35,6 @@ async function getAll(): Promise<IUser[]> {
46
35
  */
47
36
  async function add(user: IUser): Promise<void> {
48
37
  const db = await orm.openDb();
49
- user.id = getRandomInt();
50
38
  db.users.push(user);
51
39
  return orm.saveDb(db);
52
40
  }
@@ -72,7 +60,7 @@ async function update(user: IUser): Promise<void> {
72
60
  /**
73
61
  * Delete one user.
74
62
  */
75
- async function delete_(id: number): Promise<void> {
63
+ async function delete_(id: string): Promise<void> {
76
64
  const db = await orm.openDb();
77
65
  for (let i = 0; i < db.users.length; i++) {
78
66
  if (db.users[i].id === id) {
@@ -82,7 +70,7 @@ async function delete_(id: number): Promise<void> {
82
70
  }
83
71
  }
84
72
 
85
- // **** Unit-Tests Only **** //
73
+ // ============================ Unit-tests Only ============================ //
86
74
 
87
75
  /**
88
76
  * @testOnly
@@ -98,29 +86,27 @@ async function deleteAllUsers(): Promise<void> {
98
86
  /**
99
87
  * @testOnly
100
88
  *
101
- * Insert multiple users. Can't do multiple at once cause using a plain file
102
- * for now.
89
+ * Insert copies of `users` with fresh ids; the inputs are not modified.
103
90
  */
104
91
  async function insertMultiple(
105
92
  users: IUser[] | readonly IUser[],
106
93
  ): Promise<IUser[]> {
107
- const db = await orm.openDb(),
108
- usersF = [...users];
109
- for (const user of usersF) {
110
- user.id = getRandomInt();
111
- user.created = new Date();
112
- }
113
- db.users = [...db.users, ...users];
94
+ const db = await orm.openDb();
95
+ const inserted = users.map((user) => ({
96
+ ...user,
97
+ id: jetid(),
98
+ created: getISOString(),
99
+ }));
100
+ db.users = [...db.users, ...inserted];
114
101
  await orm.saveDb(db);
115
- return usersF;
102
+ return inserted;
116
103
  }
117
104
 
118
- /******************************************************************************
119
- Export default
120
- ******************************************************************************/
105
+ // ========================================================================= //
106
+ // EXPORT //
107
+ // ========================================================================= //
121
108
 
122
109
  export default {
123
- getOne,
124
110
  persists,
125
111
  getAll,
126
112
  add,
@@ -0,0 +1,28 @@
1
+ {
2
+ "users": [
3
+ {
4
+ "id": "2HF7W6BZV-23RYN-0GY0E-80X19N",
5
+ "name": "Sean Maxwell",
6
+ "email": "smaxwell@example.com",
7
+ "created": "2024-03-22T05:14:36.252Z"
8
+ },
9
+ {
10
+ "id": "56N1CSRF5-SG3XG-M7CEY-TE5G45",
11
+ "name": "John Smith",
12
+ "email": "john.smith@example.com",
13
+ "created": "2024-03-22T05:20:55.079Z"
14
+ },
15
+ {
16
+ "id": "Q78BTED9D-HBPJQ-GFEYQ-3EVFJF",
17
+ "name": "Gordan Freeman",
18
+ "email": "nova@prospect.com",
19
+ "created": "2024-03-22T05:42:18.895Z"
20
+ },
21
+ {
22
+ "id": "NEQXY1EYZ-66K6S-AMGXW-BWKYAY",
23
+ "name": "asdf",
24
+ "email": "asdf",
25
+ "created": "2026-09-25T22:31:30.844Z"
26
+ }
27
+ ]
28
+ }
@@ -1,6 +1,3 @@
1
- import { isNumber } from 'jet-validators';
2
- import { transform } from 'jet-validators/utils';
3
-
4
1
  import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
5
2
  import User from '@src/models/User.model';
6
3
  import UserService from '@src/services/UserService';
@@ -8,19 +5,19 @@ import UserService from '@src/services/UserService';
8
5
  import { Req, Res } from './common/express-types';
9
6
  import parseReq from './common/parseReq';
10
7
 
11
- /******************************************************************************
12
- Constants
13
- ******************************************************************************/
8
+ // ========================================================================= //
9
+ // EXEC //
10
+ // ========================================================================= //
14
11
 
15
12
  const reqValidators = {
16
- add: parseReq({ user: User.isComplete }),
13
+ add: parseReq({ user: User.isInput }),
17
14
  update: parseReq({ user: User.isComplete }),
18
- delete: parseReq({ id: transform(Number, isNumber) }),
15
+ delete: parseReq({ id: User.isId }),
19
16
  } as const;
20
17
 
21
- /******************************************************************************
22
- Functions
23
- ******************************************************************************/
18
+ // ========================================================================= //
19
+ // FUNCTIONS //
20
+ // ========================================================================= //
24
21
 
25
22
  /**
26
23
  * Get all users.
@@ -39,8 +36,8 @@ async function getAll(_: Req, res: Res) {
39
36
  */
40
37
  async function add(req: Req, res: Res) {
41
38
  const { user } = reqValidators.add(req.body);
42
- await UserService.addOne(user);
43
- res.status(HttpStatusCodes.CREATED).end();
39
+ const created = await UserService.addOne(user);
40
+ res.status(HttpStatusCodes.CREATED).json({ user: created });
44
41
  }
45
42
 
46
43
  /**
@@ -57,17 +54,17 @@ async function update(req: Req, res: Res) {
57
54
  /**
58
55
  * Delete one user.
59
56
  *
60
- * @route DELETE /api/users/delete/:id
57
+ * @route DELETE /api/users/delete?id=:id
61
58
  */
62
59
  async function delete_(req: Req, res: Res) {
63
- const { id } = reqValidators.delete(req.params);
60
+ const { id } = reqValidators.delete(req.query);
64
61
  await UserService.delete(id);
65
62
  res.status(HttpStatusCodes.OK).end();
66
63
  }
67
64
 
68
- /******************************************************************************
69
- Export default
70
- ******************************************************************************/
65
+ // ========================================================================= //
66
+ // EXPORT //
67
+ // ========================================================================= //
71
68
 
72
69
  export default {
73
70
  getAll,
@@ -0,0 +1,28 @@
1
+ import { Router } from 'express';
2
+
3
+ import Paths from '@src/common/constants/Paths';
4
+
5
+ import UserRoutes from './UserRoutes';
6
+
7
+ // ========================================================================= //
8
+ // EXEC //
9
+ // ========================================================================= //
10
+
11
+ const apiRouter = Router();
12
+
13
+ // ============================ Add `userRouter` =========================== //
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);
23
+
24
+ // ========================================================================= //
25
+ // EXPORT //
26
+ // ========================================================================= //
27
+
28
+ export default apiRouter;
@@ -0,0 +1,9 @@
1
+ import { Request, Response } from 'express';
2
+
3
+ // ========================================================================= //
4
+ // TYPES //
5
+ // ========================================================================= //
6
+
7
+ // Body is `unknown` until it has been validated with `parseReq`.
8
+ export type Req = Request<Record<string, string>, unknown, unknown>;
9
+ export type Res = Response;
@@ -0,0 +1,23 @@
1
+ import { parseObject, Schema } from 'jet-validators/utils';
2
+
3
+ import { ValidationError } from '@src/common/utils/route-errors';
4
+
5
+ // ========================================================================= //
6
+ // FUNCTIONS //
7
+ // ========================================================================= //
8
+
9
+ /**
10
+ * Build a request parser for `schema` that throws a `ValidationError`
11
+ * (400) listing every failed field.
12
+ */
13
+ function parseReq<U extends Schema>(schema: U) {
14
+ return parseObject(schema, (errors) => {
15
+ throw new ValidationError(errors);
16
+ });
17
+ }
18
+
19
+ // ========================================================================= //
20
+ // EXPORT //
21
+ // ========================================================================= //
22
+
23
+ export default parseReq;
@@ -4,19 +4,20 @@ import logger from 'jet-logger';
4
4
  import morgan from 'morgan';
5
5
  import path from 'path';
6
6
 
7
+ import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
7
8
  import Paths from '@src/common/constants/Paths';
8
9
  import { RouteError } from '@src/common/utils/route-errors';
9
10
  import BaseRouter from '@src/routes/apiRouter';
10
11
 
11
12
  import EnvVars, { NodeEnvs } from './common/constants/env';
12
13
 
13
- /******************************************************************************
14
- Setup
15
- ******************************************************************************/
14
+ // ========================================================================= //
15
+ // EXEC //
16
+ // ========================================================================= //
16
17
 
17
18
  const app = express();
18
19
 
19
- // **** Middleware **** //
20
+ // =============================== Middleware ============================== //
20
21
 
21
22
  // Basic middleware
22
23
  app.use(express.json());
@@ -35,22 +36,26 @@ if (EnvVars.NodeEnv === NodeEnvs.PRODUCTION) {
35
36
  // Add APIs, must be after middleware
36
37
  app.use(Paths._, BaseRouter);
37
38
 
38
- // Add error handler
39
- app.use((err: Error, _: Request, res: Response, next: NextFunction) => {
40
- if (EnvVars.NodeEnv !== NodeEnvs.TEST.valueOf()) {
41
- logger.err(err, true);
42
- }
39
+ // Add error handler. `_next` must stay: Express only treats 4-arg middleware
40
+ // as an error handler.
41
+ app.use((err: Error, _: Request, res: Response, _next: NextFunction) => {
43
42
  if (err instanceof RouteError) {
44
- res.status(err.status).json({ error: err.message });
43
+ return res
44
+ .status(err.status)
45
+ .json({ error: err.message, errors: err.errors });
46
+ }
47
+ if (EnvVars.NodeEnv !== NodeEnvs.TEST) {
48
+ logger.err(err, true);
45
49
  }
46
- return next(err);
50
+ return res
51
+ .status(HttpStatusCodes.INTERNAL_SERVER_ERROR)
52
+ .json({ error: 'Internal Server Error' });
47
53
  });
48
54
 
49
- // **** FrontEnd Content **** //
55
+ // =========================== Front-end Content =========================== //
50
56
 
51
- // Set views directory (html)
57
+ // Views directory (html)
52
58
  const viewsDir = path.join(__dirname, 'views');
53
- app.set('views', viewsDir);
54
59
 
55
60
  // Set static directory (js and css).
56
61
  const staticDir = path.join(__dirname, 'public');
@@ -61,13 +66,13 @@ app.get('/', (_: Request, res: Response) => {
61
66
  return res.redirect('/users');
62
67
  });
63
68
 
64
- // Redirect to login if not logged in.
69
+ // Users page
65
70
  app.get('/users', (_: Request, res: Response) => {
66
71
  return res.sendFile('users.html', { root: viewsDir });
67
72
  });
68
73
 
69
- /******************************************************************************
70
- Export default
71
- ******************************************************************************/
74
+ // ========================================================================= //
75
+ // EXPORT //
76
+ // ========================================================================= //
72
77
 
73
78
  export default app;
@@ -0,0 +1,66 @@
1
+ import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
2
+ import { RouteError } from '@src/common/utils/route-errors';
3
+ import User, { IUser, IUserInput } from '@src/models/User.model';
4
+ import UserRepo from '@src/repos/UserRepo';
5
+
6
+ // ========================================================================= //
7
+ // CONSTANTS //
8
+ // ========================================================================= //
9
+
10
+ const Errors = {
11
+ USER_NOT_FOUND: 'User not found',
12
+ } as const;
13
+
14
+ // ========================================================================= //
15
+ // FUNCTIONS //
16
+ // ========================================================================= //
17
+
18
+ /**
19
+ * Get all users.
20
+ */
21
+ function getAll(): Promise<IUser[]> {
22
+ return UserRepo.getAll();
23
+ }
24
+
25
+ /**
26
+ * Create a user from client input; the id and created date are set here.
27
+ */
28
+ async function addOne(input: IUserInput): Promise<IUser> {
29
+ const user = User.new(input);
30
+ await UserRepo.add(user);
31
+ return user;
32
+ }
33
+
34
+ /**
35
+ * Update one user.
36
+ */
37
+ async function updateOne(user: IUser): Promise<void> {
38
+ const persists = await UserRepo.persists(user.id);
39
+ if (!persists) {
40
+ throw new RouteError(HttpStatusCodes.NOT_FOUND, Errors.USER_NOT_FOUND);
41
+ }
42
+ return UserRepo.update(user);
43
+ }
44
+
45
+ /**
46
+ * Delete a user by their id.
47
+ */
48
+ async function deleteOne(id: string): Promise<void> {
49
+ const persists = await UserRepo.persists(id);
50
+ if (!persists) {
51
+ throw new RouteError(HttpStatusCodes.NOT_FOUND, Errors.USER_NOT_FOUND);
52
+ }
53
+ return UserRepo.delete(id);
54
+ }
55
+
56
+ // ========================================================================= //
57
+ // EXPORT //
58
+ // ========================================================================= //
59
+
60
+ export default {
61
+ Errors,
62
+ getAll,
63
+ addOne,
64
+ updateOne,
65
+ delete: deleteOne,
66
+ } as const;
@@ -7,8 +7,15 @@
7
7
  <link rel="stylesheet" type="text/css" href="stylesheets/users.css" />
8
8
 
9
9
  <!-- Bootstrap -->
10
- <link href="stylesheets/lib/bootstrap.min.css" type="text/css" rel="stylesheet"/>
11
- <script type="application/javascript" src="/scripts/lib/bootstrap.bundle.min.js"></script>
10
+ <link
11
+ href="stylesheets/lib/bootstrap.min.css"
12
+ type="text/css"
13
+ rel="stylesheet"
14
+ />
15
+ <script
16
+ type="application/javascript"
17
+ src="/scripts/lib/bootstrap.bundle.min.js"
18
+ ></script>
12
19
  </head>
13
20
 
14
21
  <!-- Body -->
@@ -42,9 +49,9 @@
42
49
  <div class="column-header">Users:</div>
43
50
  <div id="all-users-anchor"></div>
44
51
  </div>
45
- </body>
46
52
 
47
- <script src="/scripts/http.js"></script>
48
- <script src="/scripts/render-users.js"></script>
49
- <script src="/scripts/users.js"></script>
53
+ <script src="/scripts/http.js"></script>
54
+ <script src="/scripts/renderUsers.js"></script>
55
+ <script src="/scripts/users.js"></script>
56
+ </body>
50
57
  </html>
@@ -1,14 +1,14 @@
1
1
  import { IUser } from '@src/models/User.model';
2
2
 
3
- /******************************************************************************
4
- Types
5
- ******************************************************************************/
3
+ // ========================================================================= //
4
+ // TYPES //
5
+ // ========================================================================= //
6
6
 
7
7
  type UserArray = IUser[] | readonly IUser[];
8
8
 
9
- /******************************************************************************
10
- Functions
11
- ******************************************************************************/
9
+ // ========================================================================= //
10
+ // FUNCTIONS //
11
+ // ========================================================================= //
12
12
 
13
13
  /**
14
14
  * Compare to user arrays. Order does not matter and this assumes the email
@@ -0,0 +1,11 @@
1
+ import { ParseError } from 'jet-validators/utils';
2
+ import { Response } from 'supertest';
3
+
4
+ // ========================================================================= //
5
+ // TYPES //
6
+ // ========================================================================= //
7
+
8
+ // Use generics to add properties to 'body'
9
+ export interface TestRes<T = object> extends Omit<Response, 'body'> {
10
+ body: T & { error?: string; errors?: ParseError[] };
11
+ }
@@ -0,0 +1,33 @@
1
+ import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
2
+
3
+ import { agent } from './support/agent';
4
+
5
+ // ========================================================================= //
6
+ // TESTS //
7
+ // ========================================================================= //
8
+
9
+ describe('Front-end content', () => {
10
+ it('should redirect "/" to the users page.', async () => {
11
+ const res = await agent.get('/');
12
+ expect(res.status).toBe(HttpStatusCodes.FOUND);
13
+ expect(res.headers.location).toBe('/users');
14
+ });
15
+
16
+ it('should serve the users page.', async () => {
17
+ const res = await agent.get('/users');
18
+ expect(res.status).toBe(HttpStatusCodes.OK);
19
+ expect(res.headers['content-type']).toMatch(/html/);
20
+ });
21
+
22
+ it.each([
23
+ '/scripts/http.js',
24
+ '/scripts/renderUsers.js',
25
+ '/scripts/users.js',
26
+ '/scripts/lib/bootstrap.bundle.min.js',
27
+ '/stylesheets/users.css',
28
+ '/stylesheets/lib/bootstrap.min.css',
29
+ ])('should serve the static asset "%s".', async (asset) => {
30
+ const res = await agent.get(asset);
31
+ expect(res.status).toBe(HttpStatusCodes.OK);
32
+ });
33
+ });
@@ -0,0 +1,23 @@
1
+ import supertest, { Test } from 'supertest';
2
+ import TestAgent from 'supertest/lib/agent';
3
+ import { beforeAll } from 'vitest';
4
+
5
+ import MockOrm from '@src/repos/MockOrm';
6
+ import app from '@src/server';
7
+
8
+ // ========================================================================= //
9
+ // EXEC //
10
+ // ========================================================================= //
11
+
12
+ let agent: TestAgent<Test>;
13
+
14
+ beforeAll(async () => {
15
+ agent = supertest.agent(app);
16
+ await MockOrm.cleanDb();
17
+ });
18
+
19
+ // ========================================================================= //
20
+ // EXPORT //
21
+ // ========================================================================= //
22
+
23
+ export { agent };