Skip to content

Commit 611a260

Browse files
committed
Add websockets e2e for nestjs
1 parent 88078a0 commit 611a260

12 files changed

Lines changed: 197 additions & 0 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "https://json.schemastore.org/nest-cli",
3+
"collection": "@nestjs/schematics",
4+
"sourceRoot": "src",
5+
"compilerOptions": {
6+
"deleteOutDir": true
7+
}
8+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "nestjs-websockets",
3+
"version": "0.0.1",
4+
"private": true,
5+
"scripts": {
6+
"build": "nest build",
7+
"start": "nest start",
8+
"test": "playwright test",
9+
"test:build": "pnpm install && pnpm build",
10+
"test:assert": "pnpm test"
11+
},
12+
"dependencies": {
13+
"@nestjs/common": "^10.0.0",
14+
"@nestjs/core": "^10.0.0",
15+
"@nestjs/platform-express": "^10.0.0",
16+
"@nestjs/websockets": "^10.0.0",
17+
"@nestjs/platform-socket.io": "^10.0.0",
18+
"@sentry/nestjs": "latest || *",
19+
"reflect-metadata": "^0.2.0",
20+
"rxjs": "^7.8.1"
21+
},
22+
"devDependencies": {
23+
"@playwright/test": "~1.56.0",
24+
"@sentry-internal/test-utils": "link:../../../test-utils",
25+
"@nestjs/cli": "^10.0.0",
26+
"@types/node": "^18.19.1",
27+
"socket.io-client": "^4.0.0",
28+
"typescript": "~5.0.0"
29+
},
30+
"volta": {
31+
"extends": "../../package.json"
32+
}
33+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const config = getPlaywrightConfig({
4+
startCommand: `pnpm start`,
5+
});
6+
7+
export default config;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Controller, Get } from '@nestjs/common';
2+
import { flush } from '@sentry/nestjs';
3+
4+
@Controller()
5+
export class AppController {
6+
@Get('/flush')
7+
async flush() {
8+
await flush();
9+
return 'ok';
10+
}
11+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { SubscribeMessage, WebSocketGateway, MessageBody } from '@nestjs/websockets';
2+
import * as Sentry from '@sentry/nestjs';
3+
4+
@WebSocketGateway({ cors: true })
5+
export class AppGateway {
6+
@SubscribeMessage('test-message')
7+
handleTestMessage(@MessageBody() data: { message: string }) {
8+
return { event: 'test-response', data: { message: data.message } };
9+
}
10+
11+
@SubscribeMessage('test-exception')
12+
handleTestException() {
13+
throw new Error('This is an exception in a WebSocket handler');
14+
}
15+
16+
@SubscribeMessage('test-manual-capture')
17+
handleManualCapture() {
18+
try {
19+
throw new Error('Manually captured WebSocket error');
20+
} catch (e) {
21+
Sentry.captureException(e);
22+
}
23+
return { event: 'capture-response', data: { success: true } };
24+
}
25+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Module } from '@nestjs/common';
2+
import { APP_FILTER } from '@nestjs/core';
3+
import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
4+
import { AppController } from './app.controller';
5+
import { AppGateway } from './app.gateway';
6+
7+
@Module({
8+
imports: [SentryModule.forRoot()],
9+
controllers: [AppController],
10+
providers: [
11+
{
12+
provide: APP_FILTER,
13+
useClass: SentryGlobalFilter,
14+
},
15+
AppGateway,
16+
],
17+
})
18+
export class AppModule {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/nestjs';
2+
3+
Sentry.init({
4+
environment: 'qa',
5+
dsn: process.env.E2E_TEST_DSN,
6+
tunnel: `http://localhost:3031/`,
7+
tracesSampleRate: 1,
8+
transportOptions: {
9+
bufferSize: 1000,
10+
},
11+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Import this first
2+
import './instrument';
3+
4+
// Import other modules
5+
import { NestFactory } from '@nestjs/core';
6+
import { AppModule } from './app.module';
7+
8+
const PORT = 3030;
9+
10+
async function bootstrap() {
11+
const app = await NestFactory.create(AppModule);
12+
await app.listen(PORT);
13+
}
14+
15+
bootstrap();
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { startEventProxyServer } from '@sentry-internal/test-utils';
2+
3+
startEventProxyServer({
4+
port: 3031,
5+
proxyServerName: 'nestjs-websockets',
6+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError } from '@sentry-internal/test-utils';
3+
import { io } from 'socket.io-client';
4+
5+
test('Captures manually reported error in WebSocket gateway handler', async ({ baseURL }) => {
6+
const errorPromise = waitForError('nestjs-websockets', event => {
7+
return event.exception?.values?.[0]?.value === 'Manually captured WebSocket error';
8+
});
9+
10+
const socket = io(baseURL!);
11+
await new Promise<void>(resolve => socket.on('connect', resolve));
12+
13+
socket.emit('test-manual-capture', {});
14+
15+
const error = await errorPromise;
16+
17+
expect(error.exception?.values?.[0]).toMatchObject({
18+
type: 'Error',
19+
value: 'Manually captured WebSocket error',
20+
});
21+
22+
socket.disconnect();
23+
});

0 commit comments

Comments
 (0)