Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | 'use strict'; |
michael@0 | 5 | |
michael@0 | 6 | module.metadata = { |
michael@0 | 7 | 'engines': { |
michael@0 | 8 | 'Firefox': '*' |
michael@0 | 9 | } |
michael@0 | 10 | }; |
michael@0 | 11 | |
michael@0 | 12 | const { Cc, Ci } = require('chrome'); |
michael@0 | 13 | const { request } = require('sdk/addon/host'); |
michael@0 | 14 | const { filter } = require('sdk/event/utils'); |
michael@0 | 15 | const { on, off } = require('sdk/event/core'); |
michael@0 | 16 | const { setTimeout } = require('sdk/timers'); |
michael@0 | 17 | const { newURI } = require('sdk/url/utils'); |
michael@0 | 18 | const { defer, all } = require('sdk/core/promise'); |
michael@0 | 19 | const { defer: async } = require('sdk/lang/functional'); |
michael@0 | 20 | const { before, after } = require('sdk/test/utils'); |
michael@0 | 21 | |
michael@0 | 22 | const { |
michael@0 | 23 | Bookmark, Group, Separator, |
michael@0 | 24 | save, search, remove, |
michael@0 | 25 | MENU, TOOLBAR, UNSORTED |
michael@0 | 26 | } = require('sdk/places/bookmarks'); |
michael@0 | 27 | const { |
michael@0 | 28 | invalidResolve, invalidReject, createTree, |
michael@0 | 29 | compareWithHost, createBookmark, createBookmarkItem, |
michael@0 | 30 | createBookmarkTree, addVisits, resetPlaces |
michael@0 | 31 | } = require('../places-helper'); |
michael@0 | 32 | const { promisedEmitter } = require('sdk/places/utils'); |
michael@0 | 33 | const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1']. |
michael@0 | 34 | getService(Ci.nsINavBookmarksService); |
michael@0 | 35 | const tagsrv = Cc['@mozilla.org/browser/tagging-service;1']. |
michael@0 | 36 | getService(Ci.nsITaggingService); |
michael@0 | 37 | |
michael@0 | 38 | exports.testDefaultFolders = function (assert) { |
michael@0 | 39 | var ids = [ |
michael@0 | 40 | bmsrv.bookmarksMenuFolder, |
michael@0 | 41 | bmsrv.toolbarFolder, |
michael@0 | 42 | bmsrv.unfiledBookmarksFolder |
michael@0 | 43 | ]; |
michael@0 | 44 | [MENU, TOOLBAR, UNSORTED].forEach(function (g, i) { |
michael@0 | 45 | assert.ok(g.id === ids[i], ' default group matches id'); |
michael@0 | 46 | }); |
michael@0 | 47 | }; |
michael@0 | 48 | |
michael@0 | 49 | exports.testValidation = function (assert) { |
michael@0 | 50 | assert.throws(() => { |
michael@0 | 51 | Bookmark({ title: 'a title' }); |
michael@0 | 52 | }, /The `url` property must be a valid URL/, 'throws empty URL error'); |
michael@0 | 53 | |
michael@0 | 54 | assert.throws(() => { |
michael@0 | 55 | Bookmark({ title: 'a title', url: 'not.a.url' }); |
michael@0 | 56 | }, /The `url` property must be a valid URL/, 'throws invalid URL error'); |
michael@0 | 57 | |
michael@0 | 58 | assert.throws(() => { |
michael@0 | 59 | Bookmark({ url: 'http://foo.com' }); |
michael@0 | 60 | }, /The `title` property must be defined/, 'throws title error'); |
michael@0 | 61 | |
michael@0 | 62 | assert.throws(() => { |
michael@0 | 63 | Bookmark(); |
michael@0 | 64 | }, /./, 'throws any error'); |
michael@0 | 65 | |
michael@0 | 66 | assert.throws(() => { |
michael@0 | 67 | Group(); |
michael@0 | 68 | }, /The `title` property must be defined/, 'throws title error for group'); |
michael@0 | 69 | |
michael@0 | 70 | assert.throws(() => { |
michael@0 | 71 | Bookmark({ url: 'http://foo.com', title: 'my title', tags: 'a tag' }); |
michael@0 | 72 | }, /The `tags` property must be a Set, or an array/, 'throws error for non set/array tag'); |
michael@0 | 73 | }; |
michael@0 | 74 | |
michael@0 | 75 | exports.testCreateBookmarks = function (assert, done) { |
michael@0 | 76 | var bm = Bookmark({ |
michael@0 | 77 | title: 'moz', |
michael@0 | 78 | url: 'http://mozilla.org', |
michael@0 | 79 | tags: ['moz1', 'moz2', 'moz3'] |
michael@0 | 80 | }); |
michael@0 | 81 | |
michael@0 | 82 | save(bm).on('data', (bookmark, input) => { |
michael@0 | 83 | assert.equal(input, bm, 'input is original input item'); |
michael@0 | 84 | assert.ok(bookmark.id, 'Bookmark has ID'); |
michael@0 | 85 | assert.equal(bookmark.title, 'moz'); |
michael@0 | 86 | assert.equal(bookmark.url, 'http://mozilla.org'); |
michael@0 | 87 | assert.equal(bookmark.group, UNSORTED, 'Unsorted folder is default parent'); |
michael@0 | 88 | assert.ok(bookmark !== bm, 'bookmark should be a new instance'); |
michael@0 | 89 | compareWithHost(assert, bookmark); |
michael@0 | 90 | }).on('end', bookmarks => { |
michael@0 | 91 | assert.equal(bookmarks.length, 1, 'returned bookmarks in end'); |
michael@0 | 92 | assert.equal(bookmarks[0].url, 'http://mozilla.org'); |
michael@0 | 93 | assert.equal(bookmarks[0].tags.has('moz1'), true, 'has first tag'); |
michael@0 | 94 | assert.equal(bookmarks[0].tags.has('moz2'), true, 'has second tag'); |
michael@0 | 95 | assert.equal(bookmarks[0].tags.has('moz3'), true, 'has third tag'); |
michael@0 | 96 | assert.pass('end event is called'); |
michael@0 | 97 | done(); |
michael@0 | 98 | }); |
michael@0 | 99 | }; |
michael@0 | 100 | |
michael@0 | 101 | exports.testCreateGroup = function (assert, done) { |
michael@0 | 102 | save(Group({ title: 'mygroup', group: MENU })).on('data', g => { |
michael@0 | 103 | assert.ok(g.id, 'Bookmark has ID'); |
michael@0 | 104 | assert.equal(g.title, 'mygroup', 'matches title'); |
michael@0 | 105 | assert.equal(g.group, MENU, 'Menu folder matches'); |
michael@0 | 106 | compareWithHost(assert, g); |
michael@0 | 107 | }).on('end', results => { |
michael@0 | 108 | assert.equal(results.length, 1); |
michael@0 | 109 | assert.pass('end event is called'); |
michael@0 | 110 | done(); |
michael@0 | 111 | }); |
michael@0 | 112 | }; |
michael@0 | 113 | |
michael@0 | 114 | exports.testCreateSeparator = function (assert, done) { |
michael@0 | 115 | save(Separator({ group: MENU })).on('data', function (s) { |
michael@0 | 116 | assert.ok(s.id, 'Separator has id'); |
michael@0 | 117 | assert.equal(s.group, MENU, 'Parent group matches'); |
michael@0 | 118 | compareWithHost(assert, s); |
michael@0 | 119 | }).on('end', function (results) { |
michael@0 | 120 | assert.equal(results.length, 1); |
michael@0 | 121 | assert.pass('end event is called'); |
michael@0 | 122 | done(); |
michael@0 | 123 | }); |
michael@0 | 124 | }; |
michael@0 | 125 | |
michael@0 | 126 | exports.testCreateError = function (assert, done) { |
michael@0 | 127 | let bookmarks = [ |
michael@0 | 128 | { title: 'moz1', url: 'http://moz1.com', type: 'bookmark'}, |
michael@0 | 129 | { title: 'moz2', url: 'invalidurl', type: 'bookmark'}, |
michael@0 | 130 | { title: 'moz3', url: 'http://moz3.com', type: 'bookmark'} |
michael@0 | 131 | ]; |
michael@0 | 132 | |
michael@0 | 133 | let dataCount = 0, errorCount = 0; |
michael@0 | 134 | save(bookmarks).on('data', bookmark => { |
michael@0 | 135 | assert.ok(/moz[1|3]/.test(bookmark.title), 'valid bookmarks complete'); |
michael@0 | 136 | dataCount++; |
michael@0 | 137 | }).on('error', (reason, item) => { |
michael@0 | 138 | assert.ok( |
michael@0 | 139 | /The `url` property must be a valid URL/.test(reason), |
michael@0 | 140 | 'Error event called with correct reason'); |
michael@0 | 141 | assert.equal(item, bookmarks[1], 'returns input that failed in event'); |
michael@0 | 142 | errorCount++; |
michael@0 | 143 | }).on('end', items => { |
michael@0 | 144 | assert.equal(dataCount, 2, 'data event called twice'); |
michael@0 | 145 | assert.equal(errorCount, 1, 'error event called once'); |
michael@0 | 146 | assert.equal(items.length, bookmarks.length, 'all items should be in result'); |
michael@0 | 147 | assert.equal(items[0].toString(), '[object Bookmark]', |
michael@0 | 148 | 'should be a saved instance'); |
michael@0 | 149 | assert.equal(items[2].toString(), '[object Bookmark]', |
michael@0 | 150 | 'should be a saved instance'); |
michael@0 | 151 | assert.equal(items[1], bookmarks[1], 'should be original, unsaved object'); |
michael@0 | 152 | |
michael@0 | 153 | search({ query: 'moz' }).on('end', items => { |
michael@0 | 154 | assert.equal(items.length, 2, 'only two items were successfully saved'); |
michael@0 | 155 | bookmarks[1].url = 'http://moz2.com/'; |
michael@0 | 156 | dataCount = errorCount = 0; |
michael@0 | 157 | save(bookmarks).on('data', bookmark => { |
michael@0 | 158 | dataCount++; |
michael@0 | 159 | }).on('error', reason => errorCount++) |
michael@0 | 160 | .on('end', items => { |
michael@0 | 161 | assert.equal(items.length, 3, 'all 3 items saved'); |
michael@0 | 162 | assert.equal(dataCount, 3, '3 data events called'); |
michael@0 | 163 | assert.equal(errorCount, 0, 'no error events called'); |
michael@0 | 164 | search({ query: 'moz' }).on('end', items => { |
michael@0 | 165 | assert.equal(items.length, 3, 'only 3 items saved'); |
michael@0 | 166 | items.map(item => |
michael@0 | 167 | assert.ok(/moz\d\.com/.test(item.url), 'correct item')) |
michael@0 | 168 | done(); |
michael@0 | 169 | }); |
michael@0 | 170 | }); |
michael@0 | 171 | }); |
michael@0 | 172 | }); |
michael@0 | 173 | }; |
michael@0 | 174 | |
michael@0 | 175 | exports.testSaveDucktypes = function (assert, done) { |
michael@0 | 176 | save({ |
michael@0 | 177 | title: 'moz', |
michael@0 | 178 | url: 'http://mozilla.org', |
michael@0 | 179 | type: 'bookmark' |
michael@0 | 180 | }).on('data', (bookmark) => { |
michael@0 | 181 | compareWithHost(assert, bookmark); |
michael@0 | 182 | done(); |
michael@0 | 183 | }); |
michael@0 | 184 | }; |
michael@0 | 185 | |
michael@0 | 186 | exports.testSaveDucktypesParent = function (assert, done) { |
michael@0 | 187 | let folder = { title: 'myfolder', type: 'group' }; |
michael@0 | 188 | let bookmark = { title: 'mozzie', url: 'http://moz.com', group: folder, type: 'bookmark' }; |
michael@0 | 189 | let sep = { type: 'separator', group: folder }; |
michael@0 | 190 | save([sep, bookmark]).on('end', (res) => { |
michael@0 | 191 | compareWithHost(assert, res[0]); |
michael@0 | 192 | compareWithHost(assert, res[1]); |
michael@0 | 193 | assert.equal(res[0].group.title, 'myfolder', 'parent is ducktyped group'); |
michael@0 | 194 | assert.equal(res[1].group.title, 'myfolder', 'parent is ducktyped group'); |
michael@0 | 195 | done(); |
michael@0 | 196 | }); |
michael@0 | 197 | }; |
michael@0 | 198 | |
michael@0 | 199 | /* |
michael@0 | 200 | * Tests the scenario where the original bookmark item is resaved |
michael@0 | 201 | * and does not have an ID or an updated date, but should still be |
michael@0 | 202 | * mapped to the item it created previously |
michael@0 | 203 | */ |
michael@0 | 204 | exports.testResaveOriginalItemMapping = function (assert, done) { |
michael@0 | 205 | let bookmark = Bookmark({ title: 'moz', url: 'http://moz.org' }); |
michael@0 | 206 | save(bookmark).on('data', newBookmark => { |
michael@0 | 207 | bookmark.title = 'new moz'; |
michael@0 | 208 | save(bookmark).on('data', newNewBookmark => { |
michael@0 | 209 | assert.equal(newBookmark.id, newNewBookmark.id, 'should be the same bookmark item'); |
michael@0 | 210 | assert.equal(bmsrv.getItemTitle(newBookmark.id), 'new moz', 'should have updated title'); |
michael@0 | 211 | done(); |
michael@0 | 212 | }); |
michael@0 | 213 | }); |
michael@0 | 214 | }; |
michael@0 | 215 | |
michael@0 | 216 | exports.testCreateMultipleBookmarks = function (assert, done) { |
michael@0 | 217 | let data = [ |
michael@0 | 218 | Bookmark({title: 'bm1', url: 'http://bm1.com'}), |
michael@0 | 219 | Bookmark({title: 'bm2', url: 'http://bm2.com'}), |
michael@0 | 220 | Bookmark({title: 'bm3', url: 'http://bm3.com'}), |
michael@0 | 221 | ]; |
michael@0 | 222 | save(data).on('data', function (bookmark, input) { |
michael@0 | 223 | let stored = data.filter(({title}) => title === bookmark.title)[0]; |
michael@0 | 224 | assert.equal(input, stored, 'input is original input item'); |
michael@0 | 225 | assert.equal(bookmark.title, stored.title, 'titles match'); |
michael@0 | 226 | assert.equal(bookmark.url, stored.url, 'urls match'); |
michael@0 | 227 | compareWithHost(assert, bookmark); |
michael@0 | 228 | }).on('end', function (bookmarks) { |
michael@0 | 229 | assert.equal(bookmarks.length, 3, 'all bookmarks returned'); |
michael@0 | 230 | done(); |
michael@0 | 231 | }); |
michael@0 | 232 | }; |
michael@0 | 233 | |
michael@0 | 234 | exports.testCreateImplicitParent = function (assert, done) { |
michael@0 | 235 | let folder = Group({ title: 'my parent' }); |
michael@0 | 236 | let bookmarks = [ |
michael@0 | 237 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: folder }), |
michael@0 | 238 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: folder }), |
michael@0 | 239 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: folder }) |
michael@0 | 240 | ]; |
michael@0 | 241 | save(bookmarks).on('data', function (bookmark) { |
michael@0 | 242 | if (bookmark.type === 'bookmark') { |
michael@0 | 243 | assert.equal(bookmark.group.title, folder.title, 'parent is linked'); |
michael@0 | 244 | compareWithHost(assert, bookmark); |
michael@0 | 245 | } else if (bookmark.type === 'group') { |
michael@0 | 246 | assert.equal(bookmark.group.id, UNSORTED.id, 'parent ID of group is correct'); |
michael@0 | 247 | compareWithHost(assert, bookmark); |
michael@0 | 248 | } |
michael@0 | 249 | }).on('end', function (results) { |
michael@0 | 250 | assert.equal(results.length, 3, 'results should only hold explicit saves'); |
michael@0 | 251 | done(); |
michael@0 | 252 | }); |
michael@0 | 253 | }; |
michael@0 | 254 | |
michael@0 | 255 | exports.testCreateExplicitParent = function (assert, done) { |
michael@0 | 256 | let folder = Group({ title: 'my parent' }); |
michael@0 | 257 | let bookmarks = [ |
michael@0 | 258 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: folder }), |
michael@0 | 259 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: folder }), |
michael@0 | 260 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: folder }) |
michael@0 | 261 | ]; |
michael@0 | 262 | save(bookmarks.concat(folder)).on('data', function (bookmark) { |
michael@0 | 263 | if (bookmark.type === 'bookmark') { |
michael@0 | 264 | assert.equal(bookmark.group.title, folder.title, 'parent is linked'); |
michael@0 | 265 | compareWithHost(assert, bookmark); |
michael@0 | 266 | } else if (bookmark.type === 'group') { |
michael@0 | 267 | assert.equal(bookmark.group.id, UNSORTED.id, 'parent ID of group is correct'); |
michael@0 | 268 | compareWithHost(assert, bookmark); |
michael@0 | 269 | } |
michael@0 | 270 | }).on('end', function () { |
michael@0 | 271 | done(); |
michael@0 | 272 | }); |
michael@0 | 273 | }; |
michael@0 | 274 | |
michael@0 | 275 | exports.testCreateNested = function (assert, done) { |
michael@0 | 276 | let topFolder = Group({ title: 'top', group: MENU }); |
michael@0 | 277 | let midFolder = Group({ title: 'middle', group: topFolder }); |
michael@0 | 278 | let bookmarks = [ |
michael@0 | 279 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder }), |
michael@0 | 280 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder }), |
michael@0 | 281 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: midFolder }) |
michael@0 | 282 | ]; |
michael@0 | 283 | let dataEventCount = 0; |
michael@0 | 284 | save(bookmarks).on('data', function (bookmark) { |
michael@0 | 285 | if (bookmark.type === 'bookmark') { |
michael@0 | 286 | assert.equal(bookmark.group.title, midFolder.title, 'parent is linked'); |
michael@0 | 287 | } else if (bookmark.title === 'top') { |
michael@0 | 288 | assert.equal(bookmark.group.id, MENU.id, 'parent ID of top group is correct'); |
michael@0 | 289 | } else { |
michael@0 | 290 | assert.equal(bookmark.group.title, topFolder.title, 'parent title of middle group is correct'); |
michael@0 | 291 | } |
michael@0 | 292 | dataEventCount++; |
michael@0 | 293 | compareWithHost(assert, bookmark); |
michael@0 | 294 | }).on('end', () => { |
michael@0 | 295 | assert.equal(dataEventCount, 5, 'data events for all saves have occurred'); |
michael@0 | 296 | assert.ok('end event called'); |
michael@0 | 297 | done(); |
michael@0 | 298 | }); |
michael@0 | 299 | }; |
michael@0 | 300 | |
michael@0 | 301 | /* |
michael@0 | 302 | * Was a scenario when implicitly saving a bookmark that was already created, |
michael@0 | 303 | * it was not being properly fetched and attempted to recreate |
michael@0 | 304 | */ |
michael@0 | 305 | exports.testAddingToExistingParent = function (assert, done) { |
michael@0 | 306 | let group = { type: 'group', title: 'mozgroup' }; |
michael@0 | 307 | let bookmarks = [ |
michael@0 | 308 | { title: 'moz1', url: 'http://moz1.com', type: 'bookmark', group: group }, |
michael@0 | 309 | { title: 'moz2', url: 'http://moz2.com', type: 'bookmark', group: group }, |
michael@0 | 310 | { title: 'moz3', url: 'http://moz3.com', type: 'bookmark', group: group } |
michael@0 | 311 | ], |
michael@0 | 312 | firstBatch, secondBatch; |
michael@0 | 313 | |
michael@0 | 314 | saveP(bookmarks).then(data => { |
michael@0 | 315 | firstBatch = data; |
michael@0 | 316 | return saveP([ |
michael@0 | 317 | { title: 'moz4', url: 'http://moz4.com', type: 'bookmark', group: group }, |
michael@0 | 318 | { title: 'moz5', url: 'http://moz5.com', type: 'bookmark', group: group } |
michael@0 | 319 | ]); |
michael@0 | 320 | }, assert.fail).then(data => { |
michael@0 | 321 | secondBatch = data; |
michael@0 | 322 | assert.equal(firstBatch[0].group.id, secondBatch[0].group.id, |
michael@0 | 323 | 'successfully saved to the same parent'); |
michael@0 | 324 | done(); |
michael@0 | 325 | }, assert.fail); |
michael@0 | 326 | }; |
michael@0 | 327 | |
michael@0 | 328 | exports.testUpdateParent = function (assert, done) { |
michael@0 | 329 | let group = { type: 'group', title: 'mozgroup' }; |
michael@0 | 330 | saveP(group).then(item => { |
michael@0 | 331 | item[0].title = 'mozgroup-resave'; |
michael@0 | 332 | return saveP(item[0]); |
michael@0 | 333 | }).then(item => { |
michael@0 | 334 | assert.equal(item[0].title, 'mozgroup-resave', 'group saved successfully'); |
michael@0 | 335 | done(); |
michael@0 | 336 | }); |
michael@0 | 337 | }; |
michael@0 | 338 | |
michael@0 | 339 | exports.testUpdateSeparator = function (assert, done) { |
michael@0 | 340 | let sep = [Separator(), Separator(), Separator()]; |
michael@0 | 341 | saveP(sep).then(item => { |
michael@0 | 342 | item[0].index = 2; |
michael@0 | 343 | return saveP(item[0]); |
michael@0 | 344 | }).then(item => { |
michael@0 | 345 | assert.equal(item[0].index, 2, 'updated index of separator'); |
michael@0 | 346 | done(); |
michael@0 | 347 | }); |
michael@0 | 348 | }; |
michael@0 | 349 | |
michael@0 | 350 | exports.testPromisedSave = function (assert, done) { |
michael@0 | 351 | let topFolder = Group({ title: 'top', group: MENU }); |
michael@0 | 352 | let midFolder = Group({ title: 'middle', group: topFolder }); |
michael@0 | 353 | let bookmarks = [ |
michael@0 | 354 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder}), |
michael@0 | 355 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder}), |
michael@0 | 356 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: midFolder}) |
michael@0 | 357 | ]; |
michael@0 | 358 | let first, second, third; |
michael@0 | 359 | saveP(bookmarks).then(bms => { |
michael@0 | 360 | first = bms.filter(b => b.title === 'moz1')[0]; |
michael@0 | 361 | second = bms.filter(b => b.title === 'moz2')[0]; |
michael@0 | 362 | third = bms.filter(b => b.title === 'moz3')[0]; |
michael@0 | 363 | assert.equal(first.index, 0); |
michael@0 | 364 | assert.equal(second.index, 1); |
michael@0 | 365 | assert.equal(third.index, 2); |
michael@0 | 366 | first.index = 3; |
michael@0 | 367 | return saveP(first); |
michael@0 | 368 | }).then(() => { |
michael@0 | 369 | assert.equal(bmsrv.getItemIndex(first.id), 2, 'properly moved bookmark'); |
michael@0 | 370 | assert.equal(bmsrv.getItemIndex(second.id), 0, 'other bookmarks adjusted'); |
michael@0 | 371 | assert.equal(bmsrv.getItemIndex(third.id), 1, 'other bookmarks adjusted'); |
michael@0 | 372 | done(); |
michael@0 | 373 | }); |
michael@0 | 374 | }; |
michael@0 | 375 | |
michael@0 | 376 | exports.testPromisedErrorSave = function (assert, done) { |
michael@0 | 377 | let bookmarks = [ |
michael@0 | 378 | { title: 'moz1', url: 'http://moz1.com', type: 'bookmark'}, |
michael@0 | 379 | { title: 'moz2', url: 'invalidurl', type: 'bookmark'}, |
michael@0 | 380 | { title: 'moz3', url: 'http://moz3.com', type: 'bookmark'} |
michael@0 | 381 | ]; |
michael@0 | 382 | saveP(bookmarks).then(invalidResolve, reason => { |
michael@0 | 383 | assert.ok( |
michael@0 | 384 | /The `url` property must be a valid URL/.test(reason), |
michael@0 | 385 | 'Error event called with correct reason'); |
michael@0 | 386 | |
michael@0 | 387 | bookmarks[1].url = 'http://moz2.com'; |
michael@0 | 388 | return saveP(bookmarks); |
michael@0 | 389 | }).then(res => { |
michael@0 | 390 | return searchP({ query: 'moz' }); |
michael@0 | 391 | }).then(res => { |
michael@0 | 392 | assert.equal(res.length, 3, 'all 3 should be saved upon retry'); |
michael@0 | 393 | res.map(item => assert.ok(/moz\d\.com/.test(item.url), 'correct item')); |
michael@0 | 394 | done(); |
michael@0 | 395 | }, invalidReject); |
michael@0 | 396 | }; |
michael@0 | 397 | |
michael@0 | 398 | exports.testMovingChildren = function (assert, done) { |
michael@0 | 399 | let topFolder = Group({ title: 'top', group: MENU }); |
michael@0 | 400 | let midFolder = Group({ title: 'middle', group: topFolder }); |
michael@0 | 401 | let bookmarks = [ |
michael@0 | 402 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder}), |
michael@0 | 403 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder}), |
michael@0 | 404 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: midFolder}) |
michael@0 | 405 | ]; |
michael@0 | 406 | save(bookmarks).on('end', bms => { |
michael@0 | 407 | let first = bms.filter(b => b.title === 'moz1')[0]; |
michael@0 | 408 | let second = bms.filter(b => b.title === 'moz2')[0]; |
michael@0 | 409 | let third = bms.filter(b => b.title === 'moz3')[0]; |
michael@0 | 410 | assert.equal(first.index, 0); |
michael@0 | 411 | assert.equal(second.index, 1); |
michael@0 | 412 | assert.equal(third.index, 2); |
michael@0 | 413 | /* When moving down in the same container we take |
michael@0 | 414 | * into account the removal of the original item. If you want |
michael@0 | 415 | * to move from index X to index Y > X you must use |
michael@0 | 416 | * moveItem(id, folder, Y + 1) |
michael@0 | 417 | */ |
michael@0 | 418 | first.index = 3; |
michael@0 | 419 | save(first).on('end', () => { |
michael@0 | 420 | assert.equal(bmsrv.getItemIndex(first.id), 2, 'properly moved bookmark'); |
michael@0 | 421 | assert.equal(bmsrv.getItemIndex(second.id), 0, 'other bookmarks adjusted'); |
michael@0 | 422 | assert.equal(bmsrv.getItemIndex(third.id), 1, 'other bookmarks adjusted'); |
michael@0 | 423 | done(); |
michael@0 | 424 | }); |
michael@0 | 425 | }); |
michael@0 | 426 | }; |
michael@0 | 427 | |
michael@0 | 428 | exports.testMovingChildrenNewFolder = function (assert, done) { |
michael@0 | 429 | let topFolder = Group({ title: 'top', group: MENU }); |
michael@0 | 430 | let midFolder = Group({ title: 'middle', group: topFolder }); |
michael@0 | 431 | let newFolder = Group({ title: 'new', group: MENU }); |
michael@0 | 432 | let bookmarks = [ |
michael@0 | 433 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder}), |
michael@0 | 434 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder}), |
michael@0 | 435 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: midFolder}) |
michael@0 | 436 | ]; |
michael@0 | 437 | save(bookmarks).on('end', bms => { |
michael@0 | 438 | let first = bms.filter(b => b.title === 'moz1')[0]; |
michael@0 | 439 | let second = bms.filter(b => b.title === 'moz2')[0]; |
michael@0 | 440 | let third = bms.filter(b => b.title === 'moz3')[0]; |
michael@0 | 441 | let definedMidFolder = first.group; |
michael@0 | 442 | let definedNewFolder; |
michael@0 | 443 | first.group = newFolder; |
michael@0 | 444 | assert.equal(first.index, 0); |
michael@0 | 445 | assert.equal(second.index, 1); |
michael@0 | 446 | assert.equal(third.index, 2); |
michael@0 | 447 | save(first).on('data', (data) => { |
michael@0 | 448 | if (data.type === 'group') definedNewFolder = data; |
michael@0 | 449 | }).on('end', (moved) => { |
michael@0 | 450 | assert.equal(bmsrv.getItemIndex(second.id), 0, 'other bookmarks adjusted'); |
michael@0 | 451 | assert.equal(bmsrv.getItemIndex(third.id), 1, 'other bookmarks adjusted'); |
michael@0 | 452 | assert.equal(bmsrv.getItemIndex(first.id), 0, 'properly moved bookmark'); |
michael@0 | 453 | assert.equal(bmsrv.getFolderIdForItem(first.id), definedNewFolder.id, |
michael@0 | 454 | 'bookmark has new parent'); |
michael@0 | 455 | assert.equal(bmsrv.getFolderIdForItem(second.id), definedMidFolder.id, |
michael@0 | 456 | 'sibling bookmarks did not move'); |
michael@0 | 457 | assert.equal(bmsrv.getFolderIdForItem(third.id), definedMidFolder.id, |
michael@0 | 458 | 'sibling bookmarks did not move'); |
michael@0 | 459 | done(); |
michael@0 | 460 | }); |
michael@0 | 461 | }); |
michael@0 | 462 | }; |
michael@0 | 463 | |
michael@0 | 464 | exports.testRemoveFunction = function (assert) { |
michael@0 | 465 | let topFolder = Group({ title: 'new', group: MENU }); |
michael@0 | 466 | let midFolder = Group({ title: 'middle', group: topFolder }); |
michael@0 | 467 | let bookmarks = [ |
michael@0 | 468 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder}), |
michael@0 | 469 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder}), |
michael@0 | 470 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: midFolder}) |
michael@0 | 471 | ]; |
michael@0 | 472 | remove([midFolder, topFolder].concat(bookmarks)).map(item => { |
michael@0 | 473 | assert.equal(item.remove, true, 'remove toggled `remove` property to true'); |
michael@0 | 474 | }); |
michael@0 | 475 | }; |
michael@0 | 476 | |
michael@0 | 477 | exports.testRemove = function (assert, done) { |
michael@0 | 478 | let id; |
michael@0 | 479 | createBookmarkItem().then(data => { |
michael@0 | 480 | id = data.id; |
michael@0 | 481 | compareWithHost(assert, data); // ensure bookmark exists |
michael@0 | 482 | save(remove(data)).on('data', (res) => { |
michael@0 | 483 | assert.pass('data event should be called'); |
michael@0 | 484 | assert.ok(!res, 'response should be empty'); |
michael@0 | 485 | }).on('end', () => { |
michael@0 | 486 | assert.throws(function () { |
michael@0 | 487 | bmsrv.getItemTitle(id); |
michael@0 | 488 | }, 'item should no longer exist'); |
michael@0 | 489 | done(); |
michael@0 | 490 | }); |
michael@0 | 491 | }); |
michael@0 | 492 | }; |
michael@0 | 493 | |
michael@0 | 494 | /* |
michael@0 | 495 | * Tests recursively removing children when removing a group |
michael@0 | 496 | */ |
michael@0 | 497 | exports.testRemoveAllChildren = function (assert, done) { |
michael@0 | 498 | let topFolder = Group({ title: 'new', group: MENU }); |
michael@0 | 499 | let midFolder = Group({ title: 'middle', group: topFolder }); |
michael@0 | 500 | let bookmarks = [ |
michael@0 | 501 | Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder}), |
michael@0 | 502 | Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder}), |
michael@0 | 503 | Bookmark({ title: 'moz3', url: 'http://moz3.com', group: midFolder}) |
michael@0 | 504 | ]; |
michael@0 | 505 | |
michael@0 | 506 | let saved = []; |
michael@0 | 507 | save(bookmarks).on('data', (data) => saved.push(data)).on('end', () => { |
michael@0 | 508 | save(remove(topFolder)).on('end', () => { |
michael@0 | 509 | assert.equal(saved.length, 5, 'all items should have been saved'); |
michael@0 | 510 | saved.map((item) => { |
michael@0 | 511 | assert.throws(function () { |
michael@0 | 512 | bmsrv.getItemTitle(item.id); |
michael@0 | 513 | }, 'item should no longer exist'); |
michael@0 | 514 | }); |
michael@0 | 515 | done(); |
michael@0 | 516 | }); |
michael@0 | 517 | }); |
michael@0 | 518 | }; |
michael@0 | 519 | |
michael@0 | 520 | exports.testResolution = function (assert, done) { |
michael@0 | 521 | let firstSave, secondSave; |
michael@0 | 522 | createBookmarkItem().then((item) => { |
michael@0 | 523 | firstSave = item; |
michael@0 | 524 | assert.ok(item.updated, 'bookmark has updated time'); |
michael@0 | 525 | item.title = 'my title'; |
michael@0 | 526 | // Ensure delay so a different save time is set |
michael@0 | 527 | return delayed(item); |
michael@0 | 528 | }).then(saveP) |
michael@0 | 529 | .then(items => { |
michael@0 | 530 | let item = items[0]; |
michael@0 | 531 | secondSave = item; |
michael@0 | 532 | assert.ok(firstSave.updated < secondSave.updated, 'snapshots have different update times'); |
michael@0 | 533 | firstSave.title = 'updated title'; |
michael@0 | 534 | return saveP(firstSave, { resolve: (mine, theirs) => { |
michael@0 | 535 | assert.equal(mine.title, 'updated title', 'correct data for my object'); |
michael@0 | 536 | assert.equal(theirs.title, 'my title', 'correct data for their object'); |
michael@0 | 537 | assert.equal(mine.url, theirs.url, 'other data is equal'); |
michael@0 | 538 | assert.equal(mine.group, theirs.group, 'other data is equal'); |
michael@0 | 539 | assert.ok(mine !== firstSave, 'instance is not passed in'); |
michael@0 | 540 | assert.ok(theirs !== secondSave, 'instance is not passed in'); |
michael@0 | 541 | assert.equal(mine.toString(), '[object Object]', 'serialized objects'); |
michael@0 | 542 | assert.equal(theirs.toString(), '[object Object]', 'serialized objects'); |
michael@0 | 543 | mine.title = 'a new title'; |
michael@0 | 544 | return mine; |
michael@0 | 545 | }}); |
michael@0 | 546 | }).then((results) => { |
michael@0 | 547 | let result = results[0]; |
michael@0 | 548 | assert.equal(result.title, 'a new title', 'resolve handles results'); |
michael@0 | 549 | done(); |
michael@0 | 550 | }); |
michael@0 | 551 | }; |
michael@0 | 552 | |
michael@0 | 553 | /* |
michael@0 | 554 | * Same as the resolution test, but with the 'unsaved' snapshot |
michael@0 | 555 | */ |
michael@0 | 556 | exports.testResolutionMapping = function (assert, done) { |
michael@0 | 557 | let bookmark = Bookmark({ title: 'moz', url: 'http://bookmarks4life.com/' }); |
michael@0 | 558 | let saved; |
michael@0 | 559 | saveP(bookmark).then(data => { |
michael@0 | 560 | saved = data[0]; |
michael@0 | 561 | saved.title = 'updated title'; |
michael@0 | 562 | // Ensure a delay for different updated times |
michael@0 | 563 | return delayed(saved); |
michael@0 | 564 | }).then(saveP) |
michael@0 | 565 | .then(() => { |
michael@0 | 566 | bookmark.title = 'conflicting title'; |
michael@0 | 567 | return saveP(bookmark, { resolve: (mine, theirs) => { |
michael@0 | 568 | assert.equal(mine.title, 'conflicting title', 'correct data for my object'); |
michael@0 | 569 | assert.equal(theirs.title, 'updated title', 'correct data for their object'); |
michael@0 | 570 | assert.equal(mine.url, theirs.url, 'other data is equal'); |
michael@0 | 571 | assert.equal(mine.group, theirs.group, 'other data is equal'); |
michael@0 | 572 | assert.ok(mine !== bookmark, 'instance is not passed in'); |
michael@0 | 573 | assert.ok(theirs !== saved, 'instance is not passed in'); |
michael@0 | 574 | assert.equal(mine.toString(), '[object Object]', 'serialized objects'); |
michael@0 | 575 | assert.equal(theirs.toString(), '[object Object]', 'serialized objects'); |
michael@0 | 576 | mine.title = 'a new title'; |
michael@0 | 577 | return mine; |
michael@0 | 578 | }}); |
michael@0 | 579 | }).then((results) => { |
michael@0 | 580 | let result = results[0]; |
michael@0 | 581 | assert.equal(result.title, 'a new title', 'resolve handles results'); |
michael@0 | 582 | done(); |
michael@0 | 583 | }); |
michael@0 | 584 | }; |
michael@0 | 585 | |
michael@0 | 586 | exports.testUpdateTags = function (assert, done) { |
michael@0 | 587 | createBookmarkItem({ tags: ['spidermonkey'] }).then(bookmark => { |
michael@0 | 588 | bookmark.tags.add('jagermonkey'); |
michael@0 | 589 | bookmark.tags.add('ionmonkey'); |
michael@0 | 590 | bookmark.tags.delete('spidermonkey'); |
michael@0 | 591 | save(bookmark).on('data', saved => { |
michael@0 | 592 | assert.equal(saved.tags.size, 2, 'should have 2 tags'); |
michael@0 | 593 | assert.ok(saved.tags.has('jagermonkey'), 'should have added tag'); |
michael@0 | 594 | assert.ok(saved.tags.has('ionmonkey'), 'should have added tag'); |
michael@0 | 595 | assert.ok(!saved.tags.has('spidermonkey'), 'should not have removed tag'); |
michael@0 | 596 | done(); |
michael@0 | 597 | }); |
michael@0 | 598 | }); |
michael@0 | 599 | }; |
michael@0 | 600 | |
michael@0 | 601 | /* |
michael@0 | 602 | * View `createBookmarkTree` in `./places-helper.js` to see |
michael@0 | 603 | * expected tree construction |
michael@0 | 604 | */ |
michael@0 | 605 | |
michael@0 | 606 | exports.testSearchByGroupSimple = function (assert, done) { |
michael@0 | 607 | createBookmarkTree().then(() => { |
michael@0 | 608 | // In initial release of Places API, groups can only be queried |
michael@0 | 609 | // via a 'simple query', which is one folder set, and no other |
michael@0 | 610 | // parameters |
michael@0 | 611 | return searchP({ group: UNSORTED }); |
michael@0 | 612 | }).then(results => { |
michael@0 | 613 | let groups = results.filter(({type}) => type === 'group'); |
michael@0 | 614 | assert.equal(groups.length, 2, 'returns folders'); |
michael@0 | 615 | assert.equal(results.length, 7, |
michael@0 | 616 | 'should return all bookmarks and folders under UNSORTED'); |
michael@0 | 617 | assert.equal(groups[0].toString(), '[object Group]', 'returns instance'); |
michael@0 | 618 | return searchP({ |
michael@0 | 619 | group: groups.filter(({title}) => title === 'mozgroup')[0] |
michael@0 | 620 | }); |
michael@0 | 621 | }).then(results => { |
michael@0 | 622 | let groups = results.filter(({type}) => type === 'group'); |
michael@0 | 623 | assert.equal(groups.length, 1, 'returns one subfolder'); |
michael@0 | 624 | assert.equal(results.length, 6, |
michael@0 | 625 | 'returns all children bookmarks/folders'); |
michael@0 | 626 | assert.ok(results.filter(({url}) => url === 'http://w3schools.com/'), |
michael@0 | 627 | 'returns nested children'); |
michael@0 | 628 | done(); |
michael@0 | 629 | }).then(null, assert.fail); |
michael@0 | 630 | }; |
michael@0 | 631 | |
michael@0 | 632 | exports.testSearchByGroupComplex = function (assert, done) { |
michael@0 | 633 | let mozgroup; |
michael@0 | 634 | createBookmarkTree().then(results => { |
michael@0 | 635 | mozgroup = results.filter(({title}) => title === 'mozgroup')[0]; |
michael@0 | 636 | return searchP({ group: mozgroup, query: 'javascript' }); |
michael@0 | 637 | }).then(results => { |
michael@0 | 638 | assert.equal(results.length, 1, 'only one javascript result under mozgroup'); |
michael@0 | 639 | assert.equal(results[0].url, 'http://w3schools.com/', 'correct result'); |
michael@0 | 640 | return searchP({ group: mozgroup, url: '*.mozilla.org' }); |
michael@0 | 641 | }).then(results => { |
michael@0 | 642 | assert.equal(results.length, 2, 'expected results'); |
michael@0 | 643 | assert.ok( |
michael@0 | 644 | !results.filter(({url}) => /developer.mozilla/.test(url)).length, |
michael@0 | 645 | 'does not find results from other folders'); |
michael@0 | 646 | done(); |
michael@0 | 647 | }, assert.fail); |
michael@0 | 648 | }; |
michael@0 | 649 | |
michael@0 | 650 | exports.testSearchEmitters = function (assert, done) { |
michael@0 | 651 | createBookmarkTree().then(() => { |
michael@0 | 652 | let count = 0; |
michael@0 | 653 | search({ tags: ['mozilla', 'firefox'] }).on('data', data => { |
michael@0 | 654 | assert.ok(/mozilla|firefox/.test(data.title), 'one of the correct items'); |
michael@0 | 655 | assert.ok(data.tags.has('firefox'), 'has firefox tag'); |
michael@0 | 656 | assert.ok(data.tags.has('mozilla'), 'has mozilla tag'); |
michael@0 | 657 | assert.equal(data + '', '[object Bookmark]', 'returns bookmark'); |
michael@0 | 658 | count++; |
michael@0 | 659 | }).on('end', data => { |
michael@0 | 660 | assert.equal(count, 3, 'data event was called for each item'); |
michael@0 | 661 | assert.equal(data.length, 3, |
michael@0 | 662 | 'should return two bookmarks that have both mozilla AND firefox'); |
michael@0 | 663 | assert.equal(data[0].title, 'mozilla.com', 'returns correct bookmark'); |
michael@0 | 664 | assert.equal(data[1].title, 'mozilla.org', 'returns correct bookmark'); |
michael@0 | 665 | assert.equal(data[2].title, 'firefox', 'returns correct bookmark'); |
michael@0 | 666 | assert.equal(data[0] + '', '[object Bookmark]', 'returns bookmarks'); |
michael@0 | 667 | done(); |
michael@0 | 668 | }); |
michael@0 | 669 | }); |
michael@0 | 670 | }; |
michael@0 | 671 | |
michael@0 | 672 | exports.testSearchTags = function (assert, done) { |
michael@0 | 673 | createBookmarkTree().then(() => { |
michael@0 | 674 | // AND tags |
michael@0 | 675 | return searchP({ tags: ['mozilla', 'firefox'] }); |
michael@0 | 676 | }).then(data => { |
michael@0 | 677 | assert.equal(data.length, 3, |
michael@0 | 678 | 'should return two bookmarks that have both mozilla AND firefox'); |
michael@0 | 679 | assert.equal(data[0].title, 'mozilla.com', 'returns correct bookmark'); |
michael@0 | 680 | assert.equal(data[1].title, 'mozilla.org', 'returns correct bookmark'); |
michael@0 | 681 | assert.equal(data[2].title, 'firefox', 'returns correct bookmark'); |
michael@0 | 682 | assert.equal(data[0] + '', '[object Bookmark]', 'returns bookmarks'); |
michael@0 | 683 | return searchP([{tags: ['firefox']}, {tags: ['javascript']}]); |
michael@0 | 684 | }).then(data => { |
michael@0 | 685 | // OR tags |
michael@0 | 686 | assert.equal(data.length, 6, |
michael@0 | 687 | 'should return all bookmarks with firefox OR javascript tag'); |
michael@0 | 688 | done(); |
michael@0 | 689 | }); |
michael@0 | 690 | }; |
michael@0 | 691 | |
michael@0 | 692 | /* |
michael@0 | 693 | * Tests 4 scenarios |
michael@0 | 694 | * '*.mozilla.com' |
michael@0 | 695 | * 'mozilla.com' |
michael@0 | 696 | * 'http://mozilla.com/' |
michael@0 | 697 | * 'http://mozilla.com/*' |
michael@0 | 698 | */ |
michael@0 | 699 | exports.testSearchURL = function (assert, done) { |
michael@0 | 700 | createBookmarkTree().then(() => { |
michael@0 | 701 | return searchP({ url: 'mozilla.org' }); |
michael@0 | 702 | }).then(data => { |
michael@0 | 703 | assert.equal(data.length, 2, 'only URLs with host domain'); |
michael@0 | 704 | assert.equal(data[0].url, 'http://mozilla.org/'); |
michael@0 | 705 | assert.equal(data[1].url, 'http://mozilla.org/thunderbird/'); |
michael@0 | 706 | return searchP({ url: '*.mozilla.org' }); |
michael@0 | 707 | }).then(data => { |
michael@0 | 708 | assert.equal(data.length, 3, 'returns domain and when host is other than domain'); |
michael@0 | 709 | assert.equal(data[0].url, 'http://mozilla.org/'); |
michael@0 | 710 | assert.equal(data[1].url, 'http://mozilla.org/thunderbird/'); |
michael@0 | 711 | assert.equal(data[2].url, 'http://developer.mozilla.org/en-US/'); |
michael@0 | 712 | return searchP({ url: 'http://mozilla.org' }); |
michael@0 | 713 | }).then(data => { |
michael@0 | 714 | assert.equal(data.length, 1, 'only exact URL match'); |
michael@0 | 715 | assert.equal(data[0].url, 'http://mozilla.org/'); |
michael@0 | 716 | return searchP({ url: 'http://mozilla.org/*' }); |
michael@0 | 717 | }).then(data => { |
michael@0 | 718 | assert.equal(data.length, 2, 'only URLs that begin with query'); |
michael@0 | 719 | assert.equal(data[0].url, 'http://mozilla.org/'); |
michael@0 | 720 | assert.equal(data[1].url, 'http://mozilla.org/thunderbird/'); |
michael@0 | 721 | return searchP([{ url: 'mozilla.org' }, { url: 'component.fm' }]); |
michael@0 | 722 | }).then(data => { |
michael@0 | 723 | assert.equal(data.length, 3, 'returns URLs that match EITHER query'); |
michael@0 | 724 | assert.equal(data[0].url, 'http://mozilla.org/'); |
michael@0 | 725 | assert.equal(data[1].url, 'http://mozilla.org/thunderbird/'); |
michael@0 | 726 | assert.equal(data[2].url, 'http://component.fm/'); |
michael@0 | 727 | }).then(() => { |
michael@0 | 728 | done(); |
michael@0 | 729 | }); |
michael@0 | 730 | }; |
michael@0 | 731 | |
michael@0 | 732 | /* |
michael@0 | 733 | * Searches url, title, tags |
michael@0 | 734 | */ |
michael@0 | 735 | exports.testSearchQuery = function (assert, done) { |
michael@0 | 736 | createBookmarkTree().then(() => { |
michael@0 | 737 | return searchP({ query: 'thunder' }); |
michael@0 | 738 | }).then(data => { |
michael@0 | 739 | assert.equal(data.length, 3); |
michael@0 | 740 | assert.equal(data[0].title, 'mozilla.com', 'query matches tag, url, or title'); |
michael@0 | 741 | assert.equal(data[1].title, 'mozilla.org', 'query matches tag, url, or title'); |
michael@0 | 742 | assert.equal(data[2].title, 'thunderbird', 'query matches tag, url, or title'); |
michael@0 | 743 | return searchP([{ query: 'rust' }, { query: 'component' }]); |
michael@0 | 744 | }).then(data => { |
michael@0 | 745 | // rust OR component |
michael@0 | 746 | assert.equal(data.length, 3); |
michael@0 | 747 | assert.equal(data[0].title, 'mozilla.com', 'query matches tag, url, or title'); |
michael@0 | 748 | assert.equal(data[1].title, 'mozilla.org', 'query matches tag, url, or title'); |
michael@0 | 749 | assert.equal(data[2].title, 'web audio components', 'query matches tag, url, or title'); |
michael@0 | 750 | return searchP([{ query: 'moz', tags: ['javascript']}]); |
michael@0 | 751 | }).then(data => { |
michael@0 | 752 | assert.equal(data.length, 1); |
michael@0 | 753 | assert.equal(data[0].title, 'mdn', |
michael@0 | 754 | 'only one item matches moz query AND has a javascript tag'); |
michael@0 | 755 | }).then(() => { |
michael@0 | 756 | done(); |
michael@0 | 757 | }); |
michael@0 | 758 | }; |
michael@0 | 759 | |
michael@0 | 760 | /* |
michael@0 | 761 | * Test caching on bulk calls. |
michael@0 | 762 | * Each construction of a bookmark item snapshot results in |
michael@0 | 763 | * the recursive lookup of parent groups up to the root groups -- |
michael@0 | 764 | * ensure that the appropriate instances equal each other, and no duplicate |
michael@0 | 765 | * fetches are called |
michael@0 | 766 | * |
michael@0 | 767 | * Implementation-dependent, this checks the host event `sdk-places-bookmarks-get`, |
michael@0 | 768 | * and if implementation changes, this could increase or decrease |
michael@0 | 769 | */ |
michael@0 | 770 | |
michael@0 | 771 | exports.testCaching = function (assert, done) { |
michael@0 | 772 | let count = 0; |
michael@0 | 773 | let stream = filter(request, ({event}) => |
michael@0 | 774 | /sdk-places-bookmarks-get/.test(event)); |
michael@0 | 775 | on(stream, 'data', handle); |
michael@0 | 776 | |
michael@0 | 777 | let group = { type: 'group', title: 'mozgroup' }; |
michael@0 | 778 | let bookmarks = [ |
michael@0 | 779 | { title: 'moz1', url: 'http://moz1.com', type: 'bookmark', group: group }, |
michael@0 | 780 | { title: 'moz2', url: 'http://moz2.com', type: 'bookmark', group: group }, |
michael@0 | 781 | { title: 'moz3', url: 'http://moz3.com', type: 'bookmark', group: group } |
michael@0 | 782 | ]; |
michael@0 | 783 | |
michael@0 | 784 | /* |
michael@0 | 785 | * Use timeout in tests since the platform calls are synchronous |
michael@0 | 786 | * and the counting event shim may not have occurred yet |
michael@0 | 787 | */ |
michael@0 | 788 | |
michael@0 | 789 | saveP(bookmarks).then(() => { |
michael@0 | 790 | assert.equal(count, 0, 'all new items and root group, no fetches should occur'); |
michael@0 | 791 | count = 0; |
michael@0 | 792 | return saveP([ |
michael@0 | 793 | { title: 'moz4', url: 'http://moz4.com', type: 'bookmark', group: group }, |
michael@0 | 794 | { title: 'moz5', url: 'http://moz5.com', type: 'bookmark', group: group } |
michael@0 | 795 | ]); |
michael@0 | 796 | // Test `save` look-up |
michael@0 | 797 | }).then(() => { |
michael@0 | 798 | assert.equal(count, 1, 'should only look up parent once'); |
michael@0 | 799 | count = 0; |
michael@0 | 800 | return searchP({ query: 'moz' }); |
michael@0 | 801 | }).then(results => { |
michael@0 | 802 | // Should query for each bookmark (5) from the query (id -> data), |
michael@0 | 803 | // their parent during `construct` (1) and the root shouldn't |
michael@0 | 804 | // require a lookup |
michael@0 | 805 | assert.equal(count, 6, 'lookup occurs once for each item and parent'); |
michael@0 | 806 | off(stream, 'data', handle); |
michael@0 | 807 | done(); |
michael@0 | 808 | }); |
michael@0 | 809 | |
michael@0 | 810 | function handle ({data}) count++ |
michael@0 | 811 | }; |
michael@0 | 812 | |
michael@0 | 813 | /* |
michael@0 | 814 | * Search Query Options |
michael@0 | 815 | */ |
michael@0 | 816 | |
michael@0 | 817 | exports.testSearchCount = function (assert, done) { |
michael@0 | 818 | let max = 8; |
michael@0 | 819 | createBookmarkTree() |
michael@0 | 820 | .then(testCount(1)) |
michael@0 | 821 | .then(testCount(2)) |
michael@0 | 822 | .then(testCount(3)) |
michael@0 | 823 | .then(testCount(5)) |
michael@0 | 824 | .then(testCount(10)) |
michael@0 | 825 | .then(() => { |
michael@0 | 826 | done(); |
michael@0 | 827 | }); |
michael@0 | 828 | |
michael@0 | 829 | function testCount (n) { |
michael@0 | 830 | return function () { |
michael@0 | 831 | return searchP({}, { count: n }).then(results => { |
michael@0 | 832 | if (n > max) n = max; |
michael@0 | 833 | assert.equal(results.length, n, |
michael@0 | 834 | 'count ' + n + ' returns ' + n + ' results'); |
michael@0 | 835 | }); |
michael@0 | 836 | }; |
michael@0 | 837 | } |
michael@0 | 838 | }; |
michael@0 | 839 | |
michael@0 | 840 | exports.testSearchSort = function (assert, done) { |
michael@0 | 841 | let urls = [ |
michael@0 | 842 | 'http://mozilla.com/', 'http://webaud.io/', 'http://mozilla.com/webfwd/', |
michael@0 | 843 | 'http://developer.mozilla.com/', 'http://bandcamp.com/' |
michael@0 | 844 | ]; |
michael@0 | 845 | |
michael@0 | 846 | saveP( |
michael@0 | 847 | urls.map(url => |
michael@0 | 848 | Bookmark({ url: url, title: url.replace(/http:\/\/|\//g,'')})) |
michael@0 | 849 | ).then(() => { |
michael@0 | 850 | return searchP({}, { sort: 'title' }); |
michael@0 | 851 | }).then(results => { |
michael@0 | 852 | checkOrder(results, [4,3,0,2,1]); |
michael@0 | 853 | return searchP({}, { sort: 'title', descending: true }); |
michael@0 | 854 | }).then(results => { |
michael@0 | 855 | checkOrder(results, [1,2,0,3,4]); |
michael@0 | 856 | return searchP({}, { sort: 'url' }); |
michael@0 | 857 | }).then(results => { |
michael@0 | 858 | checkOrder(results, [4,3,0,2,1]); |
michael@0 | 859 | return searchP({}, { sort: 'url', descending: true }); |
michael@0 | 860 | }).then(results => { |
michael@0 | 861 | checkOrder(results, [1,2,0,3,4]); |
michael@0 | 862 | return addVisits(['http://mozilla.com/', 'http://mozilla.com']); |
michael@0 | 863 | }).then(() => |
michael@0 | 864 | saveP(Bookmark({ url: 'http://github.com', title: 'github.com' })) |
michael@0 | 865 | ).then(() => addVisits('http://bandcamp.com/')) |
michael@0 | 866 | .then(() => searchP({ query: 'webfwd' })) |
michael@0 | 867 | .then(results => { |
michael@0 | 868 | results[0].title = 'new title for webfwd'; |
michael@0 | 869 | return saveP(results[0]); |
michael@0 | 870 | }) |
michael@0 | 871 | .then(() => |
michael@0 | 872 | searchP({}, { sort: 'visitCount' }) |
michael@0 | 873 | ).then(results => { |
michael@0 | 874 | assert.equal(results[5].url, 'http://mozilla.com/', |
michael@0 | 875 | 'last entry is the highest visit count'); |
michael@0 | 876 | return searchP({}, { sort: 'visitCount', descending: true }); |
michael@0 | 877 | }).then(results => { |
michael@0 | 878 | assert.equal(results[0].url, 'http://mozilla.com/', |
michael@0 | 879 | 'first entry is the highest visit count'); |
michael@0 | 880 | return searchP({}, { sort: 'date' }); |
michael@0 | 881 | }).then(results => { |
michael@0 | 882 | assert.equal(results[5].url, 'http://bandcamp.com/', |
michael@0 | 883 | 'latest visited should be first'); |
michael@0 | 884 | return searchP({}, { sort: 'date', descending: true }); |
michael@0 | 885 | }).then(results => { |
michael@0 | 886 | assert.equal(results[0].url, 'http://bandcamp.com/', |
michael@0 | 887 | 'latest visited should be at the end'); |
michael@0 | 888 | return searchP({}, { sort: 'dateAdded' }); |
michael@0 | 889 | }).then(results => { |
michael@0 | 890 | assert.equal(results[5].url, 'http://github.com/', |
michael@0 | 891 | 'last added should be at the end'); |
michael@0 | 892 | return searchP({}, { sort: 'dateAdded', descending: true }); |
michael@0 | 893 | }).then(results => { |
michael@0 | 894 | assert.equal(results[0].url, 'http://github.com/', |
michael@0 | 895 | 'last added should be first'); |
michael@0 | 896 | return searchP({}, { sort: 'lastModified' }); |
michael@0 | 897 | }).then(results => { |
michael@0 | 898 | assert.equal(results[5].url, 'http://mozilla.com/webfwd/', |
michael@0 | 899 | 'last modified should be last'); |
michael@0 | 900 | return searchP({}, { sort: 'lastModified', descending: true }); |
michael@0 | 901 | }).then(results => { |
michael@0 | 902 | assert.equal(results[0].url, 'http://mozilla.com/webfwd/', |
michael@0 | 903 | 'last modified should be first'); |
michael@0 | 904 | }).then(() => { |
michael@0 | 905 | done(); |
michael@0 | 906 | }); |
michael@0 | 907 | |
michael@0 | 908 | function checkOrder (results, nums) { |
michael@0 | 909 | assert.equal(results.length, nums.length, 'expected return count'); |
michael@0 | 910 | for (let i = 0; i < nums.length; i++) { |
michael@0 | 911 | assert.equal(results[i].url, urls[nums[i]], 'successful order'); |
michael@0 | 912 | } |
michael@0 | 913 | } |
michael@0 | 914 | }; |
michael@0 | 915 | |
michael@0 | 916 | exports.testSearchComplexQueryWithOptions = function (assert, done) { |
michael@0 | 917 | createBookmarkTree().then(() => { |
michael@0 | 918 | return searchP([ |
michael@0 | 919 | { tags: ['rust'], url: '*.mozilla.org' }, |
michael@0 | 920 | { tags: ['javascript'], query: 'mozilla' } |
michael@0 | 921 | ], { sort: 'title' }); |
michael@0 | 922 | }).then(results => { |
michael@0 | 923 | let expected = [ |
michael@0 | 924 | 'http://developer.mozilla.org/en-US/', |
michael@0 | 925 | 'http://mozilla.org/' |
michael@0 | 926 | ]; |
michael@0 | 927 | for (let i = 0; i < expected.length; i++) |
michael@0 | 928 | assert.equal(results[i].url, expected[i], 'correct ordering and item'); |
michael@0 | 929 | done(); |
michael@0 | 930 | }); |
michael@0 | 931 | }; |
michael@0 | 932 | |
michael@0 | 933 | exports.testCheckSaveOrder = function (assert, done) { |
michael@0 | 934 | let group = Group({ title: 'mygroup' }); |
michael@0 | 935 | let bookmarks = [ |
michael@0 | 936 | Bookmark({ url: 'http://url1.com', title: 'url1', group: group }), |
michael@0 | 937 | Bookmark({ url: 'http://url2.com', title: 'url2', group: group }), |
michael@0 | 938 | Bookmark({ url: 'http://url3.com', title: 'url3', group: group }), |
michael@0 | 939 | Bookmark({ url: 'http://url4.com', title: 'url4', group: group }), |
michael@0 | 940 | Bookmark({ url: 'http://url5.com', title: 'url5', group: group }) |
michael@0 | 941 | ]; |
michael@0 | 942 | saveP(bookmarks).then(results => { |
michael@0 | 943 | for (let i = 0; i < bookmarks.length; i++) |
michael@0 | 944 | assert.equal(results[i].url, bookmarks[i].url, |
michael@0 | 945 | 'correct ordering of bookmark results'); |
michael@0 | 946 | done(); |
michael@0 | 947 | }); |
michael@0 | 948 | }; |
michael@0 | 949 | |
michael@0 | 950 | before(exports, (name, assert, done) => resetPlaces(done)); |
michael@0 | 951 | after(exports, (name, assert, done) => resetPlaces(done)); |
michael@0 | 952 | |
michael@0 | 953 | function saveP () { |
michael@0 | 954 | return promisedEmitter(save.apply(null, Array.slice(arguments))); |
michael@0 | 955 | } |
michael@0 | 956 | |
michael@0 | 957 | function searchP () { |
michael@0 | 958 | return promisedEmitter(search.apply(null, Array.slice(arguments))); |
michael@0 | 959 | } |
michael@0 | 960 | |
michael@0 | 961 | function delayed (value, ms) { |
michael@0 | 962 | let { promise, resolve } = defer(); |
michael@0 | 963 | setTimeout(() => resolve(value), ms || 10); |
michael@0 | 964 | return promise; |
michael@0 | 965 | } |