diff --git a/src/web-capabilities.spec.ts b/src/web-capabilities.spec.ts index f55d1f9..d84ef23 100644 --- a/src/web-capabilities.spec.ts +++ b/src/web-capabilities.spec.ts @@ -43,56 +43,112 @@ describe('WebCapabilities', () => { describe('supportsEncodedStreamTransforms', () => { afterEach(() => { // Clean up window modifications + delete (window as Window & { RTCRtpScriptTransform?: unknown }).RTCRtpScriptTransform; delete (window as Window & { RTCRtpSender?: unknown }).RTCRtpSender; + delete (window as Window & { RTCRtpReceiver?: unknown }).RTCRtpReceiver; }); - it('should return CAPABLE when encoded transforms are supported', () => { - expect.assertions(1); + /** + * Helper function to setup window with WebRTC mocks. + * + * @param options - Configuration for mocking WebRTC objects. + * @param options.hasRTCRtpScriptTransform - Whether RTCRtpScriptTransform exists. + * @param options.hasRTCRtpSender - Whether RTCRtpSender exists. + * @param options.hasSenderTransform - Whether RTCRtpSender.prototype has transform. + * @param options.hasRTCRtpReceiver - Whether RTCRtpReceiver exists. + * @param options.hasReceiverTransform - Whether RTCRtpReceiver.prototype has transform. + */ + const setupWindow = ({ + hasRTCRtpScriptTransform, + hasRTCRtpSender, + hasSenderTransform, + hasRTCRtpReceiver, + hasReceiverTransform, + }: { + hasRTCRtpScriptTransform: boolean; + hasRTCRtpSender: boolean; + hasSenderTransform: boolean; + hasRTCRtpReceiver: boolean; + hasReceiverTransform: boolean; + }) => { + if (hasRTCRtpScriptTransform) { + /** + * Mock RTCRtpScriptTransform constructor for testing. + */ + // eslint-disable-next-line @typescript-eslint/no-empty-function + const MockRTCRtpScriptTransform = function MockRTCRtpScriptTransform() {}; + Object.defineProperty(window, 'RTCRtpScriptTransform', { + writable: true, + configurable: true, + value: MockRTCRtpScriptTransform, + }); + } else { + delete (window as Window & { RTCRtpScriptTransform?: unknown }).RTCRtpScriptTransform; + } - /** - * Mock RTCRtpSender constructor for testing. - */ - // eslint-disable-next-line @typescript-eslint/no-empty-function - const MockRTCRtpSender = function MockRTCRtpSender() {}; - MockRTCRtpSender.prototype = { - transform: {}, - }; + if (hasRTCRtpSender) { + /** + * Mock RTCRtpSender constructor for testing. + */ + // eslint-disable-next-line @typescript-eslint/no-empty-function + const MockRTCRtpSender = function MockRTCRtpSender() {}; + MockRTCRtpSender.prototype = hasSenderTransform ? { transform: {} } : {}; + Object.defineProperty(window, 'RTCRtpSender', { + writable: true, + configurable: true, + value: MockRTCRtpSender, + }); + } else { + delete (window as Window & { RTCRtpSender?: unknown }).RTCRtpSender; + } - Object.defineProperty(window, 'RTCRtpSender', { - writable: true, - configurable: true, - value: MockRTCRtpSender, - }); + if (hasRTCRtpReceiver) { + /** + * Mock RTCRtpReceiver constructor for testing. + */ + // eslint-disable-next-line @typescript-eslint/no-empty-function + const MockRTCRtpReceiver = function MockRTCRtpReceiver() {}; + MockRTCRtpReceiver.prototype = hasReceiverTransform ? { transform: {} } : {}; + Object.defineProperty(window, 'RTCRtpReceiver', { + writable: true, + configurable: true, + value: MockRTCRtpReceiver, + }); + } else { + delete (window as Window & { RTCRtpReceiver?: unknown }).RTCRtpReceiver; + } + }; - expect(WebCapabilities.supportsEncodedStreamTransforms()).toBe(CapabilityState.CAPABLE); - }); - - it('should return NOT_CAPABLE when RTCRtpSender is not available', () => { - expect.assertions(1); - - // Ensure RTCRtpSender is not available - delete (window as Window & { RTCRtpSender?: unknown }).RTCRtpSender; - - expect(WebCapabilities.supportsEncodedStreamTransforms()).toBe(CapabilityState.NOT_CAPABLE); - }); - - it('should return NOT_CAPABLE when transform is not in RTCRtpSender prototype', () => { - expect.assertions(1); - - /** - * Mock RTCRtpSender constructor without transform property. - */ - // eslint-disable-next-line @typescript-eslint/no-empty-function - const MockRTCRtpSender = function MockRTCRtpSender() {}; - MockRTCRtpSender.prototype = {}; - - Object.defineProperty(window, 'RTCRtpSender', { - writable: true, - configurable: true, - value: MockRTCRtpSender, - }); - - expect(WebCapabilities.supportsEncodedStreamTransforms()).toBe(CapabilityState.NOT_CAPABLE); - }); + it.each` + hasRTCRtpScriptTransform | hasRTCRtpSender | hasSenderTransform | hasRTCRtpReceiver | hasReceiverTransform | expected | description + ${true} | ${true} | ${true} | ${true} | ${true} | ${CapabilityState.CAPABLE} | ${'all required features are present'} + ${false} | ${true} | ${true} | ${true} | ${true} | ${CapabilityState.NOT_CAPABLE} | ${'RTCRtpScriptTransform is missing'} + ${true} | ${false} | ${false} | ${true} | ${true} | ${CapabilityState.NOT_CAPABLE} | ${'RTCRtpSender is missing'} + ${true} | ${true} | ${false} | ${true} | ${true} | ${CapabilityState.NOT_CAPABLE} | ${'RTCRtpSender.prototype.transform is missing'} + ${true} | ${true} | ${true} | ${false} | ${false} | ${CapabilityState.NOT_CAPABLE} | ${'RTCRtpReceiver is missing'} + ${true} | ${true} | ${true} | ${true} | ${false} | ${CapabilityState.NOT_CAPABLE} | ${'RTCRtpReceiver.prototype.transform is missing'} + ${true} | ${false} | ${false} | ${false} | ${false} | ${CapabilityState.NOT_CAPABLE} | ${'both RTCRtpSender and RTCRtpReceiver are missing'} + ${false} | ${false} | ${false} | ${false} | ${false} | ${CapabilityState.NOT_CAPABLE} | ${'all features are missing'} + `( + 'should return $expected when $description', + ({ + hasRTCRtpScriptTransform, + hasRTCRtpSender, + hasSenderTransform, + hasRTCRtpReceiver, + hasReceiverTransform, + expected, + }) => { + expect.assertions(1); + setupWindow({ + hasRTCRtpScriptTransform, + hasRTCRtpSender, + hasSenderTransform, + hasRTCRtpReceiver, + hasReceiverTransform, + }); + expect(WebCapabilities.supportsEncodedStreamTransforms()).toBe(expected); + } + ); }); }); diff --git a/src/web-capabilities.ts b/src/web-capabilities.ts index 6ee10b7..c0e46f9 100644 --- a/src/web-capabilities.ts +++ b/src/web-capabilities.ts @@ -86,7 +86,12 @@ export class WebCapabilities { * @returns A {@link CapabilityState}. */ static supportsEncodedStreamTransforms(): CapabilityState { - return window.RTCRtpSender && 'transform' in RTCRtpSender.prototype + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (window as any).RTCRtpScriptTransform && + window.RTCRtpSender && + 'transform' in RTCRtpSender.prototype && + window.RTCRtpReceiver && + 'transform' in RTCRtpReceiver.prototype ? CapabilityState.CAPABLE : CapabilityState.NOT_CAPABLE; }