Skip to content

Commit 2bb16f2

Browse files
committed
test(collaboration): add comprehensive test suite and transport visibility UI
Implements extensive test coverage for all collaboration actors to ensure reliable peer-to-peer sync and automatic WebRTC/WebSocket fallback behavior. Adds visual transport indicators to demo UI for debugging and verification. Test Coverage: - WebRTCActor: 15 tests covering P2P signaling and messaging - WebSocketActor: 19 tests for connection lifecycle and relay - PeerMessagingActor: 16 tests for routing coordination - CollaborationActor: 20 tests for CRDT sync and state management - Integration: 19 tests for full actor hierarchy All 108 tests pass with deterministic async handling using flushMicrotask() helpers instead of arbitrary timeouts, following best practices for actor system testing. Transport UI: - Compact badges show WebRTC (⚡) vs WebSocket (🌐) per peer - Color-coded for instant recognition (green=P2P, blue=relay) - Integrated into header for minimal intrusion - Enables real-time verification of transport upgrades
1 parent 49addb9 commit 2bb16f2

File tree

19 files changed

+1673
-286
lines changed

19 files changed

+1673
-286
lines changed

demos/react/collaboration/src/App.css

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,58 @@ header p {
293293
color: #666;
294294
font-size: 0.9rem;
295295
}
296+
297+
/* Compact Transport Status */
298+
.transport-status {
299+
display: flex;
300+
gap: 0.75rem;
301+
justify-content: center;
302+
margin-top: 0.75rem;
303+
flex-wrap: wrap;
304+
}
305+
306+
.transport-chip {
307+
display: inline-flex;
308+
align-items: center;
309+
gap: 0.4rem;
310+
padding: 0.4rem 0.75rem;
311+
border-radius: 16px;
312+
font-size: 0.85rem;
313+
font-weight: 500;
314+
cursor: help;
315+
transition: all 0.2s;
316+
}
317+
318+
.transport-chip.webrtc {
319+
background: #e8f5e9;
320+
color: #2e7d32;
321+
border: 1px solid #81c784;
322+
}
323+
324+
.transport-chip.websocket {
325+
background: #e3f2fd;
326+
color: #1565c0;
327+
border: 1px solid #64b5f6;
328+
}
329+
330+
.transport-chip:hover {
331+
transform: translateY(-1px);
332+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
333+
}
334+
335+
.transport-chip.webrtc:hover {
336+
background: #c8e6c9;
337+
}
338+
339+
.transport-chip.websocket:hover {
340+
background: #bbdefb;
341+
}
342+
343+
.transport-icon {
344+
font-size: 1rem;
345+
}
346+
347+
.transport-label {
348+
font-size: 0.8rem;
349+
letter-spacing: 0.3px;
350+
}

demos/react/collaboration/src/App.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export function App() {
4242
const isConnecting = connectionState === 'connecting' || connectionState === 'reconnecting';
4343
const isDisconnected = connectionState === 'disconnected';
4444
const connectedPeers = peerMessaging.state.connectedPeers || [];
45+
const peerTransports = peerMessaging.state.peerTransports || {};
4546

4647
// Show loading screen while connecting
4748
if (isConnecting) {
@@ -112,6 +113,23 @@ export function App() {
112113
Disconnect
113114
</button>
114115
</div>
116+
117+
{/* Compact Transport Status */}
118+
{connectedPeers.length > 0 && (
119+
<div className="transport-status">
120+
{connectedPeers.map((peerId) => {
121+
const transport = peerTransports[peerId] || 'websocket';
122+
const isWebRTC = transport === 'webrtc';
123+
124+
return (
125+
<span key={peerId} className={`transport-chip ${transport}`} title={`Peer ${peerId.substring(0, 8)}`}>
126+
<span className="transport-icon">{isWebRTC ? '⚡' : '🌐'}</span>
127+
<span className="transport-label">{isWebRTC ? 'WebRTC' : 'WebSocket'}</span>
128+
</span>
129+
);
130+
})}
131+
</div>
132+
)}
115133
</header>
116134

117135
<main>

package-lock.json

Lines changed: 16 additions & 81 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/collaboration/README.md

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,14 @@ npm install @d-buckner/ensemble-collaboration @d-buckner/ensemble-core
3636
### Peer Dependencies
3737

3838
```bash
39-
npm install @automerge/automerge socket.io-client simple-peer
39+
npm install @automerge/automerge socket.io-client
4040
```
4141

4242
## Quick Start
4343

4444
```typescript
4545
import { CollaborationActor, PeerMessagingActor, WebSocketActor, WebRTCActor } from '@d-buckner/ensemble-collaboration';
4646
import { createActorToken, ActorSystem, action } from '@d-buckner/ensemble-core';
47-
import SimplePeer from 'simple-peer';
4847

4948
// 1. Define your document type
5049
interface TodoDoc {
@@ -258,7 +257,7 @@ Socket.IO client for signaling and fallback transport.
258257
259258
### WebRTCActor
260259
261-
WebRTC P2P transport using simple-peer.
260+
WebRTC P2P transport using my [peer-pressure](https://github.com/d-buckner/peer-pressure) library.
262261
263262
**Actions:**
264263
- `sendTo(peerId, message)` - Send via WebRTC data channel
@@ -269,14 +268,6 @@ WebRTC P2P transport using simple-peer.
269268
- `messageReceived: { peerId, message }` - Data from peer
270269
- `signalingData: { peerId, data }` - Outbound signaling for peer
271270
272-
## Bundle Size
273-
274-
- Automerge: ~27KB gzipped
275-
- Socket.IO client: ~20KB gzipped
276-
- Simple-peer: ~6KB gzipped
277-
- Package code: ~5KB gzipped
278-
- **Total**: ~58KB gzipped (only when imported)
279-
280271
## License
281272
282273
Apache-2.0

packages/collaboration/examples/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,9 @@ Socket.IO client for server communication:
182182
To enable P2P WebRTC connections (lower latency):
183183

184184
1. Add WebRTCActor to the client setup
185-
2. Install `simple-peer` package
186-
3. Configure WebRTC in actor registration
185+
2. Configure WebRTC in actor registration
187186

188187
```typescript
189-
import SimplePeer from 'simple-peer';
190188

191189
const WebRTCToken = createActorToken<WebRTCActor>('webrtc');
192190

packages/collaboration/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@
5151
},
5252
"dependencies": {
5353
"@automerge/automerge": "^2.2.8",
54+
"@d-buckner/peer-pressure": "^0.2.0",
5455
"mutative": "^1.3.0",
5556
"socket.io": "^4.8.1",
5657
"socket.io-client": "^4.7.0"
5758
},
5859
"peerDependencies": {
59-
"@d-buckner/ensemble-core": "^0.1.0",
60-
"simple-peer": "^9.11.0"
60+
"@d-buckner/ensemble-core": "*"
6161
},
6262
"devDependencies": {
63-
"@d-buckner/ensemble-core": "^0.2.0",
64-
"@d-buckner/ensemble-vite-plugin": "^0.2.0",
63+
"@d-buckner/ensemble-core": "*",
64+
"@d-buckner/ensemble-vite-plugin": "*",
6565
"concurrently": "^9.1.2",
6666
"tsx": "^4.19.2",
6767
"typescript": "~5.9.3",

packages/collaboration/src/PeerMessagingActor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export class PeerMessagingActor extends Actor<PeerMessagingState, PeerMessagingE
9696
/**
9797
* Handle peer joined via WebSocket.
9898
* Adds peer to state with WebSocket transport and emits peerConnected.
99-
* Also initiates WebRTC connection attempt.
99+
* Also initiates WebRTC connection as the initiator.
100100
*/
101101
@effect('websocket.peerJoined')
102102
private handleWebSocketPeerJoined(peerId: string): void {

0 commit comments

Comments
 (0)