test: add tab source ID tests for media handler (#51095)

* test: add getMediaSourceId tab source coverage

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* chore: move captureWithTabSourceId() to a shared helper

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* test: improve "webContents module getMediaSourceId()" testing

Co-authored-by: Charles Kerr <charles@charleskerr.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
trop[bot]
2026-04-16 15:25:33 -04:00
committed by GitHub
parent f1958d838c
commit b51e62e560
3 changed files with 99 additions and 3 deletions

View File

@@ -4,6 +4,7 @@ import { expect } from 'chai';
import * as http from 'node:http';
import { captureWithTabSourceId } from './lib/media-helpers';
import { ifit, listen } from './lib/spec-helpers';
import { closeAllWindows } from './lib/window-helpers';
@@ -417,6 +418,37 @@ describe('setDisplayMediaRequestHandler', () => {
expect(message).to.equal('Invalid state');
});
it('works when mediaDevices.getUserMedia uses a tab source id from webContents.getMediaSourceId', async () => {
const sourceWindow = new BrowserWindow({ show: false });
const requestingWindow = new BrowserWindow({ show: false });
await Promise.all([sourceWindow.loadURL(serverUrl), requestingWindow.loadURL(serverUrl)]);
const sourceId = sourceWindow.webContents.getMediaSourceId(requestingWindow.webContents);
const { ok, message, origin, videoTrackCount } = await captureWithTabSourceId(requestingWindow, sourceId);
expect(ok).to.be.true(message);
expect(origin).to.equal(new URL(serverUrl).origin);
expect(videoTrackCount).to.equal(1);
});
it('rejects a tab source id when used from a different requesting webContents', async () => {
const sourceWindow = new BrowserWindow({ show: false });
const registeredRequesterWindow = new BrowserWindow({ show: false });
const otherRequesterWindow = new BrowserWindow({ show: false });
await Promise.all([
sourceWindow.loadURL(serverUrl),
registeredRequesterWindow.loadURL(serverUrl),
otherRequesterWindow.loadURL(serverUrl)
]);
const sourceId = sourceWindow.webContents.getMediaSourceId(registeredRequesterWindow.webContents);
const { ok, message, origin } = await captureWithTabSourceId(otherRequesterWindow, sourceId);
expect(ok).to.be.false();
expect(message).to.match(/Invalid state|Error starting tab capture/);
expect(origin).to.equal(new URL(serverUrl).origin);
});
it('can remove a displayMediaRequestHandler', async () => {
const ses = session.fromPartition('' + Math.random());

View File

@@ -12,6 +12,7 @@ import * as path from 'node:path';
import { setTimeout } from 'node:timers/promises';
import * as url from 'node:url';
import { captureWithTabSourceId } from './lib/media-helpers';
import { ifdescribe, defer, waitUntil, listen, ifit } from './lib/spec-helpers';
import { cleanupWebContents, closeAllWindows } from './lib/window-helpers';
@@ -1797,9 +1798,33 @@ describe('webContents module', () => {
describe('getMediaSourceId()', () => {
afterEach(closeAllWindows);
it('returns a valid stream id', () => {
const w = new BrowserWindow({ show: false });
expect(w.webContents.getMediaSourceId(w.webContents)).to.be.a('string').that.is.not.empty();
let server: http.Server;
let serverUrl: string;
before(async () => {
server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
res.end('');
});
serverUrl = (await listen(server)).url;
});
after(() => {
server.close();
});
it('returns a stream id that can be used by the registered requester', async () => {
const sourceWindow = new BrowserWindow({ show: false });
const requesterWindow = new BrowserWindow({ show: false });
await Promise.all([sourceWindow.loadURL(serverUrl), requesterWindow.loadURL(serverUrl)]);
const streamId = sourceWindow.webContents.getMediaSourceId(requesterWindow.webContents);
const { ok, message, origin, videoTrackCount } = await captureWithTabSourceId(requesterWindow, streamId);
expect(streamId).to.be.a('string').that.is.not.empty();
expect(ok, message).to.equal(true);
expect(origin).to.equal(new url.URL(serverUrl).origin);
expect(videoTrackCount).to.equal(1);
});
});

39
spec/lib/media-helpers.ts Normal file
View File

@@ -0,0 +1,39 @@
import { BrowserWindow } from 'electron/main';
export interface TabSourceCaptureResult {
ok: boolean;
href: string;
message?: string;
origin: string;
videoTrackCount?: number;
}
export const captureWithTabSourceId = async (
requestingWindow: BrowserWindow,
streamId: string
): Promise<TabSourceCaptureResult> => {
return requestingWindow.webContents.executeJavaScript(`
navigator.mediaDevices.getUserMedia({
video: {
mandatory: {
chromeMediaSource: 'tab',
chromeMediaSourceId: ${JSON.stringify(streamId)},
},
},
}).then((stream) => {
const result = {
ok: stream instanceof MediaStream,
href: location.href,
origin: location.origin,
videoTrackCount: stream.getVideoTracks().length,
};
stream.getTracks().forEach((track) => track.stop());
return result;
}, (error) => ({
ok: false,
href: location.href,
message: error.message,
origin: location.origin,
}));
`);
};