on 8a93021 help sign in
unity8/tests/qmltests/tst_Shell.qml
1192 lines 46 kB history raw
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
4 * Authors:
5 * Daniel d'Andrada <daniel.dandrada@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
20import QtQuick 2.0
21import QtTest 1.0
22import AccountsService 0.1
23import GSettings 1.0
24import LightDM 0.1 as LightDM
25import Ubuntu.Components 1.1
26import Ubuntu.Components.ListItems 1.0 as ListItem
27import Ubuntu.Telephony 0.1 as Telephony
28import Unity.Application 0.1
29import Unity.Connectivity 0.1
30import Unity.Indicators 0.1
31import Unity.Notifications 1.0
32import Unity.Test 0.1
33import Powerd 0.1
34import Wizard 0.1 as Wizard
36import "../../qml"
38Rectangle {
39 id: root
40 color: "grey"
41 width: units.gu(100) + controls.width
42 height: units.gu(71)
44 Component.onCompleted: {
45 // must set the mock mode before loading the Shell
46 LightDM.Greeter.mockMode = "single";
47 LightDM.Users.mockMode = "single";
48 shellLoader.active = true;
49 }
51 QtObject {
52 id: applicationArguments
54 function hasGeometry() {
55 return false;
56 }
58 function width() {
59 return 0;
60 }
62 function height() {
63 return 0;
64 }
65 }
67 Item {
68 anchors.left: root.left
69 anchors.right: controls.left
70 anchors.top: root.top
71 anchors.bottom: root.bottom
72 Loader {
73 id: shellLoader
74 focus: true
76 anchors.centerIn: parent
78 state: "phone"
79 states: [
80 State {
81 name: "phone"
82 PropertyChanges {
83 target: shellLoader
84 width: units.gu(40)
85 height: units.gu(71)
86 }
87 },
88 State {
89 name: "tablet"
90 PropertyChanges {
91 target: shellLoader
92 width: units.gu(100)
93 height: units.gu(71)
94 }
95 }
96 ]
98 active: false
99 property bool itemDestroyed: false
100 sourceComponent: Component {
101 Shell {
102 property string indicatorProfile: "phone"
104 Component.onDestruction: {
105 shellLoader.itemDestroyed = true;
106 }
107 }
108 }
109 }
110 }
112 Rectangle {
113 id: controls
114 color: "darkgrey"
115 width: units.gu(30)
116 anchors.top: root.top
117 anchors.bottom: root.bottom
118 anchors.right: root.right
120 Column {
121 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
122 spacing: units.gu(1)
123 Row {
124 anchors { left: parent.left; right: parent.right }
125 Button {
126 text: "Show Greeter"
127 activeFocusOnPress: false
128 onClicked: {
129 if (shellLoader.status !== Loader.Ready)
130 return;
132 var greeter = testCase.findChild(shellLoader.item, "greeter");
133 if (!greeter.shown) {
134 LightDM.Greeter.showGreeter();
135 }
136 }
137 }
138 }
139 ListItem.ItemSelector {
140 anchors { left: parent.left; right: parent.right }
141 activeFocusOnPress: false
142 text: "LightDM mock mode"
143 model: ["single", "single-passphrase", "single-pin", "full"]
144 onSelectedIndexChanged: {
145 shellLoader.active = false;
146 LightDM.Greeter.mockMode = model[selectedIndex];
147 LightDM.Users.mockMode = model[selectedIndex];
148 shellLoader.active = true;
149 }
150 }
151 ListItem.ItemSelector {
152 anchors { left: parent.left; right: parent.right }
153 activeFocusOnPress: false
154 text: "Size"
155 model: ["phone", "tablet"]
156 onSelectedIndexChanged: {
157 shellLoader.active = false;
158 shellLoader.state = model[selectedIndex];
159 shellLoader.active = true;
160 }
161 MouseTouchEmulationCheckbox { color: "white" }
162 }
163 }
164 }
166 Component {
167 id: mockNotification
169 QtObject {
170 function invokeAction(actionId) {
171 mockNotificationsModel.actionInvoked(actionId)
172 }
173 }
174 }
175 ListModel {
176 id: mockNotificationsModel
178 signal actionInvoked(string actionId)
180 function getRaw(id) {
181 return mockNotification.createObject(mockNotificationsModel)
182 }
184 onActionInvoked: {
185 if(actionId == "ok_id") {
186 mockNotificationsModel.clear()
187 }
188 }
189 }
191 SignalSpy {
192 id: launcherShowDashHomeSpy
193 signalName: "showDashHome"
194 }
196 SignalSpy {
197 id: sessionSpy
198 signalName: "sessionStarted"
199 }
201 SignalSpy {
202 id: dashCommunicatorSpy
203 signalName: "setCurrentScopeCalled"
204 }
206 SignalSpy {
207 id: unlockAllModemsSpy
208 target: Connectivity
209 signalName: "unlockingAllModems"
210 }
212 SignalSpy {
213 id: notificationActionSpy
214 target: mockNotificationsModel
215 signalName: "actionInvoked"
216 }
218 Telephony.CallEntry {
219 id: phoneCall
220 phoneNumber: "+447812221111"
221 }
223 UnityTestCase {
224 id: testCase
225 name: "Shell"
226 when: windowShown
228 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
230 function init() {
231 if (shellLoader.active) {
232 // happens for the very first test function as shell
233 // is loaded by default
234 tearDown();
235 }
237 }
239 function cleanup() {
240 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
241 tearDown();
242 }
244 function loadShell(formFactor) {
245 shellLoader.state = formFactor;
246 shellLoader.active = true;
247 tryCompare(shellLoader, "status", Loader.Ready);
248 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
249 tryCompare(shell, "enabled", true); // enabled by greeter when ready
251 sessionSpy.target = findChild(shell, "greeter")
252 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator");
254 var launcher = findChild(shell, "launcher");
255 launcherShowDashHomeSpy.target = launcher;
257 waitForGreeterToStabilize();
258 }
260 function waitForGreeterToStabilize() {
261 var greeter = findChild(shell, "greeter");
262 verify(greeter);
264 var loginList = findChild(greeter, "loginList");
265 // Only present in WideView
266 if (loginList) {
267 var userList = findChild(loginList, "userList");
268 verify(userList);
269 tryCompare(userList, "movingInternally", false);
270 }
271 }
273 function tearDown() {
274 launcherShowDashHomeSpy.target = null;
276 shellLoader.itemDestroyed = false;
278 shellLoader.active = false;
280 tryCompare(shellLoader, "status", Loader.Null);
281 tryCompare(shellLoader, "item", null);
282 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
283 // item might still be alive. So if we set Loader.active back to true
284 // again right now we will get the very same Shell instance back. So no reload
285 // actually took place. Likely because Loader waits until the next event loop
286 // iteration to do its work. So to ensure the reload, we will wait until the
287 // Shell instance gets destroyed.
288 tryCompare(shellLoader, "itemDestroyed", true);
290 setLightDMMockMode("single"); // back to the default value
292 AccountsService.demoEdges = false;
293 Wizard.System.wizardEnabled = false;
295 // kill all (fake) running apps
296 killApps(ApplicationManager);
298 unlockAllModemsSpy.clear()
299 LightDM.Greeter.authenticate(""); // reset greeter
301 sessionSpy.clear();
302 }
304 function killApps() {
305 while (ApplicationManager.count > 1) {
306 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
307 ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
308 }
309 compare(ApplicationManager.count, 1)
310 }
312 function test_snapDecisionDismissalReturnsFocus() {
313 loadShell("phone");
314 swipeAwayGreeter();
315 var notifications = findChild(shell, "notificationList");
316 var app = ApplicationManager.startApplication("camera-app");
317 var stage = findChild(shell, "stage")
318 // Open an application and focus
319 waitUntilApplicationWindowIsFullyVisible(app);
320 ApplicationManager.focusApplication(app);
321 tryCompare(app.session.surface, "activeFocus", true);
323 notifications.model = mockNotificationsModel;
325 // FIXME: Hack: UnitySortFilterProxyModelQML doesn't work with QML ListModels which we use
326 // for mocking here (RoleType can't be found in the QML model). As we only need to show
327 // one SnapDecision lets just disable the filtering and make appear any notification as a
328 // SnapDecision.
329 var snapDecisionProxyModel = findInvisibleChild(shell, "snapDecisionProxyModel");
330 snapDecisionProxyModel.filterRegExp = RegExp("");
332 // Pop-up a notification
333 addSnapDecisionNotification();
334 waitForRendering(shell);
336 // Make sure the notification really opened
337 var notification = findChild(notifications, "notification" + (mockNotificationsModel.count - 1));
338 verify(notification !== undefined && notification != null, "notification wasn't found");
339 tryCompare(notification, "height", notification.implicitHeight)
340 waitForRendering(notification);
342 // Make sure activeFocus went away from the app window
343 tryCompare(app.session.surface, "activeFocus", false);
344 tryCompare(stage, "interactive", false);
346 // Clicking the button should dismiss the notification and return focus
347 var buttonAccept = findChild(notification, "notify_button0");
348 mouseClick(buttonAccept);
350 // Make sure we're back to normal
351 tryCompare(app.session.surface, "activeFocus", true);
352 compare(stage.interactive, true, "Stages not interactive again after modal notification has closed");
353 }
355 function addSnapDecisionNotification() {
356 var n = {
357 type: Notification.SnapDecision,
358 hints: {"x-canonical-private-affirmative-tint": "true"},
359 summary: "Tom Ato",
360 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.",
361 icon: "../graphics/avatars/funky.png",
362 secondaryIcon: "../graphics/applicationIcons/facebook.png",
363 actions: [{ id: "ok_id", label: "Ok"},
364 { id: "cancel_id", label: "Cancel"},
365 { id: "notreally_id", label: "Not really"},
366 { id: "noway_id", label: "messages:No way"},
367 { id: "nada_id", label: "messages:Nada"}]
368 }
370 mockNotificationsModel.append(n)
371 }
373 function test_phoneLeftEdgeDrag_data() {
374 return [
375 {tag: "without launcher",
376 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
377 launcherHides: true, greeterShown: false},
379 {tag: "with launcher",
380 revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
381 launcherHides: true, greeterShown: false},
383 {tag: "small swipe",
384 revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
385 launcherHides: false, greeterShown: false},
387 {tag: "long swipe",
388 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
389 launcherHides: true, greeterShown: false},
391 {tag: "small swipe with greeter",
392 revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app",
393 launcherHides: false, greeterShown: true},
395 {tag: "long swipe with greeter",
396 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
397 launcherHides: true, greeterShown: true},
399 {tag: "swipe over dash",
400 revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
401 launcherHides: false, greeterShown: false},
402 ];
403 }
405 function test_phoneLeftEdgeDrag(data) {
406 loadShell("phone");
407 swipeAwayGreeter();
408 dragLauncherIntoView();
409 tapOnAppIconInLauncher();
410 waitUntilApplicationWindowIsFullyVisible();
411 ApplicationManager.focusApplication(data.focusedApp)
412 waitUntilApplicationWindowIsFullyVisible();
414 var greeter = findChild(shell, "greeter");
415 if (data.greeterShown) {
416 showGreeter();
417 }
419 if (data.revealLauncher) {
420 dragLauncherIntoView();
421 }
423 swipeFromLeftEdge(data.swipeLength);
424 if (data.appHides) {
425 waitUntilDashIsFocused();
426 tryCompare(greeter, "shown", false);
427 } else {
428 waitUntilApplicationWindowIsFullyVisible();
429 compare(greeter.fullyShown, data.greeterShown);
430 }
432 var launcher = findChild(shell, "launcherPanel");
433 tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
435 // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
436 var animateTimer = findInvisibleChild(shell, "animateTimer");
437 compare(animateTimer.nextState, "visible");
438 }
440 function test_tabletLeftEdgeDrag_data() {
441 return [
442 {tag: "without password", user: "no-password", loggedIn: true, demo: false},
443 {tag: "with password", user: "has-password", loggedIn: false, demo: false},
444 {tag: "with demo", user: "has-password", loggedIn: true, demo: true},
445 ]
446 }
448 function test_tabletLeftEdgeDrag(data) {
449 setLightDMMockMode("full");
450 loadShell("tablet");
452 selectUser(data.user)
454 AccountsService.demoEdges = data.demo
455 var tutorial = findChild(shell, "tutorial");
456 tryCompare(tutorial, "running", data.demo);
458 swipeFromLeftEdge(shell.width * 0.75)
459 wait(500) // to give time to handle dash() signal from Launcher
460 confirmLoggedIn(data.loggedIn)
461 }
463 function test_suspend() {
464 loadShell("phone");
465 swipeAwayGreeter();
466 var greeter = findChild(shell, "greeter");
468 // Launch an app from the launcher
469 dragLauncherIntoView();
470 tapOnAppIconInLauncher();
471 waitUntilApplicationWindowIsFullyVisible();
473 var mainAppId = ApplicationManager.focusedApplicationId;
474 verify(mainAppId != "");
475 var mainApp = ApplicationManager.findApplication(mainAppId);
476 verify(mainApp);
477 tryCompare(mainApp, "state", ApplicationInfoInterface.Running);
479 // Suspend while call is active...
480 callManager.foregroundCall = phoneCall;
481 Powerd.status = Powerd.Off;
482 tryCompare(greeter, "shown", false);
484 // Now try again after ending call
485 callManager.foregroundCall = null;
486 Powerd.status = Powerd.On;
487 Powerd.status = Powerd.Off;
488 tryCompare(greeter, "fullyShown", true);
490 tryCompare(ApplicationManager, "suspended", true);
491 compare(mainApp.state, ApplicationInfoInterface.Suspended);
493 // And wake up
494 Powerd.status = Powerd.On;
495 tryCompare(greeter, "fullyShown", true);
497 // Swipe away greeter to focus app
499 // greeter unloads its internal components when hidden
500 // and reloads them when shown. Thus we have to do this
501 // again before interacting with it otherwise any
502 // DirectionalDragAreas in there won't be easily fooled by
503 // fake swipes.
504 removeTimeConstraintsFromDirectionalDragAreas(greeter);
505 swipeAwayGreeter();
507 tryCompare(ApplicationManager, "suspended", false);
508 compare(mainApp.state, ApplicationInfoInterface.Running);
509 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
510 }
512 function swipeAwayGreeter() {
513 var greeter = findChild(shell, "greeter");
514 tryCompare(greeter, "fullyShown", true);
516 var touchX = shell.width - (shell.edgeSize / 2);
517 var touchY = shell.height / 2;
518 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
520 // wait until the animation has finished
521 tryCompare(greeter, "shown", false);
522 waitForRendering(greeter);
523 }
525 function selectUserAtIndex(i) {
526 // We could be anywhere in list; find target index to know which direction
527 var greeter = findChild(shell, "greeter")
528 var userlist = findChild(greeter, "userList")
529 if (userlist.currentIndex == i)
530 keyClick(Qt.Key_Escape) // Reset state if we're not moving
531 while (userlist.currentIndex != i) {
532 var next = userlist.currentIndex + 1
533 if (userlist.currentIndex > i) {
534 next = userlist.currentIndex - 1
535 }
536 tap(findChild(greeter, "username"+next));
537 tryCompare(userlist, "currentIndex", next)
538 tryCompare(userlist, "movingInternally", false)
539 }
540 tryCompare(shell, "enabled", true); // wait for PAM to settle
541 }
543 function selectUser(name) {
544 // Find index of user with the right name
545 for (var i = 0; i < LightDM.Users.count; i++) {
546 if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == name) {
547 break
548 }
549 }
550 if (i == LightDM.Users.count) {
551 fail("Didn't find name")
552 return -1
553 }
554 selectUserAtIndex(i)
555 return i
556 }
558 function clickPasswordInput(isButton) {
559 var greeter = findChild(shell, "greeter")
560 tryCompare(greeter, "fullyShown", true);
562 var passwordMouseArea = findChild(shell, "passwordMouseArea")
563 tryCompare(passwordMouseArea, "enabled", isButton)
565 var passwordInput = findChild(shell, "passwordInput")
566 mouseClick(passwordInput)
567 }
569 function confirmLoggedIn(loggedIn) {
570 var greeter = findChild(shell, "greeter");
571 tryCompare(greeter, "shown", loggedIn ? false : true);
572 verify(loggedIn ? sessionSpy.count > 0 : sessionSpy.count === 0);
573 }
575 function setLightDMMockMode(mode) {
576 LightDM.Greeter.mockMode = mode;
577 LightDM.Users.mockMode = mode;
578 }
580 /*
581 Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
583 When the user minimizes an application (left-edge swipe) he should always end up in the
584 "Applications" scope view.
586 Steps:
587 - reveal launcher and launch an app that covers the dash
588 - perform long left edge swipe to go minimize the app and go back to the dash.
589 - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
590 */
591 function test_minimizingAppTakesToDashApps() {
592 loadShell("phone");
593 swipeAwayGreeter();
594 dragLauncherIntoView();
596 // Launch an app from the launcher
597 tapOnAppIconInLauncher();
599 waitUntilApplicationWindowIsFullyVisible();
601 verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
603 dashCommunicatorSpy.clear();
604 // Minimize the application we just launched
605 swipeFromLeftEdge(shell.width * 0.75);
607 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
609 compare(dashCommunicatorSpy.count, 1);
610 compare(dashCommunicatorSpy.signalArguments[0][0], 0);
611 }
613 function test_showInputMethod() {
614 loadShell("phone");
615 swipeAwayGreeter();
616 var item = findChild(shell, "inputMethod");
617 var surface = SurfaceManager.inputMethodSurface();
619 surface.setState(MirSurfaceItem.Minimized);
620 tryCompare(item, "visible", false);
622 surface.setState(MirSurfaceItem.Restored);
623 tryCompare(item, "visible", true);
625 surface.setState(MirSurfaceItem.Minimized);
626 tryCompare(item, "visible", false);
628 surface.setState(MirSurfaceItem.Maximized);
629 tryCompare(item, "visible", true);
631 surface.setState(MirSurfaceItem.Minimized);
632 tryCompare(item, "visible", false);
633 }
635 // wait until any transition animation has finished
636 function waitUntilTransitionsEnd(stateGroup) {
637 var transitions = stateGroup.transitions;
638 for (var i = 0; i < transitions.length; ++i) {
639 var transition = transitions[i];
640 tryCompare(transition, "running", false, 2000);
641 }
642 }
644 // Wait until the ApplicationWindow for the given Application object is fully loaded
645 // (ie, the real surface has replaced the splash screen)
646 function waitUntilAppWindowIsFullyLoaded(app) {
647 var appWindow = findChild(shell, "appWindow_" + app.appId);
648 var appWindowStateGroup = findInvisibleChild(appWindow, "applicationWindowStateGroup");
649 tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
650 waitUntilTransitionsEnd(appWindowStateGroup);
651 }
653 function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
654 loadShell("phone");
655 swipeAwayGreeter();
656 var app = ApplicationManager.startApplication("dialer-app");
657 waitUntilAppWindowIsFullyLoaded(app);
659 tryCompare(app.session.surface, "activeFocus", true);
661 // Drag the indicators panel half-open
662 var touchX = shell.width / 2;
663 var indicators = findChild(shell, "indicators");
664 touchFlick(indicators,
665 touchX /* fromX */, indicators.minimizedPanelHeight * 0.5 /* fromY */,
666 touchX /* toX */, shell.height * 0.5 /* toY */,
667 true /* beginTouch */, false /* endTouch */);
668 verify(indicators.partiallyOpened);
670 tryCompare(app.session.surface, "activeFocus", false);
672 // And finish getting it open
673 touchFlick(indicators,
674 touchX /* fromX */, shell.height * 0.5 /* fromY */,
675 touchX /* toX */, shell.height * 0.9 /* toY */,
676 false /* beginTouch */, true /* endTouch */);
677 tryCompare(indicators, "fullyOpened", true);
679 tryCompare(app.session.surface, "activeFocus", false);
681 dragToCloseIndicatorsPanel();
683 tryCompare(app.session.surface, "activeFocus", true);
684 }
686 function test_launchedAppHasActiveFocus_data() {
687 return [
688 {tag:"phone", formFactor:"phone"},
689 {tag:"tablet", formFactor:"tablet"},
690 ];
691 }
693 function test_launchedAppHasActiveFocus(data) {
694 loadShell(data.formFactor);
695 swipeAwayGreeter();
697 var dialerApp = ApplicationManager.startApplication("webbrowser-app");
698 verify(dialerApp);
699 waitUntilAppSurfaceShowsUp("webbrowser-app")
701 verify(dialerApp.session.surface);
703 tryCompare(dialerApp.session.surface, "activeFocus", true);
704 }
706 function waitUntilAppSurfaceShowsUp(appId) {
707 var appWindow = findChild(shell, "appWindow_" + appId);
708 verify(appWindow);
709 var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
710 verify(appWindowStates);
711 tryCompare(appWindowStates, "state", "surface");
712 }
714 function dragToCloseIndicatorsPanel() {
715 var indicators = findChild(shell, "indicators");
717 var touchStartX = shell.width / 2;
718 var touchStartY = shell.height - (indicators.minimizedPanelHeight * 0.5);
719 touchFlick(shell,
720 touchStartX, touchStartY,
721 touchStartX, shell.height * 0.1);
723 tryCompare(indicators, "fullyClosed", true);
724 }
726 function dragLauncherIntoView() {
727 var launcher = findChild(shell, "launcher");
728 var launcherPanel = findChild(launcher, "launcherPanel");
729 verify(launcherPanel.x = - launcherPanel.width);
731 var touchStartX = 2;
732 var touchStartY = shell.height / 2;
733 touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
735 tryCompare(launcherPanel, "x", 0);
736 tryCompare(launcher, "state", "visible");
737 }
739 function tapOnAppIconInLauncher() {
740 var launcherPanel = findChild(shell, "launcherPanel");
742 // pick the first icon, the one at the top.
743 var appIcon = findChild(launcherPanel, "launcherDelegate0")
744 tap(appIcon, appIcon.width / 2, appIcon.height / 2);
745 }
747 function showIndicators() {
748 var indicators = findChild(shell, "indicators");
749 indicators.show();
750 tryCompare(indicators, "fullyOpened", true);
751 }
753 function hideIndicators() {
754 var indicators = findChild(shell, "indicators");
755 if (indicators.fullyOpened) {
756 indicators.hide();
757 }
758 }
760 function waitUntilApplicationWindowIsFullyVisible() {
761 var appDelegate = findChild(shell, "appDelegate0")
762 var surfaceContainer = findChild(appDelegate, "surfaceContainer");
763 tryCompareFunction(function() { return surfaceContainer.surface !== null; }, true);
764 }
766 function waitUntilDashIsFocused() {
767 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
768 }
770 function swipeFromLeftEdge(swipeLength) {
771 var touchStartX = 2;
772 var touchStartY = shell.height / 2;
773 touchFlick(shell,
774 touchStartX , touchStartY,
775 touchStartX + swipeLength, touchStartY);
776 }
778 function itemIsOnScreen(item) {
779 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
781 return itemRectInShell.x >= 0
782 && itemRectInShell.y >= 0
783 && itemRectInShell.x + itemRectInShell.width <= shell.width
784 && itemRectInShell.y + itemRectInShell.height <= shell.height;
785 }
787 function showGreeter() {
788 var greeter = findChild(shell, "greeter");
789 LightDM.Greeter.showGreeter();
790 waitForRendering(greeter);
791 tryCompare(greeter, "fullyShown", true);
793 // greeter unloads its internal components when hidden
794 // and reloads them when shown. Thus we have to do this
795 // again before interacting with it otherwise any
796 // DirectionalDragAreas in there won't be easily fooled by
797 // fake swipes.
798 removeTimeConstraintsFromDirectionalDragAreas(greeter);
799 }
801 function test_greeterDoesNotChangeIndicatorProfile() {
802 loadShell("phone");
803 swipeAwayGreeter();
804 var panel = findChild(shell, "panel");
805 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
807 showGreeter();
808 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
810 LightDM.Greeter.hideGreeter();
811 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
812 }
814 function test_shellProfileChangesReachIndicators() {
815 loadShell("phone");
816 swipeAwayGreeter();
817 var panel = findChild(shell, "panel");
819 shell.indicatorProfile = "test1";
820 for (var i = 0; i < panel.indicators.indicatorsModel.count; ++i) {
821 var properties = panel.indicators.indicatorsModel.data(i, IndicatorsModelRole.IndicatorProperties);
822 verify(properties["menuObjectPath"].substr(-5), "test1");
823 }
825 shell.indicatorProfile = "test2";
826 for (var i = 0; i < panel.indicators.indicatorsModel.count; ++i) {
827 var properties = panel.indicators.indicatorsModel.data(i, IndicatorsModelRole.IndicatorProperties);
828 verify(properties["menuObjectPath"].substr(-5), "test2");
829 }
830 }
832 function test_focusRequestedHidesGreeter() {
833 loadShell("phone");
834 swipeAwayGreeter();
835 var greeter = findChild(shell, "greeter");
837 var app = ApplicationManager.startApplication("dialer-app");
838 // wait until the app is fully loaded (ie, real surface replaces splash screen)
839 tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
841 // Minimize the application we just launched
842 swipeFromLeftEdge(shell.width * 0.75);
844 waitUntilDashIsFocused();
846 showGreeter();
848 // The main point of this test
849 ApplicationManager.requestFocusApplication("dialer-app");
850 tryCompare(greeter, "shown", false);
851 waitForRendering(greeter);
852 }
854 function test_focusRequestedHidesIndicators() {
855 loadShell("phone");
856 swipeAwayGreeter();
857 var indicators = findChild(shell, "indicators");
859 showIndicators();
861 var oldCount = ApplicationManager.count;
862 ApplicationManager.startApplication("camera-app");
863 tryCompare(ApplicationManager, "count", oldCount + 1);
865 tryCompare(indicators, "fullyClosed", true);
866 }
868 function test_showAndHideGreeterDBusCalls() {
869 loadShell("phone");
870 swipeAwayGreeter();
871 var greeter = findChild(shell, "greeter")
872 tryCompare(greeter, "shown", false)
873 waitForRendering(greeter);
874 LightDM.Greeter.showGreeter()
875 waitForRendering(greeter)
876 tryCompare(greeter, "fullyShown", true)
877 LightDM.Greeter.hideGreeter()
878 tryCompare(greeter, "shown", false)
879 }
881 function test_greeterLoginsAutomaticallyWhenNoPasswordSet() {
882 loadShell("phone");
883 swipeAwayGreeter();
885 sessionSpy.clear();
886 verify(sessionSpy.valid);
888 showGreeter();
890 tryCompare(sessionSpy, "count", 1);
891 }
893 function test_fullscreen() {
894 loadShell("phone");
895 swipeAwayGreeter();
896 var panel = findChild(shell, "panel");
897 compare(panel.fullscreenMode, false);
898 ApplicationManager.startApplication("camera-app");
899 tryCompare(panel, "fullscreenMode", true);
900 ApplicationManager.startApplication("dialer-app");
901 tryCompare(panel, "fullscreenMode", false);
902 ApplicationManager.requestFocusApplication("camera-app");
903 tryCompare(panel, "fullscreenMode", true);
904 ApplicationManager.requestFocusApplication("dialer-app");
905 tryCompare(panel, "fullscreenMode", false);
906 }
908 function test_leftEdgeDragFullscreen() {
909 loadShell("phone");
910 swipeAwayGreeter();
911 var panel = findChild(shell, "panel");
912 tryCompare(panel, "fullscreenMode", false)
914 ApplicationManager.startApplication("camera-app");
915 tryCompare(panel, "fullscreenMode", true)
917 var touchStartX = 2;
918 var touchStartY = shell.height / 2;
920 touchFlick(shell, touchStartX, touchStartY, units.gu(2), touchStartY, true, false);
922 compare(panel.fullscreenMode, true);
924 touchFlick(shell, units.gu(2), touchStartY, shell.width * 0.5, touchStartY, false, false);
926 tryCompare(panel, "fullscreenMode", false);
928 touchRelease(shell);
929 }
931 function test_unlockedProperties() {
932 loadShell("phone");
933 swipeAwayGreeter();
934 // Confirm that various properties have the correct values when unlocked
935 var greeter = findChild(shell, "greeter");
936 tryCompare(greeter, "locked", false);
938 var launcher = findChild(shell, "launcher")
939 tryCompare(launcher, "available", true)
941 var indicators = findChild(shell, "indicators")
942 tryCompare(indicators, "available", true)
943 }
945 function test_unlockAllModemsOnBoot() {
946 loadShell("phone");
947 swipeAwayGreeter();
948 tryCompare(unlockAllModemsSpy, "count", 1)
949 }
951 function test_unlockAllModemsAfterWizard() {
952 Wizard.System.wizardEnabled = true;
953 loadShell("phone");
955 var wizard = findChild(shell, "wizard");
956 compare(wizard.active, true);
957 compare(Wizard.System.wizardEnabled, true);
958 compare(unlockAllModemsSpy.count, 0);
960 wizard.hide();
961 tryCompare(wizard, "active", false);
962 compare(Wizard.System.wizardEnabled, false);
963 compare(unlockAllModemsSpy.count, 1);
964 }
966 function test_wizardEarlyExit() {
967 Wizard.System.wizardEnabled = true;
968 AccountsService.demoEdges = true;
969 loadShell("phone");
971 var wizard = findChild(shell, "wizard");
972 var tutorial = findChild(shell, "tutorial");
973 tryCompare(wizard, "active", true);
974 tryCompare(tutorial, "running", true);
975 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
977 // Make sure we stay running when nothing focused (can happen for
978 // a moment when we restart the dash after switching language)
979 ApplicationManager.stopApplication("unity8-dash");
980 tryCompare(ApplicationManager, "focusedApplicationId", "");
981 compare(wizard.shown, true);
982 compare(tutorial.running, true);
984 // And make sure we stay running when dash focused again
985 ApplicationManager.startApplication("unity8-dash");
986 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
987 compare(wizard.shown, true);
988 compare(tutorial.running, true);
990 // And make sure we stop when something else is focused
991 ApplicationManager.startApplication("gallery-app");
992 tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
993 compare(wizard.shown, false);
994 compare(tutorial.running, false);
995 tryCompare(AccountsService, "demoEdges", false);
996 tryCompare(Wizard.System, "wizardEnabled", false);
998 var tutorialLeft = findChild(tutorial, "tutorialLeft");
999 compare(tutorialLeft, null); // should be destroyed with tutorial
1002 function test_tapOnRightEdgeReachesApplicationSurface() {
1003 loadShell("phone");
1004 swipeAwayGreeter();
1005 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1006 var topmostSurface = findChild(topmostSpreadDelegate, "surfaceContainer").surface;
1007 var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1009 topmostSurface.touchPressCount = 0;
1010 topmostSurface.touchReleaseCount = 0;
1012 var tapPoint = rightEdgeDragArea.mapToItem(shell, rightEdgeDragArea.width / 2,
1013 rightEdgeDragArea.height / 2);
1015 tap(shell, tapPoint.x, tapPoint.y);
1017 tryCompare(topmostSurface, "touchPressCount", 1);
1018 tryCompare(topmostSurface, "touchReleaseCount", 1);
1021 /*
1022 Perform a right edge drag over an application surface and check
1023 that no touch event was sent to it (ie, they were all consumed
1024 by the right-edge drag area)
1025 */
1026 function test_rightEdgeDragDoesNotReachApplicationSurface() {
1027 loadShell("phone");
1028 swipeAwayGreeter();
1029 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1030 var topmostSurface = findChild(topmostSpreadDelegate, "surfaceContainer").surface;
1031 var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1033 topmostSurface.touchPressCount = 0;
1034 topmostSurface.touchReleaseCount = 0;
1036 var gestureStartPoint = rightEdgeDragArea.mapToItem(shell, rightEdgeDragArea.width / 2,
1037 rightEdgeDragArea.height / 2);
1039 touchFlick(shell,
1040 gestureStartPoint.x /* fromX */, gestureStartPoint.y /* fromY */,
1041 units.gu(1) /* toX */, gestureStartPoint.y /* toY */);
1043 tryCompare(topmostSurface, "touchPressCount", 0);
1044 tryCompare(topmostSurface, "touchReleaseCount", 0);
1047 function waitUntilFocusedApplicationIsShowingItsSurface()
1049 var spreadDelegate = findChild(shell, "appDelegate0");
1050 var appState = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
1051 tryCompare(appState, "state", "surface");
1052 var transitions = appState.transitions;
1053 for (var i = 0; i < transitions.length; ++i) {
1054 var transition = transitions[i];
1055 tryCompare(transition, "running", false, 2000);
1059 function swipeFromRightEdgeToShowAppSpread()
1061 // perform a right-edge drag to show the spread
1062 var touchStartX = shell.width - (shell.edgeSize / 2)
1063 var touchStartY = shell.height / 2;
1064 touchFlick(shell, touchStartX, touchStartY, units.gu(1) /* endX */, touchStartY /* endY */);
1066 // check if it's indeed showing the spread
1067 var stage = findChild(shell, "stage");
1068 var spreadView = findChild(stage, "spreadView");
1069 tryCompare(spreadView, "phase", 2);
1072 function test_tapUbuntuIconInLauncherOverAppSpread() {
1073 loadShell("phone");
1074 swipeAwayGreeter();
1076 waitUntilFocusedApplicationIsShowingItsSurface();
1078 swipeFromRightEdgeToShowAppSpread();
1080 var launcher = findChild(shell, "launcher");
1082 // ensure the launcher dimissal timer never gets triggered during the test run
1083 var dismissTimer = findInvisibleChild(launcher, "dismissTimer");
1084 dismissTimer.interval = 60 * 60 * 1000;
1086 dragLauncherIntoView();
1088 // Emulate a tap with a finger, where the touch position drifts during the tap.
1089 // This is to test the touch ownership changes. The tap is happening on the button
1090 // area but then drifting into the left edge drag area. This test makes sure
1091 // the touch ownership stays with the button and doesn't move over to the
1092 // left edge drag area.
1094 var buttonShowDashHome = findChild(launcher, "buttonShowDashHome");
1095 var startPos = buttonShowDashHome.mapToItem(shell,
1096 buttonShowDashHome.width * 0.2,
1097 buttonShowDashHome.height * 0.8);
1098 var endPos = buttonShowDashHome.mapToItem(shell,
1099 buttonShowDashHome.width * 0.8,
1100 buttonShowDashHome.height * 0.2);
1101 touchFlick(shell, startPos.x, startPos.y, endPos.x, endPos.y);
1104 compare(launcherShowDashHomeSpy.count, 1);
1106 // check that the stage has left spread mode.
1108 var stage = findChild(shell, "stage");
1109 var spreadView = findChild(stage, "spreadView");
1110 tryCompare(spreadView, "phase", 0);
1113 // check that the launcher got dismissed
1114 var launcherPanel = findChild(shell, "launcherPanel");
1115 tryCompare(launcherPanel, "x", -launcherPanel.width);
1118 function test_background_data() {
1119 return [
1120 {tag: "color", accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"), gsettings: "", output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
1121 {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
1122 {tag: "as-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: "", output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
1123 {tag: "gs-specified", accounts: "", gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
1124 {tag: "both-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
1125 {tag: "invalid-as", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
1126 {tag: "invalid-both", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/stillnope.png"), output: "defaultBackground"},
1129 function test_background(data) {
1130 loadShell("phone");
1131 swipeAwayGreeter();
1132 AccountsService.backgroundFile = data.accounts;
1133 var backgroundSettings = findInvisibleChild(shell, "backgroundSettings");
1134 backgroundSettings.pictureUri = data.gsettings;
1136 if (data.output === "defaultBackground") {
1137 tryCompare(shell, "background", shell.defaultBackground);
1138 } else {
1139 tryCompare(shell, "background", data.output);
1143 function test_tabletLogin_data() {
1144 return [
1145 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},
1146 {tag: "with password", user: "has-password", loggedIn: true, password: "password"},
1147 {tag: "without password", user: "no-password", loggedIn: true, password: ""},
1151 function test_tabletLogin(data) {
1152 setLightDMMockMode("full");
1153 loadShell("tablet");
1155 selectUser(data.user);
1157 clickPasswordInput(data.password === "" /* isButton */);
1159 if (data.password !== "") {
1160 typeString(data.password);
1161 keyClick(Qt.Key_Enter);
1164 confirmLoggedIn(data.loggedIn);
1167 function test_appLaunchDuringGreeter_data() {
1168 return [
1169 {tag: "auth error", user: "auth-error", loggedIn: false, passwordFocus: false},
1170 {tag: "without password", user: "no-password", loggedIn: true, passwordFocus: false},
1171 {tag: "with password", user: "has-password", loggedIn: false, passwordFocus: true},
1175 function test_appLaunchDuringGreeter(data) {
1176 setLightDMMockMode("full");
1177 loadShell("tablet");
1179 selectUser(data.user)
1181 var greeter = findChild(shell, "greeter")
1182 var app = ApplicationManager.startApplication("dialer-app")
1184 confirmLoggedIn(data.loggedIn)
1186 if (data.passwordFocus) {
1187 var passwordInput = findChild(greeter, "passwordInput")
1188 tryCompare(passwordInput, "focus", true)