Merge lp:~elopio/ubuntu-ui-toolkit/reorg_autopilot_helpers into lp:ubuntu-ui-toolkit

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/ubuntu-ui-toolkit/reorg_autopilot_helpers
Merge into: lp:ubuntu-ui-toolkit
Prerequisite: lp:~elopio/ubuntu-ui-toolkit/flake8
Diff against target: 7625 lines (+3882/-2405)
54 files modified
components.api (+10/-0)
debian/control (+4/-0)
modules/Ubuntu/Components/plugin/adapters/alarmsadapter_organizer.cpp (+61/-24)
modules/Ubuntu/Components/plugin/adapters/alarmsadapter_p.h (+2/-0)
modules/Ubuntu/Components/plugin/alarmmanager_p.h (+10/-0)
modules/Ubuntu/Components/plugin/plugin.cpp (+3/-5)
modules/Ubuntu/Components/plugin/ucalarm.cpp (+33/-27)
modules/Ubuntu/Components/plugin/ucalarm_p.h (+1/-1)
modules/Ubuntu/Components/plugin/ucmouse.h (+52/-11)
modules/Ubuntu/Components/plugin/ucmousefilters.cpp (+220/-84)
modules/Ubuntu/Test/UbuntuTestCase.qml (+69/-1)
modules/Ubuntu/Test/deployment.pri (+6/-1)
modules/Ubuntu/Test/plugin/uctestcase.cpp (+12/-0)
modules/Ubuntu/Test/plugin/uctestcase.h (+1/-1)
tests/autopilot/ubuntuuitoolkit/__init__.py (+0/-17)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py (+66/-781)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_checkbox.py (+65/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py (+69/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py (+150/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py (+57/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_listitems.py (+114/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_mainview.py (+162/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_popups.py (+82/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py (+65/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabbar.py (+68/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabs.py (+41/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py (+88/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_toolbar.py (+106/-0)
tests/autopilot/ubuntuuitoolkit/emulators.py (+85/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_checkbox.py (+136/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_common.py (+68/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_header.py (+41/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitems.py (+170/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_main_view.py (+132/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_popups.py (+172/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_qquicklistview.py (+193/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py (+149/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textfield.py (+93/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_toolbar.py (+121/-0)
tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py (+0/-1081)
tests/resources/alarm/Alarms.qml (+6/-2)
tests/unit/runtest.sh (+1/-2)
tests/unit/tst_alarms/tst_alarms.cpp (+204/-21)
tests/unit/tst_mainview/tst_mainview.cpp (+3/-6)
tests/unit/tst_page/tst_page.cpp (+3/-39)
tests/unit_x11/tst_mousefilters/ForwardComposedEvents.qml (+55/-0)
tests/unit_x11/tst_mousefilters/ForwardEventChained.qml (+51/-0)
tests/unit_x11/tst_mousefilters/HoverEvent.qml (+38/-0)
tests/unit_x11/tst_mousefilters/tst_mousefilters.pro (+4/-1)
tests/unit_x11/tst_mousefilters/tst_mousefilterstest.cpp (+396/-201)
tests/unit_x11/tst_orientation/tst_orientation.cpp (+9/-47)
tests/unit_x11/tst_statesaver/tst_statesaver.cpp (+29/-37)
tests/unit_x11/tst_test/tst_ubuntutestcase.qml (+103/-15)
tests/unit_x11/tst_theme_engine/tst_theme_enginetest.cpp (+3/-0)
To merge this branch: bzr merge lp:~elopio/ubuntu-ui-toolkit/reorg_autopilot_helpers
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Cris Dywan Approve
Review via email: mp+215340@code.launchpad.net

This proposal supersedes a proposal from 2014-04-11.

This proposal has been superseded by a proposal from 2014-04-17.

Commit message

Deprecated the ubuntuuitoolkit.emulators module from the autopilot helpers.

To post a comment you must log in.
1011. By Leo Arias

Renamed the custom proxy object tests.

1012. By Leo Arias

Renamed UbuntuUIToolkitException to ToolkitException as suggested by thomi.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1013. By Leo Arias

Added a test that checks that the deprecated symbols point to the new namespace.

1014. By Leo Arias

Added a test for the deprecation warning.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1015. By Leo Arias

Added a _custom_proxy_objects module.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1016. By Leo Arias

For now, make it a file.

1017. By Leo Arias

Added the missing init.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

Makes sense. I presume we need to wait on having this altered in the Jenkins configuration since it'll always fail otherwise.

review: Approve
1018. By Leo Arias

Split the big file.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1019. By Leo Arias

Use __all__.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1020. By Leo Arias

Split the tests.

1021. By Leo Arias

Use __all__.

1022. By Leo Arias

Fixed flake8.

1023. By Leo Arias

Rename the rest of emulators.

1024. By Leo Arias

Merged with trunk.

1025. By Leo Arias

Merged with staging.

1026. By Leo Arias

Added the missing option selector files.

1027. By Leo Arias

Flake8 is now not prerequisite.

1028. By Leo Arias

Flake8 is now not prerequisite.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1029. By Leo Arias

Use a reload compatible with py3
.

1030. By Leo Arias

Expose the rest of the modules.

1031. By Leo Arias

Fixed the new circular dep.

1032. By Leo Arias

Fixed the new circular dep.

1033. By Leo Arias

Fixed the new circular dep.

1034. By Leo Arias

Merged with 30-optIn-tabsDrawer.

1035. By Leo Arias

Merged with 40-back-in-header.

1036. By Leo Arias

Reverted the change on the control.

1037. By Leo Arias

Reverted the change in debina/rules.

1038. By Leo Arias

Merged with staging.

1039. By Leo Arias

Merged with staging.

1040. By Leo Arias

Changed the namespace for listitems and popups.

1041. By Leo Arias

Merged with staging.

1042. By Leo Arias

Fixed the popups import.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2014-04-01 12:57:27 +0000
3+++ components.api 2014-04-17 01:29:24 +0000
4@@ -593,6 +593,8 @@
5 function findChild(obj,objectName)
6 function findInvisibleChild(obj,objectName)
7 function mouseMoveSlowly(item,x,y,dx,dy,steps,stepdelay)
8+ function flick(item, x, y, dx, dy, pressTimeout, steps, button, modifiers, delay)
9+ function mouseLongPress(item, x, y, button, modifiers, delay)
10 function tryCompareFunction(func, expectedResult, timeout)
11 plugins.qmltypes
12 name: "InverseMouseAreaType"
13@@ -687,27 +689,35 @@
14 Signal {
15 name: "pressed"
16 Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
17+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
18 Signal {
19 name: "released"
20 Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
21+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
22 Signal {
23 name: "clicked"
24 Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
25+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
26 Signal {
27 name: "pressAndHold"
28 Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
29+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
30 Signal {
31 name: "doubleClicked"
32 Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
33+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
34 Signal {
35 name: "positionChanged"
36 Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
37+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
38 Signal {
39 name: "entered"
40 Parameter { name: "event"; type: "QQuickMouseEvent"; isPointer: true }
41+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
42 Signal {
43 name: "exited"
44 Parameter { name: "event"; type: "QQuickMouseEvent"; isPointer: true }
45+ Parameter { name: "host"; type: "QQuickItem"; isPointer: true }
46 name: "UCQQuickImageExtension"
47 prototype: "QQuickImageBase"
48 exports: ["QQuickImageBase 0.1"]
49
50=== modified file 'debian/control'
51--- debian/control 2014-04-17 01:29:24 +0000
52+++ debian/control 2014-04-17 01:29:24 +0000
53@@ -135,7 +135,11 @@
54 python-autopilot (>= 1.4),
55 python-fixtures,
56 python-mock,
57+ python-testscenarios,
58+ python-testtools,
59 python3-fixtures,
60+ python3-testscenarios,
61+ python3-testtools,
62 python3-autopilot (>= 1.4),
63 ubuntu-ui-toolkit-examples (>= ${source:Version}),
64 Description: Test package for Ubuntu UI Toolkit
65
66=== modified file 'modules/Ubuntu/Components/plugin/adapters/alarmsadapter_organizer.cpp'
67--- modules/Ubuntu/Components/plugin/adapters/alarmsadapter_organizer.cpp 2014-02-21 20:02:29 +0000
68+++ modules/Ubuntu/Components/plugin/adapters/alarmsadapter_organizer.cpp 2014-04-17 01:29:24 +0000
69@@ -21,6 +21,7 @@
70 #include "alarmmanager_p.h"
71 #include "alarmrequest_p.h"
72 #include "alarmsadapter_p.h"
73+#include <qorganizertodooccurrence.h>
74
75 #include <QtCore/QFile>
76 #include <QtCore/QDir>
77@@ -55,11 +56,15 @@
78 , manager(0)
79 , fetchRequest(0)
80 {
81- QOrganizerManager local;
82- bool usingDefaultManager = local.availableManagers().contains(ALARM_MANAGER);
83- manager = (usingDefaultManager) ? new QOrganizerManager(ALARM_MANAGER) : new QOrganizerManager(ALARM_MANAGER_FALLBACK);
84+ QString envManager(qgetenv("ALARM_BACKEND"));
85+ if (!envManager.isEmpty() && QOrganizerManager::availableManagers().contains(envManager)) {
86+ manager = new QOrganizerManager(envManager);
87+ } else {
88+ manager = QOrganizerManager::availableManagers().contains(ALARM_MANAGER) ?
89+ new QOrganizerManager(ALARM_MANAGER) : new QOrganizerManager(ALARM_MANAGER_FALLBACK);
90+ }
91 manager->setParent(q_ptr);
92- if (!usingDefaultManager) {
93+ if (manager->managerName() != ALARM_MANAGER) {
94 qWarning() << "WARNING: default alarm manager not installed, using" << manager->managerName() << "manager.";
95 qWarning() << "This manager may not provide all the needed features.";
96 }
97@@ -114,6 +119,7 @@
98
99 int type, days;
100 in >> alarm.message >> alarm.date >> alarm.sound >> type >> days >> alarm.enabled;
101+ alarm.originalDate = alarm.date = AlarmData::transcodeDate(alarm.date, Qt::LocalTime);
102 alarm.type = static_cast<UCAlarm::AlarmType>(type);
103 alarm.days = static_cast<UCAlarm::DaysOfWeek>(days);
104
105@@ -142,7 +148,7 @@
106
107 Q_FOREACH(const AlarmData &alarm, alarmList) {
108 out << alarm.message
109- << alarm.date
110+ << AlarmData::transcodeDate(alarm.originalDate, Qt::UTC)
111 << alarm.sound
112 << alarm.type
113 << alarm.days
114@@ -156,8 +162,7 @@
115 {
116 event.setCollectionId(collection.id());
117 event.setAllDay(false);
118- event.setStartDateTime(alarm.date);
119- event.setDueDateTime(alarm.date);
120+ event.setStartDateTime(AlarmData::transcodeDate(alarm.date, Qt::UTC));
121 event.setDisplayLabel(alarm.message);
122
123 if (alarm.enabled) {
124@@ -224,8 +229,9 @@
125
126 alarm.cookie = QVariant::fromValue<QOrganizerItemId>(event.id());
127 alarm.message = event.displayLabel();
128- alarm.date = AlarmData::normalizeDate(event.dueDateTime());
129+ alarm.date = AlarmData::transcodeDate(event.startDateTime().toUTC(), Qt::LocalTime);
130 alarm.sound = QUrl(event.description());
131+ alarm.originalDate = alarm.date;
132
133 // check if the alarm is enabled or not
134 QOrganizerItemVisualReminder visual = event.detail(QOrganizerItemDetail::TypeVisualReminder);
135@@ -303,8 +309,8 @@
136 Q_FOREACH(const QOrganizerItem &item, alarms) {
137 // repeating alarms may be fetched as occurences, therefore check their parent event
138 if (item.type() == QOrganizerItemType::TypeTodoOccurrence) {
139- QOrganizerTodoOccurrence occurence = static_cast<QOrganizerTodoOccurrence>(item);
140- QOrganizerItemId eventId = occurence.parentId();
141+ QOrganizerTodoOccurrence occurrence = static_cast<QOrganizerTodoOccurrence>(item);
142+ QOrganizerItemId eventId = occurrence.parentId();
143 if (parentId.contains(eventId)) {
144 continue;
145 }
146@@ -317,6 +323,7 @@
147 }
148 AlarmData alarm;
149 if (alarmDataFromOrganizerEvent(event, alarm) == UCAlarm::NoError) {
150+ adjustAlarmOccurrence(event, alarm);
151 alarmList << alarm;
152 }
153 }
154@@ -324,9 +331,52 @@
155 saveAlarms();
156 Q_EMIT q_ptr->alarmsChanged();
157 completed = true;
158+ fetchRequest->deleteLater();
159 fetchRequest = 0;
160 }
161
162+void AlarmsAdapter::adjustAlarmOccurrence(const QOrganizerTodo &event, AlarmData &alarm)
163+{
164+ if (alarm.type == UCAlarm::OneTime) {
165+ return;
166+ }
167+ // with EDS we need to query the occurrences separately as the fetch reports only the main events
168+ // with fallback manager this does not reduce the performance and does work the same way.
169+ QDateTime currentDate = AlarmData::normalizeDate(QDateTime::currentDateTime());
170+ if (alarm.date > currentDate) {
171+ // no need to adjust date, the event occurs in the future
172+ return;
173+ }
174+ QDateTime startDate;
175+ QDateTime endDate;
176+ if (alarm.type == UCAlarm::Repeating) {
177+ // 8 days is enough from the starting date (or current date depending on the start date)
178+ startDate = (alarm.date > currentDate) ? alarm.date : currentDate;
179+ endDate = startDate.addDays(8);
180+ }
181+
182+ // transcode both dates
183+ startDate = AlarmData::transcodeDate(startDate, Qt::UTC);
184+ endDate = AlarmData::transcodeDate(endDate, Qt::UTC);
185+
186+ QList<QOrganizerItem> occurrences = manager->itemOccurrences(event, startDate, endDate, 10);
187+ // get the first occurrence and use the date from it
188+ if ((occurrences.length() > 0) && (occurrences[0].type() == QOrganizerItemType::TypeTodoOccurrence)) {
189+ // loop till we get a proper future due date
190+ for (int i = 0; i < occurrences.count(); i++) {
191+ QOrganizerTodoOccurrence occurrence = static_cast<QOrganizerTodoOccurrence>(occurrences[i]);
192+ // check if the date is after the current datetime
193+ // the first occurrence is the one closest to the currentDate, therefore we can safely
194+ // set that startDate to the alarm
195+ alarm.date = AlarmData::transcodeDate(occurrence.startDateTime().toUTC(), Qt::LocalTime);
196+ if (alarm.date > currentDate) {
197+ // we have the proper date set, leave
198+ break;
199+ }
200+ }
201+ }
202+}
203+
204 /*-----------------------------------------------------------------------------
205 * AlarmRequestAdapter implementation
206 */
207@@ -410,23 +460,10 @@
208 QOrganizerItemFetchRequest *operation = new QOrganizerItemFetchRequest(q_ptr);
209 operation->setManager(owner->manager);
210
211- // FIXME: Since returning all events without a limit of date is not a good solution we need to find
212- // a better solution for that.
213- // The current solution filters only the next 7 days (one week).
214- // This will be enough for now, since the current alarms occur weekly, but for the future
215- // we want to allow create alarms with monthly or yearly recurrence
216- QDate currentDate = QDate::currentDate();
217- QDateTime startDate(currentDate,
218- QTime(0,0,0));
219- QDateTime endDate(currentDate.addDays(7),
220- QTime(23,59,59));
221- operation->setStartDate(startDate);
222- operation->setEndDate(endDate);
223-
224 // set sort order
225 QOrganizerItemSortOrder sortOrder;
226 sortOrder.setDirection(Qt::AscendingOrder);
227- sortOrder.setDetail(QOrganizerItemDetail::TypeTodoTime, QOrganizerTodoTime::FieldDueDateTime);
228+ sortOrder.setDetail(QOrganizerItemDetail::TypeTodoTime, QOrganizerTodoTime::FieldStartDateTime);
229 operation->setSorting(QList<QOrganizerItemSortOrder>() << sortOrder);
230
231 // set filter
232
233=== modified file 'modules/Ubuntu/Components/plugin/adapters/alarmsadapter_p.h'
234--- modules/Ubuntu/Components/plugin/adapters/alarmsadapter_p.h 2013-09-18 10:13:04 +0000
235+++ modules/Ubuntu/Components/plugin/adapters/alarmsadapter_p.h 2014-04-17 01:29:24 +0000
236@@ -24,6 +24,7 @@
237
238 #include <qorganizer.h>
239 #include <qorganizermanager.h>
240+#include <qorganizertodo.h>
241
242 QTORGANIZER_USE_NAMESPACE
243
244@@ -78,6 +79,7 @@
245 QOrganizerCollection collection;
246
247 void completeFetchAlarms(const QList<QOrganizerItem> &alarmList);
248+ void adjustAlarmOccurrence(const QOrganizerTodo &event, AlarmData &alarm);
249
250 void loadAlarms();
251 void saveAlarms();
252
253=== modified file 'modules/Ubuntu/Components/plugin/alarmmanager_p.h'
254--- modules/Ubuntu/Components/plugin/alarmmanager_p.h 2013-09-12 05:27:40 +0000
255+++ modules/Ubuntu/Components/plugin/alarmmanager_p.h 2014-04-17 01:29:24 +0000
256@@ -48,6 +48,7 @@
257 AlarmData(const AlarmData &other)
258 : changes(0)
259 , cookie(other.cookie)
260+ , originalDate(other.originalDate)
261 , date(other.date)
262 , message(other.message)
263 , type(other.type)
264@@ -100,10 +101,19 @@
265 return QDateTime(dt.date(), time, dt.timeSpec());
266 }
267
268+ // the function normalizes and transcodes the date into UTC/LocalTime equivalent
269+ static QDateTime transcodeDate(const QDateTime &dt, Qt::TimeSpec targetSpec) {
270+ if (dt.timeSpec() == targetSpec) {
271+ return normalizeDate(dt);
272+ }
273+ return QDateTime(dt.date(), normalizeDate(dt).time(), targetSpec);
274+ }
275+
276 unsigned int changes;
277 QVariant cookie;
278
279 // data members
280+ QDateTime originalDate;
281 QDateTime date;
282 QString message;
283 QUrl sound;
284
285=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
286--- modules/Ubuntu/Components/plugin/plugin.cpp 2014-03-05 12:29:58 +0000
287+++ modules/Ubuntu/Components/plugin/plugin.cpp 2014-04-17 01:29:24 +0000
288@@ -53,9 +53,6 @@
289 #include <unistd.h>
290 #include <stdexcept>
291
292-// Needed for unit tests
293-Q_DECLARE_METATYPE(QList<QQmlError>)
294-
295 /*
296 * Type registration functions.
297 */
298@@ -177,10 +174,11 @@
299 qmlRegisterSingletonType<UCUriHandler>(uri, 0, 1, "UriHandler", registerUriHandler);
300 qmlRegisterType<UCMouse>(uri, 0, 1, "Mouse");
301 qmlRegisterType<UCInverseMouse>(uri, 0, 1, "InverseMouse");
302- // Needed for unit tests
303- qRegisterMetaType<QList <QQmlError> >();
304 // register QML singletons
305 qmlRegisterSingletonType<QObject>(uri, 0, 1, "PickerPanel", registerPickerPanel);
306+
307+ // register custom event
308+ ForwardedEvent::registerForwardedEvent();
309 }
310
311 void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
312
313=== modified file 'modules/Ubuntu/Components/plugin/ucalarm.cpp'
314--- modules/Ubuntu/Components/plugin/ucalarm.cpp 2013-09-18 10:25:18 +0000
315+++ modules/Ubuntu/Components/plugin/ucalarm.cpp 2014-04-17 01:29:24 +0000
316@@ -102,15 +102,21 @@
317
318 int UCAlarmPrivate::nextDayOfWeek(UCAlarm::DaysOfWeek days, int fromDay)
319 {
320- if (fromDay <= 0) {
321+ if (fromDay <= 0 || fromDay >= Qt::Sunday) {
322+ // start from the beginning of the week
323 fromDay = Qt::Monday;
324+ } else {
325+ // start checking from the next day onwards
326+ fromDay++;
327 }
328 for (int d = fromDay; d <= Qt::Sunday; d++) {
329 if ((1 << (d - 1)) & days) {
330 return d;
331 }
332 }
333- return 0;
334+
335+ // none found for the rest of the week, return the fist day set
336+ return firstDayOfWeek(days);
337 }
338
339 // checks whether the given num has more than one bit set
340@@ -147,23 +153,14 @@
341 return UCAlarm::NoError;
342 }
343
344-UCAlarm::Error UCAlarmPrivate::checkDow()
345+// adjust dayOfWeek
346+UCAlarm::Error UCAlarmPrivate::adjustDow()
347 {
348 if (!rawData.days) {
349 return UCAlarm::NoDaysOfWeek;
350 } else if (rawData.days == UCAlarm::AutoDetect) {
351 rawData.days = dayOfWeek(rawData.date);
352 rawData.changes |= AlarmData::Days;
353- } else if (rawData.days != UCAlarm::Daily) {
354- int alarmDay = firstDayOfWeek(rawData.days);
355- int dayOfWeek = rawData.date.date().dayOfWeek();
356- if (alarmDay < dayOfWeek) {
357- rawData.date = rawData.date.addDays(7 - dayOfWeek + alarmDay);
358- rawData.changes |= AlarmData::Date;
359- } else if (alarmDay > dayOfWeek) {
360- rawData.date = rawData.date.addDays(alarmDay - dayOfWeek);
361- rawData.changes |= AlarmData::Date;
362- }
363 }
364 return UCAlarm::NoError;
365 }
366@@ -175,8 +172,7 @@
367 return UCAlarm::OneTimeOnMoreDays;
368 }
369
370- // adjust start date and/or dayOfWeek according to their values
371- UCAlarm::Error result = checkDow();
372+ UCAlarm::Error result = adjustDow();
373 if (result != UCAlarm::NoError) {
374 return result;
375 }
376@@ -193,23 +189,25 @@
377 // start date is adjusted depending on the days value;
378 // start date can be set to be the current time, as scheduling will move
379 // it to the first occurence.
380- UCAlarm::Error result = checkDow();
381+ UCAlarm::Error result = adjustDow();
382 if (result != UCAlarm::NoError) {
383 return result;
384 }
385
386- // move start time to the first occurence if needed
387+ // move start time of the first occurence if needed
388 int dayOfWeek = rawData.date.date().dayOfWeek();
389 if (!isDaySet(dayOfWeek, rawData.days) || (rawData.date <= QDateTime::currentDateTime())) {
390 // check the next occurence of the alarm
391- int nextOccurence = nextDayOfWeek(rawData.days, dayOfWeek);
392- if (nextOccurence <= 0) {
393- // the starting date should be moved to the next week
394- nextOccurence = firstDayOfWeek(rawData.days);
395- rawData.date.addDays(7 - dayOfWeek + nextOccurence);
396+ int nextOccurrence = nextDayOfWeek(rawData.days, dayOfWeek);
397+ if (nextOccurrence == dayOfWeek) {
398+ // move the date to the same day next week
399+ rawData.date = rawData.date.addDays(7);
400+ } else if (nextOccurrence < dayOfWeek) {
401+ // the starting date should be moved to the next week's occurrence
402+ rawData.date = rawData.date.addDays(7 - dayOfWeek + nextOccurrence);
403 } else {
404 // the starting date is still this week
405- rawData.date.addDays(nextOccurence - dayOfWeek);
406+ rawData.date = rawData.date.addDays(nextOccurrence - dayOfWeek);
407 }
408 rawData.changes |= AlarmData::Date;
409 }
410@@ -304,7 +302,7 @@
411 : QObject(parent)
412 , d_ptr(new UCAlarmPrivate(this))
413 {
414- d_ptr->rawData.date = dt;
415+ d_ptr->rawData.date = AlarmData::normalizeDate(dt);
416 if (!message.isEmpty()) {
417 d_ptr->rawData.message = message;
418 }
419@@ -315,7 +313,7 @@
420 : QObject(parent)
421 , d_ptr(new UCAlarmPrivate(this))
422 {
423- d_ptr->rawData.date = dt;
424+ d_ptr->rawData.date = AlarmData::normalizeDate(dt);
425 d_ptr->rawData.type = Repeating;
426 d_ptr->rawData.days = days;
427 if (!message.isEmpty()) {
428@@ -366,12 +364,16 @@
429 void UCAlarm::setDate(const QDateTime &date)
430 {
431 Q_D(UCAlarm);
432- if (d->rawData.date == date) {
433+ if (d->rawData.date == AlarmData::normalizeDate(date)) {
434 return;
435 }
436- d->rawData.date = date;
437+ d->rawData.date = AlarmData::normalizeDate(date);
438 d->rawData.changes |= AlarmData::Date;
439 Q_EMIT dateChanged();
440+ if (d->rawData.type == UCAlarm::OneTime) {
441+ // adjust dayOfWeek as well
442+ setDaysOfWeek(UCAlarm::AutoDetect);
443+ }
444 }
445
446 /*!
447@@ -520,6 +522,8 @@
448 Q_EMIT soundChanged();
449 }
450
451+
452+
453 /*!
454 * \qmlproperty Error Alarm::error
455 * The property holds the error code occurred during the last performed operation.
456@@ -670,6 +674,8 @@
457 if (result != UCAlarm::NoError) {
458 d->_q_syncStatus(Saving, Fail, result);
459 } else {
460+ // the alarm has been modified, therefore update the original date as well
461+ d->rawData.originalDate = d->rawData.date;
462 if (d->createRequest()) {
463 d->request->save(d->rawData);
464 }
465
466=== modified file 'modules/Ubuntu/Components/plugin/ucalarm_p.h'
467--- modules/Ubuntu/Components/plugin/ucalarm_p.h 2013-09-18 10:25:18 +0000
468+++ modules/Ubuntu/Components/plugin/ucalarm_p.h 2014-04-17 01:29:24 +0000
469@@ -50,7 +50,7 @@
470 static int nextDayOfWeek(UCAlarm::DaysOfWeek days, int fromDay);
471 static bool multipleDaysSet(UCAlarm::DaysOfWeek days);
472 UCAlarm::Error checkAlarm();
473- UCAlarm::Error checkDow();
474+ UCAlarm::Error adjustDow();
475 UCAlarm::Error checkOneTime();
476 UCAlarm::Error checkRepeatingWeekly();
477
478
479=== modified file 'modules/Ubuntu/Components/plugin/ucmouse.h'
480--- modules/Ubuntu/Components/plugin/ucmouse.h 2014-02-13 17:16:06 +0000
481+++ modules/Ubuntu/Components/plugin/ucmouse.h 2014-04-17 01:29:24 +0000
482@@ -20,10 +20,47 @@
483
484 #include <QtCore/QObject>
485 #include <QtQml>
486+#include <QtQuick/QQuickItem>
487 #include <private/qquickevents_p_p.h>
488 #include <QtCore/qbasictimer.h>
489
490-class QQuickItem;
491+class ForwardedEvent : public QEvent {
492+public:
493+ enum EventType {
494+ MousePress,
495+ MouseRelease,
496+ MouseMove,
497+ MouseDblClick,
498+ HoverEnter,
499+ HoverExit,
500+ MouseClick,
501+ MouseLongPress,
502+ };
503+ ForwardedEvent(EventType type, QQuickItem *sender, QEvent *originalEvent, QQuickMouseEvent *quickEvent)
504+ : QEvent((QEvent::Type)m_eventBase)
505+ , m_subType(type)
506+ , m_sender(sender)
507+ , m_originalEvent(originalEvent)
508+ , m_quickEvent(quickEvent)
509+ {
510+ setAccepted(false);
511+ }
512+
513+ static void registerForwardedEvent();
514+
515+ EventType subType() const { return m_subType; }
516+ QQuickItem *sender() const { return m_sender; }
517+ QQuickMouseEvent *quickEvent() const { return m_quickEvent; }
518+ QEvent *originalEvent() const { return m_originalEvent; }
519+ static QEvent::Type baseType() { return m_eventBase; }
520+private:
521+ EventType m_subType;
522+ QPointer<QQuickItem> m_sender;
523+ QEvent *m_originalEvent;
524+ QPointer<QQuickMouseEvent> m_quickEvent;
525+ static QEvent::Type m_eventBase;
526+};
527+
528 class UCMouse : public QObject
529 {
530 Q_OBJECT
531@@ -62,23 +99,24 @@
532 void clickAndHoldThresholdChanged();
533 void priorityChanged();
534
535- void pressed(QQuickMouseEvent *mouse);
536- void released(QQuickMouseEvent *mouse);
537- void clicked(QQuickMouseEvent *mouse);
538- void pressAndHold(QQuickMouseEvent *mouse);
539- void doubleClicked(QQuickMouseEvent *mouse);
540- void positionChanged(QQuickMouseEvent *mouse);
541- void entered(QQuickMouseEvent *event);
542- void exited(QQuickMouseEvent *event);
543+ void pressed(QQuickMouseEvent *mouse, QQuickItem *host);
544+ void released(QQuickMouseEvent *mouse, QQuickItem *host);
545+ void clicked(QQuickMouseEvent *mouse, QQuickItem *host);
546+ void pressAndHold(QQuickMouseEvent *mouse, QQuickItem *host);
547+ void doubleClicked(QQuickMouseEvent *mouse, QQuickItem *host);
548+ void positionChanged(QQuickMouseEvent *mouse, QQuickItem *host);
549+ void entered(QQuickMouseEvent *event, QQuickItem *host);
550+ void exited(QQuickMouseEvent *event, QQuickItem *host);
551
552 protected:
553 virtual bool eventFilter(QObject *, QEvent *);
554 virtual void timerEvent(QTimerEvent *event);
555 virtual bool mouseEvents(QObject *target, QMouseEvent *event);
556 virtual bool hoverEvents(QObject *target, QHoverEvent *event);
557+ virtual bool forwardedEvents(ForwardedEvent *event);
558 virtual bool hasAttachedFilter(QQuickItem *item);
559
560- void setHovered(bool hovered);
561+ void setHovered(bool hovered, QEvent *hoverEvent);
562 bool mousePressed(QMouseEvent *event);
563 bool mouseReleased(QMouseEvent *event);
564 bool mouseDblClick(QMouseEvent *event);
565@@ -91,7 +129,7 @@
566 bool isDoubleClickConnected();
567 bool isMouseEvent(QEvent::Type type);
568 bool isHoverEvent(QEvent::Type type);
569- void forwardEvent(QEvent *event);
570+ bool forwardEvent(ForwardedEvent::EventType type, QEvent *event, QQuickMouseEvent *quickEvent);
571
572 protected:
573 QQuickItem *m_owner;
574@@ -107,6 +145,7 @@
575 Priority m_priority;
576 int m_moveThreshold;
577
578+ bool m_signalWhenContains:1;
579 bool m_enabled: 1;
580 bool m_moved:1;
581 bool m_longPress:1;
582@@ -115,4 +154,6 @@
583 };
584 QML_DECLARE_TYPEINFO(UCMouse, QML_HAS_ATTACHED_PROPERTIES)
585
586+extern const int DefaultPressAndHoldDelay;
587+
588 #endif // UCMOUSE_H
589
590=== modified file 'modules/Ubuntu/Components/plugin/ucmousefilters.cpp'
591--- modules/Ubuntu/Components/plugin/ucmousefilters.cpp 2014-02-13 17:16:06 +0000
592+++ modules/Ubuntu/Components/plugin/ucmousefilters.cpp 2014-04-17 01:29:24 +0000
593@@ -18,7 +18,6 @@
594 #include "ucmouse.h"
595 #include "ucinversemouse.h"
596 #include <QtQml/QQmlInfo>
597-#include <QtQuick/QQuickItem>
598 #include <QtGui/QGuiApplication>
599 #include <private/qqmlglobal_p.h>
600 #include <QtQuick/private/qquickmousearea_p.h>
601@@ -31,6 +30,15 @@
602 // keep in sync with QQuickMouseArea PressAndHoldDelay
603 const int DefaultPressAndHoldDelay = 800;
604
605+QEvent::Type ForwardedEvent::m_eventBase = QEvent::None;
606+void ForwardedEvent::registerForwardedEvent()
607+{
608+ if (m_eventBase > 0) {
609+ return;
610+ }
611+ m_eventBase = (QEvent::Type)QEvent::registerEventType();
612+}
613+
614 /*
615 * Attached filter instantiator template
616 */
617@@ -158,7 +166,8 @@
618 their parent. In this way mouse events will land in these items too, and mouse
619 filter attached to those can also handle the event. This is useful when creating
620 custom types where the mouse handling item is nested into a non-mouse handling
621- one, and we want to provide additional filtering possibility to the user.
622+ one, and we want to provide additional filtering possibility to the user. These
623+ type of items are called proxy handlers.
624
625 \qml
626 Item {
627@@ -180,7 +189,54 @@
628 In this example the mouse press is first handled by the mouse filter attached
629 to TextInput, then it is forwarded to the top item and finally to the TextInput.
630 Accepting the mouse event will stop propagation to the top item as well as to
631- the TextInput.
632+ the TextInput. The topmost item itself does not handle mouse events, therefore
633+ it will be a sinple proxy handler item. However, proxies can themself handle
634+ mouse events. Therefore each mouse event signal has the \a host parameter specifying
635+ the sender of the mouse event reported.
636+
637+ \note The forwarded events are handled in the proxy handlers only if the mouse
638+ position points inside their area. If the forwarded mouse position falls outside
639+ the target area, the event will not be reported, however will be forwarded further
640+ to the items in the list. In the following example the mouse press in red rectangle
641+ will be printed as well as the proxied mouse press from the main item.
642+ \qml
643+ import QtQuick 2.0
644+ import Ubuntu.Components 0.1
645+
646+ Item {
647+ id: main
648+ width: units.gu(40)
649+ height: units.gu(71)
650+
651+ Mouse.onPressed: console.log("got the mouse press forwarded by " + host.objectName)
652+
653+ Column {
654+ anchors.fill: parent
655+ spacing: units.gu(1)
656+
657+ Rectangle {
658+ id: blueRect
659+ objectName: "BlueRect"
660+ width: parent.width
661+ height: units.gu(20)
662+ color: "blue"
663+ Mouse.forwardTo: [main]
664+ Mouse.onPressed: console.log("This should not be printed")
665+ }
666+ Rectangle {
667+ objectName: "RedRect"
668+ width: parent.width
669+ height: units.gu(20)
670+ color: "red"
671+ MouseArea {
672+ anchors.fill: parent
673+ Mouse.forwardTo: [blueRect]
674+ Mouse.onPressed: console.log("Pressed in " + host.objectName)
675+ }
676+ }
677+ }
678+ }
679+ \endqml
680
681 An interesting feature that can be achieved using Mouse filter is the event
682 "transparency" towards the MouseArea lying behind the items which handle mouse
683@@ -226,6 +282,7 @@
684 }
685 \endqml
686
687+
688 Mouse filter provides ability to control the order of the event dispatching.
689 The filter can receive the events prior the owner or after the owner. This
690 can be controlled through the \l priority property. In the following example
691@@ -281,6 +338,9 @@
692 UCMouse::UCMouse(QObject *parent)
693 : QObject(parent)
694 , m_owner(qobject_cast<QQuickItem*>(parent))
695+ , m_lastButton(Qt::NoButton)
696+ , m_lastButtons(Qt::NoButton)
697+ , m_lastModifiers(Qt::NoModifier)
698 , m_pressedButtons(Qt::NoButton)
699 , m_priority(BeforeItem)
700 , m_moveThreshold(0.0)
701@@ -328,6 +388,9 @@
702 } else {
703 return hoverEvents(target, static_cast<QHoverEvent*>(event));
704 }
705+ } else if (type == ForwardedEvent::baseType()) {
706+ // this is a forwarded event, handle it as such
707+ return forwardedEvents(static_cast<ForwardedEvent*>(event));
708 }
709
710 return QObject::eventFilter(target, event);
711@@ -341,7 +404,8 @@
712 QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
713 false, m_longPress);
714 mev.setAccepted(false);
715- Q_EMIT pressAndHold(&mev);
716+ Q_EMIT pressAndHold(&mev, m_owner);
717+ mev.setAccepted(forwardEvent(ForwardedEvent::MouseLongPress, 0, &mev));
718 // if the event wasn't handled, allow click
719 if (!mev.isAccepted()) {
720 m_longPress = false;
721@@ -376,7 +440,6 @@
722 default:
723 break;
724 }
725- forwardEvent(event);
726 return result || event->isAccepted();
727 }
728
729@@ -400,27 +463,67 @@
730 default:
731 break;
732 }
733- forwardEvent(event);
734 return result || event->isAccepted();
735 }
736
737+// emit signals and forward the events further
738+bool UCMouse::forwardedEvents(ForwardedEvent *event)
739+{
740+ // the quick event is always specified!
741+ switch (event->subType()) {
742+ case ForwardedEvent::MousePress: {
743+ Q_EMIT pressed(event->quickEvent(), event->sender());
744+ } break;
745+ case ForwardedEvent::MouseRelease: {
746+ Q_EMIT released(event->quickEvent(), event->sender());
747+ } break;
748+ case ForwardedEvent::MouseMove: {
749+ Q_EMIT positionChanged(event->quickEvent(), event->sender());
750+ } break;
751+ case ForwardedEvent::MouseDblClick: {
752+ Q_EMIT doubleClicked(event->quickEvent(), event->sender());
753+ } break;
754+ case ForwardedEvent::HoverEnter: {
755+ Q_EMIT entered(event->quickEvent(), event->sender());
756+ } break;
757+ case ForwardedEvent::HoverExit: {
758+ Q_EMIT exited(event->quickEvent(), event->sender());
759+ } break;
760+ // composed events
761+ case ForwardedEvent::MouseClick: {
762+ Q_EMIT clicked(event->quickEvent(), event->sender());
763+ } break;
764+ case ForwardedEvent::MouseLongPress: {
765+ Q_EMIT pressAndHold(event->quickEvent(), event->sender());
766+ } break;
767+ default: break;
768+ }
769+
770+ // forward event, but use the current owner as sender
771+ event->setAccepted(forwardEvent(event->subType(), event->originalEvent(), event->quickEvent()));
772+ return event->isAccepted();
773+}
774+
775 bool UCMouse::hasAttachedFilter(QQuickItem *item)
776 {
777 return (qmlAttachedPropertiesObject<UCMouse>(item, false) != 0);
778 }
779
780-void UCMouse::setHovered(bool hovered)
781+void UCMouse::setHovered(bool hovered, QEvent *hoverEvent)
782 {
783 if (m_hovered != hovered) {
784 m_hovered = hovered;
785 QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
786 false, false);
787 mev.setAccepted(false);
788+ ForwardedEvent::EventType type = (m_hovered) ? ForwardedEvent::HoverEnter : ForwardedEvent::HoverExit;
789 if (m_hovered) {
790- Q_EMIT entered(&mev);
791+ Q_EMIT entered(&mev, m_owner);
792 } else {
793- Q_EMIT exited(&mev);
794+ m_pressAndHoldTimer.stop();
795+ Q_EMIT exited(&mev, m_owner);
796 }
797+ forwardEvent(type, hoverEvent, &mev);
798 }
799 }
800
801@@ -432,13 +535,13 @@
802 m_pressedButtons |= m_lastButton;
803 m_longPress = m_doubleClicked = false;
804
805- setHovered(true);
806+ setHovered(true, 0);
807
808 QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
809 false, m_longPress);
810 mev.setAccepted(false);
811- Q_EMIT pressed(&mev);
812- event->setAccepted(mev.isAccepted());
813+ Q_EMIT pressed(&mev, m_owner);
814+ event->setAccepted(forwardEvent(ForwardedEvent::MousePress, event, &mev));
815
816 // start long press timer
817 m_pressAndHoldTimer.start(DefaultPressAndHoldDelay, this);
818@@ -460,14 +563,14 @@
819 m_pressAndHoldTimer.stop();
820 }
821
822- setHovered(true);
823+ setHovered(true, 0);
824 m_moved = true;
825 m_doubleClicked = false;
826 QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
827 false, m_longPress);
828 mev.setAccepted(false);
829- Q_EMIT positionChanged(&mev);
830- event->setAccepted(mev.isAccepted());
831+ Q_EMIT positionChanged(&mev, m_owner);
832+ event->setAccepted(forwardEvent(ForwardedEvent::MouseMove, event, &mev));
833 return mev.isAccepted();
834 }
835
836@@ -487,18 +590,20 @@
837 QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
838 isClicked, m_longPress);
839 mev.setAccepted(false);
840- Q_EMIT released(&mev);
841- event->setAccepted(mev.isAccepted());
842+ Q_EMIT released(&mev, m_owner);
843+ event->setAccepted(forwardEvent(ForwardedEvent::MouseRelease, event, &mev));
844
845 // remove button from press
846 m_pressedButtons &= ~m_lastButton;
847 if (isClicked) {
848 // emit clicked
849- Q_EMIT clicked(&mev);
850+ mev.setAccepted(false);
851+ Q_EMIT clicked(&mev, m_owner);
852+ forwardEvent(ForwardedEvent::MouseClick, 0, &mev);
853 }
854
855 if (!m_pressedButtons && !m_owner->acceptHoverEvents()) {
856- setHovered(false);
857+ setHovered(false, 0);
858 }
859 return mev.isAccepted();
860 }
861@@ -511,18 +616,17 @@
862 {
863 if (m_pressedButtons) {
864 saveEvent(event);
865+
866+ QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
867+ true, m_longPress);
868+ mev.setAccepted(false);
869 // if double click connected, suppress release() and click() signals
870 if (isDoubleClickConnected()) {
871- QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
872- true, m_longPress);
873- mev.setAccepted(false);
874- Q_EMIT doubleClicked(&mev);
875- event->setAccepted(mev.isAccepted());
876+ Q_EMIT doubleClicked(&mev, m_owner);
877 m_doubleClicked = true;
878- } else {
879- // set accepted to false still
880- event->setAccepted(false);
881 }
882+ // forward event even if it wasn't handled for the owner
883+ event->setAccepted(forwardEvent(ForwardedEvent::MouseDblClick, event, &mev));
884 return event->isAccepted();
885 }
886
887@@ -534,7 +638,9 @@
888 {
889 m_lastPos = event->posF();
890 m_lastModifiers = event->modifiers();
891- setHovered(true);
892+ m_lastButton = Qt::NoButton;
893+ m_lastButtons = Qt::NoButton;
894+ setHovered(true, event);
895 return false;
896 }
897
898@@ -542,11 +648,11 @@
899 {
900 m_lastPos = event->posF();
901 m_lastModifiers = event->modifiers();
902- QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), m_lastButton, m_lastButtons, m_lastModifiers,
903+ QQuickMouseEvent mev(m_lastPos.x(), m_lastPos.y(), Qt::NoButton, Qt::NoButton, m_lastModifiers,
904 false, m_longPress);
905 mev.setAccepted(false);
906- Q_EMIT positionChanged(&mev);
907- forwardEvent(event);
908+ Q_EMIT positionChanged(&mev, m_owner);
909+ event->setAccepted(forwardEvent(ForwardedEvent::MouseMove, event, &mev));
910 return false;
911 }
912
913@@ -554,7 +660,9 @@
914 {
915 m_lastPos = event->posF();
916 m_lastModifiers = event->modifiers();
917- setHovered(false);
918+ m_lastButton = Qt::NoButton;
919+ m_lastButtons = Qt::NoButton;
920+ setHovered(false, event);
921 return false;
922 }
923
924@@ -562,8 +670,10 @@
925 {
926 m_lastPos = event->localPos();
927 m_lastScenePos = event->windowPos();
928- m_lastButton = event->button();
929- m_lastButtons = event->buttons();
930+ if (event->type() != QEvent::MouseMove) {
931+ m_lastButton = event->button();
932+ m_lastButtons = event->buttons();
933+ }
934 m_lastModifiers = event->modifiers();
935 if ((event->type() == QEvent::MouseButtonPress) && (m_moveThreshold > 0.0)) {
936 m_toleranceArea.setX(m_lastPos.x() - m_moveThreshold);
937@@ -574,7 +684,7 @@
938 }
939 bool UCMouse::isDoubleClickConnected()
940 {
941- IS_SIGNAL_CONNECTED(this, UCMouse, doubleClicked, (QQuickMouseEvent*));
942+ IS_SIGNAL_CONNECTED(this, UCMouse, doubleClicked, (QQuickMouseEvent*,QQuickItem*));
943 }
944
945 bool UCMouse::isMouseEvent(QEvent::Type type)
946@@ -588,57 +698,73 @@
947 return (type == QEvent::HoverEnter) || (type == QEvent::HoverMove) || (type == QEvent::HoverLeave);
948 }
949
950-// forwards the events to the listed items; only mouse and hover events qualify
951-void UCMouse::forwardEvent(QEvent *event)
952+
953+/*
954+ * Forwards the events to the listed items. The event coordinates are mapped to the destination's coordinates
955+ * and sent to the destination in case the destination has no filter attached. Otherwise the quick event
956+ * coordinates will be mapped and sent as ForwardedEvents.
957+ */
958+bool UCMouse::forwardEvent(ForwardedEvent::EventType type, QEvent *event, QQuickMouseEvent *quickEvent)
959 {
960- /*
961- * alter acceptedButtons and hoverEnabled for the time the event is delivered
962- * exclude MouseArea and InverseMouseArea!!
963- */
964+ // first set the accepted flag to the original event
965+ if (event && quickEvent) {
966+ event->setAccepted(quickEvent->isAccepted());
967+ }
968+ bool accepted = event ? event->isAccepted() : (quickEvent ? quickEvent->isAccepted() : false);
969+
970 Q_FOREACH(QQuickItem *item, m_forwardList) {
971
972- if (event->isAccepted()) {
973+ if (accepted) {
974 // the event got accepted, therefore should not be forwarded
975- return;
976+ return accepted;
977 }
978 // skip InverseMouseArea otherwise those will get the event twice
979 if (qobject_cast<InverseMouseAreaType*>(item)) {
980 continue;
981 }
982- /*
983- * temporarily enable mouse buttons and hover for items which have Mouse or InverseMouse
984- * filter attached, but exclude targets which is MouseArea or InverseMouseArea
985- */
986- Qt::MouseButtons acceptedButtons = item->acceptedMouseButtons();
987- bool hoverEnabled = item->acceptHoverEvents();
988- if (!qobject_cast<QQuickMouseArea*>(item) && hasAttachedFilter(item)) {
989- // set accepted buttons and hover temporarily
990- item->setAcceptedMouseButtons(m_owner->acceptedMouseButtons());
991- item->setAcceptHoverEvents(m_owner->acceptHoverEvents());
992- }
993-
994- // forward events
995- bool accepted = false;
996- if (isMouseEvent(event->type())) {
997- QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
998- QPointF itemPos = item->mapFromScene(m_owner->mapToScene(mouse->pos()));
999- QMouseEvent me = QMouseEvent(event->type(), itemPos, mouse->button(), mouse->buttons(), mouse->modifiers());
1000- QGuiApplication::sendEvent(item, &me);
1001- accepted = me.isAccepted();
1002- } else {
1003- QHoverEvent *hover = static_cast<QHoverEvent*>(event);
1004- QPointF itemPos = item->mapFromScene(m_owner->mapToScene(hover->pos()));
1005- QPointF itemOldPos = item->mapFromScene(m_owner->mapToScene(hover->oldPos()));
1006- QHoverEvent he = QHoverEvent(event->type(), itemPos, itemOldPos, hover->modifiers());
1007- QGuiApplication::sendEvent(item, &he);
1008- accepted = he.isAccepted();
1009- }
1010- event->setAccepted(accepted);
1011-
1012- // restore acceptedButtons and hover
1013- item->setAcceptedMouseButtons(acceptedButtons);
1014- item->setAcceptHoverEvents(hoverEnabled);
1015+
1016+ // map the normal event coordinates to item
1017+ QEvent *mappedEvent = 0;
1018+ if (event) {
1019+ if (isMouseEvent(event->type())) {
1020+ QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
1021+ QPointF itemPos = item->mapFromScene(m_owner->mapToScene(mouse->pos()));
1022+ mappedEvent = new QMouseEvent(event->type(), itemPos, mouse->button(), mouse->buttons(), mouse->modifiers());
1023+ } else if (isHoverEvent(event->type())){
1024+ QHoverEvent *hover = static_cast<QHoverEvent*>(event);
1025+ QPointF itemPos = item->mapFromScene(m_owner->mapToScene(hover->pos()));
1026+ QPointF itemOldPos = item->mapFromScene(m_owner->mapToScene(hover->oldPos()));
1027+ mappedEvent = new QHoverEvent(event->type(), itemPos, itemOldPos, hover->modifiers());
1028+ }
1029+ }
1030+
1031+ // if the item has no filter attached, deliver the mapped event to it as it is
1032+ UCMouse *filter = qobject_cast<UCMouse*>(qmlAttachedPropertiesObject<UCMouse>(item, false));
1033+ if (!filter && mappedEvent) {
1034+ QGuiApplication::sendEvent(item, mappedEvent);
1035+ accepted = mappedEvent->isAccepted();
1036+ } else if (quickEvent) {
1037+ // map the quick event coordinates as well
1038+ QPoint itemPos(item->mapFromScene(m_owner->mapToScene(QPointF(quickEvent->x(), quickEvent->y()))).toPoint());
1039+ QQuickMouseEvent mev(itemPos.x(), itemPos.y(), (Qt::MouseButton)quickEvent->button(), (Qt::MouseButtons)quickEvent->buttons(),
1040+ (Qt::KeyboardModifiers)quickEvent->modifiers(), quickEvent->isClick(), quickEvent->wasHeld());
1041+ mev.setAccepted(false);
1042+ ForwardedEvent forwardedEvent(type, m_owner, mappedEvent, &mev);
1043+ QGuiApplication::sendEvent(item, &forwardedEvent);
1044+ accepted = mev.isAccepted();
1045+ }
1046+
1047+ // cleanup and transfer accepted flag
1048+ delete mappedEvent;
1049+ if (event) {
1050+ event->setAccepted(accepted);
1051+ }
1052+ if (quickEvent) {
1053+ quickEvent->setAccepted(accepted);
1054+ }
1055 }
1056+
1057+ return accepted;
1058 }
1059
1060
1061@@ -697,6 +823,9 @@
1062 composed events will come until the mouse is moved inside the owner's area.
1063
1064 The default value is 0.
1065+
1066+ \note The value has no effect for the forwarded events. The threshold is only
1067+ valid when the host handles mouse events.
1068 */
1069 int UCMouse::clickAndHoldThreshold() const
1070 {
1071@@ -767,29 +896,34 @@
1072 }
1073
1074 /*!
1075- \qmlsignal Mouse::onPressed(MouseEvent event)
1076- The signal reports the mouse press.
1077+ \qmlsignal Mouse::onPressed(MouseEvent event, Item host)
1078+ The signal reports the mouse press. The \a host specifies the item that triggered
1079+ the event.
1080 */
1081
1082 /*!
1083 \qmlsignal Mouse::onReleased(MouseEvent event)
1084- The signal reports the mouse release.
1085+ The signal reports the mouse release. The \a host specifies the item that triggered
1086+ the event.
1087 */
1088
1089 /*!
1090 \qmlsignal Mouse::onClicked(MouseEvent event)
1091 The signal reports the mouse click. The signal is not emitted if the onPressAndHold
1092- got triggered or if onDoubleClicked is handled (a slot is connected to it).
1093+ got triggered or if onDoubleClicked is handled (a slot is connected to it). The \a
1094+ host specifies the item that triggered the event.
1095 */
1096
1097 /*!
1098 \qmlsignal Mouse::onPressAndHold(MouseEvent event)
1099- The signal reports the mouse press and hold.
1100+ The signal reports the mouse press and hold. The \a host specifies the item that triggered
1101+ the event.
1102 */
1103
1104 /*!
1105 \qmlsignal Mouse::onDoubleClicked(MouseEvent event)
1106- The signal reports mouse double click.
1107+ The signal reports mouse double click. The \a host specifies the item that triggered
1108+ the event.
1109 */
1110
1111 /*!
1112@@ -797,20 +931,22 @@
1113 The signal reports the mouse pointer position change. If the hover events are
1114 enabled for the owner, the signal will come continuously. Otherwise the position
1115 chanes are reported when one of the accepted mouse buttons are being kept pressed.
1116+ The \a host specifies the item that triggered the event.
1117 */
1118
1119 /*!
1120 \qmlsignal Mouse::onEntered(MouseEvent event)
1121 The signal reports that the mouse has entered into the area. The signal is
1122 emitted when the hover events are enabled and the mouse enters the area or
1123- when one of the accepted mouse button is pressed.
1124+ when one of the accepted mouse button is pressed. The \a host specifies the
1125+ item that triggered the event.
1126 */
1127
1128 /*!
1129 \qmlsignal Mouse::onExited(MouseEvent event)
1130 The signal reports that the mouse has left the area. The signal is emitted when
1131 the hover events are enabled for the owner or if not, when one of the accepted
1132- button is released.
1133+ button is released. The \a host specifies the item that triggered the event.
1134 */
1135
1136 /******************************************************************************
1137
1138=== modified file 'modules/Ubuntu/Test/UbuntuTestCase.qml'
1139--- modules/Ubuntu/Test/UbuntuTestCase.qml 2014-02-25 12:36:27 +0000
1140+++ modules/Ubuntu/Test/UbuntuTestCase.qml 2014-04-17 01:29:24 +0000
1141@@ -87,7 +87,75 @@
1142 }
1143 }
1144
1145- /*!
1146+ /*!
1147+ \qmlmethod UbuntuTestCase::flick(item, x, y, dx, dy, pressTimeout = -1, steps = -1, button = Qt.LeftButton, modifiers = Qt.NoModifiers, delay = -1)
1148+
1149+ The function produces a flick event when executed on Flickables. When used
1150+ on other components it provides the same functionality as \l mouseDrag()
1151+ function. The optional \a pressTimeout parameter can be used to introduce
1152+ a small delay between the mouse press and the first mouse move. Setting a
1153+ negative or zero value will disable the timeout.
1154+
1155+ The default flick velocity is built up using 5 move points. This can be altered
1156+ by setting a positive value to \a steps parameter. The bigger the number the
1157+ longer the flick will be. When a negative or zero value is given, the default
1158+ of 5 move points will be used.
1159+
1160+ \note The function can be used to select a text in a TextField or TextArea by
1161+ specifying at least 400 millisecods to \a pressTimeout.
1162+ */
1163+ function flick(item, x, y, dx, dy, pressTimeout, steps, button, modifiers, delay) {
1164+ if (item === undefined || item.x === undefined || item.y === undefined)
1165+ return
1166+ if (button === undefined)
1167+ button = Qt.LeftButton
1168+ if (modifiers === undefined)
1169+ modifiers = Qt.NoModifier
1170+ if (steps === undefined || steps <= 0)
1171+ steps = 4;
1172+ // make sure we have at least two move steps so the flick will be sensed
1173+ steps += 1;
1174+ if (delay === undefined)
1175+ delay = -1;
1176+
1177+ var ddx = dx / steps;
1178+ var ddy = dy / steps;
1179+
1180+ mousePress(item, x, y, button, modifiers, delay);
1181+ if (pressTimeout !== undefined && pressTimeout > 0) {
1182+ wait(pressTimeout);
1183+ }
1184+ for (var i = 1; i <= steps; i++) {
1185+ // mouse moves are all processed immediately, without delay in between events
1186+ mouseMove(item, x + i * ddx, y + i * ddy, -1, button);
1187+ }
1188+ mouseRelease(item, x + dx, y + dy, button, modifiers, delay);
1189+ // empty event buffer
1190+ wait(200);
1191+ }
1192+
1193+ /*!
1194+ \qmlmethod UbuntuTestCase::mouseLongPress(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifiers, delay = -1)
1195+
1196+ Simulates a long press on a mouse \a button with an optional \a modifier
1197+ on an \a item. The position is defined by \a x and \a y. If \a delay is
1198+ specified, the test will wait the specified amount of milliseconds before
1199+ the press.
1200+
1201+ The position given by \a x and \a y is transformed from the co-ordinate
1202+ system of \a item into window co-ordinates and then delivered.
1203+ If \a item is obscured by another item, or a child of \a item occupies
1204+ that position, then the event will be delivered to the other item instead.
1205+
1206+ \sa mouseRelease(), mouseClick(), mouseDoubleClick(), mouseMove(), mouseDrag(), mouseWheel()
1207+ */
1208+ function mouseLongPress(item, x, y, button, modifiers, delay) {
1209+ mousePress(item, x, y, button, modifiers, delay);
1210+ // the delay is taken from QQuickMouseArea
1211+ wait(800);
1212+ }
1213+
1214+ /*!
1215 Keeps executing a given parameter-less function until it returns the given
1216 expected result or the timemout is reached (in which case a test failure
1217 is generated)
1218
1219=== modified file 'modules/Ubuntu/Test/deployment.pri'
1220--- modules/Ubuntu/Test/deployment.pri 2014-01-17 12:30:05 +0000
1221+++ modules/Ubuntu/Test/deployment.pri 2014-04-17 01:29:24 +0000
1222@@ -7,9 +7,14 @@
1223 # make found deployables visible in Qt Creator
1224 OTHER_FILES += $$QMLDIR_FILE
1225
1226+QML_FILES = $$system(ls *.qml)
1227+JS_FILES = $$system(ls *.js)
1228+
1229 # define deployment for found deployables
1230 qmldir_file.path = $$installPath
1231 qmldir_file.files = $$QMLDIR_FILE
1232+qml_files.path = $$installPath
1233+qml_files.files = $$QML_FILES
1234 js_files.path = $$installPath
1235 js_files.files = $$JS_FILES
1236
1237@@ -20,4 +25,4 @@
1238 # https://bugreports.qt-project.org/browse/QTBUG-36243
1239 plugins_qmltypes.extra = $$[QT_INSTALL_BINS]/qmlplugindump -notrelocatable Ubuntu.Test 0.1 ../../ 2>/dev/null > $(INSTALL_ROOT)/$$installPath/plugins.qmltypes
1240
1241-INSTALLS += qmldir_file plugins_qmltypes
1242+INSTALLS += qmldir_file plugins_qmltypes qml_files js_files
1243
1244=== modified file 'modules/Ubuntu/Test/plugin/uctestcase.cpp'
1245--- modules/Ubuntu/Test/plugin/uctestcase.cpp 2014-03-19 12:48:33 +0000
1246+++ modules/Ubuntu/Test/plugin/uctestcase.cpp 2014-04-17 01:29:24 +0000
1247@@ -26,6 +26,8 @@
1248 #include <QtTest/QtTest>
1249 #include <QtQuick/QQuickItem>
1250
1251+Q_DECLARE_METATYPE(QList<QQmlError>)
1252+
1253 /*!
1254 * \ingroup ubuntu
1255 * \brief UbuntuTestCase is the C++ pendant to the QML UbuntuTestCase.
1256@@ -37,6 +39,7 @@
1257 QString modulePath(QDir(modules).absolutePath());
1258 engine()->addImportPath(modulePath);
1259
1260+ qRegisterMetaType<QList <QQmlError> >();
1261 m_spy = new QSignalSpy(engine(), SIGNAL(warnings(QList<QQmlError>)));
1262 m_spy->setParent(this);
1263
1264@@ -48,3 +51,12 @@
1265 QTest::qWaitForWindowExposed(this);
1266 }
1267
1268+/*!
1269+ * The number of all warnings from the point of loading the first line of QML code.
1270+ */
1271+int
1272+UbuntuTestCase::warnings() const
1273+{
1274+ return m_spy->count();
1275+}
1276+
1277
1278=== modified file 'modules/Ubuntu/Test/plugin/uctestcase.h'
1279--- modules/Ubuntu/Test/plugin/uctestcase.h 2014-03-19 12:48:33 +0000
1280+++ modules/Ubuntu/Test/plugin/uctestcase.h 2014-04-17 01:29:24 +0000
1281@@ -29,6 +29,7 @@
1282
1283 public:
1284 UbuntuTestCase(const QString& file, QWindow* parent = 0);
1285+ int warnings() const;
1286 // getter
1287 template<class T>
1288 inline T findItem(const QString& objectName) const {
1289@@ -39,7 +40,6 @@
1290 qFatal("Item '%s' found with unexpected type", qPrintable(objectName));
1291 qFatal("No item '%s' found", qPrintable(objectName));
1292 }
1293-
1294 private:
1295 QSignalSpy* m_spy;
1296 };
1297
1298=== removed file 'tests/autopilot/ubuntuuitoolkit/__init__.py'
1299--- tests/autopilot/ubuntuuitoolkit/__init__.py 2013-07-10 16:07:33 +0000
1300+++ tests/autopilot/ubuntuuitoolkit/__init__.py 1970-01-01 00:00:00 +0000
1301@@ -1,17 +0,0 @@
1302-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1303-#
1304-# Copyright (C) 2012, 2013 Canonical Ltd.
1305-#
1306-# This program is free software; you can redistribute it and/or modify
1307-# it under the terms of the GNU Lesser General Public License as published by
1308-# the Free Software Foundation; version 3.
1309-#
1310-# This program is distributed in the hope that it will be useful,
1311-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1312-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1313-# GNU Lesser General Public License for more details.
1314-#
1315-# You should have received a copy of the GNU Lesser General Public License
1316-# along with this program. If not, see <http://www.gnu.org/licenses/>.
1317-
1318-"""Ubuntu UI Toolkit autopilot tests and emulators - top level package."""
1319
1320=== added directory 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects'
1321=== renamed file 'tests/autopilot/ubuntuuitoolkit/emulators.py' => 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py'
1322--- tests/autopilot/ubuntuuitoolkit/emulators.py 2014-04-08 14:46:25 +0000
1323+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py 2014-04-17 01:29:24 +0000
1324@@ -14,784 +14,69 @@
1325 # You should have received a copy of the GNU Lesser General Public License
1326 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1327
1328-import logging
1329-from distutils import version
1330-
1331-import autopilot
1332-from autopilot import (
1333- input,
1334- logging as autopilot_logging,
1335- platform
1336-)
1337-from autopilot.introspection import dbus
1338-
1339-
1340-_NO_TABS_ERROR = 'The MainView has no Tabs.'
1341-
1342-logger = logging.getLogger(__name__)
1343-
1344-
1345-class ToolkitEmulatorException(Exception):
1346- """Exception raised when there is an error with the emulator."""
1347-
1348-
1349-def get_pointing_device():
1350- """Return the pointing device depending on the platform.
1351-
1352- If the platform is `Desktop`, the pointing device will be a `Mouse`.
1353- If not, the pointing device will be `Touch`.
1354-
1355- """
1356- if platform.model() == 'Desktop':
1357- input_device_class = input.Mouse
1358- else:
1359- input_device_class = input.Touch
1360- return input.Pointer(device=input_device_class.create())
1361-
1362-
1363-def get_keyboard():
1364- """Return the keyboard device."""
1365- # TODO return the OSK if we are on the phone. --elopio - 2014-01-13
1366- return input.Keyboard.create()
1367-
1368-
1369-def check_autopilot_version():
1370- """Check that the Autopilot installed version matches the one required.
1371-
1372- :raise ToolkitEmulatorException: If the installed Autopilot version does't
1373- match the required by the emulators.
1374-
1375- """
1376- installed_version = version.LooseVersion(autopilot.version)
1377- if installed_version < version.LooseVersion('1.4'):
1378- raise ToolkitEmulatorException(
1379- 'The emulators need Autopilot 1.4 or higher.')
1380-
1381-
1382-# Containers helpers.
1383-
1384-def _get_visible_container_top(containers):
1385- containers_top = [container.globalRect.y for container in containers]
1386- return max(containers_top)
1387-
1388-
1389-def _get_visible_container_bottom(containers):
1390- containers_bottom = [
1391- container.globalRect.y + container.globalRect.height
1392- for container in containers if container.globalRect.height > 0]
1393- return min(containers_bottom)
1394-
1395-
1396-class UbuntuUIToolkitEmulatorBase(dbus.CustomEmulatorBase):
1397- """A base class for all the Ubuntu UI Toolkit emulators."""
1398-
1399- def __init__(self, *args):
1400- check_autopilot_version()
1401- super(UbuntuUIToolkitEmulatorBase, self).__init__(*args)
1402- self.pointing_device = get_pointing_device()
1403-
1404-
1405-class MainView(UbuntuUIToolkitEmulatorBase):
1406- """MainView Autopilot emulator."""
1407-
1408- def get_header(self):
1409- """Return the Header emulator of the MainView."""
1410- try:
1411- return self.select_single('Header', objectName='MainView_Header')
1412- except dbus.StateNotFoundError:
1413- raise ToolkitEmulatorException('The main view has no header.')
1414-
1415- def get_toolbar(self):
1416- """Return the Toolbar emulator of the MainView."""
1417- return self.select_single(Toolbar)
1418-
1419- @autopilot_logging.log_action(logger.info)
1420- def open_toolbar(self):
1421- """Open the toolbar if it's not already opened.
1422-
1423- :return: The toolbar.
1424-
1425- """
1426- return self.get_toolbar().open()
1427-
1428- @autopilot_logging.log_action(logger.info)
1429- def close_toolbar(self):
1430- """Close the toolbar if it's opened."""
1431- self.get_toolbar().close()
1432-
1433- def get_tabs(self):
1434- """Return the Tabs emulator of the MainView.
1435-
1436- :raise ToolkitEmulatorException: If the main view has no tabs.
1437-
1438- """
1439- try:
1440- return self.select_single(Tabs)
1441- except dbus.StateNotFoundError:
1442- raise ToolkitEmulatorException(_NO_TABS_ERROR)
1443-
1444- @autopilot_logging.log_action(logger.info)
1445- def switch_to_next_tab(self):
1446- """Open the next tab.
1447-
1448- :return: The newly opened tab.
1449-
1450- """
1451- logger.debug('Switch to next tab.')
1452- self.get_header().switch_to_next_tab()
1453- current_tab = self.get_tabs().get_current_tab()
1454- current_tab.visible.wait_for(True)
1455- return current_tab
1456-
1457- @autopilot_logging.log_action(logger.info)
1458- def switch_to_tab_by_index(self, index):
1459- """Open a tab.
1460-
1461- :parameter index: The index of the tab to open.
1462- :return: The newly opened tab.
1463- :raise ToolkitEmulatorException: If the tab index is out of range.
1464-
1465- """
1466- logger.debug('Switch to tab with index {0}.'.format(index))
1467- tabs = self.get_tabs()
1468- number_of_tabs = tabs.get_number_of_tabs()
1469- if index >= number_of_tabs:
1470- raise ToolkitEmulatorException('Tab index out of range.')
1471- current_tab = tabs.get_current_tab()
1472- number_of_switches = 0
1473- while not tabs.selectedTabIndex == index:
1474- logger.debug(
1475- 'Current tab index: {0}.'.format(tabs.selectedTabIndex))
1476- if number_of_switches >= number_of_tabs - 1:
1477- # This prevents a loop. But if this error is ever raised, it's
1478- # likely there's a bug on the emulator or on the QML Tab.
1479- raise ToolkitEmulatorException(
1480- 'The tab with index {0} was not selected.'.format(index))
1481- current_tab = self.switch_to_next_tab()
1482- number_of_switches += 1
1483- return current_tab
1484-
1485- @autopilot_logging.log_action(logger.info)
1486- def switch_to_previous_tab(self):
1487- """Open the previous tab.
1488-
1489- :return: The newly opened tab.
1490-
1491- """
1492- tabs = self.get_tabs()
1493- if tabs.selectedTabIndex == 0:
1494- previous_tab_index = tabs.get_number_of_tabs() - 1
1495- else:
1496- previous_tab_index = tabs.selectedTabIndex - 1
1497- return self.switch_to_tab_by_index(previous_tab_index)
1498-
1499- @autopilot_logging.log_action(logger.info)
1500- def switch_to_tab(self, object_name):
1501- """Open a tab.
1502-
1503- :parameter object_name: The QML objectName property of the tab.
1504- :return: The newly opened tab.
1505- :raise ToolkitEmulatorException: If there is no tab with that object
1506- name.
1507-
1508- """
1509- tabs = self.get_tabs()
1510- for index, tab in enumerate(tabs.select_many('Tab')):
1511- if tab.objectName == object_name:
1512- return self.switch_to_tab_by_index(tab.index)
1513- raise ToolkitEmulatorException(
1514- 'Tab with objectName "{0}" not found.'.format(object_name))
1515-
1516- def get_action_selection_popover(self, object_name):
1517- """Return an ActionSelectionPopover emulator.
1518-
1519- :parameter object_name: The QML objectName property of the popover.
1520-
1521- """
1522- return self.select_single(
1523- ActionSelectionPopover, objectName=object_name)
1524-
1525- @autopilot_logging.log_action(logger.info)
1526- def go_back(self):
1527- """Go to the previous page."""
1528- toolbar = self.open_toolbar()
1529- toolbar.click_back_button()
1530-
1531-
1532-class Header(UbuntuUIToolkitEmulatorBase):
1533- """Header Autopilot emulator."""
1534-
1535- def __init__(self, *args):
1536- super(Header, self).__init__(*args)
1537- self.pointing_device = get_pointing_device()
1538-
1539- def _get_animating(self):
1540- tab_bar_style = self.select_single('TabBarStyle')
1541- return tab_bar_style.animating
1542-
1543- @autopilot_logging.log_action(logger.info)
1544- def switch_to_next_tab(self):
1545- """Open the next tab.
1546-
1547- :raise ToolkitEmulatorException: If the main view has no tabs.
1548-
1549- """
1550- try:
1551- tab_bar = self.select_single(TabBar)
1552- except dbus.StateNotFoundError:
1553- raise ToolkitEmulatorException(_NO_TABS_ERROR)
1554- tab_bar.switch_to_next_tab()
1555- self._get_animating().wait_for(False)
1556-
1557-
1558-class Toolbar(UbuntuUIToolkitEmulatorBase):
1559- """Toolbar Autopilot emulator."""
1560-
1561- @autopilot_logging.log_action(logger.info)
1562- def open(self):
1563- """Open the toolbar if it's not already opened.
1564-
1565- :return: The toolbar.
1566-
1567- """
1568- self.animating.wait_for(False)
1569- if not self.opened:
1570- self._drag_to_open()
1571- self.opened.wait_for(True)
1572- self.animating.wait_for(False)
1573-
1574- return self
1575-
1576- def _drag_to_open(self):
1577- x, y, _, _ = self.globalRect
1578- line_x = x + self.width * 0.50
1579- start_y = y + self.height - 1
1580- stop_y = y
1581-
1582- self.pointing_device.drag(line_x, start_y, line_x, stop_y)
1583-
1584- @autopilot_logging.log_action(logger.info)
1585- def close(self):
1586- """Close the toolbar if it's opened."""
1587- self.animating.wait_for(False)
1588- if self.opened:
1589- self._drag_to_close()
1590- self.opened.wait_for(False)
1591- self.animating.wait_for(False)
1592-
1593- def _drag_to_close(self):
1594- x, y, _, _ = self.globalRect
1595- line_x = x + self.width * 0.50
1596- start_y = y
1597- stop_y = y + self.height - 1
1598-
1599- self.pointing_device.drag(line_x, start_y, line_x, stop_y)
1600-
1601- @autopilot_logging.log_action(logger.info)
1602- def click_button(self, object_name):
1603- """Click a button of the toolbar.
1604-
1605- The toolbar should be opened before clicking the button, or an
1606- exception will be raised. If the toolbar is closed for some reason
1607- (e.g., timer finishes) after moving the mouse cursor and before
1608- clicking the button, it is re-opened automatically by this function.
1609-
1610- :parameter object_name: The QML objectName property of the button.
1611- :raise ToolkitEmulatorException: If there is no button with that object
1612- name.
1613-
1614- """
1615- # ensure the toolbar is open
1616- if not self.opened:
1617- raise ToolkitEmulatorException(
1618- 'Toolbar must be opened before calling click_button().')
1619- try:
1620- button = self._get_button(object_name)
1621- except dbus.StateNotFoundError:
1622- raise ToolkitEmulatorException(
1623- 'Button with objectName "{0}" not found.'.format(object_name))
1624- self.pointing_device.move_to_object(button)
1625- # ensure the toolbar is still open (may have closed due to timeout)
1626- self.open()
1627- # click the button
1628- self.pointing_device.click_object(button)
1629-
1630- def _get_button(self, object_name):
1631- return self.select_single('ActionItem', objectName=object_name)
1632-
1633- @autopilot_logging.log_action(logger.info)
1634- def click_back_button(self):
1635- """Click the back button of the toolbar."""
1636- self.click_button('back_toolbar_button')
1637-
1638-
1639-class Tabs(UbuntuUIToolkitEmulatorBase):
1640- """Tabs Autopilot emulator."""
1641-
1642- def get_current_tab(self):
1643- """Return the currently selected tab."""
1644- return self._get_tab(self.selectedTabIndex)
1645-
1646- def _get_tab(self, index):
1647- tabs = self._get_tabs()
1648- for tab in tabs:
1649- if tab.index == index:
1650- return tab
1651- else:
1652- raise ToolkitEmulatorException(
1653- 'There is no tab with index {0}.'.format(index))
1654-
1655- def _get_tabs(self):
1656- return self.select_many('Tab')
1657-
1658- def get_number_of_tabs(self):
1659- """Return the number of tabs."""
1660- return len(self._get_tabs())
1661-
1662-
1663-class TabBar(UbuntuUIToolkitEmulatorBase):
1664- """TabBar Autopilot emulator."""
1665-
1666- @autopilot_logging.log_action(logger.info)
1667- def switch_to_next_tab(self):
1668- """Open the next tab."""
1669- self._activate_tab_bar()
1670- logger.debug('Click the next tab bar button.')
1671- self.pointing_device.click_object(self._get_next_tab_button())
1672-
1673- def _activate_tab_bar(self):
1674- # First move to the tab bar to avoid timing issues when we find it in
1675- # selection mode but it's deselected while we move to it.
1676- self.pointing_device.move_to_object(self)
1677- if self.selectionMode:
1678- logger.debug('Already in selection mode.')
1679- else:
1680- # Click the tab bar to switch to selection mode.
1681- logger.debug('Click the tab bar to enable selection mode.')
1682- self.pointing_device.click_object(self)
1683-
1684- def _get_next_tab_button(self):
1685- current_index = self._get_selected_button_index()
1686- next_index = (current_index + 1) % self._get_number_of_tab_buttons()
1687- return self._get_tab_button(next_index)
1688-
1689- def _get_selected_button_index(self):
1690- return self.select_single('QQuickPathView').selectedButtonIndex
1691-
1692- def _get_number_of_tab_buttons(self):
1693- return len(self._get_tab_buttons())
1694-
1695- def _get_tab_buttons(self):
1696- return self.select_many('AbstractButton')
1697-
1698- def _get_tab_button(self, index):
1699- buttons = self._get_tab_buttons()
1700- for button in buttons:
1701- if button.buttonIndex == index:
1702- return button
1703- raise ToolkitEmulatorException(
1704- 'There is no tab button with index {0}.'.format(index))
1705-
1706-
1707-class ActionSelectionPopover(UbuntuUIToolkitEmulatorBase):
1708- """ActionSelectionPopover Autopilot emulator."""
1709-
1710- def click_button_by_text(self, text):
1711- """Click a button on the popover.
1712-
1713- XXX We are receiving the text because there's no way to set the
1714- objectName on the action. This is reported at
1715- https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1205144
1716- --elopio - 2013-07-25
1717-
1718- :parameter text: The text of the button.
1719- :raise ToolkitEmulatorException: If the popover is not open.
1720-
1721- """
1722- if not self.visible:
1723- raise ToolkitEmulatorException('The popover is not open.')
1724- button = self._get_button(text)
1725- if button is None:
1726- raise ToolkitEmulatorException(
1727- 'Button with text "{0}" not found.'.format(text))
1728- self.pointing_device.click_object(button)
1729- if self.autoClose:
1730- try:
1731- self.visible.wait_for(False)
1732- except dbus.StateNotFoundError:
1733- # The popover was removed from the tree.
1734- pass
1735-
1736- def _get_button(self, text):
1737- buttons = self.select_many('Empty')
1738- for button in buttons:
1739- if button.text == text:
1740- return button
1741-
1742-
1743-class CheckBox(UbuntuUIToolkitEmulatorBase):
1744- """CheckBox Autopilot emulator."""
1745-
1746- @autopilot_logging.log_action(logger.info)
1747- def check(self, timeout=10):
1748- """Check a CheckBox, if its not already checked.
1749-
1750- :parameter timeout: number of seconds to wait for the CheckBox to be
1751- checked. Default is 10.
1752-
1753- """
1754- if not self.checked:
1755- self.change_state(timeout)
1756-
1757- @autopilot_logging.log_action(logger.info)
1758- def uncheck(self, timeout=10):
1759- """Uncheck a CheckBox, if its not already unchecked.
1760-
1761- :parameter timeout: number of seconds to wait for the CheckBox to be
1762- unchecked. Default is 10.
1763-
1764- """
1765- if self.checked:
1766- self.change_state(timeout)
1767-
1768- @autopilot_logging.log_action(logger.info)
1769- def change_state(self, timeout=10):
1770- """Change the state of a CheckBox.
1771-
1772- If it is checked, it will be unchecked. If it is unchecked, it will be
1773- checked.
1774-
1775- :parameter time_out: number of seconds to wait for the CheckBox state
1776- to change. Default is 10.
1777-
1778- """
1779- original_state = self.checked
1780- self.pointing_device.click_object(self)
1781- self.checked.wait_for(not original_state, timeout)
1782-
1783-
1784-class TextField(UbuntuUIToolkitEmulatorBase):
1785- """TextField Autopilot emulator."""
1786-
1787- def __init__(self, *args):
1788- super(TextField, self).__init__(*args)
1789- self.keyboard = get_keyboard()
1790-
1791- def write(self, text, clear=True):
1792- """Write into the text field.
1793-
1794- :parameter text: The text to write.
1795- :parameter clear: If True, the text field will be cleared before
1796- writing the text. If False, the text will be appended at the end
1797- of the text field. Default is True.
1798-
1799- """
1800- with self.keyboard.focused_type(self):
1801- self.focus.wait_for(True)
1802- if clear:
1803- self.clear()
1804- else:
1805- if not self.is_empty():
1806- self.keyboard.press_and_release('End')
1807- self.keyboard.type(text)
1808-
1809- def clear(self):
1810- """Clear the text field."""
1811- if not self.is_empty():
1812- if self.hasClearButton:
1813- self._click_clear_button()
1814- else:
1815- self._clear_with_keys()
1816- self.text.wait_for('')
1817-
1818- def is_empty(self):
1819- """Return True if the text field is empty. False otherwise."""
1820- return self.text == ''
1821-
1822- def _click_clear_button(self):
1823- clear_button = self.select_single(
1824- 'AbstractButton', objectName='clear_button')
1825- if not clear_button.visible:
1826- self.pointing_device.click_object(self)
1827- self.pointing_device.click_object(clear_button)
1828-
1829- def _clear_with_keys(self):
1830- if platform.model() == 'Desktop':
1831- self._select_all()
1832- else:
1833- # Touch tap currently doesn't have a press_duration parameter, so
1834- # we can't show the popover. Reported as bug http://pad.lv/1268782
1835- # --elopio - 2014-01-13
1836- self.keyboard.press_and_release('End')
1837- while not self.is_empty():
1838- # We delete with backspace because the on-screen keyboard has that
1839- # key.
1840- self.keyboard.press_and_release('BackSpace')
1841-
1842- def _select_all(self):
1843- self.pointing_device.click_object(self, press_duration=1)
1844- root = self.get_root_instance()
1845- main_view = root.select_single(MainView)
1846- popover = main_view.get_action_selection_popover('text_input_popover')
1847- popover.click_button_by_text('Select All')
1848-
1849-
1850-class Flickable(UbuntuUIToolkitEmulatorBase):
1851-
1852- @autopilot_logging.log_action(logger.info)
1853- def swipe_child_into_view(self, child):
1854- """Make the child visible.
1855-
1856- Currently it works only when the object needs to be swiped vertically.
1857- TODO implement horizontal swiping. --elopio - 2014-03-21
1858-
1859- """
1860- containers = self._get_containers()
1861- if not self._is_child_visible(child, containers):
1862- self._swipe_non_visible_child_into_view(child, containers)
1863- else:
1864- logger.debug('The element is already visible.')
1865-
1866- def _get_containers(self):
1867- """Return a list with the containers to take into account when swiping.
1868-
1869- The list includes this flickable and the top-most container.
1870- TODO add additional flickables that are between this and the top
1871- container. --elopio - 2014-03-22
1872-
1873- """
1874- containers = [self._get_top_container(), self]
1875- return containers
1876-
1877- def _get_top_container(self):
1878- """Return the top-most container with a globalRect."""
1879- root = self.get_root_instance()
1880- containers = [root]
1881- while len(containers) == 1:
1882- try:
1883- containers[0].globalRect
1884- return containers[0]
1885- except AttributeError:
1886- containers = containers[0].get_children()
1887-
1888- raise ToolkitEmulatorException("Couldn't find the top-most container.")
1889-
1890- def _is_child_visible(self, child, containers):
1891- """Check if the center of the child is visible.
1892-
1893- :return: True if the center of the child is visible, False otherwise.
1894-
1895- """
1896- object_center = child.globalRect.y + child.globalRect.height // 2
1897- visible_top = _get_visible_container_top(containers)
1898- visible_bottom = _get_visible_container_bottom(containers)
1899- return (object_center >= visible_top and
1900- object_center <= visible_bottom)
1901-
1902- @autopilot_logging.log_action(logger.info)
1903- def _swipe_non_visible_child_into_view(self, child, containers):
1904- while not self._is_child_visible(child, containers):
1905- # Check the direction of the swipe based on the position of the
1906- # child relative to the immediate flickable container.
1907- if child.globalRect.y < self.globalRect.y:
1908- self._swipe_to_show_more_above(containers)
1909- else:
1910- self._swipe_to_show_more_below(containers)
1911-
1912- @autopilot_logging.log_action(logger.info)
1913- def _swipe_to_show_more_above(self, containers):
1914- if self.atYBeginning:
1915- raise ToolkitEmulatorException(
1916- "Can't swipe more, we are already at the top of the "
1917- "container.")
1918- else:
1919- self._swipe_to_show_more('above', containers)
1920-
1921- @autopilot_logging.log_action(logger.info)
1922- def _swipe_to_show_more_below(self, containers):
1923- if self.atYEnd:
1924- raise ToolkitEmulatorException(
1925- "Can't swipe more, we are already at the bottom of the "
1926- "container.")
1927- else:
1928- self._swipe_to_show_more('below', containers)
1929-
1930- def _swipe_to_show_more(self, direction, containers):
1931- start_x = stop_x = self.globalRect.x + (self.globalRect.width // 2)
1932- # Start and stop just a little under the top and a little over the
1933- # bottom.
1934- top = _get_visible_container_top(containers) + 5
1935- bottom = _get_visible_container_bottom(containers) - 5
1936- if direction == 'below':
1937- start_y = bottom
1938- stop_y = top
1939- elif direction == 'above':
1940- start_y = top
1941- stop_y = bottom
1942- else:
1943- raise ToolkitEmulatorException(
1944- 'Invalid direction {}.'.format(direction))
1945- self._slow_drag(start_x, stop_x, start_y, stop_y)
1946- self.dragging.wait_for(False)
1947- self.moving.wait_for(False)
1948-
1949- def _slow_drag(self, start_x, stop_x, start_y, stop_y):
1950- # If we drag too fast, we end up scrolling more than what we
1951- # should, sometimes missing the element we are looking for.
1952- self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate=5)
1953-
1954- @autopilot_logging.log_action(logger.info)
1955- def _scroll_to_top(self):
1956- if not self.atYBeginning:
1957- containers = self._get_containers()
1958- while not self.atYBeginning:
1959- self._swipe_to_show_more_above(containers)
1960-
1961-
1962-class QQuickListView(Flickable):
1963-
1964- @autopilot_logging.log_action(logger.info)
1965- def click_element(self, object_name):
1966- """Click an element from the list.
1967-
1968- It swipes the element into view if it's center is not visible.
1969-
1970- :parameter objectName: The objectName property of the element to click.
1971-
1972- """
1973- try:
1974- element = self.select_single(objectName=object_name)
1975- except dbus.StateNotFoundError:
1976- # The element might be on a part of the list that hasn't been
1977- # created yet. We have to search for it scrolling the entire list.
1978- element = self._find_element(object_name)
1979- self.swipe_child_into_view(element)
1980- self.pointing_device.click_object(element)
1981-
1982- @autopilot_logging.log_action(logger.info)
1983- def _find_element(self, object_name):
1984- self._scroll_to_top()
1985- while not self.atYEnd:
1986- containers = self._get_containers()
1987- self._swipe_to_show_more_below(containers)
1988- try:
1989- return self.select_single(objectName=object_name)
1990- except dbus.StateNotFoundError:
1991- pass
1992- raise ToolkitEmulatorException(
1993- 'List element with objectName "{}" not found.'.format(object_name))
1994-
1995- def _is_element_clickable(self, object_name):
1996- child = self.select_single(objectName=object_name)
1997- containers = self._get_containers()
1998- return self._is_child_visible(child, containers)
1999-
2000-
2001-class Empty(UbuntuUIToolkitEmulatorBase):
2002- """Base class to emulate swipe to delete."""
2003-
2004- def exists(self):
2005- try:
2006- return self.implicitHeight > 0
2007- except dbus.StateNotFoundError:
2008- return False
2009-
2010- def _get_confirm_button(self):
2011- return self.select_single(
2012- 'QQuickItem', objectName='confirmRemovalDialog')
2013-
2014- @autopilot_logging.log_action(logger.info)
2015- def swipe_to_delete(self, direction='right'):
2016- """Swipe the item in a specific direction."""
2017- if self.removable:
2018- self._drag_pointing_device_to_delete(direction)
2019- if self.confirmRemoval:
2020- self.waitingConfirmationForRemoval.wait_for(True)
2021- else:
2022- self._wait_until_deleted()
2023- else:
2024- raise ToolkitEmulatorException(
2025- 'The item "{0}" is not removable'.format(self.objectName))
2026-
2027- def _drag_pointing_device_to_delete(self, direction):
2028- x, y, w, h = self.globalRect
2029- tx = x + (w // 8)
2030- ty = y + (h // 2)
2031-
2032- if direction == 'right':
2033- self.pointing_device.drag(tx, ty, w, ty)
2034- elif direction == 'left':
2035- self.pointing_device.drag(w - (w*0.1), ty, x, ty)
2036- else:
2037- raise ToolkitEmulatorException(
2038- 'Invalid direction "{0}" used on swipe to delete function'
2039- .format(direction))
2040-
2041- def _wait_until_deleted(self):
2042- try:
2043- # The item was hidden.
2044- self.implicitHeight.wait_for(0)
2045- except dbus.StateNotFoundError:
2046- # The item was destroyed.
2047- pass
2048-
2049- @autopilot_logging.log_action(logger.info)
2050- def confirm_removal(self):
2051- """Comfirm item removal if this was already swiped."""
2052- if self.waitingConfirmationForRemoval:
2053- deleteButton = self._get_confirm_button()
2054- self.pointing_device.click_object(deleteButton)
2055- self._wait_until_deleted()
2056- else:
2057- raise ToolkitEmulatorException(
2058- 'The item "{0}" is not waiting for removal confirmation'.
2059- format(self.objectName))
2060-
2061-
2062-class Base(Empty):
2063- pass
2064-
2065-
2066-class Standard(Empty):
2067- pass
2068-
2069-
2070-class ItemSelector(Empty):
2071- pass
2072-
2073-
2074-class SingleControl(Empty):
2075- pass
2076-
2077-
2078-class MultiValue(Base):
2079- pass
2080-
2081-
2082-class SingleValue(Base):
2083- pass
2084-
2085-
2086-class Subtitled(Base):
2087- pass
2088-
2089-
2090-class ComposerSheet(UbuntuUIToolkitEmulatorBase):
2091- """ComposerSheet Autopilot emulator."""
2092-
2093- def __init__(self, *args):
2094- super(ComposerSheet, self).__init__(*args)
2095-
2096- @autopilot_logging.log_action(logger.info)
2097- def confirm(self):
2098- """Confirm the composer sheet."""
2099- button = self.select_single('Button', objectName='confirmButton')
2100- self.pointing_device.click_object(button)
2101- self.wait_until_destroyed()
2102-
2103- @autopilot_logging.log_action(logger.info)
2104- def cancel(self):
2105- """Cancel the composer sheet."""
2106- button = self.select_single('Button', objectName='cancelButton')
2107- self.pointing_device.click_object(button)
2108- self.wait_until_destroyed()
2109+"""Ubuntu UI Toolkit Autopilot custom proxy objects."""
2110+
2111+
2112+__all__ = [
2113+ 'ActionSelectionPopover',
2114+ 'Base',
2115+ 'check_autopilot_version',
2116+ 'CheckBox',
2117+ 'ComposerSheet',
2118+ 'Empty',
2119+ 'Flickable',
2120+ 'get_keyboard',
2121+ 'get_pointing_device',
2122+ 'Header',
2123+ 'ItemSelector',
2124+ 'MainView',
2125+ 'MultiValue',
2126+ 'OptionSelector',
2127+ 'QQuickListView',
2128+ 'SingleControl',
2129+ 'SingleValue',
2130+ 'Standard',
2131+ 'Subtitled',
2132+ 'TabBar',
2133+ 'Tabs',
2134+ 'TextField',
2135+ 'Toolbar',
2136+ 'ToolkitException',
2137+ 'UbuntuUIToolkitCustomProxyObjectBase',
2138+]
2139+
2140+from ubuntuuitoolkit._custom_proxy_objects._checkbox import CheckBox
2141+from ubuntuuitoolkit._custom_proxy_objects._common import (
2142+ check_autopilot_version,
2143+ get_keyboard,
2144+ get_pointing_device,
2145+ ToolkitException,
2146+ UbuntuUIToolkitCustomProxyObjectBase,
2147+)
2148+from ubuntuuitoolkit._custom_proxy_objects._flickable import Flickable
2149+from ubuntuuitoolkit._custom_proxy_objects._header import Header
2150+from ubuntuuitoolkit._custom_proxy_objects._listitems import (
2151+ Base,
2152+ Empty,
2153+ ItemSelector,
2154+ MultiValue,
2155+ SingleControl,
2156+ SingleValue,
2157+ Standard,
2158+ Subtitled,
2159+)
2160+from ubuntuuitoolkit._custom_proxy_objects._mainview import MainView
2161+from ubuntuuitoolkit._custom_proxy_objects._optionselector import (
2162+ OptionSelector
2163+)
2164+from ubuntuuitoolkit._custom_proxy_objects._popups import (
2165+ ActionSelectionPopover,
2166+ ComposerSheet,
2167+)
2168+from ubuntuuitoolkit._custom_proxy_objects._qquicklistview import (
2169+ QQuickListView
2170+)
2171+from ubuntuuitoolkit._custom_proxy_objects._tabbar import TabBar
2172+from ubuntuuitoolkit._custom_proxy_objects._tabs import Tabs
2173+from ubuntuuitoolkit._custom_proxy_objects._textfield import TextField
2174+from ubuntuuitoolkit._custom_proxy_objects._toolbar import Toolbar
2175
2176=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_checkbox.py'
2177--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_checkbox.py 1970-01-01 00:00:00 +0000
2178+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_checkbox.py 2014-04-17 01:29:24 +0000
2179@@ -0,0 +1,65 @@
2180+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2181+#
2182+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2183+#
2184+# This program is free software; you can redistribute it and/or modify
2185+# it under the terms of the GNU Lesser General Public License as published by
2186+# the Free Software Foundation; version 3.
2187+#
2188+# This program is distributed in the hope that it will be useful,
2189+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2190+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2191+# GNU Lesser General Public License for more details.
2192+#
2193+# You should have received a copy of the GNU Lesser General Public License
2194+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2195+
2196+import logging
2197+
2198+from autopilot import logging as autopilot_logging
2199+
2200+from ubuntuuitoolkit._custom_proxy_objects import _common
2201+
2202+
2203+logger = logging.getLogger(__name__)
2204+
2205+
2206+class CheckBox(_common.UbuntuUIToolkitCustomProxyObjectBase):
2207+ """CheckBox Autopilot emulator."""
2208+
2209+ @autopilot_logging.log_action(logger.info)
2210+ def check(self, timeout=10):
2211+ """Check a CheckBox, if its not already checked.
2212+
2213+ :parameter timeout: number of seconds to wait for the CheckBox to be
2214+ checked. Default is 10.
2215+
2216+ """
2217+ if not self.checked:
2218+ self.change_state(timeout)
2219+
2220+ @autopilot_logging.log_action(logger.info)
2221+ def uncheck(self, timeout=10):
2222+ """Uncheck a CheckBox, if its not already unchecked.
2223+
2224+ :parameter timeout: number of seconds to wait for the CheckBox to be
2225+ unchecked. Default is 10.
2226+
2227+ """
2228+ if self.checked:
2229+ self.change_state(timeout)
2230+
2231+ @autopilot_logging.log_action(logger.info)
2232+ def change_state(self, timeout=10):
2233+ """Change the state of a CheckBox.
2234+
2235+ If it is checked, it will be unchecked. If it is unchecked, it will be
2236+ checked.
2237+
2238+ :parameter time_out: number of seconds to wait for the CheckBox state
2239+ to change. Default is 10.
2240+
2241+ """
2242+ original_state = self.checked
2243+ self.pointing_device.click_object(self)
2244+ self.checked.wait_for(not original_state, timeout)
2245
2246=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py'
2247--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py 1970-01-01 00:00:00 +0000
2248+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py 2014-04-17 01:29:24 +0000
2249@@ -0,0 +1,69 @@
2250+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2251+#
2252+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2253+#
2254+# This program is free software; you can redistribute it and/or modify
2255+# it under the terms of the GNU Lesser General Public License as published by
2256+# the Free Software Foundation; version 3.
2257+#
2258+# This program is distributed in the hope that it will be useful,
2259+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2260+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2261+# GNU Lesser General Public License for more details.
2262+#
2263+# You should have received a copy of the GNU Lesser General Public License
2264+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2265+
2266+"""Common helpers for Ubuntu UI Toolkit Autopilot custom proxy objects."""
2267+
2268+from distutils import version
2269+
2270+import autopilot
2271+from autopilot import platform, input
2272+from autopilot.introspection import dbus
2273+
2274+
2275+class ToolkitException(Exception):
2276+ """Exception raised when there is an error with the emulator."""
2277+
2278+
2279+def get_pointing_device():
2280+ """Return the pointing device depending on the platform.
2281+
2282+ If the platform is `Desktop`, the pointing device will be a `Mouse`.
2283+ If not, the pointing device will be `Touch`.
2284+
2285+ """
2286+ if platform.model() == 'Desktop':
2287+ input_device_class = input.Mouse
2288+ else:
2289+ input_device_class = input.Touch
2290+ return input.Pointer(device=input_device_class.create())
2291+
2292+
2293+def get_keyboard():
2294+ """Return the keyboard device."""
2295+ # TODO return the OSK if we are on the phone. --elopio - 2014-01-13
2296+ return input.Keyboard.create()
2297+
2298+
2299+def check_autopilot_version():
2300+ """Check that the Autopilot installed version matches the one required.
2301+
2302+ :raise ToolkitException: If the installed Autopilot version does't
2303+ match the required by the emulators.
2304+
2305+ """
2306+ installed_version = version.LooseVersion(autopilot.version)
2307+ if installed_version < version.LooseVersion('1.4'):
2308+ raise ToolkitException(
2309+ 'The emulators need Autopilot 1.4 or higher.')
2310+
2311+
2312+class UbuntuUIToolkitCustomProxyObjectBase(dbus.CustomEmulatorBase):
2313+ """A base class for all the Ubuntu UI Toolkit emulators."""
2314+
2315+ def __init__(self, *args):
2316+ check_autopilot_version()
2317+ super(UbuntuUIToolkitCustomProxyObjectBase, self).__init__(*args)
2318+ self.pointing_device = get_pointing_device()
2319
2320=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py'
2321--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py 1970-01-01 00:00:00 +0000
2322+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py 2014-04-17 01:29:24 +0000
2323@@ -0,0 +1,150 @@
2324+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2325+#
2326+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2327+#
2328+# This program is free software; you can redistribute it and/or modify
2329+# it under the terms of the GNU Lesser General Public License as published by
2330+# the Free Software Foundation; version 3.
2331+#
2332+# This program is distributed in the hope that it will be useful,
2333+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2334+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2335+# GNU Lesser General Public License for more details.
2336+#
2337+# You should have received a copy of the GNU Lesser General Public License
2338+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2339+
2340+import logging
2341+
2342+from autopilot import logging as autopilot_logging
2343+
2344+from ubuntuuitoolkit._custom_proxy_objects import _common
2345+
2346+
2347+logger = logging.getLogger(__name__)
2348+
2349+
2350+# Containers helpers.
2351+
2352+def _get_visible_container_top(containers):
2353+ containers_top = [container.globalRect.y for container in containers]
2354+ return max(containers_top)
2355+
2356+
2357+def _get_visible_container_bottom(containers):
2358+ containers_bottom = [
2359+ container.globalRect.y + container.globalRect.height
2360+ for container in containers if container.globalRect.height > 0]
2361+ return min(containers_bottom)
2362+
2363+
2364+class Flickable(_common.UbuntuUIToolkitCustomProxyObjectBase):
2365+
2366+ @autopilot_logging.log_action(logger.info)
2367+ def swipe_child_into_view(self, child):
2368+ """Make the child visible.
2369+
2370+ Currently it works only when the object needs to be swiped vertically.
2371+ TODO implement horizontal swiping. --elopio - 2014-03-21
2372+
2373+ """
2374+ containers = self._get_containers()
2375+ if not self._is_child_visible(child, containers):
2376+ self._swipe_non_visible_child_into_view(child, containers)
2377+ else:
2378+ logger.debug('The element is already visible.')
2379+
2380+ def _get_containers(self):
2381+ """Return a list with the containers to take into account when swiping.
2382+
2383+ The list includes this flickable and the top-most container.
2384+ TODO add additional flickables that are between this and the top
2385+ container. --elopio - 2014-03-22
2386+
2387+ """
2388+ containers = [self._get_top_container(), self]
2389+ return containers
2390+
2391+ def _get_top_container(self):
2392+ """Return the top-most container with a globalRect."""
2393+ root = self.get_root_instance()
2394+ containers = [root]
2395+ while len(containers) == 1:
2396+ try:
2397+ containers[0].globalRect
2398+ return containers[0]
2399+ except AttributeError:
2400+ containers = containers[0].get_children()
2401+
2402+ raise _common.ToolkitException("Couldn't find the top-most container.")
2403+
2404+ def _is_child_visible(self, child, containers):
2405+ """Check if the center of the child is visible.
2406+
2407+ :return: True if the center of the child is visible, False otherwise.
2408+
2409+ """
2410+ object_center = child.globalRect.y + child.globalRect.height // 2
2411+ visible_top = _get_visible_container_top(containers)
2412+ visible_bottom = _get_visible_container_bottom(containers)
2413+ return (object_center >= visible_top and
2414+ object_center <= visible_bottom)
2415+
2416+ @autopilot_logging.log_action(logger.info)
2417+ def _swipe_non_visible_child_into_view(self, child, containers):
2418+ while not self._is_child_visible(child, containers):
2419+ # Check the direction of the swipe based on the position of the
2420+ # child relative to the immediate flickable container.
2421+ if child.globalRect.y < self.globalRect.y:
2422+ self._swipe_to_show_more_above(containers)
2423+ else:
2424+ self._swipe_to_show_more_below(containers)
2425+
2426+ @autopilot_logging.log_action(logger.info)
2427+ def _swipe_to_show_more_above(self, containers):
2428+ if self.atYBeginning:
2429+ raise _common.ToolkitException(
2430+ "Can't swipe more, we are already at the top of the "
2431+ "container.")
2432+ else:
2433+ self._swipe_to_show_more('above', containers)
2434+
2435+ @autopilot_logging.log_action(logger.info)
2436+ def _swipe_to_show_more_below(self, containers):
2437+ if self.atYEnd:
2438+ raise _common.ToolkitException(
2439+ "Can't swipe more, we are already at the bottom of the "
2440+ "container.")
2441+ else:
2442+ self._swipe_to_show_more('below', containers)
2443+
2444+ def _swipe_to_show_more(self, direction, containers):
2445+ start_x = stop_x = self.globalRect.x + (self.globalRect.width // 2)
2446+ # Start and stop just a little under the top and a little over the
2447+ # bottom.
2448+ top = _get_visible_container_top(containers) + 5
2449+ bottom = _get_visible_container_bottom(containers) - 5
2450+ if direction == 'below':
2451+ start_y = bottom
2452+ stop_y = top
2453+ elif direction == 'above':
2454+ start_y = top
2455+ stop_y = bottom
2456+ else:
2457+ raise _common.ToolkitException(
2458+ 'Invalid direction {}.'.format(direction))
2459+ self._slow_drag(start_x, stop_x, start_y, stop_y)
2460+ self.dragging.wait_for(False)
2461+ self.moving.wait_for(False)
2462+
2463+ def _slow_drag(self, start_x, stop_x, start_y, stop_y):
2464+ # If we drag too fast, we end up scrolling more than what we
2465+ # should, sometimes missing the element we are looking for.
2466+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate=5)
2467+
2468+ @autopilot_logging.log_action(logger.info)
2469+ def _scroll_to_top(self):
2470+ if not self.atYBeginning:
2471+ containers = self._get_containers()
2472+ while not self.atYBeginning:
2473+ self._swipe_to_show_more_above(containers)
2474
2475=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py'
2476--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py 1970-01-01 00:00:00 +0000
2477+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py 2014-04-17 01:29:24 +0000
2478@@ -0,0 +1,57 @@
2479+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2480+#
2481+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2482+#
2483+# This program is free software; you can redistribute it and/or modify
2484+# it under the terms of the GNU Lesser General Public License as published by
2485+# the Free Software Foundation; version 3.
2486+#
2487+# This program is distributed in the hope that it will be useful,
2488+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2489+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2490+# GNU Lesser General Public License for more details.
2491+#
2492+# You should have received a copy of the GNU Lesser General Public License
2493+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2494+
2495+import logging
2496+
2497+from autopilot import logging as autopilot_logging
2498+from autopilot.introspection import dbus
2499+
2500+from ubuntuuitoolkit._custom_proxy_objects import (
2501+ _common,
2502+ _tabbar
2503+)
2504+
2505+
2506+_NO_TABS_ERROR = 'The MainView has no Tabs.'
2507+
2508+
2509+logger = logging.getLogger(__name__)
2510+
2511+
2512+class Header(_common.UbuntuUIToolkitCustomProxyObjectBase):
2513+ """Header Autopilot emulator."""
2514+
2515+ def __init__(self, *args):
2516+ super(Header, self).__init__(*args)
2517+ self.pointing_device = _common.get_pointing_device()
2518+
2519+ def _get_animating(self):
2520+ tab_bar_style = self.select_single('TabBarStyle')
2521+ return tab_bar_style.animating
2522+
2523+ @autopilot_logging.log_action(logger.info)
2524+ def switch_to_next_tab(self):
2525+ """Open the next tab.
2526+
2527+ :raise ToolkitException: If the main view has no tabs.
2528+
2529+ """
2530+ try:
2531+ tab_bar = self.select_single(_tabbar.TabBar)
2532+ except dbus.StateNotFoundError:
2533+ raise _common.ToolkitException(_NO_TABS_ERROR)
2534+ tab_bar.switch_to_next_tab()
2535+ self._get_animating().wait_for(False)
2536
2537=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_listitems.py'
2538--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_listitems.py 1970-01-01 00:00:00 +0000
2539+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_listitems.py 2014-04-17 01:29:24 +0000
2540@@ -0,0 +1,114 @@
2541+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2542+#
2543+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2544+#
2545+# This program is free software; you can redistribute it and/or modify
2546+# it under the terms of the GNU Lesser General Public License as published by
2547+# the Free Software Foundation; version 3.
2548+#
2549+# This program is distributed in the hope that it will be useful,
2550+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2551+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2552+# GNU Lesser General Public License for more details.
2553+#
2554+# You should have received a copy of the GNU Lesser General Public License
2555+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2556+
2557+import logging
2558+
2559+from autopilot import logging as autopilot_logging
2560+from autopilot.introspection import dbus
2561+
2562+from ubuntuuitoolkit._custom_proxy_objects import _common
2563+
2564+
2565+logger = logging.getLogger(__name__)
2566+
2567+
2568+class Empty(_common.UbuntuUIToolkitCustomProxyObjectBase):
2569+ """Base class to emulate swipe to delete."""
2570+
2571+ def exists(self):
2572+ try:
2573+ return self.implicitHeight > 0
2574+ except dbus.StateNotFoundError:
2575+ return False
2576+
2577+ def _get_confirm_button(self):
2578+ return self.select_single(
2579+ 'QQuickItem', objectName='confirmRemovalDialog')
2580+
2581+ @autopilot_logging.log_action(logger.info)
2582+ def swipe_to_delete(self, direction='right'):
2583+ """Swipe the item in a specific direction."""
2584+ if self.removable:
2585+ self._drag_pointing_device_to_delete(direction)
2586+ if self.confirmRemoval:
2587+ self.waitingConfirmationForRemoval.wait_for(True)
2588+ else:
2589+ self._wait_until_deleted()
2590+ else:
2591+ raise _common.ToolkitException(
2592+ 'The item "{0}" is not removable'.format(self.objectName))
2593+
2594+ def _drag_pointing_device_to_delete(self, direction):
2595+ x, y, w, h = self.globalRect
2596+ tx = x + (w // 8)
2597+ ty = y + (h // 2)
2598+
2599+ if direction == 'right':
2600+ self.pointing_device.drag(tx, ty, w, ty)
2601+ elif direction == 'left':
2602+ self.pointing_device.drag(w - (w*0.1), ty, x, ty)
2603+ else:
2604+ raise _common.ToolkitException(
2605+ 'Invalid direction "{0}" used on swipe to delete function'
2606+ .format(direction))
2607+
2608+ def _wait_until_deleted(self):
2609+ try:
2610+ # The item was hidden.
2611+ self.implicitHeight.wait_for(0)
2612+ except dbus.StateNotFoundError:
2613+ # The item was destroyed.
2614+ pass
2615+
2616+ @autopilot_logging.log_action(logger.info)
2617+ def confirm_removal(self):
2618+ """Comfirm item removal if this was already swiped."""
2619+ if self.waitingConfirmationForRemoval:
2620+ deleteButton = self._get_confirm_button()
2621+ self.pointing_device.click_object(deleteButton)
2622+ self._wait_until_deleted()
2623+ else:
2624+ raise _common.ToolkitException(
2625+ 'The item "{0}" is not waiting for removal confirmation'.
2626+ format(self.objectName))
2627+
2628+
2629+class Base(Empty):
2630+ pass
2631+
2632+
2633+class Standard(Empty):
2634+ pass
2635+
2636+
2637+class ItemSelector(Empty):
2638+ pass
2639+
2640+
2641+class SingleControl(Empty):
2642+ pass
2643+
2644+
2645+class MultiValue(Base):
2646+ pass
2647+
2648+
2649+class SingleValue(Base):
2650+ pass
2651+
2652+
2653+class Subtitled(Base):
2654+ pass
2655
2656=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_mainview.py'
2657--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_mainview.py 1970-01-01 00:00:00 +0000
2658+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_mainview.py 2014-04-17 01:29:24 +0000
2659@@ -0,0 +1,162 @@
2660+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2661+#
2662+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2663+#
2664+# This program is free software; you can redistribute it and/or modify
2665+# it under the terms of the GNU Lesser General Public License as published by
2666+# the Free Software Foundation; version 3.
2667+#
2668+# This program is distributed in the hope that it will be useful,
2669+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2670+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2671+# GNU Lesser General Public License for more details.
2672+#
2673+# You should have received a copy of the GNU Lesser General Public License
2674+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2675+
2676+"""Ubuntu UI Toolkit Autopilot custom proxy objects."""
2677+
2678+import logging
2679+
2680+from autopilot import logging as autopilot_logging
2681+from autopilot.introspection import dbus
2682+
2683+from ubuntuuitoolkit._custom_proxy_objects import (
2684+ _common,
2685+ _popups,
2686+ _tabs,
2687+ _toolbar,
2688+)
2689+
2690+
2691+_NO_TABS_ERROR = 'The MainView has no Tabs.'
2692+
2693+
2694+logger = logging.getLogger(__name__)
2695+
2696+
2697+class MainView(_common.UbuntuUIToolkitCustomProxyObjectBase):
2698+ """MainView Autopilot emulator."""
2699+
2700+ def get_header(self):
2701+ """Return the Header emulator of the MainView."""
2702+ try:
2703+ return self.select_single('Header', objectName='MainView_Header')
2704+ except dbus.StateNotFoundError:
2705+ raise _common.ToolkitException('The main view has no header.')
2706+
2707+ def get_toolbar(self):
2708+ """Return the Toolbar emulator of the MainView."""
2709+ return self.select_single(_toolbar.Toolbar)
2710+
2711+ @autopilot_logging.log_action(logger.info)
2712+ def open_toolbar(self):
2713+ """Open the toolbar if it's not already opened.
2714+
2715+ :return: The toolbar.
2716+
2717+ """
2718+ return self.get_toolbar().open()
2719+
2720+ @autopilot_logging.log_action(logger.info)
2721+ def close_toolbar(self):
2722+ """Close the toolbar if it's opened."""
2723+ self.get_toolbar().close()
2724+
2725+ def get_tabs(self):
2726+ """Return the Tabs emulator of the MainView.
2727+
2728+ :raise ToolkitException: If the main view has no tabs.
2729+
2730+ """
2731+ try:
2732+ return self.select_single(_tabs.Tabs)
2733+ except dbus.StateNotFoundError:
2734+ raise _common.ToolkitException(_NO_TABS_ERROR)
2735+
2736+ @autopilot_logging.log_action(logger.info)
2737+ def switch_to_next_tab(self):
2738+ """Open the next tab.
2739+
2740+ :return: The newly opened tab.
2741+
2742+ """
2743+ logger.debug('Switch to next tab.')
2744+ self.get_header().switch_to_next_tab()
2745+ current_tab = self.get_tabs().get_current_tab()
2746+ current_tab.visible.wait_for(True)
2747+ return current_tab
2748+
2749+ @autopilot_logging.log_action(logger.info)
2750+ def switch_to_tab_by_index(self, index):
2751+ """Open a tab.
2752+
2753+ :parameter index: The index of the tab to open.
2754+ :return: The newly opened tab.
2755+ :raise ToolkitException: If the tab index is out of range.
2756+
2757+ """
2758+ logger.debug('Switch to tab with index {0}.'.format(index))
2759+ tabs = self.get_tabs()
2760+ number_of_tabs = tabs.get_number_of_tabs()
2761+ if index >= number_of_tabs:
2762+ raise _common.ToolkitException('Tab index out of range.')
2763+ current_tab = tabs.get_current_tab()
2764+ number_of_switches = 0
2765+ while not tabs.selectedTabIndex == index:
2766+ logger.debug(
2767+ 'Current tab index: {0}.'.format(tabs.selectedTabIndex))
2768+ if number_of_switches >= number_of_tabs - 1:
2769+ # This prevents a loop. But if this error is ever raised, it's
2770+ # likely there's a bug on the emulator or on the QML Tab.
2771+ raise _common.ToolkitException(
2772+ 'The tab with index {0} was not selected.'.format(index))
2773+ current_tab = self.switch_to_next_tab()
2774+ number_of_switches += 1
2775+ return current_tab
2776+
2777+ @autopilot_logging.log_action(logger.info)
2778+ def switch_to_previous_tab(self):
2779+ """Open the previous tab.
2780+
2781+ :return: The newly opened tab.
2782+
2783+ """
2784+ tabs = self.get_tabs()
2785+ if tabs.selectedTabIndex == 0:
2786+ previous_tab_index = tabs.get_number_of_tabs() - 1
2787+ else:
2788+ previous_tab_index = tabs.selectedTabIndex - 1
2789+ return self.switch_to_tab_by_index(previous_tab_index)
2790+
2791+ @autopilot_logging.log_action(logger.info)
2792+ def switch_to_tab(self, object_name):
2793+ """Open a tab.
2794+
2795+ :parameter object_name: The QML objectName property of the tab.
2796+ :return: The newly opened tab.
2797+ :raise ToolkitException: If there is no tab with that object
2798+ name.
2799+
2800+ """
2801+ tabs = self.get_tabs()
2802+ for index, tab in enumerate(tabs.select_many('Tab')):
2803+ if tab.objectName == object_name:
2804+ return self.switch_to_tab_by_index(tab.index)
2805+ raise _common.ToolkitException(
2806+ 'Tab with objectName "{0}" not found.'.format(object_name))
2807+
2808+ def get_action_selection_popover(self, object_name):
2809+ """Return an ActionSelectionPopover emulator.
2810+
2811+ :parameter object_name: The QML objectName property of the popover.
2812+
2813+ """
2814+ return self.select_single(
2815+ _popups.ActionSelectionPopover, objectName=object_name)
2816+
2817+ @autopilot_logging.log_action(logger.info)
2818+ def go_back(self):
2819+ """Go to the previous page."""
2820+ toolbar = self.open_toolbar()
2821+ toolbar.click_back_button()
2822
2823=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_popups.py'
2824--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_popups.py 1970-01-01 00:00:00 +0000
2825+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_popups.py 2014-04-17 01:29:24 +0000
2826@@ -0,0 +1,82 @@
2827+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2828+#
2829+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2830+#
2831+# This program is free software; you can redistribute it and/or modify
2832+# it under the terms of the GNU Lesser General Public License as published by
2833+# the Free Software Foundation; version 3.
2834+#
2835+# This program is distributed in the hope that it will be useful,
2836+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2837+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2838+# GNU Lesser General Public License for more details.
2839+#
2840+# You should have received a copy of the GNU Lesser General Public License
2841+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2842+
2843+import logging
2844+
2845+from autopilot import logging as autopilot_logging
2846+from autopilot.introspection import dbus
2847+
2848+from ubuntuuitoolkit._custom_proxy_objects import _common
2849+
2850+
2851+logger = logging.getLogger(__name__)
2852+
2853+
2854+class ActionSelectionPopover(_common.UbuntuUIToolkitCustomProxyObjectBase):
2855+ """ActionSelectionPopover Autopilot emulator."""
2856+
2857+ def click_button_by_text(self, text):
2858+ """Click a button on the popover.
2859+
2860+ XXX We are receiving the text because there's no way to set the
2861+ objectName on the action. This is reported at
2862+ https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1205144
2863+ --elopio - 2013-07-25
2864+
2865+ :parameter text: The text of the button.
2866+ :raise ToolkitException: If the popover is not open.
2867+
2868+ """
2869+ if not self.visible:
2870+ raise _common.ToolkitException('The popover is not open.')
2871+ button = self._get_button(text)
2872+ if button is None:
2873+ raise _common.ToolkitException(
2874+ 'Button with text "{0}" not found.'.format(text))
2875+ self.pointing_device.click_object(button)
2876+ if self.autoClose:
2877+ try:
2878+ self.visible.wait_for(False)
2879+ except dbus.StateNotFoundError:
2880+ # The popover was removed from the tree.
2881+ pass
2882+
2883+ def _get_button(self, text):
2884+ buttons = self.select_many('Empty')
2885+ for button in buttons:
2886+ if button.text == text:
2887+ return button
2888+
2889+
2890+class ComposerSheet(_common.UbuntuUIToolkitCustomProxyObjectBase):
2891+ """ComposerSheet Autopilot emulator."""
2892+
2893+ def __init__(self, *args):
2894+ super(ComposerSheet, self).__init__(*args)
2895+
2896+ @autopilot_logging.log_action(logger.info)
2897+ def confirm(self):
2898+ """Confirm the composer sheet."""
2899+ button = self.select_single('Button', objectName='confirmButton')
2900+ self.pointing_device.click_object(button)
2901+ self.wait_until_destroyed()
2902+
2903+ @autopilot_logging.log_action(logger.info)
2904+ def cancel(self):
2905+ """Cancel the composer sheet."""
2906+ button = self.select_single('Button', objectName='cancelButton')
2907+ self.pointing_device.click_object(button)
2908+ self.wait_until_destroyed()
2909
2910=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py'
2911--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py 1970-01-01 00:00:00 +0000
2912+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py 2014-04-17 01:29:24 +0000
2913@@ -0,0 +1,65 @@
2914+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2915+#
2916+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2917+#
2918+# This program is free software; you can redistribute it and/or modify
2919+# it under the terms of the GNU Lesser General Public License as published by
2920+# the Free Software Foundation; version 3.
2921+#
2922+# This program is distributed in the hope that it will be useful,
2923+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2924+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2925+# GNU Lesser General Public License for more details.
2926+#
2927+# You should have received a copy of the GNU Lesser General Public License
2928+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2929+
2930+import logging
2931+
2932+from autopilot import logging as autopilot_logging
2933+from autopilot.introspection import dbus
2934+
2935+from ubuntuuitoolkit._custom_proxy_objects import _flickable
2936+from ubuntuuitoolkit._custom_proxy_objects import _common
2937+
2938+
2939+logger = logging.getLogger(__name__)
2940+
2941+
2942+class QQuickListView(_flickable.Flickable):
2943+
2944+ @autopilot_logging.log_action(logger.info)
2945+ def click_element(self, object_name):
2946+ """Click an element from the list.
2947+
2948+ It swipes the element into view if it's center is not visible.
2949+
2950+ :parameter objectName: The objectName property of the element to click.
2951+
2952+ """
2953+ try:
2954+ element = self.select_single(objectName=object_name)
2955+ except dbus.StateNotFoundError:
2956+ # The element might be on a part of the list that hasn't been
2957+ # created yet. We have to search for it scrolling the entire list.
2958+ element = self._find_element(object_name)
2959+ self.swipe_child_into_view(element)
2960+ self.pointing_device.click_object(element)
2961+
2962+ @autopilot_logging.log_action(logger.info)
2963+ def _find_element(self, object_name):
2964+ self._scroll_to_top()
2965+ while not self.atYEnd:
2966+ containers = self._get_containers()
2967+ self._swipe_to_show_more_below(containers)
2968+ try:
2969+ return self.select_single(objectName=object_name)
2970+ except dbus.StateNotFoundError:
2971+ pass
2972+ raise _common.ToolkitException(
2973+ 'List element with objectName "{}" not found.'.format(object_name))
2974+
2975+ def _is_element_clickable(self, object_name):
2976+ child = self.select_single(objectName=object_name)
2977+ containers = self._get_containers()
2978+ return self._is_child_visible(child, containers)
2979
2980=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabbar.py'
2981--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabbar.py 1970-01-01 00:00:00 +0000
2982+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabbar.py 2014-04-17 01:29:24 +0000
2983@@ -0,0 +1,68 @@
2984+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2985+#
2986+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
2987+#
2988+# This program is free software; you can redistribute it and/or modify
2989+# it under the terms of the GNU Lesser General Public License as published by
2990+# the Free Software Foundation; version 3.
2991+#
2992+# This program is distributed in the hope that it will be useful,
2993+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2994+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2995+# GNU Lesser General Public License for more details.
2996+#
2997+# You should have received a copy of the GNU Lesser General Public License
2998+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2999+
3000+import logging
3001+
3002+from autopilot import logging as autopilot_logging
3003+
3004+from ubuntuuitoolkit._custom_proxy_objects import _common
3005+
3006+
3007+logger = logging.getLogger(__name__)
3008+
3009+
3010+class TabBar(_common.UbuntuUIToolkitCustomProxyObjectBase):
3011+ """TabBar Autopilot emulator."""
3012+
3013+ @autopilot_logging.log_action(logger.info)
3014+ def switch_to_next_tab(self):
3015+ """Open the next tab."""
3016+ self._activate_tab_bar()
3017+ logger.debug('Click the next tab bar button.')
3018+ self.pointing_device.click_object(self._get_next_tab_button())
3019+
3020+ def _activate_tab_bar(self):
3021+ # First move to the tab bar to avoid timing issues when we find it in
3022+ # selection mode but it's deselected while we move to it.
3023+ self.pointing_device.move_to_object(self)
3024+ if self.selectionMode:
3025+ logger.debug('Already in selection mode.')
3026+ else:
3027+ # Click the tab bar to switch to selection mode.
3028+ logger.debug('Click the tab bar to enable selection mode.')
3029+ self.pointing_device.click_object(self)
3030+
3031+ def _get_next_tab_button(self):
3032+ current_index = self._get_selected_button_index()
3033+ next_index = (current_index + 1) % self._get_number_of_tab_buttons()
3034+ return self._get_tab_button(next_index)
3035+
3036+ def _get_selected_button_index(self):
3037+ return self.select_single('QQuickPathView').selectedButtonIndex
3038+
3039+ def _get_number_of_tab_buttons(self):
3040+ return len(self._get_tab_buttons())
3041+
3042+ def _get_tab_buttons(self):
3043+ return self.select_many('AbstractButton')
3044+
3045+ def _get_tab_button(self, index):
3046+ buttons = self._get_tab_buttons()
3047+ for button in buttons:
3048+ if button.buttonIndex == index:
3049+ return button
3050+ raise _common.ToolkitException(
3051+ 'There is no tab button with index {0}.'.format(index))
3052
3053=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabs.py'
3054--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabs.py 1970-01-01 00:00:00 +0000
3055+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_tabs.py 2014-04-17 01:29:24 +0000
3056@@ -0,0 +1,41 @@
3057+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3058+#
3059+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
3060+#
3061+# This program is free software; you can redistribute it and/or modify
3062+# it under the terms of the GNU Lesser General Public License as published by
3063+# the Free Software Foundation; version 3.
3064+#
3065+# This program is distributed in the hope that it will be useful,
3066+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3067+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3068+# GNU Lesser General Public License for more details.
3069+#
3070+# You should have received a copy of the GNU Lesser General Public License
3071+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3072+
3073+from ubuntuuitoolkit._custom_proxy_objects import _common
3074+
3075+
3076+class Tabs(_common.UbuntuUIToolkitCustomProxyObjectBase):
3077+ """Tabs Autopilot emulator."""
3078+
3079+ def get_current_tab(self):
3080+ """Return the currently selected tab."""
3081+ return self._get_tab(self.selectedTabIndex)
3082+
3083+ def _get_tab(self, index):
3084+ tabs = self._get_tabs()
3085+ for tab in tabs:
3086+ if tab.index == index:
3087+ return tab
3088+ else:
3089+ raise _common.ToolkitException(
3090+ 'There is no tab with index {0}.'.format(index))
3091+
3092+ def _get_tabs(self):
3093+ return self.select_many('Tab')
3094+
3095+ def get_number_of_tabs(self):
3096+ """Return the number of tabs."""
3097+ return len(self._get_tabs())
3098
3099=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py'
3100--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py 1970-01-01 00:00:00 +0000
3101+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py 2014-04-17 01:29:24 +0000
3102@@ -0,0 +1,88 @@
3103+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3104+#
3105+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
3106+#
3107+# This program is free software; you can redistribute it and/or modify
3108+# it under the terms of the GNU Lesser General Public License as published by
3109+# the Free Software Foundation; version 3.
3110+#
3111+# This program is distributed in the hope that it will be useful,
3112+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3113+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3114+# GNU Lesser General Public License for more details.
3115+#
3116+# You should have received a copy of the GNU Lesser General Public License
3117+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3118+
3119+from autopilot import platform
3120+
3121+from ubuntuuitoolkit._custom_proxy_objects import (
3122+ _common,
3123+ _mainview
3124+)
3125+
3126+
3127+class TextField(_common.UbuntuUIToolkitCustomProxyObjectBase):
3128+ """TextField Autopilot emulator."""
3129+
3130+ def __init__(self, *args):
3131+ super(TextField, self).__init__(*args)
3132+ self.keyboard = _common.get_keyboard()
3133+
3134+ def write(self, text, clear=True):
3135+ """Write into the text field.
3136+
3137+ :parameter text: The text to write.
3138+ :parameter clear: If True, the text field will be cleared before
3139+ writing the text. If False, the text will be appended at the end
3140+ of the text field. Default is True.
3141+
3142+ """
3143+ with self.keyboard.focused_type(self):
3144+ self.focus.wait_for(True)
3145+ if clear:
3146+ self.clear()
3147+ else:
3148+ if not self.is_empty():
3149+ self.keyboard.press_and_release('End')
3150+ self.keyboard.type(text)
3151+
3152+ def clear(self):
3153+ """Clear the text field."""
3154+ if not self.is_empty():
3155+ if self.hasClearButton:
3156+ self._click_clear_button()
3157+ else:
3158+ self._clear_with_keys()
3159+ self.text.wait_for('')
3160+
3161+ def is_empty(self):
3162+ """Return True if the text field is empty. False otherwise."""
3163+ return self.text == ''
3164+
3165+ def _click_clear_button(self):
3166+ clear_button = self.select_single(
3167+ 'AbstractButton', objectName='clear_button')
3168+ if not clear_button.visible:
3169+ self.pointing_device.click_object(self)
3170+ self.pointing_device.click_object(clear_button)
3171+
3172+ def _clear_with_keys(self):
3173+ if platform.model() == 'Desktop':
3174+ self._select_all()
3175+ else:
3176+ # Touch tap currently doesn't have a press_duration parameter, so
3177+ # we can't show the popover. Reported as bug http://pad.lv/1268782
3178+ # --elopio - 2014-01-13
3179+ self.keyboard.press_and_release('End')
3180+ while not self.is_empty():
3181+ # We delete with backspace because the on-screen keyboard has that
3182+ # key.
3183+ self.keyboard.press_and_release('BackSpace')
3184+
3185+ def _select_all(self):
3186+ self.pointing_device.click_object(self, press_duration=1)
3187+ root = self.get_root_instance()
3188+ main_view = root.select_single(_mainview.MainView)
3189+ popover = main_view.get_action_selection_popover('text_input_popover')
3190+ popover.click_button_by_text('Select All')
3191
3192=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_toolbar.py'
3193--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_toolbar.py 1970-01-01 00:00:00 +0000
3194+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_toolbar.py 2014-04-17 01:29:24 +0000
3195@@ -0,0 +1,106 @@
3196+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3197+#
3198+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
3199+#
3200+# This program is free software; you can redistribute it and/or modify
3201+# it under the terms of the GNU Lesser General Public License as published by
3202+# the Free Software Foundation; version 3.
3203+#
3204+# This program is distributed in the hope that it will be useful,
3205+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3206+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3207+# GNU Lesser General Public License for more details.
3208+#
3209+# You should have received a copy of the GNU Lesser General Public License
3210+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3211+
3212+import logging
3213+
3214+from autopilot import logging as autopilot_logging
3215+from autopilot.introspection import dbus
3216+
3217+from ubuntuuitoolkit._custom_proxy_objects import _common
3218+
3219+
3220+logger = logging.getLogger(__name__)
3221+
3222+
3223+class Toolbar(_common.UbuntuUIToolkitCustomProxyObjectBase):
3224+ """Toolbar Autopilot emulator."""
3225+
3226+ @autopilot_logging.log_action(logger.info)
3227+ def open(self):
3228+ """Open the toolbar if it's not already opened.
3229+
3230+ :return: The toolbar.
3231+
3232+ """
3233+ self.animating.wait_for(False)
3234+ if not self.opened:
3235+ self._drag_to_open()
3236+ self.opened.wait_for(True)
3237+ self.animating.wait_for(False)
3238+
3239+ return self
3240+
3241+ def _drag_to_open(self):
3242+ x, y, _, _ = self.globalRect
3243+ line_x = x + self.width * 0.50
3244+ start_y = y + self.height - 1
3245+ stop_y = y
3246+
3247+ self.pointing_device.drag(line_x, start_y, line_x, stop_y)
3248+
3249+ @autopilot_logging.log_action(logger.info)
3250+ def close(self):
3251+ """Close the toolbar if it's opened."""
3252+ self.animating.wait_for(False)
3253+ if self.opened:
3254+ self._drag_to_close()
3255+ self.opened.wait_for(False)
3256+ self.animating.wait_for(False)
3257+
3258+ def _drag_to_close(self):
3259+ x, y, _, _ = self.globalRect
3260+ line_x = x + self.width * 0.50
3261+ start_y = y
3262+ stop_y = y + self.height - 1
3263+
3264+ self.pointing_device.drag(line_x, start_y, line_x, stop_y)
3265+
3266+ @autopilot_logging.log_action(logger.info)
3267+ def click_button(self, object_name):
3268+ """Click a button of the toolbar.
3269+
3270+ The toolbar should be opened before clicking the button, or an
3271+ exception will be raised. If the toolbar is closed for some reason
3272+ (e.g., timer finishes) after moving the mouse cursor and before
3273+ clicking the button, it is re-opened automatically by this function.
3274+
3275+ :parameter object_name: The QML objectName property of the button.
3276+ :raise ToolkitException: If there is no button with that object
3277+ name.
3278+
3279+ """
3280+ # ensure the toolbar is open
3281+ if not self.opened:
3282+ raise _common.ToolkitException(
3283+ 'Toolbar must be opened before calling click_button().')
3284+ try:
3285+ button = self._get_button(object_name)
3286+ except dbus.StateNotFoundError:
3287+ raise _common.ToolkitException(
3288+ 'Button with objectName "{0}" not found.'.format(object_name))
3289+ self.pointing_device.move_to_object(button)
3290+ # ensure the toolbar is still open (may have closed due to timeout)
3291+ self.open()
3292+ # click the button
3293+ self.pointing_device.click_object(button)
3294+
3295+ def _get_button(self, object_name):
3296+ return self.select_single('ActionItem', objectName=object_name)
3297+
3298+ @autopilot_logging.log_action(logger.info)
3299+ def click_back_button(self):
3300+ """Click the back button of the toolbar."""
3301+ self.click_button('back_toolbar_button')
3302
3303=== added file 'tests/autopilot/ubuntuuitoolkit/emulators.py'
3304--- tests/autopilot/ubuntuuitoolkit/emulators.py 1970-01-01 00:00:00 +0000
3305+++ tests/autopilot/ubuntuuitoolkit/emulators.py 2014-04-17 01:29:24 +0000
3306@@ -0,0 +1,85 @@
3307+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3308+#
3309+# Copyright (C) 2012, 2013, 2014 Canonical Ltd.
3310+#
3311+# This program is free software; you can redistribute it and/or modify
3312+# it under the terms of the GNU Lesser General Public License as published by
3313+# the Free Software Foundation; version 3.
3314+#
3315+# This program is distributed in the hope that it will be useful,
3316+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3317+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3318+# GNU Lesser General Public License for more details.
3319+#
3320+# You should have received a copy of the GNU Lesser General Public License
3321+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3322+
3323+"""Emulators was the old name for the custom proxy objects."""
3324+
3325+import logging
3326+
3327+
3328+logger = logging.getLogger(__name__)
3329+
3330+
3331+logger.warning(
3332+ 'The ubuntuuitoolkit.emulators module is deprecated. Import the autopilot '
3333+ 'helpers from the top-level ubuntuuitoolkit module.')
3334+
3335+
3336+__all__ = [
3337+ 'check_autopilot_version',
3338+ 'get_keyboard',
3339+ 'get_pointing_device',
3340+ 'ActionSelectionPopover',
3341+ 'Base',
3342+ 'CheckBox',
3343+ 'ComposerSheet',
3344+ 'Empty',
3345+ 'Flickable',
3346+ 'Header',
3347+ 'ItemSelector',
3348+ 'MainView',
3349+ 'MultiValue',
3350+ 'OptionSelector',
3351+ 'QQuickListView',
3352+ 'SingleControl',
3353+ 'SingleValue',
3354+ 'Standard',
3355+ 'Subtitled',
3356+ 'TabBar',
3357+ 'Tabs',
3358+ 'TextField',
3359+ 'Toolbar',
3360+ 'ToolkitEmulatorException',
3361+ 'UbuntuUIToolkitEmulatorBase',
3362+]
3363+
3364+
3365+from ubuntuuitoolkit import (
3366+ check_autopilot_version,
3367+ get_keyboard,
3368+ get_pointing_device,
3369+ ActionSelectionPopover,
3370+ Base,
3371+ CheckBox,
3372+ ComposerSheet,
3373+ Empty,
3374+ Flickable,
3375+ Header,
3376+ ItemSelector,
3377+ MainView,
3378+ MultiValue,
3379+ OptionSelector,
3380+ QQuickListView,
3381+ SingleControl,
3382+ SingleValue,
3383+ Standard,
3384+ Subtitled,
3385+ TabBar,
3386+ Tabs,
3387+ TextField,
3388+ Toolbar,
3389+ ToolkitException as ToolkitEmulatorException,
3390+ UbuntuUIToolkitCustomProxyObjectBase as UbuntuUIToolkitEmulatorBase,
3391+)
3392
3393=== added directory 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects'
3394=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/__init__.py'
3395=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_checkbox.py'
3396--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_checkbox.py 1970-01-01 00:00:00 +0000
3397+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_checkbox.py 2014-04-17 01:29:24 +0000
3398@@ -0,0 +1,136 @@
3399+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3400+#
3401+# Copyright (C) 2013, 2014 Canonical Ltd.
3402+#
3403+# This program is free software; you can redistribute it and/or modify
3404+# it under the terms of the GNU Lesser General Public License as published by
3405+# the Free Software Foundation; version 3.
3406+#
3407+# This program is distributed in the hope that it will be useful,
3408+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3409+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3410+# GNU Lesser General Public License for more details.
3411+#
3412+# You should have received a copy of the GNU Lesser General Public License
3413+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3414+
3415+import time
3416+
3417+from autopilot import input
3418+from testtools.matchers import GreaterThan, LessThan
3419+try:
3420+ from unittest import mock
3421+except ImportError:
3422+ import mock
3423+
3424+import ubuntuuitoolkit
3425+from ubuntuuitoolkit import tests
3426+
3427+
3428+TEST_QML_WITH_CHECKBOX = ("""
3429+import QtQuick 2.0
3430+import Ubuntu.Components 0.1
3431+
3432+MainView {
3433+ width: units.gu(48)
3434+ height: units.gu(60)
3435+
3436+ Item {
3437+ CheckBox {
3438+ checked: false
3439+ objectName: "test_checkbox"
3440+ }
3441+ }
3442+}
3443+""")
3444+
3445+
3446+TEST_QML_WITH_SWITCH = ("""
3447+import QtQuick 2.0
3448+import Ubuntu.Components 0.1
3449+
3450+MainView {
3451+ width: units.gu(48)
3452+ height: units.gu(60)
3453+
3454+ Item {
3455+ Switch {
3456+ checked: false
3457+ objectName: "test_switch"
3458+ }
3459+ }
3460+}
3461+""")
3462+
3463+
3464+class ToggleTestCase(tests.QMLStringAppTestCase):
3465+
3466+ scenarios = [
3467+ ('checkbox', dict(
3468+ test_qml=TEST_QML_WITH_CHECKBOX, objectName='test_checkbox')),
3469+ ('switch', dict(
3470+ test_qml=TEST_QML_WITH_SWITCH, objectName='test_switch'))
3471+ ]
3472+
3473+ def setUp(self):
3474+ super(ToggleTestCase, self).setUp()
3475+ self.toggle = self.main_view.select_single(
3476+ ubuntuuitoolkit.CheckBox, objectName=self.objectName)
3477+ self.assertFalse(self.toggle.checked)
3478+
3479+ def test_toggle_custom_proxy_object(self):
3480+ self.assertIsInstance(self.toggle, ubuntuuitoolkit.CheckBox)
3481+
3482+ def test_check_toggle(self):
3483+ self.toggle.check()
3484+ self.assertTrue(self.toggle.checked)
3485+
3486+ def test_check_toggle_already_checked(self):
3487+ self.toggle.check()
3488+ with mock.patch.object(input.Pointer, 'click_object') as mock_click:
3489+ self.toggle.check()
3490+ self.assertFalse(mock_click.called)
3491+
3492+ def test_uncheck_toggle(self):
3493+ self.toggle.check()
3494+ self.toggle.uncheck()
3495+ self.assertFalse(self.toggle.checked)
3496+
3497+ def test_uncheck_toggle_already_unchecked(self):
3498+ with mock.patch.object(input.Pointer, 'click_object') as mock_click:
3499+ self.toggle.uncheck()
3500+ self.assertFalse(mock_click.called)
3501+
3502+ def test_change_state_from_checked(self):
3503+ self.toggle.check()
3504+ self.toggle.change_state()
3505+ self.assertFalse(self.toggle.checked)
3506+
3507+ def test_change_state_from_unchecked(self):
3508+ self.toggle.change_state()
3509+ self.assertTrue(self.toggle.checked)
3510+
3511+ def test_check_with_timeout(self):
3512+ with mock.patch.object(
3513+ ubuntuuitoolkit.CheckBox, 'change_state') as mock_change:
3514+ self.toggle.check(timeout=1)
3515+
3516+ mock_change.assert_called_once_with(1)
3517+
3518+ def test_uncheck_with_timeout(self):
3519+ self.toggle.check()
3520+ with mock.patch.object(
3521+ ubuntuuitoolkit.CheckBox, 'change_state') as mock_change:
3522+ self.toggle.uncheck(timeout=1)
3523+
3524+ mock_change.assert_called_once_with(1)
3525+
3526+ def test_change_state_with_timeout(self):
3527+ with mock.patch.object(self.toggle, 'pointing_device'):
3528+ # mock the pointing device so the checkbox is not clicked.
3529+ timestamp_before_call = time.time()
3530+ self.assertRaises(AssertionError, self.toggle.change_state, 1)
3531+
3532+ waiting_time = time.time() - timestamp_before_call
3533+ self.assertThat(waiting_time, GreaterThan(1))
3534+ self.assertThat(waiting_time, LessThan(2))
3535
3536=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_common.py'
3537--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_common.py 1970-01-01 00:00:00 +0000
3538+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_common.py 2014-04-17 01:29:24 +0000
3539@@ -0,0 +1,68 @@
3540+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3541+#
3542+# Copyright (C) 2013, 2014 Canonical Ltd.
3543+#
3544+# This program is free software; you can redistribute it and/or modify
3545+# it under the terms of the GNU Lesser General Public License as published by
3546+# the Free Software Foundation; version 3.
3547+#
3548+# This program is distributed in the hope that it will be useful,
3549+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3550+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3551+# GNU Lesser General Public License for more details.
3552+#
3553+# You should have received a copy of the GNU Lesser General Public License
3554+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3555+
3556+import unittest
3557+
3558+import autopilot
3559+from autopilot import platform, input
3560+try:
3561+ from unittest import mock
3562+except ImportError:
3563+ import mock
3564+
3565+import ubuntuuitoolkit
3566+from ubuntuuitoolkit import tests
3567+
3568+
3569+class CheckAutopilotVersionTestCase(unittest.TestCase):
3570+
3571+ def test_lower_version_should_raise_exception(self):
3572+ with mock.patch.object(autopilot, 'version', '1.3'):
3573+ self.assertRaises(
3574+ ubuntuuitoolkit.ToolkitException,
3575+ ubuntuuitoolkit.check_autopilot_version)
3576+
3577+ def test_required_version_should_succeed(self):
3578+ with mock.patch.object(autopilot, 'version', '1.4'):
3579+ ubuntuuitoolkit.check_autopilot_version()
3580+
3581+ def test_higher_version_should_succeed(self):
3582+ with mock.patch.object(autopilot, 'version', '1.5'):
3583+ ubuntuuitoolkit.check_autopilot_version()
3584+
3585+
3586+class UbuntuUIToolkitCustomProxyObjectBaseTestCase(tests.QMLStringAppTestCase):
3587+
3588+ def test_pointing_device(self):
3589+ self.assertIsInstance(self.app.pointing_device, input.Pointer)
3590+
3591+ @unittest.skipIf(platform.model() != 'Desktop', 'Desktop only')
3592+ def test_pointing_device_in_desktop(self):
3593+ self.assertIsInstance(self.app.pointing_device._device, input.Mouse)
3594+
3595+ @unittest.skipIf(platform.model() == 'Desktop', 'Phablet only')
3596+ def test_pointing_device_in_phablet(self):
3597+ self.assertIsInstance(self.app.pointing_device._device, input.Touch)
3598+
3599+ def test_custom_proxy_objects_should_check_version_on_init(self):
3600+ check_name = (
3601+ 'ubuntuuitoolkit._custom_proxy_objects._common.'
3602+ 'check_autopilot_version')
3603+ with mock.patch(check_name, autospec=True) as mock_check:
3604+ # Instantiate any custom proxy object.
3605+ self.main_view
3606+
3607+ mock_check.assert_called_once_with()
3608
3609=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_header.py'
3610--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_header.py 1970-01-01 00:00:00 +0000
3611+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_header.py 2014-04-17 01:29:24 +0000
3612@@ -0,0 +1,41 @@
3613+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3614+#
3615+# Copyright (C) 2013, 2014 Canonical Ltd.
3616+#
3617+# This program is free software; you can redistribute it and/or modify
3618+# it under the terms of the GNU Lesser General Public License as published by
3619+# the Free Software Foundation; version 3.
3620+#
3621+# This program is distributed in the hope that it will be useful,
3622+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3623+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3624+# GNU Lesser General Public License for more details.
3625+#
3626+# You should have received a copy of the GNU Lesser General Public License
3627+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3628+
3629+import ubuntuuitoolkit
3630+from ubuntuuitoolkit import tests
3631+
3632+
3633+class PageTestCase(tests.QMLStringAppTestCase):
3634+
3635+ test_qml = ("""
3636+import QtQuick 2.0
3637+import Ubuntu.Components 0.1
3638+
3639+MainView {
3640+ width: units.gu(48)
3641+ height: units.gu(60)
3642+
3643+ Page {
3644+ title: "Test title"
3645+ }
3646+}
3647+""")
3648+
3649+ def test_header_custom_proxy_object(self):
3650+ header = self.main_view.get_header()
3651+ self.assertIsInstance(header, ubuntuuitoolkit.Header)
3652+ self.assertTrue(header.visible)
3653+ self.assertEqual(header.title, "Test title")
3654
3655=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitems.py'
3656--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitems.py 1970-01-01 00:00:00 +0000
3657+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitems.py 2014-04-17 01:29:24 +0000
3658@@ -0,0 +1,170 @@
3659+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3660+#
3661+# Copyright (C) 2013, 2014 Canonical Ltd.
3662+#
3663+# This program is free software; you can redistribute it and/or modify
3664+# it under the terms of the GNU Lesser General Public License as published by
3665+# the Free Software Foundation; version 3.
3666+#
3667+# This program is distributed in the hope that it will be useful,
3668+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3669+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3670+# GNU Lesser General Public License for more details.
3671+#
3672+# You should have received a copy of the GNU Lesser General Public License
3673+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3674+
3675+import ubuntuuitoolkit
3676+from ubuntuuitoolkit import tests
3677+
3678+
3679+class SwipeToDeleteTestCase(tests.QMLStringAppTestCase):
3680+
3681+ test_qml = ("""
3682+import QtQuick 2.0
3683+import Ubuntu.Components 0.1
3684+import Ubuntu.Components.ListItems 0.1
3685+
3686+
3687+MainView {
3688+ width: units.gu(48)
3689+ height: units.gu(60)
3690+
3691+ Page {
3692+
3693+ ListModel {
3694+ id: testModel
3695+
3696+ ListElement {
3697+ name: "listitem_destroyed_on_remove_with_confirm"
3698+ label: "Item destroyed on remove with confirmation"
3699+ confirm: true
3700+ }
3701+ ListElement {
3702+ name: "listitem_destroyed_on_remove_without_confirm"
3703+ label: "Item destroyed on remove without confirmation"
3704+ confirm: false
3705+ }
3706+ }
3707+
3708+ Column {
3709+ anchors { fill: parent }
3710+
3711+ Standard {
3712+ objectName: "listitem_standard"
3713+ confirmRemoval: true
3714+ removable: true
3715+ text: 'Slide to remove'
3716+ }
3717+
3718+ Empty {
3719+ objectName: "listitem_empty"
3720+ }
3721+
3722+ Standard {
3723+ objectName: "listitem_without_confirm"
3724+ confirmRemoval: false
3725+ removable: true
3726+ text: "Item without delete confirmation"
3727+ }
3728+
3729+ ListView {
3730+ anchors { left: parent.left; right: parent.right }
3731+ height: childrenRect.height
3732+ model: testModel
3733+
3734+ delegate: Standard {
3735+ removable: true
3736+ confirmRemoval: confirm
3737+ onItemRemoved: testModel.remove(index)
3738+ text: label
3739+ objectName: name
3740+ }
3741+ }
3742+ }
3743+ }
3744+}
3745+""")
3746+
3747+ def setUp(self):
3748+ super(SwipeToDeleteTestCase, self).setUp()
3749+ self._item = self.main_view.select_single(
3750+ ubuntuuitoolkit.Standard, objectName='listitem_standard')
3751+ self.assertTrue(self._item.exists())
3752+
3753+ def test_supported_class(self):
3754+ self.assertTrue(issubclass(
3755+ ubuntuuitoolkit.Base, ubuntuuitoolkit.Empty))
3756+ self.assertTrue(issubclass(
3757+ ubuntuuitoolkit.ItemSelector, ubuntuuitoolkit.Empty))
3758+ self.assertTrue(issubclass(
3759+ ubuntuuitoolkit.Standard, ubuntuuitoolkit.Empty))
3760+ self.assertTrue(issubclass(
3761+ ubuntuuitoolkit.SingleControl, ubuntuuitoolkit.Empty))
3762+ self.assertTrue(issubclass(
3763+ ubuntuuitoolkit.MultiValue, ubuntuuitoolkit.Base))
3764+ self.assertTrue(issubclass(
3765+ ubuntuuitoolkit.SingleValue, ubuntuuitoolkit.Base))
3766+ self.assertTrue(issubclass(
3767+ ubuntuuitoolkit.Subtitled, ubuntuuitoolkit.Base))
3768+
3769+ def test_standard_custom_proxy_object(self):
3770+ self.assertIsInstance(self._item, ubuntuuitoolkit.Standard)
3771+
3772+ def test_swipe_item(self):
3773+ self._item.swipe_to_delete()
3774+ self.assertTrue(self._item.waitingConfirmationForRemoval)
3775+
3776+ def test_swipe_item_to_right(self):
3777+ self._item.swipe_to_delete('right')
3778+ self.assertTrue(self._item.waitingConfirmationForRemoval)
3779+
3780+ def test_swipe_item_to_left(self):
3781+ self._item.swipe_to_delete('left')
3782+ self.assertTrue(self._item.waitingConfirmationForRemoval)
3783+
3784+ def test_swipe_item_to_wrong_direction(self):
3785+ self.assertRaises(
3786+ ubuntuuitoolkit.ToolkitException,
3787+ self._item.swipe_to_delete, 'up')
3788+
3789+ def test_delete_item_moving_right(self):
3790+ self._item.swipe_to_delete('right')
3791+ self._item.confirm_removal()
3792+ self.assertFalse(self._item.exists())
3793+
3794+ def test_delete_item_moving_left(self):
3795+ self._item.swipe_to_delete('left')
3796+ self._item.confirm_removal()
3797+ self.assertFalse(self._item.exists())
3798+
3799+ def test_delete_non_removable_item(self):
3800+ self._item = self.main_view.select_single(
3801+ ubuntuuitoolkit.Empty, objectName='listitem_empty')
3802+ self.assertRaises(
3803+ ubuntuuitoolkit.ToolkitException, self._item.swipe_to_delete)
3804+
3805+ def test_confirm_removal_when_item_was_not_swiped(self):
3806+ self.assertRaises(
3807+ ubuntuuitoolkit.ToolkitException, self._item.confirm_removal)
3808+
3809+ def test_delete_item_without_confirm(self):
3810+ item = self.main_view.select_single(
3811+ ubuntuuitoolkit.Standard, objectName='listitem_without_confirm')
3812+ item.swipe_to_delete()
3813+ self.assertFalse(item.exists())
3814+
3815+ def test_delete_item_with_confirmation_that_will_be_destroyed(self):
3816+ item = self.main_view.select_single(
3817+ ubuntuuitoolkit.Standard,
3818+ objectName='listitem_destroyed_on_remove_with_confirm')
3819+ item.swipe_to_delete()
3820+ item.confirm_removal()
3821+ self.assertFalse(item.exists())
3822+
3823+ def test_delete_item_without_confirmation_that_will_be_destroyed(self):
3824+ item = self.main_view.select_single(
3825+ ubuntuuitoolkit.Standard,
3826+ objectName='listitem_destroyed_on_remove_without_confirm')
3827+ item.swipe_to_delete()
3828+ self.assertFalse(item.exists())
3829
3830=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_main_view.py'
3831--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_main_view.py 1970-01-01 00:00:00 +0000
3832+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_main_view.py 2014-04-17 01:29:24 +0000
3833@@ -0,0 +1,132 @@
3834+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3835+#
3836+# Copyright (C) 2013, 2014 Canonical Ltd.
3837+#
3838+# This program is free software; you can redistribute it and/or modify
3839+# it under the terms of the GNU Lesser General Public License as published by
3840+# the Free Software Foundation; version 3.
3841+#
3842+# This program is distributed in the hope that it will be useful,
3843+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3844+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3845+# GNU Lesser General Public License for more details.
3846+#
3847+# You should have received a copy of the GNU Lesser General Public License
3848+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3849+
3850+try:
3851+ from unittest import mock
3852+except ImportError:
3853+ import mock
3854+
3855+import ubuntuuitoolkit
3856+from ubuntuuitoolkit import tests
3857+
3858+
3859+class MainViewTestCase(tests.QMLStringAppTestCase):
3860+
3861+ test_qml = ("""
3862+import QtQuick 2.0
3863+import Ubuntu.Components 0.1
3864+
3865+MainView {
3866+ width: units.gu(48)
3867+ height: units.gu(60)
3868+}
3869+""")
3870+
3871+ def test_main_view_custom_proxy_object(self):
3872+ self.assertIsInstance(self.main_view, ubuntuuitoolkit.MainView)
3873+
3874+ def test_get_header_without_header(self):
3875+ header = self.main_view.get_header()
3876+ self.assertFalse(header.visible)
3877+
3878+ def test_toolbar_custom_proxy_object(self):
3879+ toolbar = self.main_view.get_toolbar()
3880+ self.assertIsInstance(toolbar, ubuntuuitoolkit.Toolbar)
3881+
3882+ def test_open_toolbar(self):
3883+ with mock.patch.object(ubuntuuitoolkit.Toolbar, 'open') as mock_open:
3884+ self.main_view.open_toolbar()
3885+
3886+ mock_open.assert_called_once_with()
3887+
3888+ def test_close_toolbar(self):
3889+ with mock.patch.object(ubuntuuitoolkit.Toolbar, 'close') as mock_close:
3890+ self.main_view.close_toolbar()
3891+
3892+ mock_close.assert_called_once_with()
3893+
3894+ def test_open_toolbar_returns_the_toolbar(self):
3895+ toolbar = self.main_view.open_toolbar()
3896+ self.assertIsInstance(toolbar, ubuntuuitoolkit.Toolbar)
3897+
3898+ def test_get_tabs_without_tabs(self):
3899+ error = self.assertRaises(
3900+ ubuntuuitoolkit.ToolkitException, self.main_view.get_tabs)
3901+ self.assertEqual(
3902+ str(error), 'The MainView has no Tabs.')
3903+
3904+ def test_switch_to_next_tab_without_tabs(self):
3905+ header = self.main_view.get_header()
3906+ error = self.assertRaises(
3907+ ubuntuuitoolkit.ToolkitException,
3908+ header.switch_to_next_tab)
3909+ self.assertEqual(
3910+ str(error), 'The MainView has no Tabs.')
3911+
3912+
3913+class GoBackTestCase(tests.QMLStringAppTestCase):
3914+
3915+ test_qml = ("""
3916+import QtQuick 2.0
3917+import Ubuntu.Components 0.1
3918+
3919+MainView {
3920+ width: units.gu(48)
3921+ height: units.gu(60)
3922+
3923+ PageStack {
3924+ id: pageStack
3925+ Component.onCompleted: push(page0)
3926+
3927+ Page {
3928+ id: page0
3929+ title: "Page 0"
3930+ visible: false
3931+
3932+ Button {
3933+ objectName: "go_to_page1"
3934+ text: "Go to page 1"
3935+ onClicked: pageStack.push(page1)
3936+ }
3937+ }
3938+
3939+ Page {
3940+ id: page1
3941+ title: "Page 1"
3942+ visible: false
3943+ }
3944+ }
3945+}
3946+""")
3947+
3948+ def setUp(self):
3949+ super(GoBackTestCase, self).setUp()
3950+ self.header = self.main_view.get_header()
3951+ self.assertEqual(self.header.title, 'Page 0')
3952+
3953+ def test_open_page(self):
3954+ self._go_to_page1()
3955+ self.assertEqual(self.header.title, 'Page 1')
3956+
3957+ def _go_to_page1(self):
3958+ button = self.main_view.select_single(
3959+ 'Button', objectName='go_to_page1')
3960+ self.pointing_device.click_object(button)
3961+
3962+ def test_go_back(self):
3963+ self._go_to_page1()
3964+ self.main_view.go_back()
3965+ self.assertEqual(self.header.title, 'Page 0')
3966
3967=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_popups.py'
3968--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_popups.py 1970-01-01 00:00:00 +0000
3969+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_popups.py 2014-04-17 01:29:24 +0000
3970@@ -0,0 +1,172 @@
3971+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3972+#
3973+# Copyright (C) 2013, 2014 Canonical Ltd.
3974+#
3975+# This program is free software; you can redistribute it and/or modify
3976+# it under the terms of the GNU Lesser General Public License as published by
3977+# the Free Software Foundation; version 3.
3978+#
3979+# This program is distributed in the hope that it will be useful,
3980+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3981+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3982+# GNU Lesser General Public License for more details.
3983+#
3984+# You should have received a copy of the GNU Lesser General Public License
3985+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3986+
3987+from autopilot.introspection import dbus
3988+
3989+import ubuntuuitoolkit
3990+from ubuntuuitoolkit import tests
3991+
3992+
3993+class ActionSelectionPopoverTestCase(tests.QMLStringAppTestCase):
3994+
3995+ test_qml = ("""
3996+import QtQuick 2.0
3997+import Ubuntu.Components 0.1
3998+import Ubuntu.Components.Popups 0.1
3999+
4000+MainView {
4001+ width: units.gu(48)
4002+ height: units.gu(60)
4003+
4004+ Button {
4005+ objectName: "open_popover"
4006+ text: "Open Popover"
4007+ onClicked: testActionsPopover.show();
4008+ }
4009+
4010+ Label {
4011+ id: "label"
4012+ objectName: "clicked_label"
4013+ anchors.centerIn: parent
4014+ text: "Button not clicked."
4015+ }
4016+
4017+ ActionSelectionPopover {
4018+ objectName: "test_actions_popover"
4019+ id: testActionsPopover
4020+ actions: ActionList {
4021+ Action {
4022+ text: "Action one"
4023+ onTriggered: label.text = "Button clicked."
4024+ }
4025+ }
4026+ }
4027+}
4028+""")
4029+
4030+ def test_action_selection_popover_custom_proxy_object(self):
4031+ popover = self.main_view.get_action_selection_popover(
4032+ 'test_actions_popover')
4033+ self.assertIsInstance(popover, ubuntuuitoolkit.ActionSelectionPopover)
4034+
4035+ def test_click_action_select_popover_button(self):
4036+ label = self.app.select_single('Label', objectName='clicked_label')
4037+ self.assertNotEqual(label.text, 'Button clicked.')
4038+ self._open_popover()
4039+ popover = self.main_view.get_action_selection_popover(
4040+ 'test_actions_popover')
4041+ popover.click_button_by_text('Action one')
4042+ self.assertEqual(label.text, 'Button clicked.')
4043+
4044+ def _open_popover(self):
4045+ open_button = self.main_view.select_single(
4046+ 'Button', objectName='open_popover')
4047+ self.pointing_device.click_object(open_button)
4048+
4049+ def test_click_unexisting_button(self):
4050+ self._open_popover()
4051+ popover = self.main_view.get_action_selection_popover(
4052+ 'test_actions_popover')
4053+ error = self.assertRaises(
4054+ ubuntuuitoolkit.ToolkitException, popover.click_button_by_text,
4055+ 'unexisting')
4056+ self.assertEqual(
4057+ str(error), 'Button with text "unexisting" not found.')
4058+
4059+ def test_click_button_with_closed_popover(self):
4060+ popover = self.main_view.get_action_selection_popover(
4061+ 'test_actions_popover')
4062+ error = self.assertRaises(
4063+ ubuntuuitoolkit.ToolkitException, popover.click_button_by_text,
4064+ 'Action one')
4065+ self.assertEqual(
4066+ str(error), 'The popover is not open.')
4067+
4068+
4069+class ComposerSheetTestCase(tests.QMLStringAppTestCase):
4070+
4071+ test_qml = ("""
4072+import QtQuick 2.0
4073+import Ubuntu.Components 0.1
4074+import Ubuntu.Components.Popups 0.1
4075+
4076+MainView {
4077+ width: units.gu(48)
4078+ height: units.gu(60)
4079+
4080+ Button {
4081+ objectName: "openComposerSheetButton"
4082+ text: "Open Composer Sheet"
4083+ onClicked: PopupUtils.open(testComposerSheet);
4084+ }
4085+
4086+ Label {
4087+ id: "label"
4088+ objectName: "actionLabel"
4089+ anchors.centerIn: parent
4090+ text: "No action taken."
4091+ }
4092+
4093+ Component {
4094+ id: testComposerSheet
4095+ ComposerSheet {
4096+ id: sheet
4097+ objectName: "testComposerSheet"
4098+ onCancelClicked: {
4099+ label.text = "Cancel selected."
4100+ }
4101+ onConfirmClicked: {
4102+ label.text = "Confirm selected."
4103+ }
4104+ }
4105+ }
4106+}
4107+""")
4108+
4109+ def setUp(self):
4110+ super(ComposerSheetTestCase, self).setUp()
4111+ self.label = self.main_view.select_single(
4112+ 'Label', objectName='actionLabel')
4113+ self.assertEqual(self.label.text, 'No action taken.')
4114+ self._open_composer_sheet()
4115+ self.composer_sheet = self._select_composer_sheet()
4116+
4117+ def _open_composer_sheet(self):
4118+ button = self.main_view.select_single(
4119+ 'Button', objectName='openComposerSheetButton')
4120+ self.pointing_device.click_object(button)
4121+
4122+ def _select_composer_sheet(self):
4123+ return self.main_view.select_single(
4124+ ubuntuuitoolkit.ComposerSheet, objectName='testComposerSheet')
4125+
4126+ def test_select_composer_sheet_custom_proxy_object(self):
4127+ self.assertIsInstance(
4128+ self.composer_sheet, ubuntuuitoolkit.ComposerSheet)
4129+
4130+ def test_confirm_composer_sheet(self):
4131+ self.composer_sheet.confirm()
4132+ self.assertEqual(self.label.text, 'Confirm selected.')
4133+ self._assert_composer_sheet_is_closed()
4134+
4135+ def _assert_composer_sheet_is_closed(self):
4136+ self.assertRaises(
4137+ dbus.StateNotFoundError, self._select_composer_sheet)
4138+
4139+ def test_cancel_composer_sheet(self):
4140+ self.composer_sheet.cancel()
4141+ self.assertEqual(self.label.text, 'Cancel selected.')
4142+ self._assert_composer_sheet_is_closed()
4143
4144=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_qquicklistview.py'
4145--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_qquicklistview.py 1970-01-01 00:00:00 +0000
4146+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_qquicklistview.py 2014-04-17 01:29:24 +0000
4147@@ -0,0 +1,193 @@
4148+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4149+#
4150+# Copyright (C) 2013, 2014 Canonical Ltd.
4151+#
4152+# This program is free software; you can redistribute it and/or modify
4153+# it under the terms of the GNU Lesser General Public License as published by
4154+# the Free Software Foundation; version 3.
4155+#
4156+# This program is distributed in the hope that it will be useful,
4157+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4158+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4159+# GNU Lesser General Public License for more details.
4160+#
4161+# You should have received a copy of the GNU Lesser General Public License
4162+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4163+
4164+from autopilot.introspection import dbus
4165+
4166+import ubuntuuitoolkit
4167+from ubuntuuitoolkit import tests
4168+
4169+
4170+class QQuickListViewTestCase(tests.QMLStringAppTestCase):
4171+
4172+ test_qml = ("""
4173+import QtQuick 2.0
4174+import Ubuntu.Components 0.1
4175+import Ubuntu.Components.ListItems 0.1 as ListItem
4176+
4177+MainView {
4178+ width: units.gu(48)
4179+ height: units.gu(20)
4180+
4181+ Page {
4182+
4183+ Column {
4184+ id: column
4185+ width: units.gu(48)
4186+ height: units.gu(20)
4187+
4188+ Label {
4189+ id: clickedLabel
4190+ objectName: "clickedLabel"
4191+ text: "No element clicked."
4192+ }
4193+
4194+ ListView {
4195+ id: testListView
4196+ objectName: "testListView"
4197+ anchors.left: parent.left
4198+ anchors.right: parent.right
4199+ height: column.height - clickedLabel.paintedHeight
4200+ clip: true
4201+ model: 20
4202+
4203+ delegate: ListItem.Standard {
4204+ objectName: "testListElement%1".arg(index)
4205+ text: "test list element %1".arg(index)
4206+ onClicked: clickedLabel.text = objectName
4207+ height: units.gu(5)
4208+ }
4209+ }
4210+ }
4211+ }
4212+}
4213+""")
4214+
4215+ def setUp(self):
4216+ super(QQuickListViewTestCase, self).setUp()
4217+ self.list_view = self.main_view.select_single(
4218+ ubuntuuitoolkit.QQuickListView, objectName='testListView')
4219+ self.label = self.main_view.select_single(
4220+ 'Label', objectName='clickedLabel')
4221+ self.assertEqual(self.label.text, 'No element clicked.')
4222+
4223+ def test_qquicklistview_custom_proxy_object(self):
4224+ self.assertIsInstance(self.list_view, ubuntuuitoolkit.QQuickListView)
4225+
4226+ def test_click_element(self):
4227+ self.list_view.click_element('testListElement0')
4228+ self.assertEqual(self.label.text, 'testListElement0')
4229+
4230+ def test_click_element_outside_view_below(self):
4231+ # Click the first element out of view to make sure we are not scrolling
4232+ # to the bottom at once.
4233+ self.assertFalse(
4234+ self.list_view._is_element_clickable('testListElement5'))
4235+
4236+ self.list_view.click_element('testListElement5')
4237+ self.assertEqual(self.label.text, 'testListElement5')
4238+
4239+ def test_click_element_outside_view_above(self):
4240+ self.list_view.click_element('testListElement9')
4241+
4242+ # Click the first element out of view to make sure we are not scrolling
4243+ # to the top at once.
4244+ self.assertFalse(
4245+ self.list_view._is_element_clickable('testListElement4'))
4246+
4247+ self.list_view.click_element('testListElement4')
4248+ self.assertEqual(self.label.text, 'testListElement4')
4249+
4250+ def test_click_element_not_created_at_start(self):
4251+ objectName = 'testListElement19'
4252+ self.assertRaises(
4253+ dbus.StateNotFoundError,
4254+ self.list_view.select_single,
4255+ objectName=objectName)
4256+ self.list_view.click_element(objectName)
4257+
4258+ def test_click_unexisting_element(self):
4259+ error = self.assertRaises(
4260+ ubuntuuitoolkit.ToolkitException,
4261+ self.list_view.click_element,
4262+ 'unexisting')
4263+ self.assertEqual(
4264+ str(error), 'List element with objectName "unexisting" not found.')
4265+
4266+
4267+class QQuickListViewOutOfViewTestCase(tests.QMLStringAppTestCase):
4268+ """Test that we can make elements visible when the list is out of view."""
4269+
4270+ test_qml = ("""
4271+import QtQuick 2.0
4272+import Ubuntu.Components 0.1
4273+import Ubuntu.Components.ListItems 0.1 as ListItem
4274+
4275+MainView {
4276+ width: units.gu(48)
4277+ height: units.gu(20)
4278+
4279+ Page {
4280+
4281+ Flickable {
4282+
4283+ Column {
4284+ id: column
4285+ width: units.gu(48)
4286+ // The column height is greater than the main view height, so
4287+ // the bottom of the list is out of view.
4288+ height: units.gu(40)
4289+
4290+ Label {
4291+ id: clickedLabel
4292+ objectName: "clickedLabel"
4293+ text: "No element clicked."
4294+ }
4295+
4296+ ListView {
4297+ id: testListView
4298+ objectName: "testListView"
4299+ anchors.left: parent.left
4300+ anchors.right: parent.right
4301+ height: column.height - clickedLabel.paintedHeight
4302+ clip: true
4303+ model: 20
4304+
4305+ delegate: ListItem.Standard {
4306+ objectName: "testListElement%1".arg(index)
4307+ text: "test list element %1".arg(index)
4308+ onClicked: clickedLabel.text = objectName
4309+ height: units.gu(5)
4310+ }
4311+ }
4312+ }
4313+ }
4314+ }
4315+}
4316+""")
4317+
4318+ def setUp(self):
4319+ super(QQuickListViewOutOfViewTestCase, self).setUp()
4320+ self.list_view = self.main_view.select_single(
4321+ ubuntuuitoolkit.QQuickListView, objectName='testListView')
4322+ self.label = self.main_view.select_single(
4323+ 'Label', objectName='clickedLabel')
4324+ self.assertEqual(self.label.text, 'No element clicked.')
4325+
4326+ def test_click_element_outside_view_below(self):
4327+ """Test that we can click an element that's out of view below.
4328+
4329+ The list is also out of view, so we must scroll from the bottom of the
4330+ main view.
4331+
4332+ """
4333+ # Test for http://pad.lv/1275060.
4334+ # Click the first element out of view to make sure we are not scrolling
4335+ # to the bottom at once.
4336+ self.assertFalse(
4337+ self.list_view._is_element_clickable('testListElement9'))
4338+
4339+ self.list_view.click_element('testListElement9')
4340+ self.assertEqual(self.label.text, 'testListElement9')
4341
4342=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py'
4343--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py 1970-01-01 00:00:00 +0000
4344+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py 2014-04-17 01:29:24 +0000
4345@@ -0,0 +1,149 @@
4346+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4347+#
4348+# Copyright (C) 2013, 2014 Canonical Ltd.
4349+#
4350+# This program is free software; you can redistribute it and/or modify
4351+# it under the terms of the GNU Lesser General Public License as published by
4352+# the Free Software Foundation; version 3.
4353+#
4354+# This program is distributed in the hope that it will be useful,
4355+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4356+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4357+# GNU Lesser General Public License for more details.
4358+#
4359+# You should have received a copy of the GNU Lesser General Public License
4360+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4361+
4362+try:
4363+ from unittest import mock
4364+except ImportError:
4365+ import mock
4366+
4367+import ubuntuuitoolkit
4368+from ubuntuuitoolkit import tests
4369+
4370+
4371+class TabsTestCase(tests.QMLStringAppTestCase):
4372+
4373+ test_qml = ("""
4374+import QtQuick 2.0
4375+import Ubuntu.Components 0.1
4376+import Ubuntu.Components.ListItems 0.1 as ListItem
4377+
4378+MainView {
4379+ width: units.gu(70)
4380+ height: units.gu(60)
4381+
4382+ Tabs {
4383+ id: tabs
4384+ Tab {
4385+ objectName: "tab1"
4386+ title: "Tab1"
4387+ Page {
4388+ tools: ToolbarItems {
4389+ ToolbarButton {
4390+ text: "Test1"
4391+ }
4392+ }
4393+ }
4394+ }
4395+ Tab {
4396+ objectName: "tab2"
4397+ title: "Tab2"
4398+ Page {
4399+ tools: ToolbarItems {
4400+ ToolbarButton {
4401+ text: "Test2"
4402+ }
4403+ }
4404+ }
4405+ }
4406+ Tab {
4407+ objectName: "tab3"
4408+ title: "Tab3"
4409+ Page {
4410+ tools: ToolbarItems {
4411+ ToolbarButton {
4412+ text: "Test3"
4413+ }
4414+ }
4415+ }
4416+ }
4417+ }
4418+}
4419+""")
4420+
4421+ def test_tabs_custom_proxy_object(self):
4422+ self.assertIsInstance(self.main_view.get_tabs(), ubuntuuitoolkit.Tabs)
4423+
4424+ def test_get_current_tab(self):
4425+ tabs = self.main_view.get_tabs()
4426+ self.assertEqual(tabs.get_current_tab().title, 'Tab1')
4427+
4428+ def test_switch_to_next_tab_from_header(self):
4429+ header = self.main_view.get_header()
4430+ header.switch_to_next_tab()
4431+ current_tab = self.main_view.get_tabs().get_current_tab()
4432+ self.assertEqual(current_tab.title, 'Tab2')
4433+
4434+ def test_switch_to_next_tab_from_main_view(self):
4435+ current_tab = self.main_view.switch_to_next_tab()
4436+ self.assertEqual(current_tab.title, 'Tab2')
4437+
4438+ def test_switch_to_next_tab_from_last(self):
4439+ last_tab_index = self.main_view.get_tabs().get_number_of_tabs() - 1
4440+ self.main_view.switch_to_tab_by_index(last_tab_index)
4441+ current_tab = self.main_view.switch_to_next_tab()
4442+ self.assertEqual(current_tab.title, 'Tab1')
4443+
4444+ def test_switch_to_tab_by_index(self):
4445+ current_tab = self.main_view.switch_to_tab_by_index(2)
4446+ self.assertEqual(current_tab.title, 'Tab3')
4447+ current_tab = self.main_view.switch_to_tab_by_index(1)
4448+ self.assertEqual(current_tab.title, 'Tab2')
4449+ current_tab = self.main_view.switch_to_tab_by_index(0)
4450+ self.assertEqual(current_tab.title, 'Tab1')
4451+
4452+ def test_switch_to_opened_tab_is_not_opened_again(self):
4453+ with mock.patch.object(
4454+ ubuntuuitoolkit.Header, 'switch_to_next_tab') as mock_switch:
4455+ current_tab = self.main_view.switch_to_tab_by_index(0)
4456+
4457+ self.assertFalse(mock_switch.called)
4458+ self.assertEqual(current_tab.title, 'Tab1')
4459+
4460+ def test_get_number_of_tabs(self):
4461+ tabs = self.main_view.get_tabs()
4462+ self.assertEqual(tabs.get_number_of_tabs(), 3)
4463+
4464+ def test_swith_to_tab_by_index_out_of_range(self):
4465+ last_tab_index = self.main_view.get_tabs().get_number_of_tabs() - 1
4466+ error = self.assertRaises(
4467+ ubuntuuitoolkit.ToolkitException,
4468+ self.main_view.switch_to_tab_by_index,
4469+ last_tab_index + 1)
4470+ self.assertEqual(str(error), 'Tab index out of range.')
4471+
4472+ def test_switch_to_previous_tab_from_first(self):
4473+ current_tab = self.main_view.switch_to_previous_tab()
4474+ self.assertEqual(current_tab.title, 'Tab3')
4475+
4476+ def test_switch_to_previous_tab_not_from_first(self):
4477+ self.main_view.switch_to_tab_by_index(1)
4478+ current_tab = self.main_view.switch_to_previous_tab()
4479+ self.assertEqual(current_tab.title, 'Tab1')
4480+
4481+ def test_switch_to_tab_by_object_name(self):
4482+ current_tab = self.main_view.switch_to_tab('tab3')
4483+ self.assertEqual(current_tab.title, 'Tab3')
4484+ current_tab = self.main_view.switch_to_tab('tab2')
4485+ self.assertEqual(current_tab.title, 'Tab2')
4486+ current_tab = self.main_view.switch_to_tab('tab1')
4487+ self.assertEqual(current_tab.title, 'Tab1')
4488+
4489+ def test_switch_to_unexisting_tab(self):
4490+ error = self.assertRaises(
4491+ ubuntuuitoolkit.ToolkitException, self.main_view.switch_to_tab,
4492+ 'unexisting')
4493+ self.assertEqual(
4494+ str(error), 'Tab with objectName "unexisting" not found.')
4495
4496=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textfield.py'
4497--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textfield.py 1970-01-01 00:00:00 +0000
4498+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textfield.py 2014-04-17 01:29:24 +0000
4499@@ -0,0 +1,93 @@
4500+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4501+#
4502+# Copyright (C) 2013, 2014 Canonical Ltd.
4503+#
4504+# This program is free software; you can redistribute it and/or modify
4505+# it under the terms of the GNU Lesser General Public License as published by
4506+# the Free Software Foundation; version 3.
4507+#
4508+# This program is distributed in the hope that it will be useful,
4509+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4510+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4511+# GNU Lesser General Public License for more details.
4512+#
4513+# You should have received a copy of the GNU Lesser General Public License
4514+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4515+
4516+import ubuntuuitoolkit
4517+from ubuntuuitoolkit import tests
4518+
4519+
4520+class TextFieldTestCase(tests.QMLStringAppTestCase):
4521+
4522+ test_qml = ("""
4523+import QtQuick 2.0
4524+import Ubuntu.Components 0.1
4525+
4526+MainView {
4527+ width: units.gu(48)
4528+ height: units.gu(60)
4529+
4530+ Item {
4531+ TextField {
4532+ id: simpleTextField
4533+ objectName: "simple_text_field"
4534+ }
4535+ TextField {
4536+ id: textFieldWithoutClearButton
4537+ objectName: "text_field_without_clear_button"
4538+ hasClearButton: false
4539+ anchors.top: simpleTextField.bottom
4540+ }
4541+ }
4542+}
4543+""")
4544+
4545+ def setUp(self):
4546+ super(TextFieldTestCase, self).setUp()
4547+ self.simple_text_field = self.main_view.select_single(
4548+ ubuntuuitoolkit.TextField, objectName='simple_text_field')
4549+
4550+ def test_text_field_custom_proxy_object(self):
4551+ self.assertIsInstance(
4552+ self.simple_text_field, ubuntuuitoolkit.TextField)
4553+
4554+ def test_write(self):
4555+ self.simple_text_field.write('test')
4556+ self.assertEqual(self.simple_text_field.text, 'test')
4557+
4558+ def test_clear_with_clear_button(self):
4559+ self.simple_text_field.write('test')
4560+ self.simple_text_field.clear()
4561+ self.assertEqual(self.simple_text_field.text, '')
4562+
4563+ def test_clear_without_clear_button(self):
4564+ text_field = self.main_view.select_single(
4565+ ubuntuuitoolkit.TextField,
4566+ objectName='text_field_without_clear_button')
4567+ text_field.write('test')
4568+ text_field.clear()
4569+ self.assertEqual(text_field.text, '')
4570+
4571+ def test_clear_and_write(self):
4572+ self.simple_text_field.write('test1')
4573+ self.simple_text_field.write('test2')
4574+ self.assertEqual(self.simple_text_field.text, 'test2')
4575+
4576+ def test_write_without_clear(self):
4577+ self.simple_text_field.write('test1')
4578+ self.simple_text_field.write('test2', clear=False)
4579+ self.assertEqual(self.simple_text_field.text, 'test1test2')
4580+
4581+ def test_write_without_clear_writes_at_the_end(self):
4582+ self.simple_text_field.write(
4583+ 'long text that will fill more than half of the text field.')
4584+ self.simple_text_field.write('append', clear=False)
4585+ self.assertEqual(
4586+ self.simple_text_field.text,
4587+ 'long text that will fill more than half of the text field.append')
4588+
4589+ def test_is_empty(self):
4590+ self.assertTrue(self.simple_text_field.is_empty())
4591+ self.simple_text_field.write('test')
4592+ self.assertFalse(self.simple_text_field.is_empty())
4593
4594=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_toolbar.py'
4595--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_toolbar.py 1970-01-01 00:00:00 +0000
4596+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_toolbar.py 2014-04-17 01:29:24 +0000
4597@@ -0,0 +1,121 @@
4598+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4599+#
4600+# Copyright (C) 2013, 2014 Canonical Ltd.
4601+#
4602+# This program is free software; you can redistribute it and/or modify
4603+# it under the terms of the GNU Lesser General Public License as published by
4604+# the Free Software Foundation; version 3.
4605+#
4606+# This program is distributed in the hope that it will be useful,
4607+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4608+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4609+# GNU Lesser General Public License for more details.
4610+#
4611+# You should have received a copy of the GNU Lesser General Public License
4612+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4613+
4614+try:
4615+ from unittest import mock
4616+except ImportError:
4617+ import mock
4618+
4619+import ubuntuuitoolkit
4620+from ubuntuuitoolkit import tests
4621+
4622+
4623+class ToolbarTestCase(tests.QMLStringAppTestCase):
4624+
4625+ test_qml = ("""
4626+import QtQuick 2.0
4627+import Ubuntu.Components 0.1
4628+
4629+MainView {
4630+ width: units.gu(50)
4631+ height: units.gu(50)
4632+
4633+ // Make sure that for these tests the toolbar starts closed.
4634+ Component.onCompleted: {
4635+ __propagated.toolbar.close();
4636+ }
4637+
4638+ Page {
4639+
4640+ Label {
4641+ id: "label"
4642+ objectName: "clicked_label"
4643+ anchors.centerIn: parent
4644+ text: "Button not clicked."
4645+ }
4646+
4647+ tools: ToolbarItems {
4648+ ToolbarButton {
4649+ objectName: "buttonName"
4650+ action: Action {
4651+ text: "buttonText"
4652+ onTriggered: label.text = "Button clicked."
4653+ }
4654+ }
4655+ }
4656+ }
4657+}
4658+""")
4659+
4660+ def setUp(self):
4661+ super(ToolbarTestCase, self).setUp()
4662+ self.toolbar = self.main_view.get_toolbar()
4663+ # toolbar may be opened or closed now, depending on whether
4664+ # the application has been deactivated and resumed already
4665+
4666+ def test_open_toolbar(self):
4667+ self.toolbar.open()
4668+ self.assertTrue(self.toolbar.opened)
4669+ self.assertFalse(self.toolbar.animating)
4670+
4671+ def test_opened_toolbar_is_not_opened_again(self):
4672+ self.toolbar.open()
4673+ with mock.patch.object(
4674+ self.main_view.pointing_device, 'drag') as mock_drag:
4675+ self.toolbar.open()
4676+
4677+ self.assertFalse(mock_drag.called)
4678+ self.assertTrue(self.toolbar.opened)
4679+
4680+ def test_close_toolbar(self):
4681+ self.toolbar.open()
4682+ self.toolbar.close()
4683+ self.assertFalse(self.toolbar.opened)
4684+ self.assertFalse(self.toolbar.animating)
4685+
4686+ def test_closed_toolbar_is_not_closed_again(self):
4687+ self.toolbar.close()
4688+ with mock.patch.object(
4689+ self.main_view.pointing_device, 'drag') as mock_drag:
4690+ self.toolbar.close()
4691+
4692+ self.assertFalse(mock_drag.called)
4693+ self.assertFalse(self.toolbar.opened)
4694+
4695+ def test_click_toolbar_button(self):
4696+ self.toolbar.close()
4697+ label = self.app.select_single('Label', objectName='clicked_label')
4698+ self.assertNotEqual(label.text, 'Button clicked.')
4699+ self.toolbar.open()
4700+ self.toolbar.click_button('buttonName')
4701+ self.assertEqual(label.text, 'Button clicked.')
4702+
4703+ def test_click_unexisting_button(self):
4704+ self.main_view.open_toolbar()
4705+ error = self.assertRaises(
4706+ ubuntuuitoolkit.ToolkitException, self.toolbar.click_button,
4707+ 'unexisting')
4708+ self.assertEqual(
4709+ str(error), 'Button with objectName "unexisting" not found.')
4710+
4711+ def test_click_button_on_closed_toolbar(self):
4712+ self.toolbar.close()
4713+ error = self.assertRaises(
4714+ ubuntuuitoolkit.ToolkitException, self.toolbar.click_button,
4715+ 'buttonName')
4716+ self.assertEqual(
4717+ str(error),
4718+ 'Toolbar must be opened before calling click_button().')
4719
4720=== removed file 'tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py'
4721--- tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2014-03-22 06:53:24 +0000
4722+++ tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 1970-01-01 00:00:00 +0000
4723@@ -1,1081 +0,0 @@
4724-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4725-#
4726-# Copyright (C) 2013 Canonical Ltd.
4727-#
4728-# This program is free software; you can redistribute it and/or modify
4729-# it under the terms of the GNU Lesser General Public License as published by
4730-# the Free Software Foundation; version 3.
4731-#
4732-# This program is distributed in the hope that it will be useful,
4733-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4734-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4735-# GNU Lesser General Public License for more details.
4736-#
4737-# You should have received a copy of the GNU Lesser General Public License
4738-# along with this program. If not, see <http://www.gnu.org/licenses/>.
4739-
4740-import time
4741-import unittest
4742-
4743-import autopilot
4744-from autopilot import input, platform
4745-from autopilot.introspection import dbus
4746-from testtools.matchers import GreaterThan, LessThan
4747-try:
4748- from unittest import mock
4749-except ImportError:
4750- import mock
4751-
4752-from ubuntuuitoolkit import emulators, tests
4753-
4754-
4755-class CheckAutopilotVersionTestCase(unittest.TestCase):
4756-
4757- def test_lower_version_should_raise_exception(self):
4758- with mock.patch.object(autopilot, 'version', '1.3'):
4759- self.assertRaises(
4760- emulators.ToolkitEmulatorException,
4761- emulators.check_autopilot_version)
4762-
4763- def test_required_version_should_succeed(self):
4764- with mock.patch.object(autopilot, 'version', '1.4'):
4765- emulators.check_autopilot_version()
4766-
4767- def test_higher_version_should_succeed(self):
4768- with mock.patch.object(autopilot, 'version', '1.5'):
4769- emulators.check_autopilot_version()
4770-
4771-
4772-class UbuntuUIToolkitEmulatorBaseTestCase(tests.QMLStringAppTestCase):
4773-
4774- def test_pointing_device(self):
4775- self.assertIsInstance(self.app.pointing_device, input.Pointer)
4776-
4777- @unittest.skipIf(platform.model() != 'Desktop', 'Desktop only')
4778- def test_pointing_device_in_desktop(self):
4779- self.assertIsInstance(self.app.pointing_device._device, input.Mouse)
4780-
4781- @unittest.skipIf(platform.model() == 'Desktop', 'Phablet only')
4782- def test_pointing_device_in_phablet(self):
4783- self.assertIsInstance(self.app.pointing_device._device, input.Touch)
4784-
4785- def test_emulators_should_check_version_on_init(self):
4786- check_name = 'ubuntuuitoolkit.emulators.check_autopilot_version'
4787- with mock.patch(check_name, autospec=True) as mock_check:
4788- # Instantiate any emulator.
4789- self.main_view
4790-
4791- mock_check.assert_called_once_with()
4792-
4793-
4794-class MainViewTestCase(tests.QMLStringAppTestCase):
4795-
4796- test_qml = ("""
4797-import QtQuick 2.0
4798-import Ubuntu.Components 0.1
4799-
4800-MainView {
4801- width: units.gu(48)
4802- height: units.gu(60)
4803-}
4804-""")
4805-
4806- def test_main_view_custom_emulator(self):
4807- self.assertIsInstance(self.main_view, emulators.MainView)
4808-
4809- def test_get_header_without_header(self):
4810- header = self.main_view.get_header()
4811- self.assertFalse(header.visible)
4812-
4813- def test_toolbar_custom_emulator(self):
4814- toolbar = self.main_view.get_toolbar()
4815- self.assertIsInstance(toolbar, emulators.Toolbar)
4816-
4817- def test_open_toolbar(self):
4818- with mock.patch.object(emulators.Toolbar, 'open') as mock_open:
4819- self.main_view.open_toolbar()
4820-
4821- mock_open.assert_called_once_with()
4822-
4823- def test_close_toolbar(self):
4824- with mock.patch.object(emulators.Toolbar, 'close') as mock_close:
4825- self.main_view.close_toolbar()
4826-
4827- mock_close.assert_called_once_with()
4828-
4829- def test_open_toolbar_returns_the_toolbar(self):
4830- toolbar = self.main_view.open_toolbar()
4831- self.assertIsInstance(toolbar, emulators.Toolbar)
4832-
4833- def test_get_tabs_without_tabs(self):
4834- error = self.assertRaises(
4835- emulators.ToolkitEmulatorException, self.main_view.get_tabs)
4836- self.assertEqual(
4837- str(error), 'The MainView has no Tabs.')
4838-
4839- def test_switch_to_next_tab_without_tabs(self):
4840- header = self.main_view.get_header()
4841- error = self.assertRaises(
4842- emulators.ToolkitEmulatorException, header.switch_to_next_tab)
4843- self.assertEqual(
4844- str(error), 'The MainView has no Tabs.')
4845-
4846-
4847-class PageTestCase(tests.QMLStringAppTestCase):
4848-
4849- test_qml = ("""
4850-import QtQuick 2.0
4851-import Ubuntu.Components 0.1
4852-
4853-MainView {
4854- width: units.gu(48)
4855- height: units.gu(60)
4856-
4857- Page {
4858- title: "Test title"
4859- }
4860-}
4861-""")
4862-
4863- def test_header_custom_emulator(self):
4864- header = self.main_view.get_header()
4865- self.assertIsInstance(header, emulators.Header)
4866- self.assertTrue(header.visible)
4867- self.assertEqual(header.title, "Test title")
4868-
4869-
4870-class ToolbarTestCase(tests.QMLStringAppTestCase):
4871-
4872- test_qml = ("""
4873-import QtQuick 2.0
4874-import Ubuntu.Components 0.1
4875-
4876-MainView {
4877- width: units.gu(50)
4878- height: units.gu(50)
4879-
4880- // Make sure that for these tests the toolbar starts closed.
4881- Component.onCompleted: {
4882- __propagated.toolbar.close();
4883- }
4884-
4885- Page {
4886-
4887- Label {
4888- id: "label"
4889- objectName: "clicked_label"
4890- anchors.centerIn: parent
4891- text: "Button not clicked."
4892- }
4893-
4894- tools: ToolbarItems {
4895- ToolbarButton {
4896- objectName: "buttonName"
4897- action: Action {
4898- text: "buttonText"
4899- onTriggered: label.text = "Button clicked."
4900- }
4901- }
4902- }
4903- }
4904-}
4905-""")
4906-
4907- def setUp(self):
4908- super(ToolbarTestCase, self).setUp()
4909- self.toolbar = self.main_view.get_toolbar()
4910- # toolbar may be opened or closed now, depending on whether
4911- # the application has been deactivated and resumed already
4912-
4913- def test_open_toolbar(self):
4914- self.toolbar.open()
4915- self.assertTrue(self.toolbar.opened)
4916- self.assertFalse(self.toolbar.animating)
4917-
4918- def test_opened_toolbar_is_not_opened_again(self):
4919- self.toolbar.open()
4920- with mock.patch.object(
4921- self.main_view.pointing_device, 'drag') as mock_drag:
4922- self.toolbar.open()
4923-
4924- self.assertFalse(mock_drag.called)
4925- self.assertTrue(self.toolbar.opened)
4926-
4927- def test_close_toolbar(self):
4928- self.toolbar.open()
4929- self.toolbar.close()
4930- self.assertFalse(self.toolbar.opened)
4931- self.assertFalse(self.toolbar.animating)
4932-
4933- def test_closed_toolbar_is_not_closed_again(self):
4934- self.toolbar.close()
4935- with mock.patch.object(
4936- self.main_view.pointing_device, 'drag') as mock_drag:
4937- self.toolbar.close()
4938-
4939- self.assertFalse(mock_drag.called)
4940- self.assertFalse(self.toolbar.opened)
4941-
4942- def test_click_toolbar_button(self):
4943- self.toolbar.close()
4944- label = self.app.select_single('Label', objectName='clicked_label')
4945- self.assertNotEqual(label.text, 'Button clicked.')
4946- self.toolbar.open()
4947- self.toolbar.click_button('buttonName')
4948- self.assertEqual(label.text, 'Button clicked.')
4949-
4950- def test_click_unexisting_button(self):
4951- self.main_view.open_toolbar()
4952- error = self.assertRaises(
4953- emulators.ToolkitEmulatorException, self.toolbar.click_button,
4954- 'unexisting')
4955- self.assertEqual(
4956- str(error), 'Button with objectName "unexisting" not found.')
4957-
4958- def test_click_button_on_closed_toolbar(self):
4959- self.toolbar.close()
4960- error = self.assertRaises(
4961- emulators.ToolkitEmulatorException, self.toolbar.click_button,
4962- 'buttonName')
4963- self.assertEqual(
4964- str(error),
4965- 'Toolbar must be opened before calling click_button().')
4966-
4967-
4968-class TabsTestCase(tests.QMLStringAppTestCase):
4969-
4970- test_qml = ("""
4971-import QtQuick 2.0
4972-import Ubuntu.Components 0.1
4973-import Ubuntu.Components.ListItems 0.1 as ListItem
4974-
4975-MainView {
4976- width: units.gu(70)
4977- height: units.gu(60)
4978-
4979- Tabs {
4980- id: tabs
4981- Tab {
4982- objectName: "tab1"
4983- title: "Tab1"
4984- Page {
4985- tools: ToolbarItems {
4986- ToolbarButton {
4987- text: "Test1"
4988- }
4989- }
4990- }
4991- }
4992- Tab {
4993- objectName: "tab2"
4994- title: "Tab2"
4995- Page {
4996- tools: ToolbarItems {
4997- ToolbarButton {
4998- text: "Test2"
4999- }
5000- }
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: