@opra/testing 0.29.0 → 0.30.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.
@@ -1,109 +1,129 @@
1
1
  import '../jest-extend/index.js';
2
+ import colors from 'ansi-colors';
3
+ import { ApiExpectBase } from './api-expect-base.js';
2
4
  import { ApiExpectCollection } from './api-expect-collection.js';
3
5
  import { ApiExpectError } from './api-expect-error.js';
4
6
  import { ApiExpectObject } from './api-expect-object.js';
5
7
  import { ApiExpectOperationResult } from './api-expect-operation-result.js';
6
- export class ApiExpect {
7
- constructor(response, _isNot = false) {
8
- this.response = response;
9
- this._isNot = _isNot;
10
- }
11
- get not() {
12
- return new ApiExpect(this.response, !this._isNot);
13
- }
14
- toSuccess(status = 200) {
8
+ export class ApiExpect extends ApiExpectBase {
9
+ /**
10
+ * Tests if request succeeded
11
+ * @param status Status code number between 200-299
12
+ */
13
+ toSuccess(status) {
15
14
  let msg = '';
16
15
  try {
17
- msg = 'Unexpected "status" returned';
18
- this._expect(this.response.status).toStrictEqual(status);
16
+ msg += `The response status isn't as expected.`;
17
+ if (status) {
18
+ expect(this.response.status).toEqual(status);
19
+ }
20
+ else {
21
+ expect(this.response.status).toBeGreaterThanOrEqual(200);
22
+ expect(this.response.status).toBeLessThan(400);
23
+ }
19
24
  }
20
25
  catch (e) {
26
+ e.message = 'Request didn\'t succeeded. ' + msg + '\n\n' + e.message;
21
27
  const issues = this.response.body?.errors;
22
28
  const issue = issues?.[0]?.message;
23
- if (msg)
24
- e.message = msg + '\n\n' + (issue || e.message);
29
+ if (issue) {
30
+ e.message += '\n\n' + colors.yellow('Server message: ') + issue;
31
+ }
25
32
  Error.captureStackTrace(e, this.toSuccess);
26
33
  throw e;
27
34
  }
28
35
  return this;
29
36
  }
30
- toFail(status = 400) {
37
+ /**
38
+ * Tests if request failed
39
+ * @param status Status code number between 400-599
40
+ */
41
+ toFail(status) {
31
42
  let msg = '';
32
43
  try {
33
- msg = 'Response "status" does not match';
44
+ msg += `The response status isn't as expected. `;
34
45
  if (status) {
35
- expect(this.response.status).toStrictEqual(status);
46
+ expect(this.response.status).toEqual(status);
36
47
  }
37
48
  else {
38
49
  expect(this.response.status).toBeGreaterThanOrEqual(400);
39
50
  expect(this.response.status).toBeLessThanOrEqual(599);
40
51
  }
41
- msg = 'Api did not returned "errors"';
42
- expect(this.response.body.errors).toBeArray();
43
- expect(this.response.body.errors.length).toBeGreaterThan(0);
44
52
  }
45
53
  catch (e) {
46
- if (msg)
47
- e.message = msg + '\n\n' + e.message;
48
- Error.captureStackTrace(e, this.toFail);
54
+ e.message = 'Request didn\'t failed. ' + msg + '\n\n' + e.message;
55
+ Error.captureStackTrace(e, this.toSuccess);
49
56
  throw e;
50
57
  }
51
58
  return new ApiExpectError(this.response);
52
59
  }
53
- toReturnOperationResult() {
60
+ /**
61
+ * Tests if API returns a Collection
62
+ */
63
+ toReturnCollection() {
54
64
  let msg = '';
55
65
  try {
56
- msg = '"body" is empty';
57
- expect(this.response.body).toBeDefined();
58
- msg = '"operation" property is empty';
59
- expect(this.response.body.context).toBeDefined();
66
+ msg = 'Content-Type header value is not valid. ';
67
+ expect(this.response.contentType).toEqual('application/opra+json');
68
+ msg = 'Type of response "body" is not valid. ';
69
+ expect(typeof this.response.body).toEqual('object');
70
+ msg = 'Type of "payload" is not an Array. ';
71
+ const payload = this.response.body.payload;
72
+ expect(Array.isArray(payload) ? 'array' : typeof payload).toEqual('array');
60
73
  }
61
74
  catch (e) {
75
+ e.message = 'Api didn\'t returned a Collection. ' + msg + '\n\n' + e.message;
62
76
  if (msg)
63
77
  e.message = msg + '\n\n' + e.message;
64
- Error.captureStackTrace(e, this.toReturnOperationResult);
78
+ Error.captureStackTrace(e, this.toReturnCollection);
65
79
  throw e;
66
80
  }
67
- return new ApiExpectOperationResult(this.response);
81
+ return new ApiExpectCollection(this.response);
68
82
  }
83
+ /**
84
+ * Tests if API returns an Object
85
+ */
69
86
  toReturnObject() {
70
87
  let msg = '';
71
88
  try {
72
- msg = '"body" is empty';
73
- expect(this.response.body).toBeDefined();
74
- expect(typeof this.response.body).toStrictEqual('object');
75
- msg = `"body.payload" is ${typeof typeof this.response.body.payload}`;
76
- expect(typeof this.response.body.payload).toStrictEqual('object');
89
+ msg = 'Content-Type header value is not valid. ';
90
+ expect(this.response.contentType).toEqual('application/opra+json');
91
+ msg = 'Type of response "body" is not valid. ';
92
+ expect(typeof this.response.body).toEqual('object');
93
+ msg = 'Type of "payload" is not an Object. ';
94
+ const payload = this.response.body.payload;
95
+ expect(typeof payload).toEqual('object');
77
96
  }
78
97
  catch (e) {
98
+ e.message = 'Api didn\'t returned an Object. ' + msg + '\n\n' + e.message;
79
99
  if (msg)
80
100
  e.message = msg + '\n\n' + e.message;
81
- Error.captureStackTrace(e, this.toReturnObject);
101
+ Error.captureStackTrace(e, this.toReturnCollection);
82
102
  throw e;
83
103
  }
84
104
  return new ApiExpectObject(this.response);
85
105
  }
86
- toReturnCollection() {
106
+ /**
107
+ * Tests if API returns an OperationResult
108
+ */
109
+ toReturnOperationResult() {
87
110
  let msg = '';
88
111
  try {
89
- msg = '"body" is empty';
90
- expect(this.response.body).toBeDefined();
91
- expect(this.response.body.payload).toBeDefined();
92
- msg = '"body" is not an array';
93
- expect(this.response.body.payload).toBeArray();
112
+ msg = 'Content-Type header value is not valid. ';
113
+ expect(this.response.contentType).toEqual('application/opra+json');
114
+ msg = 'Type of response "body" is not valid. ';
115
+ expect(typeof this.response.body).toEqual('object');
116
+ msg = 'The response has payload. ';
117
+ const payload = this.response.body.payload;
118
+ expect(typeof payload).toEqual('undefined');
94
119
  }
95
120
  catch (e) {
121
+ e.message = 'Api didn\'t returned a OperationResult. ' + msg + '\n\n' + e.message;
96
122
  if (msg)
97
123
  e.message = msg + '\n\n' + e.message;
98
124
  Error.captureStackTrace(e, this.toReturnCollection);
99
125
  throw e;
100
126
  }
101
- return new ApiExpectCollection(this.response);
102
- }
103
- _expect(expected) {
104
- const out = expect(expected);
105
- if (this._isNot)
106
- return out.not;
107
- return out;
127
+ return new ApiExpectOperationResult(this.response);
108
128
  }
109
129
  }
@@ -35,65 +35,6 @@ expect.extend({
35
35
  message: () => 'Value is not an array'
36
36
  };
37
37
  },
38
- toBeSorted(received, compareFn) {
39
- let pass = true;
40
- let message;
41
- if (pass) {
42
- const sorted = [...(received || [])];
43
- sorted.sort(compareFn);
44
- try {
45
- expect(received).toEqual(sorted);
46
- }
47
- catch (e) {
48
- pass = false;
49
- message = () => 'Array items is not sorted as expected';
50
- }
51
- }
52
- return {
53
- actual: received,
54
- message,
55
- pass
56
- };
57
- },
58
- toBeSortedBy(received, properties) {
59
- const fieldsMap = properties.map(x => x.split('.'));
60
- const getValue = (obj, fieldMap) => {
61
- let v = obj;
62
- let i = 0;
63
- while (v && i < fieldMap.length) {
64
- v = v[fieldMap[i++]];
65
- }
66
- return v;
67
- };
68
- let pass = true;
69
- let message;
70
- if (pass) {
71
- const sorted = [...(received || [])];
72
- sorted.sort((a, b) => {
73
- for (const sortField of fieldsMap) {
74
- const l = getValue(a, sortField);
75
- const r = getValue(b, sortField);
76
- if (l < r)
77
- return -1;
78
- if (l > r)
79
- return 1;
80
- }
81
- return 0;
82
- });
83
- try {
84
- expect(received).toEqual(sorted);
85
- }
86
- catch (e) {
87
- pass = false;
88
- message = () => 'Array items is not sorted as expected';
89
- }
90
- }
91
- return {
92
- actual: received,
93
- message,
94
- pass
95
- };
96
- },
97
38
  toBeGreaterThanAny(received, expected) {
98
39
  return compare('toBeGreaterThan', {
99
40
  isNot: this.isNot,
@@ -16,13 +16,15 @@ function _objectMatches(received, expected, path) {
16
16
  throw new Error(`Property "${k}" does not match`);
17
17
  }
18
18
  }
19
- if (ev && typeof ev === 'object')
19
+ else if (ev && typeof ev === 'object') {
20
20
  _objectMatches(rv, ev, path ? path + '.' + k : k);
21
- try {
22
- expect(rv).toStrictEqual(ev);
23
- }
24
- catch {
25
- throw new Error(`Property "${k}" does not match`);
26
21
  }
22
+ else
23
+ try {
24
+ expect(rv).toEqual(ev);
25
+ }
26
+ catch {
27
+ throw new Error(`Property "${k}" does not match`);
28
+ }
27
29
  }
28
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/testing",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Opra testing package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -25,8 +25,8 @@
25
25
  "clean:cover": "rimraf ../../coverage/testing"
26
26
  },
27
27
  "dependencies": {
28
- "@opra/client": "^0.29.0",
29
- "@opra/common": "^0.29.0",
28
+ "@opra/client": "^0.30.0",
29
+ "@opra/common": "^0.30.0",
30
30
  "ansi-colors": "^4.1.3",
31
31
  "lodash.isnil": "^4.0.0",
32
32
  "lodash.omitby": "^4.6.0",
@@ -56,4 +56,4 @@
56
56
  "opra",
57
57
  "testing"
58
58
  ]
59
- }
59
+ }
@@ -0,0 +1,8 @@
1
+ import '../jest-extend/index.js';
2
+ import { HttpResponse } from '@opra/client';
3
+ export declare class ApiExpectBase {
4
+ readonly response: HttpResponse;
5
+ protected readonly isNot?: boolean;
6
+ constructor(response: HttpResponse, isNot?: boolean);
7
+ protected _expect(expected: any): jest.Matchers<any>;
8
+ }
@@ -1,20 +1,38 @@
1
- import '../jest-extend/index.js';
2
- import { HttpResponse } from '@opra/client';
3
1
  import { OpraFilter } from '@opra/common';
4
- export declare class ApiExpectCollection {
5
- readonly response: HttpResponse;
6
- protected _isNot: boolean;
7
- constructor(response: HttpResponse, _isNot?: boolean);
2
+ import { ApiExpectBase } from './api-expect-base.js';
3
+ export declare class ApiExpectCollection extends ApiExpectBase {
8
4
  get not(): ApiExpectCollection;
9
- forEach(callbackfn: (v: any) => void): this;
5
+ /**
6
+ * Tests if Collection have number of items in payload
7
+ * @param min Minimum number of items. Default 1
8
+ * @param max Maximum number of items
9
+ */
10
+ toReturnItems(min?: number, max?: number): this;
11
+ toContainTotalMatches(min?: number, max?: number): this;
12
+ /**
13
+ * Tests if Collection items matches given object
14
+ * @param expected
15
+ */
10
16
  toMatch<T extends {}>(expected: T): this;
11
- toHaveFields(keys: string[]): this;
12
- toHaveFieldsOnly(keys: string[]): this;
13
- toBeSortedBy(...fields: string[]): this;
17
+ /**
18
+ * Tests if Collection items has all of provided fields.
19
+ * @param fields
20
+ */
21
+ toContainFields(fields: string | string[]): this;
22
+ /**
23
+ * Tests if Collection items only contains all of provided fields.
24
+ * @param fields
25
+ */
26
+ toContainAllFields(fields: string | string[]): this;
27
+ /**
28
+ * Tests if Collection is sorted by given field(s).
29
+ * @param fields
30
+ */
31
+ toBeSortedBy(fields: string | string[]): this;
32
+ /**
33
+ * Tests if Collection is filtered by given condition.
34
+ * @param filter
35
+ */
14
36
  toBeFilteredBy(filter: string | OpraFilter.Expression): this;
15
- toHaveExactItems(expected: number): this;
16
- toHaveMaxItems(expected: number): this;
17
- toHaveMinItems(expected: number): this;
18
- protected _expect(expected: any): jest.Matchers<any>;
19
37
  }
20
38
  export declare function convertFilter(str: string | OpraFilter.Expression | undefined): any;
@@ -1,15 +1,14 @@
1
1
  import '../jest-extend/index.js';
2
- import { HttpResponse } from '@opra/client';
3
- import { ApiExpectObject } from './api-expect-object.js';
4
- export declare class ApiExpectError extends ApiExpectObject {
5
- readonly response: HttpResponse;
6
- constructor(response: HttpResponse);
7
- toContainDetail(...matching: any[]): void;
8
- }
9
- declare global {
10
- namespace jest {
11
- interface Matchers<R> {
12
- apiErrorDetailToContain(...issue: any[]): any;
13
- }
14
- }
2
+ import { ErrorIssue } from '@opra/common';
3
+ import { ApiExpectBase } from './api-expect-base.js';
4
+ export type MatchingErrorIssue = Partial<ErrorIssue> & {
5
+ message?: RegExp;
6
+ system?: RegExp;
7
+ code?: RegExp;
8
+ details?: RegExp;
9
+ diagnostics?: RegExp;
10
+ [index: string]: any;
11
+ };
12
+ export declare class ApiExpectError extends ApiExpectBase {
13
+ toMatch(expected: (string | RegExp | MatchingErrorIssue)): this;
15
14
  }
@@ -1,12 +1,19 @@
1
- import '../jest-extend/index.js';
2
- import { HttpResponse } from '@opra/client';
3
- export declare class ApiExpectObject {
4
- readonly response: HttpResponse;
5
- protected _isNot: boolean;
6
- constructor(response: HttpResponse, _isNot?: boolean);
1
+ import { ApiExpectBase } from './api-expect-base.js';
2
+ export declare class ApiExpectObject extends ApiExpectBase {
7
3
  get not(): ApiExpectObject;
4
+ /**
5
+ * Tests if Response payload matches given object
6
+ * @param expected
7
+ */
8
8
  toMatch<T extends {}>(expected: T): this;
9
- toHaveFields(fields: string[]): this;
10
- toHaveFieldsOnly(fields: string[]): this;
11
- protected _expect(expected: any): jest.Matchers<any>;
9
+ /**
10
+ * Tests if Response payload has all of provided fields.
11
+ * @param fields
12
+ */
13
+ toContainFields(fields: string | string[]): this;
14
+ /**
15
+ * Tests if Response payload only contains all of provided fields.
16
+ * @param fields
17
+ */
18
+ toContainAllFields(fields: string | string[]): this;
12
19
  }
@@ -1,12 +1,6 @@
1
1
  import '../jest-extend/index.js';
2
- import { HttpResponse } from '@opra/client';
3
- export declare class ApiExpectOperationResult {
4
- readonly response: HttpResponse;
5
- protected _isNot: boolean;
6
- constructor(response: HttpResponse, _isNot?: boolean);
2
+ import { ApiExpectBase } from './api-expect-base.js';
3
+ export declare class ApiExpectOperationResult extends ApiExpectBase {
7
4
  get not(): ApiExpectOperationResult;
8
- toBeAffectedExact(expected: number): this;
9
- toBeAffectedMin(expected: number): this;
10
- toBeAffectedMax(expected: number): this;
11
- protected _expect(expected: any): jest.Matchers<any>;
5
+ toBeAffected(min?: number, max?: number): this;
12
6
  }
@@ -1,18 +1,30 @@
1
1
  import '../jest-extend/index.js';
2
- import { HttpResponse } from '@opra/client';
2
+ import { ApiExpectBase } from './api-expect-base.js';
3
3
  import { ApiExpectCollection } from './api-expect-collection.js';
4
4
  import { ApiExpectError } from './api-expect-error.js';
5
5
  import { ApiExpectObject } from './api-expect-object.js';
6
6
  import { ApiExpectOperationResult } from './api-expect-operation-result.js';
7
- export declare class ApiExpect {
8
- readonly response: HttpResponse;
9
- protected _isNot: boolean;
10
- constructor(response: HttpResponse, _isNot?: boolean);
11
- get not(): ApiExpect;
7
+ export declare class ApiExpect extends ApiExpectBase {
8
+ /**
9
+ * Tests if request succeeded
10
+ * @param status Status code number between 200-299
11
+ */
12
12
  toSuccess(status?: number): this;
13
+ /**
14
+ * Tests if request failed
15
+ * @param status Status code number between 400-599
16
+ */
13
17
  toFail(status?: number): ApiExpectError;
14
- toReturnOperationResult(): ApiExpectOperationResult;
15
- toReturnObject(): ApiExpectObject;
18
+ /**
19
+ * Tests if API returns a Collection
20
+ */
16
21
  toReturnCollection(): ApiExpectCollection;
17
- protected _expect(expected: any): jest.Matchers<any>;
22
+ /**
23
+ * Tests if API returns an Object
24
+ */
25
+ toReturnObject(): ApiExpectObject;
26
+ /**
27
+ * Tests if API returns an OperationResult
28
+ */
29
+ toReturnOperationResult(): ApiExpectOperationResult;
18
30
  }
@@ -1,21 +1,13 @@
1
1
  declare global {
2
2
  namespace jest {
3
3
  interface Matchers<R> {
4
- toHaveFields(expected: string[]): any;
5
- toHaveFieldsOnly(expected: string[]): any;
6
4
  toBeArray(): any;
7
- toBeSorted(compareFn?: (a: any, b: any) => number): any;
8
- toBeSortedBy(properties: string[]): any;
9
5
  toBeGreaterThanAny(expected: number | bigint | string | Date): any;
10
6
  toBeGreaterThanOrEqualAny(expected: number | bigint | string | Date): any;
11
7
  toBeLessThanAny(expected: number | bigint | string | Date): any;
12
8
  toBeLessThanOrEqualAny(expected: number | bigint | string | Date): any;
13
9
  }
14
10
  interface InverseAsymmetricMatchers {
15
- toContainKeys(expected: string[]): any;
16
- toContainAllKeys(expected: string[]): any;
17
- toBeSorted(compareFn?: (a: any, b: any) => number): any;
18
- toBeSortedBy(properties: string[]): any;
19
11
  toBeGreaterThanAny(expected: number | bigint | string | Date): any;
20
12
  toBeGreaterThanOrEqualAny(expected: number | bigint | string | Date): any;
21
13
  toBeLessThanAny(expected: number | bigint | string | Date): any;