Merge lp:~elopio/ubuntu-calendar-app/fix1332173-swipe_to_create_new_event into lp:ubuntu-calendar-app
- fix1332173-swipe_to_create_new_event
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Nicholas Skaggs |
Approved revision: | 347 |
Merged at revision: | 337 |
Proposed branch: | lp:~elopio/ubuntu-calendar-app/fix1332173-swipe_to_create_new_event |
Merge into: | lp:ubuntu-calendar-app |
Diff against target: |
872 lines (+524/-183) 9 files modified
DeleteConfirmationDialog.qml (+2/-0) EventBubble.qml (+7/-5) EventDetails.qml (+6/-0) NewEvent.qml (+1/-0) tests/autopilot/calendar_app/data.py (+57/-0) tests/autopilot/calendar_app/emulators.py (+322/-122) tests/autopilot/calendar_app/tests/test_custom_proxy_objects.py (+30/-0) tests/autopilot/calendar_app/tests/test_data.py (+30/-0) tests/autopilot/calendar_app/tests/test_new_event.py (+69/-56) |
To merge this branch: | bzr merge lp:~elopio/ubuntu-calendar-app/fix1332173-swipe_to_create_new_event |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nicholas Skaggs (community) | Approve | ||
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Approve | |
Review via email: mp+224735@code.launchpad.net |
Commit message
Refactored the autopilot test to add a new event. Added helpers and self tests for them.
Description of the change
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:342
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:342
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:343
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:343
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:343
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Leo Arias (elopio) wrote : | # |
Three greens in a row, and I've also ran it many times on my machine and on my phone. I'd say this is good to go.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:344
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:346
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:347
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'DeleteConfirmationDialog.qml' |
2 | --- DeleteConfirmationDialog.qml 2014-04-13 02:03:43 +0000 |
3 | +++ DeleteConfirmationDialog.qml 2014-06-27 16:55:37 +0000 |
4 | @@ -4,6 +4,7 @@ |
5 | |
6 | Dialog { |
7 | id: dialogue |
8 | + objectName: "deleteConfirmationDialog" |
9 | |
10 | property var event; |
11 | |
12 | @@ -28,6 +29,7 @@ |
13 | } |
14 | |
15 | Button { |
16 | + objectName: "deleteEventButton" |
17 | text: event.parentId ? i18n.tr("Delete this") : i18n.tr("Delete") |
18 | color: UbuntuColors.orange |
19 | onClicked: { |
20 | |
21 | === modified file 'EventBubble.qml' |
22 | --- EventBubble.qml 2014-06-21 03:37:43 +0000 |
23 | +++ EventBubble.qml 2014-06-27 16:55:37 +0000 |
24 | @@ -133,8 +133,9 @@ |
25 | |
26 | Label{ |
27 | id: timeLabel |
28 | - fontSize:"small"; |
29 | - color:"gray" |
30 | + objectName: "timeLabel" |
31 | + fontSize: "small" |
32 | + color: "gray" |
33 | width: parent.width - rect.width |
34 | } |
35 | Rectangle{ |
36 | @@ -147,15 +148,16 @@ |
37 | } |
38 | Label{ |
39 | id: titleLabel |
40 | - fontSize:"small"; |
41 | - color:"black" |
42 | + objectName: "titleLabel" |
43 | + fontSize: "small" |
44 | + color: "black" |
45 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
46 | width: parent.width |
47 | } |
48 | |
49 | Label{ |
50 | id: descriptionLabel |
51 | - fontSize:"small"; |
52 | + fontSize: "small" |
53 | color:"gray" |
54 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
55 | width: parent.width |
56 | |
57 | === modified file 'EventDetails.qml' |
58 | --- EventDetails.qml 2014-05-27 11:28:27 +0000 |
59 | +++ EventDetails.qml 2014-06-27 16:55:37 +0000 |
60 | @@ -9,6 +9,7 @@ |
61 | |
62 | Page { |
63 | id: root |
64 | + objectName: "eventDetails" |
65 | |
66 | property var event; |
67 | property string headerColor :"black" |
68 | @@ -165,6 +166,7 @@ |
69 | ToolbarButton { |
70 | action:Action { |
71 | text: i18n.tr("Delete"); |
72 | + objectName: "delete" |
73 | iconSource: "image://theme/delete,edit-delete-symbolic" |
74 | onTriggered: { |
75 | var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event}); |
76 | @@ -274,6 +276,7 @@ |
77 | ThinDivider{} |
78 | Label{ |
79 | id: titleLabel |
80 | + objectName: "titleLabel" |
81 | fontSize: "large" |
82 | width: parent.width |
83 | wrapMode: Text.WordWrap |
84 | @@ -281,6 +284,7 @@ |
85 | } |
86 | Label{ |
87 | id: descLabel |
88 | + objectName: "descriptionLabel" |
89 | wrapMode: Text.WordWrap |
90 | fontSize: "small" |
91 | width: parent.width |
92 | @@ -293,6 +297,7 @@ |
93 | } |
94 | Label{ |
95 | id: locationLabel |
96 | + objectName: "locationLabel" |
97 | fontSize: "medium" |
98 | width: parent.width |
99 | wrapMode: Text.WordWrap |
100 | @@ -322,6 +327,7 @@ |
101 | //Guest Entery Model starts |
102 | Column{ |
103 | id: contactList |
104 | + objectName: 'contactList' |
105 | spacing: units.gu(1) |
106 | width: parent.width |
107 | clip: true |
108 | |
109 | === modified file 'NewEvent.qml' |
110 | --- NewEvent.qml 2014-06-19 13:21:24 +0000 |
111 | +++ NewEvent.qml 2014-06-27 16:55:37 +0000 |
112 | @@ -11,6 +11,7 @@ |
113 | |
114 | Page { |
115 | id: root |
116 | + objectName: 'newEventPage' |
117 | property var date; |
118 | |
119 | property var event:null; |
120 | |
121 | === added file 'tests/autopilot/calendar_app/data.py' |
122 | --- tests/autopilot/calendar_app/data.py 1970-01-01 00:00:00 +0000 |
123 | +++ tests/autopilot/calendar_app/data.py 2014-06-27 16:55:37 +0000 |
124 | @@ -0,0 +1,57 @@ |
125 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
126 | +# |
127 | +# Copyright (C) 2014 Canonical Ltd |
128 | +# |
129 | +# This program is free software: you can redistribute it and/or modify |
130 | +# it under the terms of the GNU General Public License version 3 as |
131 | +# published by the Free Software Foundation. |
132 | +# |
133 | +# This program is distributed in the hope that it will be useful, |
134 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
135 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
136 | +# GNU General Public License for more details. |
137 | +# |
138 | +# You should have received a copy of the GNU General Public License |
139 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
140 | + |
141 | +import uuid |
142 | + |
143 | + |
144 | +class DataMixin(object): |
145 | + |
146 | + """Mixin with common methods for data objects.""" |
147 | + |
148 | + def __repr__(self): |
149 | + return '%s(%r)' % (self.__class__, self.__dict__) |
150 | + |
151 | + def __eq__(self, other): |
152 | + return (isinstance(other, self.__class__) and |
153 | + self.__dict__ == other.__dict__) |
154 | + |
155 | + def __ne__(self, other): |
156 | + return not self.__eq__(other) |
157 | + |
158 | + |
159 | +class Event(DataMixin): |
160 | + |
161 | + """Event data object for user acceptance tests.""" |
162 | + |
163 | + def __init__(self, name, description, location, guests): |
164 | + # TODO add start date and end date, is all day event, recurrence and |
165 | + # reminders. --elopio - 2014-06-26 |
166 | + super(Event, self).__init__() |
167 | + self.name = name |
168 | + self.description = description |
169 | + self.location = location |
170 | + self.guests = guests |
171 | + |
172 | + @classmethod |
173 | + def make_unique(cls, unique_id=None): |
174 | + """Return a unique event.""" |
175 | + if unique_id is None: |
176 | + unique_id = str(uuid.uuid1()) |
177 | + name = 'Test event {}'.format(unique_id) |
178 | + description = 'Test description {}.'.format(unique_id) |
179 | + location = 'Test location {}'.format(unique_id) |
180 | + guests = ['Test guest {} 1'.format(unique_id)] |
181 | + return cls(name, description, location, guests) |
182 | |
183 | === modified file 'tests/autopilot/calendar_app/emulators.py' |
184 | --- tests/autopilot/calendar_app/emulators.py 2014-06-26 18:10:28 +0000 |
185 | +++ tests/autopilot/calendar_app/emulators.py 2014-06-27 16:55:37 +0000 |
186 | @@ -15,20 +15,28 @@ |
187 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
188 | |
189 | """Calendar app autopilot emulators.""" |
190 | + |
191 | +import logging |
192 | from time import sleep |
193 | |
194 | -from autopilot.introspection import dbus |
195 | - |
196 | - |
197 | +import autopilot.logging |
198 | +from dateutil import tz |
199 | + |
200 | +import ubuntuuitoolkit |
201 | from ubuntuuitoolkit import ( |
202 | emulators as toolkit_emulators, |
203 | pickers |
204 | ) |
205 | -from dateutil import tz |
206 | - |
207 | - |
208 | -class NewEventEntryField(toolkit_emulators.TextField): |
209 | - """Autopilot helper for the NewEventEntryField component.""" |
210 | + |
211 | +from calendar_app import data |
212 | + |
213 | + |
214 | +logger = logging.getLogger(__name__) |
215 | + |
216 | + |
217 | +class CalendarException(ubuntuuitoolkit.ToolkitException): |
218 | + |
219 | + """Exception raised when there are problems with the Calendar.""" |
220 | |
221 | |
222 | # for now we are borrowing the textfield helper for the textarea |
223 | @@ -40,9 +48,32 @@ |
224 | |
225 | class MainView(toolkit_emulators.MainView): |
226 | |
227 | - """ |
228 | - An emulator class that makes it easy to interact with the calendar-app. |
229 | - """ |
230 | + """An emulator that makes it easy to interact with the calendar-app.""" |
231 | + |
232 | + @autopilot.logging.log_action(logger.info) |
233 | + def go_to_day_view(self): |
234 | + """Open the day view. |
235 | + |
236 | + :return: The Day View page. |
237 | + |
238 | + """ |
239 | + day_tab = self.select_single('Tab', objectName='dayTab') |
240 | + if not day_tab.visible: |
241 | + self.switch_to_tab('dayTab') |
242 | + else: |
243 | + logger.debug('The Day View page is already opened.') |
244 | + return day_tab.select_single(DayView, objectName='DayView') |
245 | + |
246 | + @autopilot.logging.log_action(logger.info) |
247 | + def go_to_new_event(self): |
248 | + """Open the page to add a new event. |
249 | + |
250 | + :return: The New Event page. |
251 | + |
252 | + """ |
253 | + header = self.get_header() |
254 | + header.click_action_button('neweventbutton') |
255 | + return self.select_single(NewEvent, objectName='newEventPage') |
256 | |
257 | def set_picker(self, field, mode, value): |
258 | # open picker |
259 | @@ -85,52 +116,6 @@ |
260 | else: |
261 | return None |
262 | |
263 | - def get_new_event(self): |
264 | - try: |
265 | - return self.wait_select_single("NewEvent") |
266 | - except dbus.StateNotFoundError: |
267 | - return None |
268 | - |
269 | - def get_new_event_name_input_box(self): |
270 | - new_event = self.get_new_event() |
271 | - return new_event.wait_select_single(NewEventEntryField, |
272 | - objectName="newEventName") |
273 | - |
274 | - def get_event_start_time_field(self): |
275 | - new_event = self.get_new_event() |
276 | - return new_event.wait_select_single(NewEventEntryField, |
277 | - objectName="startTimeInput") |
278 | - |
279 | - def get_event_start_date_field(self): |
280 | - new_event = self.get_new_event() |
281 | - return new_event.wait_select_single(NewEventEntryField, |
282 | - objectName="startDateInput") |
283 | - |
284 | - def get_event_end_date_field(self): |
285 | - new_event = self.get_new_event() |
286 | - return new_event.wait_select_single(NewEventEntryField, |
287 | - objectName="endDateInput") |
288 | - |
289 | - def get_event_end_time_field(self): |
290 | - new_event = self.get_new_event() |
291 | - return new_event.wait_select_single(NewEventEntryField, |
292 | - objectName="endTimeInput") |
293 | - |
294 | - def get_event_location_field(self): |
295 | - new_event = self.get_new_event() |
296 | - return new_event.wait_select_single(NewEventEntryField, |
297 | - objectName="eventLocationInput") |
298 | - |
299 | - def get_event_people_field(self): |
300 | - new_event = self.get_new_event() |
301 | - return new_event.wait_select_single(NewEventEntryField, |
302 | - objectName="eventPeopleInput") |
303 | - |
304 | - def get_event_description_field(self): |
305 | - new_event = self.get_new_event() |
306 | - return new_event.wait_select_single(TextArea, |
307 | - objectName="eventDescriptionInput") |
308 | - |
309 | def safe_swipe_view(self, direction, view, date): |
310 | """ |
311 | direction: direction to swip |
312 | @@ -176,74 +161,289 @@ |
313 | return component.wait_select_single( |
314 | "Label", objectName="monthLabel").text |
315 | |
316 | - def get_num_events(self): |
317 | - return len(self.select_many("EventBubble")) |
318 | - |
319 | - def get_event(self, title): |
320 | - """ Return an event by title |
321 | - """ |
322 | - events = self.select_many("EventBubble") |
323 | - for event in events: |
324 | - try: |
325 | - event_found = event.select_single("Label", text=title) |
326 | - except: |
327 | - continue |
328 | - if event_found: |
329 | - return event |
330 | - |
331 | - return 0 |
332 | - |
333 | - def get_new_event_save_button(self): |
334 | - new_event = self.get_new_event() |
335 | - return new_event.wait_select_single("Button", |
336 | - objectName="accept") |
337 | - |
338 | - def get_new_event_cancel_button(self): |
339 | - new_event = self.get_new_event() |
340 | - return new_event.wait_select_single("Button", |
341 | - objectName="cancel") |
342 | - |
343 | def to_local_date(self, date): |
344 | utc = date.replace(tzinfo=tz.tzutc()) |
345 | local = utc.astimezone(tz.tzlocal()) |
346 | return local |
347 | |
348 | |
349 | -class Page(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
350 | - """Autopilot helper for Pages.""" |
351 | - |
352 | - def __init__(self, *args): |
353 | - super(Page, self).__init__(*args) |
354 | - # XXX we need a better way to keep reference to the main view. |
355 | - # --elopio - 2014-01-31 |
356 | - self.main_view = self.get_root_instance().select_single(MainView) |
357 | - |
358 | - def drag_page_up(self): |
359 | - """Drag the given page up.""" |
360 | - self._drag_page(direction='up') |
361 | - |
362 | - def drag_page_down(self): |
363 | - """Drag the given page down.""" |
364 | - self._drag_page(direction='down') |
365 | - |
366 | - def _drag_page(self, direction): |
367 | - """Function to drag the page up/down.""" |
368 | - self._wait_to_stop_moving() |
369 | - |
370 | - x, y, w, h = self.globalRect |
371 | - start_x = stop_x = x + (w / 2) |
372 | - start_y = y + (h / 2) |
373 | - |
374 | - if direction == "down": |
375 | - stop_y = start_y + h / 3 |
376 | - self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
377 | - else: |
378 | - stop_y = start_y - h / 3 |
379 | - self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
380 | - |
381 | - self._wait_to_stop_moving() |
382 | - |
383 | - def _wait_to_stop_moving(self): |
384 | - self.select_single( |
385 | - 'QQuickFlickable', |
386 | - objectName='animationContainer').moving.wait_for(False) |
387 | +class DayView(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
388 | + |
389 | + """Autopilot helper for the Day View page.""" |
390 | + |
391 | + def get_events(self, filter_duplicates=False): |
392 | + """Return the events for this day. |
393 | + |
394 | + :return: A list with the events. Each event is a tuple with name, start |
395 | + time and end time. |
396 | + |
397 | + """ |
398 | + event_bubbles = self._get_selected_day_event_bubbles(filter_duplicates) |
399 | + |
400 | + # sort by y, x |
401 | + event_bubbles = sorted( |
402 | + event_bubbles, |
403 | + key=lambda bubble: (bubble.globalRect.y, bubble.globalRect.x)) |
404 | + |
405 | + events = [] |
406 | + for event in event_bubbles: |
407 | + events.append(event.get_information()) |
408 | + |
409 | + return events |
410 | + |
411 | + def _get_current_day_component(self): |
412 | + components = self.select_many('TimeLineBaseComponent') |
413 | + for component in components: |
414 | + if (self.currentDay.datetime.date() == |
415 | + component.startDay.datetime.date()): |
416 | + return component |
417 | + else: |
418 | + raise CalendarException( |
419 | + 'Could not find the current day component.') |
420 | + |
421 | + def _get_selected_day_event_bubbles(self, filter_duplicates): |
422 | + selected_day = self._get_current_day_component() |
423 | + return self._get_event_bubbles(selected_day, filter_duplicates) |
424 | + |
425 | + def _get_event_bubbles(self, selected_day, filter_duplicates): |
426 | + event_bubbles = selected_day.select_many(EventBubble) |
427 | + if filter_duplicates: |
428 | + # XXX remove this once bug http://pad.lv/1334833 is fixed. |
429 | + # --elopio - 2014-06-26 |
430 | + separator_id = selected_day.select_single( |
431 | + 'QQuickRectangle', objectName='separator').id |
432 | + event_bubbles = self._remove_duplicate_events( |
433 | + separator_id, event_bubbles) |
434 | + return event_bubbles |
435 | + |
436 | + def _remove_duplicate_events(self, separator_id, event_bubbles): |
437 | + events = [] |
438 | + for bubble in event_bubbles: |
439 | + if bubble.id > separator_id: |
440 | + events.append(bubble) |
441 | + |
442 | + return events |
443 | + |
444 | + @autopilot.logging.log_action(logger.info) |
445 | + def open_event(self, name, filter_duplicates=False): |
446 | + """Open an event. |
447 | + |
448 | + :param name: The name of the event to open. |
449 | + :return: The Event Details page. |
450 | + |
451 | + """ |
452 | + event_bubbles = self._get_selected_day_event_bubbles(filter_duplicates) |
453 | + for bubble in event_bubbles: |
454 | + if bubble.get_name() == name: |
455 | + return bubble.open_event() |
456 | + else: |
457 | + raise CalendarException( |
458 | + 'Could not find event with name {}.'.format(name)) |
459 | + |
460 | + @autopilot.logging.log_action(logger.info) |
461 | + def delete_event(self, name, filter_duplicates=False): |
462 | + """Delete an event. |
463 | + |
464 | + :param name: The name of the event to delete. |
465 | + :return: The Day View page. |
466 | + |
467 | + """ |
468 | + event_details_page = self.open_event(name, filter_duplicates) |
469 | + return event_details_page.delete() |
470 | + |
471 | + |
472 | +class EventBubble(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
473 | + |
474 | + """Autopiot helper for the Event Bubble items.""" |
475 | + |
476 | + def get_information(self): |
477 | + """Return a tuple with the name, start time and end time.""" |
478 | + name = self.get_name() |
479 | + start_time, end_time = self._get_start_and_end_time() |
480 | + return name, start_time, end_time |
481 | + |
482 | + def _get_start_and_end_time(self): |
483 | + """Return a tuple with the start time and end time.""" |
484 | + time_label = self.select_single('Label', objectName='timeLabel') |
485 | + start_time, end_time = time_label.text.split(' - ') |
486 | + return start_time, end_time |
487 | + |
488 | + def get_name(self): |
489 | + """Return the event name.""" |
490 | + title_label = self.select_single('Label', objectName='titleLabel') |
491 | + return title_label.text |
492 | + |
493 | + @autopilot.logging.log_action(logger.info) |
494 | + def open_event(self): |
495 | + """Open the event. |
496 | + |
497 | + :return: The Event Details page. |
498 | + |
499 | + """ |
500 | + # If there are too many events, the center of the bubble |
501 | + # might be hidden by another event. Click the left side of the |
502 | + # bubble. |
503 | + left = self.globalRect.x + 5 |
504 | + center_y = self.globalRect.y + self.globalRect.height // 2 |
505 | + self.pointing_device.move(left, center_y) |
506 | + self.pointing_device.click() |
507 | + return self.get_root_instance().select_single( |
508 | + EventDetails, objectName='eventDetails') |
509 | + |
510 | + |
511 | +class NewEvent(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
512 | + |
513 | + """Autopilot helper for the New Event page.""" |
514 | + |
515 | + @autopilot.logging.log_action(logger.info) |
516 | + def add_event(self, event_information): |
517 | + """Add a new event. |
518 | + |
519 | + :param event_information: Values of the event to fill the form. |
520 | + :type event_information: data object with the attributes name, |
521 | + description, location and guests. |
522 | + :return: The Day View page. |
523 | + |
524 | + """ |
525 | + self._fill_form(event_information) |
526 | + self._save() |
527 | + return self.get_root_instance().select_single( |
528 | + DayView, objectName='DayView') |
529 | + |
530 | + @autopilot.logging.log_action(logger.debug) |
531 | + def _fill_form(self, event_information): |
532 | + """Fill the add event form. |
533 | + |
534 | + :param event_information: Values of the event to fill the form. |
535 | + :type event_information: data object with the attributes name, |
536 | + description, location and guests. |
537 | + |
538 | + """ |
539 | + # TODO fill start date and end date, is all day event, recurrence and |
540 | + # reminders. --elopio - 2014-06-26 |
541 | + if event_information.name is not None: |
542 | + self._fill_name(event_information.name) |
543 | + if event_information.description is not None: |
544 | + self._fill_description(event_information.description) |
545 | + if event_information.location is not None: |
546 | + self._fill_location(event_information.location) |
547 | + if event_information.guests is not None: |
548 | + self._fill_guests(event_information.guests) |
549 | + |
550 | + def _fill_name(self, value): |
551 | + self._ensure_entry_field_visible_and_write('newEventName', value) |
552 | + |
553 | + def _ensure_entry_field_visible_and_write(self, object_name, value): |
554 | + name_text_field = self._get_new_event_entry_field(object_name) |
555 | + self._ensure_visible_and_write(name_text_field, value) |
556 | + |
557 | + def _get_new_event_entry_field(self, object_name): |
558 | + return self.select_single(NewEventEntryField, objectName=object_name) |
559 | + |
560 | + def _ensure_visible_and_write(self, text_field, value): |
561 | + text_field.swipe_into_view() |
562 | + text_field.write(value) |
563 | + |
564 | + def _fill_description(self, value): |
565 | + description_text_area = self._get_description_text_area() |
566 | + self._ensure_visible_and_write(description_text_area, value) |
567 | + |
568 | + def _get_description_text_area(self): |
569 | + return self.select_single(TextArea, objectName='eventDescriptionInput') |
570 | + |
571 | + def _fill_location(self, value): |
572 | + self._ensure_entry_field_visible_and_write('eventLocationInput', value) |
573 | + |
574 | + def _fill_guests(self, value): |
575 | + if len(value) > 1: |
576 | + # See bug http://pad.lv/1295941 |
577 | + raise CalendarException( |
578 | + 'It is not yet possible to add more than one guest.') |
579 | + self._ensure_entry_field_visible_and_write( |
580 | + 'eventPeopleInput', value[0]) |
581 | + |
582 | + def _get_form_values(self): |
583 | + # TODO get start date and end date, is all day event, recurrence and |
584 | + # reminders. --elopio - 2014-06-26 |
585 | + name = self._get_new_event_entry_field('newEventName').text |
586 | + description = self._get_description_text_area().text |
587 | + location = self._get_new_event_entry_field('eventLocationInput').text |
588 | + # TODO once bug http://pad.lv/1295941 is fixed, we will have to build |
589 | + # the list of guests. --elopio - 2014-06-26 |
590 | + guests = [self._get_new_event_entry_field('eventPeopleInput').text] |
591 | + return data.Event(name, description, location, guests) |
592 | + |
593 | + @autopilot.logging.log_action(logger.info) |
594 | + def _save(self): |
595 | + """Save the new event.""" |
596 | + save_button = self.select_single('Button', objectName='accept') |
597 | + self.pointing_device.click_object(save_button) |
598 | + |
599 | + |
600 | +class NewEventEntryField(toolkit_emulators.TextField): |
601 | + |
602 | + """Autopilot helper for the NewEventEntryField component.""" |
603 | + |
604 | + |
605 | +class EventDetails(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
606 | + |
607 | + """Autopilot helper for the Event Details page.""" |
608 | + |
609 | + @autopilot.logging.log_action(logger.debug) |
610 | + def delete(self): |
611 | + """Click the delete button. |
612 | + |
613 | + :return: The Day View page. |
614 | + |
615 | + """ |
616 | + root = self.get_root_instance() |
617 | + header = root.select_single(MainView).get_header() |
618 | + header.click_action_button('delete') |
619 | + |
620 | + delete_confirmation_dialog = root.select_single( |
621 | + DeleteConfirmationDialog, objectName='deleteConfirmationDialog') |
622 | + delete_confirmation_dialog.confirm_deletion() |
623 | + |
624 | + return root.select_single(DayView, objectName='DayView') |
625 | + |
626 | + def get_event_information(self): |
627 | + """Return the information of the event.""" |
628 | + name = self._get_name() |
629 | + description = self._get_description() |
630 | + location = self._get_location() |
631 | + guests = self._get_guests() |
632 | + return data.Event(name, description, location, guests) |
633 | + |
634 | + def _get_name(self): |
635 | + return self._get_label_text('titleLabel') |
636 | + |
637 | + def _get_label_text(self, object_name): |
638 | + return self.select_single('Label', objectName=object_name).text |
639 | + |
640 | + def _get_description(self): |
641 | + return self._get_label_text('descriptionLabel') |
642 | + |
643 | + def _get_location(self): |
644 | + return self._get_label_text('locationLabel') |
645 | + |
646 | + def _get_guests(self): |
647 | + guests = [] |
648 | + contacts_list = self.select_single( |
649 | + 'QQuickColumn', objectName='contactList') |
650 | + guest_labels = contacts_list.select_many('Label') |
651 | + for label in guest_labels: |
652 | + guests.append(label.text) |
653 | + |
654 | + return guests |
655 | + |
656 | + |
657 | +class DeleteConfirmationDialog(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
658 | + |
659 | + """Autopilot helper for the Delete Confirmation dialog.""" |
660 | + |
661 | + @autopilot.logging.log_action(logger.debug) |
662 | + def confirm_deletion(self): |
663 | + """Confirm the deletion of the event.""" |
664 | + delete_button = self.select_single( |
665 | + 'Button', objectName='deleteEventButton') |
666 | + self.pointing_device.click_object(delete_button) |
667 | |
668 | === added file 'tests/autopilot/calendar_app/tests/test_custom_proxy_objects.py' |
669 | --- tests/autopilot/calendar_app/tests/test_custom_proxy_objects.py 1970-01-01 00:00:00 +0000 |
670 | +++ tests/autopilot/calendar_app/tests/test_custom_proxy_objects.py 2014-06-27 16:55:37 +0000 |
671 | @@ -0,0 +1,30 @@ |
672 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
673 | +# |
674 | +# Copyright (C) 2014 Canonical Ltd |
675 | +# |
676 | +# This program is free software: you can redistribute it and/or modify |
677 | +# it under the terms of the GNU General Public License version 3 as |
678 | +# published by the Free Software Foundation. |
679 | +# |
680 | +# This program is distributed in the hope that it will be useful, |
681 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
682 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
683 | +# GNU General Public License for more details. |
684 | +# |
685 | +# You should have received a copy of the GNU General Public License |
686 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
687 | + |
688 | +from calendar_app import data, tests |
689 | + |
690 | + |
691 | +class NewEventFormTestCase(tests.CalendarTestCase): |
692 | + |
693 | + def test_fill_form(self): |
694 | + """Test that the form can be filled with event information.""" |
695 | + test_event = data.Event.make_unique(unique_id='test uuid') |
696 | + |
697 | + new_event_page = new_event_page = self.main_view.go_to_new_event() |
698 | + new_event_page._fill_form(test_event) |
699 | + |
700 | + form_values = new_event_page._get_form_values() |
701 | + self.assertEqual(test_event, form_values) |
702 | |
703 | === added file 'tests/autopilot/calendar_app/tests/test_data.py' |
704 | --- tests/autopilot/calendar_app/tests/test_data.py 1970-01-01 00:00:00 +0000 |
705 | +++ tests/autopilot/calendar_app/tests/test_data.py 2014-06-27 16:55:37 +0000 |
706 | @@ -0,0 +1,30 @@ |
707 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
708 | +# |
709 | +# Copyright (C) 2014 Canonical Ltd |
710 | +# |
711 | +# This program is free software: you can redistribute it and/or modify |
712 | +# it under the terms of the GNU General Public License version 3 as |
713 | +# published by the Free Software Foundation. |
714 | +# |
715 | +# This program is distributed in the hope that it will be useful, |
716 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
717 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
718 | +# GNU General Public License for more details. |
719 | +# |
720 | +# You should have received a copy of the GNU General Public License |
721 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
722 | + |
723 | +import testtools |
724 | + |
725 | +from calendar_app import data |
726 | + |
727 | + |
728 | +class EventTestCase(testtools.TestCase): |
729 | + |
730 | + def test_make_unique_event_must_return_event_with_unique_id(self): |
731 | + event = data.Event.make_unique(unique_id='test uuid') |
732 | + |
733 | + self.assertEqual(event.name, 'Test event test uuid') |
734 | + self.assertEqual(event.description, 'Test description test uuid.') |
735 | + self.assertEqual(event.location, 'Test location test uuid') |
736 | + self.assertEqual(event.guests, ['Test guest test uuid 1']) |
737 | |
738 | === renamed file 'tests/autopilot/calendar_app/tests/test_calendar.py' => 'tests/autopilot/calendar_app/tests/test_new_event.py' |
739 | --- tests/autopilot/calendar_app/tests/test_calendar.py 2014-06-26 18:10:28 +0000 |
740 | +++ tests/autopilot/calendar_app/tests/test_new_event.py 2014-06-27 16:55:37 +0000 |
741 | @@ -17,62 +17,75 @@ |
742 | """Calendar app autopilot tests.""" |
743 | |
744 | from __future__ import absolute_import |
745 | + |
746 | +import logging |
747 | + |
748 | from autopilot.matchers import Eventually |
749 | -from testtools.matchers import Not, Is, NotEquals |
750 | +from testtools.matchers import HasLength |
751 | + |
752 | +from calendar_app import data |
753 | from calendar_app.tests import CalendarTestCase |
754 | |
755 | -import time |
756 | - |
757 | - |
758 | -class TestMainView(CalendarTestCase): |
759 | - |
760 | - def test_new_event(self): |
761 | - """test add new event """ |
762 | - # go to today |
763 | - self.main_view.switch_to_tab("dayTab") |
764 | - header = self.main_view.get_header() |
765 | - header.click_action_button('todaybutton') |
766 | - num_events = self.main_view.get_num_events() |
767 | - |
768 | - # click on new event button |
769 | - header = self.main_view.get_header() |
770 | - header.click_action_button('neweventbutton') |
771 | - self.assertThat(self.main_view.get_new_event, |
772 | - Eventually(Not(Is(None)))) |
773 | - |
774 | - # due to https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1326963 |
775 | - # the first event triggered is ignored, so we trigger an event |
776 | - # and a small sleep to clear before continuing input |
777 | - event_name_field = self.main_view.get_new_event_name_input_box() |
778 | - self.pointing_device.click_object(event_name_field) |
779 | - time.sleep(1) |
780 | - |
781 | - # input a new event name |
782 | - eventTitle = "Test event " + str(int(time.time())) |
783 | - self.main_view.get_new_event_name_input_box().write(eventTitle) |
784 | - |
785 | - # input description |
786 | - self.main_view.get_event_description_field(). \ |
787 | - write("My favorite test event") |
788 | - |
789 | - # input location |
790 | - self.main_view.get_event_location_field().write("England") |
791 | - |
792 | - # input guests |
793 | - self.main_view.get_event_people_field().write("me, myself, and I") |
794 | - |
795 | - # todo: iterate over all combinations |
796 | - # and include recurrence and reminders |
797 | - |
798 | - # click save button |
799 | - save_button = self.main_view.get_new_event_save_button() |
800 | - self.pointing_device.click_object(save_button) |
801 | - |
802 | - # verify that the event has been created in timeline |
803 | - self.main_view.switch_to_tab("dayTab") |
804 | - header = self.main_view.get_header() |
805 | - header.click_action_button('todaybutton') |
806 | - self.assertThat(self.main_view.get_num_events, |
807 | - Eventually(NotEquals(num_events))) |
808 | - |
809 | - # todo: verify entered event data |
810 | + |
811 | +logger = logging.getLogger(__name__) |
812 | + |
813 | + |
814 | +class NewEventTestCase(CalendarTestCase): |
815 | + |
816 | + # TODO add tests for events in the future and in the past, all day event, |
817 | + # event with recurrence and event with reminders. |
818 | + # We currently can't change the date of the new event because of bug |
819 | + # http://pad.lv/1328600 on Autopilot. |
820 | + # --elopio - 2014-06-26 |
821 | + |
822 | + def try_delete_event(self, event_name, filter_duplicates): |
823 | + try: |
824 | + day_view = self.main_view.go_to_day_view() |
825 | + day_view.delete_event(event_name, filter_duplicates) |
826 | + except Exception as exception: |
827 | + logger.warn(str(exception)) |
828 | + |
829 | + def test_add_new_event_with_default_values(self): |
830 | + """Test adding a new event with the default values. |
831 | + |
832 | + The event must be created on the currently selected date, |
833 | + with an end time, without recurrence and without reminders. |
834 | + |
835 | + """ |
836 | + test_event = data.Event.make_unique() |
837 | + |
838 | + day_view = self.main_view.go_to_day_view() |
839 | + original_events = day_view.get_events() |
840 | + |
841 | + new_event_page = self.main_view.go_to_new_event() |
842 | + # XXX remove this once bug http://pad.lv/1334833 is fixed. |
843 | + # --elopio - 2014-06-26 |
844 | + filter_duplicates = len(original_events) > 0 |
845 | + self.addCleanup( |
846 | + self.try_delete_event, test_event.name, filter_duplicates) |
847 | + day_view = new_event_page.add_event(test_event) |
848 | + |
849 | + def get_new_events(): |
850 | + return day_view.get_events(filter_duplicates) |
851 | + |
852 | + self.assertThat( |
853 | + get_new_events, Eventually(HasLength(len(original_events) + 1))) |
854 | + event_details_page = day_view.open_event(test_event.name) |
855 | + self.assertEqual( |
856 | + test_event, event_details_page.get_event_information()) |
857 | + |
858 | + def test_delete_event_must_remove_it_from_day_view(self): |
859 | + """Test deleting an event must no longer show it on the day view.""" |
860 | + # TODO remove the skip once the bug is fixed. --elopio - 2014-06-26 |
861 | + self.skipTest('This test fails because of bug http://pad.lv/1334883') |
862 | + event = data.Event.make_unique() |
863 | + |
864 | + day_view = self.main_view.go_to_day_view() |
865 | + original_events = day_view.get_events() |
866 | + |
867 | + new_event_page = self.main_view.go_to_new_event() |
868 | + day_view = new_event_page.add_event(event) |
869 | + day_view = day_view.delete_event(event.name, len(original_events) > 0) |
870 | + |
871 | + events_after_delete = day_view.get_events() |
872 | + self.assertEqual(original_events, events_after_delete) |
FAILED: Continuous integration, rev:341 91.189. 93.70:8080/ job/ubuntu- calendar- app-ci/ 567/ 91.189. 93.70:8080/ job/generic- mediumtests- utopic/ 679/console 91.189. 93.70:8080/ job/ubuntu- calendar- app-utopic- amd64-ci/ 125/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: 91.189. 93.70:8080/ job/ubuntu- calendar- app-ci/ 567/rebuild
http://