@opra/testing 0.29.1 → 0.30.1

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,134 @@
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 += `Status code do not match. `;
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 as expected. ' + 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 += `Status code do not match. `;
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 as expected. ' + msg + '\n\n' + e.message;
55
+ const issues = this.response.body?.errors;
56
+ const issue = issues?.[0]?.message;
57
+ if (issue) {
58
+ e.message += '\n\n' + colors.yellow('Server message: ') + issue;
59
+ }
60
+ Error.captureStackTrace(e, this.toSuccess);
49
61
  throw e;
50
62
  }
51
63
  return new ApiExpectError(this.response);
52
64
  }
53
- toReturnOperationResult() {
65
+ /**
66
+ * Tests if API returns a Collection
67
+ */
68
+ toReturnCollection() {
54
69
  let msg = '';
55
70
  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();
71
+ msg = 'Content-Type header value is not valid. ';
72
+ expect(this.response.contentType).toEqual('application/opra+json');
73
+ msg = 'Type of response "body" is not valid. ';
74
+ expect(typeof this.response.body).toEqual('object');
75
+ msg = 'Type of "payload" is not an Array. ';
76
+ const payload = this.response.body.payload;
77
+ expect(Array.isArray(payload) ? 'array' : typeof payload).toEqual('array');
60
78
  }
61
79
  catch (e) {
80
+ e.message = 'Api didn\'t returned a Collection. ' + msg + '\n\n' + e.message;
62
81
  if (msg)
63
82
  e.message = msg + '\n\n' + e.message;
64
- Error.captureStackTrace(e, this.toReturnOperationResult);
83
+ Error.captureStackTrace(e, this.toReturnCollection);
65
84
  throw e;
66
85
  }
67
- return new ApiExpectOperationResult(this.response);
86
+ return new ApiExpectCollection(this.response);
68
87
  }
88
+ /**
89
+ * Tests if API returns an Object
90
+ */
69
91
  toReturnObject() {
70
92
  let msg = '';
71
93
  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');
94
+ msg = 'Content-Type header value is not valid. ';
95
+ expect(this.response.contentType).toEqual('application/opra+json');
96
+ msg = 'Type of response "body" is not valid. ';
97
+ expect(typeof this.response.body).toEqual('object');
98
+ msg = 'Type of "payload" is not an Object. ';
99
+ const payload = this.response.body.payload;
100
+ expect(typeof payload).toEqual('object');
77
101
  }
78
102
  catch (e) {
103
+ e.message = 'Api didn\'t returned an Object. ' + msg + '\n\n' + e.message;
79
104
  if (msg)
80
105
  e.message = msg + '\n\n' + e.message;
81
- Error.captureStackTrace(e, this.toReturnObject);
106
+ Error.captureStackTrace(e, this.toReturnCollection);
82
107
  throw e;
83
108
  }
84
109
  return new ApiExpectObject(this.response);
85
110
  }
86
- toReturnCollection() {
111
+ /**
112
+ * Tests if API returns an OperationResult
113
+ */
114
+ toReturnOperationResult() {
87
115
  let msg = '';
88
116
  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();
117
+ msg = 'Content-Type header value is not valid. ';
118
+ expect(this.response.contentType).toEqual('application/opra+json');
119
+ msg = 'Type of response "body" is not valid. ';
120
+ expect(typeof this.response.body).toEqual('object');
121
+ msg = 'The response has payload. ';
122
+ const payload = this.response.body.payload;
123
+ expect(typeof payload).toEqual('undefined');
94
124
  }
95
125
  catch (e) {
126
+ e.message = 'Api didn\'t returned a OperationResult. ' + msg + '\n\n' + e.message;
96
127
  if (msg)
97
128
  e.message = msg + '\n\n' + e.message;
98
129
  Error.captureStackTrace(e, this.toReturnCollection);
99
130
  throw e;
100
131
  }
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;
132
+ return new ApiExpectOperationResult(this.response);
108
133
  }
109
134
  }
@@ -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.1",
3
+ "version": "0.30.1",
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.1",
29
- "@opra/common": "^0.29.1",
28
+ "@opra/client": "^0.30.1",
29
+ "@opra/common": "^0.30.1",
30
30
  "ansi-colors": "^4.1.3",
31
31
  "lodash.isnil": "^4.0.0",
32
32
  "lodash.omitby": "^4.6.0",
@@ -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;