diff --git a/packages/platform-ws/adapters/ws-adapter.ts b/packages/platform-ws/adapters/ws-adapter.ts index bfffdf8d144..d0791be91d8 100644 --- a/packages/platform-ws/adapters/ws-adapter.ts +++ b/packages/platform-ws/adapters/ws-adapter.ts @@ -1,6 +1,6 @@ import { INestApplicationContext, Logger } from '@nestjs/common'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isNil } from '@nestjs/common/utils/shared.utils'; +import { normalizePath, isNil } from '@nestjs/common/utils/shared.utils'; import { AbstractWsAdapter } from '@nestjs/websockets'; import { CLOSE_EVENT, @@ -49,9 +49,13 @@ export class WsAdapter extends AbstractWsAdapter { public create( port: number, - options?: Record & { namespace?: string; server?: any }, + options?: Record & { + namespace?: string; + server?: any; + path?: string; + }, ) { - const { server, ...wsOptions } = options; + const { server, path, ...wsOptions } = options; if (wsOptions?.namespace) { const error = new Error( '"WsAdapter" does not support namespaces. If you need namespaces in your project, consider using the "@nestjs/platform-socket.io" package instead.', @@ -69,14 +73,14 @@ export class WsAdapter extends AbstractWsAdapter { }), ); - this.addWsServerToRegistry(wsServer, port, options.path || '/'); + this.addWsServerToRegistry(wsServer, port, path); return wsServer; } if (server) { return server; } - if (options.path && port !== UNDERLYING_HTTP_SERVER_PORT) { + if (path && port !== UNDERLYING_HTTP_SERVER_PORT) { // Multiple servers with different paths // sharing a single HTTP/S server running on different port // than a regular HTTP application @@ -89,12 +93,13 @@ export class WsAdapter extends AbstractWsAdapter { ...wsOptions, }), ); - this.addWsServerToRegistry(wsServer, port, options.path); + this.addWsServerToRegistry(wsServer, port, path); return wsServer; } const wsServer = this.bindErrorHandler( new wsPackage.Server({ port, + path, ...wsOptions, }), ); @@ -202,7 +207,7 @@ export class WsAdapter extends AbstractWsAdapter { const entries = this.wsServersRegistry.get(port) ?? []; entries.push(wsServer); - wsServer.path = path; + wsServer.path = normalizePath(path); this.wsServersRegistry.set(port, entries); } } diff --git a/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts b/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts new file mode 100644 index 00000000000..26b9f42ed4b --- /dev/null +++ b/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts @@ -0,0 +1,53 @@ +import { INestApplication } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import { AppModule } from '../../src/app.module'; +import { io, Socket } from 'socket.io-client'; + +describe('EventsGateway', () => { + let app: INestApplication; + let socket: Socket; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleRef.createNestApplication(); + await app.listen(3000); + }); + + beforeEach(() => { + socket = io('http://localhost:3000'); + socket.connect(); + }); + + describe('findAll', () => { + it('should receive 3 numbers', done => { + let eventCount = 1; + socket.emit('events', { test: 'test' }); + socket.on('events', data => { + expect(data).toBe(eventCount); + if (++eventCount > 3) { + done(); + } + }); + }); + }); + + describe('identity', () => { + it('should return the same number has what was sent', done => { + socket.emit('identity', 0, response => { + expect(response).toBe(0); + done(); + }); + }); + }); + + afterEach(() => { + socket.disconnect(); + }); + + afterAll(async () => { + await app.close(); + }); +}); diff --git a/sample/02-gateways/e2e/jest-e2e.json b/sample/02-gateways/e2e/jest-e2e.json new file mode 100644 index 00000000000..72eb5c3c0a3 --- /dev/null +++ b/sample/02-gateways/e2e/jest-e2e.json @@ -0,0 +1,14 @@ +{ + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", + "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], + "coverageReporters": ["json", "lcov"] +} \ No newline at end of file diff --git a/sample/02-gateways/jest.json b/sample/02-gateways/jest.json new file mode 100644 index 00000000000..591de12b6ec --- /dev/null +++ b/sample/02-gateways/jest.json @@ -0,0 +1,14 @@ +{ + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$", + "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], + "coverageReporters": ["json", "lcov"] +} \ No newline at end of file diff --git a/sample/02-gateways/package-lock.json b/sample/02-gateways/package-lock.json index 70428dc1662..365bf5d6acb 100644 --- a/sample/02-gateways/package-lock.json +++ b/sample/02-gateways/package-lock.json @@ -27,6 +27,7 @@ "@nestjs/schematics": "9.0.1", "@nestjs/testing": "9.0.1", "@types/express": "4.17.13", + "@types/jest": "28.1.4", "@types/node": "18.0.3", "@types/supertest": "2.0.12", "@types/ws": "8.5.3", @@ -1947,6 +1948,16 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "28.1.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.4.tgz", + "integrity": "sha512-telv6G5N7zRJiLcI3Rs3o+ipZ28EnE+7EvF0pSrt2pZOMnAVI/f+6/LucDxOvcBcTeTL3JMF744BbVQAVBUQRA==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -10169,6 +10180,16 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "28.1.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.4.tgz", + "integrity": "sha512-telv6G5N7zRJiLcI3Rs3o+ipZ28EnE+7EvF0pSrt2pZOMnAVI/f+6/LucDxOvcBcTeTL3JMF744BbVQAVBUQRA==", + "dev": true, + "requires": { + "jest-matcher-utils": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -10585,9 +10606,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "requires": { - "ajv": "^8.0.0" - } + "requires": {} }, "ansi-colors": { "version": "4.1.1", diff --git a/sample/02-gateways/package.json b/sample/02-gateways/package.json index 9581df1d0f6..4e8c9c557eb 100644 --- a/sample/02-gateways/package.json +++ b/sample/02-gateways/package.json @@ -16,7 +16,7 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "test:e2e": "jest --config ./e2e/jest-e2e.json" }, "dependencies": { "@nestjs/common": "9.3.12", @@ -37,6 +37,7 @@ "@nestjs/schematics": "9.0.4", "@nestjs/testing": "9.3.12", "@types/express": "4.17.13", + "@types/jest": "28.1.4", "@types/node": "18.0.3", "@types/supertest": "2.0.12", "@types/ws": "8.5.3", @@ -54,5 +55,22 @@ "ts-node": "10.8.2", "tsconfig-paths": "4.0.0", "typescript": "4.9.5" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" } } diff --git a/sample/02-gateways/src/events/events.gateway.spec.ts b/sample/02-gateways/src/events/events.gateway.spec.ts new file mode 100644 index 00000000000..e40de3debf0 --- /dev/null +++ b/sample/02-gateways/src/events/events.gateway.spec.ts @@ -0,0 +1,40 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { reduce } from 'rxjs/operators'; +import { EventsGateway } from './events.gateway'; + +describe('EventsGateway', () => { + let gateway: EventsGateway; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [EventsGateway], + }).compile(); + + gateway = module.get(EventsGateway); + }); + + it('should be defined', () => { + expect(gateway).toBeDefined(); + }); + + describe('findAll', () => { + it('should return 3 numbers', done => { + gateway + .findAll({}) + .pipe(reduce((acc, item) => [...acc, item], [])) + .subscribe(results => { + expect(results.length).toBe(3); + results.forEach((result, index) => + expect(result.data).toBe(index + 1), + ); + done(); + }); + }); + }); + + describe('identity', () => { + it('should return the same number has what was sent', async () => { + await expect(gateway.identity(1)).resolves.toBe(1); + }); + }); +});