chromium/chrome/browser/notifications/win/notification_template_builder_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/notifications/win/notification_template_builder.h"

#include <memory>
#include <string>

#include "base/strings/strcat_win.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/browser/notifications/win/fake_notification_image_retainer.h"
#include "chrome/browser/notifications/win/notification_launch_id.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/branded_strings.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util_win.h"
#include "ui/message_center/public/cpp/notification.h"

using message_center::Notification;
using message_center::NotifierId;
using message_center::RichNotificationData;

namespace {

const char kContextMenuLabel[] = "settings";
const char kEncodedId[] =
    "0|0|Default|aumi|0|https://example.com/|notification_id";
const char kNotificationId[] = "notification_id";
const char16_t kNotificationTitle[] = u"My Title";
const char16_t kNotificationMessage[] = u"My Message";
const char kNotificationOrigin[] = "https://example.com";

base::Time FixedTime() {
  static constexpr base::Time::Exploded kTime = {.year = 1998,
                                                 .month = 9,
                                                 .day_of_month = 4,
                                                 .hour = 1,
                                                 .minute = 2,
                                                 .second = 3};
  base::Time time;
  EXPECT_TRUE(base::Time::FromUTCExploded(kTime, &time));
  return time;
}

}  // namespace

class NotificationTemplateBuilderTest : public ::testing::Test {
 public:
  NotificationTemplateBuilderTest() = default;
  NotificationTemplateBuilderTest(const NotificationTemplateBuilderTest&) =
      delete;
  NotificationTemplateBuilderTest& operator=(
      const NotificationTemplateBuilderTest&) = delete;
  ~NotificationTemplateBuilderTest() override = default;

  void SetUp() override { SetContextMenuLabelForTesting(kContextMenuLabel); }

  void TearDown() override { SetContextMenuLabelForTesting(nullptr); }

 protected:
  // Builds a notification object and initializes it to default values.
  message_center::Notification BuildNotification() {
    GURL origin_url(kNotificationOrigin);
    message_center::Notification notification(
        message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
        kNotificationTitle, kNotificationMessage, ui::ImageModel() /* icon */,
        std::u16string() /* display_source */, origin_url,
        NotifierId(origin_url), RichNotificationData(), nullptr /* delegate */);
    // Set a fixed timestamp, to avoid having to test against current timestamp.
    notification.set_timestamp(FixedTime());
    notification.set_settings_button_handler(
        message_center::SettingsButtonHandler::DELEGATE);
    return notification;
  }

  // Converts the notification data to XML and verifies it is as expected. Calls
  // must be wrapped in ASSERT_NO_FATAL_FAILURE().
  void VerifyXml(const message_center::Notification& notification,
                 const std::wstring& expected_xml_template) {
    FakeNotificationImageRetainer image_retainer;
    NotificationLaunchId launch_id(kEncodedId);
    std::wstring xml_template =
        BuildNotificationTemplate(&image_retainer, launch_id, notification);
    EXPECT_EQ(xml_template, expected_xml_template);
  }

  base::test::TaskEnvironment task_environment_;
};

TEST_F(NotificationTemplateBuilderTest, SimpleToast) {
  message_center::Notification notification = BuildNotification();

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, Buttons) {
  message_center::Notification notification = BuildNotification();

  std::vector<message_center::ButtonInfo> buttons;
  buttons.emplace_back(u"Button1");
  buttons.emplace_back(u"Button2");
  notification.set_buttons(buttons);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action activationType="foreground" content="Button1" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action activationType="foreground" content="Button2" arguments="1|1|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, InlineReplies) {
  message_center::Notification notification = BuildNotification();

  std::vector<message_center::ButtonInfo> buttons;
  message_center::ButtonInfo button1(u"Button1");
  button1.placeholder = u"Reply here";
  buttons.emplace_back(button1);
  buttons.emplace_back(u"Button2");
  notification.set_buttons(buttons);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <input id="userResponse" type="text" placeHolderContent="Reply here"/>
  <action activationType="foreground" content="Button1" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action activationType="foreground" content="Button2" arguments="1|1|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, InlineRepliesDoubleInput) {
  message_center::Notification notification = BuildNotification();

  std::vector<message_center::ButtonInfo> buttons;
  message_center::ButtonInfo button1(u"Button1");
  button1.placeholder = u"Reply here";
  buttons.emplace_back(button1);
  message_center::ButtonInfo button2(u"Button2");
  button2.placeholder = u"Should not appear";
  buttons.emplace_back(button2);
  notification.set_buttons(buttons);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <input id="userResponse" type="text" placeHolderContent="Reply here"/>
  <action activationType="foreground" content="Button1" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action activationType="foreground" content="Button2" arguments="1|1|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, InlineRepliesTextTypeNotFirst) {
  message_center::Notification notification = BuildNotification();

  std::vector<message_center::ButtonInfo> buttons;
  buttons.emplace_back(u"Button1");
  message_center::ButtonInfo button2(u"Button2");
  button2.placeholder = u"Reply here";
  buttons.emplace_back(button2);
  notification.set_buttons(buttons);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <input id="userResponse" type="text" placeHolderContent="Reply here"/>
  <action activationType="foreground" content="Button1" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action activationType="foreground" content="Button2" arguments="1|1|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, Silent) {
  message_center::Notification notification = BuildNotification();
  notification.set_silent(true);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
 <audio silent="true"/>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, RequireInteraction) {
  message_center::Notification notification = BuildNotification();

  std::vector<message_center::ButtonInfo> buttons;
  buttons.emplace_back(u"Button1");
  notification.set_buttons(buttons);
  notification.set_never_timeout(true);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" scenario="reminder" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action activationType="foreground" content="Button1" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, RequireInteractionSuppressed) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(
      features::kNotificationDurationLongForRequireInteraction);

  message_center::Notification notification = BuildNotification();
  notification.set_never_timeout(true);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" duration="long" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, NullTimestamp) {
  message_center::Notification notification = BuildNotification();
  notification.set_timestamp(base::Time());

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, LocalizedContextMenu) {
  message_center::Notification notification = BuildNotification();

  // Disable overriding context menu label.
  SetContextMenuLabelForTesting(nullptr);

  std::wstring expected_xml = base::StrCat(
      {LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action content=")",
       l10n_util::GetWideString(
           IDS_WIN_NOTIFICATION_SETTINGS_CONTEXT_MENU_ITEM_NAME),
       LR"(" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)"});

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, expected_xml));
}

TEST_F(NotificationTemplateBuilderTest, Images) {
  message_center::Notification notification = BuildNotification();

  SkBitmap icon;
  icon.allocN32Pixels(64, 64);
  icon.eraseARGB(255, 100, 150, 200);

  notification.set_icon(
      ui::ImageModel::FromImage(gfx::Image::CreateFrom1xBitmap(icon)));
  notification.SetImage(gfx::Image::CreateFrom1xBitmap(icon));

  std::vector<message_center::ButtonInfo> buttons;
  message_center::ButtonInfo button(u"Button1");
  button.placeholder = u"Reply here";
  button.icon = gfx::Image::CreateFrom1xBitmap(icon);
  buttons.emplace_back(button);
  notification.set_buttons(buttons);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
   <image placement="appLogoOverride" src="c:\temp\img0.tmp" hint-crop="none"/>
   <image placement="hero" src="c:\temp\img1.tmp"/>
  </binding>
 </visual>
 <actions>
  <input id="userResponse" type="text" placeHolderContent="Reply here"/>
  <action activationType="foreground" content="Button1" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id" imageUri="c:\temp\img2.tmp"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, ContextMessage) {
  message_center::Notification notification = BuildNotification();

  notification.set_context_message(u"context_message");

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">context_message</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, ExtensionNoContextMessage) {
  message_center::Notification notification = BuildNotification();

  // Explicitly not setting context message to ensure attribution is not added.
  // Explicitly set origin url to something non http/https to ensure that origin
  // is not used as attribution.
  notification.set_origin_url(
      GURL("chrome-extension://bfojpkhoiegeigfifhdnbeobmhlahdle/"));

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, ProgressBar) {
  message_center::Notification notification = BuildNotification();

  notification.set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
  notification.set_progress(30);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
   <progress status="" value="0.30"/>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, ProgressBar_Indeterminate) {
  message_center::Notification notification = BuildNotification();

  notification.set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
  // Setting the progress outside the [0-100] range should result in an
  // indeterminate progress notification.
  notification.set_progress(-1);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
   <progress status="" value="indeterminate"/>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, ListEntries) {
  message_center::Notification notification = BuildNotification();

  notification.set_type(message_center::NOTIFICATION_TYPE_MULTIPLE);
  std::vector<message_center::NotificationItem> items;
  items.push_back({u"title1", u"message1"});
  items.push_back({u"title2", u"message2"});
  items.push_back({u"title3", u"message3"});
  items.push_back({u"title4", u"message4"});
  items.push_back({u"title5", u"message5"});  // Will be truncated.
  notification.set_items(items);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>title1 - message1
title2 - message2
title3 - message3
title4 - message4
</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, NoSettings) {
  message_center::Notification notification = BuildNotification();

  // Disable overriding context menu label.
  SetContextMenuLabelForTesting(nullptr);

  notification.set_settings_button_handler(
      message_center::SettingsButtonHandler::NONE);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions/>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, IncomingCallFromWebApp) {
  message_center::Notification notification = BuildNotification();
  notification.set_scenario(
      message_center::NotificationScenario::INCOMING_CALL);

  std::vector<message_center::ButtonInfo> buttons;
  message_center::ButtonInfo acknowledge_button(u"Acknowledge");
  acknowledge_button.type = message_center::ButtonType::ACKNOWLEDGE;
  buttons.push_back(acknowledge_button);
  message_center::ButtonInfo dismiss_button(u"Close");
  dismiss_button.type = message_center::ButtonType::DISMISS;
  buttons.push_back(dismiss_button);
  notification.set_buttons(buttons);

  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" scenario="incomingCall" useButtonStyle="true" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action activationType="foreground" hint-buttonStyle="Success" content="Acknowledge" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action activationType="background" hint-buttonStyle="Critical" content="Close" arguments="3|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}

TEST_F(NotificationTemplateBuilderTest, IncomingCallFromNonInstalledOrigin) {
  message_center::Notification notification = BuildNotification();

  std::vector<message_center::ButtonInfo> buttons;
  message_center::ButtonInfo acknowledge_button(u"Acknowledge");
  acknowledge_button.type = message_center::ButtonType::ACKNOWLEDGE;
  buttons.push_back(acknowledge_button);
  message_center::ButtonInfo dismiss_button(u"Close");
  dismiss_button.type = message_center::ButtonType::DISMISS;
  buttons.push_back(dismiss_button);
  notification.set_buttons(buttons);

  // In this case, the toast wont' have the "scenario" and "useButtonStyle"
  // arguments being set. Thus, even if the action buttons have the
  // "hint-buttonStyle" argument set, it should not take effect.
  const wchar_t kExpectedXml[] =
      LR"(<toast launch="0|0|Default|aumi|0|https://example.com/|notification_id" displayTimestamp="1998-09-04T01:02:03Z">
 <visual>
  <binding template="ToastGeneric">
   <text>My Title</text>
   <text>My Message</text>
   <text placement="attribution">example.com</text>
  </binding>
 </visual>
 <actions>
  <action activationType="foreground" hint-buttonStyle="Success" content="Acknowledge" arguments="1|0|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action activationType="background" hint-buttonStyle="Critical" content="Close" arguments="3|0|Default|aumi|0|https://example.com/|notification_id"/>
  <action content="settings" placement="contextMenu" activationType="foreground" arguments="2|0|Default|aumi|0|https://example.com/|notification_id"/>
 </actions>
</toast>
)";

  ASSERT_NO_FATAL_FAILURE(VerifyXml(notification, kExpectedXml));
}