mockrtc 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +2 -2
- package/dist/client/mockrtc-admin-request-builder.d.ts +7 -1
- package/dist/client/mockrtc-admin-request-builder.js +30 -0
- package/dist/client/mockrtc-admin-request-builder.js.map +1 -1
- package/dist/client/mockrtc-client.d.ts +8 -4
- package/dist/client/mockrtc-client.js +21 -9
- package/dist/client/mockrtc-client.js.map +1 -1
- package/dist/handling/handler-builder.d.ts +16 -9
- package/dist/handling/handler-builder.js +11 -1
- package/dist/handling/handler-builder.js.map +1 -1
- package/dist/handling/handler-step-definitions.d.ts +40 -23
- package/dist/handling/handler-step-definitions.js +61 -19
- package/dist/handling/handler-step-definitions.js.map +1 -1
- package/dist/handling/handler-steps.d.ts +11 -1
- package/dist/handling/handler-steps.js +33 -16
- package/dist/handling/handler-steps.js.map +1 -1
- package/dist/main-browser.d.ts +1 -0
- package/dist/main-browser.js +2 -1
- package/dist/main-browser.js.map +1 -1
- package/dist/main.d.ts +5 -3
- package/dist/main.js +2 -1
- package/dist/main.js.map +1 -1
- package/dist/matching/matcher-definitions.d.ts +51 -0
- package/dist/matching/matcher-definitions.js +94 -0
- package/dist/matching/matcher-definitions.js.map +1 -0
- package/dist/matching/matchers.d.ts +27 -0
- package/dist/matching/matchers.js +87 -0
- package/dist/matching/matchers.js.map +1 -0
- package/dist/mockrtc-base.d.ts +16 -0
- package/dist/mockrtc-base.js +19 -0
- package/dist/mockrtc-base.js.map +1 -0
- package/dist/mockrtc-peer.d.ts +9 -2
- package/dist/mockrtc.d.ts +62 -0
- package/dist/mockrtc.js.map +1 -1
- package/dist/rule-builder.d.ts +86 -0
- package/dist/rule-builder.js +113 -0
- package/dist/rule-builder.js.map +1 -0
- package/dist/server/mockrtc-admin-plugin.js +18 -1
- package/dist/server/mockrtc-admin-plugin.js.map +1 -1
- package/dist/server/mockrtc-server-peer.d.ts +1 -1
- package/dist/server/mockrtc-server-peer.js +19 -7
- package/dist/server/mockrtc-server-peer.js.map +1 -1
- package/dist/server/mockrtc-server.d.ts +12 -5
- package/dist/server/mockrtc-server.js +53 -18
- package/dist/server/mockrtc-server.js.map +1 -1
- package/dist/webrtc/datachannel-stream.d.ts +2 -0
- package/dist/webrtc/datachannel-stream.js +15 -1
- package/dist/webrtc/datachannel-stream.js.map +1 -1
- package/dist/webrtc/mediatrack-stream.d.ts +2 -0
- package/dist/webrtc/mediatrack-stream.js +15 -1
- package/dist/webrtc/mediatrack-stream.js.map +1 -1
- package/dist/webrtc/mockrtc-connection.js +1 -1
- package/dist/webrtc/mockrtc-connection.js.map +1 -1
- package/dist/webrtc/rtc-connection.d.ts +6 -2
- package/dist/webrtc/rtc-connection.js +21 -21
- package/dist/webrtc/rtc-connection.js.map +1 -1
- package/dist/webrtc-hooks.js +8 -2
- package/dist/webrtc-hooks.js.map +1 -1
- package/package.json +2 -2
- package/src/client/mockrtc-admin-request-builder.ts +49 -1
- package/src/client/mockrtc-client.ts +28 -10
- package/src/handling/handler-builder.ts +22 -10
- package/src/handling/handler-step-definitions.ts +81 -23
- package/src/handling/handler-steps.ts +42 -15
- package/src/main-browser.ts +1 -0
- package/src/main.ts +5 -1
- package/src/matching/matcher-definitions.ts +109 -0
- package/src/matching/matchers.ts +118 -0
- package/src/mockrtc-base.ts +49 -0
- package/src/mockrtc-peer.ts +9 -2
- package/src/mockrtc.ts +72 -0
- package/src/rule-builder.ts +142 -0
- package/src/server/mockrtc-admin-plugin.ts +41 -3
- package/src/server/mockrtc-server-peer.ts +28 -18
- package/src/server/mockrtc-server.ts +71 -15
- package/src/webrtc/datachannel-stream.ts +15 -1
- package/src/webrtc/mediatrack-stream.ts +15 -1
- package/src/webrtc/mockrtc-connection.ts +1 -1
- package/src/webrtc/rtc-connection.ts +37 -20
- package/src/webrtc-hooks.ts +8 -2
- package/test/integration/events.spec.ts +3 -1
- package/test/integration/matching.spec.ts +189 -0
- package/test/integration/proxy.spec.ts +4 -2
- package/test/integration/send-steps.spec.ts +51 -0
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
import { DataChannelStream } from './datachannel-stream';
|
|
21
21
|
import { MediaTrackStream } from './mediatrack-stream';
|
|
22
22
|
|
|
23
|
+
export type ParsedSDP = {
|
|
24
|
+
parsedSdp: SDP.SessionDescription;
|
|
25
|
+
};
|
|
26
|
+
|
|
23
27
|
/**
|
|
24
28
|
* An RTC connection is a single connection. This base class defines the raw connection management and
|
|
25
29
|
* tracking logic for a generic connection. The MockRTCConnection subclass extends this and adds
|
|
@@ -34,8 +38,8 @@ export class RTCConnection extends EventEmitter {
|
|
|
34
38
|
private rawConn: NodeDataChannel.PeerConnection | null
|
|
35
39
|
= new NodeDataChannel.PeerConnection("MockRTCConnection", { iceServers: [] });
|
|
36
40
|
|
|
37
|
-
private remoteDescription: RTCSessionDescriptionInit | undefined;
|
|
38
|
-
private localDescription: MockRTCSessionDescription | undefined;
|
|
41
|
+
private remoteDescription: RTCSessionDescriptionInit & ParsedSDP | undefined;
|
|
42
|
+
private localDescription: MockRTCSessionDescription & ParsedSDP | undefined;
|
|
39
43
|
|
|
40
44
|
private _connectionMetadata: ConnectionMetadata = {};
|
|
41
45
|
public get metadata() {
|
|
@@ -132,13 +136,13 @@ export class RTCConnection extends EventEmitter {
|
|
|
132
136
|
channelStream.on('error', (error) => {
|
|
133
137
|
console.error('Channel error:', error);
|
|
134
138
|
});
|
|
139
|
+
this.emit('channel-created', channelStream);
|
|
140
|
+
this.emit(`${options.isLocal ? 'local' : 'remote'}-channel-created`, channelStream);
|
|
135
141
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.emit('local-channel-open
|
|
139
|
-
}
|
|
140
|
-
this.emit('remote-channel-open', channelStream);
|
|
141
|
-
}
|
|
142
|
+
channelStream.once('channel-open', () => {
|
|
143
|
+
this.emit('channel-open', channelStream);
|
|
144
|
+
this.emit(`${options.isLocal ? 'local' : 'remote'}-channel-open`, channelStream);
|
|
145
|
+
});
|
|
142
146
|
|
|
143
147
|
return channelStream;
|
|
144
148
|
}
|
|
@@ -150,7 +154,7 @@ export class RTCConnection extends EventEmitter {
|
|
|
150
154
|
trackStream.on('close', () => {
|
|
151
155
|
const trackIndex = this.trackedMediaTracks.findIndex(c => c.stream === trackStream);
|
|
152
156
|
if (trackIndex !== -1) {
|
|
153
|
-
this.
|
|
157
|
+
this.trackedMediaTracks.splice(trackIndex, 1);
|
|
154
158
|
}
|
|
155
159
|
});
|
|
156
160
|
|
|
@@ -158,12 +162,13 @@ export class RTCConnection extends EventEmitter {
|
|
|
158
162
|
console.error('Media track error:', error);
|
|
159
163
|
});
|
|
160
164
|
|
|
161
|
-
this.emit('track-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.emit('
|
|
166
|
-
|
|
165
|
+
this.emit('track-created', trackStream);
|
|
166
|
+
this.emit(`${options.isLocal ? 'local' : 'remote'}-track-created`, trackStream);
|
|
167
|
+
|
|
168
|
+
trackStream.once('track-open', () => {
|
|
169
|
+
this.emit('track-open', trackStream);
|
|
170
|
+
this.emit(`${options.isLocal ? 'local' : 'remote'}-track-open`, trackStream);
|
|
171
|
+
});
|
|
167
172
|
|
|
168
173
|
return trackStream;
|
|
169
174
|
}
|
|
@@ -171,7 +176,10 @@ export class RTCConnection extends EventEmitter {
|
|
|
171
176
|
setRemoteDescription(description: RTCSessionDescriptionInit) {
|
|
172
177
|
if (!this.rawConn) throw new Error("Can't set remote description after connection is closed");
|
|
173
178
|
|
|
174
|
-
this.remoteDescription =
|
|
179
|
+
this.remoteDescription = {
|
|
180
|
+
...description,
|
|
181
|
+
parsedSdp: SDP.parse(description.sdp ?? '')
|
|
182
|
+
};
|
|
175
183
|
const { type: offerType, sdp: offerSdp } = description;
|
|
176
184
|
if (!offerSdp) throw new Error("Cannot set MockRTC peer description without providing an SDP");
|
|
177
185
|
this.rawConn.setRemoteDescription(offerSdp, offerType[0].toUpperCase() + offerType.slice(1) as any);
|
|
@@ -207,7 +215,10 @@ export class RTCConnection extends EventEmitter {
|
|
|
207
215
|
|
|
208
216
|
const sessionDescription = this.rawConn.localDescription() as MockRTCSessionDescription;
|
|
209
217
|
setupChannel?.close(); // Close the temporary setup channel, if we created one
|
|
210
|
-
this.localDescription =
|
|
218
|
+
this.localDescription = {
|
|
219
|
+
...sessionDescription,
|
|
220
|
+
parsedSdp: SDP.parse(sessionDescription.sdp ?? '')
|
|
221
|
+
};
|
|
211
222
|
return sessionDescription;
|
|
212
223
|
}
|
|
213
224
|
|
|
@@ -324,7 +335,10 @@ export class RTCConnection extends EventEmitter {
|
|
|
324
335
|
mirrorMediaParams(offerToMirror, offerSDP);
|
|
325
336
|
localDesc.sdp = SDP.write(offerSDP);
|
|
326
337
|
|
|
327
|
-
this.localDescription =
|
|
338
|
+
this.localDescription = {
|
|
339
|
+
...localDesc as MockRTCSessionDescription,
|
|
340
|
+
parsedSdp: offerSDP
|
|
341
|
+
};
|
|
328
342
|
return this.localDescription;
|
|
329
343
|
}
|
|
330
344
|
|
|
@@ -337,7 +351,10 @@ export class RTCConnection extends EventEmitter {
|
|
|
337
351
|
|
|
338
352
|
localDesc.sdp = SDP.write(answerSDP);
|
|
339
353
|
|
|
340
|
-
this.localDescription =
|
|
354
|
+
this.localDescription = {
|
|
355
|
+
...localDesc as MockRTCSessionDescription,
|
|
356
|
+
parsedSdp: answerSDP
|
|
357
|
+
};
|
|
341
358
|
return this.localDescription;
|
|
342
359
|
}
|
|
343
360
|
|
|
@@ -413,7 +430,7 @@ export class RTCConnection extends EventEmitter {
|
|
|
413
430
|
this.localDescription = undefined;
|
|
414
431
|
|
|
415
432
|
if (rawConn.state() === 'closed') return;
|
|
416
|
-
rawConn.
|
|
433
|
+
rawConn.destroy();
|
|
417
434
|
this.emit('connection-closed');
|
|
418
435
|
}
|
|
419
436
|
|
package/src/webrtc-hooks.ts
CHANGED
|
@@ -163,7 +163,10 @@ export function hookWebRTCConnection(conn: RTCPeerConnection, mockPeer: MockRTCP
|
|
|
163
163
|
mockOffer = mockPeer.createOffer({
|
|
164
164
|
mirrorSDP: remoteDescription.sdp,
|
|
165
165
|
addDataStream: true,
|
|
166
|
-
connectionMetadata: {
|
|
166
|
+
connectionMetadata: {
|
|
167
|
+
userAgent: navigator.userAgent,
|
|
168
|
+
sourceURL: window.location.href
|
|
169
|
+
}
|
|
167
170
|
});
|
|
168
171
|
|
|
169
172
|
await _setRemoteDescription((await mockOffer).offer);
|
|
@@ -176,7 +179,10 @@ export function hookWebRTCConnection(conn: RTCPeerConnection, mockPeer: MockRTCP
|
|
|
176
179
|
// Complete the internal <-> mock connection:
|
|
177
180
|
mockPeer.answerOffer(realOffer, {
|
|
178
181
|
mirrorSDP: remoteDescription.sdp,
|
|
179
|
-
connectionMetadata: {
|
|
182
|
+
connectionMetadata: {
|
|
183
|
+
userAgent: navigator.userAgent,
|
|
184
|
+
sourceURL: window.location.href
|
|
185
|
+
}
|
|
180
186
|
}).then(({ answer }) => _setRemoteDescription(answer))
|
|
181
187
|
]);
|
|
182
188
|
|
|
@@ -32,7 +32,8 @@ describe("MockRTC event subscriptions", function () {
|
|
|
32
32
|
|
|
33
33
|
const { offer, setAnswer } = await mockPeer.createOffer({
|
|
34
34
|
connectionMetadata: {
|
|
35
|
-
userAgent: navigator.userAgent
|
|
35
|
+
userAgent: navigator.userAgent,
|
|
36
|
+
sourceURL: 'https://example.com/'
|
|
36
37
|
}
|
|
37
38
|
});
|
|
38
39
|
await localConnection.setRemoteDescription(offer);
|
|
@@ -58,6 +59,7 @@ describe("MockRTC event subscriptions", function () {
|
|
|
58
59
|
expect(connectionEvent.timingEvents.disconnectTimestamp).to.equal(undefined);
|
|
59
60
|
|
|
60
61
|
expect(connectionEvent.metadata.userAgent).to.equal(navigator.userAgent);
|
|
62
|
+
expect(connectionEvent.metadata.sourceURL).to.equal('https://example.com/');
|
|
61
63
|
|
|
62
64
|
const { selectedLocalCandidate, selectedRemoteCandidate } = connectionEvent;
|
|
63
65
|
[selectedLocalCandidate, selectedRemoteCandidate].forEach((candidate) => {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import {
|
|
2
7
|
MockRTC,
|
|
3
8
|
expect,
|
|
@@ -65,4 +70,188 @@ describe("Connection rule matching", () => {
|
|
|
65
70
|
'local message 3',
|
|
66
71
|
]);
|
|
67
72
|
});
|
|
73
|
+
|
|
74
|
+
it("can match data connections", async () => {
|
|
75
|
+
// Explicitly hard-close media connections:
|
|
76
|
+
await mockRTC.forConnections()
|
|
77
|
+
.withMedia()
|
|
78
|
+
.thenClose();
|
|
79
|
+
|
|
80
|
+
// Send a message on data channels only:
|
|
81
|
+
await mockRTC.forConnections()
|
|
82
|
+
.withDataChannels()
|
|
83
|
+
.waitForChannel()
|
|
84
|
+
.thenSend('bye');
|
|
85
|
+
|
|
86
|
+
const matchingPeer = await mockRTC.getMatchingPeer();
|
|
87
|
+
|
|
88
|
+
// Create a local data connection:
|
|
89
|
+
const localConn = new RTCPeerConnection();
|
|
90
|
+
|
|
91
|
+
const dataChannel = localConn.createDataChannel("dataChannel");
|
|
92
|
+
|
|
93
|
+
const messagePromise = new Promise((resolve) => {
|
|
94
|
+
dataChannel.addEventListener('message', ({ data }) => resolve(data));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const localOffer = await localConn.createOffer();
|
|
98
|
+
await localConn.setLocalDescription(localOffer);
|
|
99
|
+
const { answer } = await matchingPeer.answerOffer(localOffer);
|
|
100
|
+
await localConn.setRemoteDescription(answer);
|
|
101
|
+
|
|
102
|
+
// Wait until the matching handler sends the configured message:
|
|
103
|
+
const receivedMessage = await messagePromise;
|
|
104
|
+
expect(receivedMessage).to.equal('bye');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("can match media connections", async () => {
|
|
108
|
+
// Explicitly hard-close data connections:
|
|
109
|
+
await mockRTC.forConnections()
|
|
110
|
+
.withDataChannels()
|
|
111
|
+
.thenClose();
|
|
112
|
+
|
|
113
|
+
// Send a message on media connections only:
|
|
114
|
+
await mockRTC.forConnections()
|
|
115
|
+
.withMedia()
|
|
116
|
+
.thenEcho();
|
|
117
|
+
|
|
118
|
+
const matchingPeer = await mockRTC.getMatchingPeer();
|
|
119
|
+
|
|
120
|
+
// Create a local connection:
|
|
121
|
+
const localConn = new RTCPeerConnection();
|
|
122
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
123
|
+
localConn.addTrack(stream.getTracks()[0], stream);
|
|
124
|
+
|
|
125
|
+
// Turn incoming tracks into readable streams of frames:
|
|
126
|
+
const mediaStreamPromise = new Promise<ReadableStream<VideoFrame>>((resolve) => {
|
|
127
|
+
localConn.addEventListener('track', ({ track }) => {
|
|
128
|
+
const streamProcessor = new MediaStreamTrackProcessor({
|
|
129
|
+
track: track as MediaStreamVideoTrack
|
|
130
|
+
});
|
|
131
|
+
resolve(streamProcessor.readable);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Connect the local connection to the matching peer:
|
|
136
|
+
const localOffer = await localConn.createOffer();
|
|
137
|
+
await localConn.setLocalDescription(localOffer);
|
|
138
|
+
const { answer } = await matchingPeer.answerOffer(localOffer);
|
|
139
|
+
await localConn.setRemoteDescription(answer);
|
|
140
|
+
|
|
141
|
+
// Check that our video is mirrored as expected:
|
|
142
|
+
const localMedia = await mediaStreamPromise;
|
|
143
|
+
const { value: localFrame } = await localMedia!.getReader().read();
|
|
144
|
+
expect(localFrame!.displayHeight).to.be.greaterThanOrEqual(240);
|
|
145
|
+
expect(localFrame!.displayWidth).to.be.greaterThanOrEqual(320);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("can match connections by host", async () => {
|
|
149
|
+
// Send a message for connections made from example.com pages:
|
|
150
|
+
await mockRTC.forConnections()
|
|
151
|
+
.fromPageHostname('example.com')
|
|
152
|
+
.waitForChannel()
|
|
153
|
+
.thenSend('hello example.com');
|
|
154
|
+
|
|
155
|
+
// Close any other connections:
|
|
156
|
+
await mockRTC.forConnections()
|
|
157
|
+
.thenClose();
|
|
158
|
+
|
|
159
|
+
const matchingPeer = await mockRTC.getMatchingPeer();
|
|
160
|
+
|
|
161
|
+
// Create a local data connection:
|
|
162
|
+
const localConn = new RTCPeerConnection();
|
|
163
|
+
|
|
164
|
+
const dataChannel = localConn.createDataChannel("dataChannel");
|
|
165
|
+
|
|
166
|
+
const messagePromise = new Promise((resolve) => {
|
|
167
|
+
dataChannel.addEventListener('message', ({ data }) => resolve(data));
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const localOffer = await localConn.createOffer();
|
|
171
|
+
await localConn.setLocalDescription(localOffer);
|
|
172
|
+
const { answer } = await matchingPeer.answerOffer(localOffer, {
|
|
173
|
+
connectionMetadata: {
|
|
174
|
+
sourceURL: 'https://example.com/abc?x=y#123'
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
await localConn.setRemoteDescription(answer);
|
|
178
|
+
|
|
179
|
+
// Wait until the matching handler sends the configured message:
|
|
180
|
+
const receivedMessage = await messagePromise;
|
|
181
|
+
expect(receivedMessage).to.equal('hello example.com');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("can match connections by URL regex", async () => {
|
|
185
|
+
// Close some other connections:
|
|
186
|
+
await mockRTC.forConnections()
|
|
187
|
+
.fromPageUrlMatching(/\?1+1=3/)
|
|
188
|
+
.thenClose();
|
|
189
|
+
|
|
190
|
+
// Send a message for connections made from matching pages:
|
|
191
|
+
await mockRTC.forConnections()
|
|
192
|
+
.fromPageUrlMatching(/\?x=y/)
|
|
193
|
+
.waitForChannel()
|
|
194
|
+
.thenSend('hello x=y');
|
|
195
|
+
|
|
196
|
+
const matchingPeer = await mockRTC.getMatchingPeer();
|
|
197
|
+
|
|
198
|
+
// Create a local data connection:
|
|
199
|
+
const localConn = new RTCPeerConnection();
|
|
200
|
+
|
|
201
|
+
const dataChannel = localConn.createDataChannel("dataChannel");
|
|
202
|
+
|
|
203
|
+
const messagePromise = new Promise((resolve) => {
|
|
204
|
+
dataChannel.addEventListener('message', ({ data }) => resolve(data));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const localOffer = await localConn.createOffer();
|
|
208
|
+
await localConn.setLocalDescription(localOffer);
|
|
209
|
+
const { answer } = await matchingPeer.answerOffer(localOffer, {
|
|
210
|
+
connectionMetadata: {
|
|
211
|
+
sourceURL: 'https://example.com/abc?x=y#123'
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
await localConn.setRemoteDescription(answer);
|
|
215
|
+
|
|
216
|
+
// Wait until the matching handler sends the configured message:
|
|
217
|
+
const receivedMessage = await messagePromise;
|
|
218
|
+
expect(receivedMessage).to.equal('hello x=y');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("can match connections by user agent regex", async () => {
|
|
222
|
+
// Close some other connections:
|
|
223
|
+
await mockRTC.forConnections()
|
|
224
|
+
.fromUserAgentMatching(/IE6/)
|
|
225
|
+
.thenClose();
|
|
226
|
+
|
|
227
|
+
// Send a message for connections made from Firefox:
|
|
228
|
+
await mockRTC.forConnections()
|
|
229
|
+
.fromUserAgentMatching(/Firefox/)
|
|
230
|
+
.waitForChannel()
|
|
231
|
+
.thenSend('hello Firefox');
|
|
232
|
+
|
|
233
|
+
const matchingPeer = await mockRTC.getMatchingPeer();
|
|
234
|
+
|
|
235
|
+
// Create a local data connection:
|
|
236
|
+
const localConn = new RTCPeerConnection();
|
|
237
|
+
|
|
238
|
+
const dataChannel = localConn.createDataChannel("dataChannel");
|
|
239
|
+
|
|
240
|
+
const messagePromise = new Promise((resolve) => {
|
|
241
|
+
dataChannel.addEventListener('message', ({ data }) => resolve(data));
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const localOffer = await localConn.createOffer();
|
|
245
|
+
await localConn.setLocalDescription(localOffer);
|
|
246
|
+
const { answer } = await matchingPeer.answerOffer(localOffer, {
|
|
247
|
+
connectionMetadata: {
|
|
248
|
+
userAgent: 'Mozilla/5.0 (Firefox/123)'
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
await localConn.setRemoteDescription(answer);
|
|
252
|
+
|
|
253
|
+
// Wait until the matching handler sends the configured message:
|
|
254
|
+
const receivedMessage = await messagePromise;
|
|
255
|
+
expect(receivedMessage).to.equal('hello Firefox');
|
|
256
|
+
});
|
|
68
257
|
});
|
|
@@ -525,7 +525,7 @@ describe("When proxying WebRTC traffic", () => {
|
|
|
525
525
|
expect(remoteFrame!.displayWidth).to.be.greaterThanOrEqual(320);
|
|
526
526
|
});
|
|
527
527
|
|
|
528
|
-
it("should include user-agent metadata when creating a hooked offer", async () => {
|
|
528
|
+
it("should include user-agent & URL metadata when creating a hooked offer", async () => {
|
|
529
529
|
const eventPromise = getDeferred<MockRTCEventData['peer-connected']>();
|
|
530
530
|
mockRTC.on('peer-connected', (peer) => eventPromise.resolve(peer));
|
|
531
531
|
|
|
@@ -555,9 +555,10 @@ describe("When proxying WebRTC traffic", () => {
|
|
|
555
555
|
// Check that the connection event automatically includes the user agent:
|
|
556
556
|
const connectEvent = await eventPromise;
|
|
557
557
|
expect(connectEvent.metadata.userAgent).to.equal(navigator.userAgent);
|
|
558
|
+
expect(connectEvent.metadata.sourceURL).to.equal(window.location.href);
|
|
558
559
|
});
|
|
559
560
|
|
|
560
|
-
it("should include user-agent metadata when creating a hooked answer", async () => {
|
|
561
|
+
it("should include user-agent & URL metadata when creating a hooked answer", async () => {
|
|
561
562
|
const eventPromise = getDeferred<MockRTCEventData['peer-connected']>();
|
|
562
563
|
mockRTC.on('peer-connected', (peer) => eventPromise.resolve(peer));
|
|
563
564
|
|
|
@@ -586,6 +587,7 @@ describe("When proxying WebRTC traffic", () => {
|
|
|
586
587
|
// Check that the connection event automatically includes the user agent:
|
|
587
588
|
const connectEvent = await eventPromise;
|
|
588
589
|
expect(connectEvent.metadata.userAgent).to.equal(navigator.userAgent);
|
|
590
|
+
expect(connectEvent.metadata.sourceURL).to.equal(window.location.href);
|
|
589
591
|
});
|
|
590
592
|
|
|
591
593
|
});
|
|
@@ -73,4 +73,55 @@ describe("Send steps", function () {
|
|
|
73
73
|
]);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
it("should be able to create a new data channel, and send a message there", async () => {
|
|
77
|
+
const mockPeer = await mockRTC.buildPeer()
|
|
78
|
+
.createDataChannel('new-channel')
|
|
79
|
+
.thenSend('Hello from new channel');
|
|
80
|
+
|
|
81
|
+
const localConnection = new RTCPeerConnection();
|
|
82
|
+
|
|
83
|
+
const messagePromise = new Promise((resolve) => {
|
|
84
|
+
localConnection.addEventListener('datachannel', ({ channel }) => {
|
|
85
|
+
expect(channel.label).to.equal('new-channel');
|
|
86
|
+
channel.addEventListener('message', ({ data }) => resolve(data));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const { offer, setAnswer } = await mockPeer.createOffer();
|
|
91
|
+
await localConnection.setRemoteDescription(offer);
|
|
92
|
+
const localAnswer = await localConnection.createAnswer();
|
|
93
|
+
await localConnection.setLocalDescription(localAnswer);
|
|
94
|
+
await setAnswer(localAnswer);
|
|
95
|
+
|
|
96
|
+
// Wait for a response:
|
|
97
|
+
const message = await messagePromise;
|
|
98
|
+
expect(message).to.equal('Hello from new channel');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should be able to send a Buffer-based message", async () => {
|
|
102
|
+
const mockPeer = await mockRTC.buildPeer()
|
|
103
|
+
.waitForChannel('dataChannel1')
|
|
104
|
+
.thenSend(Buffer.from('Hello from buffer'));
|
|
105
|
+
|
|
106
|
+
const localConnection = new RTCPeerConnection();
|
|
107
|
+
const dataChannel1 = localConnection.createDataChannel("dataChannel1");
|
|
108
|
+
|
|
109
|
+
const localOffer = await localConnection.createOffer();
|
|
110
|
+
await localConnection.setLocalDescription(localOffer);
|
|
111
|
+
const { answer } = await mockPeer.answerOffer(localOffer);
|
|
112
|
+
await localConnection.setRemoteDescription(answer);
|
|
113
|
+
|
|
114
|
+
// Wait for a response:
|
|
115
|
+
let messages: Array<any> = [];
|
|
116
|
+
dataChannel1.addEventListener('message', (event) => {
|
|
117
|
+
messages.push(Buffer.from(event.data)); // ArrayBuffer -> node Buffer
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
await waitForChannelClose(dataChannel1);
|
|
121
|
+
|
|
122
|
+
expect(messages.map(m => m.toString('utf8'))).to.deep.equal([
|
|
123
|
+
'Hello from buffer'
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
|
|
76
127
|
});
|