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.
6 // browser_tests.exe --gtest_filter=ExtensionApiTest.Bookmarks
8 // This is global state that is maintained across tests as a reference
9 // to compare against what's fetched from the browser (using compareTrees).
10 // TODO(erikkay) It would be better if each test was self-contained and
11 // didn't depend on global state.
14 {children:[], id:"1", parentId:"0", index:0, title:"Bookmarks bar"},
15 {children:[], id:"2", parentId:"0", index:1, title:"Other bookmarks"},
16 {id:"4", parentId:"0", index:3, title:"Managed bookmarks",
17 unmodifiable:"managed", children:[
18 {id:"6", parentId:"4", index:0, title:"Managed Bookmark",
19 url:"http://www.chromium.org/", unmodifiable:"managed"},
20 {id:"7", parentId:"4", index:1, title:"Managed Folder",
21 children:[], unmodifiable:"managed"}
24 {id:"5", parentId:"0", index:4, title:"Parent suggestions",
25 unmodifiable:"managed", children:[
26 {id:"8", parentId:"4", index:0, title:"Supervised Bookmark",
27 url:"http://www.pbskids.org/", unmodifiable:"managed"},
28 {id:"9", parentId:"4", index:1, title:"Supervised Folder",
29 children:[], unmodifiable:"managed"}
37 function bookmarksBar() { return expected[0].children[0]; }
38 function otherBookmarks() { return expected[0].children[1]; }
40 // Some variables that are used across multiple tests.
41 var node1 = {parentId:"1", title:"bar baz",
42 url:"http://www.example.com/hello"};
43 var node2 = {parentId:"1", title:"foo quux",
44 url:"http://www.example.com/bar"};
45 var node3 = {parentId:"1", title:"Foo bar baz",
46 url:"http://www.google.com/hello/quux"};
48 var pass = chrome.test.callbackPass;
49 var fail = chrome.test.callbackFail;
51 function compareNode(left, right) {
52 //chrome.test.log("compareNode()");
53 //chrome.test.log(JSON.stringify(left, null, 2));
54 //chrome.test.log(JSON.stringify(right, null, 2));
55 // TODO(erikkay): do some comparison of dateAdded
56 if (left.id != right.id)
57 return "id mismatch: " + left.id + " != " + right.id;
58 if (left.title != right.title) {
59 // TODO(erikkay): This resource dependency still isn't working reliably.
61 // return "title mismatch: " + left.title + " != " + right.title;
62 chrome.test.log("title mismatch: " + left.title + " != " + right.title);
64 if (left.url != right.url)
65 return "url mismatch: " + left.url + " != " + right.url;
66 if (left.index != right.index)
67 return "index mismatch: " + left.index + " != " + right.index;
68 if (left.unmodifiable != right.unmodifiable) {
69 return "unmodifiable mismatch: " + left.unmodifiable +
70 " != " + right.unmodifiable;
75 function compareTrees(left, right, verbose) {
77 chrome.test.log(JSON.stringify(left) || "<null>");
78 chrome.test.log(JSON.stringify(right) || "<null>");
80 if (left == null && right == null) {
83 if (left == null || right == null)
84 return left + " != " + right;
85 if (left.length != right.length)
86 return "count mismatch: " + left.length + " != " + right.length;
87 for (var i = 0; i < left.length; i++) {
88 var result = compareNode(left[i], right[i]);
89 if (result !== true) {
90 chrome.test.log(result);
91 chrome.test.log(JSON.stringify(left, null, 2));
92 chrome.test.log(JSON.stringify(right, null, 2));
95 result = compareTrees(left[i].children, right[i].children);
102 // |expectedParent| is a child within the global |expected| that each node in
103 // the array|nodes| should be added to. |callback| will be called when all of
104 // the nodes have been successfully created.
105 function createNodes(expectedParent, nodes, callback) {
106 function createNext() {
108 var node = nodes.shift();
109 chrome.bookmarks.create(node, function(results) {
110 node.id = results.id;
111 node.index = results.index;
112 expectedParent.children.push(node);
114 // Create each node a minimum of 1ms apart. This ensures that each
115 // node will have a unique timestamp.
116 setTimeout(createNext, 1);
125 // Calls getTree and verifies that the results match the global |expected|
126 // state. Assigns over expected in the end since getTree has more data
127 // (e.g. createdDate) which tests may want to depend on.
128 function verifyTreeIsExpected(callback) {
129 chrome.bookmarks.getTree(pass(function(results) {
130 chrome.test.assertTrue(compareTrees(expected, results),
131 "getTree() result != expected");
138 chrome.test.runTests([
140 verifyTreeIsExpected(pass());
144 chrome.bookmarks.get("1", pass(function(results) {
145 chrome.test.assertTrue(compareNode(results[0], expected[0].children[0]));
147 chrome.bookmarks.get("6", pass(function(results) {
148 chrome.test.assertTrue(compareNode(
149 results[0], expected[0].children[2].children[0]));
151 chrome.bookmarks.get("42", fail("Can't find bookmark for id."));
154 function getArray() {
155 chrome.bookmarks.get(["1", "2"], pass(function(results) {
156 chrome.test.assertTrue(compareNode(results[0], expected[0].children[0]),
157 "get() result != expected");
158 chrome.test.assertTrue(compareNode(results[1], expected[0].children[1]),
159 "get() result != expected");
163 function getChildren() {
164 chrome.bookmarks.getChildren("0", pass(function(results) {
165 chrome.test.assertTrue(compareNode(results[0], expected[0].children[0]),
166 "getChildren() result != expected");
167 chrome.test.assertTrue(compareNode(results[1], expected[0].children[1]),
168 "getChildren() result != expected");
173 var node = {parentId:"1", title:"google", url:"http://www.google.com/"};
174 chrome.test.listenOnce(chrome.bookmarks.onCreated, function(id, created) {
175 node.id = created.id;
177 chrome.test.assertEq(id, node.id);
178 chrome.test.assertTrue(compareNode(node, created));
180 chrome.bookmarks.create(node, pass(function(results) {
181 node.id = results.id; // since we couldn't know this going in
183 chrome.test.assertTrue(compareNode(node, results),
184 "created node != source");
185 expected[0].children[0].children.push(node);
189 function createNoParentId() {
190 var node = {title:"google", url:"http://www.google.com/"};
191 chrome.test.listenOnce(chrome.bookmarks.onCreated, function(id, created) {
192 node.id = created.id;
194 chrome.test.assertEq(id, node.id);
195 // Make sure the parentId defaults to the Other Bookmarks folder.
196 chrome.test.assertEq(expected[0].children[1].id, created.parentId);
197 chrome.test.assertTrue(compareNode(node, created));
198 expected[0].children[1].children.push(node);
200 chrome.bookmarks.create(node, pass(function(results) {
201 node.id = results.id; // since we couldn't know this going in
203 chrome.test.assertTrue(compareNode(node, results),
204 "created node != source");
208 function createInRoot() {
209 const error = "Can't modify the root bookmark folders.";
210 var node = {parentId:"0", title:"g404", url:"http://www.google.com/404"};
211 chrome.bookmarks.create(node, fail(error));
214 function createInManaged() {
215 const error = "Can't modify managed bookmarks.";
216 var node = {parentId:"4", title:"g404", url:"http://www.google.com/404"};
217 chrome.bookmarks.create(node, fail(error));
220 function createFolder() {
221 var node = {parentId:"1", title:"foo bar"}; // folder
222 chrome.test.listenOnce(chrome.bookmarks.onCreated, function(id, created) {
223 node.id = created.id;
226 chrome.test.assertTrue(compareNode(node, created));
228 chrome.bookmarks.create(node, pass(function(results) {
229 node.id = results.id; // since we couldn't know this going in
232 chrome.test.assertTrue(compareNode(node, results),
233 "created node != source");
234 expected[0].children[0].children.push(node);
238 function getSubTree() {
239 chrome.bookmarks.getSubTree(expected[0].children[0].id,
240 pass(function(results) {
241 chrome.test.assertTrue(compareTrees([expected[0].children[0]],
242 results), "getTree() result != expected");
246 function moveSetup() {
247 createNodes(bookmarksBar(), [node1, node2, node3], pass(function() {
248 verifyTreeIsExpected(pass());
253 // Move node1, node2, and node3 from their current location (the bookmark
254 // bar) to be under the "foo bar" folder (created in createFolder()).
255 // Then move that folder to be under the "other bookmarks" folder.
256 var folder = expected[0].children[0].children[1];
257 var old_node1 = expected[0].children[0].children[2];
258 chrome.test.listenOnce(chrome.bookmarks.onMoved, function(id, moveInfo) {
259 chrome.test.assertEq(node1.id, id);
260 chrome.test.assertEq(moveInfo.parentId, folder.id);
261 chrome.test.assertEq(moveInfo.index, 0);
262 chrome.test.assertEq(moveInfo.oldParentId, old_node1.parentId);
263 chrome.test.assertEq(moveInfo.oldIndex, old_node1.index);
265 chrome.bookmarks.move(node1.id, {parentId:folder.id},
266 pass(function(results) {
267 chrome.test.assertEq(results.parentId, folder.id);
268 node1.parentId = results.parentId;
271 chrome.bookmarks.move(node2.id, {parentId:folder.id},
272 pass(function(results) {
273 chrome.test.assertEq(results.parentId, folder.id);
274 node2.parentId = results.parentId;
277 // Insert node3 at index 1 rather than at the end. This should result in
278 // an order of node1, node3, node2.
279 chrome.bookmarks.move(node3.id, {parentId:folder.id, index:1},
280 pass(function(results) {
281 chrome.test.assertEq(results.parentId, folder.id);
282 chrome.test.assertEq(results.index, 1);
283 node3.parentId = results.parentId;
287 // update expected to match
288 expected[0].children[0].children.pop();
289 expected[0].children[0].children.pop();
290 expected[0].children[0].children.pop();
291 folder.children.push(node1);
292 folder.children.push(node3);
293 folder.children.push(node2);
295 verifyTreeIsExpected(pass());
298 // Move folder (and its children) to be a child of Other Bookmarks.
299 var other = expected[0].children[1];
300 chrome.bookmarks.move(folder.id, {parentId:other.id},
301 pass(function(results) {
302 chrome.test.assertEq(results.parentId, other.id);
303 folder.parentId = results.parentId;
304 folder.index = results.index;
306 var folder2 = expected[0].children[0].children.pop();
307 chrome.test.assertEq(folder2.id, folder.id);
308 expected[0].children[1].children.push(folder2);
309 verifyTreeIsExpected(pass());
313 function moveToManaged() {
314 var managed_node = expected[0].children[2];
315 chrome.test.assertEq("4", managed_node.id);
316 const error = "Can't modify managed bookmarks.";
317 chrome.bookmarks.move(node1.id, {parentId:managed_node.id}, fail(error));
318 verifyTreeIsExpected(pass());
321 function moveFromManaged() {
322 var managed_node = expected[0].children[2];
323 var moving_node = managed_node.children[0];
324 var other = expected[0].children[1];
325 const error = "Can't modify managed bookmarks.";
326 chrome.bookmarks.move(moving_node.id, {parentId:other.id}, fail(error));
327 verifyTreeIsExpected(pass());
331 chrome.bookmarks.search("baz bar", pass(function(results) {
332 // matches node1 & node3
333 chrome.test.assertEq(2, results.length);
335 chrome.bookmarks.search("www hello", pass(function(results) {
336 // matches node1 & node3
337 chrome.test.assertEq(2, results.length);
339 chrome.bookmarks.search("bar example",
340 pass(function(results) {
342 chrome.test.assertEq(1, results.length);
344 chrome.bookmarks.search("foo bar", pass(function(results) {
345 // matches node3 & folder "foo bar" from createFolder
346 chrome.test.assertEq(2, results.length);
348 chrome.bookmarks.search("quux", pass(function(results) {
349 // matches node2 & node1
350 chrome.test.assertEq(2, results.length);
352 chrome.bookmarks.search("Bookmark Bar", pass(function(results) {
353 // Does not match any node since permanent nodes are stripped from search
354 chrome.test.assertEq(0, results.length);
356 chrome.bookmarks.search("Managed", pass(function(results) {
357 // Matches the Managed Bookmark and the Managed Folder but not the
359 chrome.test.assertEq(2, results.length);
364 var title = "hello world";
365 chrome.test.listenOnce(chrome.bookmarks.onChanged, function(id, changes) {
366 chrome.test.assertEq(title, changes.title);
368 chrome.bookmarks.update(node1.id, {"title": title}, pass(function(results) {
369 chrome.test.assertEq(title, results.title);
372 var url = "http://example.com/hello";
373 chrome.bookmarks.update(node1.id, {"url": url}, pass(function(results) {
374 // Make sure that leaving out the title does not set the title to empty.
375 chrome.test.assertEq(title, results.title);
376 chrome.test.assertEq(url, results.url);
378 // Empty or invalid URLs should not change the URL.
380 chrome.bookmarks.update(node1.id, {"url": bad_url},
381 pass(function(results) {
382 chrome.bookmarks.get(node1.id, pass(function(results) {
383 chrome.test.assertEq(url, results[0].url);
384 chrome.test.log("URL UNCHANGED");
389 // Invalid URLs also generate an error.
390 bad_url = "I am not an URL";
391 chrome.bookmarks.update(node1.id, {"url": bad_url}, fail("Invalid URL.",
393 chrome.bookmarks.get(node1.id, pass(function(results) {
394 chrome.test.assertEq(url, results[0].url);
395 chrome.test.log("URL UNCHANGED");
402 function updateManaged() {
403 var managed_node = expected[0].children[2];
404 var updating_node = managed_node.children[0];
405 const error = "Can't modify managed bookmarks.";
406 chrome.bookmarks.update(updating_node.id, {"title": "New"}, fail(error));
410 var parentId = node1.parentId;
411 chrome.test.listenOnce(chrome.bookmarks.onRemoved,
412 function(id, removeInfo) {
413 chrome.test.assertEq(id, node1.id);
414 chrome.test.assertEq(removeInfo.parentId, parentId);
415 chrome.test.assertEq(removeInfo.index, node1.index);
417 chrome.bookmarks.remove(node1.id, pass(function() {
418 // Update expected to match.
419 // We removed node1, which means that the index of the other two nodes
421 expected[0].children[1].children[1].children.shift();
422 expected[0].children[1].children[1].children[0].index = 0;
423 expected[0].children[1].children[1].children[1].index = 1;
424 verifyTreeIsExpected(pass());
428 function removeManaged() {
429 var managed_node = expected[0].children[2];
430 var removing_node = managed_node.children[0];
431 const error = "Can't modify managed bookmarks.";
432 chrome.bookmarks.remove(removing_node.id, fail(error));
435 function searchRemoved() {
436 // Search for deleted node
437 chrome.bookmarks.search("baz bar", pass(function(results) {
438 // matches only node3 since node1 was removed
439 chrome.test.assertEq(1, results.length);
443 function removeTree() {
444 var parentId = node2.parentId;
445 var folder = expected[0].children[1].children[1];
446 chrome.test.listenOnce(chrome.bookmarks.onRemoved,
447 function(id, removeInfo) {
448 chrome.test.assertEq(id, folder.id);
449 chrome.test.assertEq(removeInfo.parentId, folder.parentId);
450 chrome.test.assertEq(removeInfo.index, folder.index);
452 chrome.bookmarks.removeTree(parentId, pass(function(){
453 // Update expected to match.
454 expected[0].children[1].children.pop();
455 verifyTreeIsExpected(pass());
459 function removeManagedTree() {
460 var managed_node = expected[0].children[2];
461 var managed_folder = managed_node.children[1];
462 const error = "Can't modify managed bookmarks.";
463 chrome.bookmarks.removeTree(managed_folder.id, fail(error));
466 function searchRemovedTree() {
467 // Search for deleted folder and enclosed node3
468 chrome.bookmarks.search("foo bar", pass(function(results) {
469 // Does not match anything since folder was removed with node3 in it
470 chrome.test.assertEq(0, results.length);
474 function getRecentSetup() {
476 ["1", "2"].forEach(function(id) {
477 chrome.bookmarks.getChildren(id, pass(function(children) {
478 children.forEach(function(child, i) {
479 chrome.bookmarks.removeTree(child.id, pass(function() {}));
480 // When we have removed the last child we can continue.
481 if (id == "2" && i == children.length - 1)
487 function afterRemove() {
488 // Once done add 3 nodes
489 chrome.bookmarks.getTree(pass(function(results) {
490 chrome.test.assertEq(0, results[0].children[0].children.length);
491 chrome.test.assertEq(0, results[0].children[1].children.length);
495 node1 = {parentId:"1", title:"bar baz",
496 url:"http://www.example.com/hello"};
497 node2 = {parentId:"1", title:"foo quux",
498 url:"http://www.example.com/bar"};
499 node3 = {parentId:"1", title:"Foo bar baz",
500 url:"http://www.google.com/hello/quux"};
501 createNodes(bookmarksBar(), [node1, node2, node3], pass(function() {
502 verifyTreeIsExpected(pass());
508 function getRecent() {
511 chrome.bookmarks.getRecent(0, function() {});
515 chrome.test.assertTrue(failed, "Calling with 0 should fail");
517 chrome.bookmarks.getRecent(10000, pass(function(results) {
518 // Should include the "Managed Bookmark" and "Supervised Bookmark".
519 chrome.test.assertEq(5, results.length,
520 "Should have gotten all recent bookmarks");
523 chrome.bookmarks.getRecent(2, pass(function(results) {
524 chrome.test.assertEq(2, results.length,
525 "Should only get the last 2 bookmarks");
527 chrome.test.assertTrue(compareNode(node3, results[0]));
528 chrome.test.assertTrue(compareNode(node2, results[1]));
532 function updateFolder() {
533 chrome.bookmarks.create({title: 'folder'}, function(folder) {
534 var newTitle = 'changedFolder';
535 chrome.test.listenOnce(chrome.bookmarks.onChanged, pass(
536 function(id, changes) {
537 chrome.test.assertEq(folder.id, id);
538 chrome.test.assertEq(newTitle, changes.title);
539 chrome.test.assertFalse('url' in changes);
542 chrome.bookmarks.update(folder.id, {title: newTitle}, pass(
544 chrome.test.assertEq(newTitle, result.title);
545 chrome.test.assertFalse('url' in result)