@opra/testing 0.4.0 → 0.6.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.
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertFilter = exports.ApiExpectCollection = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const lodash_1 = tslib_1.__importDefault(require("lodash"));
5
+ const lodash_1 = require("lodash");
6
6
  const rule_judgment_1 = tslib_1.__importDefault(require("rule-judgment"));
7
- const url_1 = require("@opra/url");
7
+ const common_1 = require("@opra/common");
8
8
  class ApiExpectCollection {
9
9
  response;
10
10
  _isNot;
@@ -21,7 +21,7 @@ class ApiExpectCollection {
21
21
  }
22
22
  toMatch(expected) {
23
23
  try {
24
- const v = lodash_1.default.omitBy(expected, lodash_1.default.isNil);
24
+ const v = (0, lodash_1.omitBy)(expected, lodash_1.isNil);
25
25
  for (const item of this.response.data) {
26
26
  this._expect(item).toMatchObject(v);
27
27
  }
@@ -133,10 +133,10 @@ class ApiExpectCollection {
133
133
  }
134
134
  exports.ApiExpectCollection = ApiExpectCollection;
135
135
  function convertFilter(str) {
136
- const ast = typeof str === 'string' ? (0, url_1.$parse)(str) : str;
136
+ const ast = typeof str === 'string' ? (0, common_1.parseFilter)(str) : str;
137
137
  if (!ast)
138
138
  return;
139
- if (ast instanceof url_1.ComparisonExpression) {
139
+ if (ast instanceof common_1.ComparisonExpression) {
140
140
  const left = convertFilter(ast.left);
141
141
  const right = convertFilter(ast.right);
142
142
  switch (ast.op) {
@@ -160,29 +160,29 @@ function convertFilter(str) {
160
160
  throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
161
161
  }
162
162
  }
163
- if (ast instanceof url_1.QualifiedIdentifier) {
163
+ if (ast instanceof common_1.QualifiedIdentifier) {
164
164
  return ast.value;
165
165
  }
166
- if (ast instanceof url_1.NumberLiteral ||
167
- ast instanceof url_1.StringLiteral ||
168
- ast instanceof url_1.BooleanLiteral ||
169
- ast instanceof url_1.NullLiteral ||
170
- ast instanceof url_1.DateLiteral ||
171
- ast instanceof url_1.TimeLiteral) {
166
+ if (ast instanceof common_1.NumberLiteral ||
167
+ ast instanceof common_1.StringLiteral ||
168
+ ast instanceof common_1.BooleanLiteral ||
169
+ ast instanceof common_1.NullLiteral ||
170
+ ast instanceof common_1.DateLiteral ||
171
+ ast instanceof common_1.TimeLiteral) {
172
172
  return ast.value;
173
173
  }
174
- if (ast instanceof url_1.ArrayExpression) {
174
+ if (ast instanceof common_1.ArrayExpression) {
175
175
  return ast.items.map(convertFilter);
176
176
  }
177
- if (ast instanceof url_1.LogicalExpression) {
177
+ if (ast instanceof common_1.LogicalExpression) {
178
178
  if (ast.op === 'or')
179
179
  return { $or: ast.items.map(convertFilter) };
180
180
  return { $and: ast.items.map(convertFilter) };
181
181
  }
182
- if (ast instanceof url_1.ArrayExpression) {
182
+ if (ast instanceof common_1.ArrayExpression) {
183
183
  return ast.items.map(convertFilter);
184
184
  }
185
- if (ast instanceof url_1.ParenthesesExpression) {
185
+ if (ast instanceof common_1.ParenthesesExpression) {
186
186
  return convertFilter(ast.expression);
187
187
  }
188
188
  throw new Error(`${ast.kind} is not implemented yet`);
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiExpectObject = void 0;
4
- const tslib_1 = require("tslib");
5
- const lodash_1 = tslib_1.__importDefault(require("lodash"));
4
+ const lodash_1 = require("lodash");
6
5
  class ApiExpectObject {
7
6
  response;
8
7
  _isNot;
@@ -15,7 +14,7 @@ class ApiExpectObject {
15
14
  }
16
15
  toMatch(expected) {
17
16
  try {
18
- const v = lodash_1.default.omitBy(expected, lodash_1.default.isNil);
17
+ const v = (0, lodash_1.omitBy)(expected, lodash_1.isNil);
19
18
  this._expect(this.response.data).toMatchObject(v);
20
19
  }
21
20
  catch (e) {
@@ -73,6 +73,7 @@ class ApiExpect {
73
73
  try {
74
74
  msg = '"body" is empty';
75
75
  expect(this.response.data).toBeDefined();
76
+ expect(typeof this.response.data).toStrictEqual('object');
76
77
  }
77
78
  catch (e) {
78
79
  if (msg)
@@ -1,32 +1,57 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpraTestClient = void 0;
4
- const tslib_1 = require("tslib");
5
- const axiosist = tslib_1.__importStar(require("axiosist"));
4
+ const http_1 = require("http");
5
+ const url_1 = require("url");
6
6
  const client_1 = require("@opra/client");
7
+ const common_1 = require("@opra/common");
7
8
  const api_expect_js_1 = require("./api-expect/api-expect.js");
8
- class OpraTestClient extends client_1.OpraClient {
9
- // @ts-ignore
10
- collection(name) {
11
- return super.collection(name);
9
+ const is_absolute_url_util_js_1 = require("./utils/is-absolute-url.util.js");
10
+ class OpraTestClient extends client_1.OpraHttpClient {
11
+ _server;
12
+ constructor(app, options) {
13
+ super('/', options);
14
+ this._server = app instanceof http_1.Server ? app : (0, http_1.createServer)(app);
12
15
  }
13
16
  // @ts-ignore
14
- singleton(name) {
15
- return super.singleton(name);
16
- }
17
- async _send(req, options) {
18
- const resp = (await super._send(req, options));
17
+ async _fetch(urlString, req) {
18
+ const resp = await new Promise((resolve, reject) => {
19
+ urlString = (0, is_absolute_url_util_js_1.isAbsoluteUrl)(urlString) ? urlString : (0, common_1.joinPath)('http://opra.test', urlString);
20
+ const url = new url_1.URL(urlString);
21
+ // Set protocol to HTTP
22
+ url.protocol = 'http';
23
+ // Apply original host to request header
24
+ const headers = req.headers = (req.headers || {});
25
+ if (url.host !== 'opra.test' && headers?.host == null)
26
+ headers.host = url.host;
27
+ new Promise((subResolve) => {
28
+ if (this._server.listening)
29
+ subResolve();
30
+ else
31
+ this._server.listen(0, '127.0.0.1', () => subResolve());
32
+ }).then(() => {
33
+ const address = this._server.address();
34
+ url.host = '127.0.0.1';
35
+ url.port = address.port.toString();
36
+ return super._fetch(url.toString(), req);
37
+ }).then(res => {
38
+ if (!this._server.listening)
39
+ return resolve(res);
40
+ this._server.close(() => resolve(res));
41
+ }).then()
42
+ .catch(error => {
43
+ if (!this._server.listening)
44
+ return reject(error);
45
+ this._server.close(() => reject(error));
46
+ });
47
+ });
19
48
  resp.expect = new api_expect_js_1.ApiExpect(resp);
20
49
  return resp;
21
50
  }
22
51
  static async create(app, options) {
23
- const instance = await super.create('/', {
24
- validateStatus: false,
25
- ...options,
26
- adapter: axiosist.createAdapter(app)
27
- });
28
- Object.setPrototypeOf(instance, OpraTestClient.prototype);
29
- return instance;
52
+ const client = new this(app, options);
53
+ await client.init();
54
+ return client;
30
55
  }
31
56
  }
32
57
  exports.OpraTestClient = OpraTestClient;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAbsoluteUrl = void 0;
4
+ const url_1 = require("url");
5
+ const isAbsoluteUrl = (urlString) => {
6
+ const url = new url_1.URL(urlString, 'http://opra.test/');
7
+ return url.host !== 'opra.test';
8
+ };
9
+ exports.isAbsoluteUrl = isAbsoluteUrl;
@@ -1,9 +1,9 @@
1
- import { OpraResponse } from '@opra/client';
2
- import { Expression } from '@opra/url';
1
+ import { HttpResponse } from '@opra/client';
2
+ import { Expression } from '@opra/common';
3
3
  export declare class ApiExpectCollection {
4
- readonly response: OpraResponse;
4
+ readonly response: HttpResponse;
5
5
  protected _isNot: boolean;
6
- constructor(response: OpraResponse, _isNot?: boolean);
6
+ constructor(response: HttpResponse, _isNot?: boolean);
7
7
  get not(): ApiExpectCollection;
8
8
  forEach(callbackfn: (v: any) => void): this;
9
9
  toMatch<T extends {}>(expected: T): this;
@@ -1,6 +1,6 @@
1
- import _ from 'lodash';
1
+ import { isNil, omitBy } from 'lodash';
2
2
  import ruleJudgment from 'rule-judgment';
3
- import { $parse, ArrayExpression, BooleanLiteral, ComparisonExpression, DateLiteral, LogicalExpression, NullLiteral, NumberLiteral, ParenthesesExpression, QualifiedIdentifier, StringLiteral, TimeLiteral } from '@opra/url';
3
+ import { ArrayExpression, BooleanLiteral, ComparisonExpression, DateLiteral, LogicalExpression, NullLiteral, NumberLiteral, ParenthesesExpression, parseFilter, QualifiedIdentifier, StringLiteral, TimeLiteral } from '@opra/common';
4
4
  export class ApiExpectCollection {
5
5
  response;
6
6
  _isNot;
@@ -17,7 +17,7 @@ export class ApiExpectCollection {
17
17
  }
18
18
  toMatch(expected) {
19
19
  try {
20
- const v = _.omitBy(expected, _.isNil);
20
+ const v = omitBy(expected, isNil);
21
21
  for (const item of this.response.data) {
22
22
  this._expect(item).toMatchObject(v);
23
23
  }
@@ -128,7 +128,7 @@ export class ApiExpectCollection {
128
128
  }
129
129
  }
130
130
  export function convertFilter(str) {
131
- const ast = typeof str === 'string' ? $parse(str) : str;
131
+ const ast = typeof str === 'string' ? parseFilter(str) : str;
132
132
  if (!ast)
133
133
  return;
134
134
  if (ast instanceof ComparisonExpression) {
@@ -1,8 +1,8 @@
1
- import { OpraResponse } from '@opra/client';
1
+ import { HttpResponse } from '@opra/client';
2
2
  import { ApiExpectObject } from './api-expect-object.js';
3
3
  export declare class ApiExpectError extends ApiExpectObject {
4
- readonly response: OpraResponse;
5
- constructor(response: OpraResponse);
4
+ readonly response: HttpResponse;
5
+ constructor(response: HttpResponse);
6
6
  toContainDetail(...matching: any[]): void;
7
7
  }
8
8
  declare global {
@@ -1,8 +1,8 @@
1
- import { OpraResponse } from '@opra/client';
1
+ import { HttpResponse } from '@opra/client';
2
2
  export declare class ApiExpectObject {
3
- readonly response: OpraResponse;
3
+ readonly response: HttpResponse;
4
4
  protected _isNot: boolean;
5
- constructor(response: OpraResponse, _isNot?: boolean);
5
+ constructor(response: HttpResponse, _isNot?: boolean);
6
6
  get not(): ApiExpectObject;
7
7
  toMatch<T extends {}>(expected: T): this;
8
8
  toHaveFields(fields: string[]): this;
@@ -1,4 +1,4 @@
1
- import _ from 'lodash';
1
+ import { isNil, omitBy } from 'lodash';
2
2
  export class ApiExpectObject {
3
3
  response;
4
4
  _isNot;
@@ -11,7 +11,7 @@ export class ApiExpectObject {
11
11
  }
12
12
  toMatch(expected) {
13
13
  try {
14
- const v = _.omitBy(expected, _.isNil);
14
+ const v = omitBy(expected, isNil);
15
15
  this._expect(this.response.data).toMatchObject(v);
16
16
  }
17
17
  catch (e) {
@@ -1,8 +1,8 @@
1
- import { OpraResponse } from '@opra/client';
1
+ import { HttpResponse } from '@opra/client';
2
2
  export declare class ApiExpectOperationResult {
3
- readonly response: OpraResponse;
3
+ readonly response: HttpResponse;
4
4
  protected _isNot: boolean;
5
- constructor(response: OpraResponse, _isNot?: boolean);
5
+ constructor(response: HttpResponse, _isNot?: boolean);
6
6
  get not(): ApiExpectOperationResult;
7
7
  toBeAffectedExact(expected: number): this;
8
8
  toBeAffectedMin(expected: number): this;
@@ -1,12 +1,12 @@
1
- import { OpraResponse } from '@opra/client';
1
+ import { HttpResponse } from '@opra/client';
2
2
  import { ApiExpectCollection } from './api-expect-collection.js';
3
3
  import { ApiExpectError } from './api-expect-error.js';
4
4
  import { ApiExpectObject } from './api-expect-object.js';
5
5
  import { ApiExpectOperationResult } from './api-expect-operation-result.js';
6
6
  export declare class ApiExpect {
7
- readonly response: OpraResponse;
7
+ readonly response: HttpResponse;
8
8
  protected _isNot: boolean;
9
- constructor(response: OpraResponse, _isNot?: boolean);
9
+ constructor(response: HttpResponse, _isNot?: boolean);
10
10
  get not(): ApiExpect;
11
11
  toSuccess(status?: number): this;
12
12
  toFail(status?: number): ApiExpectError;
@@ -70,6 +70,7 @@ export class ApiExpect {
70
70
  try {
71
71
  msg = '"body" is empty';
72
72
  expect(this.response.data).toBeDefined();
73
+ expect(typeof this.response.data).toStrictEqual('object');
73
74
  }
74
75
  catch (e) {
75
76
  if (msg)
@@ -1,18 +1,20 @@
1
1
  /// <reference types="node" />
2
- import { AxiosRequestConfig } from 'axios';
3
2
  import { IncomingMessage, Server, ServerResponse } from 'http';
4
- import { CollectionService, CommonRequestOptions, OpraClient, OpraClientOptions, OpraResponse } from '@opra/client';
5
- import { SingletonService } from '@opra/client/src/services/singleton-service';
3
+ import { Type } from 'ts-gems';
4
+ import { BatchRequest, HttpCollectionService, HttpRequest, HttpResponse, HttpSingletonService, OpraHttpClient, OpraHttpClientOptions } from '@opra/client';
6
5
  import { ApiExpect } from './api-expect/api-expect.js';
7
6
  declare type RequestListener = (req: IncomingMessage, res: ServerResponse) => void;
8
7
  declare type Handler = RequestListener | Server;
9
- declare type OpraTestResponse<T = any> = OpraResponse<T> & {
8
+ export declare type TestHttpResponse<T = any> = HttpResponse<T> & {
10
9
  expect: ApiExpect;
11
10
  };
12
- export declare class OpraTestClient extends OpraClient {
13
- collection<T = any, TResponse extends OpraTestResponse<T> = OpraTestResponse<T>>(name: string): CollectionService<T, TResponse>;
14
- singleton<T = any, TResponse extends OpraTestResponse<T> = OpraTestResponse<T>>(name: string): SingletonService<T, TResponse>;
15
- protected _send(req: AxiosRequestConfig, options: CommonRequestOptions): Promise<OpraTestResponse>;
16
- static create(app: Handler, options?: OpraClientOptions): Promise<OpraTestClient>;
11
+ export declare class OpraTestClient extends OpraHttpClient {
12
+ protected _server: Server;
13
+ constructor(app: Server | RequestListener, options?: OpraHttpClientOptions);
14
+ batch<TResponse extends TestHttpResponse = TestHttpResponse>(requests: HttpRequest[]): BatchRequest<TResponse>;
15
+ collection<T = any, TResponse extends TestHttpResponse<T> = TestHttpResponse<T>>(name: string): HttpCollectionService<T, TResponse>;
16
+ singleton<T = any, TResponse extends TestHttpResponse<T> = TestHttpResponse<T>>(name: string): HttpSingletonService<T, TResponse>;
17
+ protected _fetch<TResponse extends TestHttpResponse = TestHttpResponse>(urlString: string, req: RequestInit): Promise<TResponse>;
18
+ static create<T extends OpraTestClient>(this: Type<T>, app: Handler, options?: OpraHttpClientOptions): Promise<T>;
17
19
  }
18
20
  export {};
@@ -1,27 +1,53 @@
1
- import * as axiosist from 'axiosist';
2
- import { OpraClient } from '@opra/client';
1
+ import { createServer, Server } from 'http';
2
+ import { URL } from 'url';
3
+ import { OpraHttpClient, } from '@opra/client';
4
+ import { joinPath } from '@opra/common';
3
5
  import { ApiExpect } from './api-expect/api-expect.js';
4
- export class OpraTestClient extends OpraClient {
5
- // @ts-ignore
6
- collection(name) {
7
- return super.collection(name);
6
+ import { isAbsoluteUrl } from './utils/is-absolute-url.util.js';
7
+ export class OpraTestClient extends OpraHttpClient {
8
+ _server;
9
+ constructor(app, options) {
10
+ super('/', options);
11
+ this._server = app instanceof Server ? app : createServer(app);
8
12
  }
9
13
  // @ts-ignore
10
- singleton(name) {
11
- return super.singleton(name);
12
- }
13
- async _send(req, options) {
14
- const resp = (await super._send(req, options));
14
+ async _fetch(urlString, req) {
15
+ const resp = await new Promise((resolve, reject) => {
16
+ urlString = isAbsoluteUrl(urlString) ? urlString : joinPath('http://opra.test', urlString);
17
+ const url = new URL(urlString);
18
+ // Set protocol to HTTP
19
+ url.protocol = 'http';
20
+ // Apply original host to request header
21
+ const headers = req.headers = (req.headers || {});
22
+ if (url.host !== 'opra.test' && headers?.host == null)
23
+ headers.host = url.host;
24
+ new Promise((subResolve) => {
25
+ if (this._server.listening)
26
+ subResolve();
27
+ else
28
+ this._server.listen(0, '127.0.0.1', () => subResolve());
29
+ }).then(() => {
30
+ const address = this._server.address();
31
+ url.host = '127.0.0.1';
32
+ url.port = address.port.toString();
33
+ return super._fetch(url.toString(), req);
34
+ }).then(res => {
35
+ if (!this._server.listening)
36
+ return resolve(res);
37
+ this._server.close(() => resolve(res));
38
+ }).then()
39
+ .catch(error => {
40
+ if (!this._server.listening)
41
+ return reject(error);
42
+ this._server.close(() => reject(error));
43
+ });
44
+ });
15
45
  resp.expect = new ApiExpect(resp);
16
46
  return resp;
17
47
  }
18
48
  static async create(app, options) {
19
- const instance = await super.create('/', {
20
- validateStatus: false,
21
- ...options,
22
- adapter: axiosist.createAdapter(app)
23
- });
24
- Object.setPrototypeOf(instance, OpraTestClient.prototype);
25
- return instance;
49
+ const client = new this(app, options);
50
+ await client.init();
51
+ return client;
26
52
  }
27
53
  }
@@ -0,0 +1 @@
1
+ export declare const isAbsoluteUrl: (urlString: string) => boolean;
@@ -0,0 +1,5 @@
1
+ import { URL } from 'url';
2
+ export const isAbsoluteUrl = (urlString) => {
3
+ const url = new URL(urlString, 'http://opra.test/');
4
+ return url.host !== 'opra.test';
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/testing",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Opra testing package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -25,17 +25,18 @@
25
25
  "clean:cover": "rimraf ../../coverage/testing"
26
26
  },
27
27
  "dependencies": {
28
- "@opra/schema": "^0.4.0",
29
- "@opra/url": "^0.4.0",
30
- "@opra/client": "^0.4.0",
28
+ "@opra/client": "^0.6.0",
29
+ "@opra/common": "^0.6.0",
31
30
  "ansi-colors": "^4.1.3",
32
31
  "lodash": "^4.17.21",
33
- "rule-judgment": "^1.1.5",
34
- "supertest": "^6.3.0"
32
+ "rule-judgment": "^1.1.5"
35
33
  },
36
34
  "devDependencies": {
37
35
  "@types/supertest": "^2.0.12"
38
36
  },
37
+ "peerDependencies": {
38
+ "axios": "^1.2.1"
39
+ },
39
40
  "type": "module",
40
41
  "types": "esm/index.d.ts",
41
42
  "exports": {
package/cjs/constants.js DELETED
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expectSymbolTag = void 0;
4
- exports.expectSymbolTag = Symbol('opra.test.expect');
@@ -1 +0,0 @@
1
- export declare const expectSymbolTag: unique symbol;
package/esm/constants.js DELETED
@@ -1 +0,0 @@
1
- export const expectSymbolTag = Symbol('opra.test.expect');