on xenial help sign in
Really fix binding and add a test for the binding, fixing up some mock delta along the way
9007ce1Michael Terry
qml/Shell.qml+2−1
@@ -669,7 +669,8 @@ Item {
669
669
670 Binding {
670 Binding {
671 target: NotificationBackend.Model
671 target: NotificationBackend.Model
672+ property: "paused"
672− paused: tutorial.running
673+ value: tutorial.running
673 }
674 }
674
675
675 states: [
676 states: [
tests/mocks/Unity/Notifications/MockNotificationModel.cpp+23−5
@@ -31,14 +31,14 @@
31
31
32using namespace unity::shell::notifications;
32using namespace unity::shell::notifications;
33
33
34−MockNotificationModel::MockNotificationModel(QObject *parent) : QAbstractListModel(parent) {
34+MockNotificationModel::MockNotificationModel(QObject *parent)
35+ : QAbstractListModel(parent)
36+ , m_paused(false)
37+{
35}
38}
36
39
37MockNotificationModel::~MockNotificationModel() {
40MockNotificationModel::~MockNotificationModel() {
38− Q_FOREACH(MockNotification *n, m_queue) {
39− n->deleteLater();
40− }
41− m_queue.clear();
41+ clear();
42}
42}
43
43
44int MockNotificationModel::rowCount(const QModelIndex &) const {
44int MockNotificationModel::rowCount(const QModelIndex &) const {
@@ -89,6 +89,17 @@ QVariant MockNotificationModel::data(const QModelIndex &index, int role) const {
89 }
89 }
90}
90}
91
91
92+bool MockNotificationModel::getPaused() const {
93+ return m_paused;
94+}
95+
96+void MockNotificationModel::setPaused(bool paused) {
97+ if (m_paused != paused) {
98+ m_paused = paused;
99+ Q_EMIT pausedChanged(paused);
100+ }
101+}
102+
92void MockNotificationModel::append(MockNotification* n) {
103void MockNotificationModel::append(MockNotification* n) {
93 int location = m_queue.size();
104 int location = m_queue.size();
94 QModelIndex insertionPoint = QModelIndex();
105 QModelIndex insertionPoint = QModelIndex();
@@ -107,6 +118,13 @@ MockNotification* MockNotificationModel::getNotification(int id) const {
107 return nullptr;
118 return nullptr;
108}
119}
109
120
121+void MockNotificationModel::clear() {
122+ Q_FOREACH(MockNotification *n, m_queue) {
123+ n->deleteLater();
124+ }
125+ m_queue.clear();
126+}
127+
110void MockNotificationModel::remove(const int id) {
128void MockNotificationModel::remove(const int id) {
111 for(int i = 0; i < m_queue.size(); i++) {
129 for(int i = 0; i < m_queue.size(); i++) {
112 if(m_queue[i]->getID() == id) {
130 if(m_queue[i]->getID() == id) {
tests/mocks/Unity/Notifications/MockNotificationModel.h+8
@@ -30,6 +30,7 @@ class MockNotification;
30class MockNotificationModel : public QAbstractListModel {
30class MockNotificationModel : public QAbstractListModel {
31 Q_OBJECT
31 Q_OBJECT
32 Q_PROPERTY(int count READ getCount)
32 Q_PROPERTY(int count READ getCount)
33+ Q_PROPERTY(bool paused READ getPaused WRITE setPaused NOTIFY pausedChanged)
33
34
34public:
35public:
35 MockNotificationModel(QObject *parent=nullptr);
36 MockNotificationModel(QObject *parent=nullptr);
@@ -39,6 +40,9 @@ public:
39 QVariant data(const QModelIndex &index, int role) const override;
40 QVariant data(const QModelIndex &index, int role) const override;
40 QHash<int, QByteArray> roleNames() const override;
41 QHash<int, QByteArray> roleNames() const override;
41
42
43+ bool getPaused() const;
44+ void setPaused(bool paused);
45+
42 Q_INVOKABLE void append(MockNotification* n);
46 Q_INVOKABLE void append(MockNotification* n);
43 MockNotification* getNotification(int id) const;
47 MockNotification* getNotification(int id) const;
44
48
@@ -50,6 +54,8 @@ public:
50 Q_INVOKABLE void remove(const int id);
54 Q_INVOKABLE void remove(const int id);
51 Q_INVOKABLE void removeSecond();
55 Q_INVOKABLE void removeSecond();
52
56
57+ Q_INVOKABLE void clear(); // only in mock
58+
53 int getCount() const;
59 int getCount() const;
54
60
55Q_SIGNALS:
61Q_SIGNALS:
@@ -63,9 +69,11 @@ private Q_SLOTS:
63
69
64Q_SIGNALS:
70Q_SIGNALS:
65 void queueSizeChanged(int newSize);
71 void queueSizeChanged(int newSize);
72+ void pausedChanged(bool paused);
66
73
67private:
74private:
68 QList<MockNotification*> m_queue;
75 QList<MockNotification*> m_queue;
76+ bool m_paused;
69 void removeInternal(int loc);
77 void removeInternal(int loc);
70};
78};
71
79
tests/mocks/Unity/Notifications/plugin.cpp+6−1
@@ -24,10 +24,15 @@
24
24
25#include <QtQml/qqml.h>
25#include <QtQml/qqml.h>
26
26
27+static QObject* modelProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
28+{
29+ return new MockNotificationModel;
30+}
31+
27void TestNotificationPlugin::registerTypes(const char* uri)
32void TestNotificationPlugin::registerTypes(const char* uri)
28{
33{
29 // @uri Unity.Notifications
34 // @uri Unity.Notifications
30 qmlRegisterType<MockNotification>(uri, 1, 0, "Notification");
35 qmlRegisterType<MockNotification>(uri, 1, 0, "Notification");
31− qmlRegisterType<MockNotificationModel>(uri, 1, 0, "NotificationModel");
36+ qmlRegisterSingletonType<MockNotificationModel>(uri, 1, 0, "Model", modelProvider);
32 qmlRegisterType<ActionModel>(uri, 1, 0, "ActionModel");
37 qmlRegisterType<ActionModel>(uri, 1, 0, "ActionModel");
33}
38}
tests/qmltests/Notifications/tst_Notifications.qml+45−49
@@ -34,23 +34,19 @@ Item {
34 Row {
34 Row {
35 id: rootRow
35 id: rootRow
36
36
37− NotificationModel {
38− id: mockModel
39−
40− // add the default/PlaceHolder notification to the model
37+ // add the default/PlaceHolder notification to the model
41− Component.onCompleted: {
38+ Component.onCompleted: {
42− var component = Qt.createComponent("Notification.qml")
39+ var component = Qt.createComponent("Notification.qml")
43− var n = component.createObject("notification", {"nid": index++,
40+ var n = component.createObject("notification", {"nid": index++,
44− "type": Notification.PlaceHolder,
41+ "type": Notification.PlaceHolder,
45− "hints": {},
42+ "hints": {},
46− "summary": "",
43+ "summary": "",
47− "body": "",
44+ "body": "",
48− "icon": "",
45+ "icon": "",
49− "secondaryIcon": "",
46+ "secondaryIcon": "",
50− "rawActions": []})
47+ "rawActions": []})
51− n.completed.connect(mockModel.onCompleted)
48+ n.completed.connect(Model.onCompleted)
52− append(n)
49+ Model.append(n)
53− }
54 }
50 }
55
51
56 function add2over1SnapDecisionNotification() {
52 function add2over1SnapDecisionNotification() {
@@ -65,8 +61,8 @@ Item {
65 "rawActions": ["ok_id", "Ok",
61 "rawActions": ["ok_id", "Ok",
66 "snooze_id", "Snooze",
62 "snooze_id", "Snooze",
67 "view_id", "View"]})
63 "view_id", "View"]})
68− n.completed.connect(mockModel.onCompleted)
64+ n.completed.connect(Model.onCompleted)
69− mockModel.append(n)
65+ Model.append(n)
70 }
66 }
71
67
72 function addEphemeralNotification() {
68 function addEphemeralNotification() {
@@ -79,8 +75,8 @@ Item {
79 "icon": "../../tests/graphics/avatars/amanda.png",
75 "icon": "../../tests/graphics/avatars/amanda.png",
80 "secondaryIcon": "../../tests/graphics/applicationIcons/facebook.png",
76 "secondaryIcon": "../../tests/graphics/applicationIcons/facebook.png",
81 "rawActions": ["reply_id", "Dummy"]})
77 "rawActions": ["reply_id", "Dummy"]})
82− n.completed.connect(mockModel.onCompleted)
78+ n.completed.connect(Model.onCompleted)
83− mockModel.append(n)
79+ Model.append(n)
84 }
80 }
85
81
86 function addEphemeralNonShapedIconNotification() {
82 function addEphemeralNonShapedIconNotification() {
@@ -93,8 +89,8 @@ Item {
93 "icon": "../../tests/graphics/applicationIcons/contacts-app.png",
89 "icon": "../../tests/graphics/applicationIcons/contacts-app.png",
94 "secondaryIcon": "",
90 "secondaryIcon": "",
95 "rawActions": ["reply_id", "Dummy"]})
91 "rawActions": ["reply_id", "Dummy"]})
96− n.completed.connect(mockModel.onCompleted)
92+ n.completed.connect(Model.onCompleted)
97− mockModel.append(n)
93+ Model.append(n)
98 }
94 }
99
95
100 function addEphemeralIconSummaryNotification() {
96 function addEphemeralIconSummaryNotification() {
@@ -107,8 +103,8 @@ Item {
107 "icon": "../../tests/graphics/applicationIcons/facebook.png",
103 "icon": "../../tests/graphics/applicationIcons/facebook.png",
108 "secondaryIcon": "",
104 "secondaryIcon": "",
109 "rawActions": ["reply_id", "Dummy"]})
105 "rawActions": ["reply_id", "Dummy"]})
110− n.completed.connect(mockModel.onCompleted)
106+ n.completed.connect(Model.onCompleted)
111− mockModel.append(n)
107+ Model.append(n)
112 }
108 }
113
109
114 function addInteractiveNotification() {
110 function addInteractiveNotification() {
@@ -121,8 +117,8 @@ Item {
121 "icon": "../../tests/graphics/avatars/anna_olsson.png",
117 "icon": "../../tests/graphics/avatars/anna_olsson.png",
122 "secondaryIcon": "",
118 "secondaryIcon": "",
123 "rawActions": ["reply_id", "Dummy"]})
119 "rawActions": ["reply_id", "Dummy"]})
124− n.completed.connect(mockModel.onCompleted)
120+ n.completed.connect(Model.onCompleted)
125− mockModel.append(n)
121+ Model.append(n)
126 }
122 }
127
123
128 function addConfirmationNotification() {
124 function addConfirmationNotification() {
@@ -136,8 +132,8 @@ Item {
136 "secondaryIcon": "",
132 "secondaryIcon": "",
137 "value": 50,
133 "value": 50,
138 "rawActions": ["reply_id", "Dummy"]})
134 "rawActions": ["reply_id", "Dummy"]})
139− n.completed.connect(mockModel.onCompleted)
135+ n.completed.connect(Model.onCompleted)
140− mockModel.append(n)
136+ Model.append(n)
141 }
137 }
142
138
143 function add2ndConfirmationNotification() {
139 function add2ndConfirmationNotification() {
@@ -152,19 +148,19 @@ Item {
152 "secondaryIcon": "",
148 "secondaryIcon": "",
153 "value": 85,
149 "value": 85,
154 "rawActions": ["reply_id", "Dummy"]})
150 "rawActions": ["reply_id", "Dummy"]})
155− n.completed.connect(mockModel.onCompleted)
151+ n.completed.connect(Model.onCompleted)
156− mockModel.append(n)
152+ Model.append(n)
157 }
153 }
158
154
159 function clearNotifications() {
155 function clearNotifications() {
160− while(mockModel.count > 1) {
156+ while(Model.count > 1) {
161 remove1stNotification()
157 remove1stNotification()
162 }
158 }
163 }
159 }
164
160
165 function remove1stNotification() {
161 function remove1stNotification() {
166− if (mockModel.count > 1) {
162+ if (Model.count > 1) {
167− mockModel.removeSecond()
163+ Model.removeSecond()
168 }
164 }
169 }
165 }
170
166
@@ -186,7 +182,7 @@ Item {
186 margin: units.gu(1)
182 margin: units.gu(1)
187
183
188 anchors.fill: parent
184 anchors.fill: parent
189− model: mockModel
185+ model: Model
190 hasMouse: fakeMouseCB.checked
186 hasMouse: fakeMouseCB.checked
191 inverseMode: inverseModeCB.checked
187 inverseMode: inverseModeCB.checked
192 }
188 }
@@ -558,7 +554,7 @@ Item {
558 SignalSpy {
554 SignalSpy {
559 id: actionSpy
555 id: actionSpy
560
556
561− target: mockModel
557+ target: Model
562 signalName: "actionInvoked"
558 signalName: "actionInvoked"
563 }
559 }
564
560
@@ -569,19 +565,19 @@ Item {
569
565
570 function test_NotificationRenderer(data) {
566 function test_NotificationRenderer(data) {
571 // make sure the clicks on mocked notifications can be checked against by "actionSpy" (mimicking the NotificationServer component)
567 // make sure the clicks on mocked notifications can be checked against by "actionSpy" (mimicking the NotificationServer component)
572− data.n.actionInvoked.connect(mockModel.actionInvoked)
568+ data.n.actionInvoked.connect(Model.actionInvoked)
573
569
574 // hook up notification's completed-signal with model's onCompleted-slot, so that remove() (model) happens on close() (notification)
570 // hook up notification's completed-signal with model's onCompleted-slot, so that remove() (model) happens on close() (notification)
575− data.n.completed.connect(mockModel.onCompleted)
571+ data.n.completed.connect(Model.onCompleted)
576
572
577 // populate model with some mock notifications
573 // populate model with some mock notifications
578− mockModel.append(data.n)
574+ Model.append(data.n)
579
575
580 // make sure the view is properly updated before going on
576 // make sure the view is properly updated before going on
581 notifications.forceLayout();
577 notifications.forceLayout();
582 waitForRendering(notifications);
578 waitForRendering(notifications);
583
579
584− var notification = findChild(notifications, "notification" + (mockModel.count - 1))
580+ var notification = findChild(notifications, "notification" + (Model.count - 1))
585 verify(notification !== undefined, "notification wasn't found");
581 verify(notification !== undefined, "notification wasn't found");
586
582
587 waitForRendering(notification);
583 waitForRendering(notification);
@@ -627,7 +623,7 @@ Item {
627
623
628 // After clicking the state of notifications can change so let's wait
624 // After clicking the state of notifications can change so let's wait
629 // for their height animations to finish before continuing
625 // for their height animations to finish before continuing
630− for (var i = 0; i < mockModel.count; ++i) {
626+ for (var i = 0; i < Model.count; ++i) {
631 var n = findChild(notifications, "notification" + i)
627 var n = findChild(notifications, "notification" + i)
632 tryCompare(n, "height", n.state === "contracted" ? units.gu(10) : n.implicitHeight);
628 tryCompare(n, "height", n.state === "contracted" ? units.gu(10) : n.implicitHeight);
633 }
629 }
@@ -685,16 +681,16 @@ Item {
685
681
686 // swipe-to-dismiss check
682 // swipe-to-dismiss check
687 waitForRendering(notification)
683 waitForRendering(notification)
688− var before = mockModel.count
684+ var before = Model.count
689 var dragStart = notification.width * 0.25;
685 var dragStart = notification.width * 0.25;
690 var dragEnd = notification.width;
686 var dragEnd = notification.width;
691 var dragY = notification.height / 2;
687 var dragY = notification.height / 2;
692 touchFlick(notification, dragStart, dragY, dragEnd, dragY)
688 touchFlick(notification, dragStart, dragY, dragEnd, dragY)
693 waitForRendering(notification)
689 waitForRendering(notification)
694 if ((data.n.type === Notification.SnapDecision && notification.state === "expanded") || data.n.type === Notification.Confirmation) {
690 if ((data.n.type === Notification.SnapDecision && notification.state === "expanded") || data.n.type === Notification.Confirmation) {
695− tryCompare(mockModel, "count", before)
691+ tryCompare(Model, "count", before)
696 } else {
692 } else {
697− tryCompare(mockModel, "count", before - 1)
693+ tryCompare(Model, "count", before - 1)
698 }
694 }
699 }
695 }
700
696
@@ -705,27 +701,27 @@ Item {
705
701
706 function test_clickToClose(data) {
702 function test_clickToClose(data) {
707 // populate model with some mock notifications
703 // populate model with some mock notifications
708− mockModel.append(data.n)
704+ Model.append(data.n)
709
705
710 // make sure the view is properly updated before going on
706 // make sure the view is properly updated before going on
711 notifications.forceLayout();
707 notifications.forceLayout();
712 waitForRendering(notifications);
708 waitForRendering(notifications);
713
709
714− var notification = findChild(notifications, "notification" + (mockModel.count - 1))
710+ var notification = findChild(notifications, "notification" + (Model.count - 1))
715 verify(!!notification, "notification wasn't found");
711 verify(!!notification, "notification wasn't found");
716
712
717 // click-to-dismiss check
713 // click-to-dismiss check
718 waitForRendering(notification);
714 waitForRendering(notification);
719− var before = mockModel.count;
715+ var before = Model.count;
720 var canBeClosed = notification.canBeClosed;
716 var canBeClosed = notification.canBeClosed;
721 mouseClick(notification);
717 mouseClick(notification);
722
718
723 if (canBeClosed) {
719 if (canBeClosed) {
724 // closing the notification, the model count should be one less
720 // closing the notification, the model count should be one less
725− tryCompare(mockModel, "count", before - 1)
721+ tryCompare(Model, "count", before - 1)
726 } else {
722 } else {
727 // not closing, model stays the same
723 // not closing, model stays the same
728− tryCompare(mockModel, "count", before)
724+ tryCompare(Model, "count", before)
729 }
725 }
730 }
726 }
731
727
tests/qmltests/Tutorial/tst_Tutorial.qml+13−26
@@ -22,7 +22,7 @@ import Ubuntu.Components 1.3
22import Ubuntu.Components.ListItems 1.3
22import Ubuntu.Components.ListItems 1.3
23import Ubuntu.Telephony 0.1 as Telephony
23import Ubuntu.Telephony 0.1 as Telephony
24import Unity.Application 0.1
24import Unity.Application 0.1
25−import Unity.Notifications 1.0
25+import Unity.Notifications 1.0 as NotificationBackend
26import Unity.Test 0.1 as UT
26import Unity.Test 0.1 as UT
27import Utils 0.1
27import Utils 0.1
28
28
@@ -57,32 +57,21 @@ Rectangle {
57 }
57 }
58
58
59 Component {
59 Component {
60− id: mockNotification
60+ id: notification
61− QtObject {}
61+ NotificationBackend.Notification {
62− }
62+ nid: 1
63−
63+ type: NotificationBackend.Notification.Confirmation
64− ListModel {
64+ summary: ""
65− id: mockNotificationsModel
65+ body: ""
66−
66+ icon: ""
67− property bool paused
67+ secondaryIcon: ""
68−
68+ value: 0
69− function getRaw(id) {
69+ rawActions: []
70− return mockNotification.createObject(mockNotificationsModel)
71 }
70 }
72 }
71 }
73
72
74 function addNotification() {
73 function addNotification() {
75− var n = {
74+ NotificationBackend.Model.append(notification.createObject());
76− type: Notification.Confirmation,
77− hints: {},
78− summary: "",
79− body: "",
80− icon: "",
81− secondaryIcon: "",
82− actions: []
83− };
84−
85− mockNotificationsModel.append(n);
86 }
75 }
87
76
88 Component.onCompleted: {
77 Component.onCompleted: {
@@ -288,11 +277,11 @@ Rectangle {
288
277
289 // kill all (fake) running apps
278 // kill all (fake) running apps
290 killApps();
279 killApps();
280+ NotificationBackend.Model.clear();
291
281
292 // reload our test subject to get it in a fresh state once again
282 // reload our test subject to get it in a fresh state once again
293 shellLoader.active = true;
283 shellLoader.active = true;
294
284
295− mockNotificationsModel.clear();
296 tryCompare(shellLoader, "status", Loader.Ready);
285 tryCompare(shellLoader, "status", Loader.Ready);
297 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
286 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
298 }
287 }
@@ -734,8 +723,6 @@ Rectangle {
734 var tutorial = findChild(shell, "tutorial");
723 var tutorial = findChild(shell, "tutorial");
735 verify(!tutorial.delayed);
724 verify(!tutorial.delayed);
736
725
737− var notifications = findChild(shell, "notificationList");
738− notifications.model = mockNotificationsModel;
739 addNotification();
726 addNotification();
740
727
741 tryCompare(tutorial, "delayed", true);
728 tryCompare(tutorial, "delayed", true);
tests/qmltests/tst_Shell.qml+16−1
@@ -25,7 +25,7 @@ import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Application 0.1
25import Unity.Application 0.1
26import Unity.Connectivity 0.1
26import Unity.Connectivity 0.1
27import Unity.Indicators 0.1
27import Unity.Indicators 0.1
28−import Unity.Notifications 1.0
28+import Unity.Notifications 1.0 as NotificationBackend
29import Unity.Launcher 0.1
29import Unity.Launcher 0.1
30import Unity.Test 0.1
30import Unity.Test 0.1
31import Powerd 0.1
31import Powerd 0.1
@@ -1289,6 +1289,21 @@ Rectangle {
1289 tryCompare(tutorial, "paused", false);
1289 tryCompare(tutorial, "paused", false);
1290 }
1290 }
1291
1291
1292+ function test_notificationsPausedDuringTutorial() {
1293+ loadShell("phone");
1294+ swipeAwayGreeter();
1295+ verify(!NotificationBackend.Model.paused);
1296+
1297+ AccountsService.demoEdges = true;
1298+ var tutorialLeft = findChild(shell, "tutorialLeft");
1299+ var tutorialLeftTimer = findInvisibleChild(tutorialLeft, "tutorialLeftTimer");
1300+ tutorialLeftTimer.interval = 0;
1301+ tryCompare(NotificationBackend.Model, "paused", true);
1302+
1303+ AccountsService.demoEdges = false;
1304+ tryCompare(NotificationBackend.Model, "paused", false);
1305+ }
1306+
1292 function test_tapOnRightEdgeReachesApplicationSurface() {
1307 function test_tapOnRightEdgeReachesApplicationSurface() {
1293 loadShell("phone");
1308 loadShell("phone");
1294 swipeAwayGreeter();
1309 swipeAwayGreeter();