@@ -47,6 +47,7 @@ Rectangle {
4747 }
4848
4949 Item {
50+ id: shellContainer
5051 anchors.left: root.left
5152 anchors.right: controls.left
5253 anchors.top: root.top
@@ -74,6 +75,18 @@ Rectangle {
7475 width: units.gu(100)
7576 height: units.gu(71)
7677 }
78+ },
79+ State {
80+ name: "desktop"
81+ PropertyChanges {
82+ target: shellLoader
83+ width: shellContainer.width
84+ height: shellContainer.height
85+ }
86+ PropertyChanges {
87+ target: mouseEmulation
88+ checked: false
89+ }
7790 }
7891 ]
7992
@@ -81,6 +94,7 @@ Rectangle {
8194 property bool itemDestroyed: false
8295 sourceComponent: Component {
8396 Shell {
97+ id: __shell
8498 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
8599 orientation: Qt.PortraitOrientation
86100 primaryOrientation: Qt.PortraitOrientation
@@ -88,6 +102,10 @@ Rectangle {
88102 Component.onDestruction: {
89103 shellLoader.itemDestroyed = true;
90104 }
105+ Component.onCompleted: {
106+ var keyMapper = testCase.findChild(__shell, "physicalKeysMapper");
107+ keyMapper.controlInsteadOfAlt = true;
108+ }
91109 }
92110 }
93111 }
@@ -112,16 +130,32 @@ Rectangle {
112130 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
113131 spacing: units.gu(1)
114132
115− Button {
116− text: "Show Greeter"
117− activeFocusOnPress: false
118− onClicked: {
119− if (shellLoader.status !== Loader.Ready)
120− return;
121−
122− var greeter = testCase.findChild(shellLoader.item, "greeter");
123− if (!greeter.shown) {
124− LightDM.Greeter.showGreeter();
133+ Row {
134+ spacing: units.gu(1)
135+ Button {
136+ text: "Show Greeter"
137+ activeFocusOnPress: false
138+ onClicked: {
139+ if (shellLoader.status !== Loader.Ready)
140+ return;
141+
142+ var greeter = testCase.findChild(shellLoader.item, "greeter");
143+ if (!greeter.shown) {
144+ LightDM.Greeter.showGreeter();
145+ }
146+ }
147+ }
148+ Button {
149+ text: "Hide Greeter"
150+ activeFocusOnPress: false
151+ onClicked: {
152+ if (shellLoader.status !== Loader.Ready)
153+ return;
154+
155+ var greeter = testCase.findChild(shellLoader.item, "greeter");
156+ if (greeter.shown) {
157+ greeter.hide()
158+ }
125159 }
126160 }
127161 }
@@ -141,7 +175,7 @@ Rectangle {
141175 anchors { left: parent.left; right: parent.right }
142176 activeFocusOnPress: false
143177 text: "Size"
144− model: ["phone", "tablet"]
178+ model: ["phone", "tablet", "desktop"]
145179 onSelectedIndexChanged: {
146180 shellLoader.active = false;
147181 shellLoader.state = model[selectedIndex];
@@ -163,6 +197,17 @@ Rectangle {
163197
164198 Label { text: "Applications"; font.bold: true }
165199
200+ Button {
201+ text: "Start all apps"
202+ width: parent.width
203+ onClicked: {
204+ for (var i = 0; i < ApplicationManager.availableApplications.length; i++) {
205+ var appId = ApplicationManager.availableApplications[i];
206+ ApplicationManager.startApplication(appId)
207+ }
208+ }
209+ }
210+
166211 Repeater {
167212 id: appRepeater
168213 model: ApplicationManager.availableApplications
@@ -226,6 +271,12 @@ Rectangle {
226271 signalName: "actionInvoked"
227272 }
228273
274+ SignalSpy {
275+ id: appRemovedSpy
276+ target: ApplicationManager
277+ signalName: "applicationRemoved"
278+ }
279+
229280 Telephony.CallEntry {
230281 id: phoneCall
231282 phoneNumber: "+447812221111"
@@ -248,9 +299,9 @@ Rectangle {
248299 }
249300
250301 function cleanup() {
302+ mouseEmulation.checked = true;
251303 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
252304 tearDown();
253− GSettingsController.setUsageMode("Staged");
254305 }
255306
256307 function loadShell(formFactor) {
@@ -269,6 +320,20 @@ Rectangle {
269320 waitForGreeterToStabilize();
270321 }
271322
323+ function loadDesktopShellWithApps() {
324+ loadShell("desktop");
325+ shell.usageScenario = "desktop"
326+ waitForRendering(root)
327+ var app1 = ApplicationManager.startApplication("dialer-app")
328+ var app2 = ApplicationManager.startApplication("webbrowser-app")
329+ var app3 = ApplicationManager.startApplication("camera-app")
330+ var app4 = ApplicationManager.startApplication("facebook-webapp")
331+ var app5 = ApplicationManager.startApplication("calendar-app")
332+ var app6 = ApplicationManager.startApplication("gallery-app")
333+ var app7 = ApplicationManager.startApplication("camera-app")
334+ waitUntilAppWindowIsFullyLoaded(app7);
335+ }
336+
272337 function waitForGreeterToStabilize() {
273338 var greeter = findChild(shell, "greeter");
274339 verify(greeter);
@@ -472,6 +537,41 @@ Rectangle {
472537 confirmLoggedIn(data.loggedIn)
473538 }
474539
540+ function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
541+ loadShell("phone");
542+ swipeAwayGreeter();
543+ dragLauncherIntoView();
544+ dashCommunicatorSpy.clear();
545+
546+ tapOnAppIconInLauncher();
547+ waitUntilApplicationWindowIsFullyVisible();
548+
549+ verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
550+
551+ //Long left swipe
552+ swipeFromLeftEdge(units.gu(30));
553+
554+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
555+
556+ compare(dashCommunicatorSpy.count, 1);
557+ }
558+
559+ function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
560+ loadShell("phone");
561+ swipeAwayGreeter();
562+ dragLauncherIntoView();
563+ dashCommunicatorSpy.clear();
564+
565+ var launcher = findChild(shell, "launcher");
566+ var dashIcon = findChild(launcher, "dashItem");
567+ verify(dashIcon != undefined);
568+ mouseClick(dashIcon);
569+
570+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
571+
572+ compare(dashCommunicatorSpy.count, 1);
573+ }
574+
475575 function test_suspend() {
476576 loadShell("phone");
477577 swipeAwayGreeter();
@@ -486,9 +586,9 @@ Rectangle {
486586 verify(mainAppId != "");
487587 var mainApp = ApplicationManager.findApplication(mainAppId);
488588 verify(mainApp);
489− tryCompare(mainApp, "state", ApplicationInfoInterface.Running);
589+ tryCompare(mainApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
490590
491− // Suspend while call is active...
591+ // Display off while call is active...
492592 callManager.foregroundCall = phoneCall;
493593 Powerd.status = Powerd.Off;
494594 tryCompare(greeter, "shown", false);
@@ -499,8 +599,7 @@ Rectangle {
499599 Powerd.status = Powerd.Off;
500600 tryCompare(greeter, "fullyShown", true);
501601
502− tryCompare(ApplicationManager, "suspended", true);
503− compare(mainApp.state, ApplicationInfoInterface.Suspended);
602+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
504603
505604 // And wake up
506605 Powerd.status = Powerd.On;
@@ -516,8 +615,7 @@ Rectangle {
516615 removeTimeConstraintsFromDirectionalDragAreas(greeter);
517616 swipeAwayGreeter();
518617
519− tryCompare(ApplicationManager, "suspended", false);
520− compare(mainApp.state, ApplicationInfoInterface.Running);
618+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedRunning);
521619 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
522620 }
523621
@@ -688,15 +786,15 @@ Rectangle {
688786
689787 function test_launchedAppHasActiveFocus_data() {
690788 return [
691− {tag: "phone", formFactor: "phone", usageMode: "Staged"},
692− {tag: "tablet", formFactor: "tablet", usageMode: "Staged"},
693− {tag: "desktop", formFactor: "tablet", usageMode: "Windowed"}
789+ {tag: "phone", formFactor: "phone", usageScenario: "phone"},
790+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"},
791+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop"}
694792 ]
695793 }
696794
697795 function test_launchedAppHasActiveFocus(data) {
698− GSettingsController.setUsageMode(data.usageMode);
699796 loadShell(data.formFactor);
797+ shell.usageScenario = data.usageScenario;
700798 swipeAwayGreeter();
701799
702800 var webApp = ApplicationManager.startApplication("webbrowser-app");
@@ -724,7 +822,7 @@ Rectangle {
724822
725823 // check that the desktop stage and window have been loaded
726824 {
727− var desktopWindow = findChild(shell, "decoratedWindow_webbrowser-app");
825+ var desktopWindow = findChild(shell, "appWindow_webbrowser-app");
728826 verify(desktopWindow);
729827 }
730828
@@ -1082,6 +1180,8 @@ Rectangle {
10821180 }
10831181
10841182 function test_tapUbuntuIconInLauncherOverAppSpread() {
1183+ launcherShowDashHomeSpy.clear();
1184+
10851185 loadShell("phone");
10861186 swipeAwayGreeter();
10871187
@@ -1228,5 +1328,272 @@ Rectangle {
12281328 var launcher = findChild(shell, "launcher");
12291329 compare(launcher.inverted, data.launcherInverted);
12301330 }
1331+
1332+ function test_unfocusedAppsGetSuspendedAfterEnteringStagedMode() {
1333+ loadShell("tablet");
1334+ shell.usageScenario = "desktop";
1335+
1336+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1337+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1338+
1339+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1340+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1341+
1342+ ApplicationManager.requestFocusApplication("unity8-dash");
1343+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1344+
1345+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1346+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1347+
1348+ shell.usageScenario = "tablet";
1349+
1350+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1351+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1352+ }
1353+
1354+ function test_unfocusedAppsAreResumedWhenEnteringWindowedMode() {
1355+ loadShell("tablet");
1356+ shell.usageScenario = "tablet";
1357+
1358+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1359+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1360+
1361+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1362+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1363+
1364+ ApplicationManager.requestFocusApplication("unity8-dash");
1365+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1366+
1367+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1368+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1369+
1370+ shell.usageScenario = "desktop";
1371+
1372+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1373+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1374+ }
1375+
1376+ function test_altTabSwitchesFocus() {
1377+ loadShell("desktop");
1378+ shell.usageScenario = "desktop"
1379+ waitForRendering(root)
1380+
1381+ var desktopStage = findChild(shell, "stage");
1382+ verify(desktopStage != null)
1383+
1384+ var app1 = ApplicationManager.startApplication("dialer-app")
1385+ waitUntilAppWindowIsFullyLoaded(app1);
1386+ var app2 = ApplicationManager.startApplication("webbrowser-app")
1387+ waitUntilAppWindowIsFullyLoaded(app2);
1388+ var app3 = ApplicationManager.startApplication("camera-app")
1389+ waitUntilAppWindowIsFullyLoaded(app3);
1390+
1391+ // Do a quick alt-tab and see if focus changes
1392+ tryCompare(app3.session.surface, "activeFocus", true)
1393+ keyClick(Qt.Key_Tab, Qt.ControlModifier)
1394+ tryCompare(app2.session.surface, "activeFocus", true)
1395+
1396+
1397+ tryCompare(desktopStage, "state", "")
1398+
1399+ // Just press Alt, make sure the spread comes up
1400+ keyPress(Qt.Key_Control);
1401+ keyClick(Qt.Key_Tab);
1402+ tryCompare(desktopStage, "state", "altTab")
1403+
1404+ // Release control, check if spread disappears
1405+ keyRelease(Qt.Key_Control)
1406+ tryCompare(desktopStage, "state", "")
1407+
1408+ // Focus should have switched back now
1409+ tryCompare(app3.session.surface, "activeFocus", true)
1410+ }
1411+
1412+ function test_altTabWrapAround() {
1413+ loadDesktopShellWithApps();
1414+
1415+ var desktopStage = findChild(shell, "stage");
1416+ verify(desktopStage !== null)
1417+
1418+ var appContainer = findInvisibleChild(shell, "appContainer")
1419+ verify(appContainer !== null)
1420+
1421+ var appRepeater = findInvisibleChild(shell, "appRepeater")
1422+ verify(appRepeater !== null)
1423+
1424+ // remember the focused appId
1425+ var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
1426+
1427+ tryCompare(desktopStage, "state", "")
1428+
1429+ // Just press Alt, make sure the spread comes up
1430+ keyPress(Qt.Key_Control);
1431+ keyClick(Qt.Key_Tab);
1432+ tryCompare(desktopStage, "state", "altTab")
1433+ tryCompare(appRepeater, "highlightedIndex", 1)
1434+ waitForRendering(shell)
1435+
1436+ // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
1437+ // We can't simulate a pressed key with keyPress() currently, so let's inject the events
1438+ // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
1439+ for (var i = 0; i < 10; i++) {
1440+ appContainer.selectNext(true); // true == isAutoRepeat
1441+ wait(0); // Trigger the event loop to make sure all the things happen
1442+ }
1443+ tryCompare(appRepeater, "highlightedIndex", 6)
1444+
1445+ // Now release it once, and verify that it does wrap around with an additional Tab press
1446+ keyRelease(Qt.Key_Tab);
1447+ keyClick(Qt.Key_Tab);
1448+ tryCompare(appRepeater, "highlightedIndex", 0)
1449+
1450+ // Release control, check if spread disappears
1451+ keyRelease(Qt.Key_Control)
1452+ tryCompare(desktopStage, "state", "")
1453+
1454+ // Make sure that after wrapping around once, we have the same one focused as at the beginning
1455+ tryCompare(focused.session.surface, "activeFocus", true)
1456+ }
1457+
1458+ function test_altBackTabNavigation() {
1459+ loadDesktopShellWithApps();
1460+
1461+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1462+ verify(appRepeater !== null);
1463+
1464+ keyPress(Qt.Key_Control)
1465+ keyClick(Qt.Key_Tab);
1466+ tryCompare(appRepeater, "highlightedIndex", 1);
1467+
1468+ keyClick(Qt.Key_Tab);
1469+ tryCompare(appRepeater, "highlightedIndex", 2);
1470+
1471+ keyClick(Qt.Key_Tab);
1472+ tryCompare(appRepeater, "highlightedIndex", 3);
1473+
1474+ keyClick(Qt.Key_Tab);
1475+ tryCompare(appRepeater, "highlightedIndex", 4);
1476+
1477+ keyClick(Qt.Key_Backtab);
1478+ tryCompare(appRepeater, "highlightedIndex", 3);
1479+
1480+ keyClick(Qt.Key_Backtab);
1481+ tryCompare(appRepeater, "highlightedIndex", 2);
1482+
1483+ keyClick(Qt.Key_Backtab);
1484+ tryCompare(appRepeater, "highlightedIndex", 1);
1485+
1486+ keyRelease(Qt.Key_Control);
1487+ }
1488+
1489+ function test_highlightFollowsMouse() {
1490+ loadDesktopShellWithApps()
1491+
1492+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1493+ verify(appRepeater !== null);
1494+
1495+ keyPress(Qt.Key_Control)
1496+ keyClick(Qt.Key_Tab);
1497+
1498+ tryCompare(appRepeater, "highlightedIndex", 1);
1499+
1500+ var x = 0;
1501+ var y = shell.height * .75;
1502+ mouseMove(shell, x, y)
1503+
1504+ for (var i = 0; i < 7; i++) {
1505+ while (appRepeater.highlightedIndex != i && x <= 4000) {
1506+ x+=10;
1507+ mouseMove(shell, x, y)
1508+ wait(0); // spin the loop so bindings get evaluated
1509+ }
1510+ }
1511+
1512+ verify(y < 4000);
1513+
1514+ keyRelease(Qt.Key_Control);
1515+ }
1516+
1517+ function test_closeFromSpread() {
1518+ loadDesktopShellWithApps()
1519+
1520+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1521+ verify(appRepeater !== null);
1522+
1523+ keyPress(Qt.Key_Control)
1524+ keyClick(Qt.Key_Tab);
1525+
1526+ appRemovedSpy.clear();
1527+
1528+ var closedAppId = ApplicationManager.get(2).appId;
1529+ var appDelegate2 = appRepeater.itemAt(2);
1530+ var closeMouseArea = findChild(appDelegate2, "closeMouseArea");
1531+
1532+ // Move the mosue over tile 2 and verify the close button becomes visible
1533+ var x = 0;
1534+ var y = shell.height * .5;
1535+ mouseMove(shell, x, y)
1536+ while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1537+ x+=10;
1538+ mouseMove(shell, x, y)
1539+ wait(0); // spin the loop so bindings get evaluated
1540+ }
1541+ tryCompare(closeMouseArea, "enabled", true)
1542+
1543+ // Close the app using the close button
1544+ mouseClick(closeMouseArea, closeMouseArea.width / 2, closeMouseArea.height / 2)
1545+
1546+ // Verify applicationRemoved has been emitted correctly
1547+ tryCompare(appRemovedSpy, "count", 1)
1548+ compare(appRemovedSpy.signalArguments[0][0], closedAppId);
1549+
1550+ keyRelease(Qt.Key_Control);
1551+ }
1552+
1553+ function test_selectFromSpreadWithMouse_data() {
1554+ return [
1555+ {tag: "click on tileInfo", tileInfo: true },
1556+ {tag: "click on surface", tileInfo: false },
1557+ ]
1558+ }
1559+
1560+ function test_selectFromSpreadWithMouse(data) {
1561+ loadDesktopShellWithApps()
1562+
1563+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1564+ verify(appRepeater !== null);
1565+
1566+ keyPress(Qt.Key_Control)
1567+ keyClick(Qt.Key_Tab);
1568+
1569+ appRemovedSpy.clear();
1570+
1571+ var focusAppId = ApplicationManager.get(2).appId;
1572+ var appDelegate2 = appRepeater.itemAt(2);
1573+ var decoratedWindow = findChild(appDelegate2, "decoratedWindow");
1574+ var stage = findChild(shell, "stage");
1575+
1576+ tryCompare(stage, "state", "altTab");
1577+
1578+ // Move the mouse over tile 2 and verify the highlight becomes visible
1579+ var x = 0;
1580+ var y = shell.height * (data.tileInfo ? .5 : 0.95)
1581+ mouseMove(shell, x, y)
1582+ while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1583+ x+=10;
1584+ mouseMove(shell, x, y)
1585+ wait(0); // spin the loop so bindings get evaluated
1586+ }
1587+ tryCompare(decoratedWindow, "highlightShown", true);
1588+
1589+ // Click the tile
1590+ mouseClick(decoratedWindow, decoratedWindow.width / 2, decoratedWindow.height / 2)
1591+
1592+ // Verify that we left the spread and app2 is the focused one now
1593+ tryCompare(stage, "state", "");
1594+ tryCompare(ApplicationManager, "focusedApplicationId", focusAppId);
1595+
1596+ keyRelease(Qt.Key_Control);
1597+ }
12311598 }
12321599}