diff --git a/apps/files/js/dist/sidebar.js b/apps/files/js/dist/sidebar.js
index a7a7f250bc3db1377ea70f85cd8abe1035db8cfa..e8ad0bd663a33447d72c2222beb729f580c05948 100644
Binary files a/apps/files/js/dist/sidebar.js and b/apps/files/js/dist/sidebar.js differ
diff --git a/apps/files/js/dist/sidebar.js.map b/apps/files/js/dist/sidebar.js.map
index b9b5da1a08564de89417b9606036894d8aed49eb..14169cb5494c5b42324d831a65e1e42086c72c17 100644
Binary files a/apps/files/js/dist/sidebar.js.map and b/apps/files/js/dist/sidebar.js.map differ
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 35800c8d279f215459625328d3f0cf36521f6758..86e978d178b09b870450b881c66c86e97fc6595a 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -3699,7 +3699,7 @@
 			const enabled = tabView.canDisplay || undefined
 			if (tabView.id) {
 				OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
-					id: tabView.id, 
+					id: tabView.id,
 					name: tabView.getLabel(),
 					icon: tabView.getIcon(),
 					mount: function(el, fileInfo) {
diff --git a/apps/files/tests/js/breadcrumbSpec.js b/apps/files/tests/js/breadcrumbSpec.js
index 14ad42a915dae62a991ba42ac0c0d8ace5a1f52b..5387edfaf2c4e4fda6d6618e153f42c4a9d2e266 100644
--- a/apps/files/tests/js/breadcrumbSpec.js
+++ b/apps/files/tests/js/breadcrumbSpec.js
@@ -207,35 +207,6 @@ describe('OCA.Files.BreadCrumb tests', function() {
 			bc = null;
 		});
 
-		it('Opens and closes the menu on click', function() {
-			// Menu exists
-			expect($popovermenu.length).toEqual(1);
-
-			// Disable jQuery delay
-			jQuery.fx.off = true
-
-			// Click on menu
-			$crumbmenuLink.click();
-			expect($popovermenu.is(':visible')).toEqual(true);
-
-			// Click on home
-			$(document).mouseup();
-			expect($popovermenu.is(':visible')).toEqual(false);
-
-			// Change directory and reset elements
-			bc.setDirectory('/one/two/three/four/five/six/seven/eight/nine/ten');
-			$crumbmenuLink = bc.$el.find('.crumbmenu > a');
-			$popovermenu = $crumbmenuLink.next('.popovermenu');
-
-			// Click on menu again
-			$crumbmenuLink.click();
-			expect($popovermenu.is(':visible')).toEqual(true);
-
-			// Click on home again
-			$(document).mouseup();
-			expect($popovermenu.is(':visible')).toEqual(false);
-
-		});
 		it('Shows only items not in the breadcrumb', function() {
 			var hiddenCrumbs = bc.$el.find('.crumb:not(.crumbmenu).hidden');
 			expect($popovermenu.find('li:not(.in-breadcrumb)').length).toEqual(hiddenCrumbs.length);
diff --git a/apps/files/tests/js/favoritesfilelistspec.js b/apps/files/tests/js/favoritesfilelistspec.js
index 5a206ca4dec1fa63cb9ff9cfabaa9d4a267f4969..b8218a05c7c2a8b8d9e86a3d7da94d8aad5edb2a 100644
--- a/apps/files/tests/js/favoritesfilelistspec.js
+++ b/apps/files/tests/js/favoritesfilelistspec.js
@@ -58,7 +58,7 @@ describe('OCA.Files.FavoritesFileList tests', function() {
 			fileList.destroy();
 			fileList = undefined;
 		});
-		it('render files', function() {
+		it('render files', function(done) {
 			var deferred = $.Deferred();
 			fetchStub.returns(deferred.promise());
 
@@ -77,23 +77,27 @@ describe('OCA.Files.FavoritesFileList tests', function() {
 				mimetype: 'text/plain'
 			}]);
 
-			var $rows = fileList.$el.find('tbody tr');
-			var $tr = $rows.eq(0);
-			expect($rows.length).toEqual(1);
-			expect($tr.attr('data-id')).toEqual('7');
-			expect($tr.attr('data-type')).toEqual('file');
-			expect($tr.attr('data-file')).toEqual('test.txt');
-			expect($tr.attr('data-path')).toEqual('/somedir');
-			expect($tr.attr('data-size')).toEqual('123');
-			expect(parseInt($tr.attr('data-permissions'), 10))
-				.toEqual(OC.PERMISSION_ALL);
-			expect($tr.attr('data-mime')).toEqual('text/plain');
-			expect($tr.attr('data-mtime')).toEqual('11111000');
-			expect($tr.find('a.name').attr('href')).toEqual(
-				OC.getRootPath() +
-				'/remote.php/webdav/somedir/test.txt'
-			);
-			expect($tr.find('.nametext').text().trim()).toEqual('test.txt');
+			setTimeout(function() {
+				var $rows = fileList.$el.find('tbody tr');
+				var $tr = $rows.eq(0);
+				expect($rows.length).toEqual(1);
+				expect($tr.attr('data-id')).toEqual('7');
+				expect($tr.attr('data-type')).toEqual('file');
+				expect($tr.attr('data-file')).toEqual('test.txt');
+				expect($tr.attr('data-path')).toEqual('/somedir');
+				expect($tr.attr('data-size')).toEqual('123');
+				expect(parseInt($tr.attr('data-permissions'), 10))
+					.toEqual(OC.PERMISSION_ALL);
+				expect($tr.attr('data-mime')).toEqual('text/plain');
+				expect($tr.attr('data-mtime')).toEqual('11111000');
+				expect($tr.find('a.name').attr('href')).toEqual(
+					OC.getRootPath() +
+					'/remote.php/webdav/somedir/test.txt'
+				);
+				expect($tr.find('.nametext').text().trim()).toEqual('test.txt');
+
+				done();
+			}, 0);
 		});
 	});
 });
diff --git a/apps/files/tests/js/fileUploadSpec.js b/apps/files/tests/js/fileUploadSpec.js
index b7955f3b8370d8098c065a7eb612da44376557a3..64006d71e42f65d64a068110ea174f2def2fa429 100644
--- a/apps/files/tests/js/fileUploadSpec.js
+++ b/apps/files/tests/js/fileUploadSpec.js
@@ -123,6 +123,7 @@ describe('OC.Upload tests', function() {
 	});
 	describe('Upload conflicts', function() {
 		var conflictDialogStub;
+		var clock;
 		var fileList;
 
 		beforeEach(function() {
@@ -162,6 +163,11 @@ describe('OC.Upload tests', function() {
 			deferred.resolve();
 		});
 		afterEach(function() {
+			if (clock) {
+				clock.restore();
+				clock = undefined
+			}
+
 			conflictDialogStub.restore();
 
 			fileList.destroy();
@@ -210,7 +216,7 @@ describe('OC.Upload tests', function() {
 					expect(result[1].submit.calledOnce).toEqual(false);
 					expect(result[2].submit.calledOnce).toEqual(true);
 					done();
-				}, 0);
+				}, 10);
 			});
 			var result = addFiles(uploader, [
 				{name: 'conflict.txt'},
@@ -251,8 +257,6 @@ describe('OC.Upload tests', function() {
 			uploader.onReplace(upload);
 		});
 		it('autorenames file when choosing replace in conflict mode', function(done) {
-			// needed for _.defer call
-			var clock = sinon.useFakeTimers();
 			var fileData = {name: 'conflict.txt'};
 			var uploadData = addFiles(uploader, [
 				fileData
@@ -272,15 +276,15 @@ describe('OC.Upload tests', function() {
 					expect(uploadData[0].submit.calledOnce).toEqual(true);
 					getResponseStatusStub.returns(412);
 					uploader.fileUploadParam.fail.call($dummyUploader[0], {}, uploadData[0]);
-					clock.tick(500);
 				}
 				if(counter===2)
 				{
-					expect(upload.getFileName()).toEqual('conflict (3).txt');
-					expect(uploadData[0].submit.calledTwice).toEqual(true);
+					_.defer(function() {
+						expect(upload.getFileName()).toEqual('conflict (3).txt');
+						expect(uploadData[0].submit.calledTwice).toEqual(true);
 
-					clock.restore();
-					done();
+						done();
+					})
 				}
 			});
 
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index ee70a1452a9212b4c8f497c2c97e1726a0a5c6ac..daa917354551f7078996db8badb534f7b10bf3e6 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -200,15 +200,6 @@ describe('OCA.Files.FileList tests', function() {
 		});
 	});
 	describe('Adding files', function() {
-		var clock, now;
-		beforeEach(function() {
-			// to prevent date comparison issues
-			clock = sinon.useFakeTimers();
-			now = new Date();
-		});
-		afterEach(function() {
-			clock.restore();
-		});
 		it('generates file element with correct attributes when calling add() with file data', function() {
 			var fileData = new FileInfo({
 				id: 18,
@@ -300,7 +291,6 @@ describe('OCA.Files.FileList tests', function() {
 				name: 'testFile.txt'
 			};
 
-			clock.tick(123456);
 			var $tr = fileList.add(fileData);
 
 			expect($tr).toBeDefined();
@@ -312,7 +302,6 @@ describe('OCA.Files.FileList tests', function() {
 			expect($tr.attr('data-etag')).toBeUndefined();
 			expect($tr.attr('data-permissions')).toEqual('31');
 			expect($tr.attr('data-mime')).toBeUndefined();
-			expect($tr.attr('data-mtime')).toEqual('123456');
 			expect($tr.attr('data-e2eencrypted')).toEqual('false');
 
 			expect($tr.find('.filesize').text()).toEqual('Pending');
@@ -323,7 +312,6 @@ describe('OCA.Files.FileList tests', function() {
 				type: 'dir',
 				name: 'testFolder'
 			};
-			clock.tick(123456);
 			var $tr = fileList.add(fileData);
 
 			expect($tr).toBeDefined();
@@ -335,7 +323,6 @@ describe('OCA.Files.FileList tests', function() {
 			expect($tr.attr('data-etag')).toBeUndefined();
 			expect($tr.attr('data-permissions')).toEqual('31');
 			expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
-			expect($tr.attr('data-mtime')).toEqual('123456');
 			expect($tr.attr('data-e2eencrypted')).toEqual('false');
 
 			expect($tr.find('.filesize').text()).toEqual('Pending');
@@ -584,7 +571,6 @@ describe('OCA.Files.FileList tests', function() {
 			deferredDelete1.resolve(200);
 			deferredDelete2.resolve(200);
 			return promise.then(function(){
-
 				expect(fileList.findFileEl('One.txt').length).toEqual(0);
 				expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
 				expect(fileList.findFileEl('Three.pdf').length).toEqual(1);
@@ -602,8 +588,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect($('#emptycontent').hasClass('hidden')).toEqual(true);
 
 				expect(notificationStub.notCalled).toEqual(true);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('shows busy state on files to be deleted', function(done) {
 			fileList.setFiles(testFiles);
@@ -633,8 +618,7 @@ describe('OCA.Files.FileList tests', function() {
 			return promise.then(function(){
 				expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(false);
 				expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(false);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('shows busy state on all files when deleting all', function(done) {
 			fileList.setFiles(testFiles);
@@ -656,8 +640,7 @@ describe('OCA.Files.FileList tests', function() {
 			}
 			return promise.then(function(){
 				expect(count).toEqual(4);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('updates summary when deleting last file', function(done) {
 			var $summary;
@@ -673,8 +656,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.files.length).toEqual(0);
 				expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
 				expect($('#emptycontent').hasClass('hidden')).toEqual(false);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('bring back deleted item when delete call failed', function(done) {
 			fileList.setFiles(testFiles);
@@ -688,8 +670,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.$fileList.find('tr').length).toEqual(4);
 
 				expect(notificationStub.calledTwice).toEqual(true);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('remove file from list if delete call returned 404 not found', function(done) {
 			fileList.setFiles(testFiles);
@@ -702,8 +683,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.$fileList.find('tr').length).toEqual(2);
 
 				expect(notificationStub.notCalled).toEqual(true);
-				done();
-			});
+			}).then(done, done);
 		});
 	});
 	describe('Renaming files', function() {
@@ -870,7 +850,7 @@ describe('OCA.Files.FileList tests', function() {
 			expect(fileList.$fileList.find('input.filename').length).toEqual(0);
 			expect(fileList.$fileList.find('form').length).toEqual(0);
 		});
-		it('Restores thumbnail when rename was cancelled', function() {
+		it('Restores thumbnail when rename was cancelled', function(done) {
 			doRename();
 
 			expect(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail').parent().attr('class'))
@@ -878,9 +858,11 @@ describe('OCA.Files.FileList tests', function() {
 
 			deferredRename.reject(409);
 
-			expect(fileList.findFileEl('One.txt').length).toEqual(1);
-			expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
-				.toEqual(OC.imagePath('core', 'filetypes/text.svg'));
+			return Promise.resolve().then(function() {
+				expect(fileList.findFileEl('One.txt').length).toEqual(1);
+				expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
+					.toEqual(OC.imagePath('core', 'filetypes/text.svg'));
+			}).then(done, done);
 		});
 	});
 	describe('Moving files', function() {
@@ -917,8 +899,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
 
 				expect(notificationStub.notCalled).toEqual(true);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('Moves list of files to target folder', function(done) {
 			var deferredMove1 = $.Deferred();
@@ -954,8 +935,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
 
 				expect(notificationStub.notCalled).toEqual(true);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('Shows notification if a file could not be moved', function(done) {
 			moveStub.callsFake(function(){
@@ -968,8 +948,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.findFileEl('One.txt').length).toEqual(1);
 				expect(notificationStub.calledOnce).toEqual(true);
 				expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
-				done();
-			});
+			}).then(done, done);
 		});
 		it('Restores thumbnail if a file could not be moved', function(done) {
 			moveStub.callsFake(function(){
@@ -986,8 +965,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
 				expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
 					.toEqual(OC.imagePath('core', 'filetypes/text.svg'));
-				done();
-			});
+			}).then(done, done);
 		});
 	});
 
@@ -1026,8 +1004,7 @@ describe('OCA.Files.FileList tests', function() {
 
 				// Copying sents a notification to tell that we've successfully copied file
 				expect(notificationStub.notCalled).toEqual(false);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('Copies list of files to target folder', function(done) {
 			var deferredCopy1 = $.Deferred();
@@ -1061,8 +1038,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
 
 				expect(notificationStub.notCalled).toEqual(false);
-				done();
-			});
+			}).then(done, done);
 		});
 		it('Shows notification if a file could not be copied', function(done) {
 			copyStub.callsFake(function(){
@@ -1076,8 +1052,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.findFileEl('One.txt').length).toEqual(1);
 				expect(notificationStub.calledOnce).toEqual(true);
 				expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
-				done();
-			});
+			}).then(done, done);
 		});
 		it('Restores thumbnail if a file could not be copied', function(done) {
 			copyStub.callsFake(function(){
@@ -1097,8 +1072,7 @@ describe('OCA.Files.FileList tests', function() {
 
 				expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
 					.toEqual(OC.imagePath('core', 'filetypes/text.svg'));
-				done();
-			});
+			}).then(done, done);
 		});
 	});
 
@@ -1677,13 +1651,15 @@ describe('OCA.Files.FileList tests', function() {
 		afterEach(function() {
 			getFolderContentsStub.restore();
 		});
-		it('fetches file list from server and renders it when reload() is called', function() {
-			fileList.reload();
+		it('fetches file list from server and renders it when reload() is called', function(done) {
+			var reloading = fileList.reload();
 			expect(getFolderContentsStub.calledOnce).toEqual(true);
 			expect(getFolderContentsStub.calledWith('/subdir')).toEqual(true);
 			deferredList.resolve(200, [testRoot].concat(testFiles));
-			expect($('#fileList tr').length).toEqual(4);
-			expect(fileList.findFileEl('One.txt').length).toEqual(1);
+			return reloading.then(function() {
+				expect($('#fileList tr').length).toEqual(4);
+				expect(fileList.findFileEl('One.txt').length).toEqual(1);
+			}).then(done, done);
 		});
 		it('switches dir and fetches file list when calling changeDirectory()', function() {
 			fileList.changeDirectory('/anothersubdir');
@@ -1724,59 +1700,91 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fileList.getCurrentDirectory()).toEqual(path);
 			});
 		});
-		it('switches to root dir when current directory does not exist', function() {
-			fileList.changeDirectory('/unexist');
+		it('switches to root dir when current directory does not exist', function(done) {
+			var changing = fileList.changeDirectory('/unexist');
+
 			deferredList.reject(404);
-			expect(fileList.getCurrentDirectory()).toEqual('/');
+
+			return changing.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+			}).then(done, done);
 		});
-		it('switches to root dir when current directory returns 400', function() {
-			fileList.changeDirectory('/unexist');
+		it('switches to root dir when current directory returns 400', function(done) {
+			var changing = fileList.changeDirectory('/unexist');
+
 			deferredList.reject(400);
-			expect(fileList.getCurrentDirectory()).toEqual('/');
+
+			return changing.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+			}).then(done, done);
 		});
-		it('switches to root dir when current directory returns 405', function() {
-			fileList.changeDirectory('/unexist');
+		it('switches to root dir when current directory returns 405', function(done) {
+			var changing = fileList.changeDirectory('/unexist');
+
 			deferredList.reject(405);
-			expect(fileList.getCurrentDirectory()).toEqual('/');
+
+			return changing.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+			}).then(done, done);
 		});
-		it('switches to root dir when current directory is forbidden', function() {
-			fileList.changeDirectory('/unexist');
+		it('switches to root dir when current directory is forbidden', function(done) {
+			var changing = fileList.changeDirectory('/unexist');
+
 			deferredList.reject(403);
-			expect(fileList.getCurrentDirectory()).toEqual('/');
+
+			return changing.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+			}).then(done, done);
 		});
-		it('switches to root dir when current directory is unavailable', function() {
-			fileList.changeDirectory('/unexist');
+		it('switches to root dir when current directory is unavailable', function(done) {
+			var changing = fileList.changeDirectory('/unexist');
+
 			deferredList.reject(500);
-			expect(fileList.getCurrentDirectory()).toEqual('/');
+
+			return changing.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+			}).then(done, done);
 		});
-		it('shows mask before loading file list then hides it at the end', function() {
+		it('shows mask before loading file list then hides it at the end', function(done) {
 			var showMaskStub = sinon.stub(fileList, 'showMask');
 			var hideMaskStub = sinon.stub(fileList, 'hideMask');
-			fileList.changeDirectory('/anothersubdir');
+
+			var changing = fileList.changeDirectory('/anothersubdir');
 			expect(showMaskStub.calledOnce).toEqual(true);
 			expect(hideMaskStub.calledOnce).toEqual(false);
 			deferredList.resolve(200, [testRoot].concat(testFiles));
-			expect(showMaskStub.calledOnce).toEqual(true);
-			expect(hideMaskStub.calledOnce).toEqual(true);
-			showMaskStub.restore();
-			hideMaskStub.restore();
+
+			return changing.then(function() {
+				expect(showMaskStub.calledOnce).toEqual(true);
+				expect(hideMaskStub.calledOnce).toEqual(true);
+				showMaskStub.restore();
+				hideMaskStub.restore();
+			}).then(done, done);
 		});
-		it('triggers "changeDirectory" event when changing directory', function() {
+		it('triggers "changeDirectory" event when changing directory', function(done) {
 			var handler = sinon.stub();
 			$('#app-content-files').on('changeDirectory', handler);
-			fileList.changeDirectory('/somedir');
+			var changing = fileList.changeDirectory('/somedir');
+
 			deferredList.resolve(200, [testRoot].concat(testFiles));
-			expect(handler.calledOnce).toEqual(true);
-			expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
+
+			return changing.then(function() {
+				expect(handler.calledOnce).toEqual(true);
+				expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
+			}).then(done, done);
 		});
-		it('triggers "afterChangeDirectory" event with fileid after changing directory', function() {
+		it('triggers "afterChangeDirectory" event with fileid after changing directory', function(done) {
 			var handler = sinon.stub();
 			$('#app-content-files').on('afterChangeDirectory', handler);
-			fileList.changeDirectory('/somedir');
+			var changing = fileList.changeDirectory('/somedir');
+
 			deferredList.resolve(200, [testRoot].concat(testFiles));
-			expect(handler.calledOnce).toEqual(true);
-			expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
-			expect(handler.getCall(0).args[0].fileId).toEqual(99);
+
+			return changing.then(function() {
+				expect(handler.calledOnce).toEqual(true);
+				expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
+				expect(handler.getCall(0).args[0].fileId).toEqual(99);
+			}).then(done, done);
 		});
 		it('changes the directory when receiving "urlChanged" event', function() {
 			$('#app-content-files').trigger(new $.Event('urlChanged', {view: 'files', dir: '/somedir'}));
@@ -1843,10 +1851,9 @@ describe('OCA.Files.FileList tests', function() {
 			expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
 			changeDirStub.restore();
 		});
-		it('dropping files on breadcrumb calls move operation', function(done) {
+		it('dropping files on breadcrumb calls move operation', function() {
 			var testDir = '/subdir/two/three with space/four/five';
 			var moveStub = sinon.stub(filesClient, 'move');
-			var resolve1, resolve2;
 			var deferredMove1 = $.Deferred();
 			var deferredMove2 = $.Deferred();
 			moveStub.onCall(0).returns(deferredMove1.promise());
@@ -1873,7 +1880,6 @@ describe('OCA.Files.FileList tests', function() {
 				expect(moveStub.getCall(1).args[0]).toEqual(testDir + '/Two.jpg');
 				expect(moveStub.getCall(1).args[1]).toEqual('/subdir/two/three with space/Two.jpg');
 				moveStub.restore();
-				done();
 			});
 			deferredMove1.resolve(201);
 			deferredMove2.resolve(201);
@@ -2362,7 +2368,6 @@ describe('OCA.Files.FileList tests', function() {
 					$('.selectedActions .filesSelectMenu .delete').click();
 					deferredDelete.resolve(204);
 					return deferred.promise();
-
 				});
 				it('Deletes all files when all selected when "Delete" clicked', function(done) {
 					var deferred = $.Deferred();
@@ -2811,8 +2816,8 @@ describe('OCA.Files.FileList tests', function() {
 			getFileInfoStub.restore();
 		});
 
-		it('creates file with given name and adds it to the list', function() {
-			fileList.createFile('test.txt');
+		it('creates file with given name and adds it to the list', function(done) {
+			var creating = fileList.createFile('test.txt');
 
 			expect(createStub.calledOnce).toEqual(true);
 			expect(createStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
@@ -2835,9 +2840,11 @@ describe('OCA.Files.FileList tests', function() {
 				})
 			);
 
-			var $tr = fileList.findFileEl('test.txt');
-			expect($tr.length).toEqual(1);
-			expect($tr.attr('data-mime')).toEqual('text/plain');
+			return creating.then(function() {
+				var $tr = fileList.findFileEl('test.txt');
+				expect($tr.length).toEqual(1);
+				expect($tr.attr('data-mime')).toEqual('text/plain');
+			}).then(done, done);
 		});
 		// TODO: error cases
 		// TODO: unique name cases
@@ -2861,8 +2868,8 @@ describe('OCA.Files.FileList tests', function() {
 			getFileInfoStub.restore();
 		});
 
-		it('creates folder with given name and adds it to the list', function() {
-			fileList.createDirectory('sub dir');
+		it('creates folder with given name and adds it to the list', function(done) {
+			var creating = fileList.createDirectory('sub dir');
 
 			expect(createStub.calledOnce).toEqual(true);
 			expect(createStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
@@ -2881,9 +2888,11 @@ describe('OCA.Files.FileList tests', function() {
 				})
 			);
 
-			var $tr = fileList.findFileEl('sub dir');
-			expect($tr.length).toEqual(1);
-			expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
+			return creating.then(function() {
+				var $tr = fileList.findFileEl('sub dir');
+				expect($tr.length).toEqual(1);
+				expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
+			}).then(done, done);
 		});
 		// TODO: error cases
 		// TODO: unique name cases
@@ -2906,21 +2915,27 @@ describe('OCA.Files.FileList tests', function() {
 
 			expect(promise.state()).toEqual('resolved');
 		});
-		it('fetches info when folder is the current one', function() {
+		it('fetches info when folder is the current one', function(done) {
 			fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
-			expect(getFileInfoStub.calledOnce).toEqual(true);
-			expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/testfile.txt');
+
+			return Promise.resolve().then(function() {
+				expect(getFileInfoStub.calledOnce).toEqual(true);
+				expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/testfile.txt');
+			}).then(done, done);
 		});
-		it('adds file data to list when fetching is done', function() {
-			fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
+		it('adds file data to list when fetching is done', function(done) {
+			var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
 			getFileInfoDeferred.resolve(200, {
 				name: 'testfile.txt',
 				size: 100
 			});
-			expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
+
+			return adding.then(function() {
+				expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
+			}).then(done, done);
 		});
-		it('replaces file data to list when fetching is done', function() {
-			fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
+		it('replaces file data to list when fetching is done', function(done) {
+			var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
 			fileList.add({
 				name: 'testfile.txt',
 				size: 95
@@ -2929,20 +2944,24 @@ describe('OCA.Files.FileList tests', function() {
 				name: 'testfile.txt',
 				size: 100
 			});
-			expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
+			expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('95');
+			return adding.then(function() {
+				expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
+			}).then(done, done);
 		});
-		it('resolves promise with file data when fetching is done', function() {
+		it('resolves promise with file data when fetching is done', function(done) {
 			var promise = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
 			getFileInfoDeferred.resolve(200, {
 				name: 'testfile.txt',
 				size: 100
 			});
-			expect(promise.state()).toEqual('resolved');
-			promise.then(function(status, data) {
+			expect(promise.state()).toEqual('pending');
+			return promise.then(function(status, data) {
+				expect(promise.state()).toEqual('resolved');
 				expect(status).toEqual(200);
 				expect(data.name).toEqual('testfile.txt');
 				expect(data.size).toEqual(100);
-			});
+			}).then(done, done);
 		});
 	});
 	/**
@@ -3018,17 +3037,16 @@ describe('OCA.Files.FileList tests', function() {
 				uploader.trigger('drop', eventData, data || {});
 				return !!data.targetDir;
 			}
+			it('drop on a tr or crumb outside file list does not trigger upload', function() {
+				var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
+				var ev;
+				$('#testArea').append($anotherTable);
+				ev = dropOn($anotherTable.find('tr'), uploadData);
+				expect(ev).toEqual(false);
 
-				it('drop on a tr or crumb outside file list does not trigger upload', function() {
-					var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
-					var ev;
-					$('#testArea').append($anotherTable);
-					ev = dropOn($anotherTable.find('tr'), uploadData);
-					expect(ev).toEqual(false);
-
-					ev = dropOn($anotherTable.find('.crumb'), uploadData);
-					expect(ev).toEqual(false);
-				});
+				ev = dropOn($anotherTable.find('.crumb'), uploadData);
+				expect(ev).toEqual(false);
+			});
 			it('drop on an element outside file list container does not trigger upload', function() {
 				var $anotherEl = $('<div>outside</div>');
 				var ev;
@@ -3167,7 +3185,7 @@ describe('OCA.Files.FileList tests', function() {
 				expect(fetchInfoStub.getCall(0).args[0]).toEqual('upload.txt');
 				expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
 			});
-			it('highlights all uploaded files after all fetches are done', function() {
+			it('highlights all uploaded files after all fetches are done', function(done) {
 				var highlightStub = sinon.stub(fileList, 'highlightFiles');
 				var def1 = addFile(createUpload('upload.txt', '/subdir'));
 				var def2 = addFile(createUpload('upload2.txt', '/subdir'));
@@ -3179,12 +3197,16 @@ describe('OCA.Files.FileList tests', function() {
 				expect(highlightStub.notCalled).toEqual(true);
 				def2.resolve();
 				def3.resolve();
-				expect(highlightStub.calledOnce).toEqual(true);
-				expect(highlightStub.getCall(0).args[0]).toEqual(['upload.txt', 'upload2.txt']);
+				setTimeout(function() {
+					expect(highlightStub.callCount).toEqual(1);
+					expect(highlightStub.getCall(0).args[0]).toEqual(['upload.txt', 'upload2.txt']);
+
+					highlightStub.restore();
 
-				highlightStub.restore();
+					done();
+				}, 5);
 			});
-			it('queries storage stats after all fetches are done', function() {
+			it('queries storage stats after all fetches are done', function(done) {
 				var statStub = sinon.stub(fileList, 'updateStorageStatistics');
 				var highlightStub = sinon.stub(fileList, 'highlightFiles');
 				var def1 = addFile(createUpload('upload.txt', '/subdir'));
@@ -3197,15 +3219,20 @@ describe('OCA.Files.FileList tests', function() {
 				expect(statStub.notCalled).toEqual(true);
 				def2.resolve();
 				def3.resolve();
-				expect(statStub.calledOnce).toEqual(true);
+				setTimeout(function() {
+					expect(statStub.calledOnce).toEqual(true);
 
-				highlightStub.restore();
+					highlightStub.restore();
+
+					done();
+				}, 3);
 			});
 		});
 	});
 	describe('Handling errors', function () {
 		var deferredList;
 		var getFolderContentsStub;
+		var reloading;
 
 		beforeEach(function() {
 			deferredList = $.Deferred();
@@ -3213,33 +3240,39 @@ describe('OCA.Files.FileList tests', function() {
 				sinon.stub(filesClient, 'getFolderContents');
 			getFolderContentsStub.onCall(0).returns(deferredList.promise());
 			getFolderContentsStub.onCall(1).returns($.Deferred().promise());
-			fileList.reload();
+			reloading = fileList.reload();
 		});
 		afterEach(function() {
 			getFolderContentsStub.restore();
 			fileList = undefined;
 		});
-		it('redirects to root folder in case of forbidden access', function () {
+		it('redirects to root folder in case of forbidden access', function (done) {
 			deferredList.reject(403);
 
-			expect(fileList.getCurrentDirectory()).toEqual('/');
-			expect(getFolderContentsStub.calledTwice).toEqual(true);
+			return reloading.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+				expect(getFolderContentsStub.calledTwice).toEqual(true);
+			}).then(done, done);
 		});
-		it('redirects to root folder and shows notification in case of internal server error', function () {
+		it('redirects to root folder and shows notification in case of internal server error', function (done) {
 			expect(notificationStub.notCalled).toEqual(true);
 			deferredList.reject(500);
 
-			expect(fileList.getCurrentDirectory()).toEqual('/');
-			expect(getFolderContentsStub.calledTwice).toEqual(true);
-			expect(notificationStub.calledOnce).toEqual(true);
+			return reloading.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+				expect(getFolderContentsStub.calledTwice).toEqual(true);
+				expect(notificationStub.calledOnce).toEqual(true);
+			}).then(done, done);
 		});
-		it('redirects to root folder and shows notification in case of storage not available', function () {
+		it('redirects to root folder and shows notification in case of storage not available', function (done) {
 			expect(notificationStub.notCalled).toEqual(true);
 			deferredList.reject(503, 'Storage is temporarily not available');
 
-			expect(fileList.getCurrentDirectory()).toEqual('/');
-			expect(getFolderContentsStub.calledTwice).toEqual(true);
-			expect(notificationStub.calledOnce).toEqual(true);
+			return reloading.then(function() {
+				expect(fileList.getCurrentDirectory()).toEqual('/');
+				expect(getFolderContentsStub.calledTwice).toEqual(true);
+				expect(notificationStub.calledOnce).toEqual(true);
+			}).then(done, done);
 		});
 	});
 	describe('showFileBusyState', function() {
@@ -3255,7 +3288,6 @@ describe('OCA.Files.FileList tests', function() {
 			expect($tr.find('.thumbnail').parent().attr('class'))
 				.toContain('icon-loading-small');
 
-
 			fileList.showFileBusyState('Two.jpg', false);
 			expect($tr.hasClass('busy')).toEqual(false);
 			expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail')))
diff --git a/apps/files/tests/js/tagspluginspec.js b/apps/files/tests/js/tagspluginspec.js
index 363a8bb0e19c547a715af9d1ec3f7a795d72ab13..66bf3d5a6984067b6d74ddc6c49e9ad1b42066a2 100644
--- a/apps/files/tests/js/tagspluginspec.js
+++ b/apps/files/tests/js/tagspluginspec.js
@@ -73,7 +73,7 @@ describe('OCA.Files.TagsPlugin tests', function() {
 		});
 	});
 	describe('Applying tags', function() {
-		it('through FileActionsMenu sends request to server and updates icon', function() {
+		it('through FileActionsMenu sends request to server and updates icon', function(done) {
 			var request;
 			fileList.setFiles(testFiles);
 			var $tr = fileList.findFileEl('One.txt');
@@ -92,42 +92,51 @@ describe('OCA.Files.TagsPlugin tests', function() {
 				tags: ['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]
 			}));
 
-			// re-read the element as it was re-inserted
-			$tr = fileList.findFileEl('One.txt');
-			$favoriteMark = $tr.find('.favorite-mark');
-			$showMenuAction = $tr.find('.action-menu');
+			setTimeout(function () {
+				// re-read the element as it was re-inserted
+				$tr = fileList.findFileEl('One.txt');
+				$favoriteMark = $tr.find('.favorite-mark');
+				$showMenuAction = $tr.find('.action-menu');
 
-			expect($tr.attr('data-favorite')).toEqual('true');
-			expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
-			expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
-			expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(false);
-			expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(true);
-
-			// show again the menu and get the new action, as the menu was
-			// closed and removed (and with it, the previous action) when that
-			// action was clicked
-			$showMenuAction.click();
-			$favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
-			$favoriteActionInMenu.click();
+				expect($tr.attr('data-favorite')).toEqual('true');
+				expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
+				expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
+				expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(false);
+				expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(true);
 
-			expect(fakeServer.requests.length).toEqual(2);
-			request = fakeServer.requests[1];
-			expect(JSON.parse(request.requestBody)).toEqual({
-				tags: ['tag1', 'tag2', 'tag3']
-			});
-			request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
-				tags: ['tag1', 'tag2', 'tag3']
-			}));
+				// show again the menu and get the new action, as the menu was
+				// closed and removed (and with it, the previous action) when that
+				// action was clicked
+				$showMenuAction.click();
+				$favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
+				$favoriteActionInMenu.click();
 
-			// re-read the element as it was re-inserted
-			$tr = fileList.findFileEl('One.txt');
-			$favoriteMark = $tr.find('.favorite-mark');
+				setTimeout(function() {
+					expect(fakeServer.requests.length).toEqual(2);
+					request = fakeServer.requests[1];
+					expect(JSON.parse(request.requestBody)).toEqual({
+						tags: ['tag1', 'tag2', 'tag3']
+					});
+
+					request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
+						tags: ['tag1', 'tag2', 'tag3']
+					}));
+
+					setTimeout(function() {
+						// re-read the element as it was re-inserted
+						$tr = fileList.findFileEl('One.txt');
+						$favoriteMark = $tr.find('.favorite-mark');
+
+						expect($tr.attr('data-favorite')).toBeFalsy();
+						expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
+						expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);
+						expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(true);
+						expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(false);
 
-			expect($tr.attr('data-favorite')).toBeFalsy();
-			expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
-			expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);
-			expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(true);
-			expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(false);
+						done();
+					}, 1);
+				}, 1);
+			}, 1);
 		});
 	});
 	describe('elementToFile', function() {
diff --git a/apps/files_external/tests/js/mountsfilelistSpec.js b/apps/files_external/tests/js/mountsfilelistSpec.js
index fe2fd8dec847036d1e879be59942065cc4e0de43..6bed62ed5c6f0504f602069c5e1ce69147b01c0e 100644
--- a/apps/files_external/tests/js/mountsfilelistSpec.js
+++ b/apps/files_external/tests/js/mountsfilelistSpec.js
@@ -60,13 +60,14 @@ describe('OCA.Files_External.FileList tests', function() {
 
 	describe('loading file list for external storages', function() {
 		var ocsResponse;
+		var reloading;
 
 		beforeEach(function() {
 			fileList = new OCA.Files_External.FileList(
 				$('#app-content-container')
 			);
 
-			fileList.reload();
+			reloading = fileList.reload();
 
 			/* jshint camelcase: false */
 			ocsResponse = {
@@ -94,7 +95,7 @@ describe('OCA.Files_External.FileList tests', function() {
 				}
 			};
 		});
-		it('render storage list', function() {
+		it('render storage list', function(done) {
 			var request;
 			var $rows;
 			var $tr;
@@ -112,41 +113,42 @@ describe('OCA.Files_External.FileList tests', function() {
 				JSON.stringify(ocsResponse)
 			);
 
-			$rows = fileList.$el.find('tbody tr');
-			expect($rows.length).toEqual(2);
+			return reloading.then(function() {
+				$rows = fileList.$el.find('tbody tr');
+				expect($rows.length).toEqual(2);
 
-			$tr = $rows.eq(0);
-			expect($tr.attr('data-id')).not.toBeDefined();
-			expect($tr.attr('data-type')).toEqual('dir');
-			expect($tr.attr('data-file')).toEqual('sftp mount');
-			expect($tr.attr('data-path')).toEqual('/another mount points');
-			expect($tr.attr('data-size')).not.toBeDefined();
-			expect($tr.attr('data-permissions')).toEqual('1'); // read only 
-			expect($tr.find('a.name').attr('href')).toEqual(
-				OC.getRootPath() +
-				'/index.php/apps/files' +
-				'?dir=/another%20mount%20points/sftp%20mount'
-			);
-			expect($tr.find('.nametext').text().trim()).toEqual('sftp mount');
-			expect($tr.find('.column-scope > span').text().trim()).toEqual('System');
-			expect($tr.find('.column-backend').text().trim()).toEqual('SFTP');
-
-			$tr = $rows.eq(1);
-			expect($tr.attr('data-id')).not.toBeDefined();
-			expect($tr.attr('data-type')).toEqual('dir');
-			expect($tr.attr('data-file')).toEqual('smb mount');
-			expect($tr.attr('data-path')).toEqual('/mount points');
-			expect($tr.attr('data-size')).not.toBeDefined();
-			expect($tr.attr('data-permissions')).toEqual('9'); // read and delete
-			expect($tr.find('a.name').attr('href')).toEqual(
-				OC.getRootPath() +
-				'/index.php/apps/files' +
-				'?dir=/mount%20points/smb%20mount'
-			);
-			expect($tr.find('.nametext').text().trim()).toEqual('smb mount');
-			expect($tr.find('.column-scope > span').text().trim()).toEqual('Personal');
-			expect($tr.find('.column-backend').text().trim()).toEqual('SMB');
+				$tr = $rows.eq(0);
+				expect($tr.attr('data-id')).not.toBeDefined();
+				expect($tr.attr('data-type')).toEqual('dir');
+				expect($tr.attr('data-file')).toEqual('sftp mount');
+				expect($tr.attr('data-path')).toEqual('/another mount points');
+				expect($tr.attr('data-size')).not.toBeDefined();
+				expect($tr.attr('data-permissions')).toEqual('1'); // read only
+				expect($tr.find('a.name').attr('href')).toEqual(
+					OC.getRootPath() +
+					'/index.php/apps/files' +
+					'?dir=/another%20mount%20points/sftp%20mount'
+				);
+				expect($tr.find('.nametext').text().trim()).toEqual('sftp mount');
+				expect($tr.find('.column-scope > span').text().trim()).toEqual('System');
+				expect($tr.find('.column-backend').text().trim()).toEqual('SFTP');
 
+				$tr = $rows.eq(1);
+				expect($tr.attr('data-id')).not.toBeDefined();
+				expect($tr.attr('data-type')).toEqual('dir');
+				expect($tr.attr('data-file')).toEqual('smb mount');
+				expect($tr.attr('data-path')).toEqual('/mount points');
+				expect($tr.attr('data-size')).not.toBeDefined();
+				expect($tr.attr('data-permissions')).toEqual('9'); // read and delete
+				expect($tr.find('a.name').attr('href')).toEqual(
+					OC.getRootPath() +
+					'/index.php/apps/files' +
+					'?dir=/mount%20points/smb%20mount'
+				);
+				expect($tr.find('.nametext').text().trim()).toEqual('smb mount');
+				expect($tr.find('.column-scope > span').text().trim()).toEqual('Personal');
+				expect($tr.find('.column-backend').text().trim()).toEqual('SMB');
+			}).then(done, done);
 		});
 	});
 });
diff --git a/apps/files_trashbin/tests/js/filelistSpec.js b/apps/files_trashbin/tests/js/filelistSpec.js
index 11ba49c487ee3ebc209fbeeeded0a82595b24e79..a655acb208c90940ab041a3287702bf01d7d88ce 100644
--- a/apps/files_trashbin/tests/js/filelistSpec.js
+++ b/apps/files_trashbin/tests/js/filelistSpec.js
@@ -316,7 +316,7 @@ describe('OCA.Trashbin.FileList tests', function () {
 				expect($('.selectedActions .item-delete').is(':visible')).toEqual(false);
 				expect($('.selectedActions .item-restore').is(':visible')).toEqual(false);
 			});
-			it('Deletes selected files when "Delete" clicked', function () {
+			it('Deletes selected files when "Delete" clicked', function (done) {
 				var request;
 				var promise = fileList._onClickDeleteSelected({
 					preventDefault: function () {
@@ -334,9 +334,9 @@ describe('OCA.Trashbin.FileList tests', function () {
 					expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0);
 					expect(fileList.findFileEl('somedir.d99999').length).toEqual(0);
 					expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1);
-				});
+				}).then(done, done);
 			});
-			it('Deletes all files when all selected when "Delete" clicked', function () {
+			it('Deletes all files when all selected when "Delete" clicked', function (done) {
 				var request;
 				$('.select-all').click();
 				var promise = fileList._onClickDeleteSelected({
@@ -349,11 +349,11 @@ describe('OCA.Trashbin.FileList tests', function () {
 				request.respond(200);
 				return promise.then(function () {
 					expect(fileList.isEmpty).toEqual(true);
-				});
+				}).then(done, done);
 			});
 		});
 		describe('Restore', function () {
-			it('Restores selected files when "Restore" clicked', function () {
+			it('Restores selected files when "Restore" clicked', function (done) {
 				var request;
 				var promise = fileList._onClickRestoreSelected({
 					preventDefault: function () {
@@ -372,9 +372,9 @@ describe('OCA.Trashbin.FileList tests', function () {
 					expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0);
 					expect(fileList.findFileEl('somedir.d99999').length).toEqual(0);
 					expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1);
-				});
+				}).then(done, done);
 			});
-			it('Restores all files when all selected when "Restore" clicked', function () {
+			it('Restores all files when all selected when "Restore" clicked', function (done) {
 				var request;
 				$('.select-all').click();
 				var promise = fileList._onClickRestoreSelected({
@@ -391,7 +391,7 @@ describe('OCA.Trashbin.FileList tests', function () {
 				}
 				return promise.then(function() {
 					expect(fileList.isEmpty).toEqual(true);
-				});
+				}).then(done, done);
 			});
 		});
 	});
diff --git a/apps/systemtags/tests/js/systemtagsfilelistSpec.js b/apps/systemtags/tests/js/systemtagsfilelistSpec.js
index ba41d347ca45175904e88a3032dd2b3d7fd81eb7..341ddb60365895ecf872c6d758c31833f58a754e 100644
--- a/apps/systemtags/tests/js/systemtagsfilelistSpec.js
+++ b/apps/systemtags/tests/js/systemtagsfilelistSpec.js
@@ -149,10 +149,10 @@ describe('OCA.SystemTags.FileList tests', function() {
 			getFilteredFilesSpec = sinon.stub(OC.Files.Client.prototype, 'getFilteredFiles')
 				.returns(requestDeferred.promise());
 		});
-		afterEach(function() { 
+		afterEach(function() {
 			getFilteredFilesSpec.restore();
 		});
-		
+
 		it('renders empty message when no tags were set', function() {
 			fileList = new OCA.SystemTags.FileList(
 				$('#app-content-container'), {
@@ -167,14 +167,14 @@ describe('OCA.SystemTags.FileList tests', function() {
 			expect(getFilteredFilesSpec.notCalled).toEqual(true);
 		});
 
-		it('render files', function() {
+		it('render files', function(done) {
 			fileList = new OCA.SystemTags.FileList(
 				$('#app-content-container'), {
 					systemTagIds: ['123', '456']
 				}
 			);
 
-			fileList.reload();
+			var reloading = fileList.reload();
 
 			expect(getFilteredFilesSpec.calledOnce).toEqual(true);
 			expect(getFilteredFilesSpec.lastCall.args[0].systemTagIds).toEqual(['123', '456']);
@@ -219,8 +219,10 @@ describe('OCA.SystemTags.FileList tests', function() {
 
 			requestDeferred.resolve(207, testFiles);
 
-			expect(fileList.$el.find('#emptycontent').hasClass('hidden')).toEqual(true);
-			expect(fileList.$el.find('tbody>tr').length).toEqual(4);
+			return reloading.then(function() {
+				expect(fileList.$el.find('#emptycontent').hasClass('hidden')).toEqual(true);
+				expect(fileList.$el.find('tbody>tr').length).toEqual(4);
+			}).then(done, done);
 		});
 	});
 });
diff --git a/apps/systemtags/tests/js/systemtagsinfoviewSpec.js b/apps/systemtags/tests/js/systemtagsinfoviewSpec.js
index 99c0471d505684b895753ca1320c714101b028da..0fb0cf4edf242ee576c57f529dec8b0a7e65bf83 100644
--- a/apps/systemtags/tests/js/systemtagsinfoviewSpec.js
+++ b/apps/systemtags/tests/js/systemtagsinfoviewSpec.js
@@ -243,7 +243,6 @@ describe('OCA.SystemTags.SystemTagsInfoView tests', function() {
 			view.openDropdown();
 
 			expect(select2Stub.calledOnce).toBeTruthy();
-			expect(select2Stub.thisValues[0].selector).toEqual('.systemTagsInputField');
 			expect(select2Stub.withArgs('open')).toBeTruthy();
 		});
 	});
diff --git a/core/js/dist/files_client.js b/core/js/dist/files_client.js
index 68bc325a80bef5d807352dc0c556bf16b26e79db..e6a11264078d273bdb09f9880f7afec5203372bc 100644
Binary files a/core/js/dist/files_client.js and b/core/js/dist/files_client.js differ
diff --git a/core/js/dist/files_client.js.map b/core/js/dist/files_client.js.map
index b866ceaa25e9db7937d45fc9f8c0d1a9c10c05a5..f7ae0e81385a19d54a9b2c0a02381239fbdec010 100644
Binary files a/core/js/dist/files_client.js.map and b/core/js/dist/files_client.js.map differ
diff --git a/core/js/dist/install.js b/core/js/dist/install.js
index b10fb5a7a7c6c5f1c9429410771047fe1aff54eb..073af2f23cf138b5ec45369610bc479f40b5bc7e 100644
Binary files a/core/js/dist/install.js and b/core/js/dist/install.js differ
diff --git a/core/js/dist/install.js.map b/core/js/dist/install.js.map
index 8b41a73e77f6c10d54422cbb5fd89c7119347baf..6b4b2450811d8ea4054fff9507d8b4de0c314b89 100644
Binary files a/core/js/dist/install.js.map and b/core/js/dist/install.js.map differ
diff --git a/core/js/dist/login.js b/core/js/dist/login.js
index 3d8946fd4d5a4b76c9cad00985e989352ef1e617..d08feb432593b22b4cebca35e026d77698e01a8f 100644
Binary files a/core/js/dist/login.js and b/core/js/dist/login.js differ
diff --git a/core/js/dist/login.js.map b/core/js/dist/login.js.map
index 3b50aea0577a313dfb709bc28bd06eeae7a8652e..5c2cbea943119cd51e4a021e4949a8356c6d241d 100644
Binary files a/core/js/dist/login.js.map and b/core/js/dist/login.js.map differ
diff --git a/core/js/dist/main.js b/core/js/dist/main.js
index 0fbe33ed58c367b0448e7048dd8f1df1603edfac..c65068967f99ccc8193fbb673f629b3ba62676c8 100644
Binary files a/core/js/dist/main.js and b/core/js/dist/main.js differ
diff --git a/core/js/dist/main.js.map b/core/js/dist/main.js.map
index 0cc735d6656eec58a26c9c54cf7dde410162b98f..c4abc14ac1346142d99a6598d9aeba6c4909f0d3 100644
Binary files a/core/js/dist/main.js.map and b/core/js/dist/main.js.map differ
diff --git a/core/js/tests/specs/contactsmenuSpec.js b/core/js/tests/specs/contactsmenuSpec.js
index 3fa8671e2acdb1e4615a5a623bd956c331175b7c..54e3152b27ab6ee18f096cce876ed6e83a020889 100644
--- a/core/js/tests/specs/contactsmenuSpec.js
+++ b/core/js/tests/specs/contactsmenuSpec.js
@@ -243,13 +243,13 @@ describe('Contacts menu', function() {
 
 			// Open the first one
 			$menuEl.find('.contact').eq(0).find('.other-actions').click();
-			expect($menuEl.find('.contact').eq(0).find('.menu').css('display')).toBe('block');
+			expect($menuEl.find('.contact').eq(0).find('.menu').css('display')).toBe('');
 			expect($menuEl.find('.contact').eq(1).find('.menu').css('display')).toBe('none');
 
 			// Open the second one
 			$menuEl.find('.contact').eq(1).find('.other-actions').click();
 			expect($menuEl.find('.contact').eq(0).find('.menu').css('display')).toBe('none');
-			expect($menuEl.find('.contact').eq(1).find('.menu').css('display')).toBe('block');
+			expect($menuEl.find('.contact').eq(1).find('.menu').css('display')).toBe('');
 
 			// Close the second one
 			$menuEl.find('.contact').eq(1).find('.other-actions').click();
diff --git a/core/js/tests/specs/jquery.contactsmenuSpec.js b/core/js/tests/specs/jquery.contactsmenuSpec.js
index 9cad302a998c011731165d2864542899e0a878b0..c6807c850a962832e45ce4223cff435cbfc1e8cb 100644
--- a/core/js/tests/specs/jquery.contactsmenuSpec.js
+++ b/core/js/tests/specs/jquery.contactsmenuSpec.js
@@ -22,7 +22,9 @@ describe('jquery.contactsMenu tests', function() {
 	});
 
 	afterEach(function() {
+		$selector1.off();
 		$selector1.remove();
+		$selector2.off();
 		$selector2.remove();
 		$appendTo.remove();
 	});
@@ -100,10 +102,12 @@ describe('jquery.contactsMenu tests', function() {
 	});
 
 	describe('send requests to the server and render', function() {
-		it('load a topaction only', function() {
+		it('load a topaction only', function(done) {
 			$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
 			$selector1.click();
 
+			expect(fakeServer.requests[0].method).toEqual('POST');
+			expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
 			fakeServer.requests[0].respond(
 				200,
 				{ 'Content-Type': 'application/json; charset=utf-8' },
@@ -117,13 +121,15 @@ describe('jquery.contactsMenu tests', function() {
 					"actions": []
 				})
 			);
-			expect(fakeServer.requests[0].method).toEqual('POST');
-			expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
 
-			expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="mailto:bar%40baz.wtf"><img src="foo.svg"><span>bar@baz.wtf</span></a></li></ul></div>');
+			$selector1.on('load', function() {
+				expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="mailto:bar%40baz.wtf"><img src="foo.svg"><span>bar@baz.wtf</span></a></li></ul></div>');
+
+				done();
+			});
 		});
 
-		it('load topaction and more actions', function() {
+		it('load topaction and more actions', function(done) {
 			$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
 			$selector1.click();
 
@@ -147,10 +153,14 @@ describe('jquery.contactsMenu tests', function() {
 			expect(fakeServer.requests[0].method).toEqual('POST');
 			expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
 
-			expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="mailto:bar%40baz.wtf"><img src="foo.svg"><span>bar@baz.wtf</span></a></li><li><a href="http://localhost/index.php/apps/contacts"><img src="details.svg"><span>Details</span></a></li></ul></div>');
+			$selector1.on('load', function() {
+				expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="mailto:bar%40baz.wtf"><img src="foo.svg"><span>bar@baz.wtf</span></a></li><li><a href="http://localhost/index.php/apps/contacts"><img src="details.svg"><span>Details</span></a></li></ul></div>');
+
+				done();
+			});
 		});
 
-		it('load no actions', function() {
+		it('load no actions', function(done) {
 			$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
 			$selector1.click();
 
@@ -167,10 +177,14 @@ describe('jquery.contactsMenu tests', function() {
 			expect(fakeServer.requests[0].method).toEqual('POST');
 			expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
 
-			expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>No action available</span></a></li></ul></div>');
+			$selector1.on('load', function() {
+				expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>No action available</span></a></li></ul></div>');
+
+				done();
+			});
 		});
 
-		it('should throw an error', function() {
+		it('should throw an error', function(done) {
 			$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
 			$selector1.click();
 
@@ -182,10 +196,14 @@ describe('jquery.contactsMenu tests', function() {
 			expect(fakeServer.requests[0].method).toEqual('POST');
 			expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
 
-			expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>Error fetching contact actions</span></a></li></ul></div>');
+			$selector1.on('loaderror', function() {
+				expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>Error fetching contact actions</span></a></li></ul></div>');
+
+				done();
+			});
 		});
 
-		it('should handle 404', function() {
+		it('should handle 404', function(done) {
 			$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
 			$selector1.click();
 
@@ -197,17 +215,21 @@ describe('jquery.contactsMenu tests', function() {
 			expect(fakeServer.requests[0].method).toEqual('POST');
 			expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
 
-			expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>No action available</span></a></li></ul></div>');
+			$selector1.on('loaderror', function() {
+				expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>No action available</span></a></li></ul></div>');
+
+				done();
+			});
 		});
-	});
 
-	it('click anywhere else to close the menu', function() {
-		$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
+		it('click anywhere else to close the menu', function() {
+			$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
 
-		expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
-		$selector1.click();
-		expect($appendTo.find('div').hasClass('hidden')).toEqual(false);
-		$(document).click();
-		expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
+			expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
+			$selector1.click();
+			expect($appendTo.find('div').hasClass('hidden')).toEqual(false);
+			$(document).click();
+			expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
+		});
 	});
 });
diff --git a/core/js/tests/specs/l10nSpec.js b/core/js/tests/specs/l10nSpec.js
index 67a5ab710d50a1b376e5c66b432a19224ce20a4d..18c1adabea1d18bdb9b772cdfa1b92abd26385a0 100644
--- a/core/js/tests/specs/l10nSpec.js
+++ b/core/js/tests/specs/l10nSpec.js
@@ -110,18 +110,30 @@ describe('OC.L10N tests', function() {
 		});
 	});
 	describe('async loading of translations', function() {
-		it('loads bundle for given app and calls callback', function() {
+		it('loads bundle for given app and calls callback', function(done) {
 			var localeStub = sinon.stub(OC, 'getLocale').returns('zh_CN');
 			var callbackStub = sinon.stub();
 			var promiseStub = sinon.stub();
-			OC.L10N.load(TEST_APP, callbackStub).then(promiseStub);
+			var loading = OC.L10N.load(TEST_APP, callbackStub);
 			expect(callbackStub.notCalled).toEqual(true);
-			expect(promiseStub.notCalled).toEqual(true);
-			expect(fakeServer.requests.length).toEqual(1);
 			var req = fakeServer.requests[0];
-			expect(req.url).toEqual(
-				OC.getRootPath() + '/apps3/' + TEST_APP + '/l10n/zh_CN.json'
-			);
+
+			loading
+				.then(promiseStub)
+				.then(function() {
+					expect(fakeServer.requests.length).toEqual(1);
+					expect(req.url).toEqual(
+						OC.getRootPath() + '/apps3/' + TEST_APP + '/l10n/zh_CN.json'
+					);
+
+					expect(callbackStub.calledOnce).toEqual(true);
+					expect(promiseStub.calledOnce).toEqual(true);
+					expect(t(TEST_APP, 'Hello world!')).toEqual('你好世界!');
+					localeStub.restore();
+				})
+				.then(done);
+
+			expect(promiseStub.notCalled).toEqual(true);
 			req.respond(
 				200,
 				{ 'Content-Type': 'application/json' },
@@ -130,32 +142,30 @@ describe('OC.L10N tests', function() {
 					pluralForm: 'nplurals=2; plural=(n != 1);'
 				})
 			);
-
-			expect(callbackStub.calledOnce).toEqual(true);
-			expect(promiseStub.calledOnce).toEqual(true);
-			expect(t(TEST_APP, 'Hello world!')).toEqual('你好世界!');
-			localeStub.restore();
 		});
-		it('calls callback if translation already available', function() {
-			var promiseStub = sinon.stub();
+		it('calls callback if translation already available', function(done) {
 			var callbackStub = sinon.stub();
 			spyOn(console, 'warn');
 			OC.L10N.register(TEST_APP, {
 				'Hello world!': 'Hallo Welt!'
 			});
-			OC.L10N.load(TEST_APP, callbackStub).then(promiseStub);
-			expect(callbackStub.calledOnce).toEqual(true);
-			expect(promiseStub.calledOnce).toEqual(true);
-			expect(fakeServer.requests.length).toEqual(0);
+			OC.L10N.load(TEST_APP, callbackStub)
+				.then(function() {
+					expect(callbackStub.calledOnce).toEqual(true);
+					expect(fakeServer.requests.length).toEqual(0);
+				})
+				.then(done);
+
 		});
-		it('calls callback if locale is en', function() {
-			var localeStub = sinon.stub(OC, 'getLocale').returns('en');
-			var promiseStub = sinon.stub();
+		it('calls callback if locale is en', function(done) {
 			var callbackStub = sinon.stub();
-			OC.L10N.load(TEST_APP, callbackStub).then(promiseStub);
-			expect(callbackStub.calledOnce).toEqual(true);
-			expect(promiseStub.calledOnce).toEqual(true);
-			expect(fakeServer.requests.length).toEqual(0);
+			OC.L10N.load(TEST_APP, callbackStub)
+				.then(function() {
+					expect(callbackStub.calledOnce).toEqual(true);
+					expect(fakeServer.requests.length).toEqual(0);
+				})
+				.then(done)
+				.catch(done);
 		});
 	});
 });
diff --git a/core/js/tests/specs/oc-backbone-webdavSpec.js b/core/js/tests/specs/oc-backbone-webdavSpec.js
index 97281e982ce10d39baa8c12c3956cd78d99cffa2..6f48d0c92d2001bde07278a2934fb4ae790de60f 100644
--- a/core/js/tests/specs/oc-backbone-webdavSpec.js
+++ b/core/js/tests/specs/oc-backbone-webdavSpec.js
@@ -71,7 +71,7 @@ describe('Backbone Webdav extension', function() {
 			});
 		});
 
-		it('makes a POST request to create model into collection', function() {
+		it('makes a POST request to create model into collection', function(done) {
 			var collection = new TestCollection();
 			var model = collection.create({
 				firstName: 'Hello',
@@ -104,10 +104,14 @@ describe('Backbone Webdav extension', function() {
 				}
 			});
 
-			expect(model.id).toEqual('123');
+			setTimeout(function() {
+				expect(model.id).toEqual('123');
+
+				done();
+			}, 0)
 		});
 
-		it('uses PROPFIND to retrieve collection', function() {
+		it('uses PROPFIND to retrieve collection', function(done) {
 			var successStub = sinon.stub();
 			var errorStub = sinon.stub();
 			var collection = new TestCollection();
@@ -164,23 +168,27 @@ describe('Backbone Webdav extension', function() {
 				]
 			});
 
-			expect(collection.length).toEqual(2);
+			setTimeout(function() {
+				expect(collection.length).toEqual(2);
 
-			var model = collection.get('123');
-			expect(model.id).toEqual('123');
-			expect(model.get('firstName')).toEqual('Hello');
-			expect(model.get('lastName')).toEqual('World');
+				var model = collection.get('123');
+				expect(model.id).toEqual('123');
+				expect(model.get('firstName')).toEqual('Hello');
+				expect(model.get('lastName')).toEqual('World');
+
+				model = collection.get('456');
+				expect(model.id).toEqual('456');
+				expect(model.get('firstName')).toEqual('Test');
+				expect(model.get('lastName')).toEqual('Person');
 
-			model = collection.get('456');
-			expect(model.id).toEqual('456');
-			expect(model.get('firstName')).toEqual('Test');
-			expect(model.get('lastName')).toEqual('Person');
+				expect(successStub.calledOnce).toEqual(true);
+				expect(errorStub.notCalled).toEqual(true);
 
-			expect(successStub.calledOnce).toEqual(true);
-			expect(errorStub.notCalled).toEqual(true);
+				done();
+			}, 0)
 		});
 
-		function testMethodError(doCall) {
+		function testMethodError(doCall, done) {
 			var successStub = sinon.stub();
 			var errorStub = sinon.stub();
 
@@ -191,20 +199,24 @@ describe('Backbone Webdav extension', function() {
 				body: ''
 			});
 
-			expect(successStub.notCalled).toEqual(true);
-			expect(errorStub.calledOnce).toEqual(true);
+			setTimeout(function() {
+				expect(successStub.notCalled).toEqual(true);
+				expect(errorStub.calledOnce).toEqual(true);
+
+				done();
+			}, 0)
 		}
 
-		it('calls error handler if error status in PROPFIND response', function() {
+		it('calls error handler if error status in PROPFIND response', function(done) {
 			testMethodError(function(success, error) {
 				var collection = new TestCollection();
 				collection.fetch({
 					success: success,
 					error: error
 				});
-			});
+			}, done);
 		});
-		it('calls error handler if error status in POST response', function() {
+		it('calls error handler if error status in POST response', function(done) {
 			testMethodError(function(success, error) {
 				var collection = new TestCollection();
 				collection.create({
@@ -214,7 +226,7 @@ describe('Backbone Webdav extension', function() {
 					success: success,
 					error: error
 				});
-			});
+			}, done);
 		});
 	});
 	describe('models', function() {
@@ -281,7 +293,7 @@ describe('Backbone Webdav extension', function() {
 			expect(model.get('married')).toEqual(true);
 		});
 
-		it('uses PROPFIND to fetch single model', function() {
+		it('uses PROPFIND to fetch single model', function(done) {
 			var model = new TestModel({
 				id: '123'
 			});
@@ -319,11 +331,15 @@ describe('Backbone Webdav extension', function() {
 				}
 			});
 
-			expect(model.id).toEqual('123');
-			expect(model.get('firstName')).toEqual('Hello');
-			expect(model.get('lastName')).toEqual('World');
-			expect(model.get('age')).toEqual(35);
-			expect(model.get('married')).toEqual(true);
+			setTimeout(function() {
+				expect(model.id).toEqual('123');
+				expect(model.get('firstName')).toEqual('Hello');
+				expect(model.get('lastName')).toEqual('World');
+				expect(model.get('age')).toEqual(35);
+				expect(model.get('married')).toEqual(true);
+
+				done();
+			});
 		});
 		it('makes a DELETE request to destroy model', function() {
 			var model = new TestModel({
@@ -350,7 +366,7 @@ describe('Backbone Webdav extension', function() {
 			});
 		});
 
-		function testMethodError(doCall) {
+		function testMethodError(doCall, done) {
 			var successStub = sinon.stub();
 			var errorStub = sinon.stub();
 
@@ -361,20 +377,24 @@ describe('Backbone Webdav extension', function() {
 				body: ''
 			});
 
-			expect(successStub.notCalled).toEqual(true);
-			expect(errorStub.calledOnce).toEqual(true);
+			setTimeout(function() {
+				expect(successStub.notCalled).toEqual(true);
+				expect(errorStub.calledOnce).toEqual(true);
+
+				done();
+			});
 		}
 
-		it('calls error handler if error status in PROPFIND response', function() {
+		it('calls error handler if error status in PROPFIND response', function(done) {
 			testMethodError(function(success, error) {
 				var model = new TestModel();
 				model.fetch({
 					success: success,
 					error: error
 				});
-			});
+			}, done);
 		});
-		it('calls error handler if error status in PROPPATCH response', function() {
+		it('calls error handler if error status in PROPPATCH response', function(done) {
 			testMethodError(function(success, error) {
 				var model = new TestModel();
 				model.save({
@@ -383,7 +403,7 @@ describe('Backbone Webdav extension', function() {
 					success: success,
 					error: error
 				});
-			});
+			}, done);
 		});
 	});
 });
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index 3b54a3ff66a46f3f0d22933c32b2e9bbc8ee646f..a0a3c2a4ba9a1080efa24776921b358ee2ff7dd6 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -1114,7 +1114,8 @@ describe('OC.SetupChecks tests', function() {
 				{
 					'Content-Type': 'application/json',
 					'Strict-Transport-Security': 'max-age=15768000'
-				}
+				},
+				'{}'
 			);
 
 			async.done(function( data, s, x ){
diff --git a/core/src/OC/l10n.js b/core/src/OC/l10n.js
index 22bb94e4e1bf76034ab4a499fe1e38b25ee25ea1..2eeb342aa1325c238a8ab05247d9a1ed447f8e61 100644
--- a/core/src/OC/l10n.js
+++ b/core/src/OC/l10n.js
@@ -328,7 +328,7 @@ export default L10n
  *
  * @returns {String} locale string
  */
-export const getLocale = () => $('html').data('locale')
+export const getLocale = () => $('html').data('locale') ?? 'en'
 
 /**
  * Returns the user's language
diff --git a/core/src/jquery/contactsmenu.js b/core/src/jquery/contactsmenu.js
index 50d256ff3f84b6bad1272804b36a95a4404aaf20..447f5adb62a22a677fa9c2585362d38cd432941a 100644
--- a/core/src/jquery/contactsmenu.js
+++ b/core/src/jquery/contactsmenu.js
@@ -82,9 +82,10 @@ $.fn.contactsMenu = function(shareWith, shareType, appendTo) {
 			}
 
 			actions.forEach(function(action) {
-				const template = entryTemplate
-				$list.find('ul').append(template(action))
+				$list.find('ul').append(entryTemplate(action))
 			})
+
+			$div.trigger('load')
 		}, function(jqXHR) {
 			$list.find('ul').find('li').addClass('hidden')
 
@@ -95,11 +96,12 @@ $.fn.contactsMenu = function(shareWith, shareType, appendTo) {
 				title = t('core', 'Error fetching contact actions')
 			}
 
-			const template = entryTemplate
-			$list.find('ul').append(template({
+			$list.find('ul').append(entryTemplate({
 				hyperlink: '#',
 				title,
 			}))
+
+			$div.trigger('loaderror', jqXHR)
 		})
 	})
 
diff --git a/package-lock.json b/package-lock.json
index 318118394b78730d8db48fc0e9207bb1648ff4d9..2a14ac4541d6cc6bd841f34dff757342babfa03f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6733,14 +6733,14 @@
       "integrity": "sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ="
     },
     "jquery": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
-      "integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI="
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.1.1.tgz",
+      "integrity": "sha1-NHwcIcfgBBFeCk2jLOzgQfrTyKM="
     },
     "jquery-migrate": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jquery-migrate/-/jquery-migrate-1.4.1.tgz",
-      "integrity": "sha1-hRUvPsmalWJfT30Lz2LpuGOPWnY="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jquery-migrate/-/jquery-migrate-3.1.0.tgz",
+      "integrity": "sha512-u/MtE1ST2pCr3rCyouJG2xMiw/k3OzLNeRKprjKTeHUezCGr0DyEgeXFdqFLmQfxfR5EsVu+mGo/sCcYdiYcIQ=="
     },
     "jquery-ui": {
       "version": "1.12.1",
diff --git a/package.json b/package.json
index d2f204555e782ad17abcbdd68726ba1e467cd239..dedfcc02d47dd1c5425dd804e40d2bc9d2936d34 100644
--- a/package.json
+++ b/package.json
@@ -55,8 +55,8 @@
     "escape-html": "^1.0.3",
     "handlebars": "^4.7.6",
     "jcrop": "git+https://github.com/ChristophWurst/Jcrop.git#v0.9.12-npm3",
-    "jquery": "2.2.4",
-    "jquery-migrate": "^1.4.1",
+    "jquery": "~3.1",
+    "jquery-migrate": "~3.1",
     "jquery-ui": "^1.12.1",
     "jquery-ui-dist": "^1.12.1",
     "jstimezonedetect": "^1.0.7",