Skip to content

Commit

Permalink
Merge pull request #15 from 2gis/functional-tests
Browse files Browse the repository at this point in the history
Add basic functional tests written in python
  • Loading branch information
skyline-gleb committed Mar 12, 2015
2 parents 6adbc87 + 1384984 commit 3227015
Show file tree
Hide file tree
Showing 9 changed files with 343 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

.idea
19 changes: 19 additions & 0 deletions WindowsUniversalAppDriver/TestApp.Test/py-functional/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Functional tests for Windows Universal App Driver
Functional tests for Windows Universal App Driver written in python.

## Usage

1. Build Windows Universal App Driver solution.
2. Make store package from TestApp.
3. Run tests

```cmd
REM Run WindowsUniversalAppDriver.exe
start ..\..\WindowsUniversalAppDriver\bin\Debug\WindowsUniversalAppDriver.exe
REM Run tests
pip install -r requirements.txt
py.test tests --tb=native -s
taskkill /im WindowsUniversalAppDriver.exe /f
```
10 changes: 10 additions & 0 deletions WindowsUniversalAppDriver/TestApp.Test/py-functional/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# coding: utf-8
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
APPX_PATH = '..\\TestApp/TestApp.WindowsPhone\\AppPackages\\TestApp.WindowsPhone_1.0.0.0_AnyCPU_Debug_Test\\' \
'TestApp.WindowsPhone_1.0.0.0_AnyCPU_Debug.appx'

DESIRED_CAPABILITIES = {
"app": os.path.abspath(os.path.join(BASE_DIR, APPX_PATH))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest==2.6.4
selenium==2.45.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
REM Run WindowsUniversalAppDriver.exe
start ..\..\WindowsUniversalAppDriver\bin\Debug\WindowsUniversalAppDriver.exe

REM Run tests
pip install -r requirements.txt
py.test tests --tb=native -s

taskkill /im WindowsUniversalAppDriver.exe /f
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# coding: utf-8
from selenium.webdriver import Remote
import config


class WuaTestCase:
"""
If True, then new session is created when test class is being setup,
i.e. one session is used for all test methods in class.
If False, then new session is created when test method is being setup,
i.e. each test method will run in new session.
"""
__shared_session__ = True

desired_capabilities = config.DESIRED_CAPABILITIES

@staticmethod
def _create_session(o):
o.driver = Remote(command_executor="http://localhost:9999", desired_capabilities=o.desired_capabilities)

@staticmethod
def _destroy_session(o):
o.driver.quit()

def setup_method(self, _):
if not self.__shared_session__:
WuaTestCase._create_session(self)

def teardown_method(self, _):
if not self.__shared_session__:
WuaTestCase._destroy_session(self)

@classmethod
def setup_class(cls):
if cls.__shared_session__:
WuaTestCase._create_session(cls)

@classmethod
def teardown_class(cls):
if cls.__shared_session__:
WuaTestCase._destroy_session(cls)
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
# coding: utf-8
import pytest
from selenium.common.exceptions import NoSuchElementException

from selenium.webdriver.common.by import By
from tests import WuaTestCase


class TestGetCommands(WuaTestCase):
"""
Test GET commands that do not change anything in app, meaning they can all be run in one session.
"""
def test_get_current_window_handle(self):
"""
GET /session/:sessionId/window_handle Retrieve the current window handle.
"""
assert 'current' == self.driver.current_window_handle

def test_screenshot(self):
"""
GET /session/:sessionId/screenshot Take a screenshot of the current page.
"""
assert self.driver.get_screenshot_as_png(), 'Screenshot should not be empty'

def test_get_window_size(self):
"""
GET /session/:sessionId/window/:windowHandle/size Get the size of the specified window.
"""
size = self.driver.get_window_size()
assert {'height': 800, 'width': 480} == size

def test_get_page_source(self):
"""
GET /session/:sessionId/source Get the current page source (as xml).
"""
from xml.etree import ElementTree

source = self.driver.page_source
root = ElementTree.fromstring(source)
assert 'Windows.UI.Xaml.Controls.Frame' == root.tag
assert any(root.iterfind('*')), 'Page source should contain at least one children of root frame'

@pytest.mark.parametrize(("by", "value"), [
(By.ID, 'MyTextBox'),
(By.NAME, 'NonUniqueName'),
(By.CLASS_NAME, 'Windows.UI.Xaml.Controls.TextBox'),
(By.TAG_NAME, 'Windows.UI.Xaml.Controls.TextBox'),
(By.ID, 'FirstAppBarButton')
], ids=['by id', 'by name', 'by class name', 'by tag name', 'in popup'])
def test_find_element(self, by, value):
"""
POST /session/:sessionId/element Search for an element on the page, starting from the document root.
"""
try:
self.driver.find_element(by, value)
except NoSuchElementException as e:
pytest.fail(e)

@pytest.mark.parametrize(("by", "value", "expected_count"), [
(By.NAME, 'NonUniqueName', 2),
(By.CLASS_NAME, 'Windows.UI.Xaml.Controls.AppBarButton', 3),
(By.TAG_NAME, 'Windows.UI.Xaml.Controls.TextBlock', 29),
], ids=['by name', 'by class name', 'by tag name'])
def test_find_elements(self, by, value, expected_count):
"""
POST /session/:sessionId/elements Search for multiple elements on the page, starting from the document root.
"""
assert expected_count == len(self.driver.find_elements(by, value))

def test_find_child_element(self):
"""
POST /session/:sessionId/element/:id/element
Search for an element on the page, starting from the identified element.
"""
parent_element = self.driver.find_element_by_class_name('TestApp.MainPage')
try:
parent_element.find_element_by_id('MyTextBox')
except NoSuchElementException as e:
pytest.fail(e)

def test_find_child_elements(self):
"""
POST /session/:sessionId/element/:id/elements
Search for multiple elements on the page, starting from the identified element.
"""
parent_element = self.driver.find_element_by_id('MyListBox')
elements = parent_element.find_elements_by_class_name('Windows.UI.Xaml.Controls.TextBlock')

assert 21 == len(elements)

def test_get_element_text(self):
"""
GET /session/:sessionId/element/:id/text Returns the visible text for the element.
"""
text = self.driver.find_element_by_id('SetButton').text
assert "Set 'CARAMBA' text to TextBox" == text

@pytest.mark.parametrize(("attr_name", "expected_value"), [
('Width', '300'),
('DesiredSize.Width', '300'),
], ids=['simple property', 'nested property'])
def test_get_element_attribute(self, attr_name, expected_value):
"""
GET /session/:sessionId/element/:id/attribute/:name Get the value of an element's attribute.
"""
element = self.driver.find_element_by_id('MyTextBox')
value = element.get_attribute(attr_name)
assert expected_value == value

def test_get_element_attribute_serialized(self):
"""
GET /session/:sessionId/element/:id/attribute/:name Get the value of an element's attribute.
Complex properties (i.e. non scalar and non string should be serialized to JSON and returned as string)
"""
from json import loads
element = self.driver.find_element_by_id('MyTextBox')
value = loads(element.get_attribute('DesiredSize'))
assert isinstance(value, dict)
assert {'Width', 'Height', 'IsEmpty'} == set(value.keys())

@pytest.mark.parametrize(("automation_id", "expected_value"), [
('MyTextBox', True),
('FirstAppBarButton', False)
])
def test_is_element_displayed(self, automation_id, expected_value):
"""
GET /session/:sessionId/element/:id/displayed Determine if an element is currently displayed.
"""
is_displayed = self.driver.find_element_by_id(automation_id).is_displayed()
assert expected_value == is_displayed

def test_get_element_location(self):
"""
GET /session/:sessionId/element/:id/location Determine an element's location on the page.
"""
location = self.driver.find_element_by_id('MyTextBox').location
assert {'x': 240, 'y': 261} == location

def test_get_orientation(self):
"""
GET /session/:sessionId/orientation Get the current browser orientation.
Note: we lost orientation support in universal driver, atm it always returns portrait
"""
# TODO: rewrite and parametrize test to test different orientations
assert 'PORTRAIT' == self.driver.orientation


class TestAlert(WuaTestCase):
# __shared_session__ = False

def test_get_alert_text(self):
"""GET /session/:sessionId/alert_text Gets the text of the currently displayed alert"""
pytest.skip('TODO')

def test_accept_alert(self):
"""POST /session/:sessionId/accept_alert Accepts the currently displayed alert dialog."""
pytest.skip('TODO')

def test_dismiss_alert(self):
"""POST /session/:sessionId/dismiss_alert Dismisses the currently displayed alert dialog."""
pytest.skip('TODO')


class TestExecuteScript(WuaTestCase):
"""
Tests for Execute Script command. Supported scripts are listed at
https://github.com/2gis/windows-universal-app-driver/wiki/Command-Execute-Script
Tested scripts do affect app interface, but test methods are made in such way that they can be run in one session.
"""
def test_automation_invoke(self):
element = self.driver.find_element_by_id('SetButton')
self.driver.execute_script('automation: invoke', element)
assert 'CARAMBA' == self.driver.find_element_by_id('MyTextBox').text

def test_automation_scroll(self):
list_box = self.driver.find_element_by_id('MyListBox')
list_item = list_box.find_element_by_name('November')
start_location = list_item.location
scroll_info = {"v": "smallIncrement", "count": 10}
self.driver.execute_script("automation: scroll", list_box, scroll_info)
end_location = list_item.location

assert (end_location['y'] - start_location['y']) < 0

@pytest.mark.parametrize(("attribute", "value"), [('Width', 10, ), ('Background.Opacity', 0, )],
ids=["should set basic properties", "should set nested properties"])
def test_attribute_set(self, attribute, value):
element = self.driver.find_element_by_id('SetButton')
self.driver.execute_script("attribute: set", element, attribute, value)
assert str(value) == element.get_attribute(attribute)


class TestBasicInput(WuaTestCase):
__shared_session__ = False

def test_send_keys_to_element(self):
"""
POST /session/:sessionId/element/:id/value Send a sequence of key strokes to an element.
TODO: test magic keys
"""
actual_input = 'Some test string'
element = self.driver.find_element_by_id('MyTextBox')
element.send_keys(actual_input)
assert actual_input == element.text

def test_back(self):
self.driver.find_element_by_id('GoAppBarButton').click()
text_box = self.driver.find_element_by_id('MyTextBox')
self.driver.back()
assert 'AppBar Closed' == text_box.text

def test_click_element(self):
element = self.driver.find_element_by_id('SetButton')
element.click()
assert 'CARAMBA' == self.driver.find_element_by_id('MyTextBox').text


class TestInputChains(WuaTestCase):
def test_action_chain(self):
# TODO MouseMoveTo, MouseClick, MouseDown, MouseUp
pytest.skip('TODO')

def test_touch_actions(self):
# TODO TouchSingleTap, TouchScroll, TouchFlick
pytest.skip('TODO')
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,42 @@
x:Class="TestApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="MainPageOnLoaded">
<Pivot Title="MY APPLICATION">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>

<TextBox Name="TextBox" Grid.Row="0" Width="300" Height="100"
VerticalAlignment="Center"
AutomationProperties.AutomationId="MyTextBox"/>
<Button Content="Set 'CARAMBA' text to TextBox" Grid.Row="1"
HorizontalAlignment="Center"
Click="SetButtonOnClick"
AutomationProperties.AutomationId="SetButton"/>
<ListBox Name="ListBox" Height="200" Grid.Row="2" Width="300"
AutomationProperties.AutomationId="MyListBox"/>
</Grid>
<!--Pivot item one-->
<PivotItem Header="first">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>

<TextBox Name="TextBox" Grid.Row="0" Width="300" Height="100"
VerticalAlignment="Center"
AutomationProperties.AutomationId="MyTextBox" AutomationProperties.Name="NonUniqueName"/>
<Button Content="Set 'CARAMBA' text to TextBox" Grid.Row="1"
HorizontalAlignment="Center" Click="SetButtonOnClick"
AutomationProperties.AutomationId="SetButton" AutomationProperties.Name="NonUniqueName"/>
<ListBox Name="ListBox" Height="200" Grid.Row="2" Width="300"
AutomationProperties.AutomationId="MyListBox"/>
</Grid>
</PivotItem>
<PivotItem Header="second" />
</Pivot>
<Page.BottomAppBar>
<CommandBar AutomationProperties.AutomationId="BottomAppBar">
<CommandBar AutomationProperties.AutomationId="BottomAppBar" Opened="AppBarOnOpened" Closed="AppBarOnClosed">
<CommandBar.PrimaryCommands>
<AppBarButton Icon="Go" IsCompact="True"
Click="GoAppBarButtonOnClick"
AutomationProperties.AutomationId="GoAppBarButton"/>
</CommandBar.PrimaryCommands>

<CommandBar.SecondaryCommands>
<AppBarButton Label="First button"
Click="FirstAppBarButtonOnClick"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,15 @@ private void SecondAppBarButtonOnClick(object sender, RoutedEventArgs e)
{
TextBox.Text = "Clicked SecondAppBarButton";
}

private void AppBarOnOpened(object sender, object e)
{
TextBox.Text = "AppBar Opened";
}

private void AppBarOnClosed(object sender, object e)
{
TextBox.Text = "AppBar Closed";
}
}
}

0 comments on commit 3227015

Please sign in to comment.