Skip to content

Commit 392ee1a

Browse files
authored
1.0.0-pre.5 (#5)
* Add web related tests * Exclude browser setup * Reformat code * Fix web tests * Fix typed data * Fake client tests * Ignore coverage of IS_NOT_OPEN_AFTER_CONNECT * Update readme with current coverage * 1.0.0-pre.5
1 parent 295a97c commit 392ee1a

16 files changed

+306
-29
lines changed

.github/workflows/checkout.yml

+3
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ jobs:
6868
timeout-minutes: 1
6969
run: dart analyze --fatal-infos --fatal-warnings lib/
7070

71+
#- name: 🌐 Setup Chrome
72+
# uses: browser-actions/setup-chrome@latest
73+
7174
- name: 🧪 Run tests
7275
timeout-minutes: 2
7376
run: |

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 1.0.0-pre.4
1+
## 1.0.0-pre.5
22

33
- **BREAKING CHANGE**: Change options to separate, platform-specific object
44
- You can now pass headers and other options to the IO web socket client

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ The `ws` package provides a cross-platform WebSocket client that supports JSON.
6868
- ✅ Creating Web Socket client from custom implementation
6969
- ✅ Fake client for testing
7070
- ✅ Custom exceptions
71+
- ✅ 90% test coverage
7172
- ❌ First message after connection/reconnection
7273
- ❌ Automatic ping/pong for keep-alive & connection health check
7374
- ❌ Reusing client between isolates
74-
- ❌ 95% test coverage
7575
- ❌ Interceptors (middlewares)
7676
- ❌ RPC support
7777

lib/src/client/ws_client_base.dart

+11-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ abstract base class WebSocketClientBase implements IWebSocketClient {
2323
: null;
2424

2525
@override
26-
bool get isClosed => _isClosed;
26+
bool get isClosed => _isClosed; // coverage:ignore-line
2727
bool _isClosed = false;
2828

2929
/// {@nodoc}
@@ -65,11 +65,13 @@ abstract base class WebSocketClientBase implements IWebSocketClient {
6565
@override
6666
@mustCallSuper
6767
FutureOr<void> add(Object data) async {
68+
// coverage:ignore-start
6869
if ($debugWS) {
6970
var text = data.toString();
7071
text = text.length > 100 ? '${text.substring(0, 97)}...' : text;
7172
fine('> $text');
7273
}
74+
// coverage:ignore-end
7375
}
7476

7577
@override
@@ -90,9 +92,8 @@ abstract base class WebSocketClientBase implements IWebSocketClient {
9092
_isClosed = true;
9193
try {
9294
await disconnect(code, reason);
93-
} on Object {
94-
/* ignore */
95-
}
95+
} on Object {/* ignore */} // coverage:ignore-line
96+
9697
_dataController.close().ignore();
9798
_stateController.close().ignore();
9899
}
@@ -116,24 +117,30 @@ abstract base class WebSocketClientBase implements IWebSocketClient {
116117
/// {@nodoc}
117118
@protected
118119
void onSent(Object data) {
120+
// coverage:ignore-start
119121
if ($debugWS) {
120122
var text = data.toString();
121123
text = text.length > 100 ? '${text.substring(0, 97)}...' : text;
122124
fine('Sent: $text');
123125
}
126+
// coverage:ignore-end
124127
}
125128

126129
/// On data received callback.
127130
/// {@nodoc}
128131
@protected
129132
void onReceivedData(Object? data) {
133+
// coverage:ignore-start
130134
if (data == null || _dataController.isClosed) return;
135+
// coverage:ignore-end
131136
_dataController.add(data);
137+
// coverage:ignore-start
132138
if ($debugWS) {
133139
var text = data.toString();
134140
text = text.length > 100 ? '${text.substring(0, 97)}...' : text;
135141
fine('< $text');
136142
}
143+
// coverage:ignore-end
137144
}
138145

139146
/// {@nodoc}

lib/src/client/ws_client_fake.dart

+14-8
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import 'package:ws/src/client/state_stream.dart';
77
import 'package:ws/src/client/ws_client_interface.dart';
88
import 'package:ws/src/client/ws_options.dart';
99

10+
// coverage:ignore-start
1011
/// {@nodoc}
1112
@internal
1213
IWebSocketClient $platformWebSocketClient(WebSocketOptions? options) =>
1314
WebSocketClientFake(protocols: options?.protocols);
15+
// coverage:ignore-end
1416

1517
/// {@template ws_client_fake}
1618
/// Fake WebSocket client for testing purposes.
@@ -24,19 +26,22 @@ final class WebSocketClientFake implements IWebSocketClient {
2426
: protocols = protocols?.toList(),
2527
_controller = StreamController<Object>.broadcast(),
2628
_stateController = StreamController<WebSocketClientState>.broadcast(),
27-
isClosed = false;
29+
_isClosed = false,
30+
_state = WebSocketClientState.initial();
2831

2932
/// {@nodoc}
3033
@visibleForTesting
3134
final List<String>? protocols;
3235

3336
@override
3437
@visibleForTesting
35-
bool isClosed;
38+
bool get isClosed => _isClosed;
39+
bool _isClosed;
3640

3741
@override
3842
@visibleForTesting
39-
WebSocketClientState state = WebSocketClientState.initial();
43+
WebSocketClientState get state => _state;
44+
WebSocketClientState _state;
4045

4146
final StreamController<WebSocketClientState> _stateController;
4247

@@ -64,28 +69,29 @@ final class WebSocketClientFake implements IWebSocketClient {
6469
@override
6570
@visibleForTesting
6671
FutureOr<void> connect(String url) async {
67-
_stateController.add(state = WebSocketClientState.connecting(url: url));
72+
_stateController.add(_state = WebSocketClientState.connecting(url: url));
6873
await Future<void>.delayed(const Duration(milliseconds: 25));
69-
_stateController.add(state = WebSocketClientState.open(url: url));
74+
_stateController.add(_state = WebSocketClientState.open(url: url));
7075
}
7176

7277
@override
7378
@visibleForTesting
7479
FutureOr<void> disconnect(
7580
[int? code = 1000, String? reason = 'NORMAL_CLOSURE']) async {
76-
_stateController.add(state = WebSocketClientState.disconnecting(
81+
_stateController.add(_state = WebSocketClientState.disconnecting(
7782
closeCode: code, closeReason: reason));
7883
await Future<void>.delayed(const Duration(milliseconds: 25));
79-
_stateController.add(state =
84+
_stateController.add(_state =
8085
WebSocketClientState.closed(closeCode: code, closeReason: reason));
8186
}
8287

8388
@override
8489
@visibleForTesting
8590
FutureOr<void> close(
8691
[int? code = 1000, String? reason = 'NORMAL_CLOSURE']) async {
92+
_isClosed = true;
8793
await disconnect(code, reason);
88-
isClosed = true;
94+
await Future<void>.delayed(Duration.zero);
8995
await _stateController.close();
9096
await _controller.close();
9197
}

lib/src/client/ws_client_js.dart

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Ignore web related imports at the GitHub Actions coverage.
2+
// coverage:ignore-file
13
import 'dart:async';
24
import 'dart:convert';
35
import 'dart:html' as html show WebSocket, Blob, Event, CloseEvent, FileReader;
@@ -132,8 +134,11 @@ final class WebSocketClient$JS extends WebSocketClientBase {
132134
.asyncMap<Object?>((data) => switch (data) {
133135
String text => text,
134136
html.Blob blob => _blobCodec.read(blob),
135-
/* html.Blob blob => (blob as ByteBuffer).asUint8List(), */
136-
TypedData td => td.buffer.asInt8List(),
137+
TypedData td => Uint8List.view(
138+
td.buffer,
139+
td.offsetInBytes,
140+
td.lengthInBytes,
141+
),
137142
ByteBuffer bb => bb.asInt8List(),
138143
List<int> bytes => bytes,
139144
_ => data,
@@ -150,13 +155,15 @@ final class WebSocketClient$JS extends WebSocketClientBase {
150155
cancelOnError: false,
151156
);
152157
await completer.future;
158+
// coverage:ignore-start
153159
if (!readyState.isOpen) {
154160
disconnect(1001, 'IS_NOT_OPEN_AFTER_CONNECT');
155161
assert(
156162
false,
157163
'Invalid readyState code after connect: $readyState',
158164
);
159165
}
166+
// coverage:ignore-end
160167
super.onConnected(url);
161168
} on Object catch (error, stackTrace) {
162169
onError(error, stackTrace);
@@ -179,7 +186,7 @@ final class WebSocketClient$JS extends WebSocketClientBase {
179186
if (client != null) {
180187
try {
181188
client.close(code, reason);
182-
} on Object {/* ignore */}
189+
} on Object {/* ignore */} // coverage:ignore-line
183190
_client = null;
184191
}
185192
super.onDisconnected(code, reason);
@@ -205,7 +212,13 @@ final class _BlobCodec {
205212
case String text:
206213
return html.Blob([Uint8List.fromList(utf8.encode(text))]);
207214
case TypedData td:
208-
return html.Blob([td.buffer.asUint8List()]);
215+
return html.Blob([
216+
Uint8List.view(
217+
td.buffer,
218+
td.offsetInBytes,
219+
td.lengthInBytes,
220+
)
221+
]);
209222
case ByteBuffer bb:
210223
return html.Blob([bb.asUint8List()]);
211224
case List<int> bytes:

lib/src/client/ws_client_vm.dart

+12-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ final class WebSocketClient$VM extends WebSocketClientBase {
6767
switch (data) {
6868
case String text:
6969
client.addUtf8Text(utf8.encode(text));
70-
case Uint8List bytes:
70+
case TypedData bytes:
7171
client.add(bytes);
7272
case ByteBuffer bb:
7373
client.add(bb.asUint8List());
@@ -101,11 +101,16 @@ final class WebSocketClient$VM extends WebSocketClientBase {
101101
_options?.compression ?? io.CompressionOptions.compressionDefault,
102102
customClient: _options?.customClient,
103103
);
104+
// coverage:ignore-start
104105
_dataBindSubscription = client
105106
.asyncMap<Object?>((data) => switch (data) {
106107
String text => text,
107-
Uint8List bytes => bytes,
108108
ByteBuffer bb => bb.asUint8List(),
109+
TypedData td => Uint8List.view(
110+
td.buffer,
111+
td.offsetInBytes,
112+
td.lengthInBytes,
113+
),
109114
List<int> bytes => bytes,
110115
_ => data,
111116
})
@@ -115,13 +120,17 @@ final class WebSocketClient$VM extends WebSocketClientBase {
115120
onDone: () => disconnect(1000, 'SUBSCRIPTION_CLOSED'),
116121
cancelOnError: false,
117122
);
123+
// coverage:ignore-end
124+
125+
// coverage:ignore-start
118126
if (!readyState.isOpen) {
119127
disconnect(1001, 'IS_NOT_OPEN_AFTER_CONNECT');
120128
assert(
121129
false,
122130
'Invalid readyState code after connect: $readyState',
123131
);
124132
}
133+
// coverage:ignore-end
125134
super.onConnected(url);
126135
} on Object catch (error, stackTrace) {
127136
onError(error, stackTrace);
@@ -140,7 +149,7 @@ final class WebSocketClient$VM extends WebSocketClientBase {
140149
if (client != null) {
141150
try {
142151
await client.close(code, reason);
143-
} on Object {/* ignore */}
152+
} on Object {/* ignore */} // coverage:ignore-line
144153
_client = null;
145154
}
146155
super.onDisconnected(code, reason);

lib/src/client/ws_options.dart

+7-2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ abstract base class WebSocketOptions {
111111
timeout: timeout,
112112
);
113113

114+
// Ignore web related imports at the GitHub Actions coverage.
115+
// coverage:ignore-start
116+
114117
/// {@template ws_options_js}
115118
/// Options for JS (Browser) platform.
116119
/// Do not use this class at the VM platform.
@@ -143,16 +146,18 @@ abstract base class WebSocketOptions {
143146
useBlobForBinary: useBlobForBinary,
144147
);
145148

149+
// coverage:ignore-end
150+
146151
/// Construct options for VM or JS platform depending on the current platform.
147152
///
148153
/// {@macro ws_options}
149154
factory WebSocketOptions.selector({
150155
required WebSocketOptions Function() vm,
151-
required WebSocketOptions Function() js,
156+
required WebSocketOptions Function() js, // coverage:ignore-line
152157
}) =>
153158
$selectorOptions(
154159
vm: vm,
155-
js: js,
160+
js: js, // coverage:ignore-line
156161
);
157162

158163
/// Backoff strategy for reconnecting.

lib/src/client/ws_options_js.dart

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Ignore web related imports at the GitHub Actions coverage.
2+
// coverage:ignore-file
13
import 'package:meta/meta.dart';
24
import 'package:ws/src/client/ws_options.dart';
35

@@ -32,6 +34,7 @@ WebSocketOptions $jsOptions({
3234
connectionRetryInterval: connectionRetryInterval,
3335
protocols: protocols,
3436
timeout: timeout,
37+
useBlobForBinary: useBlobForBinary,
3538
);
3639

3740
/// {@nodoc}

lib/src/client/ws_options_vm.dart

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ WebSocketOptions $vmOptions({
3030
timeout: timeout,
3131
);
3232

33+
// coverage:ignore-start
34+
3335
/// {@nodoc}
3436
@internal
3537
WebSocketOptions $jsOptions({
@@ -46,6 +48,8 @@ WebSocketOptions $jsOptions({
4648
);
4749
}
4850

51+
// coverage:ignore-end
52+
4953
/// {@nodoc}
5054
@internal
5155
WebSocketOptions $selectorOptions({

lib/src/util/logger.dart

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void Function(
3737
StackTrace? stackTrace,
3838
String? reason,
3939
]) _logAll(String prefix, int level) => (message, [stackTrace, reason]) {
40+
// coverage:ignore-start
4041
if (!$debugWS && Zone.current[#dev.plugfox.ws.debug] != true) return;
4142
developer.log(
4243
'[ws] ${reason ?? message}',
@@ -45,4 +46,5 @@ void Function(
4546
error: message is Exception || message is Error ? message : null,
4647
stackTrace: stackTrace,
4748
);
49+
// coverage:ignore-end
4850
};

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: ws
33
description: >
44
WS: A compact, highly efficient WebSocket library. Easily establish, manage, and reconnect WebSocket connections in real-time apps.
55
6-
version: 1.0.0-pre.4
6+
version: 1.0.0-pre.5
77

88
homepage: https://github.com/plugfox/ws
99

test/client/client_fake_test.dart

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import 'package:test/test.dart';
2+
import 'package:ws/interface.dart';
3+
4+
void main() => group('Fake client', () {
5+
late WebSocketClientFake client;
6+
7+
setUp(() {
8+
client = WebSocketClientFake(protocols: {'fake'});
9+
});
10+
11+
tearDown(() {
12+
client.close();
13+
});
14+
15+
test('Fake client', () async {
16+
expect(client, isA<IWebSocketClient>());
17+
expect(client, isA<WebSocketClientFake>());
18+
expect(client.isClosed, isFalse);
19+
expect(client.protocols, equals({'fake'}));
20+
expect(client.state.readyState.isOpen, isFalse);
21+
await expectLater(client.connect('url'), completes);
22+
expect(client.state.readyState.isOpen, isTrue);
23+
expect(() => client.loopBack('Hello, world'), returnsNormally);
24+
await Future<void>.delayed(const Duration(milliseconds: 25));
25+
expect(() => client.close(), returnsNormally);
26+
expect(client.state.readyState.isOpen, isFalse);
27+
});
28+
});

0 commit comments

Comments
 (0)