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
:"5", parentId
:"4", index
:0, title
:"Managed Bookmark",
19 url
:"http://www.chromium.org/", unmodifiable
:"managed"},
20 {id
:"6", parentId
:"4", index
:1, title
:"Managed Folder",
21 children
:[], unmodifiable
:"managed"}
29 function bookmarksBar() { return expected
[0].children
[0]; }
30 function otherBookmarks() { return expected
[0].children
[1]; }
32 // Some variables that are used across multiple tests.
33 var node1
= {parentId
:"1", title
:"bar baz",
34 url
:"http://www.example.com/hello"};
35 var node2
= {parentId
:"1", title
:"foo quux",
36 url
:"http://www.example.com/bar"};
37 var node3
= {parentId
:"1", title
:"Foo bar baz",
38 url
:"http://www.google.com/hello/quux"};
40 var pass
= chrome
.test
.callbackPass
;
41 var fail
= chrome
.test
.callbackFail
;
43 function compareNode(left
, right
) {
44 //chrome.test.log("compareNode()");
45 //chrome.test.log(JSON.stringify(left, null, 2));
46 //chrome.test.log(JSON.stringify(right, null, 2));
47 // TODO(erikkay): do some comparison of dateAdded
48 if (left
.id
!= right
.id
)
49 return "id mismatch: " + left
.id
+ " != " + right
.id
;
50 if (left
.title
!= right
.title
) {
51 // TODO(erikkay): This resource dependency still isn't working reliably.
53 // return "title mismatch: " + left.title + " != " + right.title;
54 chrome
.test
.log("title mismatch: " + left
.title
+ " != " + right
.title
);
56 if (left
.url
!= right
.url
)
57 return "url mismatch: " + left
.url
+ " != " + right
.url
;
58 if (left
.index
!= right
.index
)
59 return "index mismatch: " + left
.index
+ " != " + right
.index
;
60 if (left
.unmodifiable
!= right
.unmodifiable
) {
61 return "unmodifiable mismatch: " + left
.unmodifiable
+
62 " != " + right
.unmodifiable
;
67 function compareTrees(left
, right
, verbose
) {
69 chrome
.test
.log(JSON
.stringify(left
) || "<null>");
70 chrome
.test
.log(JSON
.stringify(right
) || "<null>");
72 if (left
== null && right
== null) {
75 if (left
== null || right
== null)
76 return left
+ " != " + right
;
77 if (left
.length
!= right
.length
)
78 return "count mismatch: " + left
.length
+ " != " + right
.length
;
79 for (var i
= 0; i
< left
.length
; i
++) {
80 var result
= compareNode(left
[i
], right
[i
]);
81 if (result
!== true) {
82 chrome
.test
.log(result
);
83 chrome
.test
.log(JSON
.stringify(left
, null, 2));
84 chrome
.test
.log(JSON
.stringify(right
, null, 2));
87 result
= compareTrees(left
[i
].children
, right
[i
].children
);
94 // |expectedParent| is a child within the global |expected| that each node in
95 // the array|nodes| should be added to. |callback| will be called when all of
96 // the nodes have been successfully created.
97 function createNodes(expectedParent
, nodes
, callback
) {
98 function createNext() {
100 var node
= nodes
.shift();
101 chrome
.bookmarks
.create(node
, function(results
) {
102 node
.id
= results
.id
;
103 node
.index
= results
.index
;
104 expectedParent
.children
.push(node
);
106 // Create each node a minimum of 1ms apart. This ensures that each
107 // node will have a unique timestamp.
108 setTimeout(createNext
, 1);
117 // Calls getTree and verifies that the results match the global |expected|
118 // state. Assigns over expected in the end since getTree has more data
119 // (e.g. createdDate) which tests may want to depend on.
120 function verifyTreeIsExpected(callback
) {
121 chrome
.bookmarks
.getTree(pass(function(results
) {
122 chrome
.test
.assertTrue(compareTrees(expected
, results
),
123 "getTree() result != expected");
130 chrome
.test
.runTests([
132 verifyTreeIsExpected(pass());
136 chrome
.bookmarks
.get("1", pass(function(results
) {
137 chrome
.test
.assertTrue(compareNode(results
[0], expected
[0].children
[0]));
139 chrome
.bookmarks
.get("5", pass(function(results
) {
140 chrome
.test
.assertTrue(compareNode(
141 results
[0], expected
[0].children
[2].children
[0]));
143 chrome
.bookmarks
.get("42", fail("Can't find bookmark for id."));
146 function getArray() {
147 chrome
.bookmarks
.get(["1", "2"], pass(function(results
) {
148 chrome
.test
.assertTrue(compareNode(results
[0], expected
[0].children
[0]),
149 "get() result != expected");
150 chrome
.test
.assertTrue(compareNode(results
[1], expected
[0].children
[1]),
151 "get() result != expected");
155 function getChildren() {
156 chrome
.bookmarks
.getChildren("0", pass(function(results
) {
157 chrome
.test
.assertTrue(compareNode(results
[0], expected
[0].children
[0]),
158 "getChildren() result != expected");
159 chrome
.test
.assertTrue(compareNode(results
[1], expected
[0].children
[1]),
160 "getChildren() result != expected");
165 var node
= {parentId
:"1", title
:"google", url
:"http://www.google.com/"};
166 chrome
.test
.listenOnce(chrome
.bookmarks
.onCreated
, function(id
, created
) {
167 node
.id
= created
.id
;
169 chrome
.test
.assertEq(id
, node
.id
);
170 chrome
.test
.assertTrue(compareNode(node
, created
));
172 chrome
.bookmarks
.create(node
, pass(function(results
) {
173 node
.id
= results
.id
; // since we couldn't know this going in
175 chrome
.test
.assertTrue(compareNode(node
, results
),
176 "created node != source");
177 expected
[0].children
[0].children
.push(node
);
181 function createNoParentId() {
182 var node
= {title
:"google", url
:"http://www.google.com/"};
183 chrome
.test
.listenOnce(chrome
.bookmarks
.onCreated
, function(id
, created
) {
184 node
.id
= created
.id
;
186 chrome
.test
.assertEq(id
, node
.id
);
187 // Make sure the parentId defaults to the Other Bookmarks folder.
188 chrome
.test
.assertEq(expected
[0].children
[1].id
, created
.parentId
);
189 chrome
.test
.assertTrue(compareNode(node
, created
));
190 expected
[0].children
[1].children
.push(node
);
192 chrome
.bookmarks
.create(node
, pass(function(results
) {
193 node
.id
= results
.id
; // since we couldn't know this going in
195 chrome
.test
.assertTrue(compareNode(node
, results
),
196 "created node != source");
200 function createInRoot() {
201 const error
= "Can't modify the root bookmark folders.";
202 var node
= {parentId
:"0", title
:"g404", url
:"http://www.google.com/404"};
203 chrome
.bookmarks
.create(node
, fail(error
));
206 function createInManaged() {
207 const error
= "Can't modify managed bookmarks.";
208 var node
= {parentId
:"4", title
:"g404", url
:"http://www.google.com/404"};
209 chrome
.bookmarks
.create(node
, fail(error
));
212 function createFolder() {
213 var node
= {parentId
:"1", title
:"foo bar"}; // folder
214 chrome
.test
.listenOnce(chrome
.bookmarks
.onCreated
, function(id
, created
) {
215 node
.id
= created
.id
;
218 chrome
.test
.assertTrue(compareNode(node
, created
));
220 chrome
.bookmarks
.create(node
, pass(function(results
) {
221 node
.id
= results
.id
; // since we couldn't know this going in
224 chrome
.test
.assertTrue(compareNode(node
, results
),
225 "created node != source");
226 expected
[0].children
[0].children
.push(node
);
230 function getSubTree() {
231 chrome
.bookmarks
.getSubTree(expected
[0].children
[0].id
,
232 pass(function(results
) {
233 chrome
.test
.assertTrue(compareTrees([expected
[0].children
[0]],
234 results
), "getTree() result != expected");
238 function moveSetup() {
239 createNodes(bookmarksBar(), [node1
, node2
, node3
], pass(function() {
240 verifyTreeIsExpected(pass());
245 // Move node1, node2, and node3 from their current location (the bookmark
246 // bar) to be under the "foo bar" folder (created in createFolder()).
247 // Then move that folder to be under the "other bookmarks" folder.
248 var folder
= expected
[0].children
[0].children
[1];
249 var old_node1
= expected
[0].children
[0].children
[2];
250 chrome
.test
.listenOnce(chrome
.bookmarks
.onMoved
, function(id
, moveInfo
) {
251 chrome
.test
.assertEq(node1
.id
, id
);
252 chrome
.test
.assertEq(moveInfo
.parentId
, folder
.id
);
253 chrome
.test
.assertEq(moveInfo
.index
, 0);
254 chrome
.test
.assertEq(moveInfo
.oldParentId
, old_node1
.parentId
);
255 chrome
.test
.assertEq(moveInfo
.oldIndex
, old_node1
.index
);
257 chrome
.bookmarks
.move(node1
.id
, {parentId
:folder
.id
},
258 pass(function(results
) {
259 chrome
.test
.assertEq(results
.parentId
, folder
.id
);
260 node1
.parentId
= results
.parentId
;
263 chrome
.bookmarks
.move(node2
.id
, {parentId
:folder
.id
},
264 pass(function(results
) {
265 chrome
.test
.assertEq(results
.parentId
, folder
.id
);
266 node2
.parentId
= results
.parentId
;
269 // Insert node3 at index 1 rather than at the end. This should result in
270 // an order of node1, node3, node2.
271 chrome
.bookmarks
.move(node3
.id
, {parentId
:folder
.id
, index
:1},
272 pass(function(results
) {
273 chrome
.test
.assertEq(results
.parentId
, folder
.id
);
274 chrome
.test
.assertEq(results
.index
, 1);
275 node3
.parentId
= results
.parentId
;
279 // update expected to match
280 expected
[0].children
[0].children
.pop();
281 expected
[0].children
[0].children
.pop();
282 expected
[0].children
[0].children
.pop();
283 folder
.children
.push(node1
);
284 folder
.children
.push(node3
);
285 folder
.children
.push(node2
);
287 verifyTreeIsExpected(pass());
290 // Move folder (and its children) to be a child of Other Bookmarks.
291 var other
= expected
[0].children
[1];
292 chrome
.bookmarks
.move(folder
.id
, {parentId
:other
.id
},
293 pass(function(results
) {
294 chrome
.test
.assertEq(results
.parentId
, other
.id
);
295 folder
.parentId
= results
.parentId
;
296 folder
.index
= results
.index
;
298 var folder2
= expected
[0].children
[0].children
.pop();
299 chrome
.test
.assertEq(folder2
.id
, folder
.id
);
300 expected
[0].children
[1].children
.push(folder2
);
301 verifyTreeIsExpected(pass());
305 function moveToManaged() {
306 var managed_node
= expected
[0].children
[2];
307 chrome
.test
.assertEq("4", managed_node
.id
);
308 const error
= "Can't modify managed bookmarks.";
309 chrome
.bookmarks
.move(node1
.id
, {parentId
:managed_node
.id
}, fail(error
));
310 verifyTreeIsExpected(pass());
313 function moveFromManaged() {
314 var managed_node
= expected
[0].children
[2];
315 var moving_node
= managed_node
.children
[0];
316 var other
= expected
[0].children
[1];
317 const error
= "Can't modify managed bookmarks.";
318 chrome
.bookmarks
.move(moving_node
.id
, {parentId
:other
.id
}, fail(error
));
319 verifyTreeIsExpected(pass());
323 chrome
.bookmarks
.search("baz bar", pass(function(results
) {
324 // matches node1 & node3
325 chrome
.test
.assertEq(2, results
.length
);
327 chrome
.bookmarks
.search("www hello", pass(function(results
) {
328 // matches node1 & node3
329 chrome
.test
.assertEq(2, results
.length
);
331 chrome
.bookmarks
.search("bar example",
332 pass(function(results
) {
334 chrome
.test
.assertEq(1, results
.length
);
336 chrome
.bookmarks
.search("foo bar", pass(function(results
) {
337 // matches node3 & folder "foo bar" from createFolder
338 chrome
.test
.assertEq(2, results
.length
);
340 chrome
.bookmarks
.search("quux", pass(function(results
) {
341 // matches node2 & node1
342 chrome
.test
.assertEq(2, results
.length
);
344 chrome
.bookmarks
.search("Bookmark Bar", pass(function(results
) {
345 // Does not match any node since permanent nodes are stripped from search
346 chrome
.test
.assertEq(0, results
.length
);
348 chrome
.bookmarks
.search("Managed", pass(function(results
) {
349 // Matches the Managed Bookmark and the Managed Folder but not the
351 chrome
.test
.assertEq(2, results
.length
);
356 var title
= "hello world";
357 chrome
.test
.listenOnce(chrome
.bookmarks
.onChanged
, function(id
, changes
) {
358 chrome
.test
.assertEq(title
, changes
.title
);
360 chrome
.bookmarks
.update(node1
.id
, {"title": title
}, pass(function(results
) {
361 chrome
.test
.assertEq(title
, results
.title
);
364 var url
= "http://example.com/hello";
365 chrome
.bookmarks
.update(node1
.id
, {"url": url
}, pass(function(results
) {
366 // Make sure that leaving out the title does not set the title to empty.
367 chrome
.test
.assertEq(title
, results
.title
);
368 chrome
.test
.assertEq(url
, results
.url
);
370 // Empty or invalid URLs should not change the URL.
372 chrome
.bookmarks
.update(node1
.id
, {"url": bad_url
},
373 pass(function(results
) {
374 chrome
.bookmarks
.get(node1
.id
, pass(function(results
) {
375 chrome
.test
.assertEq(url
, results
[0].url
);
376 chrome
.test
.log("URL UNCHANGED");
381 // Invalid URLs also generate an error.
382 bad_url
= "I am not an URL";
383 chrome
.bookmarks
.update(node1
.id
, {"url": bad_url
}, fail("Invalid URL.",
385 chrome
.bookmarks
.get(node1
.id
, pass(function(results
) {
386 chrome
.test
.assertEq(url
, results
[0].url
);
387 chrome
.test
.log("URL UNCHANGED");
394 function updateManaged() {
395 var managed_node
= expected
[0].children
[2];
396 var updating_node
= managed_node
.children
[0];
397 const error
= "Can't modify managed bookmarks.";
398 chrome
.bookmarks
.update(updating_node
.id
, {"title": "New"}, fail(error
));
402 var parentId
= node1
.parentId
;
403 chrome
.test
.listenOnce(chrome
.bookmarks
.onRemoved
,
404 function(id
, removeInfo
) {
405 chrome
.test
.assertEq(id
, node1
.id
);
406 chrome
.test
.assertEq(removeInfo
.parentId
, parentId
);
407 chrome
.test
.assertEq(removeInfo
.index
, node1
.index
);
409 chrome
.bookmarks
.remove(node1
.id
, pass(function() {
410 // Update expected to match.
411 // We removed node1, which means that the index of the other two nodes
413 expected
[0].children
[1].children
[1].children
.shift();
414 expected
[0].children
[1].children
[1].children
[0].index
= 0;
415 expected
[0].children
[1].children
[1].children
[1].index
= 1;
416 verifyTreeIsExpected(pass());
420 function removeManaged() {
421 var managed_node
= expected
[0].children
[2];
422 var removing_node
= managed_node
.children
[0];
423 const error
= "Can't modify managed bookmarks.";
424 chrome
.bookmarks
.remove(removing_node
.id
, fail(error
));
427 function searchRemoved() {
428 // Search for deleted node
429 chrome
.bookmarks
.search("baz bar", pass(function(results
) {
430 // matches only node3 since node1 was removed
431 chrome
.test
.assertEq(1, results
.length
);
435 function removeTree() {
436 var parentId
= node2
.parentId
;
437 var folder
= expected
[0].children
[1].children
[1];
438 chrome
.test
.listenOnce(chrome
.bookmarks
.onRemoved
,
439 function(id
, removeInfo
) {
440 chrome
.test
.assertEq(id
, folder
.id
);
441 chrome
.test
.assertEq(removeInfo
.parentId
, folder
.parentId
);
442 chrome
.test
.assertEq(removeInfo
.index
, folder
.index
);
444 chrome
.bookmarks
.removeTree(parentId
, pass(function(){
445 // Update expected to match.
446 expected
[0].children
[1].children
.pop();
447 verifyTreeIsExpected(pass());
451 function removeManagedTree() {
452 var managed_node
= expected
[0].children
[2];
453 var managed_folder
= managed_node
.children
[1];
454 const error
= "Can't modify managed bookmarks.";
455 chrome
.bookmarks
.removeTree(managed_folder
.id
, fail(error
));
458 function searchRemovedTree() {
459 // Search for deleted folder and enclosed node3
460 chrome
.bookmarks
.search("foo bar", pass(function(results
) {
461 // Does not match anything since folder was removed with node3 in it
462 chrome
.test
.assertEq(0, results
.length
);
466 function getRecentSetup() {
468 ["1", "2"].forEach(function(id
) {
469 chrome
.bookmarks
.getChildren(id
, pass(function(children
) {
470 children
.forEach(function(child
, i
) {
471 chrome
.bookmarks
.removeTree(child
.id
, pass(function() {}));
472 // When we have removed the last child we can continue.
473 if (id
== "2" && i
== children
.length
- 1)
479 function afterRemove() {
480 // Once done add 3 nodes
481 chrome
.bookmarks
.getTree(pass(function(results
) {
482 chrome
.test
.assertEq(0, results
[0].children
[0].children
.length
);
483 chrome
.test
.assertEq(0, results
[0].children
[1].children
.length
);
487 node1
= {parentId
:"1", title
:"bar baz",
488 url
:"http://www.example.com/hello"};
489 node2
= {parentId
:"1", title
:"foo quux",
490 url
:"http://www.example.com/bar"};
491 node3
= {parentId
:"1", title
:"Foo bar baz",
492 url
:"http://www.google.com/hello/quux"};
493 createNodes(bookmarksBar(), [node1
, node2
, node3
], pass(function() {
494 verifyTreeIsExpected(pass());
500 function getRecent() {
503 chrome
.bookmarks
.getRecent(0, function() {});
507 chrome
.test
.assertTrue(failed
, "Calling with 0 should fail");
509 chrome
.bookmarks
.getRecent(10000, pass(function(results
) {
510 // Should include the "Managed Bookmark".
511 chrome
.test
.assertEq(4, results
.length
,
512 "Should have gotten all recent bookmarks");
515 chrome
.bookmarks
.getRecent(2, pass(function(results
) {
516 chrome
.test
.assertEq(2, results
.length
,
517 "Should only get the last 2 bookmarks");
519 chrome
.test
.assertTrue(compareNode(node3
, results
[0]));
520 chrome
.test
.assertTrue(compareNode(node2
, results
[1]));
524 function updateFolder() {
525 chrome
.bookmarks
.create({title
: 'folder'}, function(folder
) {
526 var newTitle
= 'changedFolder';
527 chrome
.test
.listenOnce(chrome
.bookmarks
.onChanged
, pass(
528 function(id
, changes
) {
529 chrome
.test
.assertEq(folder
.id
, id
);
530 chrome
.test
.assertEq(newTitle
, changes
.title
);
531 chrome
.test
.assertFalse('url' in changes
);
534 chrome
.bookmarks
.update(folder
.id
, {title
: newTitle
}, pass(
536 chrome
.test
.assertEq(newTitle
, result
.title
);
537 chrome
.test
.assertFalse('url' in result
)