Merge lp:~martin-borho/ubuntu-weather-app/autopilot-tests into lp:ubuntu-weather-app/obsolete.trunk

Proposed by Martin Borho
Status: Merged
Approved by: Martin Borho
Approved revision: 53
Merged at revision: 49
Proposed branch: lp:~martin-borho/ubuntu-weather-app/autopilot-tests
Merge into: lp:ubuntu-weather-app/obsolete.trunk
Diff against target: 645 lines (+418/-32)
11 files modified
components/AddLocationPage.qml (+8/-3)
components/LocationManagerPage.qml (+2/-1)
components/LocationTab.qml (+1/-0)
tests/autopilot/ubuntu_weather_app/emulators/__init__.py (+16/-5)
tests/autopilot/ubuntu_weather_app/emulators/main_window.py (+9/-3)
tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py (+154/-0)
tests/autopilot/ubuntu_weather_app/tests/__init__.py (+72/-6)
tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py (+118/-0)
tests/autopilot/ubuntu_weather_app/tests/test_mainview.py (+23/-6)
tests/autopilot/ubuntu_weather_app/tests/weatherdata.py (+10/-0)
ubuntu-weather-app.qml (+5/-8)
To merge this branch: bzr merge lp:~martin-borho/ubuntu-weather-app/autopilot-tests
Reviewer Review Type Date Requested Status
Nicholas Skaggs (community) Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+170137@code.launchpad.net

Commit message

Autopilot tests added for adding, removing and switching of locations..

Description of the change

Autopilot tests added for adding, removing and switching of locations. Bug #1188342, Bug #1188729 and Bug #1188728

$ cd tests/autopilot/
$ autopilot run ubuntu-weather-app

To post a comment you must log in.
47. By Martin Borho

fixed add_location test

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
48. By Martin Borho

merged from trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
49. By Martin Borho

added test for switching locations

50. By Martin Borho

splitted location manager tests into smaller parts

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Martin, thanks for this. I'll try and review it a bit more in a few hours, but at the moment could you make sure you are using the emulator for this?

https://code.launchpad.net/~nskaggs/+junk/ubuntusdk_autopilot_emulator

It's going to keep expanding and has many of the common functions your using. It will be easier to standardize and keep best practices if everyone adopts this. See http://www.theorangenotebook.com/2013/06/writing-emulator-for-autopilot.html for more info if needed :-)

review: Needs Fixing
51. By Martin Borho

autopiloit tests modified to run with ubuntusdk.py

52. By Martin Borho

commit files missed

53. By Martin Borho

ubuntusdk.py added from https://code.launchpad.net/~nskaggs/+junk/ubuntusdk_autopilot_emulator

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Martin Borho (martin-borho) wrote :

Since I've already copied the very helpful methods from ubuntusdk.py to main_window.py, I had only to instanciate the TestCase property "main_window" with a ubuntusdk instance. Works so far (main_window.py now obsolete?)

The database related stuff I've copied over from https://code.launchpad.net/~phablet-team/notes-app/. Would be cool, if the ubuntusdk would give somehow similar access to the storage (LocalStorage and/or u1db later on)

Cheers
Martin

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Looks excellent Martin -- your no stranger to python :-)

So the localstorage stuff is basically your DatabaseMixin class right? I wonder if I could integrate that generically somehow. Worth thinking about. Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'components/AddLocationPage.qml'
--- components/AddLocationPage.qml 2013-06-17 15:23:41 +0000
+++ components/AddLocationPage.qml 2013-06-20 16:20:34 +0000
@@ -4,7 +4,7 @@
44
5Page {5Page {
6 id: addLocationPage6 id: addLocationPage
7 objectName: "AddLocationrPage"7 objectName: "AddLocationPage"
8 title: i18n.tr("Add city")8 title: i18n.tr("Add city")
9 visible: false9 visible: false
10 tools: ToolbarActions{10 tools: ToolbarActions{
@@ -17,6 +17,7 @@
17 source: "./WeatherApi.js"17 source: "./WeatherApi.js"
18 onMessage: {18 onMessage: {
19 if(!messageObject.error) {19 if(!messageObject.error) {
20 listView.visible = true
20 messageObject.result.locations.forEach(function(loc) {21 messageObject.result.locations.forEach(function(loc) {
21 citiesModel.append(loc);22 citiesModel.append(loc);
22 });23 });
@@ -27,8 +28,9 @@
27 }28 }
2829
29 function clear() {30 function clear() {
30 locationString.text = ''31 locationString.text = '';
31 citiesModel.clear()32 citiesModel.clear();
33 listView.visible = true;
32 }34 }
3335
34 Rectangle {36 Rectangle {
@@ -38,6 +40,7 @@
38 color: "transparent"40 color: "transparent"
39 TextField {41 TextField {
40 id: locationString42 id: locationString
43 objectName: "SearchField"
41 width: parent.width-units.gu(2)44 width: parent.width-units.gu(2)
42 height:units.gu(5)45 height:units.gu(5)
43 anchors.centerIn: parent46 anchors.centerIn: parent
@@ -65,6 +68,8 @@
65 color: "transparent"68 color: "transparent"
66 ListView {69 ListView {
67 id: listView;70 id: listView;
71 objectName: "SearchResultList"
72 visible: false
68 clip: true;73 clip: true;
69 anchors.fill: parent;74 anchors.fill: parent;
70 model: citiesModel;75 model: citiesModel;
7176
=== modified file 'components/LocationManagerPage.qml'
--- components/LocationManagerPage.qml 2013-06-17 15:23:41 +0000
+++ components/LocationManagerPage.qml 2013-06-20 16:20:34 +0000
@@ -146,7 +146,7 @@
146 color:"transparent"146 color:"transparent"
147 height: units.gu(40)147 height: units.gu(40)
148 ListView {148 ListView {
149 objectName: "locationList"149 objectName: "LocationList"
150 anchors.fill: parent150 anchors.fill: parent
151 model: locationModel151 model: locationModel
152 delegate: ListItem.Standard {152 delegate: ListItem.Standard {
@@ -164,6 +164,7 @@
164 }164 }
165 }165 }
166 footer: ListItem.Standard {166 footer: ListItem.Standard {
167 objectName: "AddCityListItem"
167 text: i18n.tr("Add city")168 text: i18n.tr("Add city")
168 progression: true169 progression: true
169 onClicked: pageStack.push(addLocationPage)170 onClicked: pageStack.push(addLocationPage)
170171
=== modified file 'components/LocationTab.qml'
--- components/LocationTab.qml 2013-06-17 19:22:19 +0000
+++ components/LocationTab.qml 2013-06-20 16:20:34 +0000
@@ -4,6 +4,7 @@
44
5Tab {5Tab {
6 id: locationTab6 id: locationTab
7 objectName: "LocationTab"
7 property int locationIndex: 08 property int locationIndex: 0
8 property var locationData: {}9 property var locationData: {}
910
1011
=== modified file 'tests/autopilot/ubuntu_weather_app/emulators/__init__.py'
--- tests/autopilot/ubuntu_weather_app/emulators/__init__.py 2013-04-05 08:12:09 +0000
+++ tests/autopilot/ubuntu_weather_app/emulators/__init__.py 2013-06-20 16:20:34 +0000
@@ -1,6 +1,17 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical2#
3#3# Copyright (C) 2013 Canonical Ltd
4# This program is free software: you can redistribute it and/or modify it4#
5# under the terms of the GNU General Public License version 3, as published5# This program is free software: you can redistribute it and/or modify
6# by the Free Software Foundation.6# it under the terms of the GNU General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# Authored by: Nicholas Skaggs <nicholas.skaggs@canonical.com>
718
=== modified file 'tests/autopilot/ubuntu_weather_app/emulators/main_window.py'
--- tests/autopilot/ubuntu_weather_app/emulators/main_window.py 2013-04-08 07:59:44 +0000
+++ tests/autopilot/ubuntu_weather_app/emulators/main_window.py 2013-06-20 16:20:34 +0000
@@ -9,11 +9,17 @@
9class MainWindow(object):9class MainWindow(object):
10 """An emulator class that makes it easy to interact with the weather app"""10 """An emulator class that makes it easy to interact with the weather app"""
1111
12 def __init__(self, app):12 def __init__(self, autopilot, app):
13 self.app = app13 self.app = app
14 self.autopilot= autopilot
1415
15 def get_qml_view(self):16 def get_qml_view(self):
16 return self.app.select_single("QQuickView")17 return self.app.select_single("QQuickView")
1718
18 def get_tabs(self):19 def get_object(self, typeName, name):
19 return self.app.select_single("Tabs", objectName="Tabs")20 """Get a specific object"""
21 return self.app.select_single(typeName, objectName=name)
22
23 def get_objects(self, typeName, name):
24 """Get more than one object"""
25 return self.app.select_many(typeName, objectName=name)
2026
=== added file 'tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py'
--- tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py 1970-01-01 00:00:00 +0000
+++ tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py 2013-06-20 16:20:34 +0000
@@ -0,0 +1,154 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
3# Copyright (C) 2013 Canonical Ltd
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# Authored by: Nicholas Skaggs <nicholas.skaggs@canonical.com>
18
19from testtools.matchers import Equals, NotEquals, Not, Is
20from autopilot.matchers import Eventually
21
22class ubuntusdk(object):
23 """An emulator class that makes it easy to interact with the ubuntu sdk applications."""
24
25 def __init__(self, autopilot, app):
26 self.app = app
27 self.autopilot = autopilot
28
29 def get_qml_view(self):
30 """Get the main QML view"""
31 return self.app.select_single("QQuickView")
32
33 def get_object(self, typeName, name):
34 """Get a specific object"""
35 return self.app.select_single(typeName, objectName=name)
36
37 def get_objects(self, typeName, name):
38 """Get more than one object"""
39 return self.app.select_many(typeName, objectName=name)
40
41 def switch_to_tab(self, tab):
42 """Switch to the specified tab number"""
43 tabs = self.get_tabs()
44 currentTab = tabs.selectedTabIndex
45
46 #perform operations until tab == currentTab
47 while tab != currentTab:
48 if tab > currentTab:
49 self._previous_tab()
50 if tab < currentTab:
51 self._next_tab()
52 currentTab = tabs.selectedTabIndex
53
54 def toggle_toolbar(self):
55 """Toggle the toolbar between revealed and hidden"""
56 #check and see if the toolbar is open or not
57 if self.get_toolbar().opened:
58 self.hide_toolbar()
59 else:
60 self.open_toolbar()
61
62 def get_toolbar(self):
63 """Returns the toolbar in the main events view."""
64 return self.app.select_single("Toolbar")
65
66 def get_toolbar_button(self, buttonLabel):
67 """Returns the toolbar button at position index"""
68 toolbar = self.get_toolbar()
69 if not toolbar.opened:
70 self.open_toolbar()
71 row = toolbar.select_single("QQuickRow")
72 loaderList = row.select_many("QQuickLoader")
73 for loader in loaderList:
74 buttonList = loader.select_many("Button")
75 for button in buttonList:
76 if button.text == buttonLabel:
77 return button
78
79 def click_toolbar_button(self, buttonLabel):
80 """Clicks the toolbar button with buttonLabel"""
81 #The toolbar button is assumed to be the following format
82 #ToolbarActions {
83 # Action {
84 # objectName: "name"
85 # text: value
86 button = self.get_toolbar_button(buttonLabel)
87 self.autopilot.pointing_device.click_object(button)
88
89 def open_toolbar(self):
90 """Open the toolbar"""
91 qmlView = self.get_qml_view()
92
93 lineX = qmlView.x + qmlView.width * 0.50
94 startY = qmlView.y + qmlView.height - 1
95 stopY = qmlView.y + qmlView.height * 0.95
96
97 self.autopilot.pointing_device.drag(lineX, startY, lineX, stopY)
98
99 def hide_toolbar(self):
100 """Hide the toolbar"""
101 qmlView = self.get_qml_view()
102
103 lineX = qmlView.x + qmlView.width * 0.50
104 startY = qmlView.y + qmlView.height * 0.95
105 stopY = qmlView.y + qmlView.height - 1
106
107 self.autopilot.pointing_device.drag(lineX, startY, lineX, stopY)
108
109 def set_popup_value(self, popover, button, value):
110 """Changes the given popover selector to the request value
111 At the moment this only works for values that are currently visible. To
112 access the remaining items, a help method to drag and recheck is needed."""
113 #The popover is assumed to be the following format
114 # Popover {
115 # Column {
116 # ListView {
117 # delegate: Standard {
118 # objectName: "name"
119 # text: value
120
121 self.autopilot.pointing_device.click_object(button)
122 #we'll get all matching objects, incase the popover is reused between buttons
123 itemList = lambda: self.get_objects("Standard", popover)
124
125 for item in itemList():
126 if item.get_properties()['text'] == value:
127 self.autopilot.pointing_device.click_object(item)
128
129 def get_tabs(self):
130 """Return all tabs"""
131 return self.get_object("Tabs", "rootTabs")
132
133 def _previous_tab(self):
134 """Switch to the previous tab"""
135 qmlView = self.get_qml_view()
136
137 startX = qmlView.x + qmlView.width * 0.35
138 stopX = qmlView.x + qmlView.width * 0.50
139 lineY = qmlView.y + qmlView.height * 0.05
140
141 self.autopilot.pointing_device.drag(startX, lineY, stopX, lineY)
142 self.autopilot.pointing_device.click()
143 self.autopilot.pointing_device.click()
144
145 def _next_tab(self):
146 """Switch to the next tab"""
147 qmlView = self.get_qml_view()
148
149 startX = qmlView.x + qmlView.width * 0.50
150 stopX = qmlView.x + qmlView.width * 0.35
151 lineY = qmlView.y + qmlView.height * 0.05
152
153 self.autopilot.pointing_device.drag(startX, lineY, stopX, lineY)
154 self.autopilot.pointing_device.click()
0155
=== modified file 'tests/autopilot/ubuntu_weather_app/tests/__init__.py'
--- tests/autopilot/ubuntu_weather_app/tests/__init__.py 2013-05-31 13:21:10 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/__init__.py 2013-06-20 16:20:34 +0000
@@ -9,13 +9,15 @@
99
10import os.path10import os.path
11import glob11import glob
12import sqlite3
13import time
1214
13from autopilot.input import Mouse, Touch, Pointer15from autopilot.input import Mouse, Touch, Pointer
14from autopilot.platform import model16from autopilot.platform import model
15from autopilot.testcase import AutopilotTestCase17from autopilot.testcase import AutopilotTestCase
1618
17from ubuntu_weather_app.emulators.main_window import MainWindow19from ubuntu_weather_app.emulators.main_window import MainWindow
1820from ubuntu_weather_app.emulators.ubuntusdk import ubuntusdk
1921
20class WeatherTestCase(AutopilotTestCase):22class WeatherTestCase(AutopilotTestCase):
2123
@@ -32,10 +34,7 @@
32 def setUp(self):34 def setUp(self):
33 self.pointing_device = Pointer(self.input_device_class.create())35 self.pointing_device = Pointer(self.input_device_class.create())
34 super(WeatherTestCase, self).setUp()36 super(WeatherTestCase, self).setUp()
35 if os.path.exists(self.local_location):37 self.launch_app()
36 self.launch_test_local()
37 else:
38 self.launch_test_installed()
3938
40 """Workaround to find the qmlscene binary via shell globbing.39 """Workaround to find the qmlscene binary via shell globbing.
41 This is needed since we can't rely on qt5-default being installed on40 This is needed since we can't rely on qt5-default being installed on
@@ -43,6 +42,12 @@
43 def qmlscene(self):42 def qmlscene(self):
44 return glob.glob("/usr/lib/*/qt5/bin/qmlscene")[0]43 return glob.glob("/usr/lib/*/qt5/bin/qmlscene")[0]
4544
45 def launch_app(self):
46 if os.path.exists(self.local_location):
47 self.launch_test_local()
48 else:
49 self.launch_test_installed()
50
46 def launch_test_local(self):51 def launch_test_local(self):
47 self.app = self.launch_test_application(52 self.app = self.launch_test_application(
48 self.qmlscene(),53 self.qmlscene(),
@@ -56,6 +61,67 @@
56 "--desktop_file_hint=/usr/share/applications/ubuntu-weather-app.desktop",61 "--desktop_file_hint=/usr/share/applications/ubuntu-weather-app.desktop",
57 app_type='qt')62 app_type='qt')
5863
64 def launch_and_quit_app(self):
65 self.launch_app()
66 self.main_window.get_qml_view().visible.wait_for(True)
67
68 # When calling launch_app an instance of the spawned process
69 # control object will be stored in self.app.process, and a cleanup
70 # handler will be registered that essentially kills the process.
71 # Therefore, by triggering the cleanup handler here we're killing the
72 # process and removing the handler, which allows a clean launch of
73 # the process during regular test setup.
74 self.doCleanups()
75
59 @property76 @property
60 def main_window(self):77 def main_window(self):
61 return MainWindow(self.app)78 return ubuntusdk(self, self.app)#MainWindow(self, self.app)
79
80
81class DatabaseMixin(object):
82
83 """
84 Helper functions for dealing with sqlite databases
85 """
86
87 def find_db(self):
88 dbs_path = os.path.expanduser("~/.local/share/Qt Project/QtQmlViewer/QML/OfflineStorage/Databases/")
89 if not os.path.exists(dbs_path):
90 return None
91
92 files = [ f for f in os.listdir(dbs_path) if os.path.splitext(f)[1] == ".ini" ]
93 for f in files:
94 ini_path = os.path.join(dbs_path, f)
95 with open(ini_path) as ini:
96 for line in ini:
97 if "=" in line:
98 key, val = line.strip().split("=")
99 if key == "Name" and val == "ubuntu-weather-app":
100 try:
101 return ini_path.replace(".ini", ".sqlite")
102 except OSError:
103 pass
104 return None
105
106 def clean_db(self):
107 path = self.find_db()
108 if path is None:
109 self.launch_and_quit_app()
110 path = self.find_db()
111 if path is None:
112 self.assertNotEquals(path, None)
113
114 try:
115 os.remove(path)
116 except OSError:
117 pass
118
119 def save_locations_to_storage(self, locations):
120 path = self.find_db()
121 conn = sqlite3.connect(path)
122 cursor = conn.cursor()
123 for loc_data in locations:
124 cursor.execute("INSERT INTO Locations(date, data) VALUES('{}', '{}')".format(int(time.time()*1000), loc_data))
125 conn.commit()
126 conn.close()
127
62128
=== added file 'tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py'
--- tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py 1970-01-01 00:00:00 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py 2013-06-20 16:20:34 +0000
@@ -0,0 +1,118 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7
8"""Tests for managing locations in Weather app"""
9
10from __future__ import absolute_import
11
12from testtools.matchers import Equals
13from autopilot.matchers import Eventually
14
15from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin
16from ubuntu_weather_app.tests.weatherdata import locations_data
17
18class TestLocationManager(WeatherTestCase, DatabaseMixin):
19 def setUp(self):
20 self.clean_db()
21 super(TestLocationManager, self).setUp()
22 self.assertThat(
23 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
24
25 def _open_add_location_page(self):
26 """Opens the AddLocationPage"""
27 # click on listitem to open AddLocationPage
28 locationList = self.main_window.get_object('QQuickListView', 'LocationList')
29 addCityItem = locationList.get_children()[0]
30 self.pointing_device.move_to_object(addCityItem)
31 self.pointing_device.click()
32
33 addLocPage = self.main_window.get_object("AddLocationPage", "AddLocationPage")
34 self.assertThat(addLocPage.visible, Eventually(Equals(True)))
35
36 def test_add_location(self):
37 """Adds a location"""
38 self._open_add_location_page()
39
40 # insert city name to search for
41 searchField = self.main_window.get_object("TextField", "SearchField")
42 self.pointing_device.move_to_object(searchField)
43 self.pointing_device.click()
44 self.keyboard.type("London")
45 self.assertThat(searchField.text, Eventually(Equals("London")))
46 self.keyboard.press_and_release('Enter')
47
48 # wait for results and click on the first item
49 resultsList = self.main_window.get_object('QQuickListView', 'SearchResultList')
50 resultsModel = self.main_window.get_object('QQuickListModel', 'CitiesModel')
51 self.assertThat(resultsList.visible, Eventually(Equals(True), timeout=30))
52 firstResult = resultsList.get_children()[0].get_children()[0]
53 self.pointing_device.move_to_object(firstResult)
54 self.pointing_device.click()
55
56 # LocationManagerPage should be visible and "London" added
57 locationList = self.main_window.get_object('QQuickListView', 'LocationList')
58 addedItem = locationList.get_children()[0]
59 self.assertThat(addedItem.get_children()[1].text, Eventually(Equals("London")))
60
61 # a location is defined, so toolbar should be visible
62 toolbar = self.main_window.get_toolbar()
63 self.assertThat(toolbar.opened, Eventually(Equals(True)))
64
65 def test_toolbar_hidden_when_no_location_is_defined(self):
66 """Toolbar is not visible when there's no location is defined"""
67 toolbar = self.main_window.get_toolbar()
68 self.assertThat(toolbar.opened, Eventually(Equals(False)))
69
70 """no way found to click the back button"""
71 #def test_cancel_adding_location(self):
72 # self.open_add_location_page()
73 # self.main_window.click_toolbar_button("Back")
74 # locationList = self.main_window.get_object('QQuickListView', 'LocationList')
75 # self.assertThat(locationList, Eventually(Equals(True)))
76
77class TestLocationManagerWithLocation(WeatherTestCase, DatabaseMixin):
78 def setUp(self):
79 self.clean_db()
80 self.launch_and_quit_app()
81 # add one location to storage
82 self.save_locations_to_storage([locations_data[0]])
83 super(TestLocationManagerWithLocation, self).setUp()
84 self.assertThat(
85 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
86
87 def _open_location_manager(self):
88 """Opens the location manager"""
89 self.main_window.open_toolbar()
90 self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(True)))
91 self.main_window.click_toolbar_button("Edit")
92
93 def test_remove_location(self):
94 """Removes location"""
95 # check loading screen diappears
96 loadingPage = self.main_window.get_object("Tabs", "LoadingTabs")
97 self.assertThat(loadingPage.visible, Eventually(Equals(False)))
98
99 # go to the location manegr
100 self._open_location_manager()
101
102 # swipe right to delete the first location
103 locationList = self.main_window.get_object('QQuickListView', 'LocationList')
104 locItem = locationList.get_children()[0]
105 loc_x, loc_y, loc_w, loc_h = locItem.globalRect
106 self.pointing_device.move(loc_x + 10, loc_y + loc_h / 2)
107 self.pointing_device.press()
108 self.pointing_device.move(loc_x + loc_w - 10, loc_y + loc_h / 2)
109 self.pointing_device.release()
110
111 # toolbar disappears since no locations defined
112 self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(False)))
113
114 def test_toolbar_opened_when_location_is_defined(self):
115 """Toolbar is visible since there's a location defined"""
116 self._open_location_manager()
117 toolbar = self.main_window.get_toolbar()
118 self.assertThat(toolbar.opened, Eventually(Equals(True)))
0119
=== modified file 'tests/autopilot/ubuntu_weather_app/tests/test_mainview.py'
--- tests/autopilot/ubuntu_weather_app/tests/test_mainview.py 2013-05-31 13:21:10 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/test_mainview.py 2013-06-20 16:20:34 +0000
@@ -12,15 +12,18 @@
12from testtools.matchers import Equals12from testtools.matchers import Equals
13from autopilot.matchers import Eventually13from autopilot.matchers import Eventually
1414
15from ubuntu_weather_app.tests import WeatherTestCase15from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin
1616from ubuntu_weather_app.tests.weatherdata import locations_data
1717
18class TestMainView(WeatherTestCase):18class TestMainView(WeatherTestCase, DatabaseMixin):
19 """Tests creating weather"""19 """Tests creating weather"""
2020
21 """ This is needed to wait for the application to start.21 """ This is needed to wait for the application to start.
22 In the testfarm, the application may take some time to show up."""22 In the testfarm, the application may take some time to show up."""
23 def setUp(self):23 def setUp(self):
24 self.clean_db()
25 self.launch_and_quit_app()
26 self.save_locations_to_storage(locations_data)
24 super(TestMainView, self).setUp()27 super(TestMainView, self).setUp()
25 self.assertThat(28 self.assertThat(
26 self.main_window.get_qml_view().visible, Eventually(Equals(True)))29 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
@@ -28,6 +31,20 @@
28 def tearDown(self):31 def tearDown(self):
29 super(TestMainView, self).tearDown()32 super(TestMainView, self).tearDown()
3033
31 def test_tabs(self):34 def test_switch_tabs(self):
32 tabs = self.main_window.get_tabs()35 """Test start of app with two locations and switch from first to
36 second tab"""
37 # first tab should be visible after start
38 tabs = self.main_window.get_tabs()
39 tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
33 self.assertThat(tabs.selectedTabIndex, Eventually(Equals(0)))40 self.assertThat(tabs.selectedTabIndex, Eventually(Equals(0)))
41 self.assertThat(tabObjects[0].visible, Eventually(Equals(True)))
42 self.assertThat(tabObjects[1].visible, Eventually(Equals(False)))
43 self.assertThat(tabObjects[0].title, Eventually(Equals("London")))
44 # switch to next tab
45 self.main_window.switch_to_tab(1)
46 self.assertThat(tabs.selectedTabIndex, Eventually(Equals(1)))
47 self.assertThat(tabObjects[0].visible, Eventually(Equals(False)))
48 self.assertThat(tabObjects[1].visible, Eventually(Equals(True)))
49 self.assertThat(tabObjects[1].title, Eventually(Equals("Hamburg")))
50
3451
=== added file 'tests/autopilot/ubuntu_weather_app/tests/weatherdata.py'
--- tests/autopilot/ubuntu_weather_app/tests/weatherdata.py 1970-01-01 00:00:00 +0000
+++ tests/autopilot/ubuntu_weather_app/tests/weatherdata.py 2013-06-20 16:20:34 +0000
@@ -0,0 +1,10 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7locations_data = []
8locations_data.append( """{"daily":{"request":{"type":"daily","url":"http://api.openweathermap.org/data/2.5/forecast/daily?id=2643743&cnt=10&units=metric"},"results":[{"timestamp":1371556800,"pressure":1018.07,"metric":{"eve":18.45,"morn":20.61,"night":12.92,"max":20.61,"day":20.61,"min":12.92},"wind_speed":1.77,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":99,"humidity":77,"imperial":{"eve":65.21000000000001,"morn":69.098,"night":55.256,"max":69.098,"day":69.098,"min":55.256}},{"timestamp":1371643200,"pressure":1018.59,"metric":{"eve":19.63,"morn":14.05,"night":15.99,"max":20.92,"day":18.54,"min":14.05},"wind_speed":1.63,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":31,"humidity":97,"imperial":{"eve":67.334,"morn":57.290000000000006,"night":60.782,"max":69.656,"day":65.372,"min":57.290000000000006}},{"timestamp":1371729600,"pressure":1016.49,"metric":{"eve":18.31,"morn":16.07,"night":14.48,"max":19.64,"day":16.82,"min":14.48},"wind_speed":1.76,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":211,"humidity":92,"imperial":{"eve":64.958,"morn":60.926,"night":58.064,"max":67.352,"day":62.275999999999996,"min":58.064}},{"timestamp":1371816000,"pressure":1015.82,"metric":{"eve":18.09,"morn":13.73,"night":14.89,"max":18.65,"day":17.55,"min":13.73},"wind_speed":3.27,"condition":{"icon":"03d","description":"scattered clouds","id":802,"main":"Clouds"},"wind_deg":257,"humidity":78,"imperial":{"eve":64.562,"morn":56.714,"night":58.80200000000001,"max":65.57,"day":63.59,"min":56.714}},{"timestamp":1371902400,"pressure":1003.45,"metric":{"eve":13.5,"morn":12.02,"night":13.04,"max":13.98,"day":13.98,"min":12.02},"wind_speed":6.11,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":215,"humidity":84,"imperial":{"eve":56.3,"morn":53.635999999999996,"night":55.471999999999994,"max":57.164,"day":57.164,"min":53.635999999999996}},{"timestamp":1371988800,"pressure":1011.26,"metric":{"eve":13.66,"morn":12.54,"night":12.4,"max":15.18,"day":15.18,"min":12.4},"wind_speed":4.46,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":290,"humidity":82,"imperial":{"eve":56.588,"morn":54.572,"night":54.32,"max":59.324,"day":59.324,"min":54.32}},{"timestamp":1372075200,"pressure":1018.06,"metric":{"eve":14.96,"morn":11.73,"night":12.42,"max":14.96,"day":14.62,"min":11.73},"wind_speed":6.44,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":292,"humidity":0,"imperial":{"eve":58.928,"morn":53.114000000000004,"night":54.356,"max":58.928,"day":58.316,"min":53.114000000000004}},{"timestamp":1372161600,"pressure":1023.37,"metric":{"eve":15.08,"morn":12.04,"night":11.13,"max":15.8,"day":15.8,"min":11.13},"wind_speed":5.25,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":294,"humidity":0,"imperial":{"eve":59.144000000000005,"morn":53.672,"night":52.034000000000006,"max":60.44,"day":60.44,"min":52.034000000000006}},{"timestamp":1372248000,"pressure":1023.86,"metric":{"eve":14.99,"morn":11.59,"night":11.29,"max":15.91,"day":15.91,"min":11.29},"wind_speed":4.64,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":247,"humidity":0,"imperial":{"eve":58.982,"morn":52.862,"night":52.322,"max":60.638000000000005,"day":60.638000000000005,"min":52.322}},{"timestamp":1372334400,"pressure":1022.68,"metric":{"eve":14.6,"morn":11.33,"night":10.22,"max":15.73,"day":15.73,"min":10.22},"wind_speed":4.34,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":294,"humidity":0,"imperial":{"eve":58.28,"morn":52.394000000000005,"night":50.396,"max":60.314,"day":60.314,"min":50.396}}]},"format":1,"current":{"request":{"type":"current","url":"http://api.openweathermap.org/data/2.5/weather?id=2643743&units=metric"},"results":{"timestamp":1371568737,"pressure":1013,"service_id":2643743,"metric":{"temp_min":19,"temp":20.61,"temp_max":22.22},"date":null,"condition":{"icon":"03d","description":"scattered clouds","id":802,"main":"Clouds"},"wind_deg":98.5007,"humidity":80,"wind_speed_mph":1.0998249,"imperial":{"temp_min":66.2,"temp":69.098,"temp_max":71.99600000000001},"service":"openweathermap","wind_speed_kmh":1.77}},"location":{"service_id":2643743,"name":"London","country":"GB","service":"openweathermap"}}""")
9
10locations_data.append("""{"daily":{"request":{"type":"daily","url":"http://api.openweathermap.org/data/2.5/forecast/daily?id=2911298&cnt=10&units=metric"},"results":[{"timestamp":1371553200,"pressure":1026.02,"metric":{"eve":24.71,"morn":25.5,"night":18.12,"max":25.5,"day":25.5,"min":18.12},"wind_speed":2.52,"condition":{"icon":"01d","description":"sky is clear","id":800,"main":"Clear"},"wind_deg":89,"humidity":70,"imperial":{"eve":76.47800000000001,"morn":77.9,"night":64.616,"max":77.9,"day":77.9,"min":64.616}},{"timestamp":1371639600,"pressure":1023.37,"metric":{"eve":29.17,"morn":20.41,"night":22.73,"max":30.39,"day":29.24,"min":20.41},"wind_speed":5.28,"condition":{"icon":"02d","description":"few clouds","id":801,"main":"Clouds"},"wind_deg":147,"humidity":69,"imperial":{"eve":84.506,"morn":68.738,"night":72.914,"max":86.702,"day":84.632,"min":68.738}},{"timestamp":1371726000,"pressure":1015.84,"metric":{"eve":25.49,"morn":24.61,"night":19.26,"max":29.68,"day":26.55,"min":19.26},"wind_speed":4.91,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":197,"humidity":84,"imperial":{"eve":77.882,"morn":76.298,"night":66.668,"max":85.424,"day":79.78999999999999,"min":66.668}},{"timestamp":1371812400,"pressure":1016.63,"metric":{"eve":17.09,"morn":16.63,"night":14.89,"max":19.75,"day":19.75,"min":14.89},"wind_speed":6.37,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":217,"humidity":91,"imperial":{"eve":62.762,"morn":61.934,"night":58.80200000000001,"max":67.55000000000001,"day":67.55000000000001,"min":58.80200000000001}},{"timestamp":1371898800,"pressure":1017.64,"metric":{"eve":18.15,"morn":16.27,"night":14.89,"max":19.34,"day":18.23,"min":14.89},"wind_speed":5.96,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":197,"humidity":83,"imperial":{"eve":64.67,"morn":61.286,"night":58.80200000000001,"max":66.812,"day":64.814,"min":58.80200000000001}},{"timestamp":1371985200,"pressure":1011.58,"metric":{"eve":15.16,"morn":14.29,"night":14.3,"max":15.93,"day":15.43,"min":14.29},"wind_speed":7.46,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":211,"humidity":86,"imperial":{"eve":59.288,"morn":57.721999999999994,"night":57.74,"max":60.674,"day":59.774,"min":57.721999999999994}},{"timestamp":1372071600,"pressure":1021.31,"metric":{"eve":17.86,"morn":14.88,"night":13.72,"max":17.97,"day":17.97,"min":13.72},"wind_speed":4.75,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":205,"humidity":0,"imperial":{"eve":64.148,"morn":58.784000000000006,"night":56.696,"max":64.346,"day":64.346,"min":56.696}},{"timestamp":1372158000,"pressure":1026.21,"metric":{"eve":16.23,"morn":13.89,"night":12.53,"max":16.75,"day":16.75,"min":12.53},"wind_speed":3.7,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":277,"humidity":0,"imperial":{"eve":61.214,"morn":57.002,"night":54.554,"max":62.150000000000006,"day":62.150000000000006,"min":54.554}},{"timestamp":1372244400,"pressure":1028.72,"metric":{"eve":16.24,"morn":13.84,"night":12.09,"max":16.71,"day":16.71,"min":12.09},"wind_speed":5.86,"condition":{"icon":"10d","description":"moderate rain","id":501,"main":"Rain"},"wind_deg":249,"humidity":0,"imperial":{"eve":61.232,"morn":56.912,"night":53.762,"max":62.078,"day":62.078,"min":53.762}},{"timestamp":1372330800,"pressure":1024.91,"metric":{"eve":16.15,"morn":12.93,"night":10.44,"max":16.33,"day":16.33,"min":10.44},"wind_speed":3.21,"condition":{"icon":"10d","description":"light rain","id":500,"main":"Rain"},"wind_deg":271,"humidity":0,"imperial":{"eve":61.06999999999999,"morn":55.274,"night":50.792,"max":61.394,"day":61.394,"min":50.792}}]},"db":{"updated":"2013-06-18T16:03:34.203Z","id":2},"format":1,"current":{"request":{"type":"current","url":"http://api.openweathermap.org/data/2.5/weather?id=2911298&units=metric"},"results":{"timestamp":1371568800,"pressure":1016,"service_id":2911298,"metric":{"temp_min":25,"temp":25.5,"temp_max":26},"date":null,"condition":{"icon":"01d","description":"Sky is Clear","id":800,"main":"Clear"},"wind_deg":100,"humidity":53,"wind_speed_mph":2.8583019999999997,"imperial":{"temp_min":77,"temp":77.9,"temp_max":78.80000000000001},"service":"openweathermap","wind_speed_kmh":4.6}},"location":{"service_id":2911298,"name":"Hamburg","country":"DE","service":"openweathermap"}}""")
011
=== modified file 'ubuntu-weather-app.qml'
--- ubuntu-weather-app.qml 2013-06-17 19:22:19 +0000
+++ ubuntu-weather-app.qml 2013-06-20 16:20:34 +0000
@@ -54,15 +54,17 @@
54 tabsObject.destroy()54 tabsObject.destroy()
55 }55 }
56 var tabsString = "import QtQuick 2.0; import Ubuntu.Components 0.1; import Ubuntu.Components.Popups 0.1; import \"components\" as Components; "56 var tabsString = "import QtQuick 2.0; import Ubuntu.Components 0.1; import Ubuntu.Components.Popups 0.1; import \"components\" as Components; "
57 + "Tabs {id: tabs; anchors.fill: parent;"57 + "Tabs {id: tabs; objectName: 'rootTabs'; anchors.fill: parent;"
58 if(locLength > 0) {58 if(locLength > 0) {
59 for(var x=0;x<locLength;x++) {59 for(var x=0;x<locLength;x++) {
60 tabsString += "Components.LocationTab {title: '"+locations[x].location.name+"'; locationIndex: "+x+"} "60 tabsString += "Components.LocationTab {title: '"+locations[x].location.name+"'; locationIndex: "+x+"} "
61 };61 };
62 }62 }
63 tabsString += "}"; // END Tabs componen63 tabsString += "}"; // END Tabs componen
64 if(bigLoading !== null)64 if(bigLoading !== null) {
65 bigLoading.destroy();65 bigLoading.destroy();
66 tabs.visible = false;
67 }
66 loading.running = false;68 loading.running = false;
67 tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")69 tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")
68 if(focusToLast)70 if(focusToLast)
@@ -107,10 +109,6 @@
107 })109 })
108 }110 }
109111
110 Components.AddLocationPage {
111 id: addLocationDialog
112 }
113
114 Components.SettingsPage {112 Components.SettingsPage {
115 id: settingsPage113 id: settingsPage
116 }114 }
@@ -133,12 +131,11 @@
133131
134 Page {132 Page {
135 id: tabPage133 id: tabPage
136 title: "Simple page"
137 visible: false134 visible: false
138 ActivityIndicator{id:loading; running: false; z: 1; anchors{top: parent.top; topMargin: units.gu(0.5); right: parent.right; rightMargin: units.gu(1)}}135 ActivityIndicator{id:loading; running: false; z: 1; anchors{top: parent.top; topMargin: units.gu(0.5); right: parent.right; rightMargin: units.gu(1)}}
139 Tabs {136 Tabs {
140 id: tabs137 id: tabs
141 objectName: "Tabs"138 objectName: "LoadingTabs"
142 anchors.fill: parent139 anchors.fill: parent
143 Tab {140 Tab {
144 title: "Loading..."141 title: "Loading..."

Subscribers

People subscribed via source and target branches