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
26 // This is needed for waitForRendering calls to return
27 // if the watched element already got rendered
31 parent: testCase.parent
32 border { width: units.dp(1); color: "black" }
35 RotationAnimation on rotation {
39 loops: Animation.Infinite
44 // Fake implementation to be provided to items under test
45 property var fakeDateTime: new function() {
46 this.currentTimeMs = 0
47 this.getCurrentTimeMs = function() {return this.currentTimeMs}
50 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
51 function mouseClick(item, x, y, button, modifiers, delay) {
52 if (button === undefined)
53 button = Qt.LeftButton;
54 if (modifiers === undefined)
55 modifiers = Qt.NoModifier;
56 if (delay === undefined)
62 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
63 qtest_fail("window not shown", 2);
66 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
67 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
68 if (button === undefined)
69 button = Qt.LeftButton;
70 if (modifiers === undefined)
71 modifiers = Qt.NoModifier;
72 if (delay === undefined)
78 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
79 qtest_fail("window not shown", 2)
82 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
83 function mousePress(item, x, y, button, modifiers, delay) {
84 if (button === undefined)
85 button = Qt.LeftButton;
86 if (modifiers === undefined)
87 modifiers = Qt.NoModifier;
88 if (delay === undefined)
94 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
95 qtest_fail("window not shown", 2)
98 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
99 function mouseRelease(item, x, y, button, modifiers, delay) {
100 if (button === undefined)
101 button = Qt.LeftButton;
102 if (modifiers === undefined)
103 modifiers = Qt.NoModifier;
104 if (delay === undefined)
110 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
111 qtest_fail("window not shown", 2)
115 // Flickable won't recognise a single mouse move as dragging the flickable.
116 // Use 5 steps because it's what
117 // Qt uses in QQuickViewTestUtil::flick
118 // speed is in pixels/second
119 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
121 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
122 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
124 // set a default speed if not specified
125 speed = (speed != null) ? speed : units.gu(10);
127 // set a default iterations if not specified
128 iterations = (iterations !== undefined) ? iterations : 5
130 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
131 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
133 var timeStep = totalTime / iterations
134 var diffX = (toX - x) / iterations
135 var diffY = (toY - y) / iterations
137 fakeDateTime.currentTimeMs += timeStep
138 mousePress(item, x, y)
140 for (var i = 0; i < iterations; ++i) {
141 fakeDateTime.currentTimeMs += timeStep
142 if (i === iterations - 1) {
143 // Avoid any rounding errors by making the last move be at precisely
144 // the point specified
145 mouseMove(item, toX, toY, iterations / speed)
147 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
151 fakeDateTime.currentTimeMs += timeStep
152 mouseRelease(item, toX, toY)
157 // Find an object with the given name in the children tree of "obj"
158 function findChild(obj, objectName) {
159 return findChildIn(obj, "children", objectName);
162 // Find an object with the given name in the children tree of "obj"
163 // Including invisible children like animations, timers etc.
164 // Note: you should use findChild if you're not sure you need this
165 // as this tree is much bigger and might contain stuff that goes
167 function findInvisibleChild(obj, objectName) {
168 return findChildIn(obj, "data", objectName);
171 // Find a child in the named property
172 function findChildIn(obj, prop, objectName) {
173 var childs = new Array(0);
175 while (childs.length > 0) {
176 if (childs[0].objectName == objectName) {
179 for (var i in childs[0][prop]) {
180 childs.push(childs[0][prop][i])
187 function findChildsByType(obj, typeName) {
188 var res = new Array(0);
189 for (var i in obj.children) {
190 var c = obj.children[i];
191 if (UT.Util.isInstanceOf(c, typeName)) {
194 res = res.concat(findChildsByType(c, typeName));
199 // Type a full string instead of keyClick letter by letter
200 function typeString(str) {
201 for (var i = 0; i < str.length; i++) {
206 // Keeps executing a given parameter-less function until it returns the given
207 // expected result or the timemout is reached (in which case a test failure
209 function tryCompareFunction(func, expectedResult, timeout) {
211 if (timeout === undefined)
215 while (timeSpent < timeout && !success) {
216 actualResult = func()
217 success = qtest_compareInternal(actualResult, expectedResult)
218 if (success === false) {
224 var act = qtest_results.stringify(actualResult)
225 var exp = qtest_results.stringify(expectedResult)
226 if (!qtest_results.compare(success,
227 "function returned unexpected result",
229 util.callerFile(), util.callerLine())) {
230 throw new Error("QtQuickTest::fail")
234 function flickToYEnd(item) {
236 var x = item.width / 2;
237 var y = item.height - units.gu(1);
238 var toY = units.gu(1);
239 while (i < 5 && !item.atYEnd) {
240 touchFlick(item, x, y, x, toY);
241 tryCompare(item, "moving", false);
244 tryCompare(item, "atYEnd", true);
247 function touchEvent(item) {
248 return UT.Util.touchEvent(item)
251 // speed is in pixels/second
252 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
253 // Make sure the item is rendered
254 waitForRendering(item);
256 var root = fetchRootItem(item);
257 var rootFrom = item.mapToItem(root, x, y);
258 var rootTo = item.mapToItem(root, toX, toY);
260 // Default to true for beginTouch if not present
261 beginTouch = (beginTouch !== undefined) ? beginTouch : true
263 // Default to true for endTouch if not present
264 endTouch = (endTouch !== undefined) ? endTouch : true
266 // Set a default speed if not specified
267 speed = (speed !== undefined) ? speed : units.gu(10)
269 // Set a default iterations if not specified
270 var iterations = (iterations !== undefined) ? iterations : 5
272 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
273 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
275 var timeStep = totalTime / iterations
276 var diffX = (rootTo.x - rootFrom.x) / iterations
277 var diffY = (rootTo.y - rootFrom.y) / iterations
279 fakeDateTime.currentTimeMs += timeStep
281 var event = touchEvent(item)
282 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
285 for (var i = 0; i < iterations; ++i) {
286 fakeDateTime.currentTimeMs += timeStep
287 if (i === iterations - 1) {
288 // Avoid any rounding errors by making the last move be at precisely
289 // the point specified
290 wait(iterations / speed)
291 var event = touchEvent(item)
292 event.move(0 /* touchId */, rootTo.x, rootTo.y)
295 wait(iterations / speed)
296 var event = touchEvent(item)
297 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
302 fakeDateTime.currentTimeMs += timeStep
303 var event = touchEvent(item)
304 event.release(0 /* touchId */, rootTo.x, rootTo.y)
309 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
310 // Make sure the item is rendered
311 waitForRendering(item);
313 var event1 = touchEvent(item);
315 event1.press(0, x1Start, y1Start);
318 event1.stationary(0);
319 event1.press(1, x2Start, y2Start);
323 for (var i = 0.0; i < 1.0; i += 0.02) {
324 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
325 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
330 event1.release(0, x1End, y1End);
331 event1.release(1, x2End, y2End);
335 function fetchRootItem(item) {
337 return fetchRootItem(item.parent)
342 function touchPress(item, x, y) {
343 var root = fetchRootItem(item)
344 var rootPoint = item.mapToItem(root, x, y)
346 var event = touchEvent(item)
347 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
351 function touchRelease(item, x, y) {
352 var root = fetchRootItem(item)
353 var rootPoint = item.mapToItem(root, x, y)
355 var event = touchEvent(item)
356 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
360 /*! \brief Tap the item with a touch event.
362 \param item The item to be tapped
363 \param x The x coordinate of the tap, defaults to horizontal center
364 \param y The y coordinate of the tap, defaults to vertical center
366 function tap(item, x, y) {
367 if (typeof x !== "number") x = item.width / 2;
368 if (typeof y !== "number") y = item.height / 2;
370 var root = fetchRootItem(item)
371 var rootPoint = item.mapToItem(root, x, y)
373 var event = touchEvent(item)
374 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
377 event = touchEvent(item)
378 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
382 Component.onCompleted: {
383 UT.Util.ensureTouchRegistryInstalled();
385 var rootItem = parent;
386 while (rootItem.parent != undefined) {
387 rootItem = rootItem.parent;
389 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
393 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
394 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
395 Thus to remove a variable that qmltests cannot really control, namely time, this
396 function removes all constraints from DirectionalDragAreas that are sensible to
399 This effectively makes DirectionalDragAreas easier to fool.
401 function removeTimeConstraintsFromDirectionalDragAreas(item) {
403 // use duck-typing to identify a DirectionalDragArea
404 if (item.minSpeed != undefined
405 && item.maxSilenceTime != undefined
406 && item.compositionTime != undefined) {
408 item.maxSilenceTime = 60 * 60 * 1000;
409 item.compositionTime = 0;
411 for (var i in item.children) {
412 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
417 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
418 function waitForRendering(item, timeout) {
419 if (timeout === undefined)
422 qtest_fail("No item given to waitForRendering", 1);
423 return qtest_results.waitForRendering(item, timeout);