~lukas-kde/unity8/defaultKeymap

« back to all changes in this revision

Viewing changes to tests/qmltests/Stages/tst_SpreadDelegate.qml

  • Committer: Lukáš Tinkl
  • Date: 2016-10-27 14:18:11 UTC
  • mfrom: (2608.1.62 unity8)
  • Revision ID: lukas.tinkl@canonical.com-20161027141811-99hkk5so3o1d1lqm
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright 2014-2016 Canonical Ltd.
3
 
 *
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.
7
 
 *
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.
12
 
 *
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/>.
15
 
 */
16
 
 
17
 
import QtQuick 2.4
18
 
import QtTest 1.0
19
 
import Unity.Test 0.1 as UT
20
 
import ".."
21
 
import "../../../qml/Components"
22
 
import "../../../qml/Stages"
23
 
import Ubuntu.Components 1.3
24
 
import Ubuntu.Components.ListItems 1.3 as ListItem
25
 
import Unity.Application 0.1
26
 
 
27
 
Rectangle {
28
 
    color: "red"
29
 
    id: root
30
 
    width: fakeShell.shortestDimension + controls.width
31
 
    height: fakeShell.longestDimension
32
 
 
33
 
    property QtObject fakeApplication: null
34
 
 
35
 
    Item {
36
 
        id: fakeShell
37
 
 
38
 
        readonly property real shortestDimension: units.gu(40)
39
 
        readonly property real longestDimension: units.gu(70)
40
 
 
41
 
        width: landscape ? longestDimension : shortestDimension
42
 
        height: landscape ? shortestDimension : longestDimension
43
 
 
44
 
        x: landscape ? (height - width) / 2 : 0
45
 
        y: landscape ? (width - height) / 2 : 0
46
 
 
47
 
        property bool landscape: orientationAngle == 90 || orientationAngle == 270
48
 
        property int orientationAngle: shellOrientationAngleSelector.value
49
 
 
50
 
        rotation: orientationAngle
51
 
 
52
 
        Loader {
53
 
            id: spreadDelegateLoader
54
 
            width: parent.width
55
 
            height: parent.height
56
 
 
57
 
            active: false
58
 
            property bool itemDestroyed: false
59
 
            sourceComponent: SpreadDelegate {
60
 
                anchors.fill: parent
61
 
                swipeToCloseEnabled: swipeToCloseCheckbox.checked
62
 
                closeable: closeableCheckbox.checked
63
 
                application: fakeApplication
64
 
                surface: fakeApplication && fakeApplication.surfaceList.count > 0 ? fakeApplication.surfaceList.get(0) : null
65
 
                shellOrientationAngle: shellOrientationAngleSelector.value
66
 
                shellOrientation: {
67
 
                    switch (shellOrientationAngleSelector.value) {
68
 
                    case 0:
69
 
                        return Qt.PortraitOrientation;
70
 
                    case 90:
71
 
                        return Qt.InvertedLandscapeOrientation;
72
 
                    case 180:
73
 
                        return Qt.InvertedPortraitOrientation;
74
 
                    default: // 270
75
 
                        return Qt.LandscapeOrientation;
76
 
                    }
77
 
                }
78
 
 
79
 
                orientations: Orientations {
80
 
                    // the default values will do
81
 
                }
82
 
 
83
 
                maximizedAppTopMargin: units.gu(3)
84
 
                Component.onDestruction: {
85
 
                    spreadDelegateLoader.itemDestroyed = true;
86
 
                }
87
 
                Component.onCompleted: {
88
 
                    spreadDelegateLoader.itemDestroyed = false;
89
 
                }
90
 
            }
91
 
        }
92
 
    }
93
 
 
94
 
    Rectangle {
95
 
        id: controls
96
 
        color: "white"
97
 
        x: fakeShell.shortestDimension
98
 
        width: units.gu(30)
99
 
        anchors {
100
 
            top: parent.top
101
 
            bottom: parent.bottom
102
 
        }
103
 
 
104
 
        Column {
105
 
            anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
106
 
            spacing: units.gu(1)
107
 
            Button {
108
 
                id: loadWithWeatherApp
109
 
                text: "Load with ubuntu-weather-app"
110
 
                onClicked: { testCase.restartWithApp("ubuntu-weather-app"); }
111
 
            }
112
 
            Button {
113
 
                id: loadWithGalleryApp
114
 
                text: "Load with gallery-app"
115
 
                onClicked: { testCase.restartWithApp("gallery-app"); }
116
 
            }
117
 
            Row {
118
 
                anchors { left: parent.left; right: parent.right }
119
 
                CheckBox { id: swipeToCloseCheckbox; checked: false; }
120
 
                Label { text: "swipeToCloseEnabled" }
121
 
            }
122
 
            Row {
123
 
                anchors { left: parent.left; right: parent.right }
124
 
                CheckBox { id: closeableCheckbox; checked: false }
125
 
                Label { text: "closeable" }
126
 
            }
127
 
            ListItem.ItemSelector {
128
 
                id: shellOrientationAngleSelector
129
 
                anchors { left: parent.left; right: parent.right }
130
 
                text: "shellOrientationAngle"
131
 
                model: ["0", "90", "180", "270"]
132
 
                property int value: selectedIndex * 90
133
 
            }
134
 
            Button {
135
 
                id: matchShellButton
136
 
                text: "matchShellOrientation()"
137
 
                onClicked: { spreadDelegateLoader.item.matchShellOrientation(); }
138
 
            }
139
 
            Button {
140
 
                id: animateToShellButton
141
 
                text: "animateToShellOrientation()"
142
 
                onClicked: { spreadDelegateLoader.item.animateToShellOrientation(); }
143
 
            }
144
 
        }
145
 
    }
146
 
 
147
 
    UT.UnityTestCase {
148
 
        id: testCase
149
 
        name: "SpreadDelegate"
150
 
        when: windowShown
151
 
 
152
 
        SignalSpy {
153
 
            id: spyClosedSignal
154
 
            target: spreadDelegateLoader.item
155
 
            signalName: "closed"
156
 
        }
157
 
 
158
 
        property var dragArea
159
 
        property Item spreadDelegate: spreadDelegateLoader.item
160
 
 
161
 
        function init() {
162
 
        }
163
 
 
164
 
        function cleanup() {
165
 
            unloadSpreadDelegate();
166
 
            spyClosedSignal.clear();
167
 
            shellOrientationAngleSelector.selectedIndex = 0;
168
 
            root.fakeApplication = null;
169
 
            killApps();
170
 
        }
171
 
 
172
 
        function restartWithApp(appId) {
173
 
            if (spreadDelegateLoader.active) {
174
 
                unloadSpreadDelegate();
175
 
            }
176
 
            if (root.fakeApplication) {
177
 
                ApplicationManager.stopApplication(root.fakeApplication.appId);
178
 
            }
179
 
 
180
 
            root.fakeApplication = ApplicationManager.startApplication(appId);
181
 
            spreadDelegateLoader.active = true;
182
 
            tryCompare(spreadDelegateLoader, "status", Loader.Ready);
183
 
 
184
 
            dragArea = findInvisibleChild(spreadDelegate, "dragArea");
185
 
            dragArea.__dateTime = fakeDateTime;
186
 
        }
187
 
 
188
 
        function unloadSpreadDelegate() {
189
 
            spreadDelegateLoader.active = false;
190
 
            tryCompare(spreadDelegateLoader, "status", Loader.Null);
191
 
            tryCompare(spreadDelegateLoader, "item", null);
192
 
            // Loader.status might be Loader.Null and Loader.item might be null but the Loader
193
 
            // item might still be alive. So if we set Loader.active back to true
194
 
            // again right now we will get the very same Shell instance back. So no reload
195
 
            // actually took place. Likely because Loader waits until the next event loop
196
 
            // iteration to do its work. So to ensure the reload, we will wait until the
197
 
            // Shell instance gets destroyed.
198
 
            tryCompare(spreadDelegateLoader, "itemDestroyed", true);
199
 
 
200
 
        }
201
 
 
202
 
        function waitUntilAppWindowIsFullyLoaded() {
203
 
            var appWindowStateGroup = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
204
 
            tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
205
 
            waitUntilTransitionsEnd(appWindowStateGroup);
206
 
 
207
 
            var priv = findInvisibleChild(spreadDelegate, "spreadDelegatePriv");
208
 
            verify(priv);
209
 
            tryCompare(priv, "startingUp", false);
210
 
        }
211
 
 
212
 
        function rotateShellTo(angle) {
213
 
            shellOrientationAngleSelector.selectedIndex = angle / 90;
214
 
        }
215
 
 
216
 
        function waitUntilRotationAnimationStops() {
217
 
            var orientationChangeAnimation = findInvisibleChild(spreadDelegate, "orientationChangeAnimation");
218
 
            verify(orientationChangeAnimation);
219
 
            tryCompare(orientationChangeAnimation, "running", false);
220
 
        }
221
 
 
222
 
        function checkAppWindowTransformationMatchesRotation(appWindow, angle) {
223
 
 
224
 
            var topLeftX = 0;
225
 
            var topLeftY = 0;
226
 
            var bottomRightX = 0;
227
 
            var bottomRightY = 0;
228
 
 
229
 
            switch (angle) {
230
 
            case 0:
231
 
                topLeftX = 0;
232
 
                topLeftY = 0;
233
 
                bottomRightX = fakeShell.shortestDimension;
234
 
                bottomRightY = fakeShell.longestDimension;
235
 
                break;
236
 
            case 90:
237
 
                topLeftX = fakeShell.shortestDimension;
238
 
                topLeftY = 0;
239
 
                bottomRightX = 0;
240
 
                bottomRightY = fakeShell.longestDimension;
241
 
                break;
242
 
            default:
243
 
                // TODO implement 180 and 270 once we need them
244
 
                verify(false);
245
 
            }
246
 
 
247
 
            var appWindowTopLeftInRootCoords = appWindow.mapToItem(root, 0, 0);
248
 
 
249
 
            if (appWindowTopLeftInRootCoords.x !== topLeftX) {
250
 
                qtest_fail("appWindow topLeft.x ("+appWindowTopLeftInRootCoords.x+")"
251
 
                           +" doesn't match expectations ("+topLeftX+").", 1);
252
 
            }
253
 
            if (appWindowTopLeftInRootCoords.y !== topLeftY) {
254
 
                qtest_fail("appWindow topLeft.y ("+appWindowTopLeftInRootCoords.y+")"
255
 
                           +" doesn't match expectations ("+topLeftY+").", 1);
256
 
            }
257
 
 
258
 
            var appWindowBottomRightInRootCoords = appWindow.mapToItem(root, appWindow.width,
259
 
                                                                             appWindow.height);
260
 
            compare(appWindowBottomRightInRootCoords.x, bottomRightX);
261
 
            compare(appWindowBottomRightInRootCoords.y, bottomRightY);
262
 
 
263
 
            if (appWindowBottomRightInRootCoords.x !== bottomRightX) {
264
 
                qtest_fail("appWindow bottomRight.x ("+appWindowBottomRightInRootCoords.x+")"
265
 
                           +" doesn't match expectations ("+bottomRightX+").", 1);
266
 
            }
267
 
            if (appWindowBottomRightInRootCoords.y !== bottomRightY) {
268
 
                qtest_fail("appWindow bottomRight.y ("+appWindowBottomRightInRootCoords.y+")"
269
 
                           +" doesn't match expectations ("+bottomRightY+").", 1);
270
 
            }
271
 
        }
272
 
 
273
 
        function test_swipeToClose_data() {
274
 
            return [
275
 
                {tag: "swipeToClose=true closeable=true -> appWindow moves away",
276
 
                 swipeToClose: true, closeable: true },
277
 
 
278
 
                {tag: "swipeToClose=true closeable=false -> appWindow bounces back",
279
 
                 swipeToClose: true, closeable: false },
280
 
 
281
 
                {tag: "swipeToClose=false -> appWindow stays put",
282
 
                 swipeToClose: false, closeable: true },
283
 
            ]
284
 
        }
285
 
 
286
 
        function test_swipeToClose(data) {
287
 
            loadWithGalleryApp.clicked();
288
 
            var displacedAppWindowWithShadow = findChild(spreadDelegateLoader.item, "displacedAppWindowWithShadow");
289
 
 
290
 
            verify(displacedAppWindowWithShadow.y === 0);
291
 
 
292
 
            swipeToCloseCheckbox.checked = data.swipeToClose;
293
 
            closeableCheckbox.checked = data.closeable;
294
 
 
295
 
            var dragDistance = spreadDelegateLoader.item.height * 0.8;
296
 
            var touchX = spreadDelegateLoader.item.width / 2;
297
 
            var fromY = spreadDelegateLoader.item.height * 0.9;
298
 
            var toY = fromY - dragDistance;
299
 
            touchFlick(spreadDelegateLoader.item,
300
 
                touchX /* fromX */,  fromY, touchX /* toX */,  toY,
301
 
                true /* beginTouch */, false /* endTouch */, dragArea.minSpeedToClose * 1.1 /* speed */);
302
 
 
303
 
            if (data.swipeToClose) {
304
 
                verify(displacedAppWindowWithShadow.y < 0);
305
 
                var threshold = findChild(spreadDelegateLoader.item, "dragArea").threshold
306
 
                if (data.closeable) {
307
 
                    // Verify that the delegate started moving exactly "threshold" after the finger movement
308
 
                    // and did not jump up to the finger, but lags the threshold behind
309
 
                    compare(Math.abs(Math.abs(displacedAppWindowWithShadow.y) - dragDistance), threshold);
310
 
                } else {
311
 
                    verify(Math.abs(Math.abs(displacedAppWindowWithShadow.y) - dragDistance) > threshold);
312
 
                }
313
 
 
314
 
                touchRelease(spreadDelegateLoader.item, touchX, toY - units.gu(1));
315
 
 
316
 
                waitForCloseAnimationToFinish();
317
 
 
318
 
                if (data.closeable) {
319
 
                    verify(spyClosedSignal.count === 1);
320
 
                } else {
321
 
                    verify(spyClosedSignal.count === 0);
322
 
                    tryCompare(displacedAppWindowWithShadow, "y", 0);
323
 
                }
324
 
 
325
 
            } else {
326
 
                verify(displacedAppWindowWithShadow.y === 0);
327
 
 
328
 
                touchRelease(spreadDelegateLoader.item, touchX, toY);
329
 
            }
330
 
        }
331
 
 
332
 
        function test_loadingLandscapeOnlyAppWhenShellInPortrait() {
333
 
            loadWithWeatherApp.clicked();
334
 
 
335
 
            var appWindow = findChild(spreadDelegate, "appWindow");
336
 
            verify(appWindow);
337
 
 
338
 
            // It must have landscape dimensions as it does not support portrait
339
 
            tryCompare(appWindow, "width", fakeShell.height);
340
 
            tryCompare(appWindow, "height", fakeShell.width - spreadDelegate.maximizedAppTopMargin);
341
 
        }
342
 
 
343
 
        function test_keepsSceneTransformationWhenShellRotates_data() {
344
 
            return [
345
 
                {tag: "0", selectedIndex: 0},
346
 
                {tag: "90", selectedIndex: 1},
347
 
                {tag: "180", selectedIndex: 2},
348
 
                {tag: "270", selectedIndex: 3}
349
 
            ];
350
 
        }
351
 
        function test_keepsSceneTransformationWhenShellRotates(data) {
352
 
            loadWithGalleryApp.clicked();
353
 
 
354
 
            waitUntilAppWindowIsFullyLoaded();
355
 
 
356
 
            var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
357
 
            verify(appWindowWithShadow);
358
 
 
359
 
            compare(appWindowWithShadow.state, "keepSceneRotation");
360
 
 
361
 
            shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
362
 
 
363
 
            // must keep same aspect ratio
364
 
            compare(appWindowWithShadow.width, fakeShell.shortestDimension);
365
 
            compare(appWindowWithShadow.height, fakeShell.longestDimension);
366
 
 
367
 
 
368
 
            // and scene transformation must be the identity (ie, no rotation or translation)
369
 
            checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
370
 
        }
371
 
 
372
 
        function waitForCloseAnimationToFinish() {
373
 
            var closeAnimation = findInvisibleChild(spreadDelegateLoader.item, "closeAnimation");
374
 
            wait(closeAnimation.duration * 1.5);
375
 
            tryCompare(closeAnimation, "running", false);
376
 
        }
377
 
 
378
 
        function test_showHighLight() {
379
 
            loadWithGalleryApp.clicked();
380
 
            var highlightRect = findChild(spreadDelegateLoader.item, "selectionHighlight")
381
 
            tryCompare(highlightRect, "visible", false)
382
 
            spreadDelegateLoader.item.highlightShown = true;
383
 
            tryCompare(highlightRect, "visible", true)
384
 
        }
385
 
 
386
 
        /*
387
 
            Checks that the ApplicationWindow position, size and rotation is correct after
388
 
            a sequence of shell rotations folowed by rotations (immediate or not) to match them
389
 
         */
390
 
        function test_animateToShellThenMatchShell() {
391
 
            loadWithGalleryApp.clicked();
392
 
            waitUntilAppWindowIsFullyLoaded();
393
 
 
394
 
            var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
395
 
            verify(appWindowWithShadow);
396
 
 
397
 
            // appWindow transformation is 0 relative to display
398
 
            checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
399
 
 
400
 
            rotateShellTo(90);
401
 
 
402
 
            // appWindow transformation remains 0 relative to display
403
 
            checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
404
 
 
405
 
            animateToShellButton.clicked();
406
 
            waitUntilRotationAnimationStops();
407
 
 
408
 
            // appWindow transformation is now 90 relative to display, just like shell
409
 
            checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 90);
410
 
 
411
 
            rotateShellTo(0);
412
 
 
413
 
            // appWindow transformation is remains 90 relative to display.
414
 
            checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 90);
415
 
 
416
 
            matchShellButton.clicked();
417
 
 
418
 
            // appWindow transformation is back to 0 relative to display, just like shell
419
 
            checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
420
 
 
421
 
        }
422
 
    }
423
 
}