@@ -25,14 +25,16 @@ import Ubuntu.Components 1.1
2525import Ubuntu.Telephony 0.1 as Telephony
2626import Unity.Application 0.1
2727import Unity.Connectivity 0.1
28+import Unity.Notifications 1.0
2829import Unity.Test 0.1 as UT
2930import Powerd 0.1
3031
3132import "../../qml"
3233
33−Row {
34+Item {
3435 id: root
35− spacing: 0
36+ width: units.gu(60)
37+ height: units.gu(71)
3638
3739 QtObject {
3840 id: applicationArguments
@@ -50,49 +52,43 @@ Row {
5052 }
5153 }
5254
53− Loader {
54− id: shellLoader
55−
56− // Copied from Shell.qml
57− property bool tablet: false
58− width: tablet ? units.gu(160)
59− : applicationArguments.hasGeometry() ? applicationArguments.width()
60− : units.gu(40)
61− height: tablet ? units.gu(100)
62− : applicationArguments.hasGeometry() ? applicationArguments.height()
63− : units.gu(71)
64−
65− property bool itemDestroyed: false
66− sourceComponent: Component {
67− Shell {
68− property string indicatorProfile: "phone"
69−
70− Component.onDestruction: {
71− shellLoader.itemDestroyed = true;
55+ Row {
56+ anchors.fill: parent
57+ Loader {
58+ id: shellLoader
59+
60+ property bool itemDestroyed: false
61+ sourceComponent: Component {
62+ Shell {
63+ property string indicatorProfile: "phone"
64+
65+ Component.onDestruction: {
66+ shellLoader.itemDestroyed = true;
67+ }
7268 }
7369 }
7470 }
75− }
7671
77− Rectangle {
78− color: "white"
79− width: units.gu(30)
80− height: shellLoader.height
81−
82− Column {
83− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
84− spacing: units.gu(1)
85− Row {
86− anchors { left: parent.left; right: parent.right }
87− Button {
88− text: "Show Greeter"
89− onClicked: {
90− if (shellLoader.status !== Loader.Ready)
91− return;
92−
93− var greeter = testCase.findChild(shellLoader.item, "greeter");
94− if (!greeter.shown) {
95− greeter.show();
72+ Rectangle {
73+ color: "white"
74+ width: units.gu(30)
75+ height: shellLoader.height
76+
77+ Column {
78+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
79+ spacing: units.gu(1)
80+ Row {
81+ anchors { left: parent.left; right: parent.right }
82+ Button {
83+ text: "Show Greeter"
84+ onClicked: {
85+ if (shellLoader.status !== Loader.Ready)
86+ return;
87+
88+ var greeter = testCase.findChild(shellLoader.item, "greeter");
89+ if (!greeter.shown) {
90+ greeter.show();
91+ }
9692 }
9793 }
9894 }
@@ -100,6 +96,31 @@ Row {
10096 }
10197 }
10298
99+ Component {
100+ id: mockNotification
101+
102+ QtObject {
103+ function invokeAction(actionId) {
104+ mockNotificationsModel.actionInvoked(actionId)
105+ }
106+ }
107+ }
108+ ListModel {
109+ id: mockNotificationsModel
110+
111+ signal actionInvoked(string actionId)
112+
113+ function getRaw(id) {
114+ return mockNotification.createObject(mockNotificationsModel)
115+ }
116+
117+ onActionInvoked: {
118+ if(actionId == "ok_id") {
119+ mockNotificationsModel.clear()
120+ }
121+ }
122+ }
123+
103124 SignalSpy {
104125 id: launcherShowDashHomeSpy
105126 signalName: "showDashHome"
@@ -121,6 +142,12 @@ Row {
121142 signalName: "unlockingAllModems"
122143 }
123144
145+ SignalSpy {
146+ id: notificationActionSpy
147+ target: mockNotificationsModel
148+ signalName: "actionInvoked"
149+ }
150+
124151 Telephony.CallEntry {
125152 id: phoneCall
126153 phoneNumber: "+447812221111"
@@ -180,6 +207,65 @@ Row {
180207 compare(ApplicationManager.count, 1)
181208 }
182209
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);
218+
219+ notifications.model = mockNotificationsModel;
220+
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
224+ // SnapDecision.
225+ var snapDecisionProxyModel = findInvisibleChild(shell, "snapDecisionProxyModel");
226+ snapDecisionProxyModel.filterRegExp = RegExp("");
227+
228+ // Pop-up a notification
229+ addSnapDecisionNotification();
230+ waitForRendering(shell);
231+
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);
237+
238+ // Make sure activeFocus went away from the app window
239+ tryCompare(app.session.surface, "activeFocus", false);
240+ tryCompare(stage, "interactive", false);
241+
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);
245+
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");
249+ }
250+
251+ function addSnapDecisionNotification() {
252+ var n = {
253+ type: Notification.SnapDecision,
254+ hints: {"x-canonical-private-affirmative-tint": "true"},
255+ summary: "Tom Ato",
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"}]
264+ }
265+
266+ mockNotificationsModel.append(n)
267+ }
268+
183269 function test_leftEdgeDrag_data() {
184270 return [
185271 {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},