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
1=== modified file 'components/AddLocationPage.qml'
2--- components/AddLocationPage.qml 2013-06-17 15:23:41 +0000
3+++ components/AddLocationPage.qml 2013-06-20 16:20:34 +0000
4@@ -4,7 +4,7 @@
5
6 Page {
7 id: addLocationPage
8- objectName: "AddLocationrPage"
9+ objectName: "AddLocationPage"
10 title: i18n.tr("Add city")
11 visible: false
12 tools: ToolbarActions{
13@@ -17,6 +17,7 @@
14 source: "./WeatherApi.js"
15 onMessage: {
16 if(!messageObject.error) {
17+ listView.visible = true
18 messageObject.result.locations.forEach(function(loc) {
19 citiesModel.append(loc);
20 });
21@@ -27,8 +28,9 @@
22 }
23
24 function clear() {
25- locationString.text = ''
26- citiesModel.clear()
27+ locationString.text = '';
28+ citiesModel.clear();
29+ listView.visible = true;
30 }
31
32 Rectangle {
33@@ -38,6 +40,7 @@
34 color: "transparent"
35 TextField {
36 id: locationString
37+ objectName: "SearchField"
38 width: parent.width-units.gu(2)
39 height:units.gu(5)
40 anchors.centerIn: parent
41@@ -65,6 +68,8 @@
42 color: "transparent"
43 ListView {
44 id: listView;
45+ objectName: "SearchResultList"
46+ visible: false
47 clip: true;
48 anchors.fill: parent;
49 model: citiesModel;
50
51=== modified file 'components/LocationManagerPage.qml'
52--- components/LocationManagerPage.qml 2013-06-17 15:23:41 +0000
53+++ components/LocationManagerPage.qml 2013-06-20 16:20:34 +0000
54@@ -146,7 +146,7 @@
55 color:"transparent"
56 height: units.gu(40)
57 ListView {
58- objectName: "locationList"
59+ objectName: "LocationList"
60 anchors.fill: parent
61 model: locationModel
62 delegate: ListItem.Standard {
63@@ -164,6 +164,7 @@
64 }
65 }
66 footer: ListItem.Standard {
67+ objectName: "AddCityListItem"
68 text: i18n.tr("Add city")
69 progression: true
70 onClicked: pageStack.push(addLocationPage)
71
72=== modified file 'components/LocationTab.qml'
73--- components/LocationTab.qml 2013-06-17 19:22:19 +0000
74+++ components/LocationTab.qml 2013-06-20 16:20:34 +0000
75@@ -4,6 +4,7 @@
76
77 Tab {
78 id: locationTab
79+ objectName: "LocationTab"
80 property int locationIndex: 0
81 property var locationData: {}
82
83
84=== modified file 'tests/autopilot/ubuntu_weather_app/emulators/__init__.py'
85--- tests/autopilot/ubuntu_weather_app/emulators/__init__.py 2013-04-05 08:12:09 +0000
86+++ tests/autopilot/ubuntu_weather_app/emulators/__init__.py 2013-06-20 16:20:34 +0000
87@@ -1,6 +1,17 @@
88 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
89-# Copyright 2013 Canonical
90-#
91-# This program is free software: you can redistribute it and/or modify it
92-# under the terms of the GNU General Public License version 3, as published
93-# by the Free Software Foundation.
94+#
95+# Copyright (C) 2013 Canonical Ltd
96+#
97+# This program is free software: you can redistribute it and/or modify
98+# it under the terms of the GNU General Public License version 3 as
99+# published by the Free Software Foundation.
100+#
101+# This program is distributed in the hope that it will be useful,
102+# but WITHOUT ANY WARRANTY; without even the implied warranty of
103+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
104+# GNU General Public License for more details.
105+#
106+# You should have received a copy of the GNU General Public License
107+# along with this program. If not, see <http://www.gnu.org/licenses/>.
108+#
109+# Authored by: Nicholas Skaggs <nicholas.skaggs@canonical.com>
110
111=== modified file 'tests/autopilot/ubuntu_weather_app/emulators/main_window.py'
112--- tests/autopilot/ubuntu_weather_app/emulators/main_window.py 2013-04-08 07:59:44 +0000
113+++ tests/autopilot/ubuntu_weather_app/emulators/main_window.py 2013-06-20 16:20:34 +0000
114@@ -9,11 +9,17 @@
115 class MainWindow(object):
116 """An emulator class that makes it easy to interact with the weather app"""
117
118- def __init__(self, app):
119+ def __init__(self, autopilot, app):
120 self.app = app
121+ self.autopilot= autopilot
122
123 def get_qml_view(self):
124 return self.app.select_single("QQuickView")
125
126- def get_tabs(self):
127- return self.app.select_single("Tabs", objectName="Tabs")
128+ def get_object(self, typeName, name):
129+ """Get a specific object"""
130+ return self.app.select_single(typeName, objectName=name)
131+
132+ def get_objects(self, typeName, name):
133+ """Get more than one object"""
134+ return self.app.select_many(typeName, objectName=name)
135
136=== added file 'tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py'
137--- tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py 1970-01-01 00:00:00 +0000
138+++ tests/autopilot/ubuntu_weather_app/emulators/ubuntusdk.py 2013-06-20 16:20:34 +0000
139@@ -0,0 +1,154 @@
140+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
141+#
142+# Copyright (C) 2013 Canonical Ltd
143+#
144+# This program is free software: you can redistribute it and/or modify
145+# it under the terms of the GNU General Public License version 3 as
146+# published by the Free Software Foundation.
147+#
148+# This program is distributed in the hope that it will be useful,
149+# but WITHOUT ANY WARRANTY; without even the implied warranty of
150+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
151+# GNU General Public License for more details.
152+#
153+# You should have received a copy of the GNU General Public License
154+# along with this program. If not, see <http://www.gnu.org/licenses/>.
155+#
156+# Authored by: Nicholas Skaggs <nicholas.skaggs@canonical.com>
157+
158+from testtools.matchers import Equals, NotEquals, Not, Is
159+from autopilot.matchers import Eventually
160+
161+class ubuntusdk(object):
162+ """An emulator class that makes it easy to interact with the ubuntu sdk applications."""
163+
164+ def __init__(self, autopilot, app):
165+ self.app = app
166+ self.autopilot = autopilot
167+
168+ def get_qml_view(self):
169+ """Get the main QML view"""
170+ return self.app.select_single("QQuickView")
171+
172+ def get_object(self, typeName, name):
173+ """Get a specific object"""
174+ return self.app.select_single(typeName, objectName=name)
175+
176+ def get_objects(self, typeName, name):
177+ """Get more than one object"""
178+ return self.app.select_many(typeName, objectName=name)
179+
180+ def switch_to_tab(self, tab):
181+ """Switch to the specified tab number"""
182+ tabs = self.get_tabs()
183+ currentTab = tabs.selectedTabIndex
184+
185+ #perform operations until tab == currentTab
186+ while tab != currentTab:
187+ if tab > currentTab:
188+ self._previous_tab()
189+ if tab < currentTab:
190+ self._next_tab()
191+ currentTab = tabs.selectedTabIndex
192+
193+ def toggle_toolbar(self):
194+ """Toggle the toolbar between revealed and hidden"""
195+ #check and see if the toolbar is open or not
196+ if self.get_toolbar().opened:
197+ self.hide_toolbar()
198+ else:
199+ self.open_toolbar()
200+
201+ def get_toolbar(self):
202+ """Returns the toolbar in the main events view."""
203+ return self.app.select_single("Toolbar")
204+
205+ def get_toolbar_button(self, buttonLabel):
206+ """Returns the toolbar button at position index"""
207+ toolbar = self.get_toolbar()
208+ if not toolbar.opened:
209+ self.open_toolbar()
210+ row = toolbar.select_single("QQuickRow")
211+ loaderList = row.select_many("QQuickLoader")
212+ for loader in loaderList:
213+ buttonList = loader.select_many("Button")
214+ for button in buttonList:
215+ if button.text == buttonLabel:
216+ return button
217+
218+ def click_toolbar_button(self, buttonLabel):
219+ """Clicks the toolbar button with buttonLabel"""
220+ #The toolbar button is assumed to be the following format
221+ #ToolbarActions {
222+ # Action {
223+ # objectName: "name"
224+ # text: value
225+ button = self.get_toolbar_button(buttonLabel)
226+ self.autopilot.pointing_device.click_object(button)
227+
228+ def open_toolbar(self):
229+ """Open the toolbar"""
230+ qmlView = self.get_qml_view()
231+
232+ lineX = qmlView.x + qmlView.width * 0.50
233+ startY = qmlView.y + qmlView.height - 1
234+ stopY = qmlView.y + qmlView.height * 0.95
235+
236+ self.autopilot.pointing_device.drag(lineX, startY, lineX, stopY)
237+
238+ def hide_toolbar(self):
239+ """Hide the toolbar"""
240+ qmlView = self.get_qml_view()
241+
242+ lineX = qmlView.x + qmlView.width * 0.50
243+ startY = qmlView.y + qmlView.height * 0.95
244+ stopY = qmlView.y + qmlView.height - 1
245+
246+ self.autopilot.pointing_device.drag(lineX, startY, lineX, stopY)
247+
248+ def set_popup_value(self, popover, button, value):
249+ """Changes the given popover selector to the request value
250+ At the moment this only works for values that are currently visible. To
251+ access the remaining items, a help method to drag and recheck is needed."""
252+ #The popover is assumed to be the following format
253+ # Popover {
254+ # Column {
255+ # ListView {
256+ # delegate: Standard {
257+ # objectName: "name"
258+ # text: value
259+
260+ self.autopilot.pointing_device.click_object(button)
261+ #we'll get all matching objects, incase the popover is reused between buttons
262+ itemList = lambda: self.get_objects("Standard", popover)
263+
264+ for item in itemList():
265+ if item.get_properties()['text'] == value:
266+ self.autopilot.pointing_device.click_object(item)
267+
268+ def get_tabs(self):
269+ """Return all tabs"""
270+ return self.get_object("Tabs", "rootTabs")
271+
272+ def _previous_tab(self):
273+ """Switch to the previous tab"""
274+ qmlView = self.get_qml_view()
275+
276+ startX = qmlView.x + qmlView.width * 0.35
277+ stopX = qmlView.x + qmlView.width * 0.50
278+ lineY = qmlView.y + qmlView.height * 0.05
279+
280+ self.autopilot.pointing_device.drag(startX, lineY, stopX, lineY)
281+ self.autopilot.pointing_device.click()
282+ self.autopilot.pointing_device.click()
283+
284+ def _next_tab(self):
285+ """Switch to the next tab"""
286+ qmlView = self.get_qml_view()
287+
288+ startX = qmlView.x + qmlView.width * 0.50
289+ stopX = qmlView.x + qmlView.width * 0.35
290+ lineY = qmlView.y + qmlView.height * 0.05
291+
292+ self.autopilot.pointing_device.drag(startX, lineY, stopX, lineY)
293+ self.autopilot.pointing_device.click()
294
295=== modified file 'tests/autopilot/ubuntu_weather_app/tests/__init__.py'
296--- tests/autopilot/ubuntu_weather_app/tests/__init__.py 2013-05-31 13:21:10 +0000
297+++ tests/autopilot/ubuntu_weather_app/tests/__init__.py 2013-06-20 16:20:34 +0000
298@@ -9,13 +9,15 @@
299
300 import os.path
301 import glob
302+import sqlite3
303+import time
304
305 from autopilot.input import Mouse, Touch, Pointer
306 from autopilot.platform import model
307 from autopilot.testcase import AutopilotTestCase
308
309 from ubuntu_weather_app.emulators.main_window import MainWindow
310-
311+from ubuntu_weather_app.emulators.ubuntusdk import ubuntusdk
312
313 class WeatherTestCase(AutopilotTestCase):
314
315@@ -32,10 +34,7 @@
316 def setUp(self):
317 self.pointing_device = Pointer(self.input_device_class.create())
318 super(WeatherTestCase, self).setUp()
319- if os.path.exists(self.local_location):
320- self.launch_test_local()
321- else:
322- self.launch_test_installed()
323+ self.launch_app()
324
325 """Workaround to find the qmlscene binary via shell globbing.
326 This is needed since we can't rely on qt5-default being installed on
327@@ -43,6 +42,12 @@
328 def qmlscene(self):
329 return glob.glob("/usr/lib/*/qt5/bin/qmlscene")[0]
330
331+ def launch_app(self):
332+ if os.path.exists(self.local_location):
333+ self.launch_test_local()
334+ else:
335+ self.launch_test_installed()
336+
337 def launch_test_local(self):
338 self.app = self.launch_test_application(
339 self.qmlscene(),
340@@ -56,6 +61,67 @@
341 "--desktop_file_hint=/usr/share/applications/ubuntu-weather-app.desktop",
342 app_type='qt')
343
344+ def launch_and_quit_app(self):
345+ self.launch_app()
346+ self.main_window.get_qml_view().visible.wait_for(True)
347+
348+ # When calling launch_app an instance of the spawned process
349+ # control object will be stored in self.app.process, and a cleanup
350+ # handler will be registered that essentially kills the process.
351+ # Therefore, by triggering the cleanup handler here we're killing the
352+ # process and removing the handler, which allows a clean launch of
353+ # the process during regular test setup.
354+ self.doCleanups()
355+
356 @property
357 def main_window(self):
358- return MainWindow(self.app)
359+ return ubuntusdk(self, self.app)#MainWindow(self, self.app)
360+
361+
362+class DatabaseMixin(object):
363+
364+ """
365+ Helper functions for dealing with sqlite databases
366+ """
367+
368+ def find_db(self):
369+ dbs_path = os.path.expanduser("~/.local/share/Qt Project/QtQmlViewer/QML/OfflineStorage/Databases/")
370+ if not os.path.exists(dbs_path):
371+ return None
372+
373+ files = [ f for f in os.listdir(dbs_path) if os.path.splitext(f)[1] == ".ini" ]
374+ for f in files:
375+ ini_path = os.path.join(dbs_path, f)
376+ with open(ini_path) as ini:
377+ for line in ini:
378+ if "=" in line:
379+ key, val = line.strip().split("=")
380+ if key == "Name" and val == "ubuntu-weather-app":
381+ try:
382+ return ini_path.replace(".ini", ".sqlite")
383+ except OSError:
384+ pass
385+ return None
386+
387+ def clean_db(self):
388+ path = self.find_db()
389+ if path is None:
390+ self.launch_and_quit_app()
391+ path = self.find_db()
392+ if path is None:
393+ self.assertNotEquals(path, None)
394+
395+ try:
396+ os.remove(path)
397+ except OSError:
398+ pass
399+
400+ def save_locations_to_storage(self, locations):
401+ path = self.find_db()
402+ conn = sqlite3.connect(path)
403+ cursor = conn.cursor()
404+ for loc_data in locations:
405+ cursor.execute("INSERT INTO Locations(date, data) VALUES('{}', '{}')".format(int(time.time()*1000), loc_data))
406+ conn.commit()
407+ conn.close()
408+
409
410=== added file 'tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py'
411--- tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py 1970-01-01 00:00:00 +0000
412+++ tests/autopilot/ubuntu_weather_app/tests/test_locationmanager.py 2013-06-20 16:20:34 +0000
413@@ -0,0 +1,118 @@
414+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
415+# Copyright 2013 Canonical
416+#
417+# This program is free software: you can redistribute it and/or modify it
418+# under the terms of the GNU General Public License version 3, as published
419+# by the Free Software Foundation.
420+
421+"""Tests for managing locations in Weather app"""
422+
423+from __future__ import absolute_import
424+
425+from testtools.matchers import Equals
426+from autopilot.matchers import Eventually
427+
428+from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin
429+from ubuntu_weather_app.tests.weatherdata import locations_data
430+
431+class TestLocationManager(WeatherTestCase, DatabaseMixin):
432+ def setUp(self):
433+ self.clean_db()
434+ super(TestLocationManager, self).setUp()
435+ self.assertThat(
436+ self.main_window.get_qml_view().visible, Eventually(Equals(True)))
437+
438+ def _open_add_location_page(self):
439+ """Opens the AddLocationPage"""
440+ # click on listitem to open AddLocationPage
441+ locationList = self.main_window.get_object('QQuickListView', 'LocationList')
442+ addCityItem = locationList.get_children()[0]
443+ self.pointing_device.move_to_object(addCityItem)
444+ self.pointing_device.click()
445+
446+ addLocPage = self.main_window.get_object("AddLocationPage", "AddLocationPage")
447+ self.assertThat(addLocPage.visible, Eventually(Equals(True)))
448+
449+ def test_add_location(self):
450+ """Adds a location"""
451+ self._open_add_location_page()
452+
453+ # insert city name to search for
454+ searchField = self.main_window.get_object("TextField", "SearchField")
455+ self.pointing_device.move_to_object(searchField)
456+ self.pointing_device.click()
457+ self.keyboard.type("London")
458+ self.assertThat(searchField.text, Eventually(Equals("London")))
459+ self.keyboard.press_and_release('Enter')
460+
461+ # wait for results and click on the first item
462+ resultsList = self.main_window.get_object('QQuickListView', 'SearchResultList')
463+ resultsModel = self.main_window.get_object('QQuickListModel', 'CitiesModel')
464+ self.assertThat(resultsList.visible, Eventually(Equals(True), timeout=30))
465+ firstResult = resultsList.get_children()[0].get_children()[0]
466+ self.pointing_device.move_to_object(firstResult)
467+ self.pointing_device.click()
468+
469+ # LocationManagerPage should be visible and "London" added
470+ locationList = self.main_window.get_object('QQuickListView', 'LocationList')
471+ addedItem = locationList.get_children()[0]
472+ self.assertThat(addedItem.get_children()[1].text, Eventually(Equals("London")))
473+
474+ # a location is defined, so toolbar should be visible
475+ toolbar = self.main_window.get_toolbar()
476+ self.assertThat(toolbar.opened, Eventually(Equals(True)))
477+
478+ def test_toolbar_hidden_when_no_location_is_defined(self):
479+ """Toolbar is not visible when there's no location is defined"""
480+ toolbar = self.main_window.get_toolbar()
481+ self.assertThat(toolbar.opened, Eventually(Equals(False)))
482+
483+ """no way found to click the back button"""
484+ #def test_cancel_adding_location(self):
485+ # self.open_add_location_page()
486+ # self.main_window.click_toolbar_button("Back")
487+ # locationList = self.main_window.get_object('QQuickListView', 'LocationList')
488+ # self.assertThat(locationList, Eventually(Equals(True)))
489+
490+class TestLocationManagerWithLocation(WeatherTestCase, DatabaseMixin):
491+ def setUp(self):
492+ self.clean_db()
493+ self.launch_and_quit_app()
494+ # add one location to storage
495+ self.save_locations_to_storage([locations_data[0]])
496+ super(TestLocationManagerWithLocation, self).setUp()
497+ self.assertThat(
498+ self.main_window.get_qml_view().visible, Eventually(Equals(True)))
499+
500+ def _open_location_manager(self):
501+ """Opens the location manager"""
502+ self.main_window.open_toolbar()
503+ self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(True)))
504+ self.main_window.click_toolbar_button("Edit")
505+
506+ def test_remove_location(self):
507+ """Removes location"""
508+ # check loading screen diappears
509+ loadingPage = self.main_window.get_object("Tabs", "LoadingTabs")
510+ self.assertThat(loadingPage.visible, Eventually(Equals(False)))
511+
512+ # go to the location manegr
513+ self._open_location_manager()
514+
515+ # swipe right to delete the first location
516+ locationList = self.main_window.get_object('QQuickListView', 'LocationList')
517+ locItem = locationList.get_children()[0]
518+ loc_x, loc_y, loc_w, loc_h = locItem.globalRect
519+ self.pointing_device.move(loc_x + 10, loc_y + loc_h / 2)
520+ self.pointing_device.press()
521+ self.pointing_device.move(loc_x + loc_w - 10, loc_y + loc_h / 2)
522+ self.pointing_device.release()
523+
524+ # toolbar disappears since no locations defined
525+ self.assertThat(self.main_window.get_toolbar().opened, Eventually(Equals(False)))
526+
527+ def test_toolbar_opened_when_location_is_defined(self):
528+ """Toolbar is visible since there's a location defined"""
529+ self._open_location_manager()
530+ toolbar = self.main_window.get_toolbar()
531+ self.assertThat(toolbar.opened, Eventually(Equals(True)))
532
533=== modified file 'tests/autopilot/ubuntu_weather_app/tests/test_mainview.py'
534--- tests/autopilot/ubuntu_weather_app/tests/test_mainview.py 2013-05-31 13:21:10 +0000
535+++ tests/autopilot/ubuntu_weather_app/tests/test_mainview.py 2013-06-20 16:20:34 +0000
536@@ -12,15 +12,18 @@
537 from testtools.matchers import Equals
538 from autopilot.matchers import Eventually
539
540-from ubuntu_weather_app.tests import WeatherTestCase
541-
542-
543-class TestMainView(WeatherTestCase):
544+from ubuntu_weather_app.tests import WeatherTestCase, DatabaseMixin
545+from ubuntu_weather_app.tests.weatherdata import locations_data
546+
547+class TestMainView(WeatherTestCase, DatabaseMixin):
548 """Tests creating weather"""
549
550 """ This is needed to wait for the application to start.
551 In the testfarm, the application may take some time to show up."""
552 def setUp(self):
553+ self.clean_db()
554+ self.launch_and_quit_app()
555+ self.save_locations_to_storage(locations_data)
556 super(TestMainView, self).setUp()
557 self.assertThat(
558 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
559@@ -28,6 +31,20 @@
560 def tearDown(self):
561 super(TestMainView, self).tearDown()
562
563- def test_tabs(self):
564- tabs = self.main_window.get_tabs()
565+ def test_switch_tabs(self):
566+ """Test start of app with two locations and switch from first to
567+ second tab"""
568+ # first tab should be visible after start
569+ tabs = self.main_window.get_tabs()
570+ tabObjects = self.main_window.get_objects('LocationTab','LocationTab')
571 self.assertThat(tabs.selectedTabIndex, Eventually(Equals(0)))
572+ self.assertThat(tabObjects[0].visible, Eventually(Equals(True)))
573+ self.assertThat(tabObjects[1].visible, Eventually(Equals(False)))
574+ self.assertThat(tabObjects[0].title, Eventually(Equals("London")))
575+ # switch to next tab
576+ self.main_window.switch_to_tab(1)
577+ self.assertThat(tabs.selectedTabIndex, Eventually(Equals(1)))
578+ self.assertThat(tabObjects[0].visible, Eventually(Equals(False)))
579+ self.assertThat(tabObjects[1].visible, Eventually(Equals(True)))
580+ self.assertThat(tabObjects[1].title, Eventually(Equals("Hamburg")))
581+
582
583=== added file 'tests/autopilot/ubuntu_weather_app/tests/weatherdata.py'
584--- tests/autopilot/ubuntu_weather_app/tests/weatherdata.py 1970-01-01 00:00:00 +0000
585+++ tests/autopilot/ubuntu_weather_app/tests/weatherdata.py 2013-06-20 16:20:34 +0000
586@@ -0,0 +1,10 @@
587+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
588+# Copyright 2013 Canonical
589+#
590+# This program is free software: you can redistribute it and/or modify it
591+# under the terms of the GNU General Public License version 3, as published
592+# by the Free Software Foundation.
593+locations_data = []
594+locations_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"}}""")
595+
596+locations_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"}}""")
597
598=== modified file 'ubuntu-weather-app.qml'
599--- ubuntu-weather-app.qml 2013-06-17 19:22:19 +0000
600+++ ubuntu-weather-app.qml 2013-06-20 16:20:34 +0000
601@@ -54,15 +54,17 @@
602 tabsObject.destroy()
603 }
604 var tabsString = "import QtQuick 2.0; import Ubuntu.Components 0.1; import Ubuntu.Components.Popups 0.1; import \"components\" as Components; "
605- + "Tabs {id: tabs; anchors.fill: parent;"
606+ + "Tabs {id: tabs; objectName: 'rootTabs'; anchors.fill: parent;"
607 if(locLength > 0) {
608 for(var x=0;x<locLength;x++) {
609 tabsString += "Components.LocationTab {title: '"+locations[x].location.name+"'; locationIndex: "+x+"} "
610 };
611 }
612 tabsString += "}"; // END Tabs componen
613- if(bigLoading !== null)
614+ if(bigLoading !== null) {
615 bigLoading.destroy();
616+ tabs.visible = false;
617+ }
618 loading.running = false;
619 tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")
620 if(focusToLast)
621@@ -107,10 +109,6 @@
622 })
623 }
624
625- Components.AddLocationPage {
626- id: addLocationDialog
627- }
628-
629 Components.SettingsPage {
630 id: settingsPage
631 }
632@@ -133,12 +131,11 @@
633
634 Page {
635 id: tabPage
636- title: "Simple page"
637 visible: false
638 ActivityIndicator{id:loading; running: false; z: 1; anchors{top: parent.top; topMargin: units.gu(0.5); right: parent.right; rightMargin: units.gu(1)}}
639 Tabs {
640 id: tabs
641- objectName: "Tabs"
642+ objectName: "LoadingTabs"
643 anchors.fill: parent
644 Tab {
645 title: "Loading..."

Subscribers

People subscribed via source and target branches