express-generator-typescript 2.3.7 → 2.4.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 +5 -34
- package/bin/cli.js +22 -7
- package/lib/cli.js +22 -7
- package/lib/express-generator-typescript.js +38 -28
- package/lib/project-files/spec/tests/users.spec.ts +9 -20
- package/lib/project-files/src/models/User.ts +50 -53
- package/lib/project-files/src/repos/database.json +1 -1
- package/lib/project-files/src/routes/api.ts +1 -24
- package/lib/project-files/src/routes/constants/Paths.ts +0 -5
- package/lib/project-files/src/server.ts +4 -9
- package/lib/project-files/src/views/users.html +0 -5
- package/lib/project-files-auth/.eslintignore +1 -0
- package/lib/project-files-auth/.eslintrc.json +44 -0
- package/lib/project-files-auth/README.md +43 -0
- package/lib/project-files-auth/build.ts +64 -0
- package/lib/project-files-auth/env/development.env +24 -0
- package/lib/project-files-auth/env/production.env +25 -0
- package/lib/project-files-auth/env/test.env +24 -0
- package/lib/project-files-auth/gitignore +3 -0
- package/lib/project-files-auth/package.json +70 -0
- package/lib/project-files-auth/spec/index.ts +77 -0
- package/lib/project-files-auth/spec/nodemon.json +6 -0
- package/lib/project-files-auth/spec/support/jasmine.json +11 -0
- package/lib/{project-files → project-files-auth}/spec/support/login.ts +1 -1
- package/lib/project-files-auth/spec/support/types.ts +1 -0
- package/lib/{project-files → project-files-auth}/spec/tests/auth.spec.ts +2 -2
- package/lib/project-files-auth/spec/tests/users.spec.ts +224 -0
- package/lib/project-files-auth/spec/types/supertest/index.d.ts +14 -0
- package/lib/project-files-auth/src/constants/EnvVars.ts +28 -0
- package/lib/project-files-auth/src/constants/HttpStatusCodes.ts +386 -0
- package/lib/project-files-auth/src/constants/misc.ts +5 -0
- package/lib/project-files-auth/src/index.ts +13 -0
- package/lib/project-files-auth/src/models/User.ts +85 -0
- package/lib/project-files-auth/src/other/classes.ts +17 -0
- package/lib/project-files-auth/src/other/types.ts +4 -0
- package/lib/project-files-auth/src/pre-start.ts +37 -0
- package/lib/project-files-auth/src/public/scripts/http.js +24 -0
- package/lib/project-files-auth/src/public/scripts/users.js +164 -0
- package/lib/project-files-auth/src/public/stylesheets/users.css +50 -0
- package/lib/project-files-auth/src/repos/MockOrm.ts +42 -0
- package/lib/project-files-auth/src/repos/UserRepo.ts +88 -0
- package/lib/project-files-auth/src/repos/database.json +1 -0
- package/lib/project-files-auth/src/routes/UserRoutes.ts +53 -0
- package/lib/project-files-auth/src/routes/api.ts +75 -0
- package/lib/project-files-auth/src/routes/constants/FullPaths.ts +40 -0
- package/lib/project-files-auth/src/routes/constants/Paths.ts +28 -0
- package/lib/project-files-auth/src/routes/types/express/index.d.ts +11 -0
- package/lib/project-files-auth/src/routes/types/express/misc.ts +16 -0
- package/lib/project-files-auth/src/routes/types/types.ts +22 -0
- package/lib/project-files-auth/src/server.ts +96 -0
- package/lib/project-files-auth/src/services/AuthService.ts +56 -0
- package/lib/project-files-auth/src/services/UserService.ts +66 -0
- package/lib/project-files-auth/src/util/PwdUtil.ts +39 -0
- package/lib/project-files-auth/src/util/SessionUtil.ts +92 -0
- package/lib/project-files-auth/src/util/misc.ts +22 -0
- package/lib/project-files-auth/src/views/users.html +42 -0
- package/lib/project-files-auth/tsconfig.json +83 -0
- package/lib/project-files-auth/tsconfig.prod.json +12 -0
- package/package.json +1 -1
- /package/lib/{project-files → project-files-auth}/src/public/scripts/login.js +0 -0
- /package/lib/{project-files → project-files-auth}/src/public/stylesheets/login.css +0 -0
- /package/lib/{project-files → project-files-auth}/src/routes/AuthRoutes.ts +0 -0
- /package/lib/{project-files → project-files-auth}/src/routes/middleware/adminMw.ts +0 -0
- /package/lib/{project-files → project-files-auth}/src/views/login.html +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove old files, copy front-end ones.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import logger from 'jet-logger';
|
|
7
|
+
import childProcess from 'child_process';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Start
|
|
12
|
+
*/
|
|
13
|
+
(async () => {
|
|
14
|
+
try {
|
|
15
|
+
// Remove current build
|
|
16
|
+
await remove('./dist/');
|
|
17
|
+
// Copy front-end files
|
|
18
|
+
await copy('./src/public', './dist/public');
|
|
19
|
+
await copy('./src/views', './dist/views');
|
|
20
|
+
// Copy back-end files
|
|
21
|
+
await exec('tsc --build tsconfig.prod.json', './');
|
|
22
|
+
} catch (err) {
|
|
23
|
+
logger.err(err);
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Remove file
|
|
29
|
+
*/
|
|
30
|
+
function remove(loc: string): Promise<void> {
|
|
31
|
+
return new Promise((res, rej) => {
|
|
32
|
+
return fs.remove(loc, (err) => {
|
|
33
|
+
return (!!err ? rej(err) : res());
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Copy file.
|
|
40
|
+
*/
|
|
41
|
+
function copy(src: string, dest: string): Promise<void> {
|
|
42
|
+
return new Promise((res, rej) => {
|
|
43
|
+
return fs.copy(src, dest, (err) => {
|
|
44
|
+
return (!!err ? rej(err) : res());
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Do command line command.
|
|
51
|
+
*/
|
|
52
|
+
function exec(cmd: string, loc: string): Promise<void> {
|
|
53
|
+
return new Promise((res, rej) => {
|
|
54
|
+
return childProcess.exec(cmd, {cwd: loc}, (err, stdout, stderr) => {
|
|
55
|
+
if (!!stdout) {
|
|
56
|
+
logger.info(stdout);
|
|
57
|
+
}
|
|
58
|
+
if (!!stderr) {
|
|
59
|
+
logger.warn(stderr);
|
|
60
|
+
}
|
|
61
|
+
return (!!err ? rej(err) : res());
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## Environment ##
|
|
2
|
+
NODE_ENV=development
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## Server ##
|
|
6
|
+
PORT=3000
|
|
7
|
+
HOST=localhost
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Setup jet-logger ##
|
|
11
|
+
JET_LOGGER_MODE=CONSOLE
|
|
12
|
+
JET_LOGGER_FILEPATH=jet-logger.log
|
|
13
|
+
JET_LOGGER_TIMESTAMP=TRUE
|
|
14
|
+
JET_LOGGER_FORMAT=LINE
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Authentication ##
|
|
18
|
+
COOKIE_DOMAIN=localhost
|
|
19
|
+
COOKIE_PATH=/
|
|
20
|
+
SECURE_COOKIE=false
|
|
21
|
+
JWT_SECRET=xxxxxxxxxxxxxx
|
|
22
|
+
COOKIE_SECRET=xxxxxxxxxxxxxx
|
|
23
|
+
# expires in 3 days
|
|
24
|
+
COOKIE_EXP=259200000
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## Environment ##
|
|
2
|
+
NODE_ENV=production
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## Server ##
|
|
6
|
+
PORT=8081
|
|
7
|
+
HOST=localhost
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Setup jet-logger ##
|
|
11
|
+
JET_LOGGER_MODE=FILE
|
|
12
|
+
JET_LOGGER_FILEPATH=jet-logger.log
|
|
13
|
+
JET_LOGGER_TIMESTAMP=TRUE
|
|
14
|
+
JET_LOGGER_FORMAT=LINE
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Authentication ##
|
|
18
|
+
# SECURE_COOKIE 'false' here for demo-ing. But ideally should be true.
|
|
19
|
+
COOKIE_DOMAIN=localhost
|
|
20
|
+
COOKIE_PATH=/
|
|
21
|
+
SECURE_COOKIE=false
|
|
22
|
+
JWT_SECRET=xxxxxxxxxxxxxx
|
|
23
|
+
COOKIE_SECRET=xxxxxxxxxxxxxx
|
|
24
|
+
# expires in 3 days
|
|
25
|
+
COOKIE_EXP=259200000
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## Environment ##
|
|
2
|
+
NODE_ENV=test
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## Server ##
|
|
6
|
+
PORT=4000
|
|
7
|
+
HOST=localhost
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Setup jet-logger ##
|
|
11
|
+
JET_LOGGER_MODE=CONSOLE
|
|
12
|
+
JET_LOGGER_FILEPATH=jet-logger.log
|
|
13
|
+
JET_LOGGER_TIMESTAMP=TRUE
|
|
14
|
+
JET_LOGGER_FORMAT=LINE
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Authentication ##
|
|
18
|
+
COOKIE_DOMAIN=localhost
|
|
19
|
+
COOKIE_PATH=/
|
|
20
|
+
SECURE_COOKIE=false
|
|
21
|
+
JWT_SECRET=xxxxxxxxxxxxxx
|
|
22
|
+
COOKIE_SECRET=xxxxxxxxxxxxxx
|
|
23
|
+
# expires in 3 days
|
|
24
|
+
COOKIE_EXP=259200000
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "express-typescript-example",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"build": "npx ts-node build.ts",
|
|
6
|
+
"lint": "npx eslint --ext .ts src/",
|
|
7
|
+
"lint:tests": "npx eslint --ext .ts spec/",
|
|
8
|
+
"start": "node -r module-alias/register ./dist --env=production",
|
|
9
|
+
"dev": "nodemon",
|
|
10
|
+
"test": "nodemon --config ./spec/nodemon.json",
|
|
11
|
+
"test:no-reloading": "npx ts-node --files -r tsconfig-paths/register ./spec"
|
|
12
|
+
},
|
|
13
|
+
"nodemonConfig": {
|
|
14
|
+
"watch": [
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"ext": "ts, html",
|
|
18
|
+
"ignore": [
|
|
19
|
+
"src/public"
|
|
20
|
+
],
|
|
21
|
+
"exec": "./node_modules/.bin/ts-node --files -r tsconfig-paths/register ./src"
|
|
22
|
+
},
|
|
23
|
+
"_moduleAliases": {
|
|
24
|
+
"@src": "dist"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=8.10.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"bcrypt": "^5.0.1",
|
|
31
|
+
"cookie-parser": "^1.4.6",
|
|
32
|
+
"dotenv": "^16.0.2",
|
|
33
|
+
"express": "^4.18.1",
|
|
34
|
+
"express-async-errors": "^3.1.1",
|
|
35
|
+
"helmet": "^6.0.0",
|
|
36
|
+
"inserturlparams": "^1.0.1",
|
|
37
|
+
"jet-logger": "^1.3.1",
|
|
38
|
+
"jet-validator": "^1.0.3",
|
|
39
|
+
"jsonfile": "^6.1.0",
|
|
40
|
+
"jsonwebtoken": "^8.5.1",
|
|
41
|
+
"module-alias": "^2.2.2",
|
|
42
|
+
"morgan": "^1.10.0",
|
|
43
|
+
"ts-command-line-args": "^2.4.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/bcrypt": "^5.0.0",
|
|
47
|
+
"@types/cookie-parser": "^1.4.3",
|
|
48
|
+
"@types/express": "^4.17.13",
|
|
49
|
+
"@types/find": "^0.2.1",
|
|
50
|
+
"@types/fs-extra": "^9.0.13",
|
|
51
|
+
"@types/jasmine": "^4.3.0",
|
|
52
|
+
"@types/jsonfile": "^6.1.0",
|
|
53
|
+
"@types/jsonwebtoken": "^8.5.9",
|
|
54
|
+
"@types/morgan": "^1.9.3",
|
|
55
|
+
"@types/node": "^18.7.14",
|
|
56
|
+
"@types/supertest": "^2.0.12",
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
|
58
|
+
"@typescript-eslint/parser": "^5.36.1",
|
|
59
|
+
"eslint": "^8.23.0",
|
|
60
|
+
"eslint-plugin-node": "^11.1.0",
|
|
61
|
+
"find": "^0.3.0",
|
|
62
|
+
"fs-extra": "^10.1.0",
|
|
63
|
+
"jasmine": "^4.3.0",
|
|
64
|
+
"nodemon": "^2.0.19",
|
|
65
|
+
"supertest": "^6.2.4",
|
|
66
|
+
"ts-node": "^10.9.1",
|
|
67
|
+
"tsconfig-paths": "^4.1.0",
|
|
68
|
+
"typescript": "^4.8.2"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import find from 'find';
|
|
3
|
+
import Jasmine from 'jasmine';
|
|
4
|
+
import { parse } from 'ts-command-line-args';
|
|
5
|
+
import logger from 'jet-logger';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// **** Types **** //
|
|
9
|
+
|
|
10
|
+
interface IArgs {
|
|
11
|
+
testFile: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// **** Setup **** //
|
|
16
|
+
|
|
17
|
+
// ** Init ** //
|
|
18
|
+
|
|
19
|
+
// NOTE: MUST BE FIRST!! Load env vars
|
|
20
|
+
const result2 = dotenv.config({
|
|
21
|
+
path: './env/test.env',
|
|
22
|
+
});
|
|
23
|
+
if (result2.error) {
|
|
24
|
+
throw result2.error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Setup command line options.
|
|
28
|
+
const args = parse<IArgs>({
|
|
29
|
+
testFile: {
|
|
30
|
+
type: String,
|
|
31
|
+
defaultValue: '',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// ** Start Jasmine ** //
|
|
37
|
+
|
|
38
|
+
// Init Jasmine
|
|
39
|
+
const jasmine = new Jasmine();
|
|
40
|
+
jasmine.exitOnCompletion = false;
|
|
41
|
+
|
|
42
|
+
// Set location of test files
|
|
43
|
+
jasmine.loadConfig({
|
|
44
|
+
random: true,
|
|
45
|
+
spec_dir: 'spec',
|
|
46
|
+
spec_files: [
|
|
47
|
+
'./tests/**/*.spec.ts',
|
|
48
|
+
],
|
|
49
|
+
stopSpecOnExpectationFailure: false,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Run all or a single unit-test
|
|
53
|
+
let execResp: Promise<jasmine.JasmineDoneInfo> | undefined;
|
|
54
|
+
if (args.testFile) {
|
|
55
|
+
const testFile = args.testFile;
|
|
56
|
+
find.file(testFile + '.spec.ts', './spec', (files: string[]) => {
|
|
57
|
+
if (files.length === 1) {
|
|
58
|
+
jasmine.execute([files[0]]);
|
|
59
|
+
} else {
|
|
60
|
+
logger.err('Test file not found!');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
execResp = jasmine.execute();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Wait for tests to finish
|
|
68
|
+
(async () => {
|
|
69
|
+
if (!!execResp) {
|
|
70
|
+
const info = await execResp;
|
|
71
|
+
if (info.overallStatus === 'passed') {
|
|
72
|
+
logger.info('All tests have passed :)');
|
|
73
|
+
} else {
|
|
74
|
+
logger.err('At least one test has failed :(');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
@@ -23,7 +23,7 @@ function login(beforeAgent: SuperTest<Test>, done: (arg: string) => void) {
|
|
|
23
23
|
// Setup dummy data
|
|
24
24
|
const role = UserRoles.Admin,
|
|
25
25
|
pwdHash = PwdUtil.hashSync(LoginCreds.password),
|
|
26
|
-
loginUser = new
|
|
26
|
+
loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
|
|
27
27
|
// Add spy
|
|
28
28
|
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
29
29
|
// Call Login API
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type TReqBody = string | object | undefined;
|
|
@@ -64,7 +64,7 @@ describe('AuthRouter', () => {
|
|
|
64
64
|
// Setup data
|
|
65
65
|
const role = UserRoles.Standard,
|
|
66
66
|
pwdHash = PwdUtil.hashSync(LoginCreds.password),
|
|
67
|
-
loginUser = new
|
|
67
|
+
loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
|
|
68
68
|
// Add spy
|
|
69
69
|
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
70
70
|
// Call API
|
|
@@ -99,7 +99,7 @@ describe('AuthRouter', () => {
|
|
|
99
99
|
// Setup data
|
|
100
100
|
const role = UserRoles.Standard,
|
|
101
101
|
pwdHash = PwdUtil.hashSync('bad password'),
|
|
102
|
-
loginUser = new
|
|
102
|
+
loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
|
|
103
103
|
// Add spy
|
|
104
104
|
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
105
105
|
// Call API
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import supertest, { SuperTest, Test, Response } from 'supertest';
|
|
2
|
+
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
3
|
+
import insertUrlParams from 'inserturlparams';
|
|
4
|
+
|
|
5
|
+
import app from '@src/server';
|
|
6
|
+
|
|
7
|
+
import UserRepo from '@src/repos/UserRepo';
|
|
8
|
+
import User from '@src/models/User';
|
|
9
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
10
|
+
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
11
|
+
import FullPaths from '@src/routes/constants/FullPaths';
|
|
12
|
+
|
|
13
|
+
import login from '../support/login';
|
|
14
|
+
import { TReqBody } from 'spec/support/types';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// **** Variables **** //
|
|
18
|
+
|
|
19
|
+
// Paths
|
|
20
|
+
const {
|
|
21
|
+
Get,
|
|
22
|
+
Add,
|
|
23
|
+
Update,
|
|
24
|
+
Delete,
|
|
25
|
+
} = FullPaths.Users;
|
|
26
|
+
|
|
27
|
+
// StatusCodes
|
|
28
|
+
const {
|
|
29
|
+
OK,
|
|
30
|
+
CREATED,
|
|
31
|
+
NOT_FOUND,
|
|
32
|
+
BAD_REQUEST,
|
|
33
|
+
} = HttpStatusCodes;
|
|
34
|
+
|
|
35
|
+
// Dummy users for GET req
|
|
36
|
+
const DummyGetAllUsers = [
|
|
37
|
+
User.new('Sean Maxwell', 'sean.maxwell@gmail.com'),
|
|
38
|
+
User.new('John Smith', 'john.smith@gmail.com'),
|
|
39
|
+
User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
40
|
+
] as const;
|
|
41
|
+
|
|
42
|
+
// Dummy update user
|
|
43
|
+
const DummyUserData = {
|
|
44
|
+
user: User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// **** Tests **** //
|
|
49
|
+
|
|
50
|
+
describe('UserRouter', () => {
|
|
51
|
+
|
|
52
|
+
let agent: SuperTest<Test>,
|
|
53
|
+
jwtCookie: string;
|
|
54
|
+
|
|
55
|
+
// Run before all tests
|
|
56
|
+
beforeAll((done) => {
|
|
57
|
+
agent = supertest.agent(app);
|
|
58
|
+
login(agent, (cookie: string) => {
|
|
59
|
+
jwtCookie = cookie;
|
|
60
|
+
done();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// ** Get all users ** //
|
|
65
|
+
describe(`"GET:${Get}"`, () => {
|
|
66
|
+
|
|
67
|
+
const callApi = () =>
|
|
68
|
+
agent
|
|
69
|
+
.get(Get)
|
|
70
|
+
.set('Cookie', jwtCookie);
|
|
71
|
+
|
|
72
|
+
// Success
|
|
73
|
+
it('should return a JSON object with all the users and a status code ' +
|
|
74
|
+
`of "${OK}" if the request was successful.`, (done) => {
|
|
75
|
+
// Add spy
|
|
76
|
+
spyOn(UserRepo, 'getAll').and.resolveTo([...DummyGetAllUsers]);
|
|
77
|
+
// Call API
|
|
78
|
+
callApi()
|
|
79
|
+
.end((_: Error, res: Response) => {
|
|
80
|
+
expect(res.status).toBe(OK);
|
|
81
|
+
for (let i = 0; i < res.body.users.length; i++) {
|
|
82
|
+
const user = res.body.users[i];
|
|
83
|
+
expect(user).toEqual(DummyGetAllUsers[i]);
|
|
84
|
+
}
|
|
85
|
+
done();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Test add user
|
|
91
|
+
describe(`"POST:${Add}"`, () => {
|
|
92
|
+
|
|
93
|
+
const callApi = (reqBody: TReqBody) =>
|
|
94
|
+
agent
|
|
95
|
+
.post(Add)
|
|
96
|
+
.set('Cookie', jwtCookie)
|
|
97
|
+
.type('form').send(reqBody);
|
|
98
|
+
|
|
99
|
+
// Test add user success
|
|
100
|
+
it(`should return a status code of "${CREATED}" if the request was ` +
|
|
101
|
+
'successful.', (done) => {
|
|
102
|
+
// Spy
|
|
103
|
+
spyOn(UserRepo, 'add').and.resolveTo();
|
|
104
|
+
// Call api
|
|
105
|
+
callApi(DummyUserData)
|
|
106
|
+
.end((_: Error, res: Response) => {
|
|
107
|
+
expect(res.status).toBe(CREATED);
|
|
108
|
+
expect(res.body.error).toBeUndefined();
|
|
109
|
+
done();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Missing param
|
|
114
|
+
it('should return a JSON object with an error message of ' +
|
|
115
|
+
`"${ValidatorErr}" and a status code of "${BAD_REQUEST}" if the user ` +
|
|
116
|
+
'param was missing.', (done) => {
|
|
117
|
+
// Call api
|
|
118
|
+
callApi({})
|
|
119
|
+
.end((_: Error, res: Response) => {
|
|
120
|
+
expect(res.status).toBe(BAD_REQUEST);
|
|
121
|
+
expect(res.body.error).toBe(ValidatorErr);
|
|
122
|
+
done();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ** Update users ** //
|
|
128
|
+
describe(`"PUT:${Update}"`, () => {
|
|
129
|
+
|
|
130
|
+
const callApi = (reqBody: TReqBody) =>
|
|
131
|
+
agent
|
|
132
|
+
.put(Update)
|
|
133
|
+
.set('Cookie', jwtCookie)
|
|
134
|
+
.type('form').send(reqBody);
|
|
135
|
+
|
|
136
|
+
// Success
|
|
137
|
+
it(`should return a status code of "${OK}" if the request was successful.`,
|
|
138
|
+
(done) => {
|
|
139
|
+
// Setup spies
|
|
140
|
+
spyOn(UserRepo, 'update').and.resolveTo();
|
|
141
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
142
|
+
// Call api
|
|
143
|
+
callApi(DummyUserData)
|
|
144
|
+
.end((_: Error, res: Response) => {
|
|
145
|
+
expect(res.status).toBe(OK);
|
|
146
|
+
expect(res.body.error).toBeUndefined();
|
|
147
|
+
done();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Param missing
|
|
152
|
+
it('should return a JSON object with an error message of ' +
|
|
153
|
+
`"${ValidatorErr}" and a status code of "${BAD_REQUEST}" if the user ` +
|
|
154
|
+
'param was missing.', (done) => {
|
|
155
|
+
// Call api
|
|
156
|
+
callApi({})
|
|
157
|
+
.end((_: Error, res: Response) => {
|
|
158
|
+
expect(res.status).toBe(BAD_REQUEST);
|
|
159
|
+
expect(res.body.error).toBe(ValidatorErr);
|
|
160
|
+
done();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// User not found
|
|
165
|
+
it('should return a JSON object with the error message of ' +
|
|
166
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of "${NOT_FOUND}" if the id ` +
|
|
167
|
+
'was not found.', (done) => {
|
|
168
|
+
// Call api
|
|
169
|
+
callApi(DummyUserData)
|
|
170
|
+
.end((_: Error, res: Response) => {
|
|
171
|
+
expect(res.status).toBe(NOT_FOUND);
|
|
172
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
173
|
+
done();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// ** Delete user ** //
|
|
179
|
+
describe(`"DELETE:${Delete}"`, () => {
|
|
180
|
+
|
|
181
|
+
const callApi = (id: number) =>
|
|
182
|
+
agent
|
|
183
|
+
.delete(insertUrlParams(Delete, { id }))
|
|
184
|
+
.set('Cookie', jwtCookie);
|
|
185
|
+
|
|
186
|
+
// Success
|
|
187
|
+
it(`should return a status code of "${OK}" if the request was successful.`,
|
|
188
|
+
(done) => {
|
|
189
|
+
// Setup spies
|
|
190
|
+
spyOn(UserRepo, 'delete').and.resolveTo();
|
|
191
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
192
|
+
// Call api
|
|
193
|
+
callApi(5)
|
|
194
|
+
.end((_: Error, res: Response) => {
|
|
195
|
+
expect(res.status).toBe(OK);
|
|
196
|
+
expect(res.body.error).toBeUndefined();
|
|
197
|
+
done();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// User not found
|
|
202
|
+
it('should return a JSON object with the error message of ' +
|
|
203
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of "${NOT_FOUND}" if the id ` +
|
|
204
|
+
'was not found.', (done) => {
|
|
205
|
+
callApi(-1)
|
|
206
|
+
.end((_: Error, res: Response) => {
|
|
207
|
+
expect(res.status).toBe(NOT_FOUND);
|
|
208
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
209
|
+
done();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Invalid param
|
|
214
|
+
it(`should return a status code of "${BAD_REQUEST}" and return an error ` +
|
|
215
|
+
`message of "${ValidatorErr}" if the id was not a valid number`, (done) => {
|
|
216
|
+
callApi('horse' as unknown as number)
|
|
217
|
+
.end((_: Error, res: Response) => {
|
|
218
|
+
expect(res.status).toBe(BAD_REQUEST);
|
|
219
|
+
expect(res.body.error).toBe(ValidatorErr);
|
|
220
|
+
done();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environments variables declared here.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint-disable node/no-process-env */
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
NodeEnv: (process.env.NODE_ENV ?? ''),
|
|
10
|
+
Port: (process.env.PORT ?? 0),
|
|
11
|
+
CookieProps: {
|
|
12
|
+
Key: 'ExpressGeneratorTs',
|
|
13
|
+
Secret: (process.env.COOKIE_SECRET ?? ''),
|
|
14
|
+
// Casing to match express cookie options
|
|
15
|
+
Options: {
|
|
16
|
+
httpOnly: true,
|
|
17
|
+
signed: true,
|
|
18
|
+
path: (process.env.COOKIE_PATH ?? ''),
|
|
19
|
+
maxAge: Number(process.env.COOKIE_EXP ?? 0),
|
|
20
|
+
domain: (process.env.COOKIE_DOMAIN ?? ''),
|
|
21
|
+
secure: (process.env.SECURE_COOKIE === 'true'),
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
Jwt: {
|
|
25
|
+
Secret: (process.env.JWT_SECRET ?? ''),
|
|
26
|
+
Exp: (process.env.COOKIE_EXP ?? ''), // exp at the same time as the cookie
|
|
27
|
+
},
|
|
28
|
+
} as const;
|