ep_webrtc 2.5.34 → 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.
@@ -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
- };