Merge lp:~martin-borho/ubuntu-weather-app/autopilot-tests into lp:ubuntu-weather-app/obsolete.trunk
- autopilot-tests
- Merge into trunk
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 | ||||||||||||
Related bugs: |
|
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
- 47. By Martin Borho
-
fixed add_location test
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
- 48. By Martin Borho
-
merged from trunk
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:48
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 49. By Martin Borho
-
added test for switching locations
- 50. By Martin Borho
-
splitted location manager tests into smaller parts
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:50
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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:/
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://
- 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
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:53
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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:/
Cheers
Martin
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!
Preview Diff
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..." |
FAILED: Continuous integration, rev:47 91.189. 93.125: 8080/job/ ubuntu- weather- app-ci/ 25/ 91.189. 93.125: 8080/job/ ubuntu- weather- app-quantal- amd64-ci/ 22/console 91.189. 93.125: 8080/job/ ubuntu- weather- app-raring- amd64-ci/ 25/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: 91.189. 93.125: 8080/job/ ubuntu- weather- app-ci/ 25/rebuild
http://