diff --git a/CHANGELOG.md b/CHANGELOG.md index da86fe2d..6e62af9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ Bug Fixes - Fixed an [issue](https://github.com/twilio/twilio-voice.js/issues/100) where a `TypeError` is thrown after rejecting a call then invoking `updateToken`. +- Fixed an issue (https://github.com/twilio/twilio-voice.js/issues/87, https://github.com/twilio/twilio-voice.js/issues/145) where the `PeerConnection` object is not properly disposed. + - Fixed an [issue](https://github.com/twilio/twilio-voice.js/issues/14) where `device.audio.disconnect`, `device.audio.incoming` and `device.audio.outgoing` do not have the correct type definitions. 2.3.2 (February 27, 2023) diff --git a/lib/twilio/call.ts b/lib/twilio/call.ts index ad304885..13d9cbbf 100644 --- a/lib/twilio/call.ts +++ b/lib/twilio/call.ts @@ -636,13 +636,13 @@ class Call extends EventEmitter { if (this._direction === Call.CallDirection.Incoming) { this._isAnswered = true; - this._pstream.on('answer', this._onAnswer.bind(this)); + this._pstream.on('answer', this._onAnswer); this._mediaHandler.answerIncomingCall(this.parameters.CallSid, this._options.offerSdp, rtcConstraints, rtcConfiguration, onAnswer); } else { const params = Array.from(this.customParameters.entries()).map(pair => `${encodeURIComponent(pair[0])}=${encodeURIComponent(pair[1])}`).join('&'); - this._pstream.on('answer', this._onAnswer.bind(this)); + this._pstream.on('answer', this._onAnswer); this._mediaHandler.makeOutgoingCall(this._pstream.token, params, this.outboundConnectionId, rtcConstraints, rtcConfiguration, onAnswer); } diff --git a/tests/unit/call.ts b/tests/unit/call.ts index 63018f92..823a7811 100644 --- a/tests/unit/call.ts +++ b/tests/unit/call.ts @@ -1706,45 +1706,72 @@ describe('Call', function() { describe('pstream.answer event', () => { let pStreamAnswerPayload: any; - beforeEach(async () => { + beforeEach(() => { pStreamAnswerPayload = { edge: 'foobar-edge', reconnect: 'foobar-reconnect-token', }; - conn = new Call(config, options); - conn.accept(); - await clock.tickAsync(0); }); - it('should set the call to "answered"', () => { - pstream.emit('answer', pStreamAnswerPayload); - assert(conn['_isAnswered']); - }); + describe('for incoming calls', () => { + beforeEach(async () => { + // With a CallSid, this becomes an incoming call + const callParameters = { CallSid: 'CA1234' }; + conn = new Call(config, Object.assign(options, { callParameters })); + conn.accept(); + await clock.tickAsync(0); + }); - it('should save the reconnect token', () => { - pstream.emit('answer', pStreamAnswerPayload); - assert.equal(conn['_signalingReconnectToken'], pStreamAnswerPayload.reconnect); + it('should remove event handler after disconnect for an incoming call', () => { + pstream.emit('answer', pStreamAnswerPayload); + conn.disconnect(); + assert.strictEqual(pstream.listenerCount('answer'), 0); + }); }); - describe('if raised multiple times', () => { - it('should save the reconnect token multiple times', () => { - pstream.emit('answer', pStreamAnswerPayload); - assert.equal(conn['_signalingReconnectToken'], pStreamAnswerPayload.reconnect); + describe('for outgoing calls', () => { + beforeEach(async () => { + conn = new Call(config, options); + conn.accept(); + await clock.tickAsync(0); + }); - pStreamAnswerPayload.reconnect = 'biffbazz-reconnect-token'; + it('should set the call to "answered"', () => { + pstream.emit('answer', pStreamAnswerPayload); + assert(conn['_isAnswered']); + }); + it('should save the reconnect token', () => { pstream.emit('answer', pStreamAnswerPayload); assert.equal(conn['_signalingReconnectToken'], pStreamAnswerPayload.reconnect); }); - it('should not invoke "call._maybeTransitionToOpen" more than once', () => { - const spy = conn['_maybeTransitionToOpen'] = sinon.spy(conn['_maybeTransitionToOpen']); - + it('should remove event handler after disconnect for an outgoing call', () => { pstream.emit('answer', pStreamAnswerPayload); - sinon.assert.calledOnce(spy); + conn.disconnect(); + assert.strictEqual(pstream.listenerCount('answer'), 0); + }); - pstream.emit('answer', pStreamAnswerPayload); - sinon.assert.calledOnce(spy); + describe('if raised multiple times', () => { + it('should save the reconnect token multiple times', () => { + pstream.emit('answer', pStreamAnswerPayload); + assert.equal(conn['_signalingReconnectToken'], pStreamAnswerPayload.reconnect); + + pStreamAnswerPayload.reconnect = 'biffbazz-reconnect-token'; + + pstream.emit('answer', pStreamAnswerPayload); + assert.equal(conn['_signalingReconnectToken'], pStreamAnswerPayload.reconnect); + }); + + it('should not invoke "call._maybeTransitionToOpen" more than once', () => { + const spy = conn['_maybeTransitionToOpen'] = sinon.spy(conn['_maybeTransitionToOpen']); + + pstream.emit('answer', pStreamAnswerPayload); + sinon.assert.calledOnce(spy); + + pstream.emit('answer', pStreamAnswerPayload); + sinon.assert.calledOnce(spy); + }); }); }); });