Merge "Updated CTS tests due to API change - touch exploration."
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 24e9fdc..e104d82 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -69,32 +69,23 @@
             <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
         </activity>
 
-        <activity android:name=".sensors.MagnetometerTestActivity" android:label="@string/snsr_mag_test"
-                android:screenOrientation="nosensor">
+        <activity android:name=".audioquality.AudioQualityVerifierActivity"
+                android:label="@string/aq_verifier">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
+            <meta-data android:name="test_category" android:value="@string/test_category_audio" />
         </activity>
 
-      <activity android:name=".audioquality.AudioQualityVerifierActivity"
-              android:label="@string/aq_verifier">
-          <intent-filter>
-              <action android:name="android.intent.action.MAIN" />
-              <category android:name="android.cts.intent.category.MANUAL_TEST" />
-          </intent-filter>
-          <meta-data android:name="test_category" android:value="@string/test_category_audio" />
-      </activity>
-      
-      <activity android:name=".audioquality.CalibrateVolumeActivity"
-                android:label="@string/aq_calibrate_volume_name" />
-      
-      <activity android:name=".audioquality.ViewResultsActivity"
-                android:label="@string/aq_view_results_name" />
+        <activity android:name=".audioquality.CalibrateVolumeActivity"
+                  android:label="@string/aq_calibrate_volume_name" />
 
-      <service android:name=".audioquality.ExperimentService" />
-      
+        <activity android:name=".audioquality.ViewResultsActivity"
+                  android:label="@string/aq_view_results_name" />
+
+        <service android:name=".audioquality.ExperimentService" />
+
    </application>
 
 </manifest> 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
deleted file mode 100644
index f3ba411..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.sensors;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-
-/**
- * CTS Verifier case for verifying correct integration of accelerometer.
- * Displays a wedge using OpenGL that, on a correctly-integrated device, always
- * points down.
- */
-public class MagnetometerTestActivity extends PassFailButtons.Activity {
-    private GLSurfaceView mGLSurfaceView;
-
-    private AccelerometerTestRenderer mListener;
-
-    private SensorManager mSensorManager;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mSensorManager = (SensorManager) getApplicationContext().getSystemService(
-                Context.SENSOR_SERVICE);
-        AccelerometerTestRenderer renderer = new MagnetometerTestRenderer(this);
-        mListener = renderer;
-
-        setContentView(R.layout.pass_fail_gl);
-        setPassFailButtonClickListeners();
-        setInfoResources(R.string.snsr_mag_test, R.string.snsr_mag_test_info, -1);
-        mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
-        mGLSurfaceView.setRenderer(renderer);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mSensorManager.unregisterListener(mListener);
-        mGLSurfaceView.onPause();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mSensorManager.registerListener(mListener, mSensorManager.getSensorList(
-                Sensor.TYPE_MAGNETIC_FIELD).get(0), SensorManager.SENSOR_DELAY_UI);
-        mGLSurfaceView.onResume();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java
deleted file mode 100644
index b5d4587..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.sensors;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-
-public class MagnetometerTestRenderer extends AccelerometerTestRenderer {
-    public MagnetometerTestRenderer(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
-            /*
-             * The base class is written for accelerometer, where the vector
-             * points *away* from the thing being measured (i.e. gravity). But
-             * our vector points *toward* the thing being measured (i.e.
-             * magnetic north pole). Accordingly, the base class has an
-             * inversion to handle that that doesn't apply to us, so the
-             * simplest method is just to flip our vector to point in the exact
-             * opposite direction and then everything works out in the base
-             * class.
-             */
-            event.values[0] *= -1;
-            event.values[1] *= -1;
-            event.values[2] *= -1;
-
-            // rest of method is the same as in base class
-            normalize(event.values);
-            event.values[1] *= -1;
-            crossProduct(event.values, Z_AXIS, mCrossProd);
-            mAngle = (float) Math.acos(dotProduct(event.values, Z_AXIS));
-        }
-    }
-}
diff --git a/tests/assets/webkit/form_page.html b/tests/assets/webkit/form_page.html
new file mode 100644
index 0000000..b7a32a3
--- /dev/null
+++ b/tests/assets/webkit/form_page.html
@@ -0,0 +1,41 @@
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<html>
+<head>
+    <title>Test Page</title>
+</head>
+<body>
+  <a name="text" />
+  <a id=someTextId>Some text&nbsp;</a>
+  <a id=nestedLinkId>Is not nested, and is not unique!</a>
+  <div id=divId>A div
+    <a id=nestedLinkId>Nested text</a>
+  </div>
+  <p id="spaces">     </p>
+  <p id="empty"></p>
+  <a href="foo" id="linkWithEqualsSign">Link=equalssign</a>
+  <p id="self-closed" />Here is some content that should not be in the previous p tag
+
+  <p class=" spaceAround ">Spaced out</p>
+
+  <span id="my_span">
+    <div>first_div</div>
+    <div>second_div</div>
+    <span>first_span</span>
+    <span>second_span</span>
+  </span>
+</body>
+</html>
diff --git a/tests/src/android/webkit/cts/WebDriverStubActivity.java b/tests/src/android/webkit/cts/WebDriverStubActivity.java
index 7ecb664..092156b 100644
--- a/tests/src/android/webkit/cts/WebDriverStubActivity.java
+++ b/tests/src/android/webkit/cts/WebDriverStubActivity.java
@@ -49,7 +49,6 @@
         setContentView(view);
     }
 
-
     public WebDriver getDriver() {
         return mDriver;
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index b2bdfc8..51f1201 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -54,6 +54,8 @@
     public static final String BLANK_PAGE_URL = "webkit/test_blankPage.html";
     public static final String ADD_JAVA_SCRIPT_INTERFACE_URL = "webkit/test_jsInterface.html";
 
+    public static final String FORM_PAGE_URL = "webkit/form_page.html";
+
     public static final String EXT_WEB_URL1 = "http://www.example.com/";
 
     public static final String getFileUrl(String assetName) {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java b/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java
index cd40caf..93c7b5f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java
@@ -17,8 +17,18 @@
 package android.webkit.cts;
 
 import android.test.ActivityInstrumentationTestCase2;
+import android.webkit.webdriver.By;
 import android.webkit.webdriver.WebDriver;
+import android.webkit.webdriver.WebElement;
+import android.webkit.webdriver.WebElementNotFoundException;
+import android.webkit.webdriver.WebElementStaleException;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.webkit.cts.TestHtmlConstants.FORM_PAGE_URL;
 import static android.webkit.cts.TestHtmlConstants.HELLO_WORLD_URL;
 
 /**
@@ -28,6 +38,13 @@
         ActivityInstrumentationTestCase2<WebDriverStubActivity>{
     private WebDriver mDriver;
     private CtsTestServer mWebServer;
+    private static final String SOME_TEXT = "Some text";
+    private static final String DIV_TEXT = "A div Nested text";
+    private static final String NESTED_TEXT = "Nested text";
+    private static final String DIV_ID = "divId";
+    private static final String SOME_TEXT_ID = "someTextId";
+    private static final String BAD_ID = "BadId";
+    private static final String NESTED_LINK_ID = "nestedLinkId";
 
     public WebDriverTest() {
         super(WebDriverStubActivity.class);
@@ -50,4 +67,415 @@
         mDriver.get(mWebServer.getDelayedAssetUrl(HELLO_WORLD_URL));
         assertTrue(mDriver.getPageSource().contains("hello world!"));
     }
+
+    // By id
+    public void testFindElementById() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByIdThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementById() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    // By linkText
+    public void testFindElementByLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByLinkTextThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    // By partialLinkText
+    public void testFindElementByPartialLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByPartialLinkTextThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByPartialLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    // by name
+    public void testFindElementByName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByNameThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    // By tagName
+    public void testFindElementByTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByTagNameThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    // By xpath
+    public void testFindElementByXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByXPathThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testGetTextThrowsIfElementIsStale() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.id(DIV_ID));
+        mDriver.get(mWebServer.getAssetUrl(HELLO_WORLD_URL));
+        try {
+            div.getText();
+            fail("This should have failed.");
+        } catch (WebElementStaleException e) {
+            // This is expected
+        }
+    }
+
+    public void testExecuteScriptShouldReturnAString() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Object result = mDriver.executeScript("return document.title");
+        assertEquals("Test Page", (String) result);
+    }
+
+    public void testExecuteScriptShouldReturnAWebElement() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Object result = mDriver.executeScript(
+                "return document.getElementsByTagName('div')[0];");
+        assertTrue(result instanceof WebElement);
+        assertEquals(DIV_TEXT, ((WebElement) result).getText());
+    }
+
+    public void testExecuteScriptShouldPassAndReturnADouble() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Double expected = new Double(1.2);
+        Object result = mDriver.executeScript("return arguments[0];", expected);
+        assertTrue(result instanceof Double);
+        assertEquals(expected, (Double) result);
+    }
+
+    public void testExecuteScriptShouldPassAndReturnALong() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Long expected = new Long(1);
+        Object result = mDriver.executeScript("return arguments[0];", expected);
+        assertTrue(result instanceof Long);
+        assertEquals(expected, (Long) result);
+    }
+
+    public void testExecuteScriptShouldPassReturnABoolean() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Boolean expected = new Boolean(true);
+        Object result = mDriver.executeScript("return arguments[0] === true;",
+                expected);
+        assertTrue(result instanceof Boolean);
+        assertEquals(expected, (Boolean) result);
+    }
+
+    public void testExecuteScriptShouldReturnAList() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<String> expected = new ArrayList();
+        expected.add("one");
+        expected.add("two");
+        expected.add("three");
+
+        Object result = mDriver.executeScript(
+                "return ['one', 'two', 'three'];");
+
+        assertTrue(expected.equals((List<String>) result));
+    }
+
+    public void testExecuteScriptShouldReturnNestedList() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<Object> expected = new ArrayList();
+        expected.add("one");
+        List<Object> nestedList = new ArrayList();
+        nestedList.add(true);
+        nestedList.add(false);
+        expected.add(nestedList);
+        Map<String, Object> nestedMap = new HashMap();
+        nestedMap.put("bread", "cheese");
+        nestedMap.put("hungry", true);
+        expected.add(nestedMap);
+
+        Object result = mDriver.executeScript(
+                "return ['one', [true, false], "
+                + "{bread:'cheese', hungry:true}];");
+
+        assertTrue(expected.equals(result));
+    }
+
+    public void testExecuteScriptShouldBeAbleToReturnALisOfwebElements() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> result = (List<WebElement>) mDriver.executeScript(
+                "return document.getElementsByTagName('a')");
+        assertEquals(5, result.size());
+    }
+
+    public void testExecuteScriptShouldReturnAMap() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Map<String, Object> expected = new HashMap();
+        expected.put("abc", Long.valueOf(123));
+        expected.put("cat", false);
+        Map<String, Object> nestedMap = new HashMap();
+        nestedMap.put("bread", "cheese");
+        nestedMap.put("hungry", true);
+        expected.put("map", nestedMap);
+        List<String> nestedList = new ArrayList();
+        nestedList.add("bou");
+        nestedList.add("truc");
+        expected.put("list", nestedList);
+
+        Object res = mDriver.executeScript("return {abc:123, cat:false, "
+                + "map:{bread:'cheese', hungry:true}, list:['bou', 'truc']};");
+        assertTrue(res instanceof Map);
+        Map<String, Object> result = (Map<String, Object>) res;
+        assertEquals(expected.size(), result.size());
+
+        assertTrue(expected.equals(result));
+    }
+
+    public void testExecuteScriptShouldThrowIfJsIsBad() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.executeScript("return bou();");
+            fail("This should have failed");
+        } catch (RuntimeException e) {
+            // This is expected.
+        }
+    }
+
+    public void testExecuteScriptShouldbeAbleToPassAString() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        String expected = "bou";
+        Object result = mDriver.executeScript("return arguments[0]", expected);
+        assertEquals(expected, (String) result);
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassWebElement() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.id(DIV_ID));
+        Object result = mDriver.executeScript(
+                "arguments[0]['flibble'] = arguments[0].getAttribute('id');"
+                + "return arguments[0]['flibble'];", div);
+        assertEquals(DIV_ID, (String) result);
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassAList() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<String> expected = new ArrayList();
+        expected.add("apple");
+        expected.add("cheese");
+        expected.add("food");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0].length", expected);
+        assertEquals(expected.size(), ((Long) result).intValue());
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassNestedLists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<Object> expected = new ArrayList();
+        expected.add("apple");
+        expected.add("cheese");
+        List<Integer> nested = new ArrayList();
+        nested.add(1);
+        nested.add(2);
+        expected.add(nested);
+        expected.add("food");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0][2].length", expected);
+        assertEquals(nested.size(), ((Long) result).intValue());
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassAMap() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Map<String, String> expected = new HashMap();
+        expected.put("apple", "pie");
+        expected.put("cheese", "cake");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0].apple", expected);
+        assertEquals(expected.get("apple"), (String) result);
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassNestedMaps() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Map<String, Object> expected = new HashMap();
+        expected.put("apple", "pie");
+        Map<String, String> nested = new HashMap();
+        nested.put("foo", "boo");
+        expected.put("nested", nested);
+        expected.put("cheese", "cake");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0].nested.foo", expected);
+        assertEquals(((Map<String, Object>)expected.get("nested")).get("foo"),
+                (String) result);
+    }
+
+
+    public void testExecuteScriptShouldThrowIfArgumentIsNotValid() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.executeScript("return arguments[0];", mDriver);
+            fail("This should have failed");
+        } catch (RuntimeException e) {
+            // This is expected.
+        }
+    }
+
+    public void testExecuteScriptHandlesStringCorrectly() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        String cheese = "cheese";
+        String bread = "bread";
+        Object result = mDriver.executeScript(
+                "return 'I love ' + arguments[0] + ' and ' + arguments[1]",
+                cheese, bread);
+        assertEquals("I love cheese and bread", (String) result);
+    }
+
+    public void testExecuteScriptShouldThrowIfNoPageLoaded() {
+        try {
+            mDriver.executeScript("return 'bou';");
+            fail("This should have failed");
+        } catch (RuntimeException e) {
+            // This is expected.
+        }
+    }
+
+    public void testExecuteScriptShouldBeAbleToCreatePersistentValue() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        mDriver.executeScript("document.bidule = ['hello']");
+        Object result = mDriver.executeScript(
+                "return document.bidule.shift();");
+        assertEquals("hello", (String) result);
+    }
+
+    public void testExecuteScriptEscapesQuotesAndBackslash() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo'\\\"\" == arguments[0];", "foo'\""));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo'\\\"bar\" == arguments[0];", "foo'\"bar"));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return 'foo\"' == arguments[0];", "foo\""));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo'\" == arguments[0];", "foo'"));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo\\\\\\\"\" == arguments[0];", "foo\\\""));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"f\\\"o\\\\o\\\\\\\\\\\"\" == arguments[0];",
+                "f\"o\\o\\\\\""));
+    }
 }