@opra/testing 0.1.1 → 0.3.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 (63) hide show
  1. package/cjs/api-response.js +22 -0
  2. package/cjs/expect/api-expect-array.js +186 -0
  3. package/cjs/expect/api-expect-error.js +62 -0
  4. package/cjs/expect/api-expect-object.js +62 -17
  5. package/cjs/expect/api-expect-operation-result.js +43 -0
  6. package/cjs/expect/api-expect.js +84 -25
  7. package/cjs/expect/{jest-extend.js → jest-extend/common.extend.js} +60 -21
  8. package/cjs/expect/utils/object-matches.util.js +32 -0
  9. package/cjs/expect/utils/print-errors.util.js +15 -0
  10. package/cjs/index.js +3 -8
  11. package/cjs/testers/base-operation-tester.js +12 -4
  12. package/cjs/testers/entity-delete-many-tester.js +4 -0
  13. package/cjs/testers/entity-delete-tester.js +2 -0
  14. package/cjs/testers/entity-get-tester.js +2 -0
  15. package/cjs/testers/entity-search-tester.js +1 -1
  16. package/cjs/testers/entity-update-many-tester.js +4 -0
  17. package/cjs/testers/entity-update-tester.js +2 -0
  18. package/esm/api-response.d.ts +12 -0
  19. package/esm/api-response.js +18 -0
  20. package/esm/expect/api-expect-array.d.ts +17 -0
  21. package/esm/expect/api-expect-array.js +180 -0
  22. package/esm/expect/api-expect-error.d.ts +14 -0
  23. package/esm/expect/api-expect-error.js +57 -0
  24. package/esm/expect/api-expect-object.d.ts +11 -9
  25. package/esm/expect/api-expect-object.js +61 -17
  26. package/esm/expect/api-expect-operation-result.d.ts +9 -0
  27. package/esm/expect/api-expect-operation-result.js +39 -0
  28. package/esm/expect/api-expect.d.ts +12 -9
  29. package/esm/expect/api-expect.js +84 -25
  30. package/esm/expect/{jest-extend.d.ts → jest-extend/common.extend.d.ts} +7 -6
  31. package/esm/expect/{jest-extend.js → jest-extend/common.extend.js} +60 -21
  32. package/esm/expect/utils/object-matches.util.d.ts +1 -0
  33. package/esm/expect/utils/object-matches.util.js +28 -0
  34. package/esm/expect/utils/print-errors.util.d.ts +1 -0
  35. package/esm/expect/utils/print-errors.util.js +11 -0
  36. package/esm/index.d.ts +1 -4
  37. package/esm/index.js +1 -5
  38. package/esm/testers/base-operation-tester.d.ts +3 -7
  39. package/esm/testers/base-operation-tester.js +12 -4
  40. package/esm/testers/entity-create-tester.d.ts +2 -2
  41. package/esm/testers/entity-delete-many-tester.d.ts +4 -2
  42. package/esm/testers/entity-delete-many-tester.js +4 -0
  43. package/esm/testers/entity-delete-tester.d.ts +2 -2
  44. package/esm/testers/entity-delete-tester.js +2 -0
  45. package/esm/testers/entity-get-tester.d.ts +2 -2
  46. package/esm/testers/entity-get-tester.js +2 -0
  47. package/esm/testers/entity-search-tester.d.ts +3 -3
  48. package/esm/testers/entity-search-tester.js +1 -1
  49. package/esm/testers/entity-tester.d.ts +8 -8
  50. package/esm/testers/entity-update-many-tester.d.ts +4 -2
  51. package/esm/testers/entity-update-many-tester.js +4 -0
  52. package/esm/testers/entity-update-tester.d.ts +2 -2
  53. package/esm/testers/entity-update-tester.js +2 -0
  54. package/package.json +3 -3
  55. package/cjs/expect/api-expect-body.js +0 -37
  56. package/cjs/expect/api-expect-fail.js +0 -15
  57. package/cjs/expect/api-expect-list.js +0 -130
  58. package/esm/expect/api-expect-body.d.ts +0 -6
  59. package/esm/expect/api-expect-body.js +0 -32
  60. package/esm/expect/api-expect-fail.d.ts +0 -6
  61. package/esm/expect/api-expect-fail.js +0 -11
  62. package/esm/expect/api-expect-list.d.ts +0 -17
  63. package/esm/expect/api-expect-list.js +0 -124
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiResponse = void 0;
4
+ const api_expect_js_1 = require("./expect/api-expect.js");
5
+ class ApiResponse {
6
+ status;
7
+ body;
8
+ rawBody;
9
+ headers;
10
+ contentType;
11
+ charset;
12
+ expect;
13
+ constructor(args) {
14
+ Object.assign(this, args);
15
+ this.expect = new api_expect_js_1.ApiExpect(this);
16
+ }
17
+ get(header) {
18
+ const key = Object.keys(this.headers).find(k => k.toLowerCase() === header.toLowerCase());
19
+ return (key && this.headers[key]) || '';
20
+ }
21
+ }
22
+ exports.ApiResponse = ApiResponse;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertFilter = exports.ApiExpectArray = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
+ const rule_judgment_1 = tslib_1.__importDefault(require("rule-judgment"));
7
+ const url_1 = require("@opra/url");
8
+ class ApiExpectArray {
9
+ response;
10
+ constructor(response) {
11
+ this.response = response;
12
+ }
13
+ toMatch(expected) {
14
+ try {
15
+ const v = lodash_1.default.omitBy(expected, lodash_1.default.isNil);
16
+ for (const item of this.response.body) {
17
+ expect(item).toMatchObject(v);
18
+ }
19
+ }
20
+ catch (e) {
21
+ Error.captureStackTrace(e, this.toMatch);
22
+ throw e;
23
+ }
24
+ return this;
25
+ }
26
+ toContainAllKeys(keys) {
27
+ try {
28
+ for (const item of this.response.body) {
29
+ expect(item).toContainAllKeys(keys);
30
+ }
31
+ }
32
+ catch (e) {
33
+ Error.captureStackTrace(e, this.toContainAllKeys);
34
+ throw e;
35
+ }
36
+ return this;
37
+ }
38
+ toContainKeys(keys) {
39
+ try {
40
+ for (const item of this.response.body) {
41
+ expect(item).toContainKeys(keys);
42
+ }
43
+ }
44
+ catch (e) {
45
+ Error.captureStackTrace(e, this.toContainKeys);
46
+ throw e;
47
+ }
48
+ return this;
49
+ }
50
+ notToContainKeys(keys) {
51
+ try {
52
+ for (const item of this.response.body) {
53
+ expect(item).not.toContainKeys(keys);
54
+ }
55
+ }
56
+ catch (e) {
57
+ Error.captureStackTrace(e, this.notToContainKeys);
58
+ throw e;
59
+ }
60
+ return this;
61
+ }
62
+ toHaveProperty(keyPath, value) {
63
+ try {
64
+ for (const item of this.response.body) {
65
+ expect(item).toHaveProperty(keyPath, value);
66
+ }
67
+ }
68
+ catch (e) {
69
+ Error.captureStackTrace(e, this.toHaveProperty);
70
+ throw e;
71
+ }
72
+ return this;
73
+ }
74
+ toBeSortedBy(...fields) {
75
+ try {
76
+ expect(this.response.body).toBeSortedBy(fields);
77
+ }
78
+ catch (e) {
79
+ Error.captureStackTrace(e, this.toBeSortedBy);
80
+ throw e;
81
+ }
82
+ return this;
83
+ }
84
+ toBeFilteredBy(filter) {
85
+ const f = convertFilter(filter);
86
+ if (f) {
87
+ const j = (0, rule_judgment_1.default)(f);
88
+ const filtered = this.response.body.filter(j);
89
+ try {
90
+ expect(this.response.body).toStrictEqual(filtered);
91
+ }
92
+ catch (e) {
93
+ Error.captureStackTrace(e, this.toBeFilteredBy);
94
+ throw e;
95
+ }
96
+ }
97
+ return this;
98
+ }
99
+ toHaveExactItems(expected) {
100
+ try {
101
+ expect(this.response.body).toHaveLength(expected);
102
+ }
103
+ catch (e) {
104
+ Error.captureStackTrace(e, this.toHaveExactItems);
105
+ throw e;
106
+ }
107
+ return this;
108
+ }
109
+ toHaveMaxItems(expected) {
110
+ try {
111
+ expect(this.response.body.length).toBeLessThanOrEqual(expected);
112
+ }
113
+ catch (e) {
114
+ Error.captureStackTrace(e, this.toHaveMaxItems);
115
+ throw e;
116
+ }
117
+ return this;
118
+ }
119
+ toHaveMinItems(expected) {
120
+ try {
121
+ expect(this.response.body.length).toBeGreaterThanOrEqual(expected);
122
+ }
123
+ catch (e) {
124
+ Error.captureStackTrace(e, this.toHaveMinItems);
125
+ throw e;
126
+ }
127
+ return this;
128
+ }
129
+ }
130
+ exports.ApiExpectArray = ApiExpectArray;
131
+ function convertFilter(str) {
132
+ const ast = typeof str === 'string' ? (0, url_1.$parse)(str) : str;
133
+ if (!ast)
134
+ return;
135
+ if (ast instanceof url_1.ComparisonExpression) {
136
+ const left = convertFilter(ast.left);
137
+ const right = convertFilter(ast.right);
138
+ switch (ast.op) {
139
+ case '=':
140
+ return { $eq: { [left]: right } };
141
+ case '!=':
142
+ return { $ne: { [left]: right } };
143
+ case '>':
144
+ return { $gt: { [left]: right } };
145
+ case '>=':
146
+ return { $gte: { [left]: right } };
147
+ case '<':
148
+ return { $lt: { [left]: right } };
149
+ case '<=':
150
+ return { $lte: { [left]: right } };
151
+ case 'in':
152
+ return { $in: { [left]: right } };
153
+ case '!in':
154
+ return { $nin: { [left]: right } };
155
+ default:
156
+ throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
157
+ }
158
+ }
159
+ if (ast instanceof url_1.QualifiedIdentifier) {
160
+ return ast.value;
161
+ }
162
+ if (ast instanceof url_1.NumberLiteral ||
163
+ ast instanceof url_1.StringLiteral ||
164
+ ast instanceof url_1.BooleanLiteral ||
165
+ ast instanceof url_1.NullLiteral ||
166
+ ast instanceof url_1.DateLiteral ||
167
+ ast instanceof url_1.TimeLiteral) {
168
+ return ast.value;
169
+ }
170
+ if (ast instanceof url_1.ArrayExpression) {
171
+ return ast.items.map(convertFilter);
172
+ }
173
+ if (ast instanceof url_1.LogicalExpression) {
174
+ if (ast.op === 'or')
175
+ return { $or: ast.items.map(convertFilter) };
176
+ return { $and: ast.items.map(convertFilter) };
177
+ }
178
+ if (ast instanceof url_1.ArrayExpression) {
179
+ return ast.items.map(convertFilter);
180
+ }
181
+ if (ast instanceof url_1.ParenthesesExpression) {
182
+ return convertFilter(ast.expression);
183
+ }
184
+ throw new Error(`${ast.kind} is not implemented yet`);
185
+ }
186
+ exports.convertFilter = convertFilter;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiExpectError = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const jest_matcher_utils_1 = tslib_1.__importDefault(require("jest-matcher-utils"));
6
+ const api_expect_object_js_1 = require("./api-expect-object.js");
7
+ const object_matches_util_js_1 = require("./utils/object-matches.util.js");
8
+ class ApiExpectError extends api_expect_object_js_1.ApiExpectObject {
9
+ response;
10
+ constructor(response) {
11
+ super(response);
12
+ this.response = response;
13
+ }
14
+ toContainDetail(...matching) {
15
+ try {
16
+ expect(this.response.body['@@Schema']).toStrictEqual('Opra:Exception');
17
+ expect(this.response.body.issues).toBeDefined();
18
+ expect(this.response.body.issues).apiErrorDetailToContain(matching);
19
+ }
20
+ catch (e) {
21
+ Error.captureStackTrace(e, this.toContainDetail);
22
+ throw e;
23
+ }
24
+ }
25
+ }
26
+ exports.ApiExpectError = ApiExpectError;
27
+ expect.extend({
28
+ apiErrorDetailToContain(received, issues) {
29
+ try {
30
+ expect(typeof received).toStrictEqual('object');
31
+ expect(Array.isArray(received)).toStrictEqual(true);
32
+ }
33
+ catch (e) {
34
+ return {
35
+ pass: false,
36
+ message: () => e.message
37
+ };
38
+ }
39
+ for (const m of issues) {
40
+ let matched = false;
41
+ for (const detail of received) {
42
+ if (typeof m === 'object') {
43
+ try {
44
+ (0, object_matches_util_js_1.objectMatches)(detail, m);
45
+ matched = true;
46
+ break;
47
+ }
48
+ catch {
49
+ //
50
+ }
51
+ }
52
+ }
53
+ if (!matched) {
54
+ return {
55
+ pass: false,
56
+ message: () => `Object does not match: \n` + jest_matcher_utils_1.default.stringify(m)
57
+ };
58
+ }
59
+ }
60
+ return { actual: true, pass: true, message: () => '' };
61
+ },
62
+ });
@@ -1,30 +1,75 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiExpectObject = void 0;
4
- const api_expect_body_js_1 = require("./api-expect-body.js");
5
- class ApiExpectObject extends api_expect_body_js_1.ApiExpectBody {
6
- _body;
7
- constructor(_body) {
8
- super();
9
- this._body = _body;
10
- }
11
- toBeDefined() {
12
- expect(this._body).toBeDefined();
4
+ const tslib_1 = require("tslib");
5
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
+ class ApiExpectObject {
7
+ response;
8
+ constructor(response) {
9
+ this.response = response;
10
+ }
11
+ get body() {
12
+ return this.response.body;
13
+ }
14
+ toMatch(expected) {
15
+ try {
16
+ const v = lodash_1.default.omitBy(expected, lodash_1.default.isNil);
17
+ expect(this.response.body).toMatchObject(v);
18
+ }
19
+ catch (e) {
20
+ Error.captureStackTrace(e, this.toMatch);
21
+ throw e;
22
+ }
13
23
  return this;
14
24
  }
15
- toMatch(value) {
16
- return this._toMatchObject([this._body], value);
25
+ toContainAllKeys(keys) {
26
+ try {
27
+ expect(this.response.body).toContainAllKeys(keys);
28
+ }
29
+ catch (e) {
30
+ Error.captureStackTrace(e, this.toContainAllKeys);
31
+ throw e;
32
+ }
33
+ return this;
34
+ }
35
+ toContainKeys(keys) {
36
+ try {
37
+ expect(this.response.body).toContainKeys(keys);
38
+ }
39
+ catch (e) {
40
+ Error.captureStackTrace(e, this.toContainKeys);
41
+ throw e;
42
+ }
43
+ return this;
17
44
  }
18
- haveKeysOnly(keys) {
19
- this._haveKeysOnly([this._body], keys);
45
+ notToContainKeys(keys) {
46
+ try {
47
+ expect(this.response.body).not.toContainKeys(keys);
48
+ }
49
+ catch (e) {
50
+ Error.captureStackTrace(e, this.notToContainKeys);
51
+ throw e;
52
+ }
20
53
  return this;
21
54
  }
22
- haveKeys(keys) {
23
- this._haveKeys([this._body], keys);
55
+ toHaveProperty(keyPath, value) {
56
+ try {
57
+ expect(this.response.body).toHaveProperty(keyPath, value);
58
+ }
59
+ catch (e) {
60
+ Error.captureStackTrace(e, this.toContainKeys);
61
+ throw e;
62
+ }
24
63
  return this;
25
64
  }
26
- notHaveKeys(keys) {
27
- this._notHaveKeys([this._body], keys);
65
+ notToHaveProperty(keyPath, value) {
66
+ try {
67
+ expect(this.response.body).not.toHaveProperty(keyPath, value);
68
+ }
69
+ catch (e) {
70
+ Error.captureStackTrace(e, this.toContainKeys);
71
+ throw e;
72
+ }
28
73
  return this;
29
74
  }
30
75
  }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiExpectOperationResult = void 0;
4
+ class ApiExpectOperationResult {
5
+ response;
6
+ constructor(response) {
7
+ this.response = response;
8
+ }
9
+ get body() {
10
+ return this.response.body;
11
+ }
12
+ toBeAffectedExact(expected) {
13
+ try {
14
+ expect(this.response.body.affected).toStrictEqual(expected);
15
+ }
16
+ catch (e) {
17
+ Error.captureStackTrace(e, this.toBeAffectedExact);
18
+ throw e;
19
+ }
20
+ return this;
21
+ }
22
+ toBeAffectedMin(expected) {
23
+ try {
24
+ expect(this.response.body.affected).toBeGreaterThanOrEqual(expected);
25
+ }
26
+ catch (e) {
27
+ Error.captureStackTrace(e, this.toBeAffectedMin);
28
+ throw e;
29
+ }
30
+ return this;
31
+ }
32
+ toBeAffectedMax(expected) {
33
+ try {
34
+ expect(this.response.body.affected).toBeLessThanOrEqual(expected);
35
+ }
36
+ catch (e) {
37
+ Error.captureStackTrace(e, this.toBeAffectedMax);
38
+ throw e;
39
+ }
40
+ return this;
41
+ }
42
+ }
43
+ exports.ApiExpectOperationResult = ApiExpectOperationResult;
@@ -1,42 +1,101 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiExpect = void 0;
4
- require("./jest-extend.js");
5
- const api_expect_fail_js_1 = require("./api-expect-fail.js");
6
- const api_expect_list_js_1 = require("./api-expect-list.js");
4
+ require("./jest-extend/common.extend.js");
5
+ const api_expect_array_js_1 = require("./api-expect-array.js");
6
+ const api_expect_error_js_1 = require("./api-expect-error.js");
7
7
  const api_expect_object_js_1 = require("./api-expect-object.js");
8
+ const api_expect_operation_result_js_1 = require("./api-expect-operation-result.js");
8
9
  class ApiExpect {
9
10
  response;
10
11
  constructor(response) {
11
12
  this.response = response;
12
13
  }
13
- toSuccess(status) {
14
- expect(this.response.body.errors).toStrictEqual(undefined);
15
- if (status)
16
- expect(this.response.status).toEqual(status);
17
- else {
18
- expect(this.response.status).toBeGreaterThanOrEqual(200);
19
- expect(this.response.status).toBeLessThan(300);
14
+ get body() {
15
+ return this.response.body;
16
+ }
17
+ toSuccess(status = 200) {
18
+ let msg = ';';
19
+ try {
20
+ msg = 'Response "status" is not valid';
21
+ expect(this.response.status).toStrictEqual(status);
22
+ }
23
+ catch (e) {
24
+ if (msg)
25
+ e.message = msg + '\n\n' + e.message;
26
+ Error.captureStackTrace(e, this.toSuccess);
27
+ throw e;
20
28
  }
21
29
  return this;
22
30
  }
23
- toReturnObject(fn) {
24
- expect(this.response.body).toBeDefined();
25
- fn(new api_expect_object_js_1.ApiExpectObject(this.response.body));
26
- return this;
31
+ toFail(status = 400) {
32
+ let msg = ';';
33
+ try {
34
+ msg = 'Response "status" does not match';
35
+ if (status) {
36
+ expect(this.response.status).toStrictEqual(status);
37
+ }
38
+ else {
39
+ expect(this.response.status).toBeGreaterThanOrEqual(400);
40
+ expect(this.response.status).toBeLessThanOrEqual(599);
41
+ }
42
+ msg = 'Api did not returned "errors"';
43
+ expect(this.response.body.errors).toBeArray();
44
+ expect(this.response.body.errors.length).toBeGreaterThan(0);
45
+ }
46
+ catch (e) {
47
+ if (msg)
48
+ e.message = msg + '\n\n' + e.message;
49
+ Error.captureStackTrace(e, this.toFail);
50
+ throw e;
51
+ }
52
+ return new api_expect_error_js_1.ApiExpectError(this.response);
27
53
  }
28
- toReturnList(fn) {
29
- expect(this.response.body).toBeDefined();
30
- expect(this.response.body.items).toBeDefined();
31
- fn(new api_expect_list_js_1.ApiExpectList(this.response.body));
32
- return this;
54
+ toReturnOperationResult() {
55
+ let msg = ';';
56
+ try {
57
+ msg = '"body" is empty';
58
+ expect(this.response.body).toBeDefined();
59
+ msg = '"operation" property is empty';
60
+ expect(this.response.body.operation).toBeDefined();
61
+ }
62
+ catch (e) {
63
+ if (msg)
64
+ e.message = msg + '\n\n' + e.message;
65
+ Error.captureStackTrace(e, this.toReturnObject);
66
+ throw e;
67
+ }
68
+ return new api_expect_operation_result_js_1.ApiExpectOperationResult(this.response);
33
69
  }
34
- toFail(status = 400, fn) {
35
- expect(this.response.body.errors).toBeDefined();
36
- expect(this.response.status).toEqual(status);
37
- if (fn)
38
- fn(new api_expect_fail_js_1.ApiExpectFail(this.response.body.errors));
39
- return this;
70
+ toReturnObject() {
71
+ let msg = ';';
72
+ try {
73
+ msg = '"body" is empty';
74
+ expect(this.response.body).toBeDefined();
75
+ }
76
+ catch (e) {
77
+ if (msg)
78
+ e.message = msg + '\n\n' + e.message;
79
+ Error.captureStackTrace(e, this.toReturnObject);
80
+ throw e;
81
+ }
82
+ return new api_expect_object_js_1.ApiExpectObject(this.response);
83
+ }
84
+ toReturnArray() {
85
+ let msg = ';';
86
+ try {
87
+ msg = '"body" is empty';
88
+ expect(this.response.body).toBeDefined();
89
+ msg = '"body" is not an array';
90
+ expect(this.response.body).toBeArray();
91
+ }
92
+ catch (e) {
93
+ if (msg)
94
+ e.message = msg + '\n\n' + e.message;
95
+ Error.captureStackTrace(e, this.toReturnArray);
96
+ throw e;
97
+ }
98
+ return new api_expect_array_js_1.ApiExpectArray(this.response);
40
99
  }
41
100
  }
42
101
  exports.ApiExpect = ApiExpect;
@@ -2,11 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const jest_matcher_utils_1 = require("jest-matcher-utils");
4
4
  expect.extend({
5
- objectHaveKeysOnly(received, expected) {
5
+ toContainKeys(received, expected) {
6
6
  if (typeof received === 'object') {
7
7
  const keys = Array.isArray(expected) ? expected : Object.keys(expected);
8
8
  const additionalKeys = Object.keys(received).filter(x => !keys.includes(x));
9
- if (additionalKeys.length) {
9
+ if (!Object.keys(received).find(x => keys.includes(x))) {
10
10
  return {
11
11
  pass: false,
12
12
  message: () => `Object contains unexpected additional keys (${additionalKeys})`
@@ -15,33 +15,33 @@ expect.extend({
15
15
  }
16
16
  return { actual: received, pass: true, message: () => '' };
17
17
  },
18
- objectMatches(received, expected) {
18
+ toContainAllKeys(received, expected) {
19
19
  if (typeof received === 'object') {
20
- const keys = Object.keys(expected);
21
- for (const k of keys) {
22
- const v = k.split('.').reduce((a, b) => a[b], received);
23
- try {
24
- const matching = expected[k];
25
- if (typeof matching === 'function')
26
- matching(v);
27
- else
28
- expect(v).toEqual(matching);
29
- }
30
- catch (e) {
31
- return {
32
- pass: false,
33
- message: () => `${k} does not match: ${e.message}`
34
- };
35
- }
20
+ const keys = Array.isArray(expected) ? expected : Object.keys(expected);
21
+ const additionalKeys = Object.keys(received).filter(x => !keys.includes(x));
22
+ if (additionalKeys.length) {
23
+ return {
24
+ pass: false,
25
+ message: () => `Object contains unexpected additional keys (${additionalKeys})`
26
+ };
36
27
  }
37
28
  }
38
29
  return { actual: received, pass: true, message: () => '' };
39
30
  },
31
+ toBeArray(received) {
32
+ if (Array.isArray(received)) {
33
+ return { actual: received, pass: true, message: () => '' };
34
+ }
35
+ return {
36
+ pass: false,
37
+ message: () => 'Value is not an array'
38
+ };
39
+ },
40
40
  toBeSorted(received, compareFn) {
41
- let pass = Array.isArray(received);
41
+ let pass = true;
42
42
  let message;
43
43
  if (pass) {
44
- const sorted = [...received];
44
+ const sorted = [...(received || [])];
45
45
  sorted.sort(compareFn);
46
46
  try {
47
47
  expect(received).toEqual(sorted);
@@ -57,6 +57,45 @@ expect.extend({
57
57
  pass
58
58
  };
59
59
  },
60
+ toBeSortedBy(received, properties) {
61
+ const fieldsMap = properties.map(x => x.split('.'));
62
+ const getValue = (obj, fieldMap) => {
63
+ let v = obj;
64
+ let i = 0;
65
+ while (v && i < fieldMap.length) {
66
+ v = v[fieldMap[i++]];
67
+ }
68
+ return v;
69
+ };
70
+ let pass = true;
71
+ let message;
72
+ if (pass) {
73
+ const sorted = [...(received || [])];
74
+ sorted.sort((a, b) => {
75
+ for (const sortField of fieldsMap) {
76
+ const l = getValue(a, sortField);
77
+ const r = getValue(b, sortField);
78
+ if (l < r)
79
+ return -1;
80
+ if (l > r)
81
+ return 1;
82
+ }
83
+ return 0;
84
+ });
85
+ try {
86
+ expect(received).toEqual(sorted);
87
+ }
88
+ catch (e) {
89
+ pass = false;
90
+ message = () => 'Array items is not sorted as expected';
91
+ }
92
+ }
93
+ return {
94
+ actual: received,
95
+ message,
96
+ pass
97
+ };
98
+ },
60
99
  toBeGreaterThanAny(received, expected) {
61
100
  return compare('toBeGreaterThan', {
62
101
  isNot: this.isNot,
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.objectMatches = void 0;
4
+ function objectMatches(received, expected) {
5
+ _objectMatches(received, expected, '');
6
+ }
7
+ exports.objectMatches = objectMatches;
8
+ function _objectMatches(received, expected, path) {
9
+ if (typeof received !== typeof expected)
10
+ expect(typeof received).toStrictEqual('object');
11
+ const keys = Object.keys(expected);
12
+ for (const k of keys) {
13
+ const rv = received[k];
14
+ const ev = expected[k];
15
+ if (ev instanceof RegExp) {
16
+ try {
17
+ expect(rv).toMatch(ev);
18
+ }
19
+ catch {
20
+ throw new Error(`Property "${k}" does not match`);
21
+ }
22
+ }
23
+ if (ev && typeof ev === 'object')
24
+ _objectMatches(rv, ev, path ? path + '.' + k : k);
25
+ try {
26
+ expect(rv).toStrictEqual(ev);
27
+ }
28
+ catch {
29
+ throw new Error(`Property "${k}" does not match`);
30
+ }
31
+ }
32
+ }