ep_webrtc 2.5.35 → 2.5.36
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/package.json +1 -1
- package/static/tests/frontend-new/helper/utils.ts +96 -0
- package/static/tests/frontend-new/specs/audio_video_on_start.spec.ts +37 -0
- package/static/tests/frontend-new/specs/checkbox.spec.ts +165 -0
- package/static/tests/frontend-new/specs/enable_disable.spec.ts +69 -0
- package/static/tests/frontend-new/specs/errors.spec.ts +92 -0
- package/static/tests/frontend-new/specs/interface_buttons.spec.ts +271 -0
- package/static/tests/frontend-new/specs/race_conditions.spec.ts +271 -0
- package/static/tests/frontend-new/specs/setStream.spec.ts +185 -0
- package/static/tests/frontend/specs/audio_video_on_start.js +0 -35
- package/static/tests/frontend/specs/checkbox.js +0 -107
- package/static/tests/frontend/specs/enable_disable.js +0 -50
- package/static/tests/frontend/specs/errors.js +0 -58
- package/static/tests/frontend/specs/interface_buttons.js +0 -251
- package/static/tests/frontend/specs/race_conditions.js +0 -206
- package/static/tests/frontend/specs/setStream.js +0 -138
- package/static/tests/frontend/utils.js +0 -44
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {fakeGetUserMedia} = require('ep_webrtc/static/tests/frontend/utils');
|
|
4
|
-
|
|
5
|
-
describe('Race conditions that leave audio/video track enabled', function () {
|
|
6
|
-
// The idea here is to place high value on making sure that the "mute" and "video-off" buttons in
|
|
7
|
-
// the video interfaces match with the audioTrack.enabled/videoTrack.enabled, so that users don't
|
|
8
|
-
// get the wrong idea about whether the audio or video feed are on.
|
|
9
|
-
//
|
|
10
|
-
// These tests are various ideas for trying to do things in quick succession
|
|
11
|
-
// or "at the same time". We can add more if we think of them.
|
|
12
|
-
|
|
13
|
-
for (const enabledOnStart of [false, true]) {
|
|
14
|
-
describe(`audio and video ${enabledOnStart ? 'en' : 'dis'}abled on start`, function () {
|
|
15
|
-
let chrome$;
|
|
16
|
-
|
|
17
|
-
const getVideo = () => chrome$('video')[0];
|
|
18
|
-
const getStream = () => getVideo().srcObject;
|
|
19
|
-
const assertTracks = (enabled = enabledOnStart) => {
|
|
20
|
-
if (enabled) {
|
|
21
|
-
expect(getStream().getTracks().length).to.equal(2);
|
|
22
|
-
expect(getStream().getTracks().every((t) => t.enabled)).to.be(true);
|
|
23
|
-
} else {
|
|
24
|
-
expect(getStream().getTracks().some((t) => t.enabled)).to.be(false);
|
|
25
|
-
}
|
|
26
|
-
const [audio] = getStream().getAudioTracks();
|
|
27
|
-
const [video] = getStream().getVideoTracks();
|
|
28
|
-
expect(chrome$('.audio-btn').hasClass('muted')).to.equal(audio == null || !audio.enabled);
|
|
29
|
-
expect(chrome$('.video-btn').hasClass('off')).to.equal(video == null || !video.enabled);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
beforeEach(async function () {
|
|
33
|
-
this.timeout(60000);
|
|
34
|
-
await helper.aNewPad({
|
|
35
|
-
padPrefs: {
|
|
36
|
-
audioEnabledOnStart: enabledOnStart,
|
|
37
|
-
videoEnabledOnStart: enabledOnStart,
|
|
38
|
-
},
|
|
39
|
-
// Disable WebRTC so we can install a fake getUserMedia() before it is called.
|
|
40
|
-
params: {av: false},
|
|
41
|
-
});
|
|
42
|
-
chrome$ = helper.padChrome$;
|
|
43
|
-
chrome$.window.navigator.mediaDevices.getUserMedia = fakeGetUserMedia;
|
|
44
|
-
chrome$('#options-enablertc').click();
|
|
45
|
-
await helper.waitForPromise(() => chrome$('#rtcbox').data('initialized'));
|
|
46
|
-
const ownVideoId = `video_${chrome$.window.ep_webrtc.getUserId().replace(/\./g, '_')}`;
|
|
47
|
-
await helper.waitForPromise(() => getVideo() != null);
|
|
48
|
-
const $interface = chrome$(`#interface_${ownVideoId}`);
|
|
49
|
-
expect($interface.length).to.equal(1);
|
|
50
|
-
expect(chrome$('.audio-btn').length).to.equal(1);
|
|
51
|
-
expect(chrome$('.video-btn').length).to.equal(1);
|
|
52
|
-
await helper.waitForPromise(
|
|
53
|
-
() => getStream() != null && (!enabledOnStart || getStream().getTracks().length === 2));
|
|
54
|
-
assertTracks();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// See if we can trip up the state by "deactivating" webrtc, clicking mute/video-off, and
|
|
58
|
-
// "activating" webrtc in quick succession. As of this writing, "deactivating" will make the
|
|
59
|
-
// buttons disappear pretty quickly, making the "click" ineffectual, regardless, but in case
|
|
60
|
-
// we ever change things around, perhaps this test will catch something.
|
|
61
|
-
it('deactivate, click, activate', async function () {
|
|
62
|
-
for (let i = 0; i < 10; ++i) {
|
|
63
|
-
const [oldAudioTrack] = getStream().getAudioTracks();
|
|
64
|
-
const [oldVideoTrack] = getStream().getVideoTracks();
|
|
65
|
-
|
|
66
|
-
await chrome$.window.ep_webrtc.deactivate();
|
|
67
|
-
chrome$('.audio-btn').click();
|
|
68
|
-
chrome$('.video-btn').click();
|
|
69
|
-
await chrome$.window.ep_webrtc.activate();
|
|
70
|
-
|
|
71
|
-
assertTracks();
|
|
72
|
-
|
|
73
|
-
const [newAudioTrack] = getStream().getAudioTracks();
|
|
74
|
-
const [newVideoTrack] = getStream().getVideoTracks();
|
|
75
|
-
|
|
76
|
-
if (enabledOnStart) {
|
|
77
|
-
expect(newAudioTrack).to.not.equal(oldAudioTrack);
|
|
78
|
-
expect(oldAudioTrack.readyState).to.equal('ended');
|
|
79
|
-
expect(newAudioTrack.readyState).to.equal('live');
|
|
80
|
-
|
|
81
|
-
expect(newVideoTrack).to.not.equal(oldVideoTrack);
|
|
82
|
-
expect(oldVideoTrack.readyState).to.equal('ended');
|
|
83
|
-
expect(newVideoTrack.readyState).to.equal('live');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// See if we can trip up the state by clicking mute/video-off, "deactivating" webrtc, and
|
|
89
|
-
// "activating" webrtc in quick succession
|
|
90
|
-
it('click, deactivate, activate', async function () {
|
|
91
|
-
for (let i = 0; i < 10; ++i) {
|
|
92
|
-
const [oldAudioTrack] = getStream().getAudioTracks();
|
|
93
|
-
const [oldVideoTrack] = getStream().getVideoTracks();
|
|
94
|
-
|
|
95
|
-
chrome$('.audio-btn').click();
|
|
96
|
-
chrome$('.video-btn').click();
|
|
97
|
-
await chrome$.window.ep_webrtc.deactivate();
|
|
98
|
-
await chrome$.window.ep_webrtc.activate();
|
|
99
|
-
|
|
100
|
-
assertTracks();
|
|
101
|
-
|
|
102
|
-
const [newAudioTrack] = getStream().getAudioTracks();
|
|
103
|
-
const [newVideoTrack] = getStream().getVideoTracks();
|
|
104
|
-
|
|
105
|
-
if (enabledOnStart) {
|
|
106
|
-
expect(newAudioTrack).to.not.equal(oldAudioTrack);
|
|
107
|
-
expect(oldAudioTrack.readyState).to.equal('ended');
|
|
108
|
-
expect(newAudioTrack.readyState).to.equal('live');
|
|
109
|
-
|
|
110
|
-
expect(newVideoTrack).to.not.equal(oldVideoTrack);
|
|
111
|
-
expect(oldVideoTrack.readyState).to.equal('ended');
|
|
112
|
-
expect(newVideoTrack.readyState).to.equal('live');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// See if we can trip up the state by "deactivating" webrtc, "activating" webrtc, and then
|
|
118
|
-
// clicking mute/video-off right after the interface returns.
|
|
119
|
-
it('deactivate, activate, click', async function () {
|
|
120
|
-
for (let i = 0; i < 10; ++i) {
|
|
121
|
-
const [oldAudioTrack] = getStream().getAudioTracks();
|
|
122
|
-
const [oldVideoTrack] = getStream().getVideoTracks();
|
|
123
|
-
|
|
124
|
-
await chrome$.window.ep_webrtc.deactivate();
|
|
125
|
-
const p = chrome$.window.ep_webrtc.activate();
|
|
126
|
-
await helper.waitForPromise(
|
|
127
|
-
() => chrome$ && chrome$('.interface-container').length === 1, 2000);
|
|
128
|
-
chrome$('.audio-btn').click();
|
|
129
|
-
chrome$('.video-btn').click();
|
|
130
|
-
await Promise.all([
|
|
131
|
-
p,
|
|
132
|
-
chrome$('.audio-btn').data('idle')('click'),
|
|
133
|
-
chrome$('.video-btn').data('idle')('click'),
|
|
134
|
-
]);
|
|
135
|
-
|
|
136
|
-
assertTracks(!enabledOnStart);
|
|
137
|
-
|
|
138
|
-
const [newAudioTrack] = getStream().getAudioTracks();
|
|
139
|
-
const [newVideoTrack] = getStream().getVideoTracks();
|
|
140
|
-
|
|
141
|
-
expect(newAudioTrack).to.not.equal(oldAudioTrack);
|
|
142
|
-
if (oldAudioTrack != null) expect(oldAudioTrack.readyState).to.equal('ended');
|
|
143
|
-
if (newAudioTrack != null) expect(newAudioTrack.readyState).to.equal('live');
|
|
144
|
-
|
|
145
|
-
expect(newVideoTrack).to.not.equal(oldVideoTrack);
|
|
146
|
-
if (oldVideoTrack != null) expect(oldVideoTrack.readyState).to.equal('ended');
|
|
147
|
-
if (newVideoTrack != null) expect(newVideoTrack.readyState).to.equal('live');
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// See if we can trip up the state by clicking mute/video-off, "deactivating"/"activating"
|
|
152
|
-
// webrtc, as close to at the same time as we can
|
|
153
|
-
it('click while reactivate', async function () {
|
|
154
|
-
for (let i = 0; i < 10; i++) {
|
|
155
|
-
const [oldAudioTrack] = getStream().getAudioTracks();
|
|
156
|
-
const [oldVideoTrack] = getStream().getVideoTracks();
|
|
157
|
-
|
|
158
|
-
await chrome$.window.ep_webrtc.deactivate();
|
|
159
|
-
const p = chrome$.window.ep_webrtc.activate();
|
|
160
|
-
chrome$('.audio-btn').click();
|
|
161
|
-
chrome$('.video-btn').click();
|
|
162
|
-
await Promise.all([
|
|
163
|
-
p,
|
|
164
|
-
chrome$('.audio-btn').data('idle')(),
|
|
165
|
-
chrome$('.video-btn').data('idle')(),
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
assertTracks(!enabledOnStart);
|
|
169
|
-
|
|
170
|
-
const [newAudioTrack] = getStream().getAudioTracks();
|
|
171
|
-
const [newVideoTrack] = getStream().getVideoTracks();
|
|
172
|
-
|
|
173
|
-
if (!enabledOnStart) {
|
|
174
|
-
expect(newAudioTrack).to.not.equal(oldAudioTrack);
|
|
175
|
-
if (oldAudioTrack != null) expect(oldAudioTrack.readyState).to.equal('ended');
|
|
176
|
-
if (newAudioTrack != null) expect(newAudioTrack.readyState).to.equal('live');
|
|
177
|
-
|
|
178
|
-
expect(newVideoTrack).to.not.equal(oldVideoTrack);
|
|
179
|
-
if (oldVideoTrack != null) expect(oldVideoTrack.readyState).to.equal('ended');
|
|
180
|
-
if (newVideoTrack != null) expect(newVideoTrack.readyState).to.equal('live');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// See if we can trip up the state by clicking mute/video-off many times at once. We click
|
|
186
|
-
// mute an odd number of times and video-off an even number of times.
|
|
187
|
-
it('many clicks', async function () {
|
|
188
|
-
for (let i = 0; i < 10; ++i) {
|
|
189
|
-
chrome$('.audio-btn').click();
|
|
190
|
-
chrome$('.audio-btn').click();
|
|
191
|
-
chrome$('.audio-btn').click();
|
|
192
|
-
chrome$('.video-btn').click();
|
|
193
|
-
chrome$('.video-btn').click();
|
|
194
|
-
await Promise.all([
|
|
195
|
-
chrome$('.audio-btn').data('idle')(),
|
|
196
|
-
chrome$('.video-btn').data('idle')(),
|
|
197
|
-
]);
|
|
198
|
-
expect(chrome$('.audio-btn').hasClass('muted'))
|
|
199
|
-
.to.equal(((i + 1) * 3 + (enabledOnStart ? 1 : 0)) % 2 === 0);
|
|
200
|
-
expect(chrome$('.video-btn').hasClass('off'))
|
|
201
|
-
.to.equal(((i + 1) * 2 + (enabledOnStart ? 1 : 0)) % 2 === 0);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
});
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {cartesian, fakeGetUserMedia} = require('ep_webrtc/static/tests/frontend/utils');
|
|
4
|
-
|
|
5
|
-
describe('setStream()', function () {
|
|
6
|
-
let chrome$;
|
|
7
|
-
const otherUserId = 'other_user_id';
|
|
8
|
-
const otherVideoId = `video_${otherUserId.replace(/\./g, '_')}`;
|
|
9
|
-
const otherInterfaceId = `interface_${otherVideoId}`;
|
|
10
|
-
let ownUserId, ownVideoId, ownInterfaceId;
|
|
11
|
-
|
|
12
|
-
describe('Audio and video enabled', function () {
|
|
13
|
-
const testCases = [...cartesian(...Array(4).fill([false, true]))].map(
|
|
14
|
-
([webrtcaudioenabled, webrtcvideoenabled, peerAudio, peerVideo]) => ({
|
|
15
|
-
params: {webrtcaudioenabled, webrtcvideoenabled},
|
|
16
|
-
peer: {audio: peerAudio, video: peerVideo},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
for (const tc of testCases) {
|
|
20
|
-
describe(JSON.stringify(tc), function () {
|
|
21
|
-
before(async function () {
|
|
22
|
-
this.timeout(60000);
|
|
23
|
-
await helper.aNewPad({
|
|
24
|
-
// Disable WebRTC so we can install a mock getUserMedia() before it is called.
|
|
25
|
-
params: Object.assign({av: false}, tc.params),
|
|
26
|
-
});
|
|
27
|
-
chrome$ = helper.padChrome$;
|
|
28
|
-
chrome$.window.navigator.mediaDevices.getUserMedia = fakeGetUserMedia;
|
|
29
|
-
// Clicking $(#options-enablertc) also activates, but calling activate() directly blocks
|
|
30
|
-
// until activation is complete.
|
|
31
|
-
await chrome$.window.ep_webrtc.activate();
|
|
32
|
-
ownUserId = chrome$.window.ep_webrtc.getUserId();
|
|
33
|
-
ownVideoId = `video_${ownUserId.replace(/\./g, '_')}`;
|
|
34
|
-
ownInterfaceId = `interface_${ownVideoId}`;
|
|
35
|
-
const peerStream =
|
|
36
|
-
tc.peer.audio || tc.peer.video ? await fakeGetUserMedia(tc.peer) : new MediaStream();
|
|
37
|
-
await chrome$.window.ep_webrtc.setStream(otherUserId, peerStream);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('self and peer elements exist', async function () {
|
|
41
|
-
expect(chrome$('.interface-container').length).to.equal(2);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('self interface', async function () {
|
|
45
|
-
// Self view is always muted because users shouldn't hear themselves (otherwise there
|
|
46
|
-
// would be audio feedback).
|
|
47
|
-
expect(chrome$(`#${ownVideoId}`).prop('muted')).to.equal(true);
|
|
48
|
-
const $audioBtn = chrome$(`#${ownInterfaceId} .audio-btn`);
|
|
49
|
-
expect($audioBtn.length).to.equal(1);
|
|
50
|
-
expect($audioBtn.hasClass('muted')).to.equal(!tc.params.webrtcaudioenabled);
|
|
51
|
-
expect($audioBtn.hasClass('disallowed')).to.equal(false);
|
|
52
|
-
const $videoBtn = chrome$(`#${ownInterfaceId} .video-btn`);
|
|
53
|
-
expect($videoBtn.length).to.equal(1);
|
|
54
|
-
expect($videoBtn.hasClass('off')).to.equal(!tc.params.webrtcvideoenabled);
|
|
55
|
-
expect($videoBtn.hasClass('disallowed')).to.equal(false);
|
|
56
|
-
const $enlargeBtn = chrome$(`#${ownInterfaceId} .enlarge-btn`);
|
|
57
|
-
expect($enlargeBtn.length).to.equal(1);
|
|
58
|
-
expect($enlargeBtn.hasClass('large')).to.equal(false);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('peer interface', async function () {
|
|
62
|
-
const $audioBtn = chrome$(`#${otherInterfaceId} .audio-btn`);
|
|
63
|
-
expect($audioBtn.length).to.equal(1);
|
|
64
|
-
// Only initially muted if the browser doesn't give permission to autoplay unless muted.
|
|
65
|
-
// (A peer without an audio track might later add an audio track; if so, the audio should
|
|
66
|
-
// start playing locally without the local user clicking anything.)
|
|
67
|
-
expect($audioBtn.hasClass('muted')).to.equal(chrome$(`#${otherVideoId}`).prop('muted'));
|
|
68
|
-
const $videoBtn = chrome$(`#${otherInterfaceId} .video-btn`);
|
|
69
|
-
expect($videoBtn.length).to.equal(0);
|
|
70
|
-
const $enlargeBtn = chrome$(`#${otherInterfaceId} .enlarge-btn`);
|
|
71
|
-
expect($enlargeBtn.length).to.equal(1);
|
|
72
|
-
expect($enlargeBtn.hasClass('large')).to.equal(false);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('Audio and video hard disabled', function () {
|
|
79
|
-
before(async function () {
|
|
80
|
-
this.timeout(60000);
|
|
81
|
-
await helper.aNewPad({
|
|
82
|
-
params: {
|
|
83
|
-
// Disable WebRTC so we can modify settings and install a fake getUserMedia() before
|
|
84
|
-
// WebRTC stuff is initialized.
|
|
85
|
-
av: false,
|
|
86
|
-
webrtcaudioenabled: true,
|
|
87
|
-
webrtcvideoenabled: true,
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
chrome$ = helper.padChrome$;
|
|
91
|
-
chrome$.window.navigator.mediaDevices.getUserMedia = fakeGetUserMedia;
|
|
92
|
-
await helper.waitForPromise(() => chrome$('#rtcbox').data('initialized'));
|
|
93
|
-
chrome$.window.ep_webrtc._settings.audio.disabled = 'hard';
|
|
94
|
-
chrome$.window.ep_webrtc._settings.video.disabled = 'hard';
|
|
95
|
-
// Clicking $(#options-enablertc) also activates, but calling activate() directly blocks until
|
|
96
|
-
// activation is complete.
|
|
97
|
-
await chrome$.window.ep_webrtc.activate();
|
|
98
|
-
ownUserId = chrome$.window.ep_webrtc.getUserId();
|
|
99
|
-
ownVideoId = `video_${ownUserId.replace(/\./g, '_')}`;
|
|
100
|
-
ownInterfaceId = `interface_${ownVideoId}`;
|
|
101
|
-
await chrome$.window.ep_webrtc.setStream(otherUserId, new MediaStream());
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('self and peer elements exist', async function () {
|
|
105
|
-
expect(chrome$('.interface-container').length).to.equal(2);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('self interface', async function () {
|
|
109
|
-
expect(chrome$(`#${ownVideoId}`).prop('muted')).to.equal(true);
|
|
110
|
-
const $audioBtn = chrome$(`#${ownInterfaceId} .audio-btn`);
|
|
111
|
-
expect($audioBtn.length).to.equal(1);
|
|
112
|
-
expect($audioBtn.hasClass('muted')).to.equal(true);
|
|
113
|
-
expect($audioBtn.hasClass('disallowed')).to.equal(true);
|
|
114
|
-
const $videoBtn = chrome$(`#${ownInterfaceId} .video-btn`);
|
|
115
|
-
expect($videoBtn.length).to.equal(1);
|
|
116
|
-
expect($videoBtn.hasClass('off')).to.equal(true);
|
|
117
|
-
expect($videoBtn.hasClass('disallowed')).to.equal(true);
|
|
118
|
-
const $enlargeBtn = chrome$(`#${ownInterfaceId} .enlarge-btn`);
|
|
119
|
-
expect($enlargeBtn.length).to.equal(1);
|
|
120
|
-
expect($enlargeBtn.hasClass('large')).to.equal(false);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('peer interface', async function () {
|
|
124
|
-
const $audioBtn = chrome$(`#${otherInterfaceId} .audio-btn`);
|
|
125
|
-
expect($audioBtn.length).to.equal(1);
|
|
126
|
-
// Mute state only depends on whether the browser gives permission to autoplay when unmuted.
|
|
127
|
-
// Hard disabling only affects what the local client sends; it doesn't affect what the remote
|
|
128
|
-
// peer sends. (Both the local client and the remote peer should see the same settings,
|
|
129
|
-
// however.)
|
|
130
|
-
expect($audioBtn.hasClass('muted')).to.equal(chrome$(`#${otherVideoId}`).prop('muted'));
|
|
131
|
-
const $videoBtn = chrome$(`#${otherInterfaceId} .video-btn`);
|
|
132
|
-
expect($videoBtn.length).to.equal(0);
|
|
133
|
-
const $enlargeBtn = chrome$(`#${otherInterfaceId} .enlarge-btn`);
|
|
134
|
-
expect($enlargeBtn.length).to.equal(1);
|
|
135
|
-
expect($enlargeBtn.hasClass('large')).to.equal(false);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Generator function that yields the Cartesian product of the given iterables.
|
|
4
|
-
exports.cartesian = function* (head, ...tail) {
|
|
5
|
-
const remainder = tail.length > 0 ? exports.cartesian(...tail) : [[]];
|
|
6
|
-
for (const r of remainder) for (const h of head) yield [h, ...r];
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const makeSilentAudioTrack = () => {
|
|
10
|
-
const ctx = new AudioContext();
|
|
11
|
-
const gain = ctx.createGain();
|
|
12
|
-
const dst = gain.connect(ctx.createMediaStreamDestination());
|
|
13
|
-
return dst.stream.getAudioTracks()[0];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const makeVideoTrack = (constraints) => {
|
|
17
|
-
const canvas = helper.padChrome$.window.document.createElement('canvas');
|
|
18
|
-
const {
|
|
19
|
-
width: {max: widthMax = 160, ideal: widthIdeal} = {},
|
|
20
|
-
height: {max: heightMax = 120, ideal: heightIdeal} = {},
|
|
21
|
-
} = constraints;
|
|
22
|
-
canvas.width = widthIdeal || widthMax;
|
|
23
|
-
canvas.height = heightIdeal || heightMax;
|
|
24
|
-
const ctx = canvas.getContext('2d');
|
|
25
|
-
// Some animation is needed because in some browsers HTMLVideoElement.play() will hang until the
|
|
26
|
-
// canvas is updated. The pad's window.setInterval is called in the hopes that the interval will
|
|
27
|
-
// be automatically stopped once the pad is unloaded.
|
|
28
|
-
helper.padChrome$.window.setInterval(() => {
|
|
29
|
-
ctx.fillStyle = `#${Math.floor(Math.random() * 2 ** 24).toString(16).padStart(6, '0')}`;
|
|
30
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
31
|
-
}, 100); // Use a relatively high frame rate to speed up tests.
|
|
32
|
-
return canvas.captureStream().getVideoTracks()[0];
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Creates dummy audio and/or video tracks. Limitations:
|
|
36
|
-
// - Most browsers prohibit audio until there has been some user interaction with the page or
|
|
37
|
-
// the real getUserMedia() has been called.
|
|
38
|
-
exports.fakeGetUserMedia = async ({audio, video}) => {
|
|
39
|
-
if (!audio && !video) throw new DOMException('either audio or video is required', 'TypeError');
|
|
40
|
-
return new MediaStream([
|
|
41
|
-
...(audio ? [makeSilentAudioTrack()] : []),
|
|
42
|
-
...(video ? [makeVideoTrack(video)] : []),
|
|
43
|
-
]);
|
|
44
|
-
};
|