mockrtc 0.1.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 (136) hide show
  1. package/.github/workflows/ci.yml +29 -0
  2. package/LICENSE +201 -0
  3. package/README.md +290 -0
  4. package/dist/admin-bin.d.ts +2 -0
  5. package/dist/admin-bin.js +67 -0
  6. package/dist/admin-bin.js.map +1 -0
  7. package/dist/client/mockrtc-client.d.ts +12 -0
  8. package/dist/client/mockrtc-client.js +67 -0
  9. package/dist/client/mockrtc-client.js.map +1 -0
  10. package/dist/client/mockrtc-remote-peer.d.ts +15 -0
  11. package/dist/client/mockrtc-remote-peer.js +246 -0
  12. package/dist/client/mockrtc-remote-peer.js.map +1 -0
  13. package/dist/control-channel.d.ts +8 -0
  14. package/dist/control-channel.js +11 -0
  15. package/dist/control-channel.js.map +1 -0
  16. package/dist/handling/handler-builder.d.ts +138 -0
  17. package/dist/handling/handler-builder.js +164 -0
  18. package/dist/handling/handler-builder.js.map +1 -0
  19. package/dist/handling/handler-step-definitions.d.ts +63 -0
  20. package/dist/handling/handler-step-definitions.js +123 -0
  21. package/dist/handling/handler-step-definitions.js.map +1 -0
  22. package/dist/handling/handler-steps.d.ts +48 -0
  23. package/dist/handling/handler-steps.js +218 -0
  24. package/dist/handling/handler-steps.js.map +1 -0
  25. package/dist/main-browser.d.ts +9 -0
  26. package/dist/main-browser.js +26 -0
  27. package/dist/main-browser.js.map +1 -0
  28. package/dist/main.d.ts +58 -0
  29. package/dist/main.js +67 -0
  30. package/dist/main.js.map +1 -0
  31. package/dist/mockrtc-admin-plugin.d.ts +56 -0
  32. package/dist/mockrtc-admin-plugin.js +151 -0
  33. package/dist/mockrtc-admin-plugin.js.map +1 -0
  34. package/dist/mockrtc-admin-server.d.ts +7 -0
  35. package/dist/mockrtc-admin-server.js +18 -0
  36. package/dist/mockrtc-admin-server.js.map +1 -0
  37. package/dist/mockrtc-client.d.ts +12 -0
  38. package/dist/mockrtc-client.js +64 -0
  39. package/dist/mockrtc-client.js.map +1 -0
  40. package/dist/mockrtc-handler-builder.d.ts +15 -0
  41. package/dist/mockrtc-handler-builder.js +24 -0
  42. package/dist/mockrtc-handler-builder.js.map +1 -0
  43. package/dist/mockrtc-peer.d.ts +147 -0
  44. package/dist/mockrtc-peer.js +7 -0
  45. package/dist/mockrtc-peer.js.map +1 -0
  46. package/dist/mockrtc-remote-peer.d.ts +15 -0
  47. package/dist/mockrtc-remote-peer.js +234 -0
  48. package/dist/mockrtc-remote-peer.js.map +1 -0
  49. package/dist/mockrtc-server-peer.d.ts +29 -0
  50. package/dist/mockrtc-server-peer.js +145 -0
  51. package/dist/mockrtc-server-peer.js.map +1 -0
  52. package/dist/mockrtc-server.d.ts +14 -0
  53. package/dist/mockrtc-server.js +53 -0
  54. package/dist/mockrtc-server.js.map +1 -0
  55. package/dist/mockrtc.d.ts +25 -0
  56. package/dist/mockrtc.js +7 -0
  57. package/dist/mockrtc.js.map +1 -0
  58. package/dist/package.json +52 -0
  59. package/dist/server/mockrtc-admin-plugin.d.ts +17 -0
  60. package/dist/server/mockrtc-admin-plugin.js +163 -0
  61. package/dist/server/mockrtc-admin-plugin.js.map +1 -0
  62. package/dist/server/mockrtc-admin-server.d.ts +7 -0
  63. package/dist/server/mockrtc-admin-server.js +18 -0
  64. package/dist/server/mockrtc-admin-server.js.map +1 -0
  65. package/dist/server/mockrtc-server-peer.d.ts +24 -0
  66. package/dist/server/mockrtc-server-peer.js +141 -0
  67. package/dist/server/mockrtc-server-peer.js.map +1 -0
  68. package/dist/server/mockrtc-server.d.ts +14 -0
  69. package/dist/server/mockrtc-server.js +53 -0
  70. package/dist/server/mockrtc-server.js.map +1 -0
  71. package/dist/src/main.d.ts +1 -0
  72. package/dist/src/main.js +24 -0
  73. package/dist/src/main.js.map +1 -0
  74. package/dist/src/mockrtc-peer.d.ts +0 -0
  75. package/dist/src/mockrtc-peer.js +2 -0
  76. package/dist/src/mockrtc-peer.js.map +1 -0
  77. package/dist/src/mockrtc.d.ts +0 -0
  78. package/dist/src/mockrtc.js +65 -0
  79. package/dist/src/mockrtc.js.map +1 -0
  80. package/dist/webrtc/control-channel.d.ts +8 -0
  81. package/dist/webrtc/control-channel.js +11 -0
  82. package/dist/webrtc/control-channel.js.map +1 -0
  83. package/dist/webrtc/datachannel-stream.d.ts +25 -0
  84. package/dist/webrtc/datachannel-stream.js +86 -0
  85. package/dist/webrtc/datachannel-stream.js.map +1 -0
  86. package/dist/webrtc/mediatrack-stream.d.ts +29 -0
  87. package/dist/webrtc/mediatrack-stream.js +109 -0
  88. package/dist/webrtc/mediatrack-stream.js.map +1 -0
  89. package/dist/webrtc/mockrtc-connection.d.ts +14 -0
  90. package/dist/webrtc/mockrtc-connection.js +147 -0
  91. package/dist/webrtc/mockrtc-connection.js.map +1 -0
  92. package/dist/webrtc/peer-connection.d.ts +16 -0
  93. package/dist/webrtc/peer-connection.js +81 -0
  94. package/dist/webrtc/peer-connection.js.map +1 -0
  95. package/dist/webrtc/rtc-connection.d.ts +47 -0
  96. package/dist/webrtc/rtc-connection.js +370 -0
  97. package/dist/webrtc/rtc-connection.js.map +1 -0
  98. package/dist/webrtc-hooks.d.ts +30 -0
  99. package/dist/webrtc-hooks.js +224 -0
  100. package/dist/webrtc-hooks.js.map +1 -0
  101. package/karma.conf.ts +89 -0
  102. package/ngi-eu-footer.png +0 -0
  103. package/package.json +86 -0
  104. package/src/admin-bin.ts +57 -0
  105. package/src/client/mockrtc-client.ts +79 -0
  106. package/src/client/mockrtc-remote-peer.ts +286 -0
  107. package/src/handling/handler-builder.ts +215 -0
  108. package/src/handling/handler-step-definitions.ts +142 -0
  109. package/src/handling/handler-steps.ts +254 -0
  110. package/src/main-browser.ts +44 -0
  111. package/src/main.ts +109 -0
  112. package/src/mockrtc-peer.ts +176 -0
  113. package/src/mockrtc.ts +36 -0
  114. package/src/server/mockrtc-admin-plugin.ts +196 -0
  115. package/src/server/mockrtc-admin-server.ts +17 -0
  116. package/src/server/mockrtc-server-peer.ts +159 -0
  117. package/src/server/mockrtc-server.ts +53 -0
  118. package/src/webrtc/control-channel.ts +13 -0
  119. package/src/webrtc/datachannel-stream.ts +102 -0
  120. package/src/webrtc/mediatrack-stream.ts +135 -0
  121. package/src/webrtc/mockrtc-connection.ts +164 -0
  122. package/src/webrtc/rtc-connection.ts +420 -0
  123. package/src/webrtc-hooks.ts +245 -0
  124. package/test/integration/close-steps.spec.ts +39 -0
  125. package/test/integration/connection-setup.spec.ts +230 -0
  126. package/test/integration/echo-steps.spec.ts +88 -0
  127. package/test/integration/proxy.spec.ts +526 -0
  128. package/test/integration/send-steps.spec.ts +76 -0
  129. package/test/integration/smoke-test.spec.ts +100 -0
  130. package/test/integration/wait-steps.spec.ts +225 -0
  131. package/test/start-test-admin-server.ts +12 -0
  132. package/test/test-setup.ts +136 -0
  133. package/test/tsconfig.json +11 -0
  134. package/tsconfig.json +14 -0
  135. package/typedoc.json +19 -0
  136. package/wallaby.js +41 -0
@@ -0,0 +1,215 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import {
7
+ type HandlerStepDefinition,
8
+ PeerProxyStepDefinition,
9
+ SendStepDefinition,
10
+ DynamicProxyStepDefinition,
11
+ WaitForChannelStepDefinition,
12
+ WaitForMessageStepDefinition,
13
+ WaitForDurationStepDefinition,
14
+ CloseStepDefinition,
15
+ EchoStepDefinition,
16
+ WaitForTrackStepDefinition,
17
+ WaitForMediaStepDefinition
18
+ } from "./handler-step-definitions";
19
+
20
+ /**
21
+ * The builder logic for composing RTC handling behaviour for both mock peers and rules,
22
+ * by internally queuing defined actions until a `.thenX()` method is called to compile
23
+ * the actions into either a peer or a rule (handled by an constructor callback param).
24
+ */
25
+ export class MockRTCHandlerBuilder<R> {
26
+
27
+ private handlerSteps: HandlerStepDefinition[] = [];
28
+
29
+ constructor(
30
+ private buildCallback: (handlerSteps: HandlerStepDefinition[]) => Promise<R>
31
+ ) {}
32
+
33
+ /**
34
+ * Wait for a given duration, in milliseconds
35
+ *
36
+ * @category Steps
37
+ */
38
+ sleep(duration: number): this {
39
+ this.handlerSteps.push(new WaitForDurationStepDefinition(duration));
40
+ return this;
41
+ }
42
+
43
+ /**
44
+ * Wait until the remote client has created at least one DataChannel.
45
+ *
46
+ * @category Steps
47
+ */
48
+ waitForChannel(channelLabel?: string): this {
49
+ this.handlerSteps.push(new WaitForChannelStepDefinition(channelLabel));
50
+ return this;
51
+ }
52
+
53
+ /**
54
+ * Wait until the remote client has created at least one media track
55
+ *
56
+ * @category Steps
57
+ */
58
+ waitForTrack(): this {
59
+ this.handlerSteps.push(new WaitForTrackStepDefinition());
60
+ return this;
61
+ }
62
+
63
+ /**
64
+ * Wait until the remote client next sends a message to us on any DataChannel.
65
+ *
66
+ * This looks for new messages, ignoring any messages already consumed by
67
+ * previous steps.
68
+ *
69
+ * @category Steps
70
+ */
71
+ waitForNextMessage(): this {
72
+ this.handlerSteps.push(new WaitForMessageStepDefinition());
73
+ return this;
74
+ }
75
+
76
+ /**
77
+ * Wait until the remote client next sends media data on a media track.
78
+ *
79
+ * This waits for new media, ignoring any media already consumed by previous steps.
80
+ *
81
+ * @category Steps
82
+ */
83
+ waitForNextMedia(): this {
84
+ this.handlerSteps.push(new WaitForMediaStepDefinition());
85
+ return this;
86
+ }
87
+
88
+ /**
89
+ * Wait until the remote client sends a message to us on a specific DataChannel.
90
+ *
91
+ * This looks for new messages, ignoring any messages already consumed by
92
+ * previous steps.
93
+ *
94
+ * @category Steps
95
+ */
96
+ waitForNextMessageOnChannel(channelLabel: string): this {
97
+ this.handlerSteps.push(new WaitForMessageStepDefinition(channelLabel));
98
+ return this;
99
+ }
100
+
101
+ /**
102
+ * Send a message or buffer on the connection's data channels.
103
+ *
104
+ * This can take one or two arguments. If only one is provided, it is used
105
+ * as a message that's sent on all open data channels. If two arguments are
106
+ * provided, the first must be the data channel label, and the message (the
107
+ * second) will be sent only to data channel(s) with that label.
108
+ *
109
+ * If no matching channels are open, this is a no-op. Use `waitForChannel()`
110
+ * to ensure the channels you're expecting are open first if necessary.
111
+ *
112
+ * @category Steps
113
+ */
114
+ send(message: string | Buffer): this;
115
+ send(channel: string | undefined, message: string | Buffer): this;
116
+ send(...args: [string | undefined, string | Buffer] | [string | Buffer]): this {
117
+ if (args[1] !== undefined) {
118
+ const [channel, message] = args as [string, string | Buffer];
119
+ this.handlerSteps.push(new SendStepDefinition(channel, message));
120
+ } else {
121
+ const [message] = args as [string | Buffer];
122
+ this.handlerSteps.push(new SendStepDefinition(undefined, message));
123
+ }
124
+ return this;
125
+ }
126
+
127
+ /**
128
+ * Immediately close the connection.
129
+ *
130
+ * This defines a final step, and will then create a mock peer from the full
131
+ * set of steps you've defined, and return it wrapped in a promise. As soon
132
+ * as the promise resolves the peer is ready to use.
133
+ *
134
+ * @category Final Steps
135
+ */
136
+ thenClose(): Promise<R> {
137
+ this.handlerSteps.push(new CloseStepDefinition());
138
+ return this.buildCallback(this.handlerSteps);
139
+ }
140
+
141
+ /**
142
+ * Send a message or buffer on the connection's data channels, then close the
143
+ * connection. This is equivalent to {@link send `.send()`} then
144
+ * {@link thenClose `.thenClose()`}.
145
+ *
146
+ * This defines a final step, and will then create a mock peer from the full
147
+ * set of steps you've defined, and return it wrapped in a promise. As soon
148
+ * as the promise resolves the peer is ready to use.
149
+ *
150
+ * @category Final Steps
151
+ */
152
+ thenSend(message: string | Buffer): Promise<R>;
153
+ thenSend(channel: string, message: string | Buffer): Promise<R>;
154
+ thenSend(...args: [string, string | Buffer] | [string | Buffer]): Promise<R> {
155
+ return this.send(...args as [string | undefined, string | Buffer])
156
+ .buildCallback(this.handlerSteps);
157
+ }
158
+
159
+ /**
160
+ * Echo all incoming data channel messages until the other peer closes the
161
+ * connection.
162
+ *
163
+ * This defines a final step, and will then create a mock peer from the full
164
+ * set of steps you've defined, and return it wrapped in a promise. As soon
165
+ * as the promise resolves the peer is ready to use.
166
+ *
167
+ * @category Final Steps
168
+ */
169
+ thenEcho(): Promise<R> {
170
+ this.handlerSteps.push(new EchoStepDefinition());
171
+ return this.buildCallback(this.handlerSteps);
172
+ }
173
+
174
+ /**
175
+ * Creates a new external connection to the given remote peer connection,
176
+ * matching the existing mocked connection, and then proxies all traffic
177
+ * through to that peer.
178
+ *
179
+ * This defines a final step, and will then create a mock peer from the full
180
+ * set of steps you've defined, and return it wrapped in a promise. As soon
181
+ * as the promise resolves the peer is ready to use.
182
+ *
183
+ * @category Final Steps
184
+ */
185
+ thenForwardTo(peer: RTCPeerConnection): Promise<R> {
186
+ this.handlerSteps.push(new PeerProxyStepDefinition(peer));
187
+ return this.buildCallback(this.handlerSteps);
188
+ }
189
+
190
+ /**
191
+ * Proxy this connection dynamically to the 'real' target peer, whoever
192
+ * that may be.
193
+ *
194
+ * This assumes that you have an existing external connection already
195
+ * set up and attached to this mock connection.
196
+ *
197
+ * You can do that either by using {@link hookWebRTCConnection} or
198
+ * {@link hookAllWebRTC} to hook your connection during normal setup to
199
+ * automatically create an external offer to the real remote peer, or you can
200
+ * do so manually using {@link MockRTCPeer.createExternalOffer} or
201
+ * {@link MockRTCPeer.answerExternalOffer} and then passing the connection
202
+ * id as {@link https://github.com/httptoolkit/mockrtc/blob/d0604f3111e0438c52aa514d00cf04ac0718dfeb/src/webrtc-hooks.ts#L83-L93 here}.
203
+ *
204
+ * This defines a final step, and will then create a mock peer from the full
205
+ * set of steps you've defined, and return it wrapped in a promise. As soon
206
+ * as the promise resolves the peer is ready to use.
207
+ *
208
+ * @category Final Steps
209
+ */
210
+ thenPassThrough(): Promise<R> {
211
+ this.handlerSteps.push(new DynamicProxyStepDefinition());
212
+ return this.buildCallback(this.handlerSteps);
213
+ }
214
+
215
+ }
@@ -0,0 +1,142 @@
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
+ type ClientServerChannel = PluggableAdmin.Serialization.ClientServerChannel;
11
+
12
+ export interface HandlerStepDefinition extends Serializable {
13
+ readonly type: keyof typeof StepDefinitionLookup;
14
+ }
15
+
16
+ export class WaitForDurationStepDefinition extends Serializable implements HandlerStepDefinition {
17
+
18
+ readonly type = 'wait-for-duration';
19
+
20
+ constructor(
21
+ protected durationMs: number
22
+ ) {
23
+ super();
24
+ }
25
+
26
+ }
27
+
28
+ export class WaitForChannelStepDefinition extends Serializable implements HandlerStepDefinition {
29
+
30
+ readonly type = 'wait-for-channel';
31
+
32
+ constructor(
33
+ protected channelLabel?: string
34
+ ) {
35
+ super();
36
+ }
37
+ }
38
+
39
+ export class WaitForMessageStepDefinition extends Serializable implements HandlerStepDefinition {
40
+
41
+ readonly type = 'wait-for-message';
42
+
43
+ constructor(
44
+ protected channelLabel?: string
45
+ ) {
46
+ super();
47
+ }
48
+
49
+ }
50
+
51
+ export class WaitForTrackStepDefinition extends Serializable implements HandlerStepDefinition {
52
+
53
+ readonly type = 'wait-for-track';
54
+
55
+ }
56
+
57
+ export class WaitForMediaStepDefinition extends Serializable implements HandlerStepDefinition {
58
+
59
+ readonly type = 'wait-for-media';
60
+
61
+ }
62
+
63
+ export class SendStepDefinition extends Serializable implements HandlerStepDefinition {
64
+
65
+ readonly type = 'send-message';
66
+
67
+ constructor(
68
+ protected channelLabel: string | undefined,
69
+ protected message: string | Buffer
70
+ ) {
71
+ super();
72
+ }
73
+
74
+ }
75
+
76
+ export class CloseStepDefinition extends Serializable implements HandlerStepDefinition {
77
+
78
+ readonly type = 'close-connection';
79
+
80
+ }
81
+
82
+ export class EchoStepDefinition extends Serializable implements HandlerStepDefinition {
83
+
84
+ readonly type = 'echo-channels';
85
+
86
+ }
87
+
88
+ export class PeerProxyStepDefinition extends Serializable implements HandlerStepDefinition {
89
+
90
+ readonly type = 'peer-proxy';
91
+
92
+ protected getAnswer: (offer: RTCSessionDescriptionInit) => Promise<RTCSessionDescriptionInit>;
93
+
94
+ constructor(
95
+ connectionTarget:
96
+ | RTCPeerConnection
97
+ | ((offer: RTCSessionDescriptionInit) => Promise<RTCSessionDescriptionInit>)
98
+ ) {
99
+ super();
100
+ if (connectionTarget instanceof Function) {
101
+ this.getAnswer = connectionTarget;
102
+ } else {
103
+ this.getAnswer = async (offer: RTCSessionDescriptionInit) => {
104
+ await connectionTarget.setRemoteDescription(offer);
105
+ const answer = await connectionTarget.createAnswer();
106
+ await connectionTarget.setLocalDescription(answer);
107
+ return answer;
108
+ };
109
+ }
110
+ }
111
+
112
+ serialize(channel: ClientServerChannel): {} {
113
+ channel.onRequest<
114
+ { offer: RTCSessionDescriptionInit },
115
+ { answer: RTCSessionDescriptionInit }
116
+ >(async (msg) => {
117
+ return { answer: await this.getAnswer(msg.offer) };
118
+ });
119
+
120
+ return { type: this.type };
121
+ }
122
+
123
+ }
124
+
125
+ export class DynamicProxyStepDefinition extends Serializable implements HandlerStepDefinition {
126
+
127
+ readonly type = 'dynamic-proxy';
128
+
129
+ }
130
+
131
+ export const StepDefinitionLookup = {
132
+ 'wait-for-duration': WaitForDurationStepDefinition,
133
+ 'wait-for-channel': WaitForChannelStepDefinition,
134
+ 'wait-for-track': WaitForTrackStepDefinition,
135
+ 'wait-for-media': WaitForMediaStepDefinition,
136
+ 'wait-for-message': WaitForMessageStepDefinition,
137
+ 'send-message': SendStepDefinition,
138
+ 'close-connection': CloseStepDefinition,
139
+ 'echo-channels': EchoStepDefinition,
140
+ 'peer-proxy': PeerProxyStepDefinition,
141
+ 'dynamic-proxy': DynamicProxyStepDefinition
142
+ };
@@ -0,0 +1,254 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { PluggableAdmin } from 'mockttp';
7
+
8
+ import type { DataChannelStream } from '../webrtc/datachannel-stream';
9
+ import type { MediaTrackStream } from '../webrtc/mediatrack-stream';
10
+ import type { MockRTCConnection } from '../webrtc/mockrtc-connection';
11
+ import { RTCConnection } from '../webrtc/rtc-connection';
12
+ import {
13
+ StepDefinitionLookup,
14
+ CloseStepDefinition,
15
+ DynamicProxyStepDefinition,
16
+ EchoStepDefinition,
17
+ HandlerStepDefinition,
18
+ PeerProxyStepDefinition,
19
+ SendStepDefinition,
20
+ WaitForChannelStepDefinition,
21
+ WaitForDurationStepDefinition,
22
+ WaitForMediaStepDefinition,
23
+ WaitForMessageStepDefinition,
24
+ WaitForTrackStepDefinition
25
+ } from './handler-step-definitions';
26
+
27
+ type ClientServerChannel = PluggableAdmin.Serialization.ClientServerChannel;
28
+
29
+ export interface HandlerStep extends HandlerStepDefinition {
30
+ handle(connection: MockRTCConnection): Promise<void>;
31
+ }
32
+
33
+ export class WaitForDurationStep extends WaitForDurationStepDefinition {
34
+
35
+ async handle(): Promise<void> {
36
+ return new Promise<void>((resolve) => setTimeout(resolve, this.durationMs));
37
+ }
38
+
39
+ }
40
+
41
+ export class WaitForChannelStep extends WaitForChannelStepDefinition {
42
+
43
+ private matchesChannel(channel: DataChannelStream) {
44
+ return this.channelLabel === undefined || this.channelLabel === channel.label;
45
+ }
46
+
47
+ async handle(connection: MockRTCConnection): Promise<void> {
48
+ return new Promise<void>((resolve) => {
49
+ const channelOpened = (channel: DataChannelStream) => {
50
+ if (this.matchesChannel(channel)) {
51
+ connection.removeListener('remote-channel-open', channelOpened);
52
+ resolve();
53
+ }
54
+ };
55
+
56
+ connection.on('remote-channel-open', channelOpened);
57
+ connection.remoteChannels.forEach(channelOpened);
58
+ });
59
+ }
60
+
61
+ }
62
+
63
+ export class WaitForMessageStep extends WaitForMessageStepDefinition {
64
+
65
+ private matchesChannel(channel: DataChannelStream) {
66
+ return this.channelLabel === undefined || this.channelLabel === channel.label;
67
+ }
68
+
69
+ async handle(connection: MockRTCConnection): Promise<void> {
70
+ return new Promise<void>((resolve) => {
71
+ const messageReceived = () => {
72
+ connection.removeListener('channel-open', listenForMessage);
73
+ connection.channels.forEach((channel) => {
74
+ channel.removeListener('data', messageReceived);
75
+ channel.pause();
76
+ });
77
+
78
+ resolve();
79
+ };
80
+
81
+ const listenForMessage = (channel: DataChannelStream) => {
82
+ if (this.matchesChannel(channel)) {
83
+ channel.once('data', messageReceived);
84
+ }
85
+ }
86
+
87
+ connection.on('channel-open', listenForMessage);
88
+ connection.channels.forEach(listenForMessage);
89
+ });
90
+ }
91
+
92
+ }
93
+
94
+ export class WaitForTrackStep extends WaitForTrackStepDefinition {
95
+
96
+ async handle(connection: MockRTCConnection): Promise<void> {
97
+ await new Promise<void>((resolve) => {
98
+ if (connection.remoteMediaTracks.length) resolve();
99
+ else connection.once('remote-track-open', () => resolve());
100
+ });
101
+ }
102
+
103
+ }
104
+
105
+ export class WaitForMediaStep extends WaitForMediaStepDefinition {
106
+
107
+ async handle(connection: MockRTCConnection): Promise<void> {
108
+ return new Promise<void>((resolve) => {
109
+ const messageReceived = () => {
110
+ connection.removeListener('track-open', listenForData);
111
+ connection.mediaTracks.forEach((track) => {
112
+ track.removeListener('data', messageReceived);
113
+ track.pause();
114
+ });
115
+
116
+ resolve();
117
+ };
118
+
119
+ const listenForData = (track: MediaTrackStream) => {
120
+ track.once('data', messageReceived);
121
+ }
122
+
123
+ connection.on('track-open', listenForData);
124
+ connection.mediaTracks.forEach(listenForData);
125
+ });
126
+ }
127
+
128
+ }
129
+
130
+ export class SendStep extends SendStepDefinition {
131
+
132
+ private matchesChannel(channel: DataChannelStream) {
133
+ return this.channelLabel === undefined || this.channelLabel === channel.label;
134
+ }
135
+
136
+ async handle({ channels }: MockRTCConnection): Promise<void> {
137
+ await Promise.all(
138
+ channels
139
+ .filter((channel) => this.matchesChannel(channel))
140
+ .map((channel) => {
141
+ return new Promise<void>((resolve, reject) => {
142
+ channel.write(this.message, (error: Error | null | undefined) => {
143
+ if (error) reject(error);
144
+ else resolve();
145
+ });
146
+ });
147
+ })
148
+ );
149
+ }
150
+
151
+ }
152
+
153
+ export class CloseStep extends CloseStepDefinition {
154
+
155
+ async handle(connection: MockRTCConnection): Promise<void> {
156
+ await connection.close();
157
+ }
158
+
159
+ }
160
+
161
+ export class EchoStep extends EchoStepDefinition {
162
+
163
+ async handle(connection: MockRTCConnection): Promise<void> {
164
+ const echoContent = (stream: DataChannelStream | MediaTrackStream) => {
165
+ stream.pipe(stream);
166
+ };
167
+
168
+ connection.on('channel-open', echoContent);
169
+ connection.on('track-open', echoContent);
170
+ connection.channels.forEach(echoContent);
171
+ connection.mediaTracks.forEach(echoContent);
172
+
173
+ // This step keeps running indefinitely, until the connection closes
174
+ return new Promise<void>((resolve) => connection.on('connection-closed', resolve));
175
+ }
176
+
177
+ }
178
+
179
+ export class PeerProxyStep extends PeerProxyStepDefinition {
180
+
181
+ private externalConnections: RTCConnection[] = [];
182
+
183
+ async handle(connection: MockRTCConnection) {
184
+ const externalConn = new RTCConnection();
185
+ this.externalConnections.push(externalConn);
186
+
187
+ // We mirror the internal peer's SDP as an offer to the given connection:
188
+ const externalOffer = await externalConn.getMirroredLocalOffer(
189
+ connection.getRemoteDescription()!.sdp!
190
+ );
191
+ externalConn.setRemoteDescription(await this.getAnswer(externalOffer));
192
+
193
+ connection.proxyTrafficTo(externalConn);
194
+
195
+ // This step keeps running indefinitely, until the connection closes
196
+ return new Promise<void>((resolve) => connection.on('connection-closed', resolve));
197
+ }
198
+
199
+ serialize(channel: ClientServerChannel): {} {
200
+ channel.onRequest<
201
+ { offer: RTCSessionDescriptionInit },
202
+ { answer: RTCSessionDescriptionInit }
203
+ >(async (msg) => {
204
+ return { answer: await this.getAnswer(msg.offer) };
205
+ });
206
+
207
+ return { type: this.type };
208
+ }
209
+
210
+ static deserialize(_data: {}, channel: ClientServerChannel): PeerProxyStep {
211
+ return new PeerProxyStep(async (offer: RTCSessionDescriptionInit) => {
212
+ const response = await channel.request<
213
+ { offer: RTCSessionDescriptionInit },
214
+ { answer: RTCSessionDescriptionInit }
215
+ >({ offer });
216
+ return response.answer;
217
+ });
218
+ }
219
+
220
+ dispose(): void {
221
+ this.externalConnections.forEach(conn => conn.close());
222
+ }
223
+
224
+ }
225
+
226
+ export class DynamicProxyStep extends DynamicProxyStepDefinition {
227
+
228
+ private externalConnections: RTCConnection[] = [];
229
+
230
+ async handle(connection: MockRTCConnection) {
231
+ await connection.proxyTrafficToExternalConnection();
232
+
233
+ // This step keeps running indefinitely, until the connection closes
234
+ return new Promise<void>((resolve) => connection.on('connection-closed', resolve));
235
+ }
236
+
237
+ dispose(): void {
238
+ this.externalConnections.forEach(conn => conn.close());
239
+ }
240
+
241
+ }
242
+
243
+ export const StepLookup: typeof StepDefinitionLookup = {
244
+ 'wait-for-duration': WaitForDurationStep,
245
+ 'wait-for-channel': WaitForChannelStep,
246
+ 'wait-for-track': WaitForTrackStep,
247
+ 'wait-for-media': WaitForMediaStep,
248
+ 'wait-for-message': WaitForMessageStep,
249
+ 'send-message': SendStep,
250
+ 'close-connection': CloseStep,
251
+ 'echo-channels': EchoStep,
252
+ 'peer-proxy': PeerProxyStep,
253
+ 'dynamic-proxy': DynamicProxyStep
254
+ };
@@ -0,0 +1,44 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import type {
7
+ MockRTC,
8
+ MockRTCOptions,
9
+ } from "./mockrtc";
10
+ import { MockRTCClient, MockRTCClientOptions } from "./client/mockrtc-client";
11
+
12
+ export type {
13
+ MockRTC,
14
+ MockRTCOptions
15
+ };
16
+
17
+ export type {
18
+ MockRTCPeer,
19
+ MockRTCSession,
20
+ MockRTCOfferParams,
21
+ MockRTCAnswerParams,
22
+ MockRTCExternalOfferParams,
23
+ MockRTCExternalAnswerParams,
24
+ OfferOptions,
25
+ AnswerOptions
26
+ } from './mockrtc-peer';
27
+
28
+ export { MOCKRTC_CONTROL_CHANNEL } from './webrtc/control-channel';
29
+ export {
30
+ hookWebRTCConnection,
31
+ hookAllWebRTC
32
+ } from "./webrtc-hooks";
33
+
34
+ export function getLocal(): MockRTC {
35
+ return new MockRTCClient();
36
+ }
37
+
38
+ export function getRemote(options: MockRTCClientOptions = {}): MockRTC {
39
+ return new MockRTCClient(options);
40
+ }
41
+
42
+ export function getAdminServer(): never {
43
+ throw new Error("Can't use MockRTC.getLocal() in a browser");
44
+ }