20 """Tests for Notifications"""
22 from unity8
import shell
26 from testtools.matchers
import Equals, NotEquals
27 from autopilot.matchers
import Eventually
29 from gi.repository
import Notify
36 logger = logging.getLogger(__name__)
40 """Base class for all notification tests that provides helper methods."""
42 scenarios = _get_device_emulation_scenarios(
'Nexus4')
44 def _get_icon_path(self, icon_name):
45 """Given an icons file name returns the full path (either system or
48 Consider the graphics directory as root so for example (running tests
49 from installed unity8-autopilot package):
50 >>> self.get_icon_path('clock.png')
51 /usr/share/unity8/graphics/clock.png
53 >>> self.get_icon_path('applicationIcons/facebook.png')
54 /usr/share/unity8/graphics/applicationIcons/facebook.png
57 if os.path.abspath(__file__).startswith(
'/usr/'):
58 return '/usr/share/unity8/graphics/' + icon_name
60 return os.path.dirname(__file__) + (
61 "/../../../../../qml/graphics/" + icon_name)
63 def _get_notifications_list(self):
64 return self.main_window.select_single(
66 objectName=
'notificationList'
69 def _assert_notification(
78 """Assert that the expected qualities of a notification are as
83 if summary
is not None:
84 self.assertThat(notification.summary, Eventually(Equals(summary)))
87 self.assertThat(notification.body, Eventually(Equals(body)))
90 self.assertThat(notification.iconSource, Eventually(NotEquals(
"")))
92 self.assertThat(notification.iconSource, Eventually(Equals(
"")))
96 notification.secondaryIconSource,
97 Eventually(NotEquals(
""))
101 notification.secondaryIconSource,
102 Eventually(Equals(
""))
105 if opacity
is not None:
106 self.assertThat(notification.opacity, Eventually(Equals(opacity)))
110 """Collection of test for Interactive tests including snap decisions."""
118 """Interactive notification must react upon click on itself."""
120 unlock_unity(unity_proxy)
124 summary =
"Interactive notification"
125 body =
"This notification can be clicked on to trigger an action."
127 actions = [(
"action_id",
"dummy")]
129 (
"x-canonical-switch-to-application",
"true"),
130 (
"x-canonical-secondary-icon",
"dialer")
142 get_notification =
lambda: notify_list.wait_select_single(
143 'Notification', objectName=
'notification1')
144 notification = get_notification()
146 notification.pointing_device.click_object(
147 notification.select_single(objectName=
"interactiveArea")
153 """Snap-decision with three actions should use one-over two button layout."""
155 unlock_unity(unity_proxy)
157 summary =
"Theatre at Ferria Stadium"
158 body =
"at Ferria Stadium in Bilbao, Spain\n07578545317"
160 (
"x-canonical-snap-decisions",
"true"),
161 (
"x-canonical-non-shaped-icon",
"true"),
162 (
"x-canonical-private-affirmative-tint",
"true")
166 (
'action_accept',
'Ok'),
167 (
'action_decline_1',
'Snooze'),
168 (
'action_decline_2',
'View'),
182 get_notification =
lambda: notify_list.wait_select_single(
183 'Notification', objectName=
'notification1')
184 notification = get_notification()
186 notification, summary, body,
False,
False, 1.0)
187 notification.pointing_device.click_object(
188 notification.select_single(objectName=
"notify_oot_button0"))
192 """Snap-decision should block input to shell without greeter/lockscreen."""
194 unlock_unity(unity_proxy)
196 summary =
"Incoming file"
197 body =
"Frank would like to send you the file: essay.pdf"
198 icon_path =
"sync-idle"
200 (
"x-canonical-snap-decisions",
"true"),
201 (
"x-canonical-non-shaped-icon",
"true"),
202 (
"x-canonical-private-affirmative-tint",
"true"),
203 (
"x-canonical-private-rejection-tint",
"true"),
207 (
'action_accept',
'Accept'),
208 (
'action_decline_1',
'Decline'),
223 self.main_window.show_dash_swiping()
225 self.main_window.is_launcher_open, Eventually(Equals(
False)))
229 get_notification =
lambda: notify_list.wait_select_single(
230 'Notification', objectName=
'notification1')
231 notification = get_notification()
233 notification, summary, body,
True,
False, 1.0)
234 notification.pointing_device.click_object(
235 notification.select_single(objectName=
"notify_button0"))
239 """A snap-decision should block input to the greeter/lockscreen beneath it."""
242 summary =
"Incoming file"
243 body =
"Frank would like to send you the file: essay.pdf"
244 icon_path =
"sync-idle"
246 (
"x-canonical-snap-decisions",
"true"),
247 (
"x-canonical-non-shaped-icon",
"true"),
248 (
"x-canonical-private-affirmative-tint",
"true"),
249 (
"x-canonical-private-rejection-tint",
"true"),
253 (
'action_accept',
'Accept'),
254 (
'action_decline_1',
'Decline'),
269 self.main_window.show_dash_swiping()
271 self.main_window.is_launcher_open, Eventually(Equals(
False)))
275 get_notification =
lambda: notify_list.wait_select_single(
276 'Notification', objectName=
'notification1')
277 notification = get_notification()
279 notification, summary, body,
True,
False, 1.0)
280 notification.pointing_device.click_object(
281 notification.select_single(objectName=
"notify_button0"))
284 def _create_interactive_notification(
293 """Create a interactive notification command.
295 :param summary: Summary text for the notification
296 :param body: Body text to display in the notification
297 :param icon: Path string to the icon to use
298 :param urgency: Urgency string for the noticiation, either: 'LOW',
300 :param actions: List of tuples containing the 'id' and 'label' for all
302 :param hint_strings: List of tuples containing the 'name' and value for
303 setting the hint strings for the notification
308 "Creating snap-decision notification with summary(%s), body(%s) "
316 '--summary', summary,
322 script_args.extend([
'--icon', icon])
326 script_args.extend([
'--hint',
"%s,%s" % (key, value)])
328 for action
in actions:
329 action_id, action_label = action
330 action_string =
"%s,%s" % (action_id, action_label)
331 script_args.extend([
'--action', action_string])
333 python_bin = subprocess.check_output([
'which',
'python3']).strip()
335 logger.info(
"Launching snap-decision notification as: %s", command)
338 stdin=subprocess.PIPE,
339 stdout=subprocess.PIPE,
340 stderr=subprocess.PIPE,
342 universal_newlines=
True,
347 poll_result = self._notify_proc.poll()
348 if poll_result
is not None and self._notify_proc.returncode != 0:
349 error_output = self._notify_proc.communicate()[1]
350 raise RuntimeError(
"Call to script failed with: %s" % error_output)
352 def _get_notify_script(self):
353 """Returns the path to the interactive notification creation script."""
354 file_path =
"../../emulators/create_interactive_notification.py"
356 the_path = os.path.abspath(
357 os.path.join(__file__, file_path))
361 def _tidy_up_script_process(self):
362 if self.
_notify_proc is not None and self._notify_proc.poll()
is None:
363 logger.error(
"Notification process wasn't killed, killing now.")
364 os.killpg(self._notify_proc.pid, signal.SIGTERM)
367 """Assert that the interactive notification callback of id *action_id*
370 :raises AssertionError: If no interactive notification has actually
372 :raises AssertionError: When *action_id* does not match the actual
374 :raises AssertionError: If no callback was called at all.
378 raise AssertionError(
"No interactive notification was created.")
380 for i
in range(timeout):
381 self._notify_proc.poll()
382 if self._notify_proc.returncode
is not None:
383 output = self._notify_proc.communicate()
384 actual_action_id = output[0].strip(
"\n")
385 if actual_action_id != action_id:
386 raise AssertionError(
387 "action id '%s' does not match actual returned '%s'"
388 % (action_id, actual_action_id)
394 os.killpg(self._notify_proc.pid, signal.SIGTERM)
396 raise AssertionError(
397 "No callback was called, killing interactivenotification script"
402 """Collection of tests for Emphemeral notifications (non-interactive.)"""
408 Notify.init(
"Autopilot Ephemeral Notification Tests")
409 self.addCleanup(Notify.uninit)
412 """Notification must display the expected summary and body text."""
414 unlock_unity(unity_proxy)
418 summary =
"Icon-Summary-Body"
419 body =
"Hey pal, what's up with the party next weekend? Will you " \
423 (
"x-canonical-secondary-icon",
"message")
426 notification = shell.create_ephemeral_notification(
436 notification =
lambda: notify_list.wait_select_single(
437 'Notification', objectName=
'notification1')
448 """Notification must display the expected summary and secondary
451 unlock_unity(unity_proxy)
455 summary =
"Upload of image completed"
459 notification = shell.create_ephemeral_notification(
469 notification =
lambda: notify_list.wait_select_single(
470 'Notification', objectName=
'notification1')
481 """Notifications must be displayed in order according to their
484 unlock_unity(unity_proxy)
488 summary_low =
'Low Urgency'
489 body_low =
"No, I'd rather see paint dry, pal *yawn*"
492 summary_normal =
'Normal Urgency'
493 body_normal =
"Hey pal, what's up with the party next weekend? Will " \
494 "you join me and Anna?"
497 summary_critical =
'Critical Urgency'
498 body_critical =
'Dude, this is so urgent you have no idea :)'
499 icon_path_critical = self.
_get_icon_path(
'avatars/anna_olsson.png')
501 notification_normal = shell.create_ephemeral_notification(
507 notification_normal.show()
509 notification_low = shell.create_ephemeral_notification(
515 notification_low.show()
517 notification_critical = shell.create_ephemeral_notification(
523 notification_critical.show()
525 get_notification =
lambda: notify_list.wait_select_single(
527 summary=summary_critical
530 notification = get_notification()
540 get_normal_notification =
lambda: notify_list.wait_select_single(
542 summary=summary_normal
544 notification = get_normal_notification()
554 get_low_notification =
lambda: notify_list.wait_select_single(
558 notification = get_low_notification()
569 """Notification must display the expected summary- and body-text."""
571 unlock_unity(unity_proxy)
575 summary =
'Summary-Body'
576 body =
'This is a superfluous notification'
578 notification = shell.create_ephemeral_notification(summary, body)
581 notification = notify_list.wait_select_single(
582 'Notification', objectName=
'notification1')
594 """Notification must display only the expected summary-text."""
596 unlock_unity(unity_proxy)
600 summary =
'Summary-Only'
602 notification = shell.create_ephemeral_notification(summary)
605 notification = notify_list.wait_select_single(
606 'Notification', objectName=
'notification1')
611 """Notification must allow updating its contents while being
614 unlock_unity(unity_proxy)
618 summary =
'Initial notification'
619 body =
'This is the original content of this notification-bubble.'
622 notification = shell.create_ephemeral_notification(
629 get_notification =
lambda: notify_list.wait_select_single(
630 'Notification', summary=summary)
640 summary =
'Updated notification'
641 body =
'Here the same bubble with new title- and body-text, even ' \
642 'the icon can be changed on the update.'
644 notification.update(summary, body, icon_path)
647 get_notification(), summary, body,
True,
False, 1.0)
650 """Notification must allow updating its contents and layout while
653 unlock_unity(unity_proxy)
657 summary =
'Initial layout'
658 body =
'This bubble uses the icon-title-body layout with a ' \
663 notification = shell.create_ephemeral_notification(
668 notification.set_hint_string(
669 'x-canonical-secondary-icon',
674 get_notification =
lambda: notify_list.wait_select_single(
675 'Notification', objectName=
'notification1')
686 notification.clear_hints()
687 summary =
'Updated layout'
688 body =
'After the update we now have a bubble using the title-body ' \
690 notification.update(summary, body,
None)
693 self.assertThat(get_notification, Eventually(NotEquals(
None)))
695 get_notification(), summary, body,
False,
False, 1.0)
698 """ use the create notification script to get a notification dialog.
699 Check that the arguments passed to the script match the fields. """
702 unlock_unity(unity_proxy)
704 summary =
'Helper summary'
707 notification = shell.create_ephemeral_notification(summary, body)
710 notification_data = self.main_window.wait_for_notification()
712 self.assertThat(notification_data[
'summary'],
713 Eventually(Equals(summary)))
714 self.assertThat(notification_data[
'body'], Eventually(Equals(body)))
def test_sd_one_over_two_layout(self)
def test_update_notification_same_layout(self)
def launch_unity(self, mode="full-greeter", args)
def _get_notify_script(self)
def _get_icon_path(self, icon_name)
def test_icon_summary(self)
def test_modal_sd_with_greeter(self)
def assert_notification_action_id_was_called
def test_icon_summary_body(self)
def _create_interactive_notification
def test_summary_only(self)
def _tidy_up_script_process(self)
def test_modal_sd_without_greeter(self)
def test_update_notification_layout_change(self)
def test_summary_and_body(self)
def test_interactive(self)
def test_urgency_order(self)
def _get_notifications_list(self)
def test_notification_helper(self)