diff options
| author | Adam Soltys <[email protected]> | 2026-10-19 00:01:52 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-19 00:01:52 -0700 |
| commit | e571187361ec9af1db8fba9050c23ec10db677f4 (patch) | |
| tree | f650d51d5e0de7ffb3b79db165509643c08c3782 /tools | |
| parent | 8db6bd1e3dff5d55eb82a854149a387b5d718148 (diff) | |
| download | coinosv3-e571187361ec9af1db8fba9050c23ec10db677f4.tar.gz coinosv3-e571187361ec9af1db8fba9050c23ec10db677f4.zip | |
Screen sharing. In a call: Share screen in the call's bar (where the browser can — desktops) puts your screen in your camera's place, or into the call if your camera's off; Stop sharing, or the browser's own, brings the camera back or stops the picture, and the other side's view follows (a picture that stops coming is hidden). Live: Share screen / Back to camera on the broadcast bar switches without stopping the stream; the browser's Stop sharing goes back to the camera instead of ending it, and a cancelled screen picker leaves the camera going.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/call-test.js | 28 | ||||
| -rw-r--r-- | tools/go-live-test.js | 14 |
2 files changed, 40 insertions, 2 deletions
diff --git a/tools/call-test.js b/tools/call-test.js index 15b3d98..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; @@ -166,6 +167,31 @@ try { 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); diff --git a/tools/go-live-test.js b/tools/go-live-test.js index 990dc1a..6c20a7a 100644 --- a/tools/go-live-test.js +++ b/tools/go-live-test.js @@ -44,7 +44,8 @@ const server = Bun.serve({ port: PORT, websocket, fetch: async (req, srv) => { return path.includes('.') && await f.exists() ? new Response(f) : new Response(Bun.file('dist/index.html')); } }); 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'] }); const relayStub = (fx) => { // what the relays held before a reload (a test can carry events across one) @@ -140,6 +141,17 @@ try { const m3u8 = await (await fetch(hls)).text(); check('the server is making HLS of it, three segments in when it\'s announced', (m3u8.match(/\.ts/g) || []).length >= 3 && !/ENDLIST/.test(m3u8), (m3u8.match(/\.ts/g) || []).length + ' segments'); + console.log('\n[camera ⇄ screen while live]'); + const sending = () => page.evaluate(() => { const v = document.querySelector('.live-page video.bc-preview'); const t = v && v.srcObject && v.srcObject.getVideoTracks()[0]; return t ? t.label + ' ' + t.readyState : ''; }); + await page.evaluate(() => document.querySelector('.bc-switch').click()); + check('Share screen, live: the stream carries on with the screen', await waitFor(() => { const v = document.querySelector('.live-page video.bc-preview'); const t = v && v.srcObject && v.srcObject.getVideoTracks()[0]; return !!t && /^screen/.test(t.label) && t.readyState === 'live'; }, 10000) + && !!(await page.$('.bc-bar')) && await page.evaluate(() => document.querySelector('.bc-switch').textContent === 'Back to camera'), await sending()); + await page.evaluate(() => document.querySelector('.bc-switch').click()); + check('...and Back to camera returns to it, still live', await waitFor(() => { const v = document.querySelector('.live-page video.bc-preview'); const t = v && v.srcObject && v.srcObject.getVideoTracks()[0]; return !!t && !/^screen/.test(t.label) && t.readyState === 'live'; }, 10000) && !!(await page.$('.bc-bar')), await sending()); + await sleep(4000); + const segsNow = ((await (await fetch(hls)).text()).match(/\.ts/g) || []).length; + check('...the server still making HLS through the switches', segsNow >= 3, segsNow + ' segments in the window'); + console.log('\n[someone watching]'); const viewer = await browser.newPage(); await viewer.setViewport({ width: 390, height: 844 }); |
