Home | History | Annotate | Download | only in notifications
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/strings/stringprintf.h"
      6 #include "chrome/browser/browser_process.h"
      7 #include "chrome/browser/chrome_notification_types.h"
      8 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
      9 #include "chrome/browser/extensions/extension_apitest.h"
     10 #include "chrome/browser/extensions/extension_function_test_utils.h"
     11 #include "chrome/common/extensions/features/feature.h"
     12 #include "content/public/browser/notification_service.h"
     13 #include "content/public/test/test_utils.h"
     14 #include "ui/message_center/message_center.h"
     15 #include "ui/message_center/message_center_switches.h"
     16 #include "ui/message_center/message_center_util.h"
     17 
     18 using extensions::Extension;
     19 
     20 namespace utils = extension_function_test_utils;
     21 
     22 namespace {
     23 
     24 class NotificationsApiTest : public ExtensionApiTest {
     25  public:
     26   const extensions::Extension* LoadExtensionAndWait(
     27       const std::string& test_name) {
     28     base::FilePath extdir = test_data_dir_.AppendASCII(test_name);
     29     content::WindowedNotificationObserver page_created(
     30         chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
     31         content::NotificationService::AllSources());
     32     const extensions::Extension* extension = LoadExtension(extdir);
     33     if (extension) {
     34       page_created.Wait();
     35     }
     36     return extension;
     37   }
     38 };
     39 
     40 }  // namespace
     41 
     42 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestIdUsage) {
     43   // Create a new notification. A lingering output of this block is the
     44   // notifications ID, which we'll use in later parts of this test.
     45   std::string notification_id;
     46   scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
     47   {
     48     scoped_refptr<extensions::NotificationsCreateFunction>
     49         notification_function(
     50             new extensions::NotificationsCreateFunction());
     51 
     52     notification_function->set_extension(empty_extension.get());
     53     notification_function->set_has_callback(true);
     54 
     55     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
     56         notification_function.get(),
     57         "[\"\", "  // Empty string: ask API to generate ID
     58         "{"
     59         "\"type\": \"basic\","
     60         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
     61         "\"title\": \"Attention!\","
     62         "\"message\": \"Check out Cirque du Soleil\""
     63         "}]",
     64         browser(),
     65         utils::NONE));
     66 
     67     ASSERT_EQ(base::Value::TYPE_STRING, result->GetType());
     68     ASSERT_TRUE(result->GetAsString(&notification_id));
     69     ASSERT_TRUE(notification_id.length() > 0);
     70   }
     71 
     72   // Update the existing notification.
     73   {
     74     scoped_refptr<extensions::NotificationsUpdateFunction>
     75         notification_function(
     76             new extensions::NotificationsUpdateFunction());
     77 
     78     notification_function->set_extension(empty_extension.get());
     79     notification_function->set_has_callback(true);
     80 
     81     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
     82         notification_function.get(),
     83         "[\"" + notification_id +
     84             "\", "
     85             "{"
     86             "\"type\": \"basic\","
     87             "\"iconUrl\": \"an/image/that/does/not/exist.png\","
     88             "\"title\": \"Attention!\","
     89             "\"message\": \"Too late! The show ended yesterday\""
     90             "}]",
     91         browser(),
     92         utils::NONE));
     93 
     94     ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
     95     bool copy_bool_value = false;
     96     ASSERT_TRUE(result->GetAsBoolean(&copy_bool_value));
     97     ASSERT_TRUE(copy_bool_value);
     98 
     99     // TODO(miket): add a testing method to query the message from the
    100     // displayed notification, and assert it matches the updated message.
    101     //
    102     // TODO(miket): add a method to count the number of outstanding
    103     // notifications, and confirm it remains at one at this point.
    104   }
    105 
    106   // Update a nonexistent notification.
    107   {
    108     scoped_refptr<extensions::NotificationsUpdateFunction>
    109         notification_function(
    110             new extensions::NotificationsUpdateFunction());
    111 
    112     notification_function->set_extension(empty_extension.get());
    113     notification_function->set_has_callback(true);
    114 
    115     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    116         notification_function.get(),
    117         "[\"xxxxxxxxxxxx\", "
    118         "{"
    119         "\"type\": \"basic\","
    120         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    121         "\"title\": \"!\","
    122         "\"message\": \"!\""
    123         "}]",
    124         browser(),
    125         utils::NONE));
    126 
    127     ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
    128     bool copy_bool_value = false;
    129     ASSERT_TRUE(result->GetAsBoolean(&copy_bool_value));
    130     ASSERT_FALSE(copy_bool_value);
    131   }
    132 
    133   // Clear a nonexistent notification.
    134   {
    135     scoped_refptr<extensions::NotificationsClearFunction>
    136         notification_function(
    137             new extensions::NotificationsClearFunction());
    138 
    139     notification_function->set_extension(empty_extension.get());
    140     notification_function->set_has_callback(true);
    141 
    142     scoped_ptr<base::Value> result(
    143         utils::RunFunctionAndReturnSingleResult(notification_function.get(),
    144                                                 "[\"xxxxxxxxxxx\"]",
    145                                                 browser(),
    146                                                 utils::NONE));
    147 
    148     ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
    149     bool copy_bool_value = false;
    150     ASSERT_TRUE(result->GetAsBoolean(&copy_bool_value));
    151     ASSERT_FALSE(copy_bool_value);
    152   }
    153 
    154   // Clear the notification we created.
    155   {
    156     scoped_refptr<extensions::NotificationsClearFunction>
    157         notification_function(
    158             new extensions::NotificationsClearFunction());
    159 
    160     notification_function->set_extension(empty_extension.get());
    161     notification_function->set_has_callback(true);
    162 
    163     scoped_ptr<base::Value> result(
    164         utils::RunFunctionAndReturnSingleResult(notification_function.get(),
    165                                                 "[\"" + notification_id + "\"]",
    166                                                 browser(),
    167                                                 utils::NONE));
    168 
    169     ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
    170     bool copy_bool_value = false;
    171     ASSERT_TRUE(result->GetAsBoolean(&copy_bool_value));
    172     ASSERT_TRUE(copy_bool_value);
    173   }
    174 }
    175 
    176 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestBaseFormatNotification) {
    177   scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
    178 
    179   // Create a new notification with the minimum required properties.
    180   {
    181     scoped_refptr<extensions::NotificationsCreateFunction>
    182         notification_create_function(
    183             new extensions::NotificationsCreateFunction());
    184     notification_create_function->set_extension(empty_extension.get());
    185     notification_create_function->set_has_callback(true);
    186 
    187     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    188         notification_create_function.get(),
    189         "[\"\", "
    190         "{"
    191         "\"type\": \"basic\","
    192         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    193         "\"title\": \"Attention!\","
    194         "\"message\": \"Check out Cirque du Soleil\""
    195         "}]",
    196         browser(),
    197         utils::NONE));
    198 
    199     std::string notification_id;
    200     ASSERT_EQ(base::Value::TYPE_STRING, result->GetType());
    201     ASSERT_TRUE(result->GetAsString(&notification_id));
    202     ASSERT_TRUE(notification_id.length() > 0);
    203   }
    204 
    205   // Create another new notification with more than the required properties.
    206   {
    207     scoped_refptr<extensions::NotificationsCreateFunction>
    208         notification_create_function(
    209             new extensions::NotificationsCreateFunction());
    210     notification_create_function->set_extension(empty_extension.get());
    211     notification_create_function->set_has_callback(true);
    212 
    213     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    214         notification_create_function.get(),
    215         "[\"\", "
    216         "{"
    217         "\"type\": \"basic\","
    218         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    219         "\"title\": \"Attention!\","
    220         "\"message\": \"Check out Cirque du Soleil\","
    221         "\"priority\": 1,"
    222         "\"eventTime\": 1234567890.12345678,"
    223         "\"buttons\": ["
    224         "  {"
    225         "   \"title\": \"Up\","
    226         "   \"iconUrl\":\"http://www.google.com/logos/2012/\""
    227         "  },"
    228         "  {"
    229         "   \"title\": \"Down\""  // note: no iconUrl
    230         "  }"
    231         "],"
    232         "\"expandedMessage\": \"This is a longer expanded message.\","
    233         "\"imageUrl\": \"http://www.google.com/logos/2012/election12-hp.jpg\""
    234         "}]",
    235         browser(),
    236         utils::NONE));
    237 
    238     std::string notification_id;
    239     ASSERT_EQ(base::Value::TYPE_STRING, result->GetType());
    240     ASSERT_TRUE(result->GetAsString(&notification_id));
    241     ASSERT_TRUE(notification_id.length() > 0);
    242   }
    243 
    244   // Error case: missing type property.
    245   {
    246     scoped_refptr<extensions::NotificationsCreateFunction>
    247         notification_create_function(
    248             new extensions::NotificationsCreateFunction());
    249     notification_create_function->set_extension(empty_extension.get());
    250     notification_create_function->set_has_callback(true);
    251 
    252     utils::RunFunction(
    253         notification_create_function.get(),
    254         "[\"\", "
    255         "{"
    256         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    257         "\"title\": \"Attention!\","
    258         "\"message\": \"Check out Cirque du Soleil\""
    259         "}]",
    260         browser(),
    261         utils::NONE);
    262 
    263     EXPECT_FALSE(notification_create_function->GetError().empty());
    264   }
    265 
    266   // Error case: missing iconUrl property.
    267   {
    268     scoped_refptr<extensions::NotificationsCreateFunction>
    269         notification_create_function(
    270             new extensions::NotificationsCreateFunction());
    271     notification_create_function->set_extension(empty_extension.get());
    272     notification_create_function->set_has_callback(true);
    273 
    274     utils::RunFunction(
    275         notification_create_function.get(),
    276         "[\"\", "
    277         "{"
    278         "\"type\": \"basic\","
    279         "\"title\": \"Attention!\","
    280         "\"message\": \"Check out Cirque du Soleil\""
    281         "}]",
    282         browser(),
    283         utils::NONE);
    284 
    285     EXPECT_FALSE(notification_create_function->GetError().empty());
    286   }
    287 
    288   // Error case: missing title property.
    289   {
    290     scoped_refptr<extensions::NotificationsCreateFunction>
    291         notification_create_function(
    292             new extensions::NotificationsCreateFunction());
    293     notification_create_function->set_extension(empty_extension.get());
    294     notification_create_function->set_has_callback(true);
    295 
    296     utils::RunFunction(
    297         notification_create_function.get(),
    298         "[\"\", "
    299         "{"
    300         "\"type\": \"basic\","
    301         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    302         "\"message\": \"Check out Cirque du Soleil\""
    303         "}]",
    304         browser(),
    305         utils::NONE);
    306 
    307     EXPECT_FALSE(notification_create_function->GetError().empty());
    308   }
    309 
    310   // Error case: missing message property.
    311   {
    312     scoped_refptr<extensions::NotificationsCreateFunction>
    313         notification_create_function(
    314             new extensions::NotificationsCreateFunction());
    315     notification_create_function->set_extension(empty_extension.get());
    316     notification_create_function->set_has_callback(true);
    317 
    318     utils::RunFunction(
    319         notification_create_function.get(),
    320         "[\"\", "
    321         "{"
    322         "\"type\": \"basic\","
    323         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    324         "\"title\": \"Attention!\""
    325         "}]",
    326         browser(),
    327         utils::NONE);
    328 
    329     EXPECT_FALSE(notification_create_function->GetError().empty());
    330   }
    331 }
    332 
    333 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestMultipleItemNotification) {
    334   scoped_refptr<extensions::NotificationsCreateFunction>
    335       notification_create_function(
    336           new extensions::NotificationsCreateFunction());
    337   scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
    338 
    339   notification_create_function->set_extension(empty_extension.get());
    340   notification_create_function->set_has_callback(true);
    341 
    342   scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    343       notification_create_function.get(),
    344       "[\"\", "
    345       "{"
    346       "\"type\": \"list\","
    347       "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    348       "\"title\": \"Multiple Item Notification Title\","
    349       "\"message\": \"Multiple item notification message.\","
    350       "\"items\": ["
    351       "  {\"title\": \"Brett Boe\","
    352       " \"message\": \"This is an important message!\"},"
    353       "  {\"title\": \"Carla Coe\","
    354       " \"message\": \"Just took a look at the proposal\"},"
    355       "  {\"title\": \"Donna Doe\","
    356       " \"message\": \"I see that you went to the conference\"},"
    357       "  {\"title\": \"Frank Foe\","
    358       " \"message\": \"I ate Harry's sandwich!\"},"
    359       "  {\"title\": \"Grace Goe\","
    360       " \"message\": \"I saw Frank steal a sandwich :-)\"}"
    361       "],"
    362       "\"priority\": 1,"
    363       "\"eventTime\": 1361488019.9999999"
    364       "}]",
    365       browser(),
    366       utils::NONE));
    367   // TODO(dharcourt): [...], items = [{title: foo, message: bar}, ...], [...]
    368 
    369   std::string notification_id;
    370   ASSERT_EQ(base::Value::TYPE_STRING, result->GetType());
    371   ASSERT_TRUE(result->GetAsString(&notification_id));
    372   ASSERT_TRUE(notification_id.length() > 0);
    373 }
    374 
    375 #if defined(OS_LINUX)
    376 #define MAYBE_TestGetAll DISABLED_TestGetAll
    377 #else
    378 #define MAYBE_TestGetAll TestGetAll
    379 #endif
    380 
    381 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, MAYBE_TestGetAll) {
    382   scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
    383 
    384   {
    385     scoped_refptr<extensions::NotificationsGetAllFunction>
    386         notification_get_all_function(
    387             new extensions::NotificationsGetAllFunction());
    388     notification_get_all_function->set_extension(empty_extension.get());
    389     notification_get_all_function->set_has_callback(true);
    390     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    391         notification_get_all_function.get(), "[]", browser(), utils::NONE));
    392 
    393     base::DictionaryValue* return_value;
    394     ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
    395     ASSERT_TRUE(result->GetAsDictionary(&return_value));
    396     ASSERT_TRUE(return_value->size() == 0);
    397   }
    398 
    399   const unsigned int kNotificationsToCreate = 4;
    400 
    401   for (unsigned int i = 0; i < kNotificationsToCreate; i++) {
    402     scoped_refptr<extensions::NotificationsCreateFunction>
    403         notification_create_function(
    404             new extensions::NotificationsCreateFunction());
    405 
    406     notification_create_function->set_extension(empty_extension.get());
    407     notification_create_function->set_has_callback(true);
    408 
    409     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    410         notification_create_function.get(),
    411         base::StringPrintf("[\"identifier-%u\", "
    412                            "{"
    413                            "\"type\": \"basic\","
    414                            "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    415                            "\"title\": \"Title\","
    416                            "\"message\": \"Message.\","
    417                            "\"priority\": 1,"
    418                            "\"eventTime\": 1361488019.9999999"
    419                            "}]",
    420                            i),
    421         browser(),
    422         utils::NONE));
    423   }
    424 
    425   {
    426     scoped_refptr<extensions::NotificationsGetAllFunction>
    427         notification_get_all_function(
    428             new extensions::NotificationsGetAllFunction());
    429     notification_get_all_function->set_extension(empty_extension.get());
    430     notification_get_all_function->set_has_callback(true);
    431     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    432         notification_get_all_function.get(), "[]", browser(), utils::NONE));
    433 
    434     base::DictionaryValue* return_value;
    435     ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
    436     ASSERT_TRUE(result->GetAsDictionary(&return_value));
    437     ASSERT_EQ(return_value->size(), kNotificationsToCreate);
    438     bool dictionary_bool = false;
    439     for (unsigned int i = 0; i < kNotificationsToCreate; i++) {
    440       std::string id = base::StringPrintf("identifier-%u", i);
    441       ASSERT_TRUE(return_value->GetBoolean(id, &dictionary_bool));
    442       ASSERT_TRUE(dictionary_bool);
    443     }
    444   }
    445 }
    446 
    447 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestEvents) {
    448   ASSERT_TRUE(RunExtensionTest("notifications/api/events")) << message_;
    449 }
    450 
    451 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestCSP) {
    452   ASSERT_TRUE(RunExtensionTest("notifications/api/csp")) << message_;
    453 }
    454 
    455 // MessaceCenter-specific test.
    456 #if defined(RUN_MESSAGE_CENTER_TESTS)
    457 #define MAYBE_TestByUser TestByUser
    458 #else
    459 #define MAYBE_TestByUser DISABLED_TestByUser
    460 #endif
    461 
    462 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, MAYBE_TestByUser) {
    463   ASSERT_TRUE(message_center::IsRichNotificationEnabled());
    464 
    465   const extensions::Extension* extension =
    466       LoadExtensionAndWait("notifications/api/by_user");
    467   ASSERT_TRUE(extension) << message_;
    468 
    469   {
    470     ResultCatcher catcher;
    471     g_browser_process->message_center()->RemoveNotification(
    472         extension->id() + "-FOO",
    473         false);
    474     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
    475   }
    476 
    477   {
    478     ResultCatcher catcher;
    479     g_browser_process->message_center()->RemoveNotification(
    480         extension->id() + "-BAR",
    481         true);
    482     EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
    483   }
    484 }
    485 
    486 
    487 #if defined(OS_LINUX)
    488 #define MAYBE_TestProgressNotification DISABLED_TestProgressNotification
    489 #else
    490 #define MAYBE_TestProgressNotification TestProgressNotification
    491 #endif
    492 
    493 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, MAYBE_TestProgressNotification) {
    494   scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
    495 
    496   // Create a new progress notification.
    497   std::string notification_id;
    498   {
    499     scoped_refptr<extensions::NotificationsCreateFunction>
    500         notification_create_function(
    501             new extensions::NotificationsCreateFunction());
    502     notification_create_function->set_extension(empty_extension.get());
    503     notification_create_function->set_has_callback(true);
    504 
    505     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    506         notification_create_function.get(),
    507         "[\"\", "
    508         "{"
    509         "\"type\": \"progress\","
    510         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    511         "\"title\": \"Test!\","
    512         "\"message\": \"This is a progress notification.\","
    513         "\"priority\": 1,"
    514         "\"eventTime\": 1234567890.12345678,"
    515         "\"progress\": 30"
    516         "}]",
    517         browser(),
    518         utils::NONE));
    519 
    520     EXPECT_EQ(base::Value::TYPE_STRING, result->GetType());
    521     EXPECT_TRUE(result->GetAsString(&notification_id));
    522     EXPECT_TRUE(notification_id.length() > 0);
    523   }
    524 
    525   // Update the progress property only.
    526   {
    527     scoped_refptr<extensions::NotificationsUpdateFunction>
    528         notification_function(
    529             new extensions::NotificationsUpdateFunction());
    530     notification_function->set_extension(empty_extension.get());
    531     notification_function->set_has_callback(true);
    532 
    533     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    534         notification_function.get(),
    535         "[\"" + notification_id +
    536             "\", "
    537             "{"
    538             "\"progress\": 60"
    539             "}]",
    540         browser(),
    541         utils::NONE));
    542 
    543     EXPECT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
    544     bool copy_bool_value = false;
    545     EXPECT_TRUE(result->GetAsBoolean(&copy_bool_value));
    546     EXPECT_TRUE(copy_bool_value);
    547   }
    548 
    549   // Error case: progress value provided for non-progress type.
    550   {
    551     scoped_refptr<extensions::NotificationsCreateFunction>
    552         notification_create_function(
    553             new extensions::NotificationsCreateFunction());
    554     notification_create_function->set_extension(empty_extension.get());
    555     notification_create_function->set_has_callback(true);
    556 
    557     utils::RunFunction(
    558         notification_create_function.get(),
    559         "[\"\", "
    560         "{"
    561         "\"type\": \"basic\","
    562         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    563         "\"title\": \"Test!\","
    564         "\"message\": \"This is a progress notification.\","
    565         "\"priority\": 1,"
    566         "\"eventTime\": 1234567890.12345678,"
    567         "\"progress\": 10"
    568         "}]",
    569         browser(),
    570         utils::NONE);
    571     EXPECT_FALSE(notification_create_function->GetError().empty());
    572   }
    573 
    574   // Error case: progress value less than lower bound.
    575   {
    576     scoped_refptr<extensions::NotificationsCreateFunction>
    577         notification_create_function(
    578             new extensions::NotificationsCreateFunction());
    579     notification_create_function->set_extension(empty_extension.get());
    580     notification_create_function->set_has_callback(true);
    581 
    582     utils::RunFunction(
    583         notification_create_function.get(),
    584         "[\"\", "
    585         "{"
    586         "\"type\": \"progress\","
    587         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    588         "\"title\": \"Test!\","
    589         "\"message\": \"This is a progress notification.\","
    590         "\"priority\": 1,"
    591         "\"eventTime\": 1234567890.12345678,"
    592         "\"progress\": -10"
    593         "}]",
    594         browser(),
    595         utils::NONE);
    596     EXPECT_FALSE(notification_create_function->GetError().empty());
    597   }
    598 
    599   // Error case: progress value greater than upper bound.
    600   {
    601     scoped_refptr<extensions::NotificationsCreateFunction>
    602         notification_create_function(
    603             new extensions::NotificationsCreateFunction());
    604     notification_create_function->set_extension(empty_extension.get());
    605     notification_create_function->set_has_callback(true);
    606 
    607     utils::RunFunction(
    608         notification_create_function.get(),
    609         "[\"\", "
    610         "{"
    611         "\"type\": \"progress\","
    612         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    613         "\"title\": \"Test!\","
    614         "\"message\": \"This is a progress notification.\","
    615         "\"priority\": 1,"
    616         "\"eventTime\": 1234567890.12345678,"
    617         "\"progress\": 101"
    618         "}]",
    619         browser(),
    620         utils::NONE);
    621     EXPECT_FALSE(notification_create_function->GetError().empty());
    622   }
    623 }
    624 
    625 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestPartialUpdate) {
    626   scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
    627 
    628   // Create a new notification.
    629   std::string notification_id;
    630   {
    631     scoped_refptr<extensions::NotificationsCreateFunction>
    632         notification_function(
    633             new extensions::NotificationsCreateFunction());
    634     notification_function->set_extension(empty_extension.get());
    635     notification_function->set_has_callback(true);
    636 
    637     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    638         notification_function.get(),
    639         "[\"\", "  // Empty string: ask API to generate ID
    640         "{"
    641         "\"type\": \"basic\","
    642         "\"iconUrl\": \"an/image/that/does/not/exist.png\","
    643         "\"title\": \"Attention!\","
    644         "\"message\": \"Check out Cirque du Soleil\""
    645         "}]",
    646         browser(),
    647         utils::NONE));
    648 
    649     ASSERT_EQ(base::Value::TYPE_STRING, result->GetType());
    650     ASSERT_TRUE(result->GetAsString(&notification_id));
    651     ASSERT_TRUE(notification_id.length() > 0);
    652   }
    653 
    654   // Update a few properties in the existing notification.
    655   {
    656     scoped_refptr<extensions::NotificationsUpdateFunction>
    657         notification_function(
    658             new extensions::NotificationsUpdateFunction());
    659     notification_function->set_extension(empty_extension.get());
    660     notification_function->set_has_callback(true);
    661 
    662     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    663         notification_function.get(),
    664         "[\"" + notification_id +
    665             "\", "
    666             "{"
    667             "\"title\": \"Changed!\","
    668             "\"message\": \"Too late! The show ended yesterday\""
    669             "}]",
    670         browser(),
    671         utils::NONE));
    672 
    673     ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
    674     bool copy_bool_value = false;
    675     ASSERT_TRUE(result->GetAsBoolean(&copy_bool_value));
    676     ASSERT_TRUE(copy_bool_value);
    677   }
    678 
    679   // Update another property in the existing notification.
    680   {
    681     scoped_refptr<extensions::NotificationsUpdateFunction>
    682         notification_function(
    683             new extensions::NotificationsUpdateFunction());
    684     notification_function->set_extension(empty_extension.get());
    685     notification_function->set_has_callback(true);
    686 
    687     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
    688         notification_function.get(),
    689         "[\"" + notification_id +
    690             "\", "
    691             "{"
    692             "\"priority\": 2"
    693             "}]",
    694         browser(),
    695         utils::NONE));
    696 
    697     ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
    698     bool copy_bool_value = false;
    699     ASSERT_TRUE(result->GetAsBoolean(&copy_bool_value));
    700     ASSERT_TRUE(copy_bool_value);
    701   }
    702 }
    703