@tsed/platform-http 8.0.0 → 8.0.2
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/.barrelsby.json +1 -1
- package/lib/esm/common/index.js +0 -1
- package/lib/esm/common/index.js.map +1 -1
- package/lib/types/common/fn/adapter.d.ts +1 -1
- package/lib/types/common/index.d.ts +0 -1
- package/package.json +19 -17
- package/src/common/builder/PlatformBuilder.spec.ts +331 -0
- package/src/common/builder/PlatformBuilder.ts +426 -0
- package/src/common/builder/__fixtures__/PlatformCustom.ts +39 -0
- package/src/common/config/interfaces/PlatformJsonMapperSettings.ts +11 -0
- package/src/common/config/interfaces/PlatformMulterSettings.ts +21 -0
- package/src/common/config/interfaces/PlatformRouterSettings.ts +11 -0
- package/src/common/config/interfaces/PlatformStaticsSettings.ts +23 -0
- package/src/common/config/services/PlatformConfiguration.spec.ts +181 -0
- package/src/common/config/services/PlatformConfiguration.ts +157 -0
- package/src/common/constants/routerOptions.ts +1 -0
- package/src/common/decorators/PlatformProvider.ts +14 -0
- package/src/common/decorators/multer/multerFileSize.spec.ts +22 -0
- package/src/common/decorators/multer/multerFileSize.ts +32 -0
- package/src/common/decorators/multer/multerOptions.spec.ts +21 -0
- package/src/common/decorators/multer/multerOptions.ts +53 -0
- package/src/common/decorators/multer/multipartFile.spec.ts +259 -0
- package/src/common/decorators/multer/multipartFile.ts +88 -0
- package/src/common/decorators/params/error.spec.ts +15 -0
- package/src/common/decorators/params/error.ts +16 -0
- package/src/common/decorators/params/next.spec.ts +15 -0
- package/src/common/decorators/params/next.ts +31 -0
- package/src/common/decorators/params/request.spec.ts +49 -0
- package/src/common/decorators/params/request.ts +85 -0
- package/src/common/decorators/params/response.spec.ts +36 -0
- package/src/common/decorators/params/response.ts +73 -0
- package/src/common/domain/AnyToPromiseWithCtx.spec.ts +20 -0
- package/src/common/domain/AnyToPromiseWithCtx.ts +30 -0
- package/src/common/domain/EndpointMetadata.ts +1 -0
- package/src/common/domain/PlatformContext.spec.ts +124 -0
- package/src/common/domain/PlatformContext.ts +135 -0
- package/src/common/domain/PlatformRouteDetails.ts +7 -0
- package/src/common/exports.ts +4 -0
- package/src/common/fn/adapter.ts +23 -0
- package/src/common/fn/application.ts +11 -0
- package/src/common/index.ts +62 -0
- package/src/common/interfaces/AfterInit.ts +3 -0
- package/src/common/interfaces/AfterListen.ts +3 -0
- package/src/common/interfaces/AfterRoutesInit.ts +3 -0
- package/src/common/interfaces/BeforeInit.ts +3 -0
- package/src/common/interfaces/BeforeListen.ts +3 -0
- package/src/common/interfaces/BeforeRoutesInit.ts +3 -0
- package/src/common/interfaces/HandlerType.ts +12 -0
- package/src/common/interfaces/IncomingEvent.ts +6 -0
- package/src/common/interfaces/OnReady.ts +3 -0
- package/src/common/interfaces/OnRequest.ts +5 -0
- package/src/common/interfaces/OnResponse.ts +5 -0
- package/src/common/interfaces/OnRoutesInit.ts +3 -0
- package/src/common/interfaces/PlatformRouteOptions.ts +17 -0
- package/src/common/interfaces/ResponseErrorObject.ts +8 -0
- package/src/common/interfaces/Route.ts +12 -0
- package/src/common/middlewares/PlatformAcceptMimesMiddleware.spec.ts +215 -0
- package/src/common/middlewares/PlatformAcceptMimesMiddleware.ts +24 -0
- package/src/common/middlewares/PlatformMulterMiddleware.spec.ts +97 -0
- package/src/common/middlewares/PlatformMulterMiddleware.ts +59 -0
- package/src/common/services/Platform.spec.ts +145 -0
- package/src/common/services/Platform.ts +79 -0
- package/src/common/services/PlatformAdapter.ts +89 -0
- package/src/common/services/PlatformApplication.spec.ts +101 -0
- package/src/common/services/PlatformApplication.ts +54 -0
- package/src/common/services/PlatformHandler.spec.ts +152 -0
- package/src/common/services/PlatformHandler.ts +140 -0
- package/src/common/services/PlatformMiddlewaresChain.spec.ts +96 -0
- package/src/common/services/PlatformMiddlewaresChain.ts +55 -0
- package/src/common/services/PlatformRequest.spec.ts +185 -0
- package/src/common/services/PlatformRequest.ts +181 -0
- package/src/common/services/PlatformResponse.spec.ts +344 -0
- package/src/common/services/PlatformResponse.ts +388 -0
- package/src/common/utils/closeServer.spec.ts +11 -0
- package/src/common/utils/closeServer.ts +7 -0
- package/src/common/utils/createContext.spec.ts +111 -0
- package/src/common/utils/createContext.ts +52 -0
- package/src/common/utils/createHttpServer.spec.ts +74 -0
- package/src/common/utils/createHttpServer.ts +15 -0
- package/src/common/utils/createHttpsServer.spec.ts +73 -0
- package/src/common/utils/createHttpsServer.ts +16 -0
- package/src/common/utils/createInjector.spec.ts +42 -0
- package/src/common/utils/createInjector.ts +55 -0
- package/src/common/utils/createServer.ts +51 -0
- package/src/common/utils/ensureContext.spec.ts +41 -0
- package/src/common/utils/ensureContext.ts +17 -0
- package/src/common/utils/getConfiguration.spec.ts +163 -0
- package/src/common/utils/getConfiguration.ts +34 -0
- package/src/common/utils/getStaticsOptions.spec.ts +27 -0
- package/src/common/utils/getStaticsOptions.ts +24 -0
- package/src/common/utils/listenServer.ts +17 -0
- package/src/common/utils/mapReturnedResponse.spec.ts +35 -0
- package/src/common/utils/mapReturnedResponse.ts +10 -0
- package/src/common/utils/printRoutes.spec.ts +19 -0
- package/src/common/utils/printRoutes.ts +40 -0
- package/src/common/utils/resolveControllers.spec.ts +40 -0
- package/src/common/utils/resolveControllers.ts +54 -0
- package/src/common/utils/setResponseHeaders.spec.ts +84 -0
- package/src/common/utils/setResponseHeaders.ts +31 -0
- package/src/index.ts +2 -0
- package/src/testing/FakeAdapter.ts +69 -0
- package/src/testing/FakeResponse.ts +72 -0
- package/src/testing/PlatformTest.spec.ts +53 -0
- package/src/testing/PlatformTest.ts +157 -0
- package/src/testing/index.ts +5 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {nameOf} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {Controller} from "../../../../../di/src/common/decorators/controller.js";
|
|
4
|
+
import {M1Ctrl1} from "../../../../../di/src/common/utils/__mock__/module1/controllers/M1Ctrl1.js";
|
|
5
|
+
import {Module1} from "../../../../../di/src/common/utils/__mock__/module1/Module1.js";
|
|
6
|
+
import {M2Ctrl} from "../../../../../di/src/common/utils/__mock__/module2/controllers/M2Ctrl.js";
|
|
7
|
+
import {Module2} from "../../../../../di/src/common/utils/__mock__/module2/Module2.js";
|
|
8
|
+
import {resolveControllers} from "./resolveControllers.js";
|
|
9
|
+
|
|
10
|
+
@Controller("/root")
|
|
11
|
+
class TestCtrl {}
|
|
12
|
+
|
|
13
|
+
describe("resolveControllers", () => {
|
|
14
|
+
it("should load providers and merge configuration", () => {
|
|
15
|
+
const configuration = {
|
|
16
|
+
mount: {
|
|
17
|
+
"/rest": [TestCtrl]
|
|
18
|
+
},
|
|
19
|
+
imports: [Module1, Module2]
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const result = resolveControllers(configuration);
|
|
23
|
+
const routes = result.map((item) => ({...item, token: nameOf(item.token)}));
|
|
24
|
+
|
|
25
|
+
expect(routes).toEqual([
|
|
26
|
+
{
|
|
27
|
+
route: "/m1",
|
|
28
|
+
token: "M1Ctrl1"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
route: "/mod2",
|
|
32
|
+
token: "M2Ctrl"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
route: "/rest",
|
|
36
|
+
token: "TestCtrl"
|
|
37
|
+
}
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {isArray, isClass} from "@tsed/core";
|
|
2
|
+
import {GlobalProviders, Provider, ProviderType, type TokenProvider, type TokenRoute} from "@tsed/di";
|
|
3
|
+
|
|
4
|
+
const lookupProperties = ["mount", "imports"];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
function getTokens(config: any): {route?: string; token: TokenProvider}[] {
|
|
10
|
+
if (!config) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (isArray(config)) {
|
|
15
|
+
return config.filter(isClass).map((value: any) => {
|
|
16
|
+
return {token: value};
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return Object.keys(config).reduce((list: any[], route: string) => {
|
|
21
|
+
return [
|
|
22
|
+
...list,
|
|
23
|
+
...[]
|
|
24
|
+
.concat(config[route])
|
|
25
|
+
.filter(isClass)
|
|
26
|
+
.map((token) => ({
|
|
27
|
+
route,
|
|
28
|
+
token
|
|
29
|
+
}))
|
|
30
|
+
];
|
|
31
|
+
}, []);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @ignore
|
|
36
|
+
*/
|
|
37
|
+
function resolveRecursively(providers: {token: TokenProvider; route?: string}[]) {
|
|
38
|
+
return providers
|
|
39
|
+
.map(({token}) => GlobalProviders.get(token))
|
|
40
|
+
.filter((provider) => provider?.type === ProviderType.MODULE && provider.configuration)
|
|
41
|
+
.flatMap((provider: Provider) => resolveControllers(provider.configuration));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Return controllers and is base route according to his configuration in module configuration.
|
|
46
|
+
*
|
|
47
|
+
* @param settings
|
|
48
|
+
* @ignore
|
|
49
|
+
*/
|
|
50
|
+
export function resolveControllers(settings: Partial<TsED.Configuration>): TokenRoute[] {
|
|
51
|
+
const providers = lookupProperties.flatMap((property) => getTokens(settings[property]));
|
|
52
|
+
|
|
53
|
+
return [...resolveRecursively(providers), ...providers].filter((provider) => !!provider.route) as any[];
|
|
54
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {EndpointMetadata, Get, Redirect, Returns} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {PlatformTest} from "../../testing/PlatformTest.js";
|
|
4
|
+
import {setResponseHeaders} from "./setResponseHeaders.js";
|
|
5
|
+
|
|
6
|
+
describe("setResponseHeaders", () => {
|
|
7
|
+
beforeEach(() => PlatformTest.create());
|
|
8
|
+
afterEach(() => PlatformTest.reset());
|
|
9
|
+
|
|
10
|
+
it("should set headers, status and contentType", async () => {
|
|
11
|
+
class Test {
|
|
12
|
+
@Get("/")
|
|
13
|
+
@(Returns(200).Header("x-header", "test"))
|
|
14
|
+
test() {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ctx = PlatformTest.createRequestContext();
|
|
18
|
+
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
19
|
+
|
|
20
|
+
// WHEN
|
|
21
|
+
await setResponseHeaders(ctx);
|
|
22
|
+
|
|
23
|
+
// THEN
|
|
24
|
+
expect(ctx.response.getHeaders()).toEqual({"x-request-id": "id", "x-header": "test"});
|
|
25
|
+
expect(ctx.response.statusCode).toEqual(200);
|
|
26
|
+
});
|
|
27
|
+
it("should not set headers automatically if the value isn't declared", async () => {
|
|
28
|
+
class Test {
|
|
29
|
+
@Get("/")
|
|
30
|
+
@(Returns(200).Header("x-header"))
|
|
31
|
+
test() {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ctx = PlatformTest.createRequestContext();
|
|
35
|
+
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
36
|
+
|
|
37
|
+
// WHEN
|
|
38
|
+
await setResponseHeaders(ctx);
|
|
39
|
+
|
|
40
|
+
// THEN
|
|
41
|
+
expect(ctx.response.getHeaders()).toEqual({"x-request-id": "id"});
|
|
42
|
+
expect(ctx.response.statusCode).toEqual(200);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should redirect", async () => {
|
|
46
|
+
class Test {
|
|
47
|
+
@Get("/")
|
|
48
|
+
@Redirect(301, "/path")
|
|
49
|
+
test() {}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ctx = PlatformTest.createRequestContext();
|
|
53
|
+
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
54
|
+
|
|
55
|
+
vi.spyOn(ctx.response, "redirect").mockReturnValue(undefined as any);
|
|
56
|
+
|
|
57
|
+
// WHEN
|
|
58
|
+
await setResponseHeaders(ctx);
|
|
59
|
+
|
|
60
|
+
// THEN
|
|
61
|
+
expect(ctx.response.redirect).toHaveBeenCalledWith(301, "/path");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should do nothing when headers is already sent", async () => {
|
|
65
|
+
class Test {
|
|
66
|
+
@Get("/")
|
|
67
|
+
@(Returns(200).Header("x-header", "test"))
|
|
68
|
+
test() {}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ctx = PlatformTest.createRequestContext();
|
|
72
|
+
ctx.response.raw.headersSent = true;
|
|
73
|
+
|
|
74
|
+
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
75
|
+
|
|
76
|
+
vi.spyOn(ctx.response.raw, "set");
|
|
77
|
+
|
|
78
|
+
// WHEN
|
|
79
|
+
await setResponseHeaders(ctx);
|
|
80
|
+
|
|
81
|
+
// THEN
|
|
82
|
+
return expect(ctx.response.raw.set).not.toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {PlatformContext} from "../domain/PlatformContext.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @ignore
|
|
5
|
+
*/
|
|
6
|
+
export function setResponseHeaders(ctx: PlatformContext) {
|
|
7
|
+
const {response, endpoint} = ctx;
|
|
8
|
+
const {operation} = endpoint;
|
|
9
|
+
|
|
10
|
+
if (ctx.isDone()) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!response.hasStatus()) {
|
|
15
|
+
// apply status only if the isn't already modified
|
|
16
|
+
response.status(operation.getStatus());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const statusCode = response.statusCode;
|
|
20
|
+
const headers = operation.getHeadersOf(statusCode);
|
|
21
|
+
|
|
22
|
+
Object.entries(headers).forEach(([key, item]: any[]) => {
|
|
23
|
+
if (!response.get(key) && item.example !== undefined) {
|
|
24
|
+
response.setHeader(key, String(item.example));
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (operation.isRedirection(statusCode)) {
|
|
29
|
+
response.redirect(statusCode, response.get("location"));
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {injectable, type ProviderOpts} from "@tsed/di";
|
|
2
|
+
import {type PlatformContextHandler, PlatformLayer} from "@tsed/platform-router";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
PlatformAdapter,
|
|
6
|
+
PlatformContext,
|
|
7
|
+
type PlatformMulter,
|
|
8
|
+
type PlatformMulterSettings,
|
|
9
|
+
type PlatformStaticsOptions
|
|
10
|
+
} from "../common/index.js";
|
|
11
|
+
|
|
12
|
+
export class FakeAdapter extends PlatformAdapter<any> {
|
|
13
|
+
readonly NAME: string = "FAKE_ADAPTER";
|
|
14
|
+
providers: ProviderOpts[] = [];
|
|
15
|
+
|
|
16
|
+
static createFakeRawDriver() {
|
|
17
|
+
// istanbul ignore next
|
|
18
|
+
function FakeRawDriver() {}
|
|
19
|
+
|
|
20
|
+
// istanbul ignore next
|
|
21
|
+
function use() {
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
FakeRawDriver.use = use;
|
|
26
|
+
FakeRawDriver.all = use;
|
|
27
|
+
FakeRawDriver.get = use;
|
|
28
|
+
FakeRawDriver.patch = use;
|
|
29
|
+
FakeRawDriver.post = use;
|
|
30
|
+
FakeRawDriver.put = use;
|
|
31
|
+
FakeRawDriver.head = use;
|
|
32
|
+
FakeRawDriver.delete = use;
|
|
33
|
+
FakeRawDriver.options = use;
|
|
34
|
+
|
|
35
|
+
return FakeRawDriver;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
createApp(): {app: any; callback(): any} {
|
|
39
|
+
const app = FakeAdapter.createFakeRawDriver();
|
|
40
|
+
return {
|
|
41
|
+
app,
|
|
42
|
+
callback() {
|
|
43
|
+
return app;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
multipart(options: PlatformMulterSettings): PlatformMulter {
|
|
49
|
+
return {} as any;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
statics(endpoint: string, options: PlatformStaticsOptions): any {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
bodyParser(type: string): any {
|
|
57
|
+
return () => {};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
mapLayers(layers: PlatformLayer[]) {}
|
|
61
|
+
|
|
62
|
+
mapHandler(handler: PlatformContextHandler<PlatformContext>) {
|
|
63
|
+
return handler;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
useContext() {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
injectable(PlatformAdapter).class(FakeAdapter);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {EventEmitter} from "node:events";
|
|
2
|
+
|
|
3
|
+
export class FakeResponse extends EventEmitter {
|
|
4
|
+
headers: Record<string, unknown> = {};
|
|
5
|
+
locals: Record<string, unknown> = {};
|
|
6
|
+
statusCode: number = 200;
|
|
7
|
+
data: any;
|
|
8
|
+
|
|
9
|
+
constructor(opts = {}) {
|
|
10
|
+
super();
|
|
11
|
+
|
|
12
|
+
Object.assign(this, opts);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
status(code: number) {
|
|
16
|
+
this.statusCode = code;
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
contentType(content: string) {
|
|
21
|
+
this.set("content-type", content);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
contentLength(content: number) {
|
|
25
|
+
this.set("content-length", content);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
redirect(status: number, path: string) {
|
|
29
|
+
this.statusCode = status;
|
|
30
|
+
this.set("location", path);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
location(path: string) {
|
|
34
|
+
this.set("location", path);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get(key: string) {
|
|
38
|
+
return this.headers[key.toLowerCase()];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getHeaders() {
|
|
42
|
+
return this.headers;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
set(key: string, value: any) {
|
|
46
|
+
this.headers[key.toLowerCase()] = value;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setHeader(key: string, value: any) {
|
|
51
|
+
this.headers[key.toLowerCase()] = value;
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
send(data: any) {
|
|
56
|
+
this.data = data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
json(data: any) {
|
|
60
|
+
this.data = data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
write(chunk: any) {
|
|
64
|
+
this.emit("data", chunk);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
end(data: any) {
|
|
68
|
+
data !== undefined && (this.data = data);
|
|
69
|
+
|
|
70
|
+
this.emit("end");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {Configuration, Controller, InjectorService} from "@tsed/di";
|
|
2
|
+
import {Get} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {PlatformContext} from "../common/domain/PlatformContext.js";
|
|
5
|
+
import {PlatformTest} from "./PlatformTest.js";
|
|
6
|
+
|
|
7
|
+
@Configuration({})
|
|
8
|
+
class Server {}
|
|
9
|
+
|
|
10
|
+
@Controller("/")
|
|
11
|
+
class MyController {
|
|
12
|
+
@Get("/")
|
|
13
|
+
get() {
|
|
14
|
+
return "hello";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("PlatformTest", () => {
|
|
19
|
+
let testContext: any;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
testContext = {};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
beforeEach(PlatformTest.create);
|
|
26
|
+
afterEach(PlatformTest.reset);
|
|
27
|
+
|
|
28
|
+
it("should get symbol from injector", () => {
|
|
29
|
+
expect(PlatformTest.get(InjectorService)).toBeInstanceOf(InjectorService);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("createRequestContext", () => {
|
|
33
|
+
it("should return request context", () => {
|
|
34
|
+
expect(PlatformTest.createRequestContext()).toBeInstanceOf(PlatformContext);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("invoke", () => {
|
|
39
|
+
it("should return request context", async () => {
|
|
40
|
+
class Test {
|
|
41
|
+
called: boolean;
|
|
42
|
+
|
|
43
|
+
$onInit() {
|
|
44
|
+
this.called = true;
|
|
45
|
+
|
|
46
|
+
return Promise.resolve();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
expect(await PlatformTest.invoke(Test)).toEqual({called: true});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {DITest, injector, InjectorService} from "@tsed/di";
|
|
2
|
+
import accepts from "accepts";
|
|
3
|
+
import type {IncomingMessage, RequestListener, ServerResponse} from "http";
|
|
4
|
+
|
|
5
|
+
import {PlatformBuilder} from "../common/builder/PlatformBuilder.js";
|
|
6
|
+
import {PlatformContext, PlatformContextOptions} from "../common/domain/PlatformContext.js";
|
|
7
|
+
import {PlatformAdapter, PlatformBuilderSettings} from "../common/services/PlatformAdapter.js";
|
|
8
|
+
import {PlatformApplication} from "../common/services/PlatformApplication.js";
|
|
9
|
+
import {createInjector} from "../common/utils/createInjector.js";
|
|
10
|
+
import {getConfiguration} from "../common/utils/getConfiguration.js";
|
|
11
|
+
import {FakeAdapter} from "./FakeAdapter.js";
|
|
12
|
+
import {FakeResponse} from "./FakeResponse.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @platform
|
|
16
|
+
*/
|
|
17
|
+
export class PlatformTest extends DITest {
|
|
18
|
+
static async create(settings: Partial<TsED.Configuration> = {}) {
|
|
19
|
+
settings.imports = [
|
|
20
|
+
{
|
|
21
|
+
token: PlatformAdapter,
|
|
22
|
+
useClass: FakeAdapter
|
|
23
|
+
},
|
|
24
|
+
...(settings.imports || [])
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
PlatformTest.createInjector(getConfiguration(settings));
|
|
28
|
+
await DITest.createContainer();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a new injector with the right default services
|
|
33
|
+
*/
|
|
34
|
+
static createInjector(settings: any = {}): InjectorService {
|
|
35
|
+
return createInjector(DITest.configure({httpPort: false, httpsPort: false, ...settings}));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Load the server silently without listening port and configure it on test profile.
|
|
40
|
+
* @decorator
|
|
41
|
+
* @param mod
|
|
42
|
+
* @param listen
|
|
43
|
+
* @param settings
|
|
44
|
+
* @returns {Promise<void>}
|
|
45
|
+
*/
|
|
46
|
+
static bootstrap(
|
|
47
|
+
mod: any,
|
|
48
|
+
{
|
|
49
|
+
listen,
|
|
50
|
+
...settings
|
|
51
|
+
}: Partial<
|
|
52
|
+
PlatformBuilderSettings & {
|
|
53
|
+
listen: boolean;
|
|
54
|
+
}
|
|
55
|
+
> = {}
|
|
56
|
+
): () => Promise<void> {
|
|
57
|
+
return async function before(): Promise<void> {
|
|
58
|
+
let instance: PlatformBuilder;
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
settings = DITest.configure(settings);
|
|
61
|
+
|
|
62
|
+
const configuration = getConfiguration(settings, mod);
|
|
63
|
+
|
|
64
|
+
instance = await PlatformBuilder.build(mod, configuration).bootstrap();
|
|
65
|
+
|
|
66
|
+
await instance.listen(!!listen);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* It injects services into the test function where you can alter, spy on, and manipulate them.
|
|
72
|
+
*
|
|
73
|
+
* The inject function has two parameters
|
|
74
|
+
*
|
|
75
|
+
* * an array of Service dependency injection tokens,
|
|
76
|
+
* * a test function whose parameters correspond exactly to each item in the injection token array.
|
|
77
|
+
*
|
|
78
|
+
* @deprecated use PlatformTest.injector.invoke instead
|
|
79
|
+
* @param targets
|
|
80
|
+
* @param func
|
|
81
|
+
*/
|
|
82
|
+
static inject<T>(targets: any[], func: (...args: any[]) => Promise<T> | T): () => Promise<T> {
|
|
83
|
+
return async (): Promise<T> => {
|
|
84
|
+
await PlatformTest.create();
|
|
85
|
+
|
|
86
|
+
const inj: InjectorService = injector();
|
|
87
|
+
const deps = [];
|
|
88
|
+
|
|
89
|
+
for (const target of targets) {
|
|
90
|
+
deps.push(inj.has(target) ? inj.get(target) : await inj.invoke(target));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return func(...deps);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Return the raw application (express or koa).
|
|
99
|
+
* Use this callback with SuperTest.
|
|
100
|
+
*
|
|
101
|
+
* ```typescript
|
|
102
|
+
* let request: SuperTest.Agent;
|
|
103
|
+
* beforeEach(PlatformTest.bootstrap(Server, {
|
|
104
|
+
* mount: {
|
|
105
|
+
* "/rest": [ProductsController]
|
|
106
|
+
* }
|
|
107
|
+
* }));
|
|
108
|
+
* beforeEach(() => {
|
|
109
|
+
* request = SuperTest(PlatformTest.callback());
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
static callback(): RequestListener<typeof IncomingMessage, typeof ServerResponse> {
|
|
114
|
+
return injector().get<PlatformApplication>(PlatformApplication)?.callback() as any;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static createRequest(options: any = {}): any {
|
|
118
|
+
return {
|
|
119
|
+
headers: {},
|
|
120
|
+
method: "GET",
|
|
121
|
+
url: "/",
|
|
122
|
+
query: {},
|
|
123
|
+
get(key: string) {
|
|
124
|
+
return this.headers[key.toLowerCase()];
|
|
125
|
+
},
|
|
126
|
+
accepts(mime?: string | string[]) {
|
|
127
|
+
return accepts(this).types([].concat(mime as never));
|
|
128
|
+
},
|
|
129
|
+
...options
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static createResponse(options: any = {}): any {
|
|
134
|
+
return new FakeResponse(options);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static createRequestContext(options: Partial<PlatformContextOptions & any> = {}) {
|
|
138
|
+
const event = {
|
|
139
|
+
...options.event,
|
|
140
|
+
request: options?.request?.request || options?.event?.request || PlatformTest.createRequest(),
|
|
141
|
+
response: options?.response?.response || options?.event?.response || PlatformTest.createResponse()
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const $ctx = new PlatformContext({
|
|
145
|
+
id: "id",
|
|
146
|
+
url: "/",
|
|
147
|
+
...options,
|
|
148
|
+
event
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (options.endpoint) {
|
|
152
|
+
$ctx.endpoint = options.endpoint;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return $ctx;
|
|
156
|
+
}
|
|
157
|
+
}
|