express-generator-typescript 2.4.6 → 2.4.8
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/lib/express-generator-typescript.js +1 -1
- package/lib/project-files/package.json +4 -3
- package/lib/project-files/spec/support/apiCb.ts +76 -0
- package/lib/project-files/spec/tests/users.spec.ts +102 -127
- package/lib/project-files/spec/types/misc.ts +18 -0
- package/lib/project-files/src/models/User.ts +13 -27
- package/lib/project-files/src/public/scripts/http.js +3 -1
- package/lib/project-files/src/public/scripts/users.js +31 -65
- package/lib/project-files/src/public/stylesheets/users.css +4 -6
- package/lib/project-files/src/repos/UserRepo.ts +6 -1
- package/lib/project-files/src/repos/database.json +22 -1
- package/lib/project-files/src/routes/types/express/misc.ts +1 -5
- package/lib/project-files/src/routes/types/types.ts +0 -8
- package/lib/project-files/src/views/users.html +100 -5
- package/lib/project-files-auth/package.json +4 -3
- package/lib/project-files-auth/spec/support/apiCb.ts +76 -0
- package/lib/project-files-auth/spec/support/login.ts +6 -8
- package/lib/project-files-auth/spec/tests/auth.spec.ts +52 -59
- package/lib/project-files-auth/spec/tests/users.spec.ts +101 -133
- package/lib/project-files-auth/spec/types/misc.ts +18 -0
- package/lib/project-files-auth/src/models/User.ts +17 -12
- package/lib/project-files-auth/src/public/scripts/http.js +3 -1
- package/lib/project-files-auth/src/public/scripts/login.js +1 -3
- package/lib/project-files-auth/src/public/scripts/users.js +31 -63
- package/lib/project-files-auth/src/public/stylesheets/users.css +4 -11
- package/lib/project-files-auth/src/repos/UserRepo.ts +6 -1
- package/lib/project-files-auth/src/repos/database.json +1 -1
- package/lib/project-files-auth/src/services/UserService.ts +2 -8
- package/lib/project-files-auth/src/views/login.html +26 -3
- package/lib/project-files-auth/src/views/users.html +106 -8
- package/package.json +1 -1
- package/lib/project-files/spec/support/types.ts +0 -1
- package/lib/project-files-auth/spec/support/types.ts +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import supertest, {
|
|
1
|
+
import supertest, { Test, Response } from 'supertest';
|
|
2
|
+
import TestAgent from 'supertest/lib/agent';
|
|
2
3
|
|
|
3
4
|
import app from '@src/server';
|
|
4
5
|
|
|
@@ -11,29 +12,21 @@ import EnvVars from '@src/constants/EnvVars';
|
|
|
11
12
|
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
12
13
|
|
|
13
14
|
import Paths from 'spec/support/Paths';
|
|
14
|
-
import
|
|
15
|
+
import apiCb from 'spec/support/apiCb';
|
|
16
|
+
import { TApiCb } from 'spec/types/misc';
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
// StatusCodes
|
|
20
|
-
const {
|
|
21
|
-
OK,
|
|
22
|
-
UNAUTHORIZED,
|
|
23
|
-
} = HttpStatusCodes;
|
|
24
|
-
|
|
25
|
-
// Login credentials
|
|
19
|
+
// Login Creds
|
|
26
20
|
const LoginCreds = {
|
|
27
21
|
email: 'jsmith@gmail.com',
|
|
28
22
|
password: 'Password@1',
|
|
29
23
|
} as const;
|
|
30
24
|
|
|
31
25
|
|
|
32
|
-
//
|
|
33
|
-
|
|
26
|
+
// Tests
|
|
34
27
|
describe('AuthRouter', () => {
|
|
35
28
|
|
|
36
|
-
let agent:
|
|
29
|
+
let agent: TestAgent<Test>;
|
|
37
30
|
|
|
38
31
|
// Run before all tests
|
|
39
32
|
beforeAll((done) => {
|
|
@@ -41,82 +34,82 @@ describe('AuthRouter', () => {
|
|
|
41
34
|
done();
|
|
42
35
|
});
|
|
43
36
|
|
|
44
|
-
//
|
|
37
|
+
// Login
|
|
45
38
|
describe(`"POST:${Paths.Auth.Login}"`, () => {
|
|
46
39
|
|
|
47
40
|
const EMAIL_NOT_FOUND_ERR = Errors.EmailNotFound(LoginCreds.email);
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
// Setup API
|
|
43
|
+
const callApi = (creds: typeof LoginCreds, cb: TApiCb) =>
|
|
50
44
|
agent
|
|
51
45
|
.post(Paths.Auth.Login)
|
|
52
|
-
.
|
|
53
|
-
.
|
|
46
|
+
.send(creds)
|
|
47
|
+
.end(apiCb(cb));
|
|
54
48
|
|
|
55
49
|
// Success
|
|
56
|
-
it(`should return a response with a status of "${OK}"
|
|
57
|
-
'a jwt if the login was successful.',
|
|
50
|
+
it(`should return a response with a status of "${HttpStatusCodes.OK}" ` +
|
|
51
|
+
'and a cookie with a jwt if the login was successful.', done => {
|
|
58
52
|
// Setup data
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const pwdHash = PwdUtil.hashSync(LoginCreds.password),
|
|
54
|
+
loginUser = User.new('john smith', LoginCreds.email, new Date(),
|
|
55
|
+
UserRoles.Standard, pwdHash);
|
|
62
56
|
// Add spy
|
|
63
57
|
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
64
58
|
// Call API
|
|
65
|
-
callApi(LoginCreds
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
});
|
|
59
|
+
callApi(LoginCreds, res => {
|
|
60
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
61
|
+
const cookie = res.headers['set-cookie'][0];
|
|
62
|
+
expect(cookie).toContain(EnvVars.CookieProps.Key);
|
|
63
|
+
done();
|
|
64
|
+
});
|
|
72
65
|
});
|
|
73
66
|
|
|
74
67
|
// Email not found error
|
|
75
|
-
it(
|
|
76
|
-
`json with an error message of
|
|
77
|
-
|
|
68
|
+
it('should return a response with a status of ' +
|
|
69
|
+
`"${HttpStatusCodes.UNAUTHORIZED}" and a json with an error message of ` +
|
|
70
|
+
`"${EMAIL_NOT_FOUND_ERR}" if the email was not found.`, done => {
|
|
78
71
|
// Spy
|
|
79
72
|
spyOn(UserRepo, 'getOne').and.resolveTo(null);
|
|
80
73
|
// Call
|
|
81
|
-
callApi(LoginCreds
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
74
|
+
callApi(LoginCreds, res => {
|
|
75
|
+
expect(res.status).toBe(HttpStatusCodes.UNAUTHORIZED);
|
|
76
|
+
expect(res.body.error).toBe(EMAIL_NOT_FOUND_ERR);
|
|
77
|
+
done();
|
|
78
|
+
});
|
|
87
79
|
});
|
|
88
80
|
|
|
89
81
|
// Password failed
|
|
90
|
-
it(
|
|
91
|
-
`
|
|
92
|
-
|
|
82
|
+
it('should return a response with a status of ' +
|
|
83
|
+
`"${HttpStatusCodes.UNAUTHORIZED}" and a json with the error ` +
|
|
84
|
+
`"${Errors.Unauth}" if the password failed.`, done => {
|
|
93
85
|
// Setup data
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
const pwdHash = PwdUtil.hashSync('bad password'),
|
|
87
|
+
loginUser = User.new('john smith', LoginCreds.email, new Date(),
|
|
88
|
+
UserRoles.Standard, pwdHash);
|
|
97
89
|
// Add spy
|
|
98
90
|
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
99
91
|
// Call API
|
|
100
|
-
callApi(LoginCreds
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
92
|
+
callApi(LoginCreds, res => {
|
|
93
|
+
expect(res.status).toBe(HttpStatusCodes.UNAUTHORIZED);
|
|
94
|
+
expect(res.body.error).toBe(Errors.Unauth);
|
|
95
|
+
done();
|
|
96
|
+
});
|
|
106
97
|
});
|
|
107
98
|
|
|
108
99
|
});
|
|
109
100
|
|
|
110
|
-
//
|
|
101
|
+
// Test logout
|
|
111
102
|
describe(`"GET:${Paths.Auth.Logout}"`, () => {
|
|
112
103
|
|
|
113
104
|
// Successful logout
|
|
114
|
-
it(`should return a response with a status of ${OK}`,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
105
|
+
it(`should return a response with a status of ${HttpStatusCodes.OK}`,
|
|
106
|
+
done => {
|
|
107
|
+
agent
|
|
108
|
+
.get(Paths.Auth.Logout)
|
|
109
|
+
.end((_: Error, res: Response) => {
|
|
110
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
111
|
+
done();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
121
114
|
});
|
|
122
115
|
});
|
|
@@ -1,222 +1,190 @@
|
|
|
1
|
-
import supertest, {
|
|
1
|
+
import supertest, { Test } from 'supertest';
|
|
2
|
+
import TestAgent from 'supertest/lib/agent';
|
|
2
3
|
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
3
4
|
import insertUrlParams from 'inserturlparams';
|
|
4
5
|
|
|
5
6
|
import app from '@src/server';
|
|
6
7
|
|
|
7
8
|
import UserRepo from '@src/repos/UserRepo';
|
|
8
|
-
import User from '@src/models/User';
|
|
9
|
+
import User, { IUser } from '@src/models/User';
|
|
9
10
|
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
10
11
|
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
11
12
|
|
|
12
|
-
import login from '../support/login';
|
|
13
13
|
import Paths from 'spec/support/Paths';
|
|
14
|
-
import
|
|
14
|
+
import apiCb from 'spec/support/apiCb';
|
|
15
|
+
import login from 'spec/support/login';
|
|
16
|
+
import { TApiCb } from 'spec/types/misc';
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
// **** Variables **** //
|
|
18
|
-
|
|
19
|
-
// StatusCodes
|
|
20
|
-
const {
|
|
21
|
-
OK,
|
|
22
|
-
CREATED,
|
|
23
|
-
NOT_FOUND,
|
|
24
|
-
BAD_REQUEST,
|
|
25
|
-
} = HttpStatusCodes;
|
|
26
|
-
|
|
27
19
|
// Dummy users for GET req
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const DummyUserData = {
|
|
36
|
-
user: User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
37
|
-
} as const;
|
|
20
|
+
const getDummyUsers = () => {
|
|
21
|
+
return [
|
|
22
|
+
User.new('Sean Maxwell', 'sean.maxwell@gmail.com'),
|
|
23
|
+
User.new('John Smith', 'john.smith@gmail.com'),
|
|
24
|
+
User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
25
|
+
];
|
|
26
|
+
};
|
|
38
27
|
|
|
39
28
|
|
|
40
|
-
//
|
|
41
|
-
|
|
29
|
+
// Tests
|
|
42
30
|
describe('UserRouter', () => {
|
|
43
31
|
|
|
44
|
-
let agent:
|
|
45
|
-
jwtCookie: string;
|
|
32
|
+
let agent: TestAgent<Test>;
|
|
46
33
|
|
|
47
34
|
// Run before all tests
|
|
48
35
|
beforeAll((done) => {
|
|
49
36
|
agent = supertest.agent(app);
|
|
50
|
-
login(agent,
|
|
51
|
-
|
|
37
|
+
login(agent, cookie => {
|
|
38
|
+
agent.set('Cookie', cookie);
|
|
52
39
|
done();
|
|
53
40
|
});
|
|
54
41
|
});
|
|
55
42
|
|
|
56
|
-
//
|
|
43
|
+
// Get all users
|
|
57
44
|
describe(`"GET:${Paths.Users.Get}"`, () => {
|
|
58
45
|
|
|
59
|
-
|
|
46
|
+
// Setup API
|
|
47
|
+
const api = (cb: TApiCb) =>
|
|
60
48
|
agent
|
|
61
49
|
.get(Paths.Users.Get)
|
|
62
|
-
.
|
|
50
|
+
.end(apiCb(cb));
|
|
63
51
|
|
|
64
52
|
// Success
|
|
65
53
|
it('should return a JSON object with all the users and a status code ' +
|
|
66
|
-
`of "${OK}" if the request was successful.`, (done) => {
|
|
54
|
+
`of "${HttpStatusCodes.OK}" if the request was successful.`, (done) => {
|
|
67
55
|
// Add spy
|
|
68
|
-
|
|
56
|
+
const data = getDummyUsers();
|
|
57
|
+
spyOn(UserRepo, 'getAll').and.resolveTo(data);
|
|
69
58
|
// Call API
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
expect(user).toEqual(DummyGetAllUsers[i]);
|
|
76
|
-
}
|
|
77
|
-
done();
|
|
78
|
-
});
|
|
59
|
+
api(res => {
|
|
60
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
61
|
+
expect(res.body).toEqual({ users: data });
|
|
62
|
+
done();
|
|
63
|
+
});
|
|
79
64
|
});
|
|
80
65
|
});
|
|
81
66
|
|
|
82
67
|
// Test add user
|
|
83
68
|
describe(`"POST:${Paths.Users.Add}"`, () => {
|
|
84
69
|
|
|
85
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
70
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
71
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
86
72
|
|
|
87
|
-
|
|
73
|
+
// Setup API
|
|
74
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
88
75
|
agent
|
|
89
76
|
.post(Paths.Users.Add)
|
|
90
|
-
.
|
|
91
|
-
.
|
|
77
|
+
.send({ user })
|
|
78
|
+
.end(apiCb(cb));
|
|
92
79
|
|
|
93
80
|
// Test add user success
|
|
94
|
-
it(`should return a status code of "${CREATED}" if the
|
|
95
|
-
'successful.', (done) => {
|
|
81
|
+
it(`should return a status code of "${HttpStatusCodes.CREATED}" if the ` +
|
|
82
|
+
'request was successful.', (done) => {
|
|
96
83
|
// Spy
|
|
97
84
|
spyOn(UserRepo, 'add').and.resolveTo();
|
|
98
85
|
// Call api
|
|
99
|
-
callApi(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
done();
|
|
104
|
-
});
|
|
86
|
+
callApi(DUMMY_USER, res => {
|
|
87
|
+
expect(res.status).toBe(HttpStatusCodes.CREATED);
|
|
88
|
+
done();
|
|
89
|
+
});
|
|
105
90
|
});
|
|
106
91
|
|
|
107
92
|
// Missing param
|
|
108
|
-
it(
|
|
109
|
-
`
|
|
93
|
+
it(`should return a JSON object with an error message of "${ERROR_MSG}" ` +
|
|
94
|
+
`and a status code of "${HttpStatusCodes.BAD_REQUEST}" if the user ` +
|
|
110
95
|
'param was missing.', (done) => {
|
|
111
96
|
// Call api
|
|
112
|
-
callApi({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
97
|
+
callApi(null, res => {
|
|
98
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
99
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
100
|
+
done();
|
|
101
|
+
});
|
|
118
102
|
});
|
|
119
103
|
});
|
|
120
104
|
|
|
121
|
-
//
|
|
105
|
+
// Update users
|
|
122
106
|
describe(`"PUT:${Paths.Users.Update}"`, () => {
|
|
123
107
|
|
|
124
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
108
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
109
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
125
110
|
|
|
126
|
-
|
|
111
|
+
// Setup API
|
|
112
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
127
113
|
agent
|
|
128
114
|
.put(Paths.Users.Update)
|
|
129
|
-
.
|
|
130
|
-
.
|
|
115
|
+
.send({ user })
|
|
116
|
+
.end(apiCb(cb));
|
|
131
117
|
|
|
132
118
|
// Success
|
|
133
|
-
it(`should return a status code of "${OK}" if the
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
expect(res.body.error).toBeUndefined();
|
|
143
|
-
done();
|
|
144
|
-
});
|
|
119
|
+
it(`should return a status code of "${HttpStatusCodes.OK}" if the ` +
|
|
120
|
+
'request was successful.', (done) => {
|
|
121
|
+
// Setup spies
|
|
122
|
+
spyOn(UserRepo, 'update').and.resolveTo();
|
|
123
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
124
|
+
// Call api
|
|
125
|
+
callApi(DUMMY_USER, res => {
|
|
126
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
127
|
+
done();
|
|
145
128
|
});
|
|
129
|
+
});
|
|
146
130
|
|
|
147
131
|
// Param missing
|
|
148
|
-
it(
|
|
149
|
-
`
|
|
132
|
+
it(`should return a JSON object with an error message of "${ERROR_MSG}" ` +
|
|
133
|
+
`and a status code of "${HttpStatusCodes.BAD_REQUEST}" if the user ` +
|
|
150
134
|
'param was missing.', (done) => {
|
|
151
135
|
// Call api
|
|
152
|
-
callApi({
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
136
|
+
callApi(null, res => {
|
|
137
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
138
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
139
|
+
done();
|
|
140
|
+
});
|
|
158
141
|
});
|
|
159
142
|
|
|
160
143
|
// User not found
|
|
161
144
|
it('should return a JSON object with the error message of ' +
|
|
162
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
163
|
-
|
|
145
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
146
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, (done) => {
|
|
164
147
|
// Call api
|
|
165
|
-
callApi(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
148
|
+
callApi(DUMMY_USER, res => {
|
|
149
|
+
expect(res.status).toBe(HttpStatusCodes.NOT_FOUND);
|
|
150
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
151
|
+
done();
|
|
152
|
+
});
|
|
171
153
|
});
|
|
172
154
|
});
|
|
173
155
|
|
|
174
|
-
//
|
|
156
|
+
// Delete User
|
|
175
157
|
describe(`"DELETE:${Paths.Users.Delete}"`, () => {
|
|
176
158
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const callApi = (id: number) =>
|
|
159
|
+
// Call API
|
|
160
|
+
const callApi = (id: number, cb: TApiCb) =>
|
|
180
161
|
agent
|
|
181
162
|
.delete(insertUrlParams(Paths.Users.Delete, { id }))
|
|
182
|
-
.
|
|
163
|
+
.end(apiCb(cb));
|
|
183
164
|
|
|
184
165
|
// Success
|
|
185
|
-
it(`should return a status code of "${OK}" if the
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
expect(res.body.error).toBeUndefined();
|
|
195
|
-
done();
|
|
196
|
-
});
|
|
166
|
+
it(`should return a status code of "${HttpStatusCodes.OK}" if the ` +
|
|
167
|
+
'request was successful.', (done) => {
|
|
168
|
+
// Setup spies
|
|
169
|
+
spyOn(UserRepo, 'delete').and.resolveTo();
|
|
170
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
171
|
+
// Call api
|
|
172
|
+
callApi(5, res => {
|
|
173
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
174
|
+
done();
|
|
197
175
|
});
|
|
176
|
+
});
|
|
198
177
|
|
|
199
178
|
// User not found
|
|
200
179
|
it('should return a JSON object with the error message of ' +
|
|
201
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// Invalid param
|
|
212
|
-
it(`should return a status code of "${BAD_REQUEST}" and return an error ` +
|
|
213
|
-
`message of "${VALIDATOR_ERR}" if the id was not a valid number`, (done) => {
|
|
214
|
-
callApi('horse' as unknown as number)
|
|
215
|
-
.end((_: Error, res: Response) => {
|
|
216
|
-
expect(res.status).toBe(BAD_REQUEST);
|
|
217
|
-
expect(res.body.error).toBe(VALIDATOR_ERR);
|
|
218
|
-
done();
|
|
219
|
-
});
|
|
180
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
181
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, done => {
|
|
182
|
+
// Setup spies
|
|
183
|
+
callApi(-1, res => {
|
|
184
|
+
expect(res.status).toBe(HttpStatusCodes.NOT_FOUND);
|
|
185
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
186
|
+
done();
|
|
187
|
+
});
|
|
220
188
|
});
|
|
221
189
|
});
|
|
222
190
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Response } from 'supertest';
|
|
2
|
+
|
|
3
|
+
import { IUser } from '@src/models/User';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Misc
|
|
7
|
+
export type TReqBody = Record<string, unknown>;
|
|
8
|
+
export type TRes = Omit<Response, 'body'> & { body: TBody };
|
|
9
|
+
export type TApiCb = (res: TRes) => void;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// Response Body: Add things to the body here over time to prevent
|
|
13
|
+
// typescript errors.
|
|
14
|
+
type TBody = {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
user?: IUser;
|
|
17
|
+
users?: IUser[];
|
|
18
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
// **** Variables **** //
|
|
2
5
|
|
|
3
|
-
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an ' +
|
|
4
|
-
'
|
|
6
|
+
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an object ' +
|
|
7
|
+
'with the appropriate user keys.';
|
|
5
8
|
|
|
6
9
|
export enum UserRoles {
|
|
7
10
|
Standard,
|
|
@@ -15,15 +18,15 @@ export interface IUser {
|
|
|
15
18
|
id: number;
|
|
16
19
|
name: string;
|
|
17
20
|
email: string;
|
|
21
|
+
created: Date;
|
|
22
|
+
role: UserRoles;
|
|
18
23
|
pwdHash?: string;
|
|
19
|
-
role?: UserRoles;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export interface ISessionUser {
|
|
23
27
|
id: number;
|
|
24
28
|
email: string;
|
|
25
29
|
name: string;
|
|
26
|
-
role: IUser['role'];
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
|
|
@@ -35,6 +38,7 @@ export interface ISessionUser {
|
|
|
35
38
|
function new_(
|
|
36
39
|
name?: string,
|
|
37
40
|
email?: string,
|
|
41
|
+
created?: Date,
|
|
38
42
|
role?: UserRoles,
|
|
39
43
|
pwdHash?: string,
|
|
40
44
|
id?: number, // id last cause usually set by db
|
|
@@ -43,8 +47,9 @@ function new_(
|
|
|
43
47
|
id: (id ?? -1),
|
|
44
48
|
name: (name ?? ''),
|
|
45
49
|
email: (email ?? ''),
|
|
50
|
+
created: (created ? new Date(created) : new Date()),
|
|
46
51
|
role: (role ?? UserRoles.Standard),
|
|
47
|
-
pwdHash
|
|
52
|
+
...( pwdHash ? { pwdHash } : {}),
|
|
48
53
|
};
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -52,13 +57,11 @@ function new_(
|
|
|
52
57
|
* Get user instance from object.
|
|
53
58
|
*/
|
|
54
59
|
function from(param: object): IUser {
|
|
55
|
-
// Check is user
|
|
56
60
|
if (!isUser(param)) {
|
|
57
61
|
throw new Error(INVALID_CONSTRUCTOR_PARAM);
|
|
58
62
|
}
|
|
59
|
-
// Get user instance
|
|
60
63
|
const p = param as IUser;
|
|
61
|
-
return new_(p.name, p.email, p.
|
|
64
|
+
return new_(p.name, p.email, p.created, p.id);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
@@ -68,14 +71,16 @@ function isUser(arg: unknown): boolean {
|
|
|
68
71
|
return (
|
|
69
72
|
!!arg &&
|
|
70
73
|
typeof arg === 'object' &&
|
|
71
|
-
'id' in arg &&
|
|
72
|
-
'email' in arg &&
|
|
73
|
-
'name' in arg &&
|
|
74
|
-
'
|
|
74
|
+
'id' in arg && typeof arg.id === 'number' &&
|
|
75
|
+
'email' in arg && typeof arg.email === 'string' &&
|
|
76
|
+
'name' in arg && typeof arg.name === 'string' &&
|
|
77
|
+
'created' in arg && moment(arg.created as string | Date).isValid()
|
|
75
78
|
);
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
|
|
82
|
+
|
|
83
|
+
|
|
79
84
|
// **** Export default **** //
|
|
80
85
|
|
|
81
86
|
export default {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
var Http = (() => {
|
|
2
|
+
|
|
2
3
|
// Setup request for json
|
|
3
4
|
var getOptions = (verb, data) => {
|
|
4
5
|
var options = {
|
|
@@ -9,11 +10,12 @@ var Http = (() => {
|
|
|
9
10
|
'Content-Type': 'application/json',
|
|
10
11
|
},
|
|
11
12
|
};
|
|
12
|
-
if (data) {
|
|
13
|
+
if (!!data) {
|
|
13
14
|
options.body = JSON.stringify(data);
|
|
14
15
|
}
|
|
15
16
|
return options;
|
|
16
17
|
};
|
|
18
|
+
|
|
17
19
|
// Set Http methods
|
|
18
20
|
return {
|
|
19
21
|
get: (path) => fetch(path, getOptions('GET')),
|