diff options
Diffstat (limited to 'tools/call-test.js')
| -rw-r--r-- | tools/call-test.js | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/tools/call-test.js b/tools/call-test.js index 5bfa38c..d5c3909 100644 --- a/tools/call-test.js +++ b/tools/call-test.js @@ -53,7 +53,8 @@ const server = Bun.serve({ port: PORT, }, }); const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', - args: ['--no-sandbox', '--use-fake-device-for-media-stream', '--use-fake-ui-for-media-stream', '--autoplay-policy=no-user-gesture-required'] }); + args: ['--no-sandbox', '--use-fake-device-for-media-stream', '--use-fake-ui-for-media-stream', '--autoplay-policy=no-user-gesture-required', + '--auto-select-desktop-capture-source=Entire screen'] }); // a screen to share, chosen without asking // our DM relays lead to the test's relay const toLocal = (port) => { const Real = window.WebSocket; @@ -106,6 +107,7 @@ try { console.log('[a video call]'); await openDm(A, B.pk); check('the conversation has voice and video call buttons', await A.page.evaluate(() => document.querySelectorAll('.chat-head .call-start').length === 2)); + const w0 = wrapIds.size; await A.page.evaluate(() => document.querySelector('.call-start[data-call="video"]').click()); check('the caller sees it calling, their own camera on', await waitFor(A.page, () => /calling/i.test(document.querySelector('.call-overlay .call-status')?.textContent || '') && document.querySelector('video.call-local')?.videoWidth > 0, 10000)); check('the callee, on their wallet page, is rung: who, and a video call', await waitFor(B.page, () => !!document.querySelector('.call-overlay .call-accept') && /Alice Caller/.test(document.querySelector('.call-overlay').innerText) && /video call/i.test(document.querySelector('.call-overlay').innerText), 15000)); @@ -119,6 +121,9 @@ try { await sleep(1500); const ma = await media(A.page), mb = await media(B.page); check('each sees the other\'s picture and gets their sound', ma.video && ma.audio && mb.video && mb.audio, JSON.stringify({ ma, mb })); + // the routes ride in the offer and answer: a household's two devices + // calling each other mustn't flood the relays (a per-IP limit ate the answer) + check('...in a handful of messages, not one per route', wrapIds.size - w0 <= 6, (wrapIds.size - w0) + ' wraps'); await B.page.evaluate(() => document.querySelector('.call-bar .call-btn[aria-label="Mute"]').click()); check('mute turns the microphone off', await waitFor(B.page, () => !!document.querySelector('.call-btn.on[aria-label="Unmute"]'), 3000)); await A.page.evaluate(() => document.querySelector('.call-btn[aria-label="Minimize"]').click()); @@ -147,6 +152,50 @@ try { await A.page.evaluate(() => document.querySelector('.call-decline').click()); await waitFor(B.page, () => !document.querySelector('.call-overlay'), 8000); + console.log('\n[a voice call turned video]'); + await A.page.evaluate(() => document.querySelector('.call-start[data-call="voice"]').click()); + await waitFor(B.page, () => !!document.querySelector('.call-overlay .call-accept'), 15000); + await B.page.evaluate(() => document.querySelector('.call-accept').click()); + const live = (p) => waitFor(p, () => /^\d+:\d\d$/.test(document.querySelector('.call-overlay .call-status')?.textContent || ''), 20000); + check('a voice call connects, no cameras', await live(A.page) && await live(B.page) && !(await A.page.evaluate(() => !!document.querySelector('video.call-local')))); + check('...each side offered Turn on camera', await waitFor(A.page, () => !!document.querySelector('.call-add-video'), 3000) && await waitFor(B.page, () => !!document.querySelector('.call-add-video'), 3000)); + await A.page.evaluate(() => document.querySelector('.call-add-video').click()); + check('Alice turns her camera on: she sees herself, and gets camera off and flip', await waitFor(A.page, () => !!document.querySelector('video.call-local')?.videoWidth && !document.querySelector('.call-add-video') + && !!document.querySelector('.call-btn[aria-label="Switch camera"]'), 10000)); + check('...Bob sees her', await waitFor(B.page, () => { const v = document.querySelector('video.call-remote'); return !!v && v.videoWidth > 0 && !v.paused; }, 15000), JSON.stringify(await media(B.page))); + check('...and can turn his on too', await waitFor(B.page, () => !!document.querySelector('.call-add-video'), 3000)); + await B.page.evaluate(() => document.querySelector('.call-add-video').click()); + check('Bob turns his on: Alice sees him, both still talking', await waitFor(A.page, () => { const v = document.querySelector('video.call-remote'); return !!v && v.videoWidth > 0 && !v.paused; }, 15000) + && (await media(A.page)).audio && (await media(B.page)).audio && await live(A.page), JSON.stringify(await media(A.page))); + + console.log('\n[sharing a screen]'); + const dims = (p) => p.evaluate(() => { const v = document.querySelector('video.call-remote'); return v ? v.videoWidth + 'x' + v.videoHeight : ''; }); + const camDims = await dims(B.page); + await A.page.evaluate(() => document.querySelector('.call-share').click()); + check('Alice shares her screen: her preview shows it, the button says Stop sharing', await waitFor(A.page, () => !!document.querySelector('video.call-local.sharing') && !!document.querySelector('.call-share.on[aria-label="Stop sharing"]'), 10000)); + check('...Bob sees the screen in place of her camera', await waitFor(B.page, (d) => { const v = document.querySelector('video.call-remote'); return !!v && v.videoWidth > 0 && v.videoWidth + 'x' + v.videoHeight !== d; }, 15000, camDims), camDims + ' → ' + await dims(B.page)); + await A.page.evaluate(() => document.querySelector('.call-share').click()); + const camOf = (p) => p.evaluate(() => { const v = document.querySelector('video.call-local'); const t = v && v.srcObject && v.srcObject.getVideoTracks()[0]; return t ? t.label + ' ' + t.readyState : ''; }); + check('stopping brings her camera back: it\'s her camera she sends, Bob still sees her', await waitFor(A.page, () => { const v = document.querySelector('video.call-local:not(.sharing)'); const t = v && v.srcObject && v.srcObject.getVideoTracks()[0]; return !!t && !/^screen/.test(t.label) && t.readyState === 'live'; }, 10000) + && await waitFor(B.page, () => { const v = document.querySelector('video.call-remote'); return !!v && v.videoWidth > 0 && !v.paused; }, 10000), await camOf(A.page) + ' | Bob ' + await dims(B.page)); + await A.page.evaluate(() => document.querySelector('.call-hang').click()); + await waitFor(A.page, () => !document.querySelector('.call-overlay'), 8000); + await waitFor(B.page, () => !document.querySelector('.call-overlay'), 8000); + await A.page.evaluate(() => document.querySelector('.call-start[data-call="voice"]').click()); + await waitFor(B.page, () => !!document.querySelector('.call-overlay .call-accept'), 15000); + await B.page.evaluate(() => document.querySelector('.call-accept').click()); + await live(A.page); await live(B.page); + await waitFor(A.page, () => !!document.querySelector('.call-share'), 3000); + await A.page.evaluate(() => document.querySelector('.call-share').click()); + check('in a voice call, a shared screen shows on Bob\'s side', await waitFor(B.page, () => !!document.querySelector('.call-overlay.has-video') && document.querySelector('video.call-remote').videoWidth > 0, 15000)); + await A.page.evaluate(() => document.querySelector('.call-share').click()); + check('...and goes when she stops (no camera to come back), the call carrying on', await waitFor(B.page, () => !document.querySelector('.call-overlay.has-video'), 15000) + && await live(B.page) && (await media(B.page)).audio); + check('...and her camera never came on', await A.page.evaluate(() => !document.querySelector('video.call-local'))); + await A.page.evaluate(() => document.querySelector('.call-hang').click()); + await waitFor(A.page, () => !document.querySelector('.call-overlay'), 8000); + await waitFor(B.page, () => !document.querySelector('.call-overlay'), 8000); + console.log('\n[a remote signer\'s way: the candidates inside the offer and answer]'); for (const w of [A, B]) await w.page.evaluate(() => localStorage.setItem('coinos-call-trickle', '0')); const before = wrapIds.size; |
