Bug 851523 - Allow users to execute action chain in segments, r=mdas, a=test-only
authorYiming Yang <yiyang@mozilla.com>
Fri, 22 Mar 2013 11:29:02 -0700
changeset 118845 6e8ba2ff90fe808ebe27f6cddaaca0dcd60b6c5a
parent 118844 d7ed28327f1c3f49373b0dd08a39cce16c9862fa
child 118846 3f3e27f4030da2ec60c2f24b978dca09022496d9
push id141
push userjgriffin@mozilla.com
push dateWed, 01 May 2013 23:02:51 +0000
reviewersmdas, test-only
bugs851523
milestone18.0
Bug 851523 - Allow users to execute action chain in segments, r=mdas, a=test-only
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_single_finger.py
testing/marionette/client/marionette/www/testAction.html
testing/marionette/client/setup.py
testing/marionette/marionette-actors.js
testing/marionette/marionette-listener.js
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -106,16 +106,17 @@ class HTMLElement(object):
     @property
     def location(self):
         return self.marionette._send_message('getElementPosition', 'value', element=self.id)
 
 class Actions(object):
     def __init__(self, marionette):
         self.action_chain = []
         self.marionette = marionette
+        self.current_id = None
 
     def press(self, element, x=None, y=None):
         element=element.id
         self.action_chain.append(['press', element, x, y])
         return self
 
     def release(self):
         self.action_chain.append(['release'])
@@ -134,17 +135,19 @@ class Actions(object):
         self.action_chain.append(['wait', time])
         return self
 
     def cancel(self):
         self.action_chain.append(['cancel'])
         return self
 
     def perform(self):
-        return self.marionette._send_message('actionChain', 'ok', value=self.action_chain)
+        self.current_id = self.marionette._send_message('actionChain', 'value', chain=self.action_chain, nextId=self.current_id)
+        self.action_chain = []
+        return self
 
 class MultiActions(object):
     def __init__(self, marionette):
         self.multi_actions = []
         self.max_length = 0
         self.marionette = marionette
 
     def add(self, action):
--- a/testing/marionette/client/marionette/tests/unit/test_single_finger.py
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger.py
@@ -1,15 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import time
 from marionette_test import MarionetteTestCase
 from marionette import Actions
+from errors import NoSuchElementException
 
 class testSingleFinger(MarionetteTestCase):
     def test_wait(self):
         testTouch = self.marionette.absolute_url("testAction.html")
         self.marionette.navigate(testTouch)
         button = self.marionette.find_element("id", "mozLinkCopy")
         action = Actions(self.marionette)
         action.press(button).wait(5).release()
@@ -34,8 +35,27 @@ class testSingleFinger(MarionetteTestCas
         self.marionette.navigate(testTouch)
         ele = self.marionette.find_element("id", "mozLink")
         action = Actions(self.marionette)
         action.press(ele).move_by_offset(0,150).move_by_offset(0,150).release()
         action.perform()
         time.sleep(15)
         self.assertEqual("Move", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
         self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
+
+    def test_chain(self):
+        testTouch = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testTouch)
+        action = Actions(self.marionette)
+        button1 = self.marionette.find_element("id", "mozLinkCopy2")
+        action.press(button1).perform()
+        button2 = self.marionette.find_element("id", "delayed")
+        time.sleep(5)
+        action.move(button2).release().perform()
+        time.sleep(15)
+        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('delayed').innerHTML;"))
+
+    def test_no_press(self):
+        testTouch = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testTouch)
+        action = Actions(self.marionette)
+        action.release()
+        self.assertRaises(NoSuchElementException, action.perform)
--- a/testing/marionette/client/marionette/www/testAction.html
+++ b/testing/marionette/client/marionette/www/testAction.html
@@ -102,24 +102,33 @@
         second.innerHTML = "Error";
       }
     }
 
     function changeTimePress() {
       var fourth = document.getElementById("mozLinkCopy2");
       var d = new Date();
       fourth.innerHTML = d.getTime();
+      var newButton = document.createElement("button");
+      newButton.id = "delayed";
+      newButton.setAttribute("style", "position:absolute;left:80px;top:105px;");
+      var content = document.createTextNode("Button6");
+      newButton.appendChild(content);
+      document.body.appendChild(newButton);
     }
 
-    function changeTimeRelease() {
+    function changeTimeRelease(event) {
       var fourth = document.getElementById("mozLinkCopy2");
       if (fourth.innerHTML != "Button4") {
         var d = new Date();
         var timeDiff = d.getTime() - fourth.innerHTML;
         fourth.innerHTML = timeDiff;
       }
       else {
         fourth.innerHTML = "Error";
       }
+      if (checkPosition(event, "delayed")) {
+        document.getElementById("delayed").innerHTML = "End";
+      }
     }
   </script>
 </body>
 </html>
--- a/testing/marionette/client/setup.py
+++ b/testing/marionette/client/setup.py
@@ -1,12 +1,12 @@
 import os
 from setuptools import setup, find_packages
 
-version = '0.5.22'
+version = '0.5.23'
 
 # get documentation from the README
 try:
     here = os.path.dirname(os.path.abspath(__file__))
     description = file(os.path.join(here, 'README.md')).read()
 except (OSError, IOError):
     description = ''
 
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -1402,17 +1402,18 @@ MarionetteDriverActor.prototype = {
    *        'value' represents a nested array: inner array represents each event; outer array represents collection of events
    */
   actionChain: function MDA_actionChain(aRequest) {
     this.command_id = this.getCommandId();
     if (this.context == "chrome") {
       this.sendError("Not in Chrome", 500, null, this.command_id);
     }
     else {
-      this.sendAsync("actionChain", {value: aRequest.value,
+      this.sendAsync("actionChain", {chain: aRequest.chain,
+                                     nextId: aRequest.nextId,
                                      command_id: this.command_id});
     }
   },
 
   /**
    * multiAction
    *
    * @param object aRequest
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -928,17 +928,17 @@ function cancelTouch(msg) {
  * Function to emit touch events for each finger. e.g. finger=[['press', id], ['wait', 5], ['release']]
  * touchId represents the finger id, i keeps track of the current action of the finger
  */
 function actions(finger, touchId, command_id, i){
   if (typeof i === "undefined") {
     i = 0;
   }
   if (i == finger.length) {
-    sendOk(command_id);
+    sendResponse({value: touchId}, command_id);
     return;
   }
   let pack = finger[i];
   let command = pack[0];
   // el has the id
   let el;
   let corx;
   let cory;
@@ -955,34 +955,46 @@ function actions(finger, touchId, comman
          return;
       }
       touch = createATouch(el, corx, cory, touchId);
       lastTouch = touch;
       emitTouchEvent('touchstart', touch);
       actions(finger,touchId, command_id, i);
       break;
     case 'release':
+      if (lastTouch == null) {
+        sendError("Element has not been pressed: no such element", 7, null, command_id);
+        return;
+      }
       touch = lastTouch;
       lastTouch = null;
       emitTouchEvent('touchend', touch);
       actions(finger, touchId, command_id, i);
       break;
     case 'move':
+      if (lastTouch == null) {
+        sendError("Element has not been pressed: no such element", 7, null, command_id);
+        return;
+      }
       el = elementManager.getKnownElement(pack[1], curWindow);
       let boxTarget = el.getBoundingClientRect();
       let startElement = lastTouch.target;
       let boxStart = startElement.getBoundingClientRect();
       corx = boxTarget.left - boxStart.left + boxTarget.width * 0.5;
       cory = boxTarget.top - boxStart.top + boxTarget.height * 0.5;
       touch = createATouch(startElement, corx, cory, touchId);
       lastTouch = touch;
       emitTouchEvent('touchmove', touch);
       actions(finger, touchId, command_id, i);
       break;
     case 'moveByOffset':
+      if (lastTouch == null) {
+        sendError("Element has not been pressed: no such element", 7, null, command_id);
+        return;
+      }
       el = lastTouch.target;
       let doc = el.ownerDocument;
       let win = doc.defaultView;
       let clientX = lastTouch.clientX + pack[1],
           clientY = lastTouch.clientY + pack[2];
       let pageX = clientX + win.pageXOffset,
           pageY = clientY + win.pageYOffset;
       let screenX = clientX + win.mozInnerScreenX,
@@ -1010,22 +1022,24 @@ function actions(finger, touchId, comman
   }
 }
 
 /**
  * Function to start action chain on one finger 
  */
 function actionChain(msg) {
   let command_id = msg.json.command_id;
-  let args = msg.json.value;
+  let args = msg.json.chain;
+  let touchId = msg.json.nextId;
   try {
     let commandArray = elementManager.convertWrappedArguments(args, curWindow);
-    // each finger associates with one touchId
-    let touchId = nextTouchId++;
     // loop the action array [ ['press', id], ['move', id], ['release', id] ]
+    if (touchId == null) {
+      touchId = nextTouchId++;
+    }
     actions(commandArray, touchId, command_id);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, msg.json.command_id);
   }
 }
 
 /**