Skip to content

Commit

Permalink
Merge pull request #6 from HDE/chained-actions
Browse files Browse the repository at this point in the history
Chained actions
  • Loading branch information
ojii committed Jun 16, 2017
2 parents 35711a6 + e80a69d commit 9df69e2
Show file tree
Hide file tree
Showing 17 changed files with 717 additions and 216 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ jobs:
steps:
- setup_remote_docker
- checkout
- run: docker-compose pull
- run: docker-compose build
- run:
name: Run tests
Expand Down
29 changes: 21 additions & 8 deletions .circleci/docker/arsenic-test/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
FROM ubuntu:zesty

RUN apt-get update
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get install -y xvfb python3.6 python3.6-venv python3.6-dev firefox build-essential
RUN apt-get update && \
apt-get install -y software-properties-common && \
add-apt-repository ppa:mozillateam/firefox-next && \
apt-get update && \
apt-get install -y \
xvfb \
python3.6 \
python3.6-venv \
python3.6-dev \
build-essential \
wget \
firefox

COPY geckodriver-v0.16.0-linux64.tar.gz geckodriver-v0.16.0-linux64.tar.gz
ARG GECKODRIVER_VERSION=0.16.1

RUN tar xzvf geckodriver-v0.16.0-linux64.tar.gz

RUN cp geckodriver /usr/local/bin/geckodriver

RUN chmod +x /usr/local/bin/geckodriver
RUN wget --no-verbose -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER_VERSION/geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz \
&& rm -rf /opt/geckodriver \
&& tar -C /opt -zxf /tmp/geckodriver.tar.gz \
&& rm /tmp/geckodriver.tar.gz \
&& mv /opt/geckodriver /opt/geckodriver-$GECKODRIVER_VERSION \
&& chmod 755 /opt/geckodriver-$GECKODRIVER_VERSION \
&& ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/local/bin/geckodriver
Binary file not shown.
95 changes: 95 additions & 0 deletions docs/howto/action_chains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from arsenic.actions import Mouse, chain
from arsenic.session import Element, Session


async def drag_and_drop(session: Session, source_element: Element, x_offset: int, y_offset: int):
mouse = Mouse()
actions = chain(
mouse.move_to(source_element),
mouse.down(),
mouse.move_by(x_offset, y_offset),
mouse.up(),
)
await session.perform_actions(actions)


import asyncio
from functools import wraps, partial

from aiohttp.web import Response, Application

from arsenic import get_session, browsers, services


async def handle(request):
return Response(status=200, content_type='text/html', body='''<html>
<body>
<input id="range" type="range" min="1" max="100" value="1" /> <span id="output" />
<script>
const range = document.getElementById('range');
const output = document.getElementById('output');
output.textContent = range.value;
range.addEventListener('change', event => output.textContent = event.target.value);
window.INITIALIZED = true;
</script>
</body>
</html>''')


def build_app():
app = Application()
app.router.add_get('/', handle)
return app


class RunApp:
def __init__(self):
self.app = build_app()
self.server = None

async def __aenter__(self):
self.server = await asyncio.get_event_loop().create_server(
self.app.make_handler(),
'127.0.0.1',
0
)
for socket in self.server.sockets:
host, port = socket.getsockname()
return f'http://{host}:{port}'

async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.server:
self.server.close()


def with_app_and_session(coro):
@wraps(coro)
async def wrapper():
gecko = services.Geckodriver()
ff = browsers.Firefox()
async with RunApp() as base_url, get_session(gecko, ff, base_url) as session:
return await coro(session)
return wrapper


@with_app_and_session
async def run_drag_and_drop(session):
values = []
await session.get('/')
await session.wait(5, partial(session.execute_script, 'return window.INITIALIZED;'))
result = await session.get_element('#output')
values.append(await result.get_text())
source = await session.get_element('#range')
await drag_and_drop(session, source, 200, 0)
values.append(await result.get_text())
return values


def main():
loop = asyncio.get_event_loop()
values = loop.run_until_complete(run_drag_and_drop())
print(f'{values[0]} -> {values[1]}')


if __name__ == '__main__':
main()
24 changes: 24 additions & 0 deletions docs/howto/action_chains.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Action Chains
#############

Arsenic supports action chains that allow you to define a sequence of mouse and/or
keyboard actions to perform in sequence (and in parallel).

.. note::

Action chains are only supported by a few browsers so far. For other browsers,
arsenic will attempt to emulate them using older APIs, however it can only
emulate a single mouse input.

Drag and drop
*************

Drag and drop functionality can be implemented using :py:class:`arsenic.actions.Mouse`
together with :py:func:`arsenic.actions.chain` and :py:meth:`arsenic.session.Session.preform_actions`.

Here is an example helper function which moves the mouse to an element and drags
it by a specified amount of pixels:

.. literalinclude:: action_chains.py
:lines: 1-13

1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Welcome to arsenic's documentation!
:caption: How-to Guides

howto/pytest
howto/action_chains


Indices and tables
Expand Down

0 comments on commit 9df69e2

Please sign in to comment.