moving to scripts
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,86 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
|
||||
from selenium.webdriver.remote.command import Command
|
||||
from . import interaction
|
||||
from .key_actions import KeyActions
|
||||
from .key_input import KeyInput
|
||||
from .pointer_actions import PointerActions
|
||||
from .pointer_input import PointerInput
|
||||
|
||||
|
||||
class ActionBuilder(object):
|
||||
def __init__(self, driver, mouse=None, keyboard=None, duration=250):
|
||||
if not mouse:
|
||||
mouse = PointerInput(interaction.POINTER_MOUSE, "mouse")
|
||||
if not keyboard:
|
||||
keyboard = KeyInput(interaction.KEY)
|
||||
self.devices = [mouse, keyboard]
|
||||
self._key_action = KeyActions(keyboard)
|
||||
self._pointer_action = PointerActions(mouse, duration=duration)
|
||||
self.driver = driver
|
||||
|
||||
def get_device_with(self, name):
|
||||
try:
|
||||
idx = self.devices.index(name)
|
||||
return self.devices[idx]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@property
|
||||
def pointer_inputs(self):
|
||||
return [device for device in self.devices if device.type == interaction.POINTER]
|
||||
|
||||
@property
|
||||
def key_inputs(self):
|
||||
return [device for device in self.devices if device.type == interaction.KEY]
|
||||
|
||||
@property
|
||||
def key_action(self):
|
||||
return self._key_action
|
||||
|
||||
@property
|
||||
def pointer_action(self):
|
||||
return self._pointer_action
|
||||
|
||||
def add_key_input(self, name):
|
||||
new_input = KeyInput(name)
|
||||
self._add_input(new_input)
|
||||
return new_input
|
||||
|
||||
def add_pointer_input(self, kind, name):
|
||||
new_input = PointerInput(kind, name)
|
||||
self._add_input(new_input)
|
||||
return new_input
|
||||
|
||||
def perform(self):
|
||||
enc = {"actions": []}
|
||||
for device in self.devices:
|
||||
encoded = device.encode()
|
||||
if encoded['actions']:
|
||||
enc["actions"].append(encoded)
|
||||
device.actions = []
|
||||
self.driver.execute(Command.W3C_ACTIONS, enc)
|
||||
|
||||
def clear_actions(self):
|
||||
"""
|
||||
Clears actions that are already stored on the remote end
|
||||
"""
|
||||
self.driver.execute(Command.W3C_CLEAR_ACTIONS)
|
||||
|
||||
def _add_input(self, input):
|
||||
self.devices.append(input)
|
||||
@@ -0,0 +1,43 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
|
||||
import uuid
|
||||
|
||||
|
||||
class InputDevice(object):
|
||||
"""
|
||||
Describes the input device being used for the action.
|
||||
"""
|
||||
def __init__(self, name=None):
|
||||
if not name:
|
||||
self.name = uuid.uuid4()
|
||||
else:
|
||||
self.name = name
|
||||
|
||||
self.actions = []
|
||||
|
||||
def add_action(self, action):
|
||||
"""
|
||||
|
||||
"""
|
||||
self.actions.append(action)
|
||||
|
||||
def clear_actions(self):
|
||||
self.actions = []
|
||||
|
||||
def create_pause(self, duration=0):
|
||||
pass
|
||||
@@ -0,0 +1,50 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
|
||||
|
||||
KEY = "key"
|
||||
POINTER = "pointer"
|
||||
NONE = "none"
|
||||
SOURCE_TYPES = set([KEY, POINTER, NONE])
|
||||
|
||||
POINTER_MOUSE = "mouse"
|
||||
POINTER_TOUCH = "touch"
|
||||
POINTER_PEN = "pen"
|
||||
|
||||
POINTER_KINDS = set([POINTER_MOUSE, POINTER_TOUCH, POINTER_PEN])
|
||||
|
||||
|
||||
class Interaction(object):
|
||||
|
||||
PAUSE = "pause"
|
||||
|
||||
def __init__(self, source):
|
||||
self.source = source
|
||||
|
||||
|
||||
class Pause(Interaction):
|
||||
|
||||
def __init__(self, source, duration=0):
|
||||
super(Interaction, self).__init__()
|
||||
self.source = source
|
||||
self.duration = duration
|
||||
|
||||
def encode(self):
|
||||
return {
|
||||
"type": self.PAUSE,
|
||||
"duration": int(self.duration * 1000)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
from .interaction import Interaction, KEY
|
||||
from .key_input import KeyInput
|
||||
from ..utils import keys_to_typing
|
||||
|
||||
|
||||
class KeyActions(Interaction):
|
||||
|
||||
def __init__(self, source=None):
|
||||
if not source:
|
||||
source = KeyInput(KEY)
|
||||
self.source = source
|
||||
super(KeyActions, self).__init__(source)
|
||||
|
||||
def key_down(self, letter):
|
||||
return self._key_action("create_key_down", letter)
|
||||
|
||||
def key_up(self, letter):
|
||||
return self._key_action("create_key_up", letter)
|
||||
|
||||
def pause(self, duration=0):
|
||||
return self._key_action("create_pause", duration)
|
||||
|
||||
def send_keys(self, text):
|
||||
if not isinstance(text, list):
|
||||
text = keys_to_typing(text)
|
||||
for letter in text:
|
||||
self.key_down(letter)
|
||||
self.key_up(letter)
|
||||
return self
|
||||
|
||||
def _key_action(self, action, letter):
|
||||
meth = getattr(self.source, action)
|
||||
meth(letter)
|
||||
return self
|
||||
@@ -0,0 +1,51 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
from . import interaction
|
||||
|
||||
from .input_device import InputDevice
|
||||
from .interaction import (Interaction,
|
||||
Pause)
|
||||
|
||||
|
||||
class KeyInput(InputDevice):
|
||||
def __init__(self, name):
|
||||
super(KeyInput, self).__init__()
|
||||
self.name = name
|
||||
self.type = interaction.KEY
|
||||
|
||||
def encode(self):
|
||||
return {"type": self.type, "id": self.name, "actions": [acts.encode() for acts in self.actions]}
|
||||
|
||||
def create_key_down(self, key):
|
||||
self.add_action(TypingInteraction(self, "keyDown", key))
|
||||
|
||||
def create_key_up(self, key):
|
||||
self.add_action(TypingInteraction(self, "keyUp", key))
|
||||
|
||||
def create_pause(self, pause_duration=0):
|
||||
self.add_action(Pause(self, pause_duration))
|
||||
|
||||
|
||||
class TypingInteraction(Interaction):
|
||||
|
||||
def __init__(self, source, type_, key):
|
||||
super(TypingInteraction, self).__init__(source)
|
||||
self.type = type_
|
||||
self.key = key
|
||||
|
||||
def encode(self):
|
||||
return {"type": self.type, "value": self.key}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
|
||||
|
||||
class MouseButton(object):
|
||||
|
||||
LEFT = 0
|
||||
MIDDLE = 1
|
||||
RIGHT = 2
|
||||
@@ -0,0 +1,109 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
from . import interaction
|
||||
|
||||
from .interaction import Interaction
|
||||
from .mouse_button import MouseButton
|
||||
from .pointer_input import PointerInput
|
||||
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
|
||||
class PointerActions(Interaction):
|
||||
|
||||
def __init__(self, source=None, duration=250):
|
||||
"""
|
||||
Args:
|
||||
- source: PointerInput instance
|
||||
- duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in source
|
||||
"""
|
||||
if not source:
|
||||
source = PointerInput(interaction.POINTER_MOUSE, "mouse")
|
||||
self.source = source
|
||||
self._duration = duration
|
||||
super(PointerActions, self).__init__(source)
|
||||
|
||||
def pointer_down(self, button=MouseButton.LEFT):
|
||||
self._button_action("create_pointer_down", button=button)
|
||||
|
||||
def pointer_up(self, button=MouseButton.LEFT):
|
||||
self._button_action("create_pointer_up", button=button)
|
||||
|
||||
def move_to(self, element, x=None, y=None):
|
||||
if not isinstance(element, WebElement):
|
||||
raise AttributeError("move_to requires a WebElement")
|
||||
if x or y:
|
||||
el_rect = element.rect
|
||||
left_offset = el_rect['width'] / 2
|
||||
top_offset = el_rect['height'] / 2
|
||||
left = -left_offset + (x or 0)
|
||||
top = -top_offset + (y or 0)
|
||||
else:
|
||||
left = 0
|
||||
top = 0
|
||||
self.source.create_pointer_move(origin=element, duration=self._duration, x=int(left), y=int(top))
|
||||
return self
|
||||
|
||||
def move_by(self, x, y):
|
||||
self.source.create_pointer_move(origin=interaction.POINTER, duration=self._duration, x=int(x), y=int(y))
|
||||
return self
|
||||
|
||||
def move_to_location(self, x, y):
|
||||
self.source.create_pointer_move(origin='viewport', duration=self._duration, x=int(x), y=int(y))
|
||||
return self
|
||||
|
||||
def click(self, element=None):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down(MouseButton.LEFT)
|
||||
self.pointer_up(MouseButton.LEFT)
|
||||
return self
|
||||
|
||||
def context_click(self, element=None):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down(MouseButton.RIGHT)
|
||||
self.pointer_up(MouseButton.RIGHT)
|
||||
return self
|
||||
|
||||
def click_and_hold(self, element=None):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down()
|
||||
return self
|
||||
|
||||
def release(self):
|
||||
self.pointer_up()
|
||||
return self
|
||||
|
||||
def double_click(self, element=None):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down(MouseButton.LEFT)
|
||||
self.pointer_up(MouseButton.LEFT)
|
||||
self.pointer_down(MouseButton.LEFT)
|
||||
self.pointer_up(MouseButton.LEFT)
|
||||
return self
|
||||
|
||||
def pause(self, duration=0):
|
||||
self.source.create_pause(duration)
|
||||
return self
|
||||
|
||||
def _button_action(self, action, button=MouseButton.LEFT):
|
||||
meth = getattr(self.source, action)
|
||||
meth(button)
|
||||
return self
|
||||
@@ -0,0 +1,63 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you 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.
|
||||
from .input_device import InputDevice
|
||||
from .interaction import POINTER, POINTER_KINDS
|
||||
|
||||
from selenium.common.exceptions import InvalidArgumentException
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
|
||||
class PointerInput(InputDevice):
|
||||
|
||||
DEFAULT_MOVE_DURATION = 250
|
||||
|
||||
def __init__(self, kind, name):
|
||||
super(PointerInput, self).__init__()
|
||||
if kind not in POINTER_KINDS:
|
||||
raise InvalidArgumentException("Invalid PointerInput kind '%s'" % kind)
|
||||
self.type = POINTER
|
||||
self.kind = kind
|
||||
self.name = name
|
||||
|
||||
def create_pointer_move(self, duration=DEFAULT_MOVE_DURATION, x=None, y=None, origin=None):
|
||||
action = dict(type="pointerMove", duration=duration)
|
||||
action["x"] = x
|
||||
action["y"] = y
|
||||
if isinstance(origin, WebElement):
|
||||
action["origin"] = {"element-6066-11e4-a52e-4f735466cecf": origin.id}
|
||||
elif origin:
|
||||
action["origin"] = origin
|
||||
|
||||
self.add_action(action)
|
||||
|
||||
def create_pointer_down(self, button):
|
||||
self.add_action({"type": "pointerDown", "duration": 0, "button": button})
|
||||
|
||||
def create_pointer_up(self, button):
|
||||
self.add_action({"type": "pointerUp", "duration": 0, "button": button})
|
||||
|
||||
def create_pointer_cancel(self):
|
||||
self.add_action({"type": "pointerCancel"})
|
||||
|
||||
def create_pause(self, pause_duration):
|
||||
self.add_action({"type": "pause", "duration": int(pause_duration * 1000)})
|
||||
|
||||
def encode(self):
|
||||
return {"type": self.type,
|
||||
"parameters": {"pointerType": self.kind},
|
||||
"id": self.name,
|
||||
"actions": [acts for acts in self.actions]}
|
||||
Reference in New Issue
Block a user