|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 const bmsvc = PlacesUtils.bookmarks; |
|
8 const tagssvc = PlacesUtils.tagging; |
|
9 const annosvc = PlacesUtils.annotations; |
|
10 const PT = PlacesTransactions; |
|
11 |
|
12 // Create and add bookmarks observer. |
|
13 let observer = { |
|
14 __proto__: NavBookmarkObserver.prototype, |
|
15 |
|
16 tagRelatedGUIDs: new Set(), |
|
17 |
|
18 reset: function () { |
|
19 this.itemsAdded = new Map(); |
|
20 this.itemsRemoved = new Map(); |
|
21 this.itemsChanged = new Map(); |
|
22 this.itemsMoved = new Map(); |
|
23 this.beginUpdateBatch = false; |
|
24 this.endUpdateBatch = false; |
|
25 }, |
|
26 |
|
27 onBeginUpdateBatch: function () { |
|
28 this.beginUpdateBatch = true; |
|
29 }, |
|
30 |
|
31 onEndUpdateBatch: function () { |
|
32 this.endUpdateBatch = true; |
|
33 }, |
|
34 |
|
35 onItemAdded: |
|
36 function (aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, |
|
37 aGUID, aParentGUID) { |
|
38 // Ignore tag items. |
|
39 if (aParentId == PlacesUtils.tagsFolderId || |
|
40 (aParentId != PlacesUtils.placesRootId && |
|
41 bmsvc.getFolderIdForItem(aParentId) == PlacesUtils.tagsFolderId)) { |
|
42 this.tagRelatedGUIDs.add(aGUID); |
|
43 return; |
|
44 } |
|
45 |
|
46 this.itemsAdded.set(aGUID, { itemId: aItemId |
|
47 , parentGUID: aParentGUID |
|
48 , index: aIndex |
|
49 , itemType: aItemType |
|
50 , title: aTitle |
|
51 , uri: aURI }); |
|
52 }, |
|
53 |
|
54 onItemRemoved: |
|
55 function (aItemId, aParentId, aIndex, aItemType, aURI, aGUID, aParentGUID) { |
|
56 if (this.tagRelatedGUIDs.has(aGUID)) |
|
57 return; |
|
58 |
|
59 this.itemsRemoved.set(aGUID, { parentGUID: aParentGUID |
|
60 , index: aIndex |
|
61 , itemType: aItemType }); |
|
62 }, |
|
63 |
|
64 onItemChanged: |
|
65 function (aItemId, aProperty, aIsAnnoProperty, aNewValue, aLastModified, |
|
66 aItemType, aParentId, aGUID, aParentGUID) { |
|
67 if (this.tagRelatedGUIDs.has(aGUID)) |
|
68 return; |
|
69 |
|
70 let changesForGUID = this.itemsChanged.get(aGUID); |
|
71 if (changesForGUID === undefined) { |
|
72 changesForGUID = new Map(); |
|
73 this.itemsChanged.set(aGUID, changesForGUID); |
|
74 } |
|
75 |
|
76 let newValue = aNewValue; |
|
77 if (aIsAnnoProperty) { |
|
78 if (annosvc.itemHasAnnotation(aItemId, aProperty)) |
|
79 newValue = annosvc.getItemAnnotation(aItemId, aProperty); |
|
80 else |
|
81 newValue = null; |
|
82 } |
|
83 let change = { isAnnoProperty: aIsAnnoProperty |
|
84 , newValue: newValue |
|
85 , lastModified: aLastModified |
|
86 , itemType: aItemType }; |
|
87 changesForGUID.set(aProperty, change); |
|
88 }, |
|
89 |
|
90 onItemVisited: () => {}, |
|
91 |
|
92 onItemMoved: |
|
93 function (aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex, aItemType, |
|
94 aGUID, aOldParentGUID, aNewParentGUID) { |
|
95 this.itemsMoved.set(aGUID, { oldParentGUID: aOldParentGUID |
|
96 , oldIndex: aOldIndex |
|
97 , newParentGUID: aNewParentGUID |
|
98 , newIndex: aNewIndex |
|
99 , itemType: aItemType }); |
|
100 } |
|
101 }; |
|
102 observer.reset(); |
|
103 |
|
104 // index at which items should begin |
|
105 let bmStartIndex = 0; |
|
106 |
|
107 // get bookmarks root id |
|
108 let root = PlacesUtils.bookmarksMenuFolderId; |
|
109 |
|
110 function run_test() { |
|
111 bmsvc.addObserver(observer, false); |
|
112 do_register_cleanup(function () { |
|
113 bmsvc.removeObserver(observer); |
|
114 }); |
|
115 |
|
116 run_next_test(); |
|
117 } |
|
118 |
|
119 function sanityCheckTransactionHistory() { |
|
120 do_check_true(PT.undoPosition <= PT.length); |
|
121 |
|
122 let check_entry_throws = f => { |
|
123 try { |
|
124 f(); |
|
125 do_throw("PT.entry should throw for invalid input"); |
|
126 } catch(ex) {} |
|
127 }; |
|
128 check_entry_throws( () => PT.entry(-1) ); |
|
129 check_entry_throws( () => PT.entry({}) ); |
|
130 check_entry_throws( () => PT.entry(PT.length) ); |
|
131 |
|
132 if (PT.undoPosition < PT.length) |
|
133 do_check_eq(PT.topUndoEntry, PT.entry(PT.undoPosition)); |
|
134 else |
|
135 do_check_null(PT.topUndoEntry); |
|
136 if (PT.undoPosition > 0) |
|
137 do_check_eq(PT.topRedoEntry, PT.entry(PT.undoPosition - 1)); |
|
138 else |
|
139 do_check_null(PT.topRedoEntry); |
|
140 } |
|
141 |
|
142 function getTransactionsHistoryState() { |
|
143 let history = []; |
|
144 for (let i = 0; i < PT.length; i++) { |
|
145 history.push(PT.entry(i)); |
|
146 } |
|
147 return [history, PT.undoPosition]; |
|
148 } |
|
149 |
|
150 function ensureUndoState(aExpectedEntries = [], aExpectedUndoPosition = 0) { |
|
151 // ensureUndoState is called in various places during this test, so it's |
|
152 // a good places to sanity-check the transaction-history APIs in all |
|
153 // cases. |
|
154 sanityCheckTransactionHistory(); |
|
155 |
|
156 let [actualEntries, actualUndoPosition] = getTransactionsHistoryState(); |
|
157 do_check_eq(actualEntries.length, aExpectedEntries.length); |
|
158 do_check_eq(actualUndoPosition, aExpectedUndoPosition); |
|
159 |
|
160 function checkEqualEntries(aExpectedEntry, aActualEntry) { |
|
161 do_check_eq(aExpectedEntry.length, aActualEntry.length); |
|
162 aExpectedEntry.forEach( (t, i) => do_check_eq(t, aActualEntry[i]) ); |
|
163 } |
|
164 aExpectedEntries.forEach( (e, i) => checkEqualEntries(e, actualEntries[i]) ); |
|
165 } |
|
166 |
|
167 function ensureItemsAdded(...items) { |
|
168 do_check_eq(observer.itemsAdded.size, items.length); |
|
169 for (let item of items) { |
|
170 do_check_true(observer.itemsAdded.has(item.GUID)); |
|
171 let info = observer.itemsAdded.get(item.GUID); |
|
172 do_check_eq(info.parentGUID, item.parentGUID); |
|
173 if ("title" in item) |
|
174 do_check_eq(info.title, item.title); |
|
175 if ("index" in item) |
|
176 do_check_eq(info.index, item.index); |
|
177 if ("itemType" in item) |
|
178 do_check_eq(info.itemType, item.itemType); |
|
179 } |
|
180 } |
|
181 |
|
182 function ensureItemsRemoved(...items) { |
|
183 do_check_eq(observer.itemsRemoved.size, items.length); |
|
184 for (let item of items) { |
|
185 do_check_true(observer.itemsRemoved.has(item.GUID)); |
|
186 let info = observer.itemsRemoved.get(item.GUID); |
|
187 do_check_eq(info.parentGUID, item.parentGUID); |
|
188 if ("index" in item) |
|
189 do_check_eq(info.index, item.index); |
|
190 } |
|
191 } |
|
192 |
|
193 function ensureItemsChanged(...items) { |
|
194 for (let item of items) { |
|
195 do_check_true(observer.itemsChanged.has(item.GUID)); |
|
196 let changes = observer.itemsChanged.get(item.GUID); |
|
197 do_check_true(changes.has(item.property)); |
|
198 let info = changes.get(item.property); |
|
199 do_check_eq(info.isAnnoProperty, Boolean(item.isAnnoProperty)); |
|
200 do_check_eq(info.newValue, item.newValue); |
|
201 if ("uri" in item) |
|
202 do_check_true(item.uri.equals(info.uri)); |
|
203 } |
|
204 } |
|
205 |
|
206 function ensureAnnotationsSet(aGUID, aAnnos) { |
|
207 do_check_true(observer.itemsChanged.has(aGUID)); |
|
208 let changes = observer.itemsChanged.get(aGUID); |
|
209 for (let anno of aAnnos) { |
|
210 do_check_true(changes.has(anno.name)); |
|
211 let changeInfo = changes.get(anno.name); |
|
212 do_check_true(changeInfo.isAnnoProperty); |
|
213 do_check_eq(changeInfo.newValue, anno.value); |
|
214 } |
|
215 } |
|
216 |
|
217 function ensureItemsMoved(...items) { |
|
218 do_check_true(observer.itemsMoved.size, items.length); |
|
219 for (let item of items) { |
|
220 do_check_true(observer.itemsMoved.has(item.GUID)); |
|
221 let info = observer.itemsMoved.get(item.GUID); |
|
222 do_check_eq(info.oldParentGUID, item.oldParentGUID); |
|
223 do_check_eq(info.oldIndex, item.oldIndex); |
|
224 do_check_eq(info.newParentGUID, item.newParentGUID); |
|
225 do_check_eq(info.newIndex, item.newIndex); |
|
226 } |
|
227 } |
|
228 |
|
229 function ensureTimestampsUpdated(aGUID, aCheckDateAdded = false) { |
|
230 do_check_true(observer.itemsChanged.has(aGUID)); |
|
231 let changes = observer.itemsChanged.get(aGUID); |
|
232 if (aCheckDateAdded) |
|
233 do_check_true(changes.has("dateAdded")) |
|
234 do_check_true(changes.has("lastModified")); |
|
235 } |
|
236 |
|
237 function ensureTagsForURI(aURI, aTags) { |
|
238 let tagsSet = tagssvc.getTagsForURI(aURI); |
|
239 do_check_eq(tagsSet.length, aTags.length); |
|
240 do_check_true(aTags.every( t => tagsSet.indexOf(t) != -1 )); |
|
241 } |
|
242 |
|
243 function* createTestFolderInfo(aTitle = "Test Folder") { |
|
244 return { parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
245 , title: "Test Folder" }; |
|
246 } |
|
247 |
|
248 add_task(function* test_invalid_transact_calls() { |
|
249 try { |
|
250 PT.transact({ execute: () => {}, undo: () => {}, redo: () => {}}); |
|
251 do_throw("transact shouldn't accept 'external' transactions"); |
|
252 PT.transact(null); |
|
253 do_throw("transact should throw for invalid arguments"); |
|
254 } |
|
255 catch(ex) { } |
|
256 }); |
|
257 |
|
258 add_task(function* test_recycled_transactions() { |
|
259 function ensureTransactThrowsFor(aTransaction) { |
|
260 let [txns, undoPosition] = getTransactionsHistoryState(); |
|
261 try { |
|
262 yield PT.transact(aTransaction); |
|
263 do_throw("Shouldn't be able to use the same transaction twice"); |
|
264 } |
|
265 catch(ex) { } |
|
266 ensureUndoState(txns, undoPosition); |
|
267 } |
|
268 |
|
269 let txn_a = PT.NewFolder(yield createTestFolderInfo()); |
|
270 ensureTransactThrowsFor(txn_a); |
|
271 yield PT.transact(txn_a); |
|
272 ensureUndoState([[txn_a]], 0); |
|
273 |
|
274 yield PT.undo(); |
|
275 ensureUndoState([[txn_a]], 1); |
|
276 ensureTransactThrowsFor(txn_a); |
|
277 |
|
278 yield PT.clearTransactionsHistory(); |
|
279 ensureUndoState(); |
|
280 ensureTransactThrowsFor(txn_a); |
|
281 |
|
282 let txn_b = PT.NewFolder(yield createTestFolderInfo()); |
|
283 yield PT.transact(function* () { |
|
284 try { |
|
285 yield txn_a; |
|
286 do_throw("Shouldn't be able to use the same transaction twice"); |
|
287 } |
|
288 catch(ex) { } |
|
289 ensureUndoState(); |
|
290 yield txn_b; |
|
291 }); |
|
292 ensureUndoState([[txn_b]], 0); |
|
293 |
|
294 yield PT.undo(); |
|
295 ensureUndoState([[txn_b]], 1); |
|
296 ensureTransactThrowsFor(txn_a); |
|
297 ensureTransactThrowsFor(txn_b); |
|
298 |
|
299 yield PT.clearTransactionsHistory(); |
|
300 ensureUndoState(); |
|
301 observer.reset(); |
|
302 }); |
|
303 |
|
304 add_task(function* test_nested_batches() { |
|
305 let txn_a = PT.NewFolder(yield createTestFolderInfo()), |
|
306 txn_b = PT.NewFolder(yield createTestFolderInfo()); |
|
307 yield PT.transact(function* () { |
|
308 yield txn_a; |
|
309 yield (function*() { |
|
310 yield txn_b; |
|
311 }()); |
|
312 }); |
|
313 ensureUndoState([[txn_b, txn_a]], 0); |
|
314 |
|
315 yield PT.undo(); |
|
316 ensureUndoState([[txn_b, txn_a]], 1); |
|
317 |
|
318 yield PT.clearTransactionsHistory(); |
|
319 ensureUndoState(); |
|
320 observer.reset(); |
|
321 }); |
|
322 |
|
323 add_task(function* test_new_folder_with_annotation() { |
|
324 const ANNO = { name: "TestAnno", value: "TestValue" }; |
|
325 let folder_info = yield createTestFolderInfo(); |
|
326 folder_info.index = bmStartIndex; |
|
327 folder_info.annotations = [ANNO]; |
|
328 ensureUndoState(); |
|
329 let txn = PT.NewFolder(folder_info); |
|
330 folder_info.GUID = yield PT.transact(txn); |
|
331 let ensureDo = function* (aRedo = false) { |
|
332 ensureUndoState([[txn]], 0); |
|
333 yield ensureItemsAdded(folder_info); |
|
334 ensureAnnotationsSet(folder_info.GUID, [ANNO]); |
|
335 if (aRedo) |
|
336 ensureTimestampsUpdated(folder_info.GUID, true); |
|
337 observer.reset(); |
|
338 }; |
|
339 |
|
340 let ensureUndo = () => { |
|
341 ensureUndoState([[txn]], 1); |
|
342 ensureItemsRemoved({ GUID: folder_info.GUID |
|
343 , parentGUID: folder_info.parentGUID |
|
344 , index: bmStartIndex }); |
|
345 observer.reset(); |
|
346 }; |
|
347 |
|
348 yield ensureDo(); |
|
349 yield PT.undo(); |
|
350 yield ensureUndo(); |
|
351 yield PT.redo(); |
|
352 yield ensureDo(true); |
|
353 yield PT.undo(); |
|
354 ensureUndo(); |
|
355 yield PT.clearTransactionsHistory(); |
|
356 ensureUndoState(); |
|
357 }); |
|
358 |
|
359 add_task(function* test_new_bookmark() { |
|
360 let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
361 , uri: NetUtil.newURI("http://test_create_item.com") |
|
362 , index: bmStartIndex |
|
363 , title: "Test creating an item" }; |
|
364 |
|
365 ensureUndoState(); |
|
366 let txn = PT.NewBookmark(bm_info); |
|
367 bm_info.GUID = yield PT.transact(txn); |
|
368 |
|
369 let ensureDo = function* (aRedo = false) { |
|
370 ensureUndoState([[txn]], 0); |
|
371 yield ensureItemsAdded(bm_info); |
|
372 if (aRedo) |
|
373 ensureTimestampsUpdated(bm_info.GUID, true); |
|
374 observer.reset(); |
|
375 }; |
|
376 let ensureUndo = () => { |
|
377 ensureUndoState([[txn]], 1); |
|
378 ensureItemsRemoved({ GUID: bm_info.GUID |
|
379 , parentGUID: bm_info.parentGUID |
|
380 , index: bmStartIndex }); |
|
381 observer.reset(); |
|
382 }; |
|
383 |
|
384 yield ensureDo(); |
|
385 yield PT.undo(); |
|
386 ensureUndo(); |
|
387 yield PT.redo(true); |
|
388 yield ensureDo(); |
|
389 yield PT.undo(); |
|
390 ensureUndo(); |
|
391 |
|
392 yield PT.clearTransactionsHistory(); |
|
393 ensureUndoState(); |
|
394 }); |
|
395 |
|
396 add_task(function* test_merge_create_folder_and_item() { |
|
397 let folder_info = yield createTestFolderInfo(); |
|
398 let bm_info = { uri: NetUtil.newURI("http://test_create_item_to_folder.com") |
|
399 , title: "Test Bookmark" |
|
400 , index: bmStartIndex }; |
|
401 |
|
402 let { folderTxn, bkmTxn } = yield PT.transact( function* () { |
|
403 let folderTxn = PT.NewFolder(folder_info); |
|
404 folder_info.GUID = bm_info.parentGUID = yield folderTxn; |
|
405 let bkmTxn = PT.NewBookmark(bm_info); |
|
406 bm_info.GUID = yield bkmTxn;; |
|
407 return { folderTxn: folderTxn, bkmTxn: bkmTxn}; |
|
408 }); |
|
409 |
|
410 let ensureDo = function* () { |
|
411 ensureUndoState([[bkmTxn, folderTxn]], 0); |
|
412 yield ensureItemsAdded(folder_info, bm_info); |
|
413 observer.reset(); |
|
414 }; |
|
415 |
|
416 let ensureUndo = () => { |
|
417 ensureUndoState([[bkmTxn, folderTxn]], 1); |
|
418 ensureItemsRemoved(folder_info, bm_info); |
|
419 observer.reset(); |
|
420 }; |
|
421 |
|
422 yield ensureDo(); |
|
423 yield PT.undo(); |
|
424 ensureUndo(); |
|
425 yield PT.redo(); |
|
426 yield ensureDo(); |
|
427 yield PT.undo(); |
|
428 ensureUndo(); |
|
429 |
|
430 yield PT.clearTransactionsHistory(); |
|
431 ensureUndoState(); |
|
432 }); |
|
433 |
|
434 add_task(function* test_move_items_to_folder() { |
|
435 let folder_a_info = yield createTestFolderInfo("Folder A"); |
|
436 let bkm_a_info = { uri: NetUtil.newURI("http://test_move_items.com") |
|
437 , title: "Bookmark A" }; |
|
438 let bkm_b_info = { uri: NetUtil.newURI("http://test_move_items.com") |
|
439 , title: "Bookmark B" }; |
|
440 |
|
441 // Test moving items within the same folder. |
|
442 let [folder_a_txn, bkm_a_txn, bkm_b_txn] = yield PT.transact(function* () { |
|
443 let folder_a_txn = PT.NewFolder(folder_a_info); |
|
444 |
|
445 folder_a_info.GUID = |
|
446 bkm_a_info.parentGUID = bkm_b_info.parentGUID = yield folder_a_txn; |
|
447 let bkm_a_txn = PT.NewBookmark(bkm_a_info); |
|
448 bkm_a_info.GUID = yield bkm_a_txn; |
|
449 let bkm_b_txn = PT.NewBookmark(bkm_b_info); |
|
450 bkm_b_info.GUID = yield bkm_b_txn; |
|
451 return [folder_a_txn, bkm_a_txn, bkm_b_txn]; |
|
452 }); |
|
453 |
|
454 ensureUndoState([[bkm_b_txn, bkm_a_txn, folder_a_txn]], 0); |
|
455 |
|
456 let moveTxn = PT.MoveItem({ GUID: bkm_a_info.GUID |
|
457 , newParentGUID: folder_a_info.GUID }); |
|
458 yield PT.transact(moveTxn); |
|
459 |
|
460 let ensureDo = () => { |
|
461 ensureUndoState([[moveTxn], [bkm_b_txn, bkm_a_txn, folder_a_txn]], 0); |
|
462 ensureItemsMoved({ GUID: bkm_a_info.GUID |
|
463 , oldParentGUID: folder_a_info.GUID |
|
464 , newParentGUID: folder_a_info.GUID |
|
465 , oldIndex: 0 |
|
466 , newIndex: 1 }); |
|
467 observer.reset(); |
|
468 }; |
|
469 let ensureUndo = () => { |
|
470 ensureUndoState([[moveTxn], [bkm_b_txn, bkm_a_txn, folder_a_txn]], 1); |
|
471 ensureItemsMoved({ GUID: bkm_a_info.GUID |
|
472 , oldParentGUID: folder_a_info.GUID |
|
473 , newParentGUID: folder_a_info.GUID |
|
474 , oldIndex: 1 |
|
475 , newIndex: 0 }); |
|
476 observer.reset(); |
|
477 }; |
|
478 |
|
479 ensureDo(); |
|
480 yield PT.undo(); |
|
481 ensureUndo(); |
|
482 yield PT.redo(); |
|
483 ensureDo(); |
|
484 yield PT.undo(); |
|
485 ensureUndo(); |
|
486 |
|
487 yield PT.clearTransactionsHistory(false, true); |
|
488 ensureUndoState([[bkm_b_txn, bkm_a_txn, folder_a_txn]], 0); |
|
489 |
|
490 // Test moving items between folders. |
|
491 let folder_b_info = yield createTestFolderInfo("Folder B"); |
|
492 let folder_b_txn = PT.NewFolder(folder_b_info); |
|
493 folder_b_info.GUID = yield PT.transact(folder_b_txn); |
|
494 ensureUndoState([ [folder_b_txn] |
|
495 , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 0); |
|
496 |
|
497 moveTxn = PT.MoveItem({ GUID: bkm_a_info.GUID |
|
498 , newParentGUID: folder_b_info.GUID |
|
499 , newIndex: bmsvc.DEFAULT_INDEX }); |
|
500 yield PT.transact(moveTxn); |
|
501 |
|
502 ensureDo = () => { |
|
503 ensureUndoState([ [moveTxn] |
|
504 , [folder_b_txn] |
|
505 , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 0); |
|
506 ensureItemsMoved({ GUID: bkm_a_info.GUID |
|
507 , oldParentGUID: folder_a_info.GUID |
|
508 , newParentGUID: folder_b_info.GUID |
|
509 , oldIndex: 0 |
|
510 , newIndex: 0 }); |
|
511 observer.reset(); |
|
512 }; |
|
513 let ensureUndo = () => { |
|
514 ensureUndoState([ [moveTxn] |
|
515 , [folder_b_txn] |
|
516 , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 1); |
|
517 ensureItemsMoved({ GUID: bkm_a_info.GUID |
|
518 , oldParentGUID: folder_b_info.GUID |
|
519 , newParentGUID: folder_a_info.GUID |
|
520 , oldIndex: 0 |
|
521 , newIndex: 0 }); |
|
522 observer.reset(); |
|
523 }; |
|
524 |
|
525 ensureDo(); |
|
526 yield PT.undo(); |
|
527 ensureUndo(); |
|
528 yield PT.redo(); |
|
529 ensureDo(); |
|
530 yield PT.undo(); |
|
531 ensureUndo(); |
|
532 |
|
533 // Clean up |
|
534 yield PT.undo(); // folder_b_txn |
|
535 yield PT.undo(); // folder_a_txn + the bookmarks; |
|
536 do_check_eq(observer.itemsRemoved.size, 4); |
|
537 ensureUndoState([ [moveTxn] |
|
538 , [folder_b_txn] |
|
539 , [bkm_b_txn, bkm_a_txn, folder_a_txn] ], 3); |
|
540 yield PT.clearTransactionsHistory(); |
|
541 ensureUndoState(); |
|
542 }); |
|
543 |
|
544 add_task(function* test_remove_folder() { |
|
545 let folder_level_1_info = yield createTestFolderInfo("Folder Level 1"); |
|
546 let folder_level_2_info = { title: "Folder Level 2" }; |
|
547 let [folder_level_1_txn, |
|
548 folder_level_2_txn] = yield PT.transact(function* () { |
|
549 let folder_level_1_txn = PT.NewFolder(folder_level_1_info); |
|
550 folder_level_1_info.GUID = yield folder_level_1_txn; |
|
551 folder_level_2_info.parentGUID = folder_level_1_info.GUID; |
|
552 let folder_level_2_txn = PT.NewFolder(folder_level_2_info); |
|
553 folder_level_2_info.GUID = yield folder_level_2_txn; |
|
554 return [folder_level_1_txn, folder_level_2_txn]; |
|
555 }); |
|
556 |
|
557 ensureUndoState([[folder_level_2_txn, folder_level_1_txn]]); |
|
558 yield ensureItemsAdded(folder_level_1_info, folder_level_2_info); |
|
559 observer.reset(); |
|
560 |
|
561 let remove_folder_2_txn = PT.RemoveItem(folder_level_2_info); |
|
562 yield PT.transact(remove_folder_2_txn); |
|
563 |
|
564 ensureUndoState([ [remove_folder_2_txn] |
|
565 , [folder_level_2_txn, folder_level_1_txn] ]); |
|
566 yield ensureItemsRemoved(folder_level_2_info); |
|
567 |
|
568 // Undo RemoveItem "Folder Level 2" |
|
569 yield PT.undo(); |
|
570 ensureUndoState([ [remove_folder_2_txn] |
|
571 , [folder_level_2_txn, folder_level_1_txn] ], 1); |
|
572 yield ensureItemsAdded(folder_level_2_info); |
|
573 ensureTimestampsUpdated(folder_level_2_info.GUID, true); |
|
574 observer.reset(); |
|
575 |
|
576 // Redo RemoveItem "Folder Level 2" |
|
577 yield PT.redo(); |
|
578 ensureUndoState([ [remove_folder_2_txn] |
|
579 , [folder_level_2_txn, folder_level_1_txn] ]); |
|
580 yield ensureItemsRemoved(folder_level_2_info); |
|
581 observer.reset(); |
|
582 |
|
583 // Undo it again |
|
584 yield PT.undo(); |
|
585 ensureUndoState([ [remove_folder_2_txn] |
|
586 , [folder_level_2_txn, folder_level_1_txn] ], 1); |
|
587 yield ensureItemsAdded(folder_level_2_info); |
|
588 ensureTimestampsUpdated(folder_level_2_info.GUID, true); |
|
589 observer.reset(); |
|
590 |
|
591 // Undo the creation of both folders |
|
592 yield PT.undo(); |
|
593 ensureUndoState([ [remove_folder_2_txn] |
|
594 , [folder_level_2_txn, folder_level_1_txn] ], 2); |
|
595 yield ensureItemsRemoved(folder_level_2_info, folder_level_1_info); |
|
596 observer.reset(); |
|
597 |
|
598 // Redo the creation of both folders |
|
599 yield PT.redo(); |
|
600 ensureUndoState([ [remove_folder_2_txn] |
|
601 , [folder_level_2_txn, folder_level_1_txn] ], 1); |
|
602 yield ensureItemsAdded(folder_level_1_info, folder_level_2_info); |
|
603 ensureTimestampsUpdated(folder_level_1_info.GUID, true); |
|
604 ensureTimestampsUpdated(folder_level_2_info.GUID, true); |
|
605 observer.reset(); |
|
606 |
|
607 // Redo RemoveItem "Folder Level 2" |
|
608 yield PT.redo(); |
|
609 ensureUndoState([ [remove_folder_2_txn] |
|
610 , [folder_level_2_txn, folder_level_1_txn] ]); |
|
611 yield ensureItemsRemoved(folder_level_2_info); |
|
612 observer.reset(); |
|
613 |
|
614 // Undo everything one last time |
|
615 yield PT.undo(); |
|
616 ensureUndoState([ [remove_folder_2_txn] |
|
617 , [folder_level_2_txn, folder_level_1_txn] ], 1); |
|
618 yield ensureItemsAdded(folder_level_2_info); |
|
619 observer.reset(); |
|
620 |
|
621 yield PT.undo(); |
|
622 ensureUndoState([ [remove_folder_2_txn] |
|
623 , [folder_level_2_txn, folder_level_1_txn] ], 2); |
|
624 yield ensureItemsRemoved(folder_level_2_info, folder_level_2_info); |
|
625 observer.reset(); |
|
626 |
|
627 yield PT.clearTransactionsHistory(); |
|
628 ensureUndoState(); |
|
629 }); |
|
630 |
|
631 add_task(function* test_add_and_remove_bookmarks_with_additional_info() { |
|
632 const testURI = NetUtil.newURI("http://add.remove.tag") |
|
633 , TAG_1 = "TestTag1", TAG_2 = "TestTag2" |
|
634 , KEYWORD = "test_keyword" |
|
635 , POST_DATA = "post_data" |
|
636 , ANNO = { name: "TestAnno", value: "TestAnnoValue" }; |
|
637 |
|
638 let folder_info = yield createTestFolderInfo(); |
|
639 folder_info.GUID = yield PT.transact(PT.NewFolder(folder_info)); |
|
640 let ensureTags = ensureTagsForURI.bind(null, testURI); |
|
641 |
|
642 // Check that the NewBookmark transaction preserves tags. |
|
643 observer.reset(); |
|
644 let b1_info = { parentGUID: folder_info.GUID, uri: testURI, tags: [TAG_1] }; |
|
645 b1_info.GUID = yield PT.transact(PT.NewBookmark(b1_info)); |
|
646 ensureTags([TAG_1]); |
|
647 yield PT.undo(); |
|
648 ensureTags([]); |
|
649 |
|
650 observer.reset(); |
|
651 yield PT.redo(); |
|
652 ensureTimestampsUpdated(b1_info.GUID, true); |
|
653 ensureTags([TAG_1]); |
|
654 |
|
655 // Check if the RemoveItem transaction removes and restores tags of children |
|
656 // correctly. |
|
657 yield PT.transact(PT.RemoveItem(folder_info.GUID)); |
|
658 ensureTags([]); |
|
659 |
|
660 observer.reset(); |
|
661 yield PT.undo(); |
|
662 ensureTimestampsUpdated(b1_info.GUID, true); |
|
663 ensureTags([TAG_1]); |
|
664 |
|
665 yield PT.redo(); |
|
666 ensureTags([]); |
|
667 |
|
668 observer.reset(); |
|
669 yield PT.undo(); |
|
670 ensureTimestampsUpdated(b1_info.GUID, true); |
|
671 ensureTags([TAG_1]); |
|
672 |
|
673 // * Check that no-op tagging (the uri is already tagged with TAG_1) is |
|
674 // also a no-op on undo. |
|
675 // * Test the "keyword" property of the NewBookmark transaction. |
|
676 observer.reset(); |
|
677 let b2_info = { parentGUID: folder_info.GUID |
|
678 , uri: testURI, tags: [TAG_1, TAG_2] |
|
679 , keyword: KEYWORD |
|
680 , postData: POST_DATA |
|
681 , annotations: [ANNO] }; |
|
682 b2_info.GUID = yield PT.transact(PT.NewBookmark(b2_info)); |
|
683 let b2_post_creation_changes = [ |
|
684 { GUID: b2_info.GUID |
|
685 , isAnnoProperty: true |
|
686 , property: ANNO.name |
|
687 , newValue: ANNO.value }, |
|
688 { GUID: b2_info.GUID |
|
689 , property: "keyword" |
|
690 , newValue: KEYWORD }, |
|
691 { GUID: b2_info.GUID |
|
692 , isAnnoProperty: true |
|
693 , property: PlacesUtils.POST_DATA_ANNO |
|
694 , newValue: POST_DATA } ]; |
|
695 ensureItemsChanged(...b2_post_creation_changes); |
|
696 ensureTags([TAG_1, TAG_2]); |
|
697 |
|
698 observer.reset(); |
|
699 yield PT.undo(); |
|
700 yield ensureItemsRemoved(b2_info); |
|
701 ensureTags([TAG_1]); |
|
702 |
|
703 // Check if RemoveItem correctly restores keywords, tags and annotations. |
|
704 observer.reset(); |
|
705 yield PT.redo(); |
|
706 ensureItemsChanged(...b2_post_creation_changes); |
|
707 ensureTags([TAG_1, TAG_2]); |
|
708 |
|
709 // Test RemoveItem for multiple items. |
|
710 observer.reset(); |
|
711 yield PT.transact(PT.RemoveItem(b1_info.GUID)); |
|
712 yield PT.transact(PT.RemoveItem(b2_info.GUID)); |
|
713 yield PT.transact(PT.RemoveItem(folder_info.GUID)); |
|
714 yield ensureItemsRemoved(b1_info, b2_info, folder_info); |
|
715 ensureTags([]); |
|
716 |
|
717 observer.reset(); |
|
718 yield PT.undo(); |
|
719 yield ensureItemsAdded(folder_info); |
|
720 ensureTags([]); |
|
721 |
|
722 observer.reset(); |
|
723 yield PT.undo(); |
|
724 ensureItemsChanged(...b2_post_creation_changes); |
|
725 ensureTags([TAG_1, TAG_2]); |
|
726 |
|
727 observer.reset(); |
|
728 yield PT.undo(); |
|
729 yield ensureItemsAdded(b1_info); |
|
730 ensureTags([TAG_1, TAG_2]); |
|
731 |
|
732 // The redo calls below cleanup everything we did. |
|
733 observer.reset(); |
|
734 yield PT.redo(); |
|
735 yield ensureItemsRemoved(b1_info); |
|
736 ensureTags([TAG_1, TAG_2]); |
|
737 |
|
738 observer.reset(); |
|
739 yield PT.redo(); |
|
740 yield ensureItemsRemoved(b2_info); |
|
741 ensureTags([]); |
|
742 |
|
743 observer.reset(); |
|
744 yield PT.redo(); |
|
745 yield ensureItemsRemoved(folder_info); |
|
746 ensureTags([]); |
|
747 |
|
748 yield PT.clearTransactionsHistory(); |
|
749 ensureUndoState(); |
|
750 }); |
|
751 |
|
752 add_task(function* test_creating_and_removing_a_separator() { |
|
753 let folder_info = yield createTestFolderInfo(); |
|
754 let separator_info = {}; |
|
755 let undoEntries = []; |
|
756 |
|
757 observer.reset(); |
|
758 let create_txns = yield PT.transact(function* () { |
|
759 let folder_txn = PT.NewFolder(folder_info); |
|
760 folder_info.GUID = separator_info.parentGUID = yield folder_txn; |
|
761 let separator_txn = PT.NewSeparator(separator_info); |
|
762 separator_info.GUID = yield separator_txn; |
|
763 return [separator_txn, folder_txn]; |
|
764 }); |
|
765 undoEntries.unshift(create_txns); |
|
766 ensureUndoState(undoEntries, 0); |
|
767 ensureItemsAdded(folder_info, separator_info); |
|
768 |
|
769 observer.reset(); |
|
770 yield PT.undo(); |
|
771 ensureUndoState(undoEntries, 1); |
|
772 ensureItemsRemoved(folder_info, separator_info); |
|
773 |
|
774 observer.reset(); |
|
775 yield PT.redo(); |
|
776 ensureUndoState(undoEntries, 0); |
|
777 ensureItemsAdded(folder_info, separator_info); |
|
778 |
|
779 observer.reset(); |
|
780 let remove_sep_txn = PT.RemoveItem(separator_info); |
|
781 yield PT.transact(remove_sep_txn); |
|
782 undoEntries.unshift([remove_sep_txn]); |
|
783 ensureUndoState(undoEntries, 0); |
|
784 ensureItemsRemoved(separator_info); |
|
785 |
|
786 observer.reset(); |
|
787 yield PT.undo(); |
|
788 ensureUndoState(undoEntries, 1); |
|
789 ensureItemsAdded(separator_info); |
|
790 |
|
791 observer.reset(); |
|
792 yield PT.undo(); |
|
793 ensureUndoState(undoEntries, 2); |
|
794 ensureItemsRemoved(folder_info, separator_info); |
|
795 |
|
796 observer.reset(); |
|
797 yield PT.redo(); |
|
798 ensureUndoState(undoEntries, 1); |
|
799 ensureItemsAdded(folder_info, separator_info); |
|
800 |
|
801 // Clear redo entries and check that |redo| does nothing |
|
802 observer.reset(); |
|
803 yield PT.clearTransactionsHistory(false, true); |
|
804 undoEntries.shift(); |
|
805 ensureUndoState(undoEntries, 0); |
|
806 yield PT.redo(); |
|
807 ensureItemsAdded(); |
|
808 ensureItemsRemoved(); |
|
809 |
|
810 // Cleanup |
|
811 observer.reset(); |
|
812 yield PT.undo(); |
|
813 ensureUndoState(undoEntries, 1); |
|
814 ensureItemsRemoved(folder_info, separator_info); |
|
815 yield PT.clearTransactionsHistory(); |
|
816 ensureUndoState(); |
|
817 }); |
|
818 |
|
819 add_task(function* test_edit_title() { |
|
820 let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
821 , uri: NetUtil.newURI("http://test_create_item.com") |
|
822 , title: "Original Title" }; |
|
823 |
|
824 function ensureTitleChange(aCurrentTitle) { |
|
825 ensureItemsChanged({ GUID: bm_info.GUID |
|
826 , property: "title" |
|
827 , newValue: aCurrentTitle}); |
|
828 } |
|
829 |
|
830 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
831 |
|
832 observer.reset(); |
|
833 yield PT.transact(PT.EditTitle({ GUID: bm_info.GUID, title: "New Title" })); |
|
834 ensureTitleChange("New Title"); |
|
835 |
|
836 observer.reset(); |
|
837 yield PT.undo(); |
|
838 ensureTitleChange("Original Title"); |
|
839 |
|
840 observer.reset(); |
|
841 yield PT.redo(); |
|
842 ensureTitleChange("New Title"); |
|
843 |
|
844 // Cleanup |
|
845 observer.reset(); |
|
846 yield PT.undo(); |
|
847 ensureTitleChange("Original Title"); |
|
848 yield PT.undo(); |
|
849 ensureItemsRemoved(bm_info); |
|
850 |
|
851 yield PT.clearTransactionsHistory(); |
|
852 ensureUndoState(); |
|
853 }); |
|
854 |
|
855 add_task(function* test_edit_url() { |
|
856 let oldURI = NetUtil.newURI("http://old.test_editing_item_uri.com/"); |
|
857 let newURI = NetUtil.newURI("http://new.test_editing_item_uri.com/"); |
|
858 let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
859 , uri: oldURI |
|
860 , tags: ["TestTag"]}; |
|
861 |
|
862 function ensureURIAndTags(aPreChangeURI, aPostChangeURI, aOLdURITagsPreserved) { |
|
863 ensureItemsChanged({ GUID: bm_info.GUID |
|
864 , property: "uri" |
|
865 , newValue: aPostChangeURI.spec }); |
|
866 ensureTagsForURI(aPostChangeURI, bm_info.tags); |
|
867 ensureTagsForURI(aPreChangeURI, aOLdURITagsPreserved ? bm_info.tags : []); |
|
868 } |
|
869 |
|
870 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
871 ensureTagsForURI(oldURI, bm_info.tags); |
|
872 |
|
873 // When there's a single bookmark for the same url, tags should be moved. |
|
874 observer.reset(); |
|
875 yield PT.transact(PT.EditURI({ GUID: bm_info.GUID, uri: newURI })); |
|
876 ensureURIAndTags(oldURI, newURI, false); |
|
877 |
|
878 observer.reset(); |
|
879 yield PT.undo(); |
|
880 ensureURIAndTags(newURI, oldURI, false); |
|
881 |
|
882 observer.reset(); |
|
883 yield PT.redo(); |
|
884 ensureURIAndTags(oldURI, newURI, false); |
|
885 |
|
886 observer.reset(); |
|
887 yield PT.undo(); |
|
888 ensureURIAndTags(newURI, oldURI, false); |
|
889 |
|
890 // When there're multiple bookmarks for the same url, tags should be copied. |
|
891 let bm2_info = Object.create(bm_info); |
|
892 bm2_info.GUID = yield PT.transact(PT.NewBookmark(bm2_info)); |
|
893 let bm3_info = Object.create(bm_info); |
|
894 bm3_info.uri = newURI; |
|
895 bm3_info.GUID = yield PT.transact(PT.NewBookmark(bm3_info)); |
|
896 |
|
897 observer.reset(); |
|
898 yield PT.transact(PT.EditURI({ GUID: bm_info.GUID, uri: newURI })); |
|
899 ensureURIAndTags(oldURI, newURI, true); |
|
900 |
|
901 observer.reset(); |
|
902 yield PT.undo(); |
|
903 ensureURIAndTags(newURI, oldURI, true); |
|
904 |
|
905 observer.reset(); |
|
906 yield PT.redo(); |
|
907 ensureURIAndTags(oldURI, newURI, true); |
|
908 |
|
909 // Cleanup |
|
910 observer.reset(); |
|
911 yield PT.undo(); |
|
912 ensureURIAndTags(newURI, oldURI, true); |
|
913 yield PT.undo(); |
|
914 yield PT.undo(); |
|
915 yield PT.undo(); |
|
916 ensureItemsRemoved(bm3_info, bm2_info, bm_info); |
|
917 |
|
918 yield PT.clearTransactionsHistory(); |
|
919 ensureUndoState(); |
|
920 }); |
|
921 |
|
922 add_task(function* test_edit_keyword() { |
|
923 let bm_info = { parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
924 , uri: NetUtil.newURI("http://test.edit.keyword") }; |
|
925 const KEYWORD = "test_keyword"; |
|
926 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
927 function ensureKeywordChange(aCurrentKeyword = "") { |
|
928 ensureItemsChanged({ GUID: bm_info.GUID |
|
929 , property: "keyword" |
|
930 , newValue: aCurrentKeyword }); |
|
931 } |
|
932 |
|
933 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
934 |
|
935 observer.reset(); |
|
936 yield PT.transact(PT.EditKeyword({ GUID: bm_info.GUID, keyword: KEYWORD })); |
|
937 ensureKeywordChange(KEYWORD); |
|
938 |
|
939 observer.reset(); |
|
940 yield PT.undo(); |
|
941 ensureKeywordChange(); |
|
942 |
|
943 observer.reset(); |
|
944 yield PT.redo(); |
|
945 ensureKeywordChange(KEYWORD); |
|
946 |
|
947 // Cleanup |
|
948 observer.reset(); |
|
949 yield PT.undo(); |
|
950 ensureKeywordChange(); |
|
951 yield PT.undo(); |
|
952 ensureItemsRemoved(bm_info); |
|
953 |
|
954 yield PT.clearTransactionsHistory(); |
|
955 ensureUndoState(); |
|
956 }); |
|
957 |
|
958 add_task(function* test_tag_uri_unbookmarked_uri() { |
|
959 let info = { uri: NetUtil.newURI("http://un.book.marked"), tags: ["MyTag"] }; |
|
960 |
|
961 function ensureDo() { |
|
962 // A new bookmark should be created. |
|
963 // (getMostRecentBookmarkForURI ignores tags) |
|
964 do_check_neq(PlacesUtils.getMostRecentBookmarkForURI(info.uri), -1); |
|
965 ensureTagsForURI(info.uri, info.tags); |
|
966 } |
|
967 function ensureUndo() { |
|
968 do_check_eq(PlacesUtils.getMostRecentBookmarkForURI(info.uri), -1); |
|
969 ensureTagsForURI(info.uri, []); |
|
970 } |
|
971 |
|
972 yield PT.transact(PT.TagURI(info)); |
|
973 ensureDo(); |
|
974 yield PT.undo(); |
|
975 ensureUndo(); |
|
976 yield PT.redo(); |
|
977 ensureDo(); |
|
978 yield PT.undo(); |
|
979 ensureUndo(); |
|
980 }); |
|
981 |
|
982 add_task(function* test_tag_uri_bookmarked_uri() { |
|
983 let bm_info = { uri: NetUtil.newURI("http://bookmarked.uri") |
|
984 , parentGUID: yield PlacesUtils.promiseItemGUID(root) }; |
|
985 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
986 |
|
987 let tagging_info = { uri: bm_info.uri, tags: ["MyTag"] }; |
|
988 yield PT.transact(PT.TagURI(tagging_info)); |
|
989 ensureTagsForURI(tagging_info.uri, tagging_info.tags); |
|
990 |
|
991 yield PT.undo(); |
|
992 ensureTagsForURI(tagging_info.uri, []); |
|
993 yield PT.redo(); |
|
994 ensureTagsForURI(tagging_info.uri, tagging_info.tags); |
|
995 |
|
996 // Cleanup |
|
997 yield PT.undo(); |
|
998 ensureTagsForURI(tagging_info.uri, []); |
|
999 observer.reset(); |
|
1000 yield PT.undo(); |
|
1001 ensureItemsRemoved(bm_info); |
|
1002 |
|
1003 yield PT.clearTransactionsHistory(); |
|
1004 ensureUndoState(); |
|
1005 }); |
|
1006 |
|
1007 add_task(function* test_untag_uri() { |
|
1008 let bm_info = { uri: NetUtil.newURI("http://test.untag.uri") |
|
1009 , parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
1010 , tags: ["T"]}; |
|
1011 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
1012 |
|
1013 yield PT.transact(PT.UntagURI(bm_info)); |
|
1014 ensureTagsForURI(bm_info.uri, []); |
|
1015 yield PT.undo(); |
|
1016 ensureTagsForURI(bm_info.uri, bm_info.tags); |
|
1017 yield PT.redo(); |
|
1018 ensureTagsForURI(bm_info.uri, []); |
|
1019 yield PT.undo(); |
|
1020 ensureTagsForURI(bm_info.uri, bm_info.tags); |
|
1021 |
|
1022 // Also test just passing the uri (should remove all tags) |
|
1023 yield PT.transact(PT.UntagURI(bm_info.uri)); |
|
1024 ensureTagsForURI(bm_info.uri, []); |
|
1025 yield PT.undo(); |
|
1026 ensureTagsForURI(bm_info.uri, bm_info.tags); |
|
1027 yield PT.redo(); |
|
1028 ensureTagsForURI(bm_info.uri, []); |
|
1029 }); |
|
1030 |
|
1031 add_task(function* test_set_item_annotation() { |
|
1032 let bm_info = { uri: NetUtil.newURI("http://test.item.annotation") |
|
1033 , parentGUID: yield PlacesUtils.promiseItemGUID(root) }; |
|
1034 let anno_info = { name: "TestAnno", value: "TestValue" }; |
|
1035 function ensureAnnoState(aSet) { |
|
1036 ensureAnnotationsSet(bm_info.GUID, |
|
1037 [{ name: anno_info.name |
|
1038 , value: aSet ? anno_info.value : null }]); |
|
1039 } |
|
1040 |
|
1041 bm_info.GUID = yield PT.transact(PT.NewBookmark(bm_info)); |
|
1042 |
|
1043 observer.reset(); |
|
1044 yield PT.transact(PT.SetItemAnnotation({ GUID: bm_info.GUID |
|
1045 , annotationObject: anno_info })); |
|
1046 ensureAnnoState(true); |
|
1047 |
|
1048 observer.reset(); |
|
1049 yield PT.undo(); |
|
1050 ensureAnnoState(false); |
|
1051 |
|
1052 observer.reset(); |
|
1053 yield PT.redo(); |
|
1054 ensureAnnoState(true); |
|
1055 |
|
1056 // Test removing the annotation by not passing the |value| property. |
|
1057 observer.reset(); |
|
1058 yield PT.transact( |
|
1059 PT.SetItemAnnotation({ GUID: bm_info.GUID |
|
1060 , annotationObject: { name: anno_info.name }})); |
|
1061 ensureAnnoState(false); |
|
1062 |
|
1063 observer.reset(); |
|
1064 yield PT.undo(); |
|
1065 ensureAnnoState(true); |
|
1066 |
|
1067 observer.reset(); |
|
1068 yield PT.redo(); |
|
1069 ensureAnnoState(false); |
|
1070 }); |
|
1071 |
|
1072 add_task(function* test_sort_folder_by_name() { |
|
1073 let folder_info = yield createTestFolderInfo(); |
|
1074 |
|
1075 let uri = NetUtil.newURI("http://sort.by.name/"); |
|
1076 let preSep = [{ title: i, uri: uri } for (i of ["3","2","1"])]; |
|
1077 let sep = {}; |
|
1078 let postSep = [{ title: l, uri: uri } for (l of ["c","b","a"])]; |
|
1079 let originalOrder = [...preSep, sep, ...postSep]; |
|
1080 let sortedOrder = [...preSep.slice(0).reverse(), |
|
1081 sep, |
|
1082 ...postSep.slice(0).reverse()]; |
|
1083 yield PT.transact(function* () { |
|
1084 folder_info.GUID = yield PT.NewFolder(folder_info); |
|
1085 for (let info of originalOrder) { |
|
1086 info.parentGUID = folder_info.GUID; |
|
1087 info.GUID = yield info == sep ? |
|
1088 PT.NewSeparator(info) : PT.NewBookmark(info); |
|
1089 } |
|
1090 }); |
|
1091 |
|
1092 let folderId = yield PlacesUtils.promiseItemId(folder_info.GUID); |
|
1093 let folderContainer = PlacesUtils.getFolderContents(folderId).root; |
|
1094 function ensureOrder(aOrder) { |
|
1095 for (let i = 0; i < folderContainer.childCount; i++) { |
|
1096 do_check_eq(folderContainer.getChild(i).bookmarkGuid, aOrder[i].GUID); |
|
1097 } |
|
1098 } |
|
1099 |
|
1100 ensureOrder(originalOrder); |
|
1101 yield PT.transact(PT.SortByName(folder_info.GUID)); |
|
1102 ensureOrder(sortedOrder); |
|
1103 yield PT.undo(); |
|
1104 ensureOrder(originalOrder); |
|
1105 yield PT.redo(); |
|
1106 ensureOrder(sortedOrder); |
|
1107 |
|
1108 // Cleanup |
|
1109 observer.reset(); |
|
1110 yield PT.undo(); |
|
1111 ensureOrder(originalOrder); |
|
1112 yield PT.undo(); |
|
1113 ensureItemsRemoved(...originalOrder, folder_info); |
|
1114 }); |
|
1115 |
|
1116 add_task(function* test_livemark_txns() { |
|
1117 let livemark_info = |
|
1118 { feedURI: NetUtil.newURI("http://test.feed.uri") |
|
1119 , parentGUID: yield PlacesUtils.promiseItemGUID(root) |
|
1120 , title: "Test Livemark" }; |
|
1121 function ensureLivemarkAdded() { |
|
1122 ensureItemsAdded({ GUID: livemark_info.GUID |
|
1123 , title: livemark_info.title |
|
1124 , parentGUID: livemark_info.parentGUID |
|
1125 , itemType: bmsvc.TYPE_FOLDER }); |
|
1126 let annos = [{ name: PlacesUtils.LMANNO_FEEDURI |
|
1127 , value: livemark_info.feedURI.spec }]; |
|
1128 if ("siteURI" in livemark_info) { |
|
1129 annos.push({ name: PlacesUtils.LMANNO_SITEURI |
|
1130 , value: livemark_info.siteURI.spec }); |
|
1131 } |
|
1132 ensureAnnotationsSet(livemark_info.GUID, annos); |
|
1133 } |
|
1134 function ensureLivemarkRemoved() { |
|
1135 ensureItemsRemoved({ GUID: livemark_info.GUID |
|
1136 , parentGUID: livemark_info.parentGUID }); |
|
1137 } |
|
1138 |
|
1139 function* _testDoUndoRedoUndo() { |
|
1140 observer.reset(); |
|
1141 livemark_info.GUID = yield PT.transact(PT.NewLivemark(livemark_info)); |
|
1142 ensureLivemarkAdded(); |
|
1143 |
|
1144 observer.reset(); |
|
1145 yield PT.undo(); |
|
1146 ensureLivemarkRemoved(); |
|
1147 |
|
1148 observer.reset(); |
|
1149 yield PT.redo(); |
|
1150 ensureLivemarkAdded(); |
|
1151 |
|
1152 yield PT.undo(); |
|
1153 ensureLivemarkRemoved(); |
|
1154 } |
|
1155 |
|
1156 yield* _testDoUndoRedoUndo() |
|
1157 livemark_info.siteURI = NetUtil.newURI("http://feed.site.uri"); |
|
1158 yield* _testDoUndoRedoUndo(); |
|
1159 |
|
1160 yield PT.clearTransactionsHistory(); |
|
1161 }); |