mockrtc 0.2.0 → 0.3.0

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.
Files changed (84) hide show
  1. package/.github/workflows/ci.yml +2 -2
  2. package/dist/client/mockrtc-admin-request-builder.d.ts +7 -1
  3. package/dist/client/mockrtc-admin-request-builder.js +30 -0
  4. package/dist/client/mockrtc-admin-request-builder.js.map +1 -1
  5. package/dist/client/mockrtc-client.d.ts +8 -4
  6. package/dist/client/mockrtc-client.js +21 -9
  7. package/dist/client/mockrtc-client.js.map +1 -1
  8. package/dist/handling/handler-builder.d.ts +16 -9
  9. package/dist/handling/handler-builder.js +11 -1
  10. package/dist/handling/handler-builder.js.map +1 -1
  11. package/dist/handling/handler-step-definitions.d.ts +40 -23
  12. package/dist/handling/handler-step-definitions.js +61 -19
  13. package/dist/handling/handler-step-definitions.js.map +1 -1
  14. package/dist/handling/handler-steps.d.ts +4 -1
  15. package/dist/handling/handler-steps.js +26 -16
  16. package/dist/handling/handler-steps.js.map +1 -1
  17. package/dist/main-browser.d.ts +1 -0
  18. package/dist/main-browser.js +2 -1
  19. package/dist/main-browser.js.map +1 -1
  20. package/dist/main.d.ts +5 -3
  21. package/dist/main.js +2 -1
  22. package/dist/main.js.map +1 -1
  23. package/dist/matching/matcher-definitions.d.ts +51 -0
  24. package/dist/matching/matcher-definitions.js +94 -0
  25. package/dist/matching/matcher-definitions.js.map +1 -0
  26. package/dist/matching/matchers.d.ts +27 -0
  27. package/dist/matching/matchers.js +87 -0
  28. package/dist/matching/matchers.js.map +1 -0
  29. package/dist/mockrtc-base.d.ts +16 -0
  30. package/dist/mockrtc-base.js +19 -0
  31. package/dist/mockrtc-base.js.map +1 -0
  32. package/dist/mockrtc-peer.d.ts +9 -2
  33. package/dist/mockrtc.d.ts +62 -0
  34. package/dist/mockrtc.js.map +1 -1
  35. package/dist/rule-builder.d.ts +86 -0
  36. package/dist/rule-builder.js +113 -0
  37. package/dist/rule-builder.js.map +1 -0
  38. package/dist/server/mockrtc-admin-plugin.js +18 -1
  39. package/dist/server/mockrtc-admin-plugin.js.map +1 -1
  40. package/dist/server/mockrtc-server-peer.d.ts +1 -1
  41. package/dist/server/mockrtc-server-peer.js +19 -7
  42. package/dist/server/mockrtc-server-peer.js.map +1 -1
  43. package/dist/server/mockrtc-server.d.ts +12 -5
  44. package/dist/server/mockrtc-server.js +53 -18
  45. package/dist/server/mockrtc-server.js.map +1 -1
  46. package/dist/webrtc/datachannel-stream.d.ts +2 -0
  47. package/dist/webrtc/datachannel-stream.js +15 -1
  48. package/dist/webrtc/datachannel-stream.js.map +1 -1
  49. package/dist/webrtc/mediatrack-stream.d.ts +2 -0
  50. package/dist/webrtc/mediatrack-stream.js +15 -1
  51. package/dist/webrtc/mediatrack-stream.js.map +1 -1
  52. package/dist/webrtc/mockrtc-connection.js +1 -1
  53. package/dist/webrtc/mockrtc-connection.js.map +1 -1
  54. package/dist/webrtc/rtc-connection.d.ts +6 -2
  55. package/dist/webrtc/rtc-connection.js +20 -20
  56. package/dist/webrtc/rtc-connection.js.map +1 -1
  57. package/dist/webrtc-hooks.js +8 -2
  58. package/dist/webrtc-hooks.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/client/mockrtc-admin-request-builder.ts +49 -1
  61. package/src/client/mockrtc-client.ts +28 -10
  62. package/src/handling/handler-builder.ts +22 -10
  63. package/src/handling/handler-step-definitions.ts +81 -23
  64. package/src/handling/handler-steps.ts +28 -15
  65. package/src/main-browser.ts +1 -0
  66. package/src/main.ts +5 -1
  67. package/src/matching/matcher-definitions.ts +109 -0
  68. package/src/matching/matchers.ts +118 -0
  69. package/src/mockrtc-base.ts +49 -0
  70. package/src/mockrtc-peer.ts +9 -2
  71. package/src/mockrtc.ts +72 -0
  72. package/src/rule-builder.ts +142 -0
  73. package/src/server/mockrtc-admin-plugin.ts +41 -3
  74. package/src/server/mockrtc-server-peer.ts +28 -18
  75. package/src/server/mockrtc-server.ts +71 -15
  76. package/src/webrtc/datachannel-stream.ts +15 -1
  77. package/src/webrtc/mediatrack-stream.ts +15 -1
  78. package/src/webrtc/mockrtc-connection.ts +1 -1
  79. package/src/webrtc/rtc-connection.ts +36 -19
  80. package/src/webrtc-hooks.ts +8 -2
  81. package/test/integration/events.spec.ts +3 -1
  82. package/test/integration/matching.spec.ts +189 -0
  83. package/test/integration/proxy.spec.ts +4 -2
  84. package/test/integration/send-steps.spec.ts +25 -0
@@ -20,76 +20,125 @@ export class WaitForDurationStepDefinition extends Serializable implements Handl
20
20
  readonly type = 'wait-for-duration';
21
21
 
22
22
  constructor(
23
- protected durationMs: number
23
+ public readonly durationMs: number
24
24
  ) {
25
25
  super();
26
26
  }
27
27
 
28
+ explain() {
29
+ return `wait for ${this.durationMs}ms`;
30
+ }
31
+
28
32
  }
29
33
 
30
34
  export class WaitForChannelStepDefinition extends Serializable implements HandlerStepDefinition {
31
35
 
32
- readonly type = 'wait-for-channel';
36
+ readonly type = 'wait-for-rtc-data-channel';
33
37
 
34
38
  constructor(
35
- protected channelLabel?: string
39
+ public readonly channelLabel?: string
36
40
  ) {
37
41
  super();
38
42
  }
43
+
44
+ explain() {
45
+ return `wait for an RTC channel${this.channelLabel ? ` labelled '${this.channelLabel}'` : ''}`;
46
+ }
47
+
39
48
  }
40
49
 
41
50
  export class WaitForMessageStepDefinition extends Serializable implements HandlerStepDefinition {
42
51
 
43
- readonly type = 'wait-for-message';
52
+ readonly type = 'wait-for-rtc-message';
44
53
 
45
54
  constructor(
46
- protected channelLabel?: string
55
+ public readonly channelLabel?: string
47
56
  ) {
48
57
  super();
49
58
  }
50
59
 
60
+ explain() {
61
+ return `wait for an RTC message${this.channelLabel ? ` on channel '${this.channelLabel}'` : ''}`;
62
+ }
63
+
51
64
  }
52
65
 
53
66
  export class WaitForTrackStepDefinition extends Serializable implements HandlerStepDefinition {
54
67
 
55
- readonly type = 'wait-for-track';
68
+ readonly type = 'wait-for-rtc-track';
69
+
70
+ explain() {
71
+ return `wait for an RTC track`;
72
+ }
56
73
 
57
74
  }
58
75
 
59
76
  export class WaitForMediaStepDefinition extends Serializable implements HandlerStepDefinition {
60
77
 
61
- readonly type = 'wait-for-media';
78
+ readonly type = 'wait-for-rtc-media';
79
+
80
+ explain() {
81
+ return `wait for RTC media data`;
82
+ }
83
+
84
+ }
85
+
86
+ export class CreateChannelStepDefinition extends Serializable implements HandlerStepDefinition {
87
+
88
+ readonly type = 'create-rtc-data-channel';
89
+
90
+ constructor(
91
+ public readonly channelLabel: string
92
+ ) {
93
+ super();
94
+ }
95
+
96
+ explain() {
97
+ return `create an RTC data channel labelled '${this.channelLabel}'`;
98
+ }
62
99
 
63
100
  }
64
101
 
65
102
  export class SendStepDefinition extends Serializable implements HandlerStepDefinition {
66
103
 
67
- readonly type = 'send-message';
104
+ readonly type = 'send-rtc-data-message';
68
105
 
69
106
  constructor(
70
- protected channelLabel: string | undefined,
71
- protected message: string | Buffer
107
+ public readonly channelLabel: string | undefined,
108
+ public readonly message: string | Buffer
72
109
  ) {
73
110
  super();
74
111
  }
75
112
 
113
+ explain() {
114
+ return `send an RTC data message${this.channelLabel ? ` on channel '${this.channelLabel}'` : ''}`;
115
+ }
116
+
76
117
  }
77
118
 
78
119
  export class CloseStepDefinition extends Serializable implements HandlerStepDefinition {
79
120
 
80
- readonly type = 'close-connection';
121
+ readonly type = 'close-rtc-connection';
122
+
123
+ explain() {
124
+ return `close the RTC connection`;
125
+ }
81
126
 
82
127
  }
83
128
 
84
129
  export class EchoStepDefinition extends Serializable implements HandlerStepDefinition {
85
130
 
86
- readonly type = 'echo-channels';
131
+ readonly type = 'echo-rtc';
132
+
133
+ explain() {
134
+ return `echo all RTC media & data`;
135
+ }
87
136
 
88
137
  }
89
138
 
90
139
  export class PeerProxyStepDefinition extends Serializable implements HandlerStepDefinition {
91
140
 
92
- readonly type = 'peer-proxy';
141
+ readonly type = 'rtc-peer-proxy';
93
142
 
94
143
  protected getAnswer: (offer: MockRTCSessionDescription) => Promise<RTCSessionDescriptionInit>;
95
144
 
@@ -111,6 +160,10 @@ export class PeerProxyStepDefinition extends Serializable implements HandlerStep
111
160
  }
112
161
  }
113
162
 
163
+ explain() {
164
+ return `proxy the RTC connection to the configured peer`;
165
+ }
166
+
114
167
  serialize(channel: ClientServerChannel): {} {
115
168
  channel.onRequest<
116
169
  { offer: MockRTCSessionDescription },
@@ -126,19 +179,24 @@ export class PeerProxyStepDefinition extends Serializable implements HandlerStep
126
179
 
127
180
  export class DynamicProxyStepDefinition extends Serializable implements HandlerStepDefinition {
128
181
 
129
- readonly type = 'dynamic-proxy';
182
+ readonly type = 'rtc-dynamic-proxy';
183
+
184
+ explain() {
185
+ return `proxy the RTC connection to a remote peer`;
186
+ }
130
187
 
131
188
  }
132
189
 
133
190
  export const StepDefinitionLookup = {
134
191
  'wait-for-duration': WaitForDurationStepDefinition,
135
- 'wait-for-channel': WaitForChannelStepDefinition,
136
- 'wait-for-track': WaitForTrackStepDefinition,
137
- 'wait-for-media': WaitForMediaStepDefinition,
138
- 'wait-for-message': WaitForMessageStepDefinition,
139
- 'send-message': SendStepDefinition,
140
- 'close-connection': CloseStepDefinition,
141
- 'echo-channels': EchoStepDefinition,
142
- 'peer-proxy': PeerProxyStepDefinition,
143
- 'dynamic-proxy': DynamicProxyStepDefinition
192
+ 'wait-for-rtc-data-channel': WaitForChannelStepDefinition,
193
+ 'wait-for-rtc-track': WaitForTrackStepDefinition,
194
+ 'wait-for-rtc-media': WaitForMediaStepDefinition,
195
+ 'wait-for-rtc-message': WaitForMessageStepDefinition,
196
+ 'create-rtc-data-channel': CreateChannelStepDefinition,
197
+ 'send-rtc-data-message': SendStepDefinition,
198
+ 'close-rtc-connection': CloseStepDefinition,
199
+ 'echo-rtc': EchoStepDefinition,
200
+ 'rtc-peer-proxy': PeerProxyStepDefinition,
201
+ 'rtc-dynamic-proxy': DynamicProxyStepDefinition
144
202
  };
@@ -17,6 +17,7 @@ import {
17
17
  EchoStepDefinition,
18
18
  HandlerStepDefinition,
19
19
  PeerProxyStepDefinition,
20
+ CreateChannelStepDefinition,
20
21
  SendStepDefinition,
21
22
  WaitForChannelStepDefinition,
22
23
  WaitForDurationStepDefinition,
@@ -70,7 +71,7 @@ export class WaitForMessageStep extends WaitForMessageStepDefinition {
70
71
  async handle(connection: MockRTCConnection): Promise<void> {
71
72
  return new Promise<void>((resolve) => {
72
73
  const messageReceived = () => {
73
- connection.removeListener('channel-open', listenForMessage);
74
+ connection.removeListener('channel-created', listenForMessage);
74
75
  connection.channels.forEach((channel) => {
75
76
  channel.removeListener('data', messageReceived);
76
77
  channel.pause();
@@ -85,7 +86,7 @@ export class WaitForMessageStep extends WaitForMessageStepDefinition {
85
86
  }
86
87
  }
87
88
 
88
- connection.on('channel-open', listenForMessage);
89
+ connection.on('channel-created', listenForMessage);
89
90
  connection.channels.forEach(listenForMessage);
90
91
  });
91
92
  }
@@ -108,7 +109,7 @@ export class WaitForMediaStep extends WaitForMediaStepDefinition {
108
109
  async handle(connection: MockRTCConnection): Promise<void> {
109
110
  return new Promise<void>((resolve) => {
110
111
  const messageReceived = () => {
111
- connection.removeListener('track-open', listenForData);
112
+ connection.removeListener('track-created', listenForData);
112
113
  connection.mediaTracks.forEach((track) => {
113
114
  track.removeListener('data', messageReceived);
114
115
  track.pause();
@@ -121,13 +122,24 @@ export class WaitForMediaStep extends WaitForMediaStepDefinition {
121
122
  track.once('data', messageReceived);
122
123
  }
123
124
 
124
- connection.on('track-open', listenForData);
125
+ connection.on('track-created', listenForData);
125
126
  connection.mediaTracks.forEach(listenForData);
126
127
  });
127
128
  }
128
129
 
129
130
  }
130
131
 
132
+ export class CreateChannelStep extends CreateChannelStepDefinition {
133
+
134
+ async handle(conn: MockRTCConnection): Promise<void> {
135
+ const channel = conn.createDataChannel(this.channelLabel);
136
+ return new Promise<void>((resolve) =>
137
+ channel.once('channel-open', resolve)
138
+ );
139
+ }
140
+
141
+ }
142
+
131
143
  export class SendStep extends SendStepDefinition {
132
144
 
133
145
  private matchesChannel(channel: DataChannelStream) {
@@ -166,8 +178,8 @@ export class EchoStep extends EchoStepDefinition {
166
178
  stream.pipe(stream);
167
179
  };
168
180
 
169
- connection.on('channel-open', echoContent);
170
- connection.on('track-open', echoContent);
181
+ connection.on('channel-created', echoContent);
182
+ connection.on('track-created', echoContent);
171
183
  connection.channels.forEach(echoContent);
172
184
  connection.mediaTracks.forEach(echoContent);
173
185
 
@@ -243,13 +255,14 @@ export class DynamicProxyStep extends DynamicProxyStepDefinition {
243
255
 
244
256
  export const StepLookup: typeof StepDefinitionLookup = {
245
257
  'wait-for-duration': WaitForDurationStep,
246
- 'wait-for-channel': WaitForChannelStep,
247
- 'wait-for-track': WaitForTrackStep,
248
- 'wait-for-media': WaitForMediaStep,
249
- 'wait-for-message': WaitForMessageStep,
250
- 'send-message': SendStep,
251
- 'close-connection': CloseStep,
252
- 'echo-channels': EchoStep,
253
- 'peer-proxy': PeerProxyStep,
254
- 'dynamic-proxy': DynamicProxyStep
258
+ 'wait-for-rtc-data-channel': WaitForChannelStep,
259
+ 'wait-for-rtc-track': WaitForTrackStep,
260
+ 'wait-for-rtc-media': WaitForMediaStep,
261
+ 'wait-for-rtc-message': WaitForMessageStep,
262
+ 'create-rtc-data-channel': CreateChannelStep,
263
+ 'send-rtc-data-message': SendStep,
264
+ 'close-rtc-connection': CloseStep,
265
+ 'echo-rtc': EchoStep,
266
+ 'rtc-peer-proxy': PeerProxyStep,
267
+ 'rtc-dynamic-proxy': DynamicProxyStep
255
268
  };
@@ -11,6 +11,7 @@ import { MockRTCClient, MockRTCClientOptions } from "./client/mockrtc-client";
11
11
 
12
12
  // Export the required structures to remotely build and send rules to the admin API:
13
13
  export * as HandlerStepDefinitions from "./handling/handler-step-definitions";
14
+ export * as MatcherDefinitions from "./matching/matcher-definitions";
14
15
  export { MockRTCAdminRequestBuilder } from "./client/mockrtc-admin-request-builder";
15
16
 
16
17
  export type {
package/src/main.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  MockRTCPeerBuilder,
10
10
  MockRTCEvent,
11
11
  MockRTCEventData,
12
+ MockRTCRuleDefinition,
12
13
  MockRTCSessionDescription,
13
14
  SelectedRTCCandidate,
14
15
  TimingEvents
@@ -22,16 +23,18 @@ import { MockRTCClient, MockRTCClientOptions } from "./client/mockrtc-client";
22
23
 
23
24
  // Export the required structures to remotely build and send rules to the admin API:
24
25
  export * as HandlerStepDefinitions from "./handling/handler-step-definitions";
26
+ export * as MatcherDefinitions from "./matching/matcher-definitions";
25
27
  export { MockRTCAdminRequestBuilder } from "./client/mockrtc-admin-request-builder";
26
28
 
27
29
  // Re-export lots of types are used in various APIs (mostly to make TypeDoc happy):
28
30
  export type { HandlerStep } from "./handling/handler-steps";
29
31
  export type { MockRTCHandlerBuilder } from "./handling/handler-builder";
32
+ export type { MockRTCRuleBuilder, RuleHandlerBuilder } from "./rule-builder";
30
33
 
31
34
  export type { MockRTCServerPeer } from "./server/mockrtc-server-peer";
32
35
  export type { SessionData } from "./server/mockrtc-admin-plugin";
33
36
 
34
- export type { RTCConnection } from "./webrtc/rtc-connection";
37
+ export type { RTCConnection, ParsedSDP } from "./webrtc/rtc-connection";
35
38
  export type { MockRTCConnection } from "./webrtc/mockrtc-connection";
36
39
  export type { DataChannelStream } from "./webrtc/datachannel-stream";
37
40
  export type { MediaTrackStream } from "./webrtc/mediatrack-stream";
@@ -46,6 +49,7 @@ export type {
46
49
  MockRTCAdminServer,
47
50
  MockRTCEvent,
48
51
  MockRTCEventData,
52
+ MockRTCRuleDefinition,
49
53
  MockRTCSessionDescription,
50
54
  SelectedRTCCandidate,
51
55
  TimingEvents
@@ -0,0 +1,109 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import * as PluggableAdmin from 'mockttp/dist/pluggable-admin-api/pluggable-admin.browser';
7
+
8
+ export type Serializable = PluggableAdmin.Serialization.Serializable;
9
+ export const { Serializable } = PluggableAdmin.Serialization;
10
+
11
+ export interface MatcherDefinition extends Serializable {
12
+ readonly type: keyof typeof MatcherDefinitionLookup;
13
+ }
14
+
15
+ export class HasDataChannelMatcherDefinition extends Serializable implements MatcherDefinition {
16
+ readonly type = 'has-rtc-data-channel';
17
+
18
+ explain() {
19
+ return `with a data channel`;
20
+ }
21
+ }
22
+
23
+ export class HasVideoTrackMatcherDefinition extends Serializable implements MatcherDefinition {
24
+ readonly type = 'has-rtc-video-track';
25
+
26
+ explain() {
27
+ return `with a video track`;
28
+ }
29
+ }
30
+
31
+ export class HasAudioTrackMatcherDefinition extends Serializable implements MatcherDefinition {
32
+ readonly type = 'has-rtc-audio-track';
33
+
34
+ explain() {
35
+ return `with an audio track`;
36
+ }
37
+ }
38
+
39
+ export class HasMediaTrackMatcherDefinition extends Serializable implements MatcherDefinition {
40
+ readonly type = 'has-rtc-media-track';
41
+
42
+ explain() {
43
+ return `with any media track`;
44
+ }
45
+ }
46
+
47
+ export class HostnameMatcherDefinition extends Serializable implements MatcherDefinition {
48
+
49
+ readonly type = 'rtc-page-hostname';
50
+
51
+ constructor(
52
+ public readonly hostname: string
53
+ ) {
54
+ super();
55
+ }
56
+
57
+ explain() {
58
+ return `from a page on ${this.hostname}`;
59
+ }
60
+
61
+ }
62
+
63
+ export class UrlRegexMatcherDefinition extends Serializable implements MatcherDefinition {
64
+
65
+ readonly type = 'rtc-page-regex';
66
+
67
+ readonly regexSource: string;
68
+ readonly regexFlags: string;
69
+
70
+ constructor(regex: RegExp) {
71
+ super();
72
+ this.regexSource = regex.source;
73
+ this.regexFlags = regex.flags;
74
+ }
75
+
76
+ explain() {
77
+ return `from a page with URL matching /${this.regexSource}/${this.regexFlags}`;
78
+ }
79
+
80
+ }
81
+
82
+ export class UserAgentRegexMatcherDefinition extends Serializable implements MatcherDefinition {
83
+
84
+ readonly type = 'rtc-user-agent-regex';
85
+
86
+ readonly regexSource: string;
87
+ readonly regexFlags: string;
88
+
89
+ constructor(regex: RegExp) {
90
+ super();
91
+ this.regexSource = regex.source;
92
+ this.regexFlags = regex.flags;
93
+ }
94
+
95
+ explain() {
96
+ return `from a user agent matching /${this.regexSource}/${this.regexFlags}`;
97
+ }
98
+
99
+ }
100
+
101
+ export const MatcherDefinitionLookup = {
102
+ 'has-rtc-data-channel': HasDataChannelMatcherDefinition,
103
+ 'has-rtc-video-track': HasVideoTrackMatcherDefinition,
104
+ 'has-rtc-audio-track': HasAudioTrackMatcherDefinition,
105
+ 'has-rtc-media-track': HasMediaTrackMatcherDefinition,
106
+ 'rtc-page-hostname': HostnameMatcherDefinition,
107
+ 'rtc-page-regex': UrlRegexMatcherDefinition,
108
+ 'rtc-user-agent-regex': UserAgentRegexMatcherDefinition
109
+ };
@@ -0,0 +1,118 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { RTCConnection } from "../webrtc/rtc-connection";
7
+ import {
8
+ MatcherDefinition,
9
+ MatcherDefinitionLookup,
10
+ HasAudioTrackMatcherDefinition,
11
+ HasDataChannelMatcherDefinition,
12
+ HasMediaTrackMatcherDefinition,
13
+ HasVideoTrackMatcherDefinition,
14
+ HostnameMatcherDefinition,
15
+ UrlRegexMatcherDefinition,
16
+ UserAgentRegexMatcherDefinition
17
+ } from "./matcher-definitions";
18
+
19
+ export interface Matcher extends MatcherDefinition {
20
+ matches(connection: RTCConnection): boolean;
21
+ }
22
+
23
+ export class HasDataChannelMatcher extends HasDataChannelMatcherDefinition {
24
+
25
+ matches(connection: RTCConnection): boolean {
26
+ return [
27
+ ...connection.getLocalDescription()!.parsedSdp.media,
28
+ ...connection.getRemoteDescription()!.parsedSdp.media
29
+ ].some(media => media.type === 'application');
30
+ }
31
+
32
+ }
33
+
34
+ export class HasVideoTrackMatcher extends HasVideoTrackMatcherDefinition {
35
+
36
+ matches(connection: RTCConnection): boolean {
37
+ return [
38
+ ...connection.getLocalDescription()!.parsedSdp.media,
39
+ ...connection.getRemoteDescription()!.parsedSdp.media
40
+ ].some(media => media.type === 'video');
41
+ }
42
+
43
+ }
44
+
45
+ export class HasAudioTrackMatcher extends HasAudioTrackMatcherDefinition {
46
+
47
+ matches(connection: RTCConnection): boolean {
48
+ return [
49
+ ...connection.getLocalDescription()!.parsedSdp.media,
50
+ ...connection.getRemoteDescription()!.parsedSdp.media
51
+ ].some(media => media.type === 'audio');
52
+ }
53
+
54
+ }
55
+
56
+ export class HasMediaTrackMatcher extends HasMediaTrackMatcherDefinition {
57
+
58
+ matches(connection: RTCConnection): boolean {
59
+ return [
60
+ ...connection.getLocalDescription()!.parsedSdp.media,
61
+ ...connection.getRemoteDescription()!.parsedSdp.media
62
+ ].some(media => media.type === 'video' || media.type === 'audio');
63
+ }
64
+
65
+ }
66
+
67
+ const getConnectionSourceURL = (connection: RTCConnection): URL | undefined => {
68
+ const { sourceURL } = connection.metadata;
69
+ if (!sourceURL) return;
70
+
71
+ try {
72
+ return new URL(sourceURL);
73
+ } catch (e) {
74
+ console.warn('Unparseable RTC source URL:', e);
75
+ return;
76
+ }
77
+ };
78
+
79
+ export class HostnameMatcher extends HostnameMatcherDefinition {
80
+
81
+ matches(connection: RTCConnection): boolean {
82
+ const url = getConnectionSourceURL(connection);
83
+ return url?.hostname === this.hostname;
84
+ }
85
+
86
+ }
87
+
88
+ export class UrlRegexMatcher extends UrlRegexMatcherDefinition {
89
+
90
+ matches(connection: RTCConnection): boolean {
91
+ const url = getConnectionSourceURL(connection);
92
+ return !!url?.toString().match(
93
+ new RegExp(this.regexSource, this.regexFlags)
94
+ );
95
+ }
96
+
97
+ }
98
+
99
+ export class UserAgentRegexMatcher extends UserAgentRegexMatcherDefinition {
100
+
101
+ matches(connection: RTCConnection): boolean {
102
+ const userAgent = connection.metadata.userAgent;
103
+ return !!userAgent?.match(
104
+ new RegExp(this.regexSource, this.regexFlags)
105
+ );
106
+ }
107
+
108
+ }
109
+
110
+ export const MatcherLookup: typeof MatcherDefinitionLookup = {
111
+ 'has-rtc-data-channel': HasDataChannelMatcher,
112
+ 'has-rtc-video-track': HasVideoTrackMatcher,
113
+ 'has-rtc-audio-track': HasAudioTrackMatcher,
114
+ 'has-rtc-media-track': HasMediaTrackMatcher,
115
+ 'rtc-page-hostname': HostnameMatcher,
116
+ 'rtc-page-regex': UrlRegexMatcher,
117
+ 'rtc-user-agent-regex': UserAgentRegexMatcher
118
+ };
@@ -0,0 +1,49 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import {
7
+ MockRTC,
8
+ MockRTCEventData,
9
+ MockRTCPeerBuilder,
10
+ MockRTCRuleDefinition
11
+ } from "./mockrtc";
12
+ import { MockRTCPeer } from "./mockrtc-peer";
13
+ import { MockRTCHandlerBuilder } from "./handling/handler-builder";
14
+ import { HandlerStepDefinition } from "./handling/handler-step-definitions";
15
+ import { MatcherDefinition } from "./matching/matcher-definitions";
16
+ import { MockRTCRuleBuilder } from "./rule-builder";
17
+
18
+ export abstract class MockRTCBase implements MockRTC {
19
+
20
+ abstract getMatchingPeer(): MockRTCPeer;
21
+ abstract start(): Promise<void>;
22
+ abstract stop(): Promise<void>;
23
+ abstract on<E extends keyof MockRTCEventData>(
24
+ event: E,
25
+ callback: (param: MockRTCEventData[E]) => void
26
+ ): Promise<void>;
27
+
28
+ buildPeer(): MockRTCPeerBuilder {
29
+ return new MockRTCHandlerBuilder(this.buildPeerFromDefinition.bind(this));
30
+ }
31
+
32
+ abstract buildPeerFromDefinition(
33
+ handlerStepDefinitions: HandlerStepDefinition[]
34
+ ): Promise<MockRTCPeer>;
35
+
36
+ forConnections(): MockRTCRuleBuilder {
37
+ return new MockRTCRuleBuilder(this.addRuleFromDefinition.bind(this));
38
+ }
39
+
40
+ abstract addRuleFromDefinition(
41
+ matcherDefinitions: MatcherDefinition[],
42
+ handlerStepDefinitions: HandlerStepDefinition[]
43
+ ): Promise<void>;
44
+
45
+ abstract setRulesFromDefinitions(
46
+ ruleDefinitions: Array<MockRTCRuleDefinition>
47
+ ): Promise<void>;
48
+
49
+ }
@@ -189,11 +189,18 @@ export interface AnswerOptions {
189
189
  * Extra metadata to associate with the connection. This will be exposed on
190
190
  * events like peer-connected, and can be used to add context to connections.
191
191
  *
192
- * The only defined value is 'userAgent' which must be a client user agent
193
- * string (in a browser, the navigator.userAgent value) if defined.
192
+ * The defined fields may only be used as defined here, but all values are
193
+ * optional, and any other metadata may be attached in any format here.
194
+ *
195
+ * The only defined values are:
196
+ * - `userAgent` - a client user-agent string (in a browser, the value of
197
+ * `navigator.userAgent`)
198
+ * - `sourceURL` - the URL of the referring page, when the request is sent by
199
+ * a browser
194
200
  */
195
201
  export interface ConnectionMetadata {
196
202
  userAgent?: string;
203
+ sourceURL?: string;
197
204
  [k: string]: any;
198
205
  }
199
206