294 lines
9.7 KiB
HTML
294 lines
9.7 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>RTCDTMFSender.prototype.ontonechange</title>
|
|
<meta name="timeout" content="long">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="RTCPeerConnection-helper.js"></script>
|
|
<script src="RTCDTMFSender-helper.js"></script>
|
|
<script>
|
|
'use strict';
|
|
|
|
// Test is based on the following editor draft:
|
|
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
|
|
|
|
// The following helper functions are called from RTCPeerConnection-helper.js
|
|
// generateAnswer
|
|
|
|
// The following helper functions are called from RTCDTMFSender-helper.js
|
|
// test_tone_change_events
|
|
// getTransceiver
|
|
|
|
/*
|
|
7. Peer-to-peer DTMF
|
|
partial interface RTCRtpSender {
|
|
readonly attribute RTCDTMFSender? dtmf;
|
|
};
|
|
|
|
interface RTCDTMFSender : EventTarget {
|
|
void insertDTMF(DOMString tones,
|
|
optional unsigned long duration = 100,
|
|
optional unsigned long interToneGap = 70);
|
|
attribute EventHandler ontonechange;
|
|
readonly attribute DOMString toneBuffer;
|
|
};
|
|
|
|
[Constructor(DOMString type, RTCDTMFToneChangeEventInit eventInitDict)]
|
|
interface RTCDTMFToneChangeEvent : Event {
|
|
readonly attribute DOMString tone;
|
|
};
|
|
*/
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
11. If a Playout task is scheduled to be run; abort these steps; otherwise queue
|
|
a task that runs the following steps (Playout task):
|
|
3. If toneBuffer is an empty string, fire an event named tonechange with an
|
|
empty string at the RTCDTMFSender object and abort these steps.
|
|
4. Remove the first character from toneBuffer and let that character be tone.
|
|
6. Queue a task to be executed in duration + interToneGap ms from now that
|
|
runs the steps labelled Playout task.
|
|
7. Fire an event named tonechange with a string consisting of tone at the
|
|
RTCDTMFSender object.
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.insertDTMF('123');
|
|
}, [
|
|
['1', '23', 0],
|
|
['2', '3', 170],
|
|
['3', '', 170],
|
|
['', '', 170]
|
|
], 'insertDTMF() with default duration and intertoneGap should fire tonechange events at the expected time');
|
|
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.insertDTMF('abc', 100, 70);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 170],
|
|
['C', '', 170],
|
|
['', '', 170]
|
|
], 'insertDTMF() with explicit duration and intertoneGap should fire tonechange events at the expected time');
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
10. If toneBuffer is an empty string, abort these steps.
|
|
*/
|
|
async_test(t => {
|
|
createDtmfSender()
|
|
.then(dtmfSender => {
|
|
dtmfSender.addEventListener('tonechange',
|
|
t.unreached_func('Expect no tonechange event to be fired'));
|
|
|
|
dtmfSender.insertDTMF('', 100, 70);
|
|
|
|
t.step_timeout(t.step_func_done(), 300);
|
|
})
|
|
.catch(t.step_func(err => {
|
|
assert_unreached(`Unexpected promise rejection: ${err}`);
|
|
}));
|
|
}, `insertDTMF('') should not fire any tonechange event, including for '' tone`);
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
8. If the value of the duration parameter is less than 40, set it to 40.
|
|
If, on the other hand, the value is greater than 6000, set it to 6000.
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.insertDTMF('ABC', 10, 70);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 110],
|
|
['C', '', 110],
|
|
['', '', 110]
|
|
], 'insertDTMF() with duration less than 40 should be clamped to 40');
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
9. If the value of the interToneGap parameter is less than 30, set it to 30.
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.insertDTMF('ABC', 100, 10);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 130],
|
|
['C', '', 130],
|
|
['', '', 130]
|
|
],
|
|
'insertDTMF() with interToneGap less than 30 should be clamped to 30');
|
|
|
|
/*
|
|
[w3c/webrtc-pc#1373]
|
|
This step is added to handle the "," character correctly. "," supposed to delay the next
|
|
tonechange event by 2000ms.
|
|
|
|
7.2. insertDTMF
|
|
11.5. If tone is "," delay sending tones for 2000 ms on the associated RTP media
|
|
stream, and queue a task to be executed in 2000 ms from now that runs the
|
|
steps labelled Playout task.
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.insertDTMF('A,B', 100, 70);
|
|
|
|
}, [
|
|
['A', ',B', 0],
|
|
[',', 'B', 170],
|
|
['B', '', 2000],
|
|
['', '', 170]
|
|
], 'insertDTMF with comma should delay next tonechange event for a constant 2000ms');
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
11.1. If transceiver.stopped is true, abort these steps.
|
|
*/
|
|
test_tone_change_events((t, dtmfSender, pc) => {
|
|
const transceiver = getTransceiver(pc);
|
|
dtmfSender.addEventListener('tonechange', ev => {
|
|
if(ev.tone === 'B') {
|
|
transceiver.stop();
|
|
}
|
|
});
|
|
|
|
dtmfSender.insertDTMF('ABC', 100, 70);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 170]
|
|
], 'insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing');
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
3. If a Playout task is scheduled to be run, abort these steps;
|
|
otherwise queue a task that runs the following steps (Playout task):
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.addEventListener('tonechange', ev => {
|
|
if(ev.tone === 'B') {
|
|
dtmfSender.insertDTMF('12', 100, 70);
|
|
}
|
|
});
|
|
|
|
dtmfSender.insertDTMF('ABC', 100, 70);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 170],
|
|
['1', '2', 170],
|
|
['2', '', 170],
|
|
['', '', 170]
|
|
], 'Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones');
|
|
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
3. If a Playout task is scheduled to be run, abort these steps;
|
|
otherwise queue a task that runs the following steps (Playout task):
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.addEventListener('tonechange', ev => {
|
|
if(ev.tone === 'B') {
|
|
dtmfSender.insertDTMF('12', 100, 70);
|
|
dtmfSender.insertDTMF('34', 100, 70);
|
|
}
|
|
});
|
|
|
|
dtmfSender.insertDTMF('ABC', 100, 70);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 170],
|
|
['3', '4', 170],
|
|
['4', '', 170],
|
|
['', '', 170]
|
|
], 'Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones');
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
3. If a Playout task is scheduled to be run, abort these steps;
|
|
otherwise queue a task that runs the following steps (Playout task):
|
|
*/
|
|
test_tone_change_events((t, dtmfSender) => {
|
|
dtmfSender.addEventListener('tonechange', ev => {
|
|
if(ev.tone === 'B') {
|
|
dtmfSender.insertDTMF('');
|
|
}
|
|
});
|
|
|
|
dtmfSender.insertDTMF('ABC', 100, 70);
|
|
}, [
|
|
['A', 'BC', 0],
|
|
['B', 'C', 170],
|
|
['', '', 170]
|
|
], `Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing`);
|
|
|
|
/*
|
|
7.2. insertDTMF
|
|
11.2. If transceiver.currentDirection is recvonly or inactive, abort these steps.
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const dtmfSender = await createDtmfSender(pc);
|
|
const pc2 = pc.otherPc;
|
|
assert_true(pc2 instanceof RTCPeerConnection,
|
|
'Expect pc2 to be a RTCPeerConnection');
|
|
t.add_cleanup(() => pc2.close());
|
|
const transceiver = pc.getTransceivers()[0];
|
|
assert_equals(transceiver.sender.dtmf, dtmfSender);
|
|
|
|
// Since setRemoteDescription happens in parallel with tonechange event,
|
|
// We use a flag and allow tonechange events to be fired as long as
|
|
// the promise returned by setRemoteDescription is not yet resolved.
|
|
let remoteDescriptionIsSet = false;
|
|
|
|
// We only do basic tone verification and not check timing here
|
|
let expectedTones = ['A', 'B', 'C', 'D', ''];
|
|
|
|
const firstTone = new Promise(resolve => {
|
|
const onToneChange = t.step_func(ev => {
|
|
assert_false(remoteDescriptionIsSet,
|
|
'Expect no tonechange event to be fired after currentDirection is changed to recvonly');
|
|
|
|
const { tone } = ev;
|
|
const expectedTone = expectedTones.shift();
|
|
assert_equals(tone, expectedTone,
|
|
`Expect fired event.tone to be ${expectedTone}`);
|
|
|
|
if(tone === 'A') {
|
|
resolve();
|
|
}
|
|
});
|
|
dtmfSender.addEventListener('tonechange', onToneChange);
|
|
});
|
|
|
|
dtmfSender.insertDTMF('ABCD', 100, 70);
|
|
await firstTone;
|
|
|
|
// Only change transceiver.direction after the first
|
|
// tonechange event, to make sure that tonechange is triggered
|
|
// then stopped
|
|
transceiver.direction = 'recvonly';
|
|
await exchangeOfferAnswer(pc, pc2);
|
|
assert_equals(transceiver.currentDirection, 'inactive');
|
|
remoteDescriptionIsSet = true;
|
|
|
|
await new Promise(resolve => t.step_timeout(resolve, 300));
|
|
}, `Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing`);
|
|
|
|
/* Section 7.3 - Tone change event */
|
|
test(t => {
|
|
let ev = new RTCDTMFToneChangeEvent('tonechange', {'tone': '1'});
|
|
assert_equals(ev.type, 'tonechange');
|
|
assert_equals(ev.tone, '1');
|
|
}, 'Tone change event constructor works');
|
|
|
|
test(t => {
|
|
let ev = new RTCDTMFToneChangeEvent('worngname', {});
|
|
}, 'Tone change event with unexpected name should not crash');
|
|
|
|
test(t => {
|
|
const ev1 = new RTCDTMFToneChangeEvent('tonechange', {});
|
|
assert_equals(ev1.tone, '');
|
|
|
|
assert_equals(RTCDTMFToneChangeEvent.constructor.length, 1);
|
|
const ev2 = new RTCDTMFToneChangeEvent('tonechange');
|
|
assert_equals(ev2.tone, '');
|
|
}, 'Tone change event init optional parameters');
|
|
|
|
</script>
|