2 * Copyright 2013 Canonical Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 import Ubuntu.Components 0.1
20 import Unity.Test 0.1 as UT
27 visible: testCase.running
28 anchors.centerIn: parent
29 Component.onCompleted: parent = testCase.parent
34 // Fake implementation to be provided to items under test
35 property var fakeDateTime: new function() {
36 this.currentTimeMs = 0
37 this.getCurrentTimeMs = function() {return this.currentTimeMs}
40 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
41 function mouseClick(item, x, y, button, modifiers, delay) {
42 if (button === undefined)
43 button = Qt.LeftButton;
44 if (modifiers === undefined)
45 modifiers = Qt.NoModifier;
46 if (delay === undefined)
52 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
53 qtest_fail("window not shown", 2);
56 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
57 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
58 if (button === undefined)
59 button = Qt.LeftButton;
60 if (modifiers === undefined)
61 modifiers = Qt.NoModifier;
62 if (delay === undefined)
68 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
69 qtest_fail("window not shown", 2)
72 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
73 function mousePress(item, x, y, button, modifiers, delay) {
74 if (button === undefined)
75 button = Qt.LeftButton;
76 if (modifiers === undefined)
77 modifiers = Qt.NoModifier;
78 if (delay === undefined)
84 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
85 qtest_fail("window not shown", 2)
88 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
89 function mouseRelease(item, x, y, button, modifiers, delay) {
90 if (button === undefined)
91 button = Qt.LeftButton;
92 if (modifiers === undefined)
93 modifiers = Qt.NoModifier;
94 if (delay === undefined)
100 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
101 qtest_fail("window not shown", 2)
105 // Flickable won't recognise a single mouse move as dragging the flickable.
106 // Use 5 steps because it's what
107 // Qt uses in QQuickViewTestUtil::flick
108 // speed is in pixels/second
109 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
111 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
112 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
114 // set a default speed if not specified
115 speed = (speed != null) ? speed : units.gu(10);
117 // set a default iterations if not specified
118 iterations = (iterations !== undefined) ? iterations : 5
120 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
121 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
123 var timeStep = totalTime / iterations
124 var diffX = (toX - x) / iterations
125 var diffY = (toY - y) / iterations
127 fakeDateTime.currentTimeMs += timeStep
128 mousePress(item, x, y)
130 for (var i = 0; i < iterations; ++i) {
131 fakeDateTime.currentTimeMs += timeStep
132 if (i === iterations - 1) {
133 // Avoid any rounding errors by making the last move be at precisely
134 // the point specified
135 mouseMove(item, toX, toY, iterations / speed)
137 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
141 fakeDateTime.currentTimeMs += timeStep
142 mouseRelease(item, toX, toY)
147 // Find an object with the given name in the children tree of "obj"
148 function findChild(obj, objectName) {
149 return findChildIn(obj, "children", objectName);
152 // Find an object with the given name in the children tree of "obj"
153 // Including invisible children like animations, timers etc.
154 // Note: you should use findChild if you're not sure you need this
155 // as this tree is much bigger and might contain stuff that goes
157 function findInvisibleChild(obj, objectName) {
158 return findChildIn(obj, "data", objectName);
161 // Find a child in the named property
162 function findChildIn(obj, prop, objectName) {
163 var childs = new Array(0);
165 while (childs.length > 0) {
166 if (childs[0].objectName == objectName) {
169 for (var i in childs[0][prop]) {
170 childs.push(childs[0][prop][i])
177 function findChildsByType(obj, typeName) {
178 var res = new Array(0);
179 for (var i in obj.children) {
180 var c = obj.children[i];
181 if (UT.Util.isInstanceOf(c, typeName)) {
184 res = res.concat(findChildsByType(c, typeName));
189 // Type a full string instead of keyClick letter by letter
190 function typeString(str) {
191 for (var i = 0; i < str.length; i++) {
196 // Keeps executing a given parameter-less function until it returns the given
197 // expected result or the timemout is reached (in which case a test failure
199 function tryCompareFunction(func, expectedResult, timeout) {
201 if (timeout === undefined)
205 while (timeSpent < timeout && !success) {
206 actualResult = func()
207 success = qtest_compareInternal(actualResult, expectedResult)
208 if (success === false) {
214 var act = qtest_results.stringify(actualResult)
215 var exp = qtest_results.stringify(expectedResult)
216 if (!qtest_results.compare(success,
217 "function returned unexpected result",
219 util.callerFile(), util.callerLine())) {
220 throw new Error("QtQuickTest::fail")
224 function touchEvent() {
225 return UT.Util.touchEvent()
228 // speed is in pixels/second
229 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
230 // Make sure the item is rendered
231 waitForRendering(item);
233 var root = fetchRootItem(item);
234 var rootFrom = item.mapToItem(root, x, y);
235 var rootTo = item.mapToItem(root, toX, toY);
237 // Default to true for beginTouch if not present
238 beginTouch = (beginTouch !== undefined) ? beginTouch : true
240 // Default to true for endTouch if not present
241 endTouch = (endTouch !== undefined) ? endTouch : true
243 // Set a default speed if not specified
244 speed = (speed !== undefined) ? speed : units.gu(10)
246 // Set a default iterations if not specified
247 var iterations = (iterations !== undefined) ? iterations : 5
249 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
250 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
252 var timeStep = totalTime / iterations
253 var diffX = (rootTo.x - rootFrom.x) / iterations
254 var diffY = (rootTo.y - rootFrom.y) / iterations
256 fakeDateTime.currentTimeMs += timeStep
258 var event = touchEvent()
259 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
262 for (var i = 0; i < iterations; ++i) {
263 fakeDateTime.currentTimeMs += timeStep
264 if (i === iterations - 1) {
265 // Avoid any rounding errors by making the last move be at precisely
266 // the point specified
267 wait(iterations / speed)
268 var event = touchEvent()
269 event.move(0 /* touchId */, rootTo.x, rootTo.y)
272 wait(iterations / speed)
273 var event = touchEvent()
274 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
279 fakeDateTime.currentTimeMs += timeStep
280 var event = touchEvent()
281 event.release(0 /* touchId */, rootTo.x, rootTo.y)
286 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
287 // Make sure the item is rendered
288 waitForRendering(item);
290 var event1 = touchEvent();
292 event1.press(0, x1Start, y1Start);
295 event1.stationary(0);
296 event1.press(1, x2Start, y2Start);
300 for (var i = 0.0; i < 1.0; i += 0.02) {
301 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
302 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
307 event1.release(0, x1End, y1End);
308 event1.release(1, x2End, y2End);
312 function fetchRootItem(item) {
314 return fetchRootItem(item.parent)
319 function touchPress(item, x, y) {
320 var root = fetchRootItem(item)
321 var rootPoint = item.mapToItem(root, x, y)
323 var event = touchEvent()
324 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
328 function touchRelease(item, x, y) {
329 var root = fetchRootItem(item)
330 var rootPoint = item.mapToItem(root, x, y)
332 var event = touchEvent()
333 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
337 /*! \brief Tap the item with a touch event.
339 \param item The item to be tapped
340 \param x The x coordinate of the tap, defaults to horizontal center
341 \param y The y coordinate of the tap, defaults to vertical center
343 function tap(item, x, y) {
344 if (typeof x !== "number") x = item.width / 2;
345 if (typeof y !== "number") y = item.height / 2;
347 var root = fetchRootItem(item)
348 var rootPoint = item.mapToItem(root, x, y)
350 var event = touchEvent()
351 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
355 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
359 Component.onCompleted: {
360 UT.Util.ensureTouchRegistryInstalled();
362 var rootItem = parent;
363 while (rootItem.parent != undefined) {
364 rootItem = rootItem.parent;
366 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
370 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
371 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
372 Thus to remove a variable that qmltests cannot really control, namely time, this
373 function removes all constraints from DirectionalDragAreas that are sensible to
376 This effectively makes DirectionalDragAreas easier to fool.
378 function removeTimeConstraintsFromDirectionalDragAreas(item) {
380 // use duck-typing to identify a DirectionalDragArea
381 if (item.minSpeed != undefined
382 && item.maxSilenceTime != undefined
383 && item.compositionTime != undefined) {
385 item.maxSilenceTime = 60 * 60 * 1000;
386 item.compositionTime = 0;
388 for (var i in item.children) {
389 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
394 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
395 function waitForRendering(item, timeout) {
396 if (timeout === undefined)
399 qtest_fail("No item given to waitForRendering", 1);
400 return qtest_results.waitForRendering(item, timeout);