'use strict'; // Test is based on the following editor draft: // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html // Code using this helper should also include RTCPeerConnection-helper.js // in the main HTML file // The following helper functions are called from RTCPeerConnection-helper.js: // getTrackFromUserMedia // exchangeOfferAnswer // Create a RTCDTMFSender using getUserMedia() // Connect the PeerConnection to another PC and wait until it is // properly connected, so that DTMF can be sent. function createDtmfSender(pc = new RTCPeerConnection()) { let dtmfSender; return getTrackFromUserMedia('audio') .then(([track, mediaStream]) => { const sender = pc.addTrack(track, mediaStream); dtmfSender = sender.dtmf; assert_true(dtmfSender instanceof RTCDTMFSender, 'Expect audio sender.dtmf to be set to a RTCDTMFSender'); // Note: spec bug open - https://github.com/w3c/webrtc-pc/issues/1774 // on whether sending should be possible before negotiation. const pc2 = new RTCPeerConnection(); Object.defineProperty(pc, 'otherPc', { value: pc2 }); exchangeIceCandidates(pc, pc2); return exchangeOfferAnswer(pc, pc2); }).then(() => { if (!('canInsertDTMF' in dtmfSender)) { return Promise.resolve(); } // Wait until dtmfSender.canInsertDTMF becomes true. // Up to 150 ms has been observed in test. Wait 1 second // in steps of 10 ms. // Note: Using a short timeout and rejected promise in order to // make test return a clear error message on failure. return new Promise((resolve, reject) => { let counter = 0; step_timeout(function checkCanInsertDTMF() { if (dtmfSender.canInsertDTMF) { resolve(); } else { if (counter >= 100) { reject('Waited too long for canInsertDTMF'); return; } ++counter; step_timeout(checkCanInsertDTMF, 10); } }, 0); }); }).then(() => { return dtmfSender; }); } /* Create an RTCDTMFSender and test tonechange events on it. testFunc Test function that is going to manipulate the DTMFSender. It will be called with: t - the test object sender - the created RTCDTMFSender pc - the associated RTCPeerConnection as second argument. toneChanges Array of expected tonechange events fired. The elements are array of 3 items: expectedTone The expected character in event.tone expectedToneBuffer The expected new value of dtmfSender.toneBuffer expectedDuration The rough time since beginning or last tonechange event was fired. desc Test description. */ function test_tone_change_events(testFunc, toneChanges, desc) { // Convert to cumulative time let cumulativeTime = 0; const cumulativeToneChanges = toneChanges.map(c => { cumulativeTime += c[2]; return [c[0], c[1], cumulativeTime]; }); // Wait for same duration as last expected duration + 100ms // before passing test in case there are new tone events fired, // in which case the test should fail. const lastWait = toneChanges.pop()[2] + 100; promise_test(async t => { const pc = new RTCPeerConnection(); const dtmfSender = await createDtmfSender(pc); const start = Date.now(); const allEventsReceived = new Promise(resolve => { const onToneChange = t.step_func(ev => { assert_true(ev instanceof RTCDTMFToneChangeEvent, 'Expect tone change event object to be an RTCDTMFToneChangeEvent'); const { tone } = ev; assert_equals(typeof tone, 'string', 'Expect event.tone to be the tone string'); assert_greater_than(cumulativeToneChanges.length, 0, 'More tonechange event is fired than expected'); const [ expectedTone, expectedToneBuffer, expectedTime ] = cumulativeToneChanges.shift(); assert_equals(tone, expectedTone, `Expect current event.tone to be ${expectedTone}`); assert_equals(dtmfSender.toneBuffer, expectedToneBuffer, `Expect dtmfSender.toneBuffer to be updated to ${expectedToneBuffer}`); // We check that the cumulative delay is at least the expected one, but // system load may cause random delays, so we do not put any // realistic upper bound on the timing of the events. assert_between_inclusive(Date.now() - start, expectedTime, expectedTime + 4000, `Expect tonechange event for "${tone}" to be fired approximately after ${expectedTime} milliseconds`); if (cumulativeToneChanges.length === 0) { resolve(); } }); dtmfSender.addEventListener('tonechange', onToneChange); }); testFunc(t, dtmfSender, pc); await allEventsReceived; const wait = ms => new Promise(resolve => t.step_timeout(resolve, ms)); await wait(lastWait); }, desc); } // Get the one and only tranceiver from pc.getTransceivers(). // Assumes that there is only one tranceiver in pc. function getTransceiver(pc) { const transceivers = pc.getTransceivers(); assert_equals(transceivers.length, 1, 'Expect there to be only one tranceiver in pc'); return transceivers[0]; }