1+
12/// Tests for dart_node_ws library types and APIs.
23///
34/// These tests run in Node.js environment to get coverage for the library.
45@TestOn ('node' )
56library ;
67
8+ import 'dart:async' ;
9+ import 'dart:js_interop' ;
10+ import 'dart:js_interop_unsafe' ;
11+
12+ import 'package:dart_node_core/dart_node_core.dart' ;
713import 'package:dart_node_coverage/dart_node_coverage.dart' ;
814import 'package:dart_node_ws/dart_node_ws.dart' ;
915import 'package:test/test.dart' ;
1016
11- //TODO: we need actual web socket server/client interaction tests here.
12-
1317void main () {
1418 setUp (initCoverage);
1519 tearDownAll (() => writeCoverageFile ('coverage/coverage.json' ));
@@ -79,14 +83,12 @@ void main() {
7983 test ('onConnection registers handler' , () {
8084 createWebSocketServer (port: 9994 )
8185 ..onConnection ((client, url) {
82- // Handler registered - just verify it doesn't throw
86+ // Handler registered
8387 })
8488 ..close ();
8589 });
8690
8791 test ('onConnection receives client on connection' , () {
88- // The connection test happens in websocket_test.dart (integration tests)
89- // Here we just verify the API works without throwing
9092 createWebSocketServer (port: 9993 )
9193 ..onConnection ((client, url) {
9294 expect (client, isNotNull);
@@ -95,4 +97,125 @@ void main() {
9597 ..close ();
9698 });
9799 });
100+
101+ group ('WebSocket Integration Tests' , () {
102+ late WebSocketServer server;
103+ const testPort = 3456 ;
104+
105+ setUp (() async {
106+ server = createWebSocketServer (port: testPort);
107+ });
108+
109+ tearDown (() async {
110+ server.close ();
111+ await Future <void >.delayed (const Duration (milliseconds: 50 ));
112+ });
113+
114+ test ('client can connect to server' , () async {
115+ final completer = Completer <void >();
116+
117+ server.onConnection ((client, url) {
118+ completer.complete ();
119+ });
120+
121+ final client = _createWebSocketClient ('ws://localhost:$testPort ' );
122+
123+ await completer.future.timeout (const Duration (seconds: 2 ));
124+ await _waitForOpen (client);
125+ client.close ();
126+ });
127+
128+ test ('server receives messages from client' , () async {
129+ final messageCompleter = Completer <String >();
130+
131+ server.onConnection ((serverClient, url) {
132+ serverClient.onMessage ((message) {
133+ messageCompleter.complete (message.text ?? '' );
134+ });
135+ });
136+
137+ final client = _createWebSocketClient ('ws://localhost:$testPort ' );
138+
139+ await _waitForOpen (client);
140+ _sendMessage (client, 'Hello from client' );
141+
142+ final receivedMessage = await messageCompleter.future
143+ .timeout (const Duration (seconds: 2 ));
144+ expect (receivedMessage, equals ('Hello from client' ));
145+ client.close ();
146+ });
147+
148+ test ('client receives messages from server' , () async {
149+ final messageCompleter = Completer <String >();
150+
151+ server.onConnection ((serverClient, url) {
152+ serverClient.send ('Welcome to server' );
153+ });
154+
155+ final client = _createWebSocketClient ('ws://localhost:$testPort ' );
156+
157+ _onMessage (client, (data) {
158+ final message = _extractMessage (data);
159+ messageCompleter.complete (message);
160+ });
161+
162+ final receivedMessage = await messageCompleter.future
163+ .timeout (const Duration (seconds: 2 ));
164+ expect (receivedMessage, equals ('Welcome to server' ));
165+ client.close ();
166+ });
167+ });
168+ }
169+
170+ /// Creates a WebSocket client using Node.js ws package
171+ JSWebSocket _createWebSocketClient (String url) {
172+ final ws = requireModule ('ws' );
173+ final wsClass = switch (ws) {
174+ final JSFunction f => f,
175+ _ => throw StateError ('WebSocket module not found' ),
176+ };
177+ return JSWebSocket (wsClass.callAsConstructor <JSObject >(url.toJS));
178+ }
179+
180+ /// Waits for WebSocket to reach OPEN state
181+ Future <void > _waitForOpen (JSWebSocket ws) async {
182+ final completer = Completer <void >();
183+
184+ if (ws.readyState == 1 ) {
185+ completer.complete ();
186+ } else {
187+ ws.on ('open' , (() => completer.complete ()).toJS);
188+ ws.on ('error' , ((JSAny error) => completer.completeError ('Connection failed: $error ' )).toJS);
189+ }
190+
191+ return completer.future.timeout (const Duration (seconds: 2 ));
192+ }
193+
194+ /// Sends a message through WebSocket
195+ void _sendMessage (JSWebSocket ws, String message) {
196+ ws.send (message.toJS);
197+ }
198+
199+ /// Sets up message handler for WebSocket
200+ void _onMessage (JSWebSocket ws, void Function (JSAny ) handler) {
201+ ws.on ('message' , handler.toJS);
202+ }
203+
204+ /// Extracts string message from JSAny data
205+ String _extractMessage (JSAny data) {
206+ // Convert using JavaScript String() function for safety
207+ try {
208+ final stringConstructor = globalContext['String' ] as JSFunction ;
209+ return (stringConstructor.callAsFunction (null , data) as JSString ).toDart;
210+ } catch (_) {
211+ return data.toString ();
212+ }
213+ }
214+
215+ /// JS interop types for WebSocket client
216+ extension type JSWebSocket (JSObject _) implements JSObject {
217+ external void on (String event, JSFunction handler);
218+ external void send (JSAny data);
219+ external void close ([int ? code, String ? reason]);
220+ external int get readyState;
98221}
0 commit comments