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.
Files changed (37) hide show
  1. package/README.md +7 -3
  2. package/bin/cli.js +1 -20
  3. package/lib/template/README.md +4 -3
  4. package/lib/template/package.json +8 -6
  5. package/lib/template/src/common/classes/route-errors.ts +4 -8
  6. package/lib/template/src/common/constants/HttpStatusCodes.ts +2 -7
  7. package/lib/template/src/common/constants/{Paths.ts → Paths/Paths.ts} +16 -9
  8. package/lib/template/src/common/constants/Paths/helpers.ts +29 -0
  9. package/lib/template/src/common/constants/Paths/index.ts +1 -0
  10. package/lib/template/src/common/constants/env.constants.ts +42 -0
  11. package/lib/template/src/common/utils/{date-utils.ts → date.utils.ts} +10 -12
  12. package/lib/template/src/entities/User/module.ts +77 -28
  13. package/lib/template/src/entities/User/types.ts +1 -1
  14. package/lib/template/src/entities/common/{types.ts → entity-base-types.ts} +1 -1
  15. package/lib/template/src/main.ts +3 -4
  16. package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +0 -1
  17. package/lib/template/src/public/scripts/users.js +20 -11
  18. package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +0 -1
  19. package/lib/template/src/public/stylesheets/users.css +24 -1
  20. package/lib/template/src/repos/MockOrm.ts +2 -2
  21. package/lib/template/src/repos/UserRepo.ts +20 -27
  22. package/lib/template/src/repos/common/database.json +3 -3
  23. package/lib/template/src/{routes/apiRouter.ts → routers/api.router.ts} +3 -11
  24. package/lib/template/src/{routes/UserRoutes.ts → routers/user.router.ts} +38 -47
  25. package/lib/template/src/server.ts +22 -11
  26. package/lib/template/src/services/UserService.ts +9 -14
  27. package/lib/template/src/views/users.html +8 -0
  28. package/lib/template/tests/support/utils.ts +23 -0
  29. package/lib/template/tests/users.test.ts +182 -140
  30. package/package.json +6 -5
  31. package/lib/template/src/common/constants/env-inv.ts +0 -32
  32. package/lib/template/src/entities/User/_internal/validators.ts +0 -47
  33. package/lib/template/src/routes/common/express-types.ts +0 -9
  34. package/lib/template/src/routes/common/parseReq.ts +0 -23
  35. package/lib/template/tests/common/comparators.ts +0 -38
  36. package/lib/template/tests/common/supertest-types.ts +0 -11
  37. /package/lib/template/src/common/{types/utility-types.ts → utils/validators.ts} +0 -0
@@ -1,29 +1,56 @@
1
- import jetid from 'jet-id';
2
-
3
1
  import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
4
2
  import Paths from '@src/common/constants/Paths';
5
- import { ValidationError } from '@src/common/classes/route-errors';
6
3
  import User, { type UserEntity } from '@src/entities/User';
7
- import UserRepo from '@src/repos/UserRepo';
4
+ import UserRepo, { UserRepoTestOnly } from '@src/repos/UserRepo';
8
5
  import UserService from '@src/services/UserService';
9
6
 
10
- import { compareUserArrays } from './common/comparators';
11
- import type { TestRes } from './common/supertest-types';
12
7
  import { agent } from './support/agent';
8
+ import {
9
+ extractErrMsg,
10
+ type TypedErrRes,
11
+ type TypedRes,
12
+ } from './support/utils';
13
13
 
14
14
  // ========================================================================= //
15
15
  // CONSTANTS //
16
16
  // ========================================================================= //
17
17
 
18
+ // Shorthand the `Paths`
19
+ const P = {
20
+ Get: {
21
+ CALL: Paths.Users.Get.FULL_PATH,
22
+ DISPLAY: Paths.Users.Get.DISPLAY,
23
+ },
24
+ Add: {
25
+ CALL: Paths.Users.Add.FULL_PATH,
26
+ DISPLAY: Paths.Users.Add.DISPLAY,
27
+ },
28
+ Update: {
29
+ CALL: Paths.Users.Update.FULL_PATH,
30
+ DISPLAY: Paths.Users.Update.DISPLAY,
31
+ },
32
+ Delete: {
33
+ CALL: Paths.Users.Delete.format,
34
+ DISPLAY: Paths.Users.Delete.DISPLAY,
35
+ FULL_PATH: Paths.Users.Delete.FULL_PATH,
36
+ },
37
+ } as const;
38
+
39
+ // Errors
40
+ const Errors = {
41
+ ...UserService.Errors,
42
+ } as const;
43
+
44
+ // Status codes
45
+ const { BAD_REQUEST, CREATED, INTERNAL_SERVER_ERROR, OK, NOT_FOUND } =
46
+ HttpStatusCodes;
47
+
18
48
  const DUMMY_USERS = [
19
49
  User.of('Sean Maxwell', 'sean.maxwell@gmail.com'),
20
50
  User.of('John Smith', 'john.smith@gmail.com'),
21
51
  User.of('Gordan Freeman', 'gordan.freeman@gmail.com'),
22
52
  ] as const;
23
53
 
24
- const { BAD_REQUEST, CREATED, INTERNAL_SERVER_ERROR, OK, NOT_FOUND } =
25
- HttpStatusCodes;
26
-
27
54
  // ========================================================================= //
28
55
  // TESTS //
29
56
  // ========================================================================= //
@@ -35,171 +62,186 @@ describe('UserRouter', () => {
35
62
  let dbUsers: UserEntity[] = [];
36
63
 
37
64
  beforeEach(async () => {
38
- await UserRepo.deleteAllUsers();
39
- dbUsers = await UserRepo.insertMultiple(DUMMY_USERS);
65
+ await UserRepoTestOnly.deleteAllUsers();
66
+ dbUsers = await UserRepoTestOnly.insertMultiple(DUMMY_USERS);
40
67
  });
41
68
 
42
69
  afterEach(() => {
43
70
  vi.restoreAllMocks();
44
71
  });
45
72
 
46
- // ---- `Get`
47
- describe(`"GET:${Paths.Users.Get()}"`, () => {
48
- it(
49
- 'should return a JSON object with all the users and a status code of ' +
50
- `"${OK}" if the request was successful.`,
51
- async () => {
52
- const res: TestRes<{ users: UserEntity[] }> = await agent.get(
53
- Paths.Users.Get(),
73
+ // --- `Get` ---
74
+ describe(P.Get.DISPLAY, () => {
75
+ describe('on success', () => {
76
+ it(`returns all users as JSON with status "${OK}"`, async () => {
77
+ const res: TypedRes<{ users: UserEntity[] }> = await agent.get(
78
+ P.Get.CALL,
54
79
  );
55
80
  expect(res.status).toBe(OK);
56
- expect(compareUserArrays(res.body.users, DUMMY_USERS)).toBeTruthy();
57
- },
58
- );
59
-
60
- it(
61
- `should return a JSON error and a status code of ` +
62
- `"${INTERNAL_SERVER_ERROR}" if an unexpected error is thrown.`,
63
- async () => {
64
- vi.spyOn(UserService, 'getAll').mockRejectedValueOnce(
65
- new Error('boom'),
66
- );
67
- const res: TestRes = await agent.get(Paths.Users.Get());
81
+ expect(res.body.users).toStrictEqual(DUMMY_USERS);
82
+ });
83
+ });
84
+
85
+ describe('when an unexpected error is thrown', () => {
86
+ it(`returns status "${INTERNAL_SERVER_ERROR}"`, async () => {
87
+ const error = new Error('boom');
88
+ const getAllSpy = vi.spyOn(UserService, 'getAll');
89
+ getAllSpy.mockRejectedValueOnce(error);
90
+ const res: TypedErrRes = await agent.get(P.Get.CALL);
68
91
  expect(res.status).toBe(INTERNAL_SERVER_ERROR);
69
92
  expect(res.body.error).toBe('Internal Server Error');
70
- },
71
- );
93
+ });
94
+ });
72
95
  });
73
96
 
74
- // ---- `Add`
75
- describe(`"POST:${Paths.Users.Add()}"`, () => {
76
- it(
77
- `should return a status code of "${CREATED}" and persist the user ` +
78
- 'with a server-generated id if the request was successful.',
79
- async () => {
97
+ // --- `Add` ---
98
+ describe(P.Add.DISPLAY, () => {
99
+ describe('on success', () => {
100
+ it(`persists the user and returns status "${CREATED}"`, async () => {
80
101
  // Same payload shape the front-end sends
81
102
  const input = { name: 'a', email: 'a@a.com' };
82
- const res: TestRes<{ user: UserEntity }> = await agent
83
- .post(Paths.Users.Add())
103
+ const res: TypedRes<{ user: UserEntity }> = await agent
104
+ .post(P.Add.CALL)
84
105
  .send({ user: input });
85
106
  expect(res.status).toBe(CREATED);
86
- expect(User.isId(res.body.user.id)).toBe(true);
107
+ const isId = User.is.id(res.body.user.id);
108
+ expect(isId).toBe(true);
87
109
  const all = await UserRepo.getAll();
88
110
  expect(all).toHaveLength(DUMMY_USERS.length + 1);
89
111
  expect(all).toContainEqual(res.body.user);
90
- },
91
- );
92
-
93
- it(
94
- 'should return a JSON object with an error message and a status ' +
95
- `code of "${BAD_REQUEST}" if the user param was missing.`,
96
- async () => {
97
- const res: TestRes = await agent
98
- .post(Paths.Users.Add())
99
- .send({ user: null });
112
+ });
113
+ });
114
+
115
+ describe('when the user param is missing', () => {
116
+ it('returns a validation error', async () => {
117
+ const res = await agent.post(P.Add.CALL).send({ user: null });
100
118
  expect(res.status).toBe(BAD_REQUEST);
101
- expect(res.body.error).toBe(ValidationError.MESSAGE);
102
- expect(res.body.errors?.[0].key).toStrictEqual('user');
103
- },
104
- );
105
-
106
- it(
107
- `should return a status code of "${BAD_REQUEST}" if the name is ` +
108
- 'empty.',
109
- async () => {
110
- const res: TestRes = await agent
111
- .post(Paths.Users.Add())
119
+ const errMsg = extractErrMsg(res);
120
+ expect(errMsg).toBe('Invalid input: expected object, received null');
121
+ });
122
+ });
123
+
124
+ describe('when the name is empty', () => {
125
+ it(`returns status "${BAD_REQUEST}"`, async () => {
126
+ const res = await agent
127
+ .post(P.Add.CALL)
112
128
  .send({ user: { name: '', email: 'a@a.com' } });
113
129
  expect(res.status).toBe(BAD_REQUEST);
114
- expect(await UserRepo.getAll()).toHaveLength(DUMMY_USERS.length);
115
- },
116
- );
130
+ // Verify it didn't persist in the database
131
+ const allUsers = await UserRepo.getAll();
132
+ expect(allUsers).toHaveLength(DUMMY_USERS.length);
133
+ });
134
+ });
135
+
136
+ describe('when the request body cannot be parsed', () => {
137
+ it('returns 400 for malformed JSON without persisting a user', async () => {
138
+ const res: TypedErrRes = await agent
139
+ .post(P.Add.CALL)
140
+ .set('Content-Type', 'application/json')
141
+ .send('{"user":');
142
+ expect(res.status).toBe(BAD_REQUEST);
143
+ expect(res.body).toStrictEqual({ error: 'Invalid JSON' });
144
+ expect(await UserRepo.getAll()).toStrictEqual(DUMMY_USERS);
145
+ });
146
+
147
+ it.each([
148
+ [
149
+ 'application/json',
150
+ JSON.stringify({ user: { name: 'a'.repeat(110000) } }),
151
+ ],
152
+ [
153
+ 'application/x-www-form-urlencoded',
154
+ `user[name]=${'a'.repeat(110000)}`,
155
+ ],
156
+ ])('returns 413 for an oversized %s body', async (contentType, body) => {
157
+ const res: TypedErrRes = await agent
158
+ .post(P.Add.CALL)
159
+ .set('Content-Type', contentType)
160
+ .send(body);
161
+ expect(res.status).toBe(HttpStatusCodes.PAYLOAD_TOO_LARGE);
162
+ expect(res.body).toStrictEqual({ error: 'Request body too large' });
163
+ expect(await UserRepo.getAll()).toStrictEqual(DUMMY_USERS);
164
+ });
165
+ });
117
166
  });
118
167
 
119
- // ---- `Update`
120
- describe(`"PUT:${Paths.Users.Update()}"`, () => {
121
- it(
122
- `should return a status code of "${OK}" and save the change if the ` +
123
- 'request was successful.',
124
- async () => {
168
+ // --- `Update` ---
169
+ describe(P.Update.DISPLAY, () => {
170
+ describe('on success', () => {
171
+ it(`saves the change and returns status "${OK}"`, async () => {
125
172
  const user = { ...dbUsers[0], name: 'Bill' };
126
- const res = await agent.put(Paths.Users.Update()).send({ user });
173
+ const res = await agent.put(P.Update.CALL).send({ user });
127
174
  expect(res.status).toBe(OK);
128
- const saved = (await UserRepo.getAll()).find((u) => u.id === user.id);
175
+ const allUsers = await UserRepo.getAll();
176
+ const saved = allUsers.find((u) => u.id === user.id);
129
177
  expect(saved?.name).toBe('Bill');
130
- },
131
- );
132
-
133
- it(
134
- 'should return a JSON object with an error message and a status code ' +
135
- `of "${BAD_REQUEST}" if id is not a valid id`,
136
- async () => {
137
- const user = { ...User.create(), name: 'a', email: 'a@a.com', id: '5' };
138
- const res: TestRes = await agent
139
- .put(Paths.Users.Update())
140
- .send({ user });
178
+ });
179
+ });
180
+
181
+ describe('when the id is invalid', () => {
182
+ it('returns a validation error', async () => {
183
+ const newUser = User.create();
184
+ const user = {
185
+ ...newUser,
186
+ name: 'a',
187
+ email: 'a@a.com',
188
+ id: '5',
189
+ };
190
+ const res = await agent.put(P.Update.CALL).send({ user });
141
191
  expect(res.status).toBe(BAD_REQUEST);
142
- expect(res.body.error).toBe(ValidationError.MESSAGE);
143
- expect(res.body.errors?.[0].keyPath).toStrictEqual(['user', 'id']);
144
- },
145
- );
146
-
147
- it(
148
- 'should return a JSON object with the error message of ' +
149
- `"${UserService.Errors.USER_NOT_FOUND}" and a status code of ` +
150
- `"${NOT_FOUND}" if the id was not found.`,
151
- async () => {
152
- const user = User.create({ id: jetid(), name: 'a', email: 'a@a.com' }),
153
- res: TestRes = await agent.put(Paths.Users.Update()).send({ user });
192
+ const errMsg = extractErrMsg(res);
193
+ expect(errMsg).toBe(errMsg);
194
+ });
195
+ });
196
+
197
+ describe('when the user is not found', () => {
198
+ it(`returns status "${NOT_FOUND}"`, async () => {
199
+ const user = User.create({ name: 'a', email: 'a@a.com' });
200
+ const res: TypedErrRes = await agent.put(P.Update.CALL).send({ user });
154
201
  expect(res.status).toBe(NOT_FOUND);
155
- expect(res.body.error).toBe(UserService.Errors.USER_NOT_FOUND);
156
- },
157
- );
202
+ expect(res.body.error).toBe(Errors.USER_NOT_FOUND);
203
+ });
204
+ });
158
205
  });
159
206
 
160
- // ---- `Delete`
161
- describe(`"DELETE:${Paths.Users.Delete()}"`, () => {
162
- it(
163
- `should return a status code of "${OK}" and remove the user if the ` +
164
- 'request was successful.',
165
- async () => {
166
- const id = dbUsers[0].id,
167
- res = await agent.delete(Paths.Users.Delete({ id }));
207
+ // --- `Delete` ---
208
+ describe(P.Delete.DISPLAY, () => {
209
+ describe('on success', () => {
210
+ it(`removes the user and returns status "${OK}"`, async () => {
211
+ const id = dbUsers[0].id;
212
+ const path = P.Delete.CALL(id);
213
+ const res = await agent.delete(path);
168
214
  expect(res.status).toBe(OK);
169
- expect(await UserRepo.persists(id)).toBe(false);
170
- },
171
- );
172
-
173
- it(
174
- 'should return a JSON object with the error message of ' +
175
- `"${UserService.Errors.USER_NOT_FOUND}" and a status code of ` +
176
- `"${NOT_FOUND}" if the id was not found.`,
177
- async () => {
178
- const res: TestRes = await agent.delete(
179
- Paths.Users.Delete({ id: jetid() }),
180
- );
215
+ const persists = await UserRepo.persists(id);
216
+ expect(persists).toBe(false);
217
+ });
218
+ });
219
+
220
+ describe('when the user is not found', () => {
221
+ it(`returns status "${NOT_FOUND}"`, async () => {
222
+ const id = User.genId();
223
+ const path = P.Delete.CALL(id);
224
+ const res: TypedErrRes = await agent.delete(path);
181
225
  expect(res.status).toBe(NOT_FOUND);
182
- expect(res.body.error).toBe(UserService.Errors.USER_NOT_FOUND);
183
- },
184
- );
185
-
186
- it(
187
- `should return a status code of "${BAD_REQUEST}" if the id is not a ` +
188
- 'valid id.',
189
- async () => {
190
- const res = await agent.delete(Paths.Users.Delete({ id: -1 }));
226
+ expect(res.body.error).toBe(Errors.USER_NOT_FOUND);
227
+ });
228
+ });
229
+
230
+ describe('when the id is invalid', () => {
231
+ it(`returns status "${BAD_REQUEST}"`, async () => {
232
+ const path = P.Delete.CALL('asdf');
233
+ const res = await agent.delete(path);
191
234
  expect(res.status).toBe(BAD_REQUEST);
192
- },
193
- );
194
-
195
- it(
196
- `should return a status code of "${BAD_REQUEST}" if the id query ` +
197
- 'param is missing.',
198
- async () => {
199
- const res: TestRes = await agent.delete(Paths.Users.Delete());
235
+ });
236
+ });
237
+
238
+ describe('when the id path param is missing', () => {
239
+ it(`returns status "${BAD_REQUEST}"`, async () => {
240
+ const res = await agent.delete(P.Delete.FULL_PATH);
200
241
  expect(res.status).toBe(BAD_REQUEST);
201
- expect(res.body.errors?.[0].key).toStrictEqual('id');
202
- },
203
- );
242
+ const errMsg = extractErrMsg(res);
243
+ expect(errMsg).toStrictEqual('Not a valid uuid7 id');
244
+ });
245
+ });
204
246
  });
205
247
  });
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "express-generator-typescript",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
4
  "description": "Generate new Express applications similar to express-generate which but sets it up to use TypeScript instead",
5
5
  "scripts": {
6
- "commit": "git add -A && git commit -m \"dev commit\"",
7
- "commit:push": "npm run commit && git push",
8
6
  "install:clean": "rm -rf node_modules && rm -rf package-lock.json && npm i",
9
7
  "test": "node --test \"tests/**/*.test.js\"",
10
- "test:e2e": "node scripts/e2e.js"
8
+ "test:e2e": "node scripts/e2e.js",
9
+ "_local:commit": "tsx --tsconfig ../my-tools/tsconfig.json ../my-tools/src/workflows/commit/run.ts",
10
+ "_local:commit:push": "npm run _local:commit -- -p",
11
+ "_local:squash-history": "tsx --tsconfig ../my-tools/tsconfig.json ../my-tools/src/workflows/squash-history/run.ts"
11
12
  },
12
13
  "main": "lib/express-generator-typescript.js",
13
14
  "bin": {
@@ -25,7 +26,7 @@
25
26
  "!lib/template/src/repos/common/database.test.json"
26
27
  ],
27
28
  "engines": {
28
- "node": ">=22.12.0"
29
+ "node": "^22.13.0 || ^24.0.0 || >=26.0.0"
29
30
  },
30
31
  "repository": {
31
32
  "type": "git",
@@ -1,32 +0,0 @@
1
- import jetEnv, { num } from 'jet-env';
2
-
3
- import type { ValueOf } from '../types/utility-types';
4
-
5
- // ========================================================================= //
6
- // CONSTANTS //
7
- // ========================================================================= //
8
-
9
- // NOTE: These need to match the names of your ".env" files
10
- export const NodeEnvs = {
11
- DEV: 'development',
12
- TEST: 'test',
13
- PRODUCTION: 'production',
14
- } as const;
15
- export type NodeEnvs = ValueOf<typeof NodeEnvs>;
16
-
17
- // ========================================================================= //
18
- // EXEC //
19
- // ========================================================================= //
20
-
21
- // Setup the is `NodeEnvs` validator
22
- const isNodeEnv = (() => {
23
- const vals = Object.values(NodeEnvs);
24
- const valsFin = vals.map((item) => item.toLowerCase());
25
- const set = new Set(valsFin);
26
- return (val: unknown): val is NodeEnvs => set.has(val as NodeEnvs);
27
- })();
28
-
29
- export const EnvVars = jetEnv({
30
- NodeEnv: isNodeEnv,
31
- Port: num,
32
- });
@@ -1,47 +0,0 @@
1
- import jetid from 'jet-id';
2
- import { isNonEmptyString, isString } from 'jet-validators';
3
- import { parseObject, type Schema, testObject } from 'jet-validators/utils';
4
-
5
- import { isISOString } from '@src/common/utils/date-utils';
6
-
7
- import type { UserEntity, UserInput } from '../types';
8
-
9
- // ========================================================================= //
10
- // FUNCTIONS //
11
- // ========================================================================= //
12
-
13
- const schema: Schema<UserEntity> = {
14
- id: isUserId,
15
- name: isString,
16
- email: isString,
17
- created: isISOString,
18
- };
19
-
20
- /**
21
- * Validate the `User` schema.
22
- */
23
- export const parseUser = parseObject<UserEntity>(schema);
24
-
25
- /**
26
- * For the APIs make sure the right fields are complete
27
- */
28
- export const isCompleteUser = testObject<UserEntity>({
29
- ...schema,
30
- name: isNonEmptyString,
31
- email: isNonEmptyString,
32
- });
33
-
34
- /**
35
- * Validate the fields a client sends to create a user.
36
- */
37
- export const isUserInput = testObject<UserInput>({
38
- name: isNonEmptyString,
39
- email: isNonEmptyString,
40
- });
41
-
42
- /**
43
- * Test if an id is a valid user id.
44
- */
45
- export function isUserId(val: unknown): val is string {
46
- return jetid.test(val);
47
- }
@@ -1,9 +0,0 @@
1
- import type { 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;
@@ -1,23 +0,0 @@
1
- import { parseObject, type Schema } from 'jet-validators/utils';
2
-
3
- import { ValidationError } from '@src/common/classes/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;
@@ -1,38 +0,0 @@
1
- import type { UserEntity } from '@src/entities/User';
2
-
3
- // ========================================================================= //
4
- // TYPES //
5
- // ========================================================================= //
6
-
7
- type UserArray = UserEntity[] | readonly UserEntity[];
8
-
9
- // ========================================================================= //
10
- // FUNCTIONS //
11
- // ========================================================================= //
12
-
13
- /**
14
- * Compare to user arrays. Order does not matter and this assumes the email
15
- * field is unique
16
- */
17
- export function compareUserArrays(a: UserArray, b: UserArray): boolean {
18
- if (a.length !== b.length) return false;
19
- const aSorted = sortByEmail(a),
20
- bSorted = sortByEmail(b);
21
- for (let i = 0; i < aSorted.length; i++) {
22
- const a = aSorted[i],
23
- b = bSorted[i];
24
- if (a.email !== b.email || a.name !== b.name) {
25
- return false;
26
- }
27
- }
28
- return true;
29
- }
30
-
31
- /**
32
- * Sort user array by email.
33
- */
34
- function sortByEmail(arr: UserArray): UserEntity[] {
35
- return [...arr].sort((x, y) => {
36
- return x.email.localeCompare(y.email);
37
- });
38
- }
@@ -1,11 +0,0 @@
1
- import type { ParseError } from 'jet-validators/utils';
2
- import type { 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
- }