on bbbdccd help sign in
unity8/tests/qmltests/tst_Shell.qml
2023 lines 78 kB history raw
1/*
2 * Copyright (C) 2013-2015 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 */
17import QtQuick 2.4
18import QtTest 1.0
19import AccountsService 0.1
20import GSettings 1.0
21import IntegratedLightDM 0.1 as LightDM
22import Ubuntu.Components 1.3
23import Ubuntu.Components.ListItems 1.3 as ListItem
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Application 0.1
26import Unity.Connectivity 0.1
27import Unity.Indicators 0.1
28import Unity.Notifications 1.0
29import Unity.Test 0.1
30import Powerd 0.1
31import Wizard 0.1 as Wizard
32import Utils 0.1
34import "../../qml"
35import "../../qml/Components"
36import "../../qml/Components/PanelState"
37import "Stages"
39Rectangle {
40 id: root
41 color: "grey"
42 width: units.gu(100) + controls.width
43 height: units.gu(71)
45 Component.onCompleted: {
46 // must set the mock mode before loading the Shell
47 LightDM.Greeter.mockMode = "single";
48 LightDM.Users.mockMode = "single";
49 shellLoader.active = true;
50 }
52 Item {
53 id: shellContainer
54 anchors.left: root.left
55 anchors.right: controls.left
56 anchors.top: root.top
57 anchors.bottom: root.bottom
58 Loader {
59 id: shellLoader
60 focus: true
62 anchors.centerIn: parent
64 property int shellOrientation: Qt.PortraitOrientation
65 property int nativeOrientation: Qt.PortraitOrientation
66 property int primaryOrientation: Qt.PortraitOrientation
68 state: "phone"
69 states: [
70 State {
71 name: "phone"
72 PropertyChanges {
73 target: shellLoader
74 width: units.gu(40)
75 height: units.gu(71)
76 }
77 },
78 State {
79 name: "tablet"
80 PropertyChanges {
81 target: shellLoader
82 width: units.gu(100)
83 height: units.gu(71)
84 shellOrientation: Qt.LandscapeOrientation
85 nativeOrientation: Qt.LandscapeOrientation
86 primaryOrientation: Qt.LandscapeOrientation
87 }
88 },
89 State {
90 name: "desktop"
91 PropertyChanges {
92 target: shellLoader
93 width: shellContainer.width
94 height: shellContainer.height
95 }
96 PropertyChanges {
97 target: mouseEmulation
98 checked: false
99 }
100 }
101 ]
103 active: false
104 property bool itemDestroyed: false
105 sourceComponent: Component {
106 Shell {
107 id: __shell
108 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
109 nativeWidth: width
110 nativeHeight: height
111 orientation: shellLoader.shellOrientation
112 orientations: Orientations {
113 native_: shellLoader.nativeOrientation
114 primary: shellLoader.primaryOrientation
115 }
116 Component.onDestruction: {
117 shellLoader.itemDestroyed = true;
118 }
119 Component.onCompleted: {
120 var keyMapper = testCase.findChild(__shell, "physicalKeysMapper");
121 keyMapper.controlInsteadOfAlt = true;
122 }
123 }
124 }
125 }
126 }
128 Flickable {
129 id: controls
130 contentHeight: controlRect.height
132 anchors.top: root.top
133 anchors.bottom: root.bottom
134 anchors.right: root.right
135 width: units.gu(30)
137 Rectangle {
138 id: controlRect
139 anchors { left: parent.left; right: parent.right }
140 color: "darkgrey"
141 height: childrenRect.height + units.gu(2)
143 Column {
144 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
145 spacing: units.gu(1)
147 Row {
148 spacing: units.gu(1)
149 Button {
150 text: "Show Greeter"
151 activeFocusOnPress: false
152 onClicked: {
153 if (shellLoader.status !== Loader.Ready)
154 return;
156 var greeter = testCase.findChild(shellLoader.item, "greeter");
157 if (!greeter.shown) {
158 LightDM.Greeter.showGreeter();
159 }
160 }
161 }
162 Button {
163 text: "Hide Greeter"
164 activeFocusOnPress: false
165 onClicked: {
166 if (shellLoader.status !== Loader.Ready)
167 return;
169 var greeter = testCase.findChild(shellLoader.item, "greeter");
170 if (greeter.shown) {
171 greeter.hide()
172 }
173 }
174 }
175 }
176 ListItem.ItemSelector {
177 anchors { left: parent.left; right: parent.right }
178 activeFocusOnPress: false
179 text: "LightDM mock mode"
180 model: ["single", "single-passphrase", "single-pin", "full"]
181 onSelectedIndexChanged: {
182 shellLoader.active = false;
183 LightDM.Greeter.mockMode = model[selectedIndex];
184 LightDM.Users.mockMode = model[selectedIndex];
185 shellLoader.active = true;
186 }
187 }
188 ListItem.ItemSelector {
189 anchors { left: parent.left; right: parent.right }
190 activeFocusOnPress: false
191 text: "Size"
192 model: ["phone", "tablet", "desktop"]
193 onSelectedIndexChanged: {
194 shellLoader.active = false;
195 shellLoader.state = model[selectedIndex];
196 shellLoader.active = true;
197 }
198 }
199 ListItem.ItemSelector {
200 id: usageScenarioSelector
201 anchors { left: parent.left; right: parent.right }
202 activeFocusOnPress: false
203 text: "Usage scenario"
204 model: ["phone", "tablet", "desktop"]
205 }
206 MouseTouchEmulationCheckbox {
207 id: mouseEmulation
208 checked: true
209 color: "white"
210 }
212 Label { text: "Applications"; font.bold: true }
214 Button {
215 text: "Start all apps"
216 width: parent.width
217 onClicked: {
218 for (var i = 0; i < ApplicationManager.availableApplications.length; i++) {
219 var appId = ApplicationManager.availableApplications[i];
220 ApplicationManager.startApplication(appId)
221 }
222 }
223 }
225 Repeater {
226 id: appRepeater
227 model: ApplicationManager.availableApplications
228 ApplicationCheckBox {
229 appId: modelData
230 }
231 }
232 }
233 }
234 }
236 Component {
237 id: mockNotification
239 QtObject {
240 function invokeAction(actionId) {
241 mockNotificationsModel.actionInvoked(actionId)
242 }
243 }
244 }
245 ListModel {
246 id: mockNotificationsModel
248 signal actionInvoked(string actionId)
250 function getRaw(id) {
251 return mockNotification.createObject(mockNotificationsModel)
252 }
254 onActionInvoked: {
255 if(actionId == "ok_id") {
256 mockNotificationsModel.clear()
257 }
258 }
259 }
261 SignalSpy {
262 id: launcherShowDashHomeSpy
263 signalName: "showDashHome"
264 }
266 SignalSpy {
267 id: sessionSpy
268 signalName: "sessionStarted"
269 }
271 SignalSpy {
272 id: dashCommunicatorSpy
273 signalName: "setCurrentScopeCalled"
274 }
276 SignalSpy {
277 id: unlockAllModemsSpy
278 target: Connectivity
279 signalName: "unlockingAllModems"
280 }
282 SignalSpy {
283 id: notificationActionSpy
284 target: mockNotificationsModel
285 signalName: "actionInvoked"
286 }
288 SignalSpy {
289 id: appRemovedSpy
290 target: ApplicationManager
291 signalName: "applicationRemoved"
292 }
294 Telephony.CallEntry {
295 id: phoneCall
296 phoneNumber: "+447812221111"
297 }
299 Item {
300 id: fakeDismissTimer
301 property bool running: false
302 signal triggered
304 function stop() {
305 running = false;
306 }
308 function restart() {
309 running = true;
310 }
311 }
313 UnityTestCase {
314 id: testCase
315 name: "Shell"
316 when: windowShown
318 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
320 function init() {
321 if (shellLoader.active) {
322 // happens for the very first test function as shell
323 // is loaded by default
324 tearDown();
325 }
327 }
329 function cleanup() {
330 waitForRendering(shell);
331 mouseEmulation.checked = true;
332 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
333 tearDown();
334 WindowStateStorage.clear();
335 }
337 function loadShell(formFactor) {
338 shellLoader.state = formFactor;
339 shellLoader.active = true;
340 tryCompare(shellLoader, "status", Loader.Ready);
341 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
342 tryCompare(shell, "enabled", true); // enabled by greeter when ready
344 sessionSpy.target = findChild(shell, "greeter")
345 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator");
347 var launcher = findChild(shell, "launcher");
348 launcherShowDashHomeSpy.target = launcher;
350 var panel = findChild(launcher, "launcherPanel");
351 verify(!!panel);
353 panel.dismissTimer = fakeDismissTimer;
355 waitForGreeterToStabilize();
356 }
358 function loadDesktopShellWithApps() {
359 loadShell("desktop");
360 waitForRendering(shell)
361 shell.usageScenario = "desktop"
362 waitForRendering(shell)
363 var app1 = ApplicationManager.startApplication("dialer-app")
364 var app2 = ApplicationManager.startApplication("webbrowser-app")
365 var app3 = ApplicationManager.startApplication("camera-app")
366 var app4 = ApplicationManager.startApplication("facebook-webapp")
367 var app5 = ApplicationManager.startApplication("camera-app")
368 var app6 = ApplicationManager.startApplication("gallery-app")
369 var app7 = ApplicationManager.startApplication("calendar-app")
370 waitUntilAppWindowIsFullyLoaded(app7);
371 }
373 function waitForGreeterToStabilize() {
374 var greeter = findChild(shell, "greeter");
375 verify(greeter);
377 var loginList = findChild(greeter, "loginList");
378 // Only present in WideView
379 if (loginList) {
380 var userList = findChild(loginList, "userList");
381 verify(userList);
382 tryCompare(userList, "movingInternally", false);
383 }
384 }
386 function tearDown() {
387 launcherShowDashHomeSpy.target = null;
389 shellLoader.itemDestroyed = false;
391 shellLoader.active = false;
393 tryCompare(shellLoader, "status", Loader.Null);
394 tryCompare(shellLoader, "item", null);
395 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
396 // item might still be alive. So if we set Loader.active back to true
397 // again right now we will get the very same Shell instance back. So no reload
398 // actually took place. Likely because Loader waits until the next event loop
399 // iteration to do its work. So to ensure the reload, we will wait until the
400 // Shell instance gets destroyed.
401 tryCompare(shellLoader, "itemDestroyed", true);
403 setLightDMMockMode("single"); // back to the default value
405 AccountsService.demoEdges = false;
406 Wizard.System.wizardEnabled = false;
408 // kill all (fake) running apps
409 killApps(ApplicationManager);
411 unlockAllModemsSpy.clear()
412 LightDM.Greeter.authenticate(""); // reset greeter
414 sessionSpy.clear();
416 GSettingsController.setLifecycleExemptAppids([]);
417 }
419 function killApps() {
420 while (ApplicationManager.count > 1) {
421 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
422 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
423 }
424 compare(ApplicationManager.count, 1)
425 }
427 function test_snapDecisionDismissalReturnsFocus() {
428 loadShell("phone");
429 swipeAwayGreeter();
430 var notifications = findChild(shell, "notificationList");
431 var app = ApplicationManager.startApplication("camera-app");
432 var stage = findChild(shell, "stage")
433 // Open an application and focus
434 waitUntilApplicationWindowIsFullyVisible(app);
435 ApplicationManager.focusApplication(app);
436 tryCompare(app.session.lastSurface, "activeFocus", true);
438 notifications.model = mockNotificationsModel;
440 // FIXME: Hack: UnitySortFilterProxyModelQML doesn't work with QML ListModels which we use
441 // for mocking here (RoleType can't be found in the QML model). As we only need to show
442 // one SnapDecision lets just disable the filtering and make appear any notification as a
443 // SnapDecision.
444 var snapDecisionProxyModel = findInvisibleChild(shell, "snapDecisionProxyModel");
445 snapDecisionProxyModel.filterRegExp = RegExp("");
447 // Pop-up a notification
448 addSnapDecisionNotification();
449 waitForRendering(shell);
451 // Make sure the notification really opened
452 var notification = findChild(notifications, "notification" + (mockNotificationsModel.count - 1));
453 verify(notification !== undefined && notification != null, "notification wasn't found");
454 tryCompare(notification, "height", notification.implicitHeight)
455 waitForRendering(notification);
457 // Make sure activeFocus went away from the app window
458 tryCompare(app.session.lastSurface, "activeFocus", false);
459 tryCompare(stage, "interactive", false);
461 // Clicking the button should dismiss the notification and return focus
462 var buttonAccept = findChild(notification, "notify_button0");
463 mouseClick(buttonAccept);
465 // Make sure we're back to normal
466 tryCompare(app.session.lastSurface, "activeFocus", true);
467 compare(stage.interactive, true, "Stages not interactive again after modal notification has closed");
468 }
470 function addSnapDecisionNotification() {
471 var n = {
472 type: Notification.SnapDecision,
473 hints: {"x-canonical-private-affirmative-tint": "true"},
474 summary: "Tom Ato",
475 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
476 icon: "../../tests/graphics/avatars/funky.png",
477 secondaryIcon: "../../tests/graphics/applicationIcons/facebook.png",
478 actions: [{ id: "ok_id", label: "Ok"},
479 { id: "cancel_id", label: "Cancel"},
480 { id: "notreally_id", label: "Not really"},
481 { id: "noway_id", label: "messages:No way"},
482 { id: "nada_id", label: "messages:Nada"}]
483 }
485 mockNotificationsModel.append(n)
486 }
488 function test_phoneLeftEdgeDrag_data() {
489 return [
490 {tag: "without launcher",
491 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
492 launcherHides: true, greeterShown: false},
494 {tag: "with launcher",
495 revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
496 launcherHides: true, greeterShown: false},
498 {tag: "small swipe",
499 revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
500 launcherHides: false, greeterShown: false},
502 {tag: "long swipe",
503 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
504 launcherHides: true, greeterShown: false},
506 {tag: "small swipe with greeter",
507 revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
508 launcherHides: false, greeterShown: true},
510 {tag: "long swipe with greeter",
511 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
512 launcherHides: true, greeterShown: true},
514 {tag: "swipe over dash",
515 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
516 launcherHides: false, greeterShown: false},
517 ];
518 }
520 function test_phoneLeftEdgeDrag(data) {
521 loadShell("phone");
522 swipeAwayGreeter();
523 dragLauncherIntoView();
524 tapOnAppIconInLauncher();
525 waitUntilApplicationWindowIsFullyVisible();
526 ApplicationManager.focusApplication(data.focusedApp)
527 waitUntilApplicationWindowIsFullyVisible();
529 var greeter = findChild(shell, "greeter");
530 if (data.greeterShown) {
531 showGreeter();
532 }
534 if (data.revealLauncher) {
535 dragLauncherIntoView();
536 }
538 swipeFromLeftEdge(data.swipeLength);
539 if (data.appHides) {
540 waitUntilDashIsFocused();
541 tryCompare(greeter, "shown", false);
542 } else {
543 waitUntilApplicationWindowIsFullyVisible();
544 compare(greeter.fullyShown, data.greeterShown);
545 }
547 var launcher = findChild(shell, "launcherPanel");
548 tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
550 // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
551 var animateTimer = findInvisibleChild(shell, "animateTimer");
552 compare(animateTimer.nextState, "visible");
553 }
555 function test_tabletLeftEdgeDrag_data() {
556 return [
557 {tag: "without password", user: "no-password", loggedIn: true, demo: false},
558 {tag: "with password", user: "has-password", loggedIn: false, demo: false},
559 {tag: "with demo", user: "has-password", loggedIn: true, demo: true},
560 ]
561 }
563 function test_tabletLeftEdgeDrag(data) {
564 setLightDMMockMode("full");
565 loadShell("tablet");
567 selectUser(data.user)
569 AccountsService.demoEdges = data.demo
570 var tutorial = findChild(shell, "tutorial");
571 tryCompare(tutorial, "running", data.demo);
573 swipeFromLeftEdge(shell.width * 0.75)
574 wait(500) // to give time to handle dash() signal from Launcher
575 confirmLoggedIn(data.loggedIn)
576 }
578 function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
579 loadShell("phone");
580 swipeAwayGreeter();
581 dragLauncherIntoView();
582 dashCommunicatorSpy.clear();
584 tapOnAppIconInLauncher();
585 waitUntilApplicationWindowIsFullyVisible();
587 verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
589 //Long left swipe
590 swipeFromLeftEdge(units.gu(30));
592 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
594 compare(dashCommunicatorSpy.count, 1);
595 }
597 function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
598 loadShell("phone");
599 swipeAwayGreeter();
600 dragLauncherIntoView();
601 dashCommunicatorSpy.clear();
603 var launcher = findChild(shell, "launcher");
604 var dashIcon = findChild(launcher, "dashItem");
605 verify(dashIcon != undefined);
606 mouseClick(dashIcon);
608 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
610 compare(dashCommunicatorSpy.count, 1);
611 }
613 function test_suspend() {
614 loadShell("phone");
615 swipeAwayGreeter();
616 var greeter = findChild(shell, "greeter");
618 // Launch an app from the launcher
619 dragLauncherIntoView();
620 tapOnAppIconInLauncher();
621 waitUntilApplicationWindowIsFullyVisible();
623 var mainAppId = ApplicationManager.focusedApplicationId;
624 verify(mainAppId != "");
625 var mainApp = ApplicationManager.findApplication(mainAppId);
626 verify(mainApp);
627 tryCompare(mainApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
629 // Display off while call is active...
630 callManager.foregroundCall = phoneCall;
631 Powerd.setStatus(Powerd.Off, Powerd.Unknown);
632 tryCompare(greeter, "shown", false);
634 // Now try again after ending call
635 callManager.foregroundCall = null;
636 Powerd.setStatus(Powerd.On, Powerd.Unknown);
637 Powerd.setStatus(Powerd.Off, Powerd.Unknown);
638 tryCompare(greeter, "fullyShown", true);
640 compare(mainApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
642 // And wake up
643 Powerd.setStatus(Powerd.On, Powerd.Unknown);
644 tryCompare(greeter, "fullyShown", true);
646 // Swipe away greeter to focus app
648 // greeter unloads its internal components when hidden
649 // and reloads them when shown. Thus we have to do this
650 // again before interacting with it otherwise any
651 // DirectionalDragAreas in there won't be easily fooled by
652 // fake swipes.
653 swipeAwayGreeter();
655 compare(mainApp.requestedState, ApplicationInfoInterface.RequestedRunning);
656 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
657 }
659 function swipeAwayGreeter() {
660 var greeter = findChild(shell, "greeter");
661 tryCompare(greeter, "fullyShown", true);
662 waitForGreeterToStabilize();
663 removeTimeConstraintsFromDirectionalDragAreas(greeter);
665 var touchX = shell.width - (shell.edgeSize / 2);
666 var touchY = shell.height / 2;
667 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
669 // wait until the animation has finished
670 tryCompare(greeter, "shown", false);
671 waitForRendering(greeter);
672 }
674 function selectUserAtIndex(i) {
675 // We could be anywhere in list; find target index to know which direction
676 var greeter = findChild(shell, "greeter")
677 var userlist = findChild(greeter, "userList")
678 if (userlist.currentIndex == i)
679 keyClick(Qt.Key_Escape) // Reset state if we're not moving
680 while (userlist.currentIndex != i) {
681 var next = userlist.currentIndex + 1
682 if (userlist.currentIndex > i) {
683 next = userlist.currentIndex - 1
684 }
685 tap(findChild(greeter, "username"+next));
686 tryCompare(userlist, "currentIndex", next)
687 tryCompare(userlist, "movingInternally", false)
688 }
689 tryCompare(shell, "enabled", true); // wait for PAM to settle
690 }
692 function selectUser(name) {
693 // Find index of user with the right name
694 for (var i = 0; i < LightDM.Users.count; i++) {
695 if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == name) {
696 break
697 }
698 }
699 if (i == LightDM.Users.count) {
700 fail("Didn't find name")
701 return -1
702 }
703 selectUserAtIndex(i)
704 return i
705 }
707 function clickPasswordInput(isButton) {
708 var greeter = findChild(shell, "greeter")
709 tryCompare(greeter, "fullyShown", true);
711 var passwordMouseArea = findChild(shell, "passwordMouseArea")
712 tryCompare(passwordMouseArea, "enabled", isButton)
714 var passwordInput = findChild(shell, "passwordInput")
715 mouseClick(passwordInput)
716 }
718 function confirmLoggedIn(loggedIn) {
719 var greeter = findChild(shell, "greeter");
720 tryCompare(greeter, "shown", loggedIn ? false : true);
721 verify(loggedIn ? sessionSpy.count > 0 : sessionSpy.count === 0);
722 }
724 function setLightDMMockMode(mode) {
725 LightDM.Greeter.mockMode = mode;
726 LightDM.Users.mockMode = mode;
727 }
729 /*
730 Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
732 When the user minimizes an application (left-edge swipe) he should always end up in the
733 "Applications" scope view.
735 Steps:
736 - reveal launcher and launch an app that covers the dash
737 - perform long left edge swipe to go minimize the app and go back to the dash.
738 - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
739 */
740 function test_minimizingAppTakesToDashApps() {
741 loadShell("phone");
742 swipeAwayGreeter();
743 dragLauncherIntoView();
745 // Launch an app from the launcher
746 tapOnAppIconInLauncher();
748 waitUntilApplicationWindowIsFullyVisible();
750 verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
752 dashCommunicatorSpy.clear();
753 // Minimize the application we just launched
754 swipeFromLeftEdge(shell.width * 0.75);
756 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
758 compare(dashCommunicatorSpy.count, 1);
759 compare(dashCommunicatorSpy.signalArguments[0][0], 0);
760 }
762 function test_showInputMethod() {
763 loadShell("phone");
764 swipeAwayGreeter();
765 var item = findChild(shell, "inputMethod");
766 var surface = SurfaceManager.inputMethodSurface();
768 surface.setState(Mir.MinimizedState);
769 tryCompare(item, "visible", false);
771 surface.setState(Mir.RestoredState);
772 tryCompare(item, "visible", true);
774 surface.setState(Mir.MinimizedState);
775 tryCompare(item, "visible", false);
777 surface.setState(Mir.MaximizedState);
778 tryCompare(item, "visible", true);
780 surface.setState(Mir.MinimizedState);
781 tryCompare(item, "visible", false);
782 }
784 // Wait until the ApplicationWindow for the given Application object is fully loaded
785 // (ie, the real surface has replaced the splash screen)
786 function waitUntilAppWindowIsFullyLoaded(app) {
787 var appWindow = findChild(shell, "appWindow_" + app.appId);
788 var appWindowStateGroup = findInvisibleChild(appWindow, "applicationWindowStateGroup");
789 tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
790 waitUntilTransitionsEnd(appWindowStateGroup);
791 }
793 function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
794 loadShell("phone");
795 swipeAwayGreeter();
796 var app = ApplicationManager.startApplication("dialer-app");
797 waitUntilAppWindowIsFullyLoaded(app);
799 tryCompare(app.session.lastSurface, "activeFocus", true);
801 // Drag the indicators panel half-open
802 var touchX = shell.width / 2;
803 var indicators = findChild(shell, "indicators");
804 touchFlick(indicators,
805 touchX /* fromX */, indicators.minimizedPanelHeight * 0.5 /* fromY */,
806 touchX /* toX */, shell.height * 0.5 /* toY */,
807 true /* beginTouch */, false /* endTouch */);
808 verify(indicators.partiallyOpened);
810 tryCompare(app.session.lastSurface, "activeFocus", false);
812 // And finish getting it open
813 touchFlick(indicators,
814 touchX /* fromX */, shell.height * 0.5 /* fromY */,
815 touchX /* toX */, shell.height * 0.9 /* toY */,
816 false /* beginTouch */, true /* endTouch */);
817 tryCompare(indicators, "fullyOpened", true);
819 tryCompare(app.session.lastSurface, "activeFocus", false);
821 dragToCloseIndicatorsPanel();
823 tryCompare(app.session.lastSurface, "activeFocus", true);
824 }
826 function test_launchedAppHasActiveFocus_data() {
827 return [
828 {tag: "phone", formFactor: "phone", usageScenario: "phone"},
829 {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"},
830 {tag: "desktop", formFactor: "tablet", usageScenario: "desktop"}
831 ]
832 }
834 function test_launchedAppHasActiveFocus(data) {
835 loadShell(data.formFactor);
836 shell.usageScenario = data.usageScenario;
837 swipeAwayGreeter();
839 var webApp = ApplicationManager.startApplication("webbrowser-app");
840 verify(webApp);
841 waitUntilAppSurfaceShowsUp("webbrowser-app")
843 verify(webApp.session.lastSurface);
845 tryCompare(webApp.session.lastSurface, "activeFocus", true);
846 }
848 function test_launchedAppKeepsActiveFocusOnUsageModeChange() {
849 loadShell("tablet");
850 swipeAwayGreeter();
852 var webApp = ApplicationManager.startApplication("webbrowser-app");
853 verify(webApp);
854 waitUntilAppSurfaceShowsUp("webbrowser-app")
856 verify(webApp.session.lastSurface);
858 tryCompare(webApp.session.lastSurface, "activeFocus", true);
860 shell.usageScenario = "desktop";
862 // check that the desktop stage and window have been loaded
863 {
864 var desktopWindow = findChild(shell, "appWindow_webbrowser-app");
865 verify(desktopWindow);
866 }
868 tryCompare(webApp.session.lastSurface, "activeFocus", true);
870 shell.usageScenario = "tablet";
872 // check that the tablet stage and app surface delegate have been loaded
873 {
874 var desktopWindow = findChild(shell, "tabletSpreadDelegate_webbrowser-app");
875 verify(desktopWindow);
876 }
878 tryCompare(webApp.session.lastSurface, "activeFocus", true);
879 }
881 function waitUntilAppSurfaceShowsUp(appId) {
882 var appWindow = findChild(shell, "appWindow_" + appId);
883 verify(appWindow);
884 var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
885 verify(appWindowStates);
886 tryCompare(appWindowStates, "state", "surface");
887 }
889 function dragToCloseIndicatorsPanel() {
890 var indicators = findChild(shell, "indicators");
892 var touchStartX = shell.width / 2;
893 var touchStartY = shell.height - (indicators.minimizedPanelHeight * 0.5);
894 touchFlick(shell,
895 touchStartX, touchStartY,
896 touchStartX, shell.height * 0.1);
898 tryCompare(indicators, "fullyClosed", true);
899 }
901 function dragLauncherIntoView() {
902 var launcher = findChild(shell, "launcher");
903 var launcherPanel = findChild(launcher, "launcherPanel");
904 verify(launcherPanel.x = - launcherPanel.width);
906 var touchStartX = 2;
907 var touchStartY = shell.height / 2;
908 touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
910 tryCompare(launcherPanel, "x", 0);
911 tryCompare(launcher, "state", "visible");
912 }
914 function tapOnAppIconInLauncher() {
915 var launcherPanel = findChild(shell, "launcherPanel");
917 // pick the first icon, the one at the top.
918 var appIcon = findChild(launcherPanel, "launcherDelegate0")
919 tap(appIcon, appIcon.width / 2, appIcon.height / 2);
920 }
922 function showIndicators() {
923 var indicators = findChild(shell, "indicators");
924 indicators.show();
925 tryCompare(indicators, "fullyOpened", true);
926 }
928 function hideIndicators() {
929 var indicators = findChild(shell, "indicators");
930 if (indicators.fullyOpened) {
931 indicators.hide();
932 }
933 }
935 function waitUntilApplicationWindowIsFullyVisible() {
936 var appDelegate = findChild(shell, "appDelegate0")
937 var surfaceContainer = findChild(appDelegate, "surfaceContainer");
938 tryCompareFunction(function() { return surfaceContainer.surface !== null; }, true);
939 }
941 function waitUntilDashIsFocused() {
942 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
943 }
945 function swipeFromLeftEdge(swipeLength) {
946 var touchStartX = 2;
947 var touchStartY = shell.height / 2;
948 touchFlick(shell,
949 touchStartX , touchStartY,
950 touchStartX + swipeLength, touchStartY);
951 }
953 function itemIsOnScreen(item) {
954 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
956 return itemRectInShell.x >= 0
957 && itemRectInShell.y >= 0
958 && itemRectInShell.x + itemRectInShell.width <= shell.width
959 && itemRectInShell.y + itemRectInShell.height <= shell.height;
960 }
962 function showGreeter() {
963 var greeter = findChild(shell, "greeter");
964 LightDM.Greeter.showGreeter();
965 waitForRendering(greeter);
966 tryCompare(greeter, "fullyShown", true);
968 // greeter unloads its internal components when hidden
969 // and reloads them when shown. Thus we have to do this
970 // again before interacting with it otherwise any
971 // DirectionalDragAreas in there won't be easily fooled by
972 // fake swipes.
973 removeTimeConstraintsFromDirectionalDragAreas(greeter);
974 }
976 function revealLauncherByEdgePushWithMouse() {
977 var launcher = findChild(shell, "launcher");
978 verify(launcher);
980 // Place the mouse against the window/screen edge and push beyond the barrier threshold
981 mouseMove(shell, 0, shell.height / 2);
982 launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
984 var panel = findChild(launcher, "launcherPanel");
985 verify(panel);
987 // wait until it gets fully extended
988 tryCompare(panel, "x", 0);
989 tryCompare(launcher, "state", "visibleTemporary");
990 }
992 function test_focusRequestedHidesGreeter() {
993 loadShell("phone");
994 swipeAwayGreeter();
995 var greeter = findChild(shell, "greeter");
997 var app = ApplicationManager.startApplication("dialer-app");
998 // wait until the app is fully loaded (ie, real surface replaces splash screen)
999 tryCompareFunction(function() { return app.session !== null && app.session.lastSurface !== null }, true);
1001 // Minimize the application we just launched
1002 swipeFromLeftEdge(shell.width * 0.75);
1004 waitUntilDashIsFocused();
1006 showGreeter();
1008 // The main point of this test
1009 ApplicationManager.requestFocusApplication("dialer-app");
1010 tryCompare(greeter, "shown", false);
1011 waitForRendering(greeter);
1014 function test_focusRequestedHidesIndicators() {
1015 loadShell("phone");
1016 swipeAwayGreeter();
1017 var indicators = findChild(shell, "indicators");
1019 showIndicators();
1021 var oldCount = ApplicationManager.count;
1022 ApplicationManager.startApplication("camera-app");
1023 tryCompare(ApplicationManager, "count", oldCount + 1);
1025 tryCompare(indicators, "fullyClosed", true);
1028 function test_showAndHideGreeterDBusCalls() {
1029 loadShell("phone");
1030 swipeAwayGreeter();
1031 var greeter = findChild(shell, "greeter")
1032 tryCompare(greeter, "shown", false)
1033 waitForRendering(greeter);
1034 LightDM.Greeter.showGreeter()
1035 waitForRendering(greeter)
1036 tryCompare(greeter, "fullyShown", true)
1037 LightDM.Greeter.hideGreeter()
1038 tryCompare(greeter, "shown", false)
1041 function test_greeterLoginsAutomaticallyWhenNoPasswordSet() {
1042 loadShell("phone");
1043 swipeAwayGreeter();
1045 sessionSpy.clear();
1046 verify(sessionSpy.valid);
1048 showGreeter();
1050 tryCompare(sessionSpy, "count", 1);
1053 function test_fullscreen() {
1054 loadShell("phone");
1055 swipeAwayGreeter();
1056 var panel = findChild(shell, "panel");
1057 compare(panel.fullscreenMode, false);
1058 ApplicationManager.startApplication("camera-app");
1059 tryCompare(panel, "fullscreenMode", true);
1060 ApplicationManager.startApplication("dialer-app");
1061 tryCompare(panel, "fullscreenMode", false);
1062 ApplicationManager.requestFocusApplication("camera-app");
1063 tryCompare(panel, "fullscreenMode", true);
1064 ApplicationManager.requestFocusApplication("dialer-app");
1065 tryCompare(panel, "fullscreenMode", false);
1068 function test_leftEdgeDragFullscreen() {
1069 loadShell("phone");
1070 swipeAwayGreeter();
1071 var panel = findChild(shell, "panel");
1072 tryCompare(panel, "fullscreenMode", false)
1074 ApplicationManager.startApplication("camera-app");
1075 tryCompare(panel, "fullscreenMode", true)
1077 var touchStartX = 2;
1078 var touchStartY = shell.height / 2;
1080 touchFlick(shell, touchStartX, touchStartY, units.gu(2), touchStartY, true, false);
1082 compare(panel.fullscreenMode, true);
1084 touchFlick(shell, units.gu(2), touchStartY, shell.width * 0.5, touchStartY, false, false);
1086 tryCompare(panel, "fullscreenMode", false);
1088 touchRelease(shell);
1091 function test_unlockedProperties() {
1092 loadShell("phone");
1093 swipeAwayGreeter();
1094 // Confirm that various properties have the correct values when unlocked
1095 var greeter = findChild(shell, "greeter");
1096 tryCompare(greeter, "locked", false);
1098 var launcher = findChild(shell, "launcher")
1099 tryCompare(launcher, "available", true)
1101 var indicators = findChild(shell, "indicators")
1102 tryCompare(indicators, "available", true)
1105 function test_unlockAllModemsOnBoot() {
1106 loadShell("phone");
1107 swipeAwayGreeter();
1108 tryCompare(unlockAllModemsSpy, "count", 1)
1111 function test_unlockAllModemsAfterWizard() {
1112 Wizard.System.wizardEnabled = true;
1113 loadShell("phone");
1115 var wizard = findChild(shell, "wizard");
1116 compare(wizard.active, true);
1117 compare(Wizard.System.wizardEnabled, true);
1118 compare(unlockAllModemsSpy.count, 0);
1120 wizard.hide();
1121 tryCompare(wizard, "active", false);
1122 compare(Wizard.System.wizardEnabled, false);
1123 compare(unlockAllModemsSpy.count, 1);
1126 function test_wizardEarlyExit() {
1127 Wizard.System.wizardEnabled = true;
1128 AccountsService.demoEdges = true;
1129 loadShell("phone");
1131 var wizard = findChild(shell, "wizard");
1132 var tutorial = findChild(shell, "tutorial");
1133 tryCompare(wizard, "active", true);
1134 tryCompare(tutorial, "running", true);
1135 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1137 // Make sure we stay running when nothing focused (can happen for
1138 // a moment when we restart the dash after switching language)
1139 ApplicationManager.stopApplication("unity8-dash");
1140 tryCompare(ApplicationManager, "focusedApplicationId", "");
1141 compare(wizard.shown, true);
1142 compare(tutorial.running, true);
1144 // And make sure we stay running when dash focused again
1145 ApplicationManager.startApplication("unity8-dash");
1146 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1147 compare(wizard.shown, true);
1148 compare(tutorial.running, true);
1150 // And make sure we stop when something else is focused
1151 ApplicationManager.startApplication("gallery-app");
1152 tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
1153 compare(wizard.shown, false);
1154 compare(tutorial.running, false);
1155 tryCompare(AccountsService, "demoEdges", false);
1156 tryCompare(Wizard.System, "wizardEnabled", false);
1158 var tutorialLeft = findChild(tutorial, "tutorialLeft");
1159 compare(tutorialLeft, null); // should be destroyed with tutorial
1162 function test_tutorialPausedDuringGreeter() {
1163 loadShell("phone");
1165 var tutorial = findChild(shell, "tutorial");
1167 AccountsService.demoEdges = true;
1168 tryCompare(tutorial, "running", true);
1169 tryCompare(tutorial, "paused", true);
1171 swipeAwayGreeter();
1172 tryCompare(tutorial, "paused", false);
1175 function test_tapOnRightEdgeReachesApplicationSurface() {
1176 loadShell("phone");
1177 swipeAwayGreeter();
1178 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1179 verify(topmostSpreadDelegate);
1181 waitUntilFocusedApplicationIsShowingItsSurface();
1183 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
1184 verify(topmostSurfaceItem);
1186 var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1187 topmostSurfaceItem.touchPressCount = 0;
1188 topmostSurfaceItem.touchReleaseCount = 0;
1190 var tapPoint = rightEdgeDragArea.mapToItem(shell, rightEdgeDragArea.width / 2,
1191 rightEdgeDragArea.height / 2);
1193 tap(shell, tapPoint.x, tapPoint.y);
1195 tryCompare(topmostSurfaceItem, "touchPressCount", 1);
1196 tryCompare(topmostSurfaceItem, "touchReleaseCount", 1);
1199 /*
1200 Perform a right edge drag over an application surface and check
1201 that no touch event was sent to it (ie, they were all consumed
1202 by the right-edge drag area)
1203 */
1204 function test_rightEdgeDragDoesNotReachApplicationSurface() {
1205 loadShell("phone");
1206 swipeAwayGreeter();
1207 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1208 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
1209 var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1211 topmostSurfaceItem.touchPressCount = 0;
1212 topmostSurfaceItem.touchReleaseCount = 0;
1214 var gestureStartPoint = rightEdgeDragArea.mapToItem(shell, rightEdgeDragArea.width / 2,
1215 rightEdgeDragArea.height / 2);
1217 touchFlick(shell,
1218 gestureStartPoint.x /* fromX */, gestureStartPoint.y /* fromY */,
1219 units.gu(1) /* toX */, gestureStartPoint.y /* toY */);
1221 tryCompare(topmostSurfaceItem, "touchPressCount", 0);
1222 tryCompare(topmostSurfaceItem, "touchReleaseCount", 0);
1225 function waitUntilFocusedApplicationIsShowingItsSurface()
1227 var spreadDelegate = findChild(shell, "appDelegate0");
1228 var appState = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
1229 tryCompare(appState, "state", "surface");
1230 var transitions = appState.transitions;
1231 for (var i = 0; i < transitions.length; ++i) {
1232 var transition = transitions[i];
1233 tryCompare(transition, "running", false, 2000);
1237 function swipeFromRightEdgeToShowAppSpread()
1239 // perform a right-edge drag to show the spread
1240 var touchStartX = shell.width - (shell.edgeSize / 2)
1241 var touchStartY = shell.height / 2;
1242 touchFlick(shell, touchStartX, touchStartY, units.gu(1) /* endX */, touchStartY /* endY */);
1244 // check if it's indeed showing the spread
1245 var stage = findChild(shell, "stage");
1246 var spreadView = findChild(stage, "spreadView");
1247 tryCompare(spreadView, "phase", 2);
1250 function test_tapUbuntuIconInLauncherOverAppSpread() {
1251 launcherShowDashHomeSpy.clear();
1253 loadShell("phone");
1254 swipeAwayGreeter();
1256 waitUntilFocusedApplicationIsShowingItsSurface();
1258 swipeFromRightEdgeToShowAppSpread();
1260 var launcher = findChild(shell, "launcher");
1262 dragLauncherIntoView();
1264 // Emulate a tap with a finger, where the touch position drifts during the tap.
1265 // This is to test the touch ownership changes. The tap is happening on the button
1266 // area but then drifting into the left edge drag area. This test makes sure
1267 // the touch ownership stays with the button and doesn't move over to the
1268 // left edge drag area.
1270 var buttonShowDashHome = findChild(launcher, "buttonShowDashHome");
1271 touchFlick(buttonShowDashHome,
1272 buttonShowDashHome.width * 0.2, /* startPos.x */
1273 buttonShowDashHome.height * 0.8, /* startPos.y */
1274 buttonShowDashHome.width * 0.8, /* endPos.x */
1275 buttonShowDashHome.height * 0.2 /* endPos.y */);
1278 compare(launcherShowDashHomeSpy.count, 1);
1280 // check that the stage has left spread mode.
1282 var stage = findChild(shell, "stage");
1283 var spreadView = findChild(stage, "spreadView");
1284 tryCompare(spreadView, "phase", 0);
1287 // check that the launcher got dismissed
1288 var launcherPanel = findChild(shell, "launcherPanel");
1289 tryCompare(launcherPanel, "x", -launcherPanel.width);
1292 function test_tabletLogin_data() {
1293 return [
1294 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},
1295 {tag: "with password", user: "has-password", loggedIn: true, password: "password"},
1296 {tag: "without password", user: "no-password", loggedIn: true, password: ""},
1300 function test_tabletLogin(data) {
1301 setLightDMMockMode("full");
1302 loadShell("tablet");
1304 selectUser(data.user);
1306 clickPasswordInput(data.password === "" /* isButton */);
1308 if (data.password !== "") {
1309 typeString(data.password);
1310 keyClick(Qt.Key_Enter);
1313 confirmLoggedIn(data.loggedIn);
1316 function test_appLaunchDuringGreeter_data() {
1317 return [
1318 {tag: "auth error", user: "auth-error", loggedIn: false, passwordFocus: false},
1319 {tag: "without password", user: "no-password", loggedIn: true, passwordFocus: false},
1320 {tag: "with password", user: "has-password", loggedIn: false, passwordFocus: true},
1324 function test_appLaunchDuringGreeter(data) {
1325 setLightDMMockMode("full");
1326 loadShell("tablet");
1328 selectUser(data.user)
1330 var greeter = findChild(shell, "greeter")
1331 var app = ApplicationManager.startApplication("dialer-app")
1333 confirmLoggedIn(data.loggedIn)
1335 if (data.passwordFocus) {
1336 var passwordInput = findChild(greeter, "passwordInput")
1337 tryCompare(passwordInput, "focus", true)
1341 function test_stageLoader_data() {
1342 return [
1343 {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageScenario: "phone"},
1344 {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageScenario: "tablet"},
1345 {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageScenario: "desktop"}
1349 function test_stageLoader(data) {
1350 loadShell(data.formFactor);
1351 shell.usageScenario = data.usageScenario;
1352 var stageLoader = findChild(shell, "applicationsDisplayLoader");
1353 verify(String(stageLoader.source).indexOf(data.source) >= 0);
1356 function test_launcherInverted_data() {
1357 return [
1358 {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},
1359 {tag: "tablet", formFactor: "tablet", usageScenario: "tablet", launcherInverted: true},
1360 {tag: "desktop", formFactor: "tablet", usageScenario: "desktop", launcherInverted: false}
1364 function test_launcherInverted(data) {
1365 loadShell(data.formFactor);
1366 shell.usageScenario = data.usageScenario;
1368 var launcher = findChild(shell, "launcher");
1369 compare(launcher.inverted, data.launcherInverted);
1372 function test_unfocusedAppsGetSuspendedAfterEnteringStagedMode() {
1373 loadShell("tablet");
1374 shell.usageScenario = "desktop";
1376 var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1377 waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1379 var galleryApp = ApplicationManager.startApplication("gallery-app");
1380 waitUntilAppWindowIsFullyLoaded(galleryApp);
1382 ApplicationManager.requestFocusApplication("unity8-dash");
1383 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1385 compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1386 compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1388 shell.usageScenario = "tablet";
1390 tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1391 tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1394 function test_unfocusedAppsAreResumedWhenEnteringWindowedMode() {
1395 loadShell("tablet");
1396 shell.usageScenario = "tablet";
1398 var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1399 waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1401 var galleryApp = ApplicationManager.startApplication("gallery-app");
1402 waitUntilAppWindowIsFullyLoaded(galleryApp);
1404 ApplicationManager.requestFocusApplication("unity8-dash");
1405 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1407 compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1408 compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1410 shell.usageScenario = "desktop";
1412 tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1413 tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1416 function test_altTabSwitchesFocus() {
1417 loadShell("desktop");
1418 shell.usageScenario = "desktop"
1419 waitForRendering(root)
1421 var desktopStage = findChild(shell, "stage");
1422 verify(desktopStage != null)
1424 var app1 = ApplicationManager.startApplication("dialer-app")
1425 waitUntilAppWindowIsFullyLoaded(app1);
1426 var app2 = ApplicationManager.startApplication("webbrowser-app")
1427 waitUntilAppWindowIsFullyLoaded(app2);
1428 var app3 = ApplicationManager.startApplication("camera-app")
1429 waitUntilAppWindowIsFullyLoaded(app3);
1431 // Do a quick alt-tab and see if focus changes
1432 tryCompare(app3.session.lastSurface, "activeFocus", true)
1433 keyClick(Qt.Key_Tab, Qt.ControlModifier)
1434 tryCompare(app2.session.lastSurface, "activeFocus", true)
1436 var desktopSpread = findChild(shell, "spread")
1438 tryCompare(desktopSpread, "state", "")
1440 // Just press Alt, make sure the spread comes up
1441 keyPress(Qt.Key_Control);
1442 keyClick(Qt.Key_Tab);
1443 tryCompare(desktopSpread, "state", "altTab")
1445 // Release control, check if spread disappears
1446 keyRelease(Qt.Key_Control)
1447 tryCompare(desktopSpread, "state", "")
1449 // Focus should have switched back now
1450 tryCompare(app3.session.lastSurface, "activeFocus", true)
1453 function test_altTabWrapAround() {
1454 loadDesktopShellWithApps();
1456 var desktopStage = findChild(shell, "stage");
1457 verify(desktopStage !== null)
1459 var desktopSpread = findChild(shell, "spread");
1460 verify(desktopSpread !== null)
1462 var spreadContainer = findInvisibleChild(shell, "spreadContainer")
1463 verify(spreadContainer !== null)
1465 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater")
1466 verify(spreadRepeater !== null)
1468 // remember the focused appId
1469 var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
1471 tryCompare(desktopSpread, "state", "")
1473 // Just press Alt, make sure the spread comes up
1474 keyPress(Qt.Key_Control);
1475 keyClick(Qt.Key_Tab);
1476 tryCompare(desktopSpread, "state", "altTab")
1477 tryCompare(spreadRepeater, "highlightedIndex", 1)
1478 waitForRendering(shell)
1480 // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
1481 // We can't simulate a pressed key with keyPress() currently, so let's inject the events
1482 // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
1483 for (var i = 0; i < 10; i++) {
1484 desktopSpread.selectNext(true); // true == isAutoRepeat
1485 wait(0); // Trigger the event loop to make sure all the things happen
1487 tryCompare(spreadRepeater, "highlightedIndex", 6)
1489 // Now release it once, and verify that it does wrap around with an additional Tab press
1490 keyRelease(Qt.Key_Tab);
1491 keyClick(Qt.Key_Tab);
1492 tryCompare(spreadRepeater, "highlightedIndex", 0)
1494 // Release control, check if spread disappears
1495 keyRelease(Qt.Key_Control)
1496 tryCompare(desktopSpread, "state", "")
1498 // Make sure that after wrapping around once, we have the same one focused as at the beginning
1499 tryCompare(focused.session.lastSurface, "activeFocus", true)
1502 function test_altBackTabNavigation() {
1503 loadDesktopShellWithApps();
1505 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1506 verify(spreadRepeater !== null);
1508 keyPress(Qt.Key_Control)
1509 keyClick(Qt.Key_Tab);
1510 tryCompare(spreadRepeater, "highlightedIndex", 1);
1512 keyClick(Qt.Key_Tab);
1513 tryCompare(spreadRepeater, "highlightedIndex", 2);
1515 keyClick(Qt.Key_Tab);
1516 tryCompare(spreadRepeater, "highlightedIndex", 3);
1518 keyClick(Qt.Key_Tab);
1519 tryCompare(spreadRepeater, "highlightedIndex", 4);
1521 keyClick(Qt.Key_Backtab);
1522 tryCompare(spreadRepeater, "highlightedIndex", 3);
1524 keyClick(Qt.Key_Backtab);
1525 tryCompare(spreadRepeater, "highlightedIndex", 2);
1527 keyClick(Qt.Key_Backtab);
1528 tryCompare(spreadRepeater, "highlightedIndex", 1);
1530 keyRelease(Qt.Key_Control);
1533 function test_highlightFollowsMouse() {
1534 loadDesktopShellWithApps()
1536 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1537 verify(spreadRepeater !== null);
1539 keyPress(Qt.Key_Control)
1540 keyClick(Qt.Key_Tab);
1542 tryCompare(spreadRepeater, "highlightedIndex", 1);
1544 var x = 0;
1545 var y = shell.height * .75;
1546 mouseMove(shell, x, y)
1548 for (var i = 0; i < 7; i++) {
1549 while (spreadRepeater.highlightedIndex != i && x <= 4000) {
1550 x+=10;
1551 mouseMove(shell, x, y)
1552 wait(0); // spin the loop so bindings get evaluated
1556 verify(y < 4000);
1558 keyRelease(Qt.Key_Control);
1561 function test_closeFromSpread() {
1562 loadDesktopShellWithApps()
1564 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1565 verify(spreadRepeater !== null);
1567 keyPress(Qt.Key_Control)
1568 keyClick(Qt.Key_Tab);
1570 appRemovedSpy.clear();
1572 var closedAppId = ApplicationManager.get(2).appId;
1573 var spreadDelegate2 = spreadRepeater.itemAt(2);
1574 var closeMouseArea = findChild(spreadDelegate2, "closeMouseArea");
1576 // Move the mosue over tile 2 and verify the close button becomes visible
1577 var x = 0;
1578 var y = shell.height * .5;
1579 mouseMove(shell, x, y)
1580 while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1581 x+=10;
1582 mouseMove(shell, x, y)
1583 wait(0); // spin the loop so bindings get evaluated
1585 tryCompare(closeMouseArea, "enabled", true)
1587 // Close the app using the close button
1588 mouseClick(closeMouseArea, closeMouseArea.width / 2, closeMouseArea.height / 2)
1590 // Verify applicationRemoved has been emitted correctly
1591 tryCompare(appRemovedSpy, "count", 1)
1592 compare(appRemovedSpy.signalArguments[0][0], closedAppId);
1594 keyRelease(Qt.Key_Control);
1597 function test_selectFromSpreadWithMouse_data() {
1598 return [
1599 {tag: "click on tileInfo", tileInfo: true },
1600 {tag: "click on surface", tileInfo: false },
1604 function test_selectFromSpreadWithMouse(data) {
1605 loadDesktopShellWithApps()
1607 var stage = findChild(shell, "stage");
1608 var spread = findChild(stage, "spread");
1609 waitForRendering(spread)
1611 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1612 verify(spreadRepeater !== null);
1614 keyPress(Qt.Key_Control)
1615 keyClick(Qt.Key_Tab);
1617 var focusAppId = ApplicationManager.get(2).appId;
1618 var spreadDelegate2 = spreadRepeater.itemAt(2);
1619 var clippedSpreadDelegate = findChild(spreadDelegate2, "clippedSpreadDelegate");
1621 tryCompare(spread, "state", "altTab");
1623 // Move the mouse over tile 2 and verify the highlight becomes visible
1624 var x = 0;
1625 var y = shell.height * (data.tileInfo ? .95 : 0.5)
1626 mouseMove(shell, x, y)
1627 while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1628 x+=10;
1629 mouseMove(shell, x, y)
1630 wait(0); // spin the loop so bindings get evaluated
1632 tryCompare(clippedSpreadDelegate, "highlightShown", true);
1634 // Click the tile
1635 mouseClick(clippedSpreadDelegate, clippedSpreadDelegate.width / 2, clippedSpreadDelegate.height / 2)
1637 // Verify that we left the spread and app2 is the focused one now
1638 tryCompare(stage, "state", "");
1639 tryCompare(ApplicationManager, "focusedApplicationId", focusAppId);
1641 keyRelease(Qt.Key_Control);
1644 function test_progressiveAutoScrolling() {
1645 loadDesktopShellWithApps()
1647 var appRepeater = findInvisibleChild(shell, "appRepeater");
1648 verify(appRepeater !== null);
1650 keyPress(Qt.Key_Control)
1651 keyClick(Qt.Key_Tab);
1653 var spreadFlickable = findChild(shell, "spreadFlickable")
1655 compare(spreadFlickable.contentX, 0);
1657 // Move the mouse to the right and make sure it scrolls the Flickable
1658 var x = 0;
1659 var y = shell.height * .5
1660 mouseMove(shell, x, y)
1661 while (x <= spreadFlickable.width) {
1662 x+=10;
1663 mouseMove(shell, x, y)
1664 wait(0); // spin the loop so bindings get evaluated
1666 tryCompare(spreadFlickable, "contentX", spreadFlickable.contentWidth - spreadFlickable.width);
1668 // And turn around
1669 while (x > 0) {
1670 x-=10;
1671 mouseMove(shell, x, y)
1672 wait(0); // spin the loop so bindings get evaluated
1674 tryCompare(spreadFlickable, "contentX", 0);
1676 keyRelease(Qt.Key_Control);
1679 // This makes sure the hoverMouseArea is set to invisible AND disabled
1680 // when not needed. Otherwise it'll eat mouse hover events for the rest of the shell/apps
1681 function test_hoverMouseAreaDisabledAndInvisible() {
1682 loadDesktopShellWithApps()
1684 var hoverMouseArea = findChild(shell, "hoverMouseArea");
1685 tryCompare(hoverMouseArea, "enabled", false)
1686 tryCompare(hoverMouseArea, "visible", false)
1688 keyPress(Qt.Key_Control)
1689 keyClick(Qt.Key_Tab);
1691 tryCompare(hoverMouseArea, "enabled", true)
1692 tryCompare(hoverMouseArea, "visible", true)
1694 keyRelease(Qt.Key_Control)
1696 tryCompare(hoverMouseArea, "enabled", false)
1697 tryCompare(hoverMouseArea, "visible", false)
1700 function test_workspacePreviewsHighlightedApp() {
1701 loadDesktopShellWithApps()
1703 var targetZ = ApplicationManager.count + 1;
1705 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1706 verify(spreadRepeater !== null);
1708 var appRepeater = findInvisibleChild(shell, "appRepeater");
1709 verify(appRepeater !== null);
1711 keyPress(Qt.Key_Control)
1712 keyClick(Qt.Key_Tab);
1714 tryCompare(spreadRepeater, "highlightedIndex", 1);
1715 tryCompare(appRepeater.itemAt(1), "z", targetZ)
1717 var x = 0;
1718 var y = shell.height * .75;
1719 mouseMove(shell, x, y)
1721 for (var i = 0; i < 7; i++) {
1722 while (spreadRepeater.highlightedIndex != i && x <= 4000) {
1723 tryCompare(appRepeater.itemAt(spreadRepeater.highlightedIndex), "z", targetZ)
1724 x+=10;
1725 mouseMove(shell, x, y)
1726 wait(0); // spin the loop so bindings get evaluated
1730 verify(y < 4000);
1732 keyRelease(Qt.Key_Control);
1735 function test_focusAppFromLauncherExitsSpread() {
1736 loadDesktopShellWithApps()
1738 var desktopSpread = findChild(shell, "spread");
1739 var launcher = findChild(shell, "launcher");
1740 var bfb = findChild(launcher, "buttonShowDashHome");
1742 keyPress(Qt.Key_Control)
1743 keyClick(Qt.Key_Tab);
1745 tryCompare(desktopSpread, "state", "altTab")
1747 revealLauncherByEdgePushWithMouse();
1748 tryCompare(launcher, "x", 0);
1749 mouseMove(bfb, bfb.width / 2, bfb.height / 2)
1750 waitForRendering(shell)
1752 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
1753 tryCompare(launcher, "state", "")
1754 tryCompare(desktopSpread, "state", "")
1756 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
1758 keyRelease(Qt.Key_Control);
1761 // regression test for http://pad.lv/1443319
1762 function test_closeMaximizedAndRestart() {
1763 loadDesktopShellWithApps();
1765 var appRepeater = findChild(shell, "appRepeater")
1766 var appId = ApplicationManager.get(0).appId;
1767 var appDelegate = appRepeater.itemAt(0);
1768 var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
1770 tryCompare(appDelegate, "state", "normal");
1771 tryCompare(PanelState, "buttonsVisible", false)
1773 mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2);
1774 tryCompare(appDelegate, "state", "maximized");
1775 tryCompare(PanelState, "buttonsVisible", true)
1777 ApplicationManager.stopApplication(appId);
1778 tryCompare(PanelState, "buttonsVisible", false)
1780 ApplicationManager.startApplication(appId);
1781 tryCompare(PanelState, "buttonsVisible", true)
1784 function test_newAppHasValidGeometry() {
1785 loadDesktopShellWithApps();
1786 var appRepeater = findChild(shell, "appRepeater");
1787 var appId = ApplicationManager.get(0).appId;
1788 var appDelegate = appRepeater.itemAt(0);
1790 var resizeArea = findChild(appDelegate, "windowResizeArea");
1791 var priv = findInvisibleChild(resizeArea, "priv");
1793 // Make sure windows are at 0,0 or greater and they have a size that's > 0
1794 compare(priv.normalX >= 0, true)
1795 compare(priv.normalY >= 0, true)
1796 compare(priv.normalWidth > 0, true)
1797 compare(priv.normalHeight > 0, true)
1800 // bug http://pad.lv/1431566
1801 function test_switchToStagedHidesPanelButtons() {
1802 loadDesktopShellWithApps();
1803 var appRepeater = findChild(shell, "appRepeater")
1804 var appId = ApplicationManager.get(0).appId;
1805 var appDelegate = appRepeater.itemAt(0);
1806 var panelButtons = findChild(shell, "panelWindowControlButtons")
1808 tryCompare(appDelegate, "state", "normal");
1809 tryCompare(panelButtons, "visible", false);
1811 appDelegate.maximize(false);
1813 shell.usageScenario = "phone";
1814 waitForRendering(shell);
1815 tryCompare(panelButtons, "visible", false);
1818 function test_lockingGreeterHidesPanelButtons() {
1819 loadDesktopShellWithApps();
1820 var appRepeater = findChild(shell, "appRepeater")
1821 var appId = ApplicationManager.get(0).appId;
1822 var appDelegate = appRepeater.itemAt(0);
1823 var panelButtons = findChild(shell, "panelWindowControlButtons")
1825 tryCompare(appDelegate, "state", "normal");
1826 tryCompare(panelButtons, "visible", false);
1828 appDelegate.maximize(false);
1830 LightDM.Greeter.showGreeter();
1831 waitForRendering(shell);
1832 tryCompare(panelButtons, "visible", false);
1835 function test_cantMoveWindowUnderPanel() {
1836 loadDesktopShellWithApps();
1837 var appRepeater = findChild(shell, "appRepeater")
1838 var appDelegate = appRepeater.itemAt(0);
1840 mousePress(appDelegate, appDelegate.width / 2, units.gu(1))
1841 mouseMove(appDelegate, appDelegate.width / 2, -units.gu(100))
1843 compare(appDelegate.y >= PanelState.panelHeight, true);
1846 function test_restoreWindowStateFixesIfUnderPanel() {
1847 loadDesktopShellWithApps();
1848 var appRepeater = findChild(shell, "appRepeater")
1849 var appId = ApplicationManager.get(0).appId;
1850 var appDelegate = appRepeater.itemAt(0);
1852 // Move it under the panel programmatically (might happen later with an alt+drag)
1853 appDelegate.y = -units.gu(10)
1855 ApplicationManager.stopApplication(appId)
1856 ApplicationManager.startApplication(appId)
1857 waitForRendering(shell)
1859 // Make sure the newly started one is at index 0 again
1860 tryCompare(ApplicationManager.get(0), "appId", appId);
1862 appDelegate = appRepeater.itemAt(0);
1863 compare(appDelegate.y >= PanelState.panelHeight, true);
1866 function test_lifecyclePolicyForNonTouchApp_data() {
1867 return [
1868 {tag: "phone", formFactor: "phone", usageScenario: "phone"},
1869 {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"}
1873 function test_lifecyclePolicyForNonTouchApp(data) {
1874 loadShell(data.formFactor);
1875 shell.usageScenario = data.usageScenario;
1877 // Add two main stage apps, the second in order to suspend the first.
1878 // LibreOffice has isTouchApp set to false by our mocks.
1879 var app1 = ApplicationManager.startApplication("libreoffice");
1880 waitUntilAppWindowIsFullyLoaded(app1);
1881 var app2 = ApplicationManager.startApplication("gallery-app");
1882 waitUntilAppWindowIsFullyLoaded(app2);
1884 // Sanity checking
1885 compare(app1.stage, ApplicationInfoInterface.MainStage);
1886 compare(app2.stage, ApplicationInfoInterface.MainStage);
1887 verify(!app1.isTouchApp);
1888 verify(!app1.session.lastSurface.activeFocus);
1890 // Make sure app1 is exempt with a requested suspend
1891 verify(app1.exemptFromLifecycle);
1892 compare(app1.requestedState, ApplicationInfoInterface.RequestedSuspended);
1895 function test_lifecyclePolicyExemption_data() {
1896 return [
1897 {tag: "phone", formFactor: "phone", usageScenario: "phone"},
1898 {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"}
1902 function test_lifecyclePolicyExemption(data) {
1903 loadShell(data.formFactor);
1904 shell.usageScenario = data.usageScenario;
1906 GSettingsController.setLifecycleExemptAppids(["webbrowser-app"]);
1908 // Add two main stage apps, the second in order to suspend the first
1909 var app1 = ApplicationManager.startApplication("webbrowser-app");
1910 waitUntilAppWindowIsFullyLoaded(app1);
1911 var app2 = ApplicationManager.startApplication("gallery-app");
1912 waitUntilAppWindowIsFullyLoaded(app2);
1914 // Sanity checking
1915 compare(app1.stage, ApplicationInfoInterface.MainStage);
1916 compare(app2.stage, ApplicationInfoInterface.MainStage);
1917 verify(!app1.session.lastSurface.activeFocus);
1919 // Make sure app1 is exempt with a requested suspend
1920 verify(app1.exemptFromLifecycle);
1921 compare(app1.requestedState, ApplicationInfoInterface.RequestedSuspended);
1924 function test_switchToStagedForcesLegacyAppClosing_data() {
1925 return [
1926 {tag: "forceClose", replug: false },
1927 {tag: "replug", replug: true }
1928 ];
1931 function test_switchToStagedForcesLegacyAppClosing(data) {
1932 loadShell("desktop")
1933 shell.usageScenario = "desktop"
1934 waitForRendering(shell);
1936 ApplicationManager.startApplication("camera-app")
1938 shell.usageScenario = "phone"
1939 waitForRendering(shell);
1941 // No legacy app running yet... Popup must *not* show.
1942 var popup = findChild(root, "modeSwitchWarningDialog");
1943 compare(popup, null);
1945 shell.usageScenario = "desktop"
1946 waitForRendering(shell);
1948 // Now start a legacy app
1949 ApplicationManager.startApplication("libreoffice")
1951 shell.usageScenario = "phone"
1952 waitForRendering(shell);
1954 // The popup must appear now
1955 popup = findChild(root, "modeSwitchWarningDialog");
1956 compare(popup !== null, true);
1958 if (data.replug) {
1959 shell.usageScenario = "desktop"
1960 waitForRendering(shell);
1962 } else {
1963 var forceCloseButton = findChild(popup, "forceCloseButton");
1964 mouseClick(forceCloseButton, forceCloseButton.width / 2, forceCloseButton.height / 2);
1965 waitForRendering(root);
1968 // Popup must be gone now
1969 popup = findChild(root, "modeSwitchWarningDialog");
1970 compare(popup === null, true);
1972 if (data.replug) {
1973 // Libreoffice must still be running
1974 compare(ApplicationManager.findApplication("libreoffice") !== null, true);
1975 } else {
1976 // Libreoffice must be gone now
1977 compare(ApplicationManager.findApplication("libreoffice") === null, true);
1981 function test_inputEventsOnEdgesEndUpInAppSurface_data() {
1982 return [
1983 { tag: "phone", repeaterName: "spreadRepeater" },
1984 { tag: "tablet", repeaterName: "spreadRepeater" },
1985 { tag: "desktop", repeaterName: "appRepeater" },
1989 function test_inputEventsOnEdgesEndUpInAppSurface(data) {
1990 loadShell(data.tag);
1991 shell.usageScenario = data.tag;
1992 waitForRendering(shell);
1993 swipeAwayGreeter();
1995 // Let's open a fullscreen app
1996 var app = ApplicationManager.startApplication("camera-app");
1997 waitUntilAppWindowIsFullyLoaded(app);
1999 var appRepeater = findChild(shell, data.repeaterName);
2000 var topmostAppDelegate = appRepeater.itemAt(0);
2001 verify(topmostAppDelegate);
2003 var topmostSurfaceItem = findChild(topmostAppDelegate, "surfaceItem");
2004 verify(topmostSurfaceItem);
2006 mouseClick(shell, 1, shell.height / 2);
2007 compare(topmostSurfaceItem.mousePressCount, 1);
2008 compare(topmostSurfaceItem.mouseReleaseCount, 1);
2010 mouseClick(shell, shell.width - 1, shell.height / 2);
2011 compare(topmostSurfaceItem.mousePressCount, 2);
2012 compare(topmostSurfaceItem.mouseReleaseCount, 2);
2014 tap(shell, 1, shell.height / 2);
2015 compare(topmostSurfaceItem.touchPressCount, 1);
2016 compare(topmostSurfaceItem.touchReleaseCount, 1);
2018 tap(shell, shell.width - 1, shell.height / 2);
2019 compare(topmostSurfaceItem.touchPressCount, 2);
2020 compare(topmostSurfaceItem.touchReleaseCount, 2);