@@ -25,14 +25,16 @@ import Ubuntu.Components 1.1
25import Ubuntu.Telephony 0.1 as Telephony
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Application 0.1
26import Unity.Application 0.1
27import Unity.Connectivity 0.1
27import Unity.Connectivity 0.1
28+import Unity.Notifications 1.0
28import Unity.Test 0.1 as UT
29import Unity.Test 0.1 as UT
29import Powerd 0.1
30import Powerd 0.1
31import "../../qml"
32import "../../qml"
35− spacing: 0
36+ width: units.gu(60)
37 QtObject {
39 QtObject {
38 id: applicationArguments
40 id: applicationArguments
@@ -50,49 +52,43 @@ Row {
54− id: shellLoader
58+ id: shellLoader
56− // Copied from Shell.qml
57− property bool tablet: false
58− width: tablet ? units.gu(160)
59− : applicationArguments.hasGeometry() ? applicationArguments.width()
61− height: tablet ? units.gu(100)
62− : applicationArguments.hasGeometry() ? applicationArguments.height()
65− property bool itemDestroyed: false
60+ property bool itemDestroyed: false
66− sourceComponent: Component {
61+ sourceComponent: Component {
68− property string indicatorProfile: "phone"
63+ property string indicatorProfile: "phone"
70− Component.onDestruction: {
65+ Component.onDestruction: {
71− shellLoader.itemDestroyed = true;
66+ shellLoader.itemDestroyed = true;
77− Rectangle {
72+ Rectangle {
78− color: "white"
73+ color: "white"
79− width: units.gu(30)
74+ width: units.gu(30)
80− height: shellLoader.height
75+ height: shellLoader.height
83− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
78+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
84− spacing: units.gu(1)
79+ spacing: units.gu(1)
86− anchors { left: parent.left; right: parent.right }
81+ anchors { left: parent.left; right: parent.right }
88− text: "Show Greeter"
83+ text: "Show Greeter"
89− onClicked: {
84+ onClicked: {
90− if (shellLoader.status !== Loader.Ready)
85+ if (shellLoader.status !== Loader.Ready)
93− var greeter = testCase.findChild(shellLoader.item, "greeter");
88+ var greeter = testCase.findChild(shellLoader.item, "greeter");
94− if (!greeter.shown) {
89+ if (!greeter.shown) {
95− greeter.show();
90+ greeter.show();
@@ -100,6 +96,31 @@ Row {
100+ id: mockNotification
103+ function invokeAction(actionId) {
104+ mockNotificationsModel.actionInvoked(actionId)
109+ id: mockNotificationsModel
111+ signal actionInvoked(string actionId)
113+ function getRaw(id) {
114+ return mockNotification.createObject(mockNotificationsModel)
118+ if(actionId == "ok_id") {
119+ mockNotificationsModel.clear()
103 SignalSpy {
124 SignalSpy {
104 id: launcherShowDashHomeSpy
125 id: launcherShowDashHomeSpy
105 signalName: "showDashHome"
126 signalName: "showDashHome"
@@ -121,6 +142,12 @@ Row {
121 signalName: "unlockingAllModems"
142 signalName: "unlockingAllModems"
146+ id: notificationActionSpy
147+ target: mockNotificationsModel
148+ signalName: "actionInvoked"
124 Telephony.CallEntry {
151 Telephony.CallEntry {
125 id: phoneCall
152 id: phoneCall
126 phoneNumber: "+447812221111"
153 phoneNumber: "+447812221111"
@@ -180,6 +207,65 @@ Row {
180 compare(ApplicationManager.count, 1)
207 compare(ApplicationManager.count, 1)
210+ function test_snapDecisionDismissalReturnsFocus() {
211+ var notifications = findChild(shell, "notificationList");
212+ var app = ApplicationManager.startApplication("camera-app");
213+ var stage = findChild(shell, "stage")
214+ // Open an application and focus
215+ waitUntilApplicationWindowIsFullyVisible(app);
216+ ApplicationManager.focusApplication(app);
217+ tryCompare(app.session.surface, "activeFocus", true);
219+ notifications.model = mockNotificationsModel;
221+ // FIXME: Hack: SortFilterProxyModelQML doesn't work with QML ListModels which we use
222+ // for mocking here (RoleType can't be found in the QML model). As we only need to show
223+ // one SnapDecision lets just disable the filtering and make appear any notification as a
225+ var snapDecisionProxyModel = findInvisibleChild(shell, "snapDecisionProxyModel");
226+ snapDecisionProxyModel.filterRegExp = RegExp("");
228+ // Pop-up a notification
229+ addSnapDecisionNotification();
230+ waitForRendering(shell);
232+ // Make sure the notification really opened
233+ var notification = findChild(notifications, "notification" + (mockNotificationsModel.count - 1));
234+ verify(notification !== undefined && notification != null, "notification wasn't found");
235+ tryCompare(notification, "height", notification.implicitHeight)
236+ waitForRendering(notification);
238+ // Make sure activeFocus went away from the app window
239+ tryCompare(app.session.surface, "activeFocus", false);
240+ tryCompare(stage, "interactive", false);
242+ // Clicking the button should dismiss the notification and return focus
243+ var buttonAccept = findChild(notification, "notify_button0");
244+ mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2);
246+ // Make sure we're back to normal
247+ tryCompare(app.session.surface, "activeFocus", true);
248+ compare(stage.interactive, true, "Stages not interactive again after modal notification has closed");
251+ function addSnapDecisionNotification() {
253+ type: Notification.SnapDecision,
254+ hints: {"x-canonical-private-affirmative-tint": "true"},
256+ 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.",
257+ icon: "../graphics/avatars/funky.png",
258+ secondaryIcon: "../graphics/applicationIcons/facebook.png",
259+ actions: [{ id: "ok_id", label: "Ok"},
260+ { id: "cancel_id", label: "Cancel"},
261+ { id: "notreally_id", label: "Not really"},
262+ { id: "noway_id", label: "messages:No way"},
263+ { id: "nada_id", label: "messages:Nada"}]
266+ mockNotificationsModel.append(n)
183 function test_leftEdgeDrag_data() {
269 function test_leftEdgeDrag_data() {
185 {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},
271 {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},