summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webrtc')
-rw-r--r--testing/web-platform/tests/webrtc/OWNERS4
-rw-r--r--testing/web-platform/tests/webrtc/datachannel-emptystring.html108
-rw-r--r--testing/web-platform/tests/webrtc/no-media-call.html139
-rw-r--r--testing/web-platform/tests/webrtc/promises-call.html121
-rw-r--r--testing/web-platform/tests/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html107
-rw-r--r--testing/web-platform/tests/webrtc/simplecall.html118
6 files changed, 597 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/OWNERS b/testing/web-platform/tests/webrtc/OWNERS
new file mode 100644
index 000000000..1deb98d49
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/OWNERS
@@ -0,0 +1,4 @@
+@dontcallmedom
+@tidoust
+@alvestrand
+@phoglund
diff --git a/testing/web-platform/tests/webrtc/datachannel-emptystring.html b/testing/web-platform/tests/webrtc/datachannel-emptystring.html
new file mode 100644
index 000000000..6af436a1d
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/datachannel-emptystring.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<!--
+This test creates a data channel between two local PeerConnection instances
+and ensures that an empty string sent by one is received by the second.
+-->
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>RTCPeerConnection Data Channel Empty String Test</title>
+</head>
+<body>
+ <div id="log"></div>
+ <h2>Messages exchanged</h2>
+ <div id="msg"></div>
+
+ <!-- These files are in place when executing on W3C. -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+ var test = async_test('Can send empty strings across a WebRTC data channel.');
+
+ var gFirstConnection = null;
+ var gSecondConnection = null;
+ var sendChannel = null;
+ var receiveChannel = null;
+
+ var onReceiveChannel = function (event) {
+ receiveChannel = event.channel;
+ receiveChannel.onmessage = onReceiveMessage;
+ };
+
+
+ // When the data channel is open, send an empty string message
+ // followed by a message that contains the string "done".
+ var onSendChannelOpen = test.step_func(function (event) {
+ var msgEl = document.getElementById('msg');
+ sendChannel.send('');
+ msgEl.innerHTML += 'Sent: [empty string]<br>';
+ sendChannel.send('done');
+ msgEl.innerHTML += 'Sent: "done"<br>';
+ });
+
+ // Check the messages received on the other side.
+ // There should be an empty string message followed by a message that
+ // contains the string "done".
+ // Pass/Fail the test according to the messages received
+ var emptyMessageReceived = false;
+ var onReceiveMessage = test.step_func(function (event) {
+ var msgEl = document.getElementById('msg');
+ msgEl.innerHTML += 'Received: ' +
+ (event.data ? '"' + event.data + '"' : '[empty string]') + '<br>';
+ if (emptyMessageReceived) {
+ assert_equals(event.data, 'done', 'The "done" message was not received');
+ test.done();
+ }
+ else {
+ assert_equals(event.data, '', 'Empty message not received');
+ emptyMessageReceived = true;
+ }
+ });
+
+ function exchangeIce(pc) {
+ return function(e) {
+ if (e.candidate) {
+ pc.addIceCandidate(e.candidate);
+ }
+ };
+ }
+
+ function exchangeDescription(pc1, pc2) {
+ return function() {
+ return pc1.setRemoteDescription(pc2.localDescription);
+ };
+ }
+
+ test.step(function() {
+ gFirstConnection = new RTCPeerConnection(null);
+
+ gSecondConnection = new RTCPeerConnection(null);
+
+ gFirstConnection.onicecandidate = exchangeIce(gSecondConnection);
+ gSecondConnection.onicecandidate = exchangeIce(gFirstConnection);
+
+ gSecondConnection.ondatachannel = onReceiveChannel;
+
+ // Note the data channel will preserve the order of messages
+ // exchanged over it by default.
+ sendChannel = gFirstConnection.createDataChannel('sendDataChannel');
+ sendChannel.onopen = onSendChannelOpen;
+
+ gFirstConnection.createOffer()
+ .then(gFirstConnection.setLocalDescription.bind(gFirstConnection))
+ .then(exchangeDescription(gSecondConnection, gFirstConnection))
+ .then(function() {
+ return gSecondConnection.createAnswer();
+ })
+ .then(gSecondConnection.setLocalDescription.bind(gSecondConnection))
+ .then(exchangeDescription(gFirstConnection, gSecondConnection))
+ .catch(test.step_func(function(e) {
+ assert_unreached('Error ' + e.name + ': ' + e.message);
+ }));
+ });
+</script>
+
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/webrtc/no-media-call.html b/testing/web-platform/tests/webrtc/no-media-call.html
new file mode 100644
index 000000000..5059ba6db
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/no-media-call.html
@@ -0,0 +1,139 @@
+<!doctype html>
+<!--
+This test uses the legacy callback API with no media, and thus does not require fake media devices.
+-->
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>RTCPeerConnection No-Media Connection Test</title>
+</head>
+<body>
+ <div id="log"></div>
+ <h2>iceConnectionState info</h2>
+ <div id="stateinfo">
+ </div>
+
+ <!-- These files are in place when executing on W3C. -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+ var test = async_test('Can set up a basic WebRTC call with no data.');
+
+ var gFirstConnection = null;
+ var gSecondConnection = null;
+
+ var onOfferCreated = test.step_func(function(offer) {
+ gFirstConnection.setLocalDescription(offer, ignoreSuccess,
+ failed('setLocalDescription first'));
+
+ // This would normally go across the application's signaling solution.
+ // In our case, the "signaling" is to call this function.
+ receiveCall(offer.sdp);
+ });
+
+ function receiveCall(offerSdp) {
+
+ var parsedOffer = new RTCSessionDescription({ type: 'offer',
+ sdp: offerSdp });
+ // These functions use the legacy interface extensions to RTCPeerConnection.
+ gSecondConnection.setRemoteDescription(parsedOffer,
+ function() {
+ gSecondConnection.createAnswer(onAnswerCreated,
+ failed('createAnswer'));
+ },
+ failed('setRemoteDescription second'));
+ };
+
+ var onAnswerCreated = test.step_func(function(answer) {
+ gSecondConnection.setLocalDescription(answer, ignoreSuccess,
+ failed('setLocalDescription second'));
+
+ // Similarly, this would go over the application's signaling solution.
+ handleAnswer(answer.sdp);
+ });
+
+ function handleAnswer(answerSdp) {
+ var parsedAnswer = new RTCSessionDescription({ type: 'answer',
+ sdp: answerSdp });
+ gFirstConnection.setRemoteDescription(parsedAnswer, ignoreSuccess,
+ failed('setRemoteDescription first'));
+ };
+
+ var onIceCandidateToFirst = test.step_func(function(event) {
+ // If event.candidate is null = no more candidates.
+ if (event.candidate) {
+ gSecondConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onIceCandidateToSecond = test.step_func(function(event) {
+ if (event.candidate) {
+ gFirstConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onRemoteStream = test.step_func(function(event) {
+ assert_unreached('WebRTC received a stream when there was none');
+ });
+
+ var onIceConnectionStateChange = test.step_func(function(event) {
+ assert_equals(event.type, 'iceconnectionstatechange');
+ assert_not_equals(gFirstConnection.iceConnectionState, "failed", "iceConnectionState of first connection");
+ assert_not_equals(gSecondConnection.iceConnectionState, "failed", "iceConnectionState of second connection");
+ var stateinfo = document.getElementById('stateinfo');
+ stateinfo.innerHTML = 'First: ' + gFirstConnection.iceConnectionState
+ + '<br>Second: ' + gSecondConnection.iceConnectionState;
+ // Note: All these combinations are legal states indicating that the
+ // call has connected. All browsers should end up in completed/completed,
+ // but as of this moment, we've chosen to terminate the test early.
+ // TODO: Revise test to ensure completed/completed is reached.
+ if (gFirstConnection.iceConnectionState == 'connected' &&
+ gSecondConnection.iceConnectionState == 'connected') {
+ test.done()
+ }
+ if (gFirstConnection.iceConnectionState == 'connected' &&
+ gSecondConnection.iceConnectionState == 'completed') {
+ test.done()
+ }
+ if (gFirstConnection.iceConnectionState == 'completed' &&
+ gSecondConnection.iceConnectionState == 'connected') {
+ test.done()
+ }
+ if (gFirstConnection.iceConnectionState == 'completed' &&
+ gSecondConnection.iceConnectionState == 'completed') {
+ test.done()
+ }
+ });
+
+ // Returns a suitable error callback.
+ function failed(function_name) {
+ return test.step_func(function() {
+ assert_unreached('WebRTC called error callback for ' + function_name);
+ });
+ }
+
+ // Returns a suitable do-nothing.
+ function ignoreSuccess(function_name) {
+ }
+
+ // This function starts the test.
+ test.step(function() {
+ gFirstConnection = new RTCPeerConnection(null);
+ gFirstConnection.onicecandidate = onIceCandidateToFirst;
+ gFirstConnection.oniceconnectionstatechange = onIceConnectionStateChange;
+
+ gSecondConnection = new RTCPeerConnection(null);
+ gSecondConnection.onicecandidate = onIceCandidateToSecond;
+ gSecondConnection.onaddstream = onRemoteStream;
+ gSecondConnection.oniceconnectionstatechange = onIceConnectionStateChange;
+
+ // The offerToReceiveVideo is necessary and sufficient to make
+ // an actual connection.
+ gFirstConnection.createOffer(onOfferCreated, failed('createOffer'),
+ {offerToReceiveVideo: true});
+ });
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/webrtc/promises-call.html b/testing/web-platform/tests/webrtc/promises-call.html
new file mode 100644
index 000000000..d83508a38
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/promises-call.html
@@ -0,0 +1,121 @@
+<!doctype html>
+<!--
+This test uses data only, and thus does not require fake media devices.
+-->
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>RTCPeerConnection Data-Only Connection Test with Promises</title>
+</head>
+<body>
+ <div id="log"></div>
+ <h2>iceConnectionState info</h2>
+ <div id="stateinfo">
+ </div>
+
+ <!-- These files are in place when executing on W3C. -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+ var test = async_test('Can set up a basic WebRTC call with only data using promises.');
+
+ var gFirstConnection = null;
+ var gSecondConnection = null;
+
+ var onIceCandidateToFirst = test.step_func(function(event) {
+ // If event.candidate is null = no more candidates.
+ if (event.candidate) {
+ gSecondConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onIceCandidateToSecond = test.step_func(function(event) {
+ if (event.candidate) {
+ gFirstConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onRemoteStream = test.step_func(function(event) {
+ assert_unreached('WebRTC received a stream when there was none');
+ });
+
+ var onIceConnectionStateChange = test.step_func(function(event) {
+ assert_equals(event.type, 'iceconnectionstatechange');
+ var stateinfo = document.getElementById('stateinfo');
+ stateinfo.innerHTML = 'First: ' + gFirstConnection.iceConnectionState
+ + '<br>Second: ' + gSecondConnection.iceConnectionState;
+ // Note: All these combinations are legal states indicating that the
+ // call has connected. All browsers should end up in completed/completed,
+ // but as of this moment, we've chosen to terminate the test early.
+ // TODO: Revise test to ensure completed/completed is reached.
+ if (gFirstConnection.iceConnectionState == 'connected' &&
+ gSecondConnection.iceConnectionState == 'connected') {
+ test.done()
+ }
+ if (gFirstConnection.iceConnectionState == 'connected' &&
+ gSecondConnection.iceConnectionState == 'completed') {
+ test.done()
+ }
+ if (gFirstConnection.iceConnectionState == 'completed' &&
+ gSecondConnection.iceConnectionState == 'connected') {
+ test.done()
+ }
+ if (gFirstConnection.iceConnectionState == 'completed' &&
+ gSecondConnection.iceConnectionState == 'completed') {
+ test.done()
+ }
+ });
+
+ // This function starts the test.
+ test.step(function() {
+ gFirstConnection = new RTCPeerConnection(null);
+ gFirstConnection.onicecandidate = onIceCandidateToFirst;
+ gFirstConnection.oniceconnectionstatechange = onIceConnectionStateChange;
+
+ gSecondConnection = new RTCPeerConnection(null);
+ gSecondConnection.onicecandidate = onIceCandidateToSecond;
+ gSecondConnection.onaddstream = onRemoteStream;
+ gSecondConnection.oniceconnectionstatechange = onIceConnectionStateChange;
+
+ // The createDataChannel is necessary and sufficient to make
+ // sure the ICE connection be attempted.
+ gFirstConnection.createDataChannel('channel');
+
+ var atStep = 'Create offer';
+
+ gFirstConnection.createOffer()
+ .then(function(offer) {
+ atStep = 'Set local description at first';
+ return gFirstConnection.setLocalDescription(offer);
+ })
+ .then(function() {
+ atStep = 'Set remote description at second';
+ return gSecondConnection.setRemoteDescription(
+ gFirstConnection.localDescription);
+ })
+ .then(function() {
+ atStep = 'Create answer';
+ return gSecondConnection.createAnswer();
+ })
+ .then(function(answer) {
+ atStep = 'Set local description at second';
+ return gSecondConnection.setLocalDescription(answer);
+ })
+ .then(function() {
+ atStep = 'Set remote description at first';
+ return gFirstConnection.setRemoteDescription(
+ gSecondConnection.localDescription);
+ })
+ .then(function() {
+ atStep = 'Negotiation completed';
+ })
+ .catch(test.step_func(function(e) {
+ assert_unreached('Error ' + e.name + ': ' + e.message +
+ ' happened at step ' + atStep);
+ }));
+ });
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html b/testing/web-platform/tests/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html
new file mode 100644
index 000000000..298cfb5ca
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html
@@ -0,0 +1,107 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>IDL check of RTCPeerConnection</title>
+<link rel="author" title="Harald Alvestrand" href="mailto:hta@google.com"/>
+<link rel="help" href="http://w3c.github.io/webrtc-pc/#rtcpeerconnection-interface">
+</head>
+<body>
+
+<h1 class="instructions">Description</h1>
+<p class="instructions">This test verifies the availability of the RTCPeerConnection interface.</p>
+<div id='log'></div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+
+<!-- The IDL is copied from the 22 September 2015 editors' draft. -->
+<script type="text/plain">
+interface EventTarget {
+ // Only a dummy definition is needed here.
+};
+[ Constructor (optional RTCConfiguration configuration)]
+interface RTCPeerConnection : EventTarget {
+ Promise<RTCSessionDescription> createOffer (optional RTCOfferOptions options);
+ Promise<RTCSessionDescription> createAnswer (optional RTCAnswerOptions options);
+ Promise<void> setLocalDescription (RTCSessionDescription description);
+ readonly attribute RTCSessionDescription? localDescription;
+ readonly attribute RTCSessionDescription? currentLocalDescription;
+ readonly attribute RTCSessionDescription? pendingLocalDescription;
+ Promise<void> setRemoteDescription (RTCSessionDescription description);
+ readonly attribute RTCSessionDescription? remoteDescription;
+ readonly attribute RTCSessionDescription? currentRemoteDescription;
+ readonly attribute RTCSessionDescription? pendingRemoteDescription;
+ Promise<void> addIceCandidate (RTCIceCandidate candidate);
+ readonly attribute RTCSignalingState signalingState;
+ readonly attribute RTCIceGatheringState iceGatheringState;
+ readonly attribute RTCIceConnectionState iceConnectionState;
+ readonly attribute boolean? canTrickleIceCandidates;
+ RTCConfiguration getConfiguration ();
+ void setConfiguration (RTCConfiguration configuration);
+ void close ();
+ attribute EventHandler onnegotiationneeded;
+ attribute EventHandler onicecandidate;
+ attribute EventHandler onsignalingstatechange;
+ attribute EventHandler oniceconnectionstatechange;
+ attribute EventHandler onicegatheringstatechange;
+};
+
+partial interface RTCPeerConnection {
+ void createOffer (RTCSessionDescriptionCallback successCallback, RTCPeerConnectionErrorCallback failureCallback, optional RTCOfferOptions options);
+ void setLocalDescription (RTCSessionDescription description, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
+ void createAnswer (RTCSessionDescriptionCallback successCallback, RTCPeerConnectionErrorCallback failureCallback);
+ void setRemoteDescription (RTCSessionDescription description, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
+ void addIceCandidate (RTCIceCandidate candidate, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
+ void getStats (MediaStreamTrack? selector, RTCStatsCallback successCallback, RTCPeerConnectionErrorCallback failureCallback);
+};
+
+partial interface RTCPeerConnection {
+ static Promise<RTCCertificate> generateCertificate (AlgorithmIdentifier keygenAlgorithm);
+};
+
+partial interface RTCPeerConnection {
+ sequence<RTCRtpSender> getSenders ();
+ sequence<RTCRtpReceiver> getReceivers ();
+ RTCRtpSender addTrack (MediaStreamTrack track, MediaStream... streams);
+ void removeTrack (RTCRtpSender sender);
+ attribute EventHandler ontrack;
+};
+
+partial interface RTCPeerConnection {
+ RTCDataChannel createDataChannel ([TreatNullAs=EmptyString] DOMString label, optional RTCDataChannelInit dataChannelDict);
+ attribute EventHandler ondatachannel;
+};
+
+partial interface RTCPeerConnection {
+ readonly attribute RTCDTMFSender? dtmf;
+};
+
+partial interface RTCPeerConnection {
+ Promise<RTCStatsReport> getStats (optional MediaStreamTrack? selector);
+};
+
+partial interface RTCPeerConnection {
+ void setIdentityProvider (DOMString provider, optional DOMString protocol, optional DOMString usernameHint);
+ Promise<DOMString> getIdentityAssertion ();
+ readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
+ readonly attribute DOMString? idpLoginUrl;
+};
+
+</script>
+<script>
+(function() {
+ var idl_array = new IdlArray();
+ [].forEach.call(document.querySelectorAll("script[type=text\\/plain]"),
+ function(node) {
+ idl_array.add_idls(node.textContent);
+ });
+ window.pc = new RTCPeerConnection(null);
+ idl_array.add_objects({"RTCPeerConnection": ["pc"]});
+ idl_array.test();
+ done();
+})();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/webrtc/simplecall.html b/testing/web-platform/tests/webrtc/simplecall.html
new file mode 100644
index 000000000..698586894
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/simplecall.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<!--
+To quickly iterate when developing this test, use --use-fake-ui-for-media-stream
+for Chrome and set the media.navigator.permission.disabled property to true in
+Firefox. You must either have a webcam/mic available on the system or use for
+instance --use-fake-device-for-media-stream for Chrome.
+-->
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>RTCPeerConnection Connection Test</title>
+</head>
+<body>
+ <div id="log"></div>
+ <div>
+ <video id="local-view" autoplay="autoplay"></video>
+ <video id="remote-view" autoplay="autoplay"/>
+ </video>
+ </div>
+
+ <!-- These files are in place when executing on W3C. -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/vendor-prefix.js"
+ data-prefixed-objects=
+ '[{"ancestors":["navigator"], "name":"getUserMedia"}]'
+ data-prefixed-prototypes=
+ '[{"ancestors":["HTMLMediaElement"],"name":"srcObject"}]'>
+ </script>
+ <script type="text/javascript">
+ var test = async_test('Can set up a basic WebRTC call.', {timeout: 5000});
+
+ var gFirstConnection = null;
+ var gSecondConnection = null;
+
+ function getUserMediaOkCallback(localStream) {
+ gFirstConnection = new RTCPeerConnection(null);
+ gFirstConnection.onicecandidate = onIceCandidateToFirst;
+ gFirstConnection.addStream(localStream);
+ gFirstConnection.createOffer(onOfferCreated, failed('createOffer'));
+
+ var videoTag = document.getElementById('local-view');
+ videoTag.srcObject = localStream;
+ };
+
+ var onOfferCreated = test.step_func(function(offer) {
+ gFirstConnection.setLocalDescription(offer);
+
+ // This would normally go across the application's signaling solution.
+ // In our case, the "signaling" is to call this function.
+ receiveCall(offer.sdp);
+ });
+
+ function receiveCall(offerSdp) {
+ gSecondConnection = new RTCPeerConnection(null);
+ gSecondConnection.onicecandidate = onIceCandidateToSecond;
+ gSecondConnection.onaddstream = onRemoteStream;
+
+ var parsedOffer = new RTCSessionDescription({ type: 'offer',
+ sdp: offerSdp });
+ gSecondConnection.setRemoteDescription(parsedOffer);
+
+ gSecondConnection.createAnswer(onAnswerCreated,
+ failed('createAnswer'));
+ };
+
+ var onAnswerCreated = test.step_func(function(answer) {
+ gSecondConnection.setLocalDescription(answer);
+
+ // Similarly, this would go over the application's signaling solution.
+ handleAnswer(answer.sdp);
+ });
+
+ function handleAnswer(answerSdp) {
+ var parsedAnswer = new RTCSessionDescription({ type: 'answer',
+ sdp: answerSdp });
+ gFirstConnection.setRemoteDescription(parsedAnswer);
+
+ // Call negotiated: done.
+ test.done();
+ };
+
+ var onIceCandidateToFirst = test.step_func(function(event) {
+ // If event.candidate is null = no more candidates.
+ if (event.candidate) {
+ gSecondConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onIceCandidateToSecond = test.step_func(function(event) {
+ if (event.candidate) {
+ gFirstConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onRemoteStream = test.step_func(function(event) {
+ var videoTag = document.getElementById('remote-view');
+ videoTag.srcObject = event.stream;
+ });
+
+ // Returns a suitable error callback.
+ function failed(function_name) {
+ return test.step_func(function() {
+ assert_unreached('WebRTC called error callback for ' + function_name);
+ });
+ }
+
+ // This function starts the test.
+ test.step(function() {
+ navigator.getUserMedia({ video: true, audio: true },
+ getUserMediaOkCallback,
+ failed('getUserMedia'));
+ });
+</script>
+
+</body>
+</html>