searchTest.js (15597B)
1 describe("Zotero.Search", function() { 2 describe("#addCondition()", function () { 3 it("should convert old-style 'collection' condition value", function* () { 4 var col = yield createDataObject('collection'); 5 var item = yield createDataObject('item', { collections: [col.id] }); 6 7 var s = new Zotero.Search(); 8 s.libraryID = item.libraryID; 9 s.name = "Test"; 10 s.addCondition('collection', 'is', '0_' + col.key); 11 var matches = yield s.search(); 12 assert.sameMembers(matches, [item.id]); 13 }); 14 }); 15 16 // This is for Zotero.Search._loadConditions() 17 describe("Loading", function () { 18 it("should convert old-style 'collection' condition value", function* () { 19 var col = yield createDataObject('collection'); 20 var item = yield createDataObject('item', { collections: [col.id] }); 21 22 var s = new Zotero.Search(); 23 s.libraryID = item.libraryID; 24 s.name = "Test"; 25 s.addCondition('collection', 'is', col.key); 26 yield s.saveTx(); 27 yield Zotero.DB.queryAsync( 28 "UPDATE savedSearchConditions SET value=? WHERE savedSearchID=? AND condition=?", 29 ["0_" + col.key, s.id, 'collection'] 30 ); 31 yield s.reload(['conditions'], true); 32 var matches = yield s.search(); 33 assert.sameMembers(matches, [item.id]); 34 }); 35 }); 36 37 38 describe("#save()", function () { 39 it("should fail without a name", function* () { 40 var s = new Zotero.Search; 41 s.addCondition('title', 'is', 'test'); 42 var e = yield getPromiseError(s.saveTx()); 43 assert.ok(e); 44 assert.equal(e.constructor.name, Error.prototype.constructor.name); // TEMP: Error mismatch 45 assert.equal(e.message, "Name not provided for saved search"); 46 }); 47 48 it("should save a new search", function* () { 49 // Save search 50 var s = new Zotero.Search; 51 s.name = "Test"; 52 s.addCondition('title', 'is', 'test'); 53 var id = yield s.saveTx(); 54 assert.typeOf(id, 'number'); 55 56 // Check saved search 57 s = Zotero.Searches.get(id); 58 assert.ok(s); 59 assert.instanceOf(s, Zotero.Search); 60 assert.equal(s.libraryID, Zotero.Libraries.userLibraryID); 61 assert.equal(s.name, "Test"); 62 var conditions = s.getConditions(); 63 assert.lengthOf(Object.keys(conditions), 1); 64 assert.property(conditions, "0"); 65 var condition = conditions[0]; 66 assert.propertyVal(condition, 'condition', 'title') 67 assert.propertyVal(condition, 'operator', 'is') 68 assert.propertyVal(condition, 'value', 'test') 69 assert.propertyVal(condition, 'required', false) 70 }); 71 72 it("should add a condition to an existing search", function* () { 73 // Save search 74 var s = new Zotero.Search; 75 s.libraryID = Zotero.Libraries.userLibraryID; 76 s.name = "Test"; 77 s.addCondition('title', 'is', 'test'); 78 var id = yield s.saveTx(); 79 assert.typeOf(id, 'number'); 80 81 // Add condition 82 s = yield Zotero.Searches.getAsync(id); 83 s.addCondition('title', 'contains', 'foo'); 84 var saved = yield s.saveTx(); 85 assert.isTrue(saved); 86 87 // Check saved search 88 s = yield Zotero.Searches.getAsync(id); 89 var conditions = s.getConditions(); 90 assert.lengthOf(Object.keys(conditions), 2); 91 }); 92 93 it("should remove a condition from an existing search", function* () { 94 // Save search 95 var s = new Zotero.Search; 96 s.libraryID = Zotero.Libraries.userLibraryID; 97 s.name = "Test"; 98 s.addCondition('title', 'is', 'test'); 99 s.addCondition('title', 'contains', 'foo'); 100 var id = yield s.saveTx(); 101 assert.typeOf(id, 'number'); 102 103 // Remove condition 104 s = yield Zotero.Searches.getAsync(id); 105 s.removeCondition(0); 106 var saved = yield s.saveTx(); 107 assert.isTrue(saved); 108 109 // Check saved search 110 s = yield Zotero.Searches.getAsync(id); 111 var conditions = s.getConditions(); 112 assert.lengthOf(Object.keys(conditions), 1); 113 assert.property(conditions, "0"); 114 assert.propertyVal(conditions[0], 'value', 'foo') 115 }); 116 }); 117 118 describe("#search()", function () { 119 var win; 120 var userLibraryID; 121 var fooItem; 122 var foobarItem; 123 var fooItemGroup; 124 var foobarItemGroup; 125 126 before(function* () { 127 // Hidden browser, which requires a browser window, needed for charset detection 128 // (until we figure out a better way) 129 win = yield loadBrowserWindow(); 130 fooItem = yield importFileAttachment("search/foo.html"); 131 foobarItem = yield importFileAttachment("search/foobar.html"); 132 userLibraryID = fooItem.libraryID; 133 134 let group = yield getGroup(); 135 fooItemGroup = yield importFileAttachment("search/foo.html", { libraryID: group.libraryID }); 136 foobarItemGroup = yield importFileAttachment("search/foobar.html", { libraryID: group.libraryID }); 137 }); 138 139 after(function* () { 140 if (win) { 141 win.close(); 142 } 143 yield fooItem.eraseTx(); 144 yield foobarItem.eraseTx(); 145 yield fooItemGroup.eraseTx(); 146 yield foobarItemGroup.eraseTx(); 147 }); 148 149 describe("Conditions", function () { 150 describe("collection", function () { 151 it("should find item in collection", function* () { 152 var col = yield createDataObject('collection'); 153 var item = yield createDataObject('item', { collections: [col.id] }); 154 155 var s = new Zotero.Search(); 156 s.libraryID = item.libraryID; 157 s.addCondition('collection', 'is', col.key); 158 var matches = yield s.search(); 159 assert.sameMembers(matches, [item.id]); 160 }); 161 162 it("should find items not in collection", function* () { 163 var col = yield createDataObject('collection'); 164 var item = yield createDataObject('item', { collections: [col.id] }); 165 166 var s = new Zotero.Search(); 167 s.libraryID = item.libraryID; 168 s.addCondition('collection', 'isNot', col.key); 169 var matches = yield s.search(); 170 assert.notInclude(matches, item.id); 171 }); 172 173 it("shouldn't find item in collection with no items", function* () { 174 var col = yield createDataObject('collection'); 175 var item = yield createDataObject('item'); 176 177 var s = new Zotero.Search(); 178 s.libraryID = item.libraryID; 179 s.addCondition('collection', 'is', col.key); 180 var matches = yield s.search(); 181 assert.lengthOf(matches, 0); 182 }); 183 184 it("should find item in subcollection in recursive mode", function* () { 185 var col1 = yield createDataObject('collection'); 186 var col2 = yield createDataObject('collection', { parentID: col1.id }); 187 var item = yield createDataObject('item', { collections: [col2.id] }); 188 189 var s = new Zotero.Search(); 190 s.libraryID = item.libraryID; 191 s.addCondition('collection', 'is', col1.key); 192 s.addCondition('recursive', 'true'); 193 var matches = yield s.search(); 194 assert.sameMembers(matches, [item.id]); 195 }); 196 }); 197 198 describe("fileTypeID", function () { 199 it("should search by attachment file type", function* () { 200 let s = new Zotero.Search(); 201 s.libraryID = userLibraryID; 202 s.addCondition('fileTypeID', 'is', Zotero.FileTypes.getID('webpage')); 203 let matches = yield s.search(); 204 assert.sameMembers(matches, [fooItem.id, foobarItem.id]); 205 }); 206 }); 207 208 describe("fulltextContent", function () { 209 it("should find text in HTML files", function* () { 210 var s = new Zotero.Search(); 211 s.libraryID = userLibraryID; 212 s.addCondition('fulltextContent', 'contains', 'foo bar'); 213 var matches = yield s.search(); 214 assert.sameMembers(matches, [foobarItem.id]); 215 }); 216 217 it("should work in subsearch", function* () { 218 var s = new Zotero.Search(); 219 s.libraryID = userLibraryID; 220 s.addCondition('fulltextContent', 'contains', 'foo bar'); 221 222 var s2 = new Zotero.Search(); 223 s2.setScope(s); 224 s2.addCondition('title', 'contains', 'foobar'); 225 var matches = yield s2.search(); 226 assert.sameMembers(matches, [foobarItem.id]); 227 }); 228 229 it("should find matching item with joinMode=ANY and non-matching other condition", function* () { 230 var s = new Zotero.Search(); 231 s.libraryID = userLibraryID; 232 s.addCondition('joinMode', 'any'); 233 s.addCondition('fulltextContent', 'contains', 'foo bar'); 234 s.addCondition('title', 'contains', 'nomatch'); 235 var matches = yield s.search(); 236 assert.sameMembers(matches, [foobarItem.id]); 237 }); 238 239 it("should find matching items in regexp mode with joinMode=ANY with matching other condition", function* () { 240 var s = new Zotero.Search(); 241 s.libraryID = userLibraryID; 242 s.addCondition('joinMode', 'any'); 243 s.addCondition('fulltextContent/regexp', 'contains', 'foo.+bar'); 244 s.addCondition('title', 'is', fooItem.getField('title')); 245 var matches = yield s.search(); 246 assert.sameMembers(matches, [fooItem.id, foobarItem.id]); 247 }); 248 249 it("should find matching item in regexp mode with joinMode=ANY and non-matching other condition", function* () { 250 var s = new Zotero.Search(); 251 s.libraryID = userLibraryID; 252 s.addCondition('joinMode', 'any'); 253 s.addCondition('fulltextContent/regexp', 'contains', 'foo.+bar'); 254 s.addCondition('title', 'contains', 'nomatch'); 255 var matches = yield s.search(); 256 assert.sameMembers(matches, [foobarItem.id]); 257 }); 258 259 it("should find item matching other condition in regexp mode when joinMode=ANY", function* () { 260 var s = new Zotero.Search(); 261 s.libraryID = userLibraryID; 262 s.addCondition('joinMode', 'any'); 263 s.addCondition('fulltextContent/regexp', 'contains', 'nomatch'); 264 s.addCondition('title', 'is', foobarItem.getField('title')); 265 var matches = yield s.search(); 266 assert.sameMembers(matches, [foobarItem.id]); 267 }); 268 269 it("should find matching item in regexp mode with joinMode=ANY and recursive mode flag", function* () { 270 var s = new Zotero.Search(); 271 s.libraryID = userLibraryID; 272 s.addCondition('joinMode', 'any'); 273 s.addCondition('fulltextContent/regexp', 'contains', 'foo.+bar'); 274 s.addCondition('recursive', 'true'); 275 var matches = yield s.search(); 276 assert.sameMembers(matches, [foobarItem.id]); 277 }); 278 }); 279 280 describe("fulltextWord", function () { 281 it("should return matches with full-text conditions", function* () { 282 let s = new Zotero.Search(); 283 s.libraryID = userLibraryID; 284 s.addCondition('fulltextWord', 'contains', 'foo'); 285 let matches = yield s.search(); 286 assert.lengthOf(matches, 2); 287 assert.sameMembers(matches, [fooItem.id, foobarItem.id]); 288 }); 289 290 it("should not return non-matches with full-text conditions", function* () { 291 let s = new Zotero.Search(); 292 s.libraryID = userLibraryID; 293 s.addCondition('fulltextWord', 'contains', 'baz'); 294 let matches = yield s.search(); 295 assert.lengthOf(matches, 0); 296 }); 297 298 it("should return matches for full-text conditions in ALL mode", function* () { 299 let s = new Zotero.Search(); 300 s.libraryID = userLibraryID; 301 s.addCondition('joinMode', 'all'); 302 s.addCondition('fulltextWord', 'contains', 'foo'); 303 s.addCondition('fulltextWord', 'contains', 'bar'); 304 let matches = yield s.search(); 305 assert.deepEqual(matches, [foobarItem.id]); 306 }); 307 308 it("should not return non-matches for full-text conditions in ALL mode", function* () { 309 let s = new Zotero.Search(); 310 s.libraryID = userLibraryID; 311 s.addCondition('joinMode', 'all'); 312 s.addCondition('fulltextWord', 'contains', 'mjktkiuewf'); 313 s.addCondition('fulltextWord', 'contains', 'zijajkvudk'); 314 let matches = yield s.search(); 315 assert.lengthOf(matches, 0); 316 }); 317 318 it("should return a match that satisfies only one of two full-text condition in ANY mode", function* () { 319 let s = new Zotero.Search(); 320 s.libraryID = userLibraryID; 321 s.addCondition('joinMode', 'any'); 322 s.addCondition('fulltextWord', 'contains', 'bar'); 323 s.addCondition('fulltextWord', 'contains', 'baz'); 324 let matches = yield s.search(); 325 assert.deepEqual(matches, [foobarItem.id]); 326 }); 327 }); 328 329 describe("key", function () { 330 it("should allow more than max bound parameters", function* () { 331 let s = new Zotero.Search(); 332 let max = Zotero.DB.MAX_BOUND_PARAMETERS + 100; 333 for (let i = 0; i < max; i++) { 334 s.addCondition('key', 'is', Zotero.DataObjectUtilities.generateKey()); 335 } 336 yield s.search(); 337 }); 338 }); 339 340 describe("savedSearch", function () { 341 it("should return items in the saved search", function* () { 342 var search = yield createDataObject('search'); 343 var itemTitle = search.getConditions()[0].value; 344 var item = yield createDataObject('item', { title: itemTitle }) 345 346 var s = new Zotero.Search; 347 s.libraryID = Zotero.Libraries.userLibraryID; 348 s.addCondition('savedSearch', 'is', search.key); 349 var matches = yield s.search(); 350 assert.deepEqual(matches, [item.id]); 351 }); 352 353 it("should return items not in the saved search for isNot operator", function* () { 354 var search = yield createDataObject('search'); 355 var itemTitle = search.getConditions()[0].value; 356 var item = yield createDataObject('item', { title: itemTitle }) 357 358 var s = new Zotero.Search; 359 s.libraryID = Zotero.Libraries.userLibraryID; 360 s.addCondition('savedSearch', 'isNot', search.key); 361 var matches = yield s.search(); 362 assert.notInclude(matches, item.id); 363 }); 364 }); 365 366 describe("unfiled", function () { 367 it("shouldn't include items in My Publications", function* () { 368 var item1 = yield createDataObject('item'); 369 var item2 = yield createDataObject('item', { inPublications: true }); 370 371 var s = new Zotero.Search; 372 s.libraryID = Zotero.Libraries.userLibraryID; 373 s.addCondition('unfiled', 'true'); 374 var matches = yield s.search(); 375 assert.include(matches, item1.id); 376 assert.notInclude(matches, item2.id); 377 }); 378 }); 379 }); 380 }); 381 382 describe("#toJSON()", function () { 383 it("should output all data", function* () { 384 let s = new Zotero.Search(); 385 s.name = "Test"; 386 s.addCondition('joinMode', 'any'); 387 s.addCondition('fulltextContent/regexp', 'contains', 's.+'); 388 let json = s.toJSON(); 389 assert.equal(json.name, "Test"); 390 391 assert.lengthOf(json.conditions, 2); 392 393 assert.equal(json.conditions[0].condition, 'joinMode'); 394 assert.equal(json.conditions[0].operator, 'any'); 395 // TODO: Change to 'is' + 'any'? 396 assert.strictEqual(json.conditions[0].value, ''); 397 assert.notProperty(json.conditions[0], 'id'); 398 assert.notProperty(json.conditions[0], 'required'); 399 assert.notProperty(json.conditions[0], 'mode'); 400 401 assert.equal(json.conditions[1].condition, 'fulltextContent/regexp'); 402 assert.equal(json.conditions[1].operator, 'contains'); 403 assert.equal(json.conditions[1].value, 's.+'); 404 assert.notProperty(json.conditions[1], 'mode'); 405 }); 406 }); 407 408 describe("#fromJSON()", function () { 409 it("should update all data", function* () { 410 let s = new Zotero.Search(); 411 s.name = "Test"; 412 s.addCondition('joinMode', 'any'); 413 s.addCondition('title', 'isNot', 'foo'); 414 let json = s.toJSON(); 415 json.name = "Test 2"; 416 json.conditions = [ 417 { 418 condition: 'title', 419 operator: 'contains', 420 value: 'foo' 421 }, 422 { 423 condition: 'year', 424 operator: 'is', 425 value: '2016' 426 } 427 ]; 428 s.fromJSON(json); 429 assert.equal(s.name, "Test 2"); 430 var conditions = s.getConditions(); 431 assert.lengthOf(Object.keys(conditions), 2); 432 assert.equal(conditions["0"].condition, 'title'); 433 assert.equal(conditions["0"].operator, 'contains'); 434 assert.equal(conditions["0"].value, 'foo'); 435 assert.equal(conditions["1"].condition, 'year'); 436 assert.equal(conditions["1"].operator, 'is'); 437 assert.equal(conditions["1"].value, '2016'); 438 }); 439 }); 440 });