From d007dbec5d44963b4650baf9668389ec7da944f3 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 18 Oct 2026 23:19:11 -0700 Subject: A voice call can turn video: Turn on camera in the call's bar puts your camera in (a renegotiation, NIP-AC's kind 25055); the other side sees you at once and gets the same button for theirs. The call's sound carries on throughout. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg --- tools/call-test.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'tools') diff --git a/tools/call-test.js b/tools/call-test.js index 5bfa38c..5a0c3a0 100644 --- a/tools/call-test.js +++ b/tools/call-test.js @@ -147,6 +147,25 @@ 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))); + 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; -- cgit v1.2.3