www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

zoteroPaneTest.js (22510B)


      1 "use strict";
      2 
      3 describe("ZoteroPane", function() {
      4 	var win, doc, zp, userLibraryID;
      5 	
      6 	// Load Zotero pane and select library
      7 	before(function* () {
      8 		win = yield loadZoteroPane();
      9 		doc = win.document;
     10 		zp = win.ZoteroPane;
     11 		userLibraryID = Zotero.Libraries.userLibraryID;
     12 	});
     13 	
     14 	after(function () {
     15 		win.close();
     16 	});
     17 	
     18 	describe("#newItem", function () {
     19 		it("should create an item and focus the title field", function* () {
     20 			yield zp.newItem(Zotero.ItemTypes.getID('book'), {}, null, true);
     21 			var itemBox = doc.getElementById('zotero-editpane-item-box');
     22 			var textboxes = doc.getAnonymousNodes(itemBox)[0].getElementsByTagName('textbox');
     23 			assert.lengthOf(textboxes, 1);
     24 			assert.equal(textboxes[0].getAttribute('fieldname'), 'title');
     25 			textboxes[0].blur();
     26 			yield Zotero.Promise.delay(1);
     27 		})
     28 		
     29 		it("should save an entered value when New Item is used", function* () {
     30 			var value = "Test";
     31 			var item = yield zp.newItem(Zotero.ItemTypes.getID('book'), {}, null, true);
     32 			var itemBox = doc.getElementById('zotero-editpane-item-box');
     33 			var textbox = doc.getAnonymousNodes(itemBox)[0].getElementsByTagName('textbox')[0];
     34 			textbox.value = value;
     35 			yield itemBox.blurOpenField();
     36 			item = yield Zotero.Items.getAsync(item.id);
     37 			assert.equal(item.getField('title'), value);
     38 		})
     39 	});
     40 	
     41 	describe("#newNote()", function () {
     42 		it("should create a child note and select it", function* () {
     43 			var item = yield createDataObject('item');
     44 			var noteID = yield zp.newNote(false, item.key, "Test");
     45 			var selected = zp.itemsView.getSelectedItems(true);
     46 			assert.lengthOf(selected, 1);
     47 			assert.equal(selected, noteID);
     48 		})
     49 		
     50 		it("should create a standalone note within a collection and select it", function* () {
     51 			var collection = yield createDataObject('collection');
     52 			var noteID = yield zp.newNote(false, false, "Test");
     53 			assert.equal(zp.collectionsView.getSelectedCollection(), collection);
     54 			var selected = zp.itemsView.getSelectedItems(true);
     55 			assert.lengthOf(selected, 1);
     56 			assert.equal(selected, noteID);
     57 		})
     58 	})
     59 	
     60 	describe("#newCollection()", function () {
     61 		it("should create a collection", function* () {
     62 			var promise = waitForDialog();
     63 			var id = yield zp.newCollection();
     64 			yield promise;
     65 			var collection = Zotero.Collections.get(id);
     66 			assert.isTrue(collection.name.startsWith(Zotero.getString('pane.collections.untitled')));
     67 		});
     68 	});
     69 	
     70 	describe("#newSearch()", function () {
     71 		it("should create a saved search", function* () {
     72 			var promise = waitForDialog(
     73 				// TODO: Test changing a condition
     74 				function (dialog) {},
     75 				'accept',
     76 				'chrome://zotero/content/searchDialog.xul'
     77 			);
     78 			var id = yield zp.newSearch();
     79 			yield promise;
     80 			var search = Zotero.Searches.get(id);
     81 			assert.ok(search);
     82 			assert.isTrue(search.name.startsWith(Zotero.getString('pane.collections.untitled')));
     83 		});
     84 		
     85 		it("should handle clicking Cancel in the search window", function* () {
     86 			var promise = waitForDialog(
     87 				function (dialog) {},
     88 				'cancel',
     89 				'chrome://zotero/content/searchDialog.xul'
     90 			);
     91 			var id = yield zp.newSearch();
     92 			yield promise;
     93 			assert.isFalse(id);
     94 		});
     95 	});
     96 	
     97 	describe("#itemSelected()", function () {
     98 		it.skip("should update the item count", function* () {
     99 			var collection = new Zotero.Collection;
    100 			collection.name = "Count Test";
    101 			var id = yield collection.saveTx();
    102 			yield waitForItemsLoad(win);
    103 			
    104 			// Unselected, with no items in view
    105 			assert.equal(
    106 				doc.getElementById('zotero-item-pane-message-box').textContent,
    107 				Zotero.getString('pane.item.unselected.zero', 0)
    108 			);
    109 			
    110 			// Unselected, with one item in view
    111 			var item = new Zotero.Item('newspaperArticle');
    112 			item.setCollections([id]);
    113 			var itemID1 = yield item.saveTx({
    114 				skipSelect: true
    115 			});
    116 			assert.equal(
    117 				doc.getElementById('zotero-item-pane-message-box').textContent,
    118 				Zotero.getString('pane.item.unselected.singular', 1)
    119 			);
    120 			
    121 			// Unselected, with multiple items in view
    122 			var item = new Zotero.Item('audioRecording');
    123 			item.setCollections([id]);
    124 			var itemID2 = yield item.saveTx({
    125 				skipSelect: true
    126 			});
    127 			assert.equal(
    128 				doc.getElementById('zotero-item-pane-message-box').textContent,
    129 				Zotero.getString('pane.item.unselected.plural', 2)
    130 			);
    131 			
    132 			// Multiple items selected
    133 			var promise = zp.itemsView._getItemSelectedPromise();
    134 			zp.itemsView.rememberSelection([itemID1, itemID2]);
    135 			yield promise;
    136 			assert.equal(
    137 				doc.getElementById('zotero-item-pane-message-box').textContent,
    138 				Zotero.getString('pane.item.selected.multiple', 2)
    139 			);
    140 		})
    141 	})
    142 	
    143 	describe("#viewAttachment", function () {
    144 		Components.utils.import("resource://zotero-unit/httpd.js");
    145 		var apiKey = Zotero.Utilities.randomString(24);
    146 		var port = 16213;
    147 		var baseURL = `http://localhost:${port}/`;
    148 		var server;
    149 		var responses = {};
    150 		var httpd;
    151 		
    152 		var setup = Zotero.Promise.coroutine(function* (options = {}) {
    153 			server = sinon.fakeServer.create();
    154 			server.autoRespond = true;
    155 		});
    156 		
    157 		function setResponse(response) {
    158 			setHTTPResponse(server, baseURL, response, responses);
    159 		}
    160 		
    161 		async function downloadOnDemand() {
    162 			var item = new Zotero.Item("attachment");
    163 			item.attachmentLinkMode = 'imported_file';
    164 			item.attachmentPath = 'storage:test.txt';
    165 			// TODO: Test binary data
    166 			var text = Zotero.Utilities.randomString();
    167 			item.attachmentSyncState = "to_download";
    168 			await item.saveTx();
    169 			
    170 			var mtime = "1441252524000";
    171 			var md5 = Zotero.Utilities.Internal.md5(text)
    172 			
    173 			var s3Path = `pretend-s3/${item.key}`;
    174 			httpd.registerPathHandler(
    175 				`/users/1/items/${item.key}/file`,
    176 				{
    177 					handle: function (request, response) {
    178 						response.setStatusLine(null, 302, "Found");
    179 						response.setHeader("Zotero-File-Modification-Time", mtime, false);
    180 						response.setHeader("Zotero-File-MD5", md5, false);
    181 						response.setHeader("Zotero-File-Compressed", "No", false);
    182 						response.setHeader("Location", baseURL + s3Path, false);
    183 					}
    184 				}
    185 			);
    186 			httpd.registerPathHandler(
    187 				"/" + s3Path,
    188 				{
    189 					handle: function (request, response) {
    190 						response.setStatusLine(null, 200, "OK");
    191 						response.write(text);
    192 					}
    193 				}
    194 			);
    195 			
    196 			// Disable loadURI() so viewAttachment() doesn't trigger translator loading
    197 			var stub = sinon.stub(zp, "loadURI");
    198 			
    199 			await zp.viewAttachment(item.id);
    200 			
    201 			assert.ok(stub.calledOnce);
    202 			assert.ok(stub.calledWith(OS.Path.toFileURI(item.getFilePath())));
    203 			stub.restore();
    204 			
    205 			assert.equal(await item.attachmentHash, md5);
    206 			assert.equal(await item.attachmentModificationTime, mtime);
    207 			var path = await item.getFilePathAsync();
    208 			assert.equal(await Zotero.File.getContentsAsync(path), text);
    209 		};
    210 		
    211 		before(function () {
    212 			Zotero.HTTP.mock = sinon.FakeXMLHttpRequest;
    213 		})
    214 		beforeEach(function* () {
    215 			Zotero.Prefs.set("api.url", baseURL);
    216 			Zotero.Sync.Runner.apiKey = apiKey;
    217 				
    218 			httpd = new HttpServer();
    219 			httpd.start(port);
    220 			
    221 			yield Zotero.Users.setCurrentUserID(1);
    222 			yield Zotero.Users.setCurrentUsername("testuser");
    223 		})
    224 		afterEach(function* () {
    225 			var defer = new Zotero.Promise.defer();
    226 			httpd.stop(() => defer.resolve());
    227 			yield defer.promise;
    228 		})
    229 		after(function () {
    230 			Zotero.HTTP.mock = null;
    231 		});
    232 		
    233 		it("should download an attachment on-demand in as-needed mode", function* () {
    234 			yield setup();
    235 			Zotero.Sync.Storage.Local.downloadAsNeeded(Zotero.Libraries.userLibraryID, true);
    236 			yield downloadOnDemand();
    237 		});
    238 		
    239 		it("should download an attachment on-demand in at-sync-time mode", function* () {
    240 			yield setup();
    241 			Zotero.Sync.Storage.Local.downloadOnSync(Zotero.Libraries.userLibraryID, true);
    242 			yield downloadOnDemand();
    243 		});
    244 	})
    245 	
    246 	
    247 	describe("#renameSelectedAttachmentsFromParents()", function () {
    248 		it("should rename a linked file", async function () {
    249 			var oldFilename = 'old.png';
    250 			var newFilename = 'Test.png';
    251 			var file = getTestDataDirectory();
    252 			file.append('test.png');
    253 			var tmpDir = await getTempDirectory();
    254 			var oldFile = OS.Path.join(tmpDir, oldFilename);
    255 			await OS.File.copy(file.path, oldFile);
    256 			
    257 			var item = createUnsavedDataObject('item');
    258 			item.setField('title', 'Test');
    259 			await item.saveTx();
    260 			
    261 			var attachment = await Zotero.Attachments.linkFromFile({
    262 				file: oldFile,
    263 				parentItemID: item.id
    264 			});
    265 			await zp.selectItem(attachment.id);
    266 			
    267 			await assert.eventually.isTrue(zp.renameSelectedAttachmentsFromParents());
    268 			assert.equal(attachment.attachmentFilename, newFilename);
    269 			var path = await attachment.getFilePathAsync();
    270 			assert.equal(OS.Path.basename(path), newFilename)
    271 			await OS.File.exists(path);
    272 		});
    273 		
    274 		it("should use unique name for linked file if target name is taken", async function () {
    275 			var oldFilename = 'old.png';
    276 			var newFilename = 'Test.png';
    277 			var uniqueFilename = 'Test 2.png';
    278 			var file = getTestDataDirectory();
    279 			file.append('test.png');
    280 			var tmpDir = await getTempDirectory();
    281 			var oldFile = OS.Path.join(tmpDir, oldFilename);
    282 			await OS.File.copy(file.path, oldFile);
    283 			// Create file with target filename
    284 			await Zotero.File.putContentsAsync(OS.Path.join(tmpDir, newFilename), '');
    285 			
    286 			var item = createUnsavedDataObject('item');
    287 			item.setField('title', 'Test');
    288 			await item.saveTx();
    289 			
    290 			var attachment = await Zotero.Attachments.linkFromFile({
    291 				file: oldFile,
    292 				parentItemID: item.id
    293 			});
    294 			await zp.selectItem(attachment.id);
    295 			
    296 			await assert.eventually.isTrue(zp.renameSelectedAttachmentsFromParents());
    297 			assert.equal(attachment.attachmentFilename, uniqueFilename);
    298 			var path = await attachment.getFilePathAsync();
    299 			assert.equal(OS.Path.basename(path), uniqueFilename)
    300 			await OS.File.exists(path);
    301 		});
    302 		
    303 		it("should use unique name for linked file without extension if target name is taken", async function () {
    304 			var oldFilename = 'old';
    305 			var newFilename = 'Test';
    306 			var uniqueFilename = 'Test 2';
    307 			var file = getTestDataDirectory();
    308 			file.append('test.png');
    309 			var tmpDir = await getTempDirectory();
    310 			var oldFile = OS.Path.join(tmpDir, oldFilename);
    311 			await OS.File.copy(file.path, oldFile);
    312 			// Create file with target filename
    313 			await Zotero.File.putContentsAsync(OS.Path.join(tmpDir, newFilename), '');
    314 			
    315 			var item = createUnsavedDataObject('item');
    316 			item.setField('title', 'Test');
    317 			await item.saveTx();
    318 			
    319 			var attachment = await Zotero.Attachments.linkFromFile({
    320 				file: oldFile,
    321 				parentItemID: item.id
    322 			});
    323 			await zp.selectItem(attachment.id);
    324 			
    325 			await assert.eventually.isTrue(zp.renameSelectedAttachmentsFromParents());
    326 			assert.equal(attachment.attachmentFilename, uniqueFilename);
    327 			var path = await attachment.getFilePathAsync();
    328 			assert.equal(OS.Path.basename(path), uniqueFilename)
    329 			await OS.File.exists(path);
    330 		});
    331 	});
    332 	
    333 	
    334 	describe("#duplicateSelectedItem()", function () {
    335 		it("should add reverse relations", async function () {
    336 			await selectLibrary(win);
    337 			var item1 = await createDataObject('item');
    338 			var item2 = await createDataObject('item');
    339 			item1.addRelatedItem(item2);
    340 			await item1.saveTx();
    341 			item2.addRelatedItem(item1);
    342 			await item2.saveTx();
    343 			var item3 = await zp.duplicateSelectedItem();
    344 			assert.sameMembers(item3.relatedItems, [item1.key]);
    345 			assert.sameMembers(item2.relatedItems, [item1.key]);
    346 			assert.sameMembers(item1.relatedItems, [item2.key, item3.key]);
    347 		});
    348 	});
    349 	
    350 	
    351 	describe("#deleteSelectedItems()", function () {
    352 		it("should remove an item from My Publications", function* () {
    353 			var item = createUnsavedDataObject('item');
    354 			item.inPublications = true;
    355 			yield item.saveTx();
    356 			
    357 			yield zp.collectionsView.selectByID("P" + userLibraryID);
    358 			yield waitForItemsLoad(win);
    359 			var iv = zp.itemsView;
    360 			
    361 			var selected = iv.selectItem(item.id);
    362 			assert.ok(selected);
    363 			
    364 			var tree = doc.getElementById('zotero-items-tree');
    365 			tree.focus();
    366 			
    367 			yield Zotero.Promise.delay(1);
    368 			
    369 			var promise = waitForDialog();
    370 			var modifyPromise = waitForItemEvent('modify');
    371 			
    372 			var event = doc.createEvent("KeyboardEvent");
    373 			event.initKeyEvent("keypress", true, true, window, false, false, false, false, 46, 0);
    374 			tree.dispatchEvent(event);
    375 			yield promise;
    376 			yield modifyPromise;
    377 			
    378 			assert.isFalse(item.inPublications);
    379 			assert.isFalse(item.deleted);
    380 		});
    381 		
    382 		it("should move an item to trash from My Publications", function* () {
    383 			var item = createUnsavedDataObject('item');
    384 			item.inPublications = true;
    385 			yield item.saveTx();
    386 			
    387 			yield zp.collectionsView.selectByID("P" + userLibraryID);
    388 			yield waitForItemsLoad(win);
    389 			var iv = zp.itemsView;
    390 			
    391 			var selected = iv.selectItem(item.id);
    392 			assert.ok(selected);
    393 			
    394 			var tree = doc.getElementById('zotero-items-tree');
    395 			tree.focus();
    396 			
    397 			yield Zotero.Promise.delay(1);
    398 			
    399 			var promise = waitForDialog();
    400 			var modifyPromise = waitForItemEvent('modify');
    401 			
    402 			var event = doc.createEvent("KeyboardEvent");
    403 			event.initKeyEvent(
    404 				"keypress",
    405 				true,
    406 				true,
    407 				window,
    408 				false,
    409 				false,
    410 				!Zotero.isMac, // shift
    411 				Zotero.isMac, // meta
    412 				46,
    413 				0
    414 			);
    415 			tree.dispatchEvent(event);
    416 			yield promise;
    417 			yield modifyPromise;
    418 			
    419 			assert.isTrue(item.inPublications);
    420 			assert.isTrue(item.deleted);
    421 		});
    422 	});
    423 	
    424 	describe("#deleteSelectedCollection()", function () {
    425 		it("should delete collection but not descendant items by default", function* () {
    426 			var collection = yield createDataObject('collection');
    427 			var item = yield createDataObject('item', { collections: [collection.id] });
    428 			var promise = waitForDialog();
    429 			yield zp.deleteSelectedCollection();
    430 			assert.isFalse(Zotero.Collections.exists(collection.id));
    431 			assert.isTrue(Zotero.Items.exists(item.id));
    432 			assert.isFalse(item.deleted);
    433 		});
    434 		
    435 		it("should delete collection and descendant items when deleteItems=true", function* () {
    436 			var collection = yield createDataObject('collection');
    437 			var item = yield createDataObject('item', { collections: [collection.id] });
    438 			var promise = waitForDialog();
    439 			yield zp.deleteSelectedCollection(true);
    440 			assert.isFalse(Zotero.Collections.exists(collection.id));
    441 			assert.isTrue(Zotero.Items.exists(item.id));
    442 			assert.isTrue(item.deleted);
    443 		});
    444 	});
    445 	
    446 	
    447 	describe("#setVirtual()", function () {
    448 		var cv;
    449 		
    450 		before(function* () {
    451 			cv = zp.collectionsView;
    452 		});
    453 		beforeEach(function () {
    454 			Zotero.Prefs.clear('duplicateLibraries');
    455 			Zotero.Prefs.clear('unfiledLibraries');
    456 			return selectLibrary(win);
    457 		})
    458 		
    459 		it("should show a hidden virtual collection in My Library", function* () {
    460 			// Create unfiled, duplicate items
    461 			var title = Zotero.Utilities.randomString();
    462 			var item1 = yield createDataObject('item', { title });
    463 			var item2 = yield createDataObject('item', { title });
    464 			
    465 			// Start hidden (tested in collectionTreeViewTest)
    466 			Zotero.Prefs.set('duplicateLibraries', `{"${userLibraryID}": false}`);
    467 			Zotero.Prefs.set('unfiledLibraries', `{"${userLibraryID}": false}`);
    468 			yield cv.refresh();
    469 			
    470 			// Show Duplicate Items
    471 			var id = "D" + userLibraryID;
    472 			assert.isFalse(cv.getRowIndexByID(id));
    473 			yield zp.setVirtual(userLibraryID, 'duplicates', true);
    474 			// Duplicate Items should be selected
    475 			assert.equal(cv.selectedTreeRow.id, id);
    476 			// Should be missing from pref
    477 			assert.isUndefined(JSON.parse(Zotero.Prefs.get('duplicateLibraries'))[userLibraryID])
    478 			
    479 			// Clicking should select both items
    480 			var row = cv.getRowIndexByID(id);
    481 			assert.ok(row);
    482 			assert.equal(cv.selection.currentIndex, row);
    483 			yield waitForItemsLoad(win);
    484 			var iv = zp.itemsView;
    485 			row = iv.getRowIndexByID(item1.id);
    486 			assert.isNumber(row);
    487 			clickOnItemsRow(iv, row);
    488 			assert.equal(iv.selection.count, 2);
    489 			
    490 			// Show Unfiled Items
    491 			id = "U" + userLibraryID;
    492 			assert.isFalse(cv.getRowIndexByID(id));
    493 			yield zp.setVirtual(userLibraryID, 'unfiled', true);
    494 			// Unfiled Items should be selected
    495 			assert.equal(cv.selectedTreeRow.id, id);
    496 			// Should be missing from pref
    497 			assert.isUndefined(JSON.parse(Zotero.Prefs.get('unfiledLibraries'))[userLibraryID])
    498 		});
    499 		
    500 		it("should expand library if collapsed when showing virtual collection", function* () {
    501 			// Start hidden (tested in collectionTreeViewTest)
    502 			Zotero.Prefs.set('duplicateLibraries', `{"${userLibraryID}": false}`);
    503 			yield cv.refresh();
    504 			
    505 			var libraryRow = cv.getRowIndexByID(Zotero.Libraries.userLibrary.treeViewID);
    506 			if (cv.isContainerOpen(libraryRow)) {
    507 				yield cv.toggleOpenState(libraryRow);
    508 				cv._saveOpenStates();
    509 			}
    510 			
    511 			// Show Duplicate Items
    512 			var id = "D" + userLibraryID;
    513 			yield zp.setVirtual(userLibraryID, 'duplicates', true);
    514 			
    515 			// Library should have been expanded and Duplicate Items selected
    516 			assert.ok(cv.getRowIndexByID(id));
    517 			assert.equal(cv.selectedTreeRow.id, id);
    518 		});
    519 		
    520 		it("should hide a virtual collection in My Library", function* () {
    521 			yield cv.refresh();
    522 			
    523 			// Hide Duplicate Items
    524 			var id = "D" + userLibraryID;
    525 			assert.ok(yield cv.selectByID(id));
    526 			yield zp.setVirtual(userLibraryID, 'duplicates', false);
    527 			assert.isFalse(cv.getRowIndexByID(id));
    528 			assert.isFalse(JSON.parse(Zotero.Prefs.get('duplicateLibraries'))[userLibraryID])
    529 			
    530 			// Hide Unfiled Items
    531 			id = "U" + userLibraryID;
    532 			assert.ok(yield cv.selectByID(id));
    533 			yield zp.setVirtual(userLibraryID, 'unfiled', false);
    534 			assert.isFalse(cv.getRowIndexByID(id));
    535 			assert.isFalse(JSON.parse(Zotero.Prefs.get('unfiledLibraries'))[userLibraryID])
    536 		});
    537 		
    538 		it("should hide a virtual collection in a group", function* () {
    539 			yield cv.refresh();
    540 			
    541 			var group = yield createGroup();
    542 			var groupRow = cv.getRowIndexByID(group.treeViewID);
    543 			var rowCount = cv.rowCount;
    544 			
    545 			// Make sure group is open
    546 			if (!cv.isContainerOpen(groupRow)) {
    547 				yield cv.toggleOpenState(groupRow);
    548 			}
    549 			
    550 			// Make sure Duplicate Items is showing
    551 			var id = "D" + group.libraryID;
    552 			assert.ok(cv.getRowIndexByID(id));
    553 			
    554 			// Hide Duplicate Items
    555 			assert.ok(yield cv.selectByID(id));
    556 			yield zp.setVirtual(group.libraryID, 'duplicates', false);
    557 			// Row should have been removed
    558 			assert.isFalse(cv.getRowIndexByID(id));
    559 			// Pref should have been updated
    560 			Zotero.debug(Zotero.Prefs.get('duplicateLibraries'));
    561 			assert.isFalse(JSON.parse(Zotero.Prefs.get('duplicateLibraries'))[group.libraryID]);
    562 			// Group row shouldn't have changed
    563 			assert.equal(cv.getRowIndexByID(group.treeViewID), groupRow);
    564 			// Group should remain open
    565 			assert.isTrue(cv.isContainerOpen(groupRow));
    566 			// Row count should be 1 less
    567 			assert.equal(cv.rowCount, --rowCount);
    568 			
    569 			// Hide Unfiled Items
    570 			id = "U" + group.libraryID;
    571 			assert.ok(yield cv.selectByID(id));
    572 			// Hide Unfiled Items
    573 			yield zp.setVirtual(group.libraryID, 'unfiled', false);
    574 			// Row should have been removed
    575 			assert.isFalse(cv.getRowIndexByID(id));
    576 			// Pref should have been udpated
    577 			assert.isFalse(JSON.parse(Zotero.Prefs.get('unfiledLibraries'))[group.libraryID]);
    578 			// Group row shouldn't have changed
    579 			assert.equal(cv.getRowIndexByID(group.treeViewID), groupRow);
    580 			// Group should remain open
    581 			assert.isTrue(cv.isContainerOpen(groupRow));
    582 			// Row count should be 1 less
    583 			assert.equal(cv.rowCount, --rowCount);
    584 		});
    585 	});
    586 	
    587 	describe("#editSelectedCollection()", function () {
    588 		it("should edit a saved search", function* () {
    589 			var search = yield createDataObject('search');
    590 			var promise = waitForWindow('chrome://zotero/content/searchDialog.xul', function (win) {
    591 				let searchBox = win.document.getElementById('search-box');
    592 				var c = searchBox.search.getCondition(
    593 					searchBox.search.addCondition("title", "contains", "foo")
    594 				);
    595 				searchBox.addCondition(c);
    596 				win.document.documentElement.acceptDialog();
    597 			});
    598 			yield zp.editSelectedCollection();
    599 			yield promise;
    600 			var conditions = search.getConditions();
    601 			assert.lengthOf(Object.keys(conditions), 3);
    602 		});
    603 		
    604 		it("should edit a saved search in a group", function* () {
    605 			var group = yield getGroup();
    606 			var search = yield createDataObject('search', { libraryID: group.libraryID });
    607 			var promise = waitForWindow('chrome://zotero/content/searchDialog.xul', function (win) {
    608 				let searchBox = win.document.getElementById('search-box');
    609 				var c = searchBox.search.getCondition(
    610 					searchBox.search.addCondition("title", "contains", "foo")
    611 				);
    612 				searchBox.addCondition(c);
    613 				win.document.documentElement.acceptDialog();
    614 			});
    615 			yield zp.editSelectedCollection();
    616 			yield promise;
    617 			var conditions = search.getConditions();
    618 			assert.lengthOf(Object.keys(conditions), 3);
    619 		});
    620 	});
    621 	
    622 	describe("#onCollectionSelected()", function() {
    623 		var cv;
    624 		
    625 		beforeEach(function* () {
    626 			cv = zp.collectionsView;
    627 			yield cv.selectLibrary(Zotero.Libraries.userLibraryID);
    628 			Zotero.Prefs.clear('itemsView.columnVisibility');
    629 			yield clearFeeds();
    630 		});
    631 		
    632 		it("should store column visibility settings when switching from default to feeds", function* () {
    633 			doc.getElementById('zotero-items-column-dateAdded').setAttribute('hidden', false);
    634 			var feed = yield createFeed();
    635 			yield cv.selectLibrary(feed.libraryID);
    636 			var settings = JSON.parse(Zotero.Prefs.get('itemsView.columnVisibility'));
    637 			assert.isOk(settings.default.dateAdded);
    638 		});
    639 		
    640 		it("should restore column visiblity when switching between default and feeds", function* () {
    641 			doc.getElementById('zotero-items-column-dateAdded').setAttribute('hidden', false);
    642 			var feed = yield createFeed();
    643 			yield cv.selectLibrary(feed.libraryID);
    644 			assert.equal(doc.getElementById('zotero-items-column-dateAdded').getAttribute('hidden'), 'true');
    645 			doc.getElementById('zotero-items-column-firstCreator').setAttribute('hidden', true);
    646 			yield cv.selectLibrary(Zotero.Libraries.userLibraryID);
    647 			assert.equal(doc.getElementById('zotero-items-column-dateAdded').getAttribute('hidden'), 'false');
    648 			yield cv.selectLibrary(feed.libraryID);
    649 			assert.equal(doc.getElementById('zotero-items-column-firstCreator').getAttribute('hidden'), 'true');
    650 		});
    651 		
    652 		it("should restore column visibility settings on restart", function* () {
    653 			doc.getElementById('zotero-items-column-dateAdded').setAttribute('hidden', false);
    654 			assert.equal(doc.getElementById('zotero-items-column-dateAdded').getAttribute('hidden'), 'false');
    655 			
    656 			win.close();
    657 			win = yield loadZoteroPane();
    658 			doc = win.document;
    659 			zp = win.ZoteroPane;
    660 			
    661 			assert.equal(doc.getElementById('zotero-items-column-dateAdded').getAttribute('hidden'), 'false');
    662 		});
    663 	});
    664 })