sharedialogviewSpec.js 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  1. /**
  2. * ownCloud
  3. *
  4. * @author Vincent Petry
  5. * @copyright 2015 Vincent Petry <pvince81@owncloud.com>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. /* global oc_appconfig */
  22. describe('OC.Share.ShareDialogView', function() {
  23. var $container;
  24. var oldAppConfig;
  25. var autocompleteStub;
  26. var oldEnableAvatars;
  27. var avatarStub;
  28. var placeholderStub;
  29. var oldCurrentUser;
  30. var saveLinkShareStub;
  31. var fetchStub;
  32. var notificationStub;
  33. var configModel;
  34. var shareModel;
  35. var fileInfoModel;
  36. var dialog;
  37. beforeEach(function() {
  38. // horrible parameters
  39. $('#testArea').append('<input id="allowShareWithLink" type="hidden" value="yes">');
  40. $('#testArea').append('<input id="mailPublicNotificationEnabled" name="mailPublicNotificationEnabled" type="hidden" value="yes">');
  41. $container = $('#shareContainer');
  42. /* jshint camelcase:false */
  43. oldAppConfig = _.extend({}, oc_appconfig.core);
  44. oc_appconfig.core.enforcePasswordForPublicLink = false;
  45. fetchStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'fetch');
  46. saveLinkShareStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'saveLinkShare');
  47. fileInfoModel = new OCA.Files.FileInfoModel({
  48. id: 123,
  49. name: 'shared_file_name.txt',
  50. path: '/subdir',
  51. size: 100,
  52. mimetype: 'text/plain',
  53. permissions: 31,
  54. sharePermissions: 31
  55. });
  56. var attributes = {
  57. itemType: fileInfoModel.isDirectory() ? 'folder' : 'file',
  58. itemSource: fileInfoModel.get('id'),
  59. possiblePermissions: 31,
  60. permissions: 31
  61. };
  62. configModel = new OC.Share.ShareConfigModel({
  63. enforcePasswordForPublicLink: false,
  64. isResharingAllowed: true,
  65. enforcePasswordForPublicLink: false,
  66. isDefaultExpireDateEnabled: false,
  67. isDefaultExpireDateEnforced: false,
  68. defaultExpireDate: 7
  69. });
  70. shareModel = new OC.Share.ShareItemModel(attributes, {
  71. configModel: configModel,
  72. fileInfoModel: fileInfoModel
  73. });
  74. dialog = new OC.Share.ShareDialogView({
  75. configModel: configModel,
  76. model: shareModel
  77. });
  78. // required for proper event propagation when simulating clicks in some cases (jquery bugs)
  79. $('#testArea').append(dialog.$el);
  80. // triggers rendering
  81. shareModel.set({
  82. shares: [],
  83. linkShare: {isLinkShare: false}
  84. });
  85. autocompleteStub = sinon.stub($.fn, 'autocomplete', function() {
  86. // dummy container with the expected attributes
  87. if (!$(this).length) {
  88. // simulate the real autocomplete that returns
  89. // nothing at all when no element is specified
  90. // (and potentially break stuff)
  91. return null;
  92. }
  93. var $el = $('<div></div>').data('ui-autocomplete', {});
  94. return $el;
  95. });
  96. oldEnableAvatars = oc_config.enable_avatars;
  97. oc_config.enable_avatars = false;
  98. avatarStub = sinon.stub($.fn, 'avatar');
  99. placeholderStub = sinon.stub($.fn, 'imageplaceholder');
  100. oldCurrentUser = OC.currentUser;
  101. OC.currentUser = 'user0';
  102. });
  103. afterEach(function() {
  104. OC.currentUser = oldCurrentUser;
  105. /* jshint camelcase:false */
  106. oc_appconfig.core = oldAppConfig;
  107. dialog.remove();
  108. fetchStub.restore();
  109. saveLinkShareStub.restore();
  110. autocompleteStub.restore();
  111. avatarStub.restore();
  112. placeholderStub.restore();
  113. oc_config.enable_avatars = oldEnableAvatars;
  114. });
  115. describe('Share with link', function() {
  116. // TODO: test ajax calls
  117. // TODO: test password field visibility (whenever enforced or not)
  118. it('update password on focus out', function() {
  119. $('#allowShareWithLink').val('yes');
  120. dialog.model.set('linkShare', {
  121. isLinkShare: true
  122. });
  123. dialog.render();
  124. // Enable password, enter password and focusout
  125. dialog.$el.find('[name=showPassword]').click();
  126. dialog.$el.find('.linkPassText').focus();
  127. dialog.$el.find('.linkPassText').val('foo');
  128. dialog.$el.find('.linkPassText').focusout();
  129. expect(saveLinkShareStub.calledOnce).toEqual(true);
  130. expect(saveLinkShareStub.firstCall.args[0]).toEqual({
  131. password: 'foo'
  132. });
  133. });
  134. it('update password on enter', function() {
  135. $('#allowShareWithLink').val('yes');
  136. dialog.model.set('linkShare', {
  137. isLinkShare: true
  138. });
  139. dialog.render();
  140. // Toggle linkshare
  141. dialog.$el.find('.linkCheckbox').click();
  142. // Enable password and enter password
  143. dialog.$el.find('[name=showPassword]').click();
  144. dialog.$el.find('.linkPassText').focus();
  145. dialog.$el.find('.linkPassText').val('foo');
  146. dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
  147. expect(saveLinkShareStub.calledOnce).toEqual(true);
  148. expect(saveLinkShareStub.firstCall.args[0]).toEqual({
  149. password: 'foo'
  150. });
  151. });
  152. it('shows share with link checkbox when allowed', function() {
  153. $('#allowShareWithLink').val('yes');
  154. dialog.render();
  155. expect(dialog.$el.find('.linkCheckbox').length).toEqual(1);
  156. });
  157. it('does not show share with link checkbox when not allowed', function() {
  158. $('#allowShareWithLink').val('no');
  159. dialog.render();
  160. expect(dialog.$el.find('.linkCheckbox').length).toEqual(0);
  161. expect(dialog.$el.find('.shareWithField').length).toEqual(1);
  162. });
  163. it('shows populated link share when a link share exists', function() {
  164. // this is how the OC.Share class does it...
  165. var link = parent.location.protocol + '//' + location.host +
  166. OC.generateUrl('/s/') + 'tehtoken';
  167. shareModel.set('linkShare', {
  168. isLinkShare: true,
  169. token: 'tehtoken',
  170. link: link,
  171. expiration: '',
  172. permissions: OC.PERMISSION_READ,
  173. stime: 1403884258,
  174. });
  175. dialog.render();
  176. expect(dialog.$el.find('.linkCheckbox').prop('checked')).toEqual(true);
  177. expect(dialog.$el.find('.linkText').val()).toEqual(link);
  178. });
  179. it('autofocus link text when clicked', function() {
  180. $('#allowShareWithLink').val('yes');
  181. dialog.model.set('linkShare', {
  182. isLinkShare: true
  183. });
  184. dialog.render();
  185. var focusStub = sinon.stub($.fn, 'focus');
  186. var selectStub = sinon.stub($.fn, 'select');
  187. dialog.$el.find('.linkText').click();
  188. expect(focusStub.calledOnce).toEqual(true);
  189. expect(selectStub.calledOnce).toEqual(true);
  190. focusStub.restore();
  191. selectStub.restore();
  192. });
  193. describe('password', function() {
  194. var slideToggleStub;
  195. beforeEach(function() {
  196. $('#allowShareWithLink').val('yes');
  197. configModel.set({
  198. enforcePasswordForPublicLink: false
  199. });
  200. slideToggleStub = sinon.stub($.fn, 'slideToggle');
  201. });
  202. afterEach(function() {
  203. slideToggleStub.restore();
  204. });
  205. it('enforced but toggled does not fire request', function() {
  206. configModel.set('enforcePasswordForPublicLink', true);
  207. dialog.render();
  208. dialog.$el.find('.linkCheckbox').click();
  209. // The password linkPass field is shown (slideToggle is called).
  210. // No request is made yet
  211. expect(slideToggleStub.callCount).toEqual(1);
  212. expect(slideToggleStub.getCall(0).thisValue.eq(0).attr('id')).toEqual('linkPass');
  213. expect(fakeServer.requests.length).toEqual(0);
  214. // Now untoggle share by link
  215. dialog.$el.find('.linkCheckbox').click();
  216. dialog.render();
  217. // Password field disappears and no ajax requests have been made
  218. expect(fakeServer.requests.length).toEqual(0);
  219. expect(slideToggleStub.callCount).toEqual(2);
  220. expect(slideToggleStub.getCall(1).thisValue.eq(0).attr('id')).toEqual('linkPass');
  221. });
  222. });
  223. describe('expiration date', function() {
  224. var shareData;
  225. var shareItem;
  226. var clock;
  227. var expectedMinDate;
  228. beforeEach(function() {
  229. // pick a fake date
  230. clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime());
  231. expectedMinDate = new Date(2014, 0, 21, 14, 0, 0);
  232. configModel.set({
  233. enforcePasswordForPublicLink: false,
  234. isDefaultExpireDateEnabled: false,
  235. isDefaultExpireDateEnforced: false,
  236. defaultExpireDate: 7
  237. });
  238. shareModel.set('linkShare', {
  239. isLinkShare: true,
  240. token: 'tehtoken',
  241. permissions: OC.PERMISSION_READ,
  242. expiration: null
  243. });
  244. });
  245. afterEach(function() {
  246. clock.restore();
  247. });
  248. it('does not check expiration date checkbox when no date was set', function() {
  249. shareModel.get('linkShare').expiration = null;
  250. dialog.render();
  251. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
  252. expect(dialog.$el.find('.datepicker').val()).toEqual('');
  253. });
  254. it('does not check expiration date checkbox for new share', function() {
  255. dialog.render();
  256. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
  257. expect(dialog.$el.find('.datepicker').val()).toEqual('');
  258. });
  259. it('checks expiration date checkbox and populates field when expiration date was set', function() {
  260. shareModel.get('linkShare').expiration = '2014-02-01 00:00:00';
  261. dialog.render();
  262. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
  263. expect(dialog.$el.find('.datepicker').val()).toEqual('01-02-2014');
  264. });
  265. it('sets default date when default date setting is enabled', function() {
  266. configModel.set('isDefaultExpireDateEnabled', true);
  267. dialog.render();
  268. dialog.$el.find('.linkCheckbox').click();
  269. // here fetch would be called and the server returns the expiration date
  270. shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
  271. dialog.render();
  272. // enabled by default
  273. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
  274. expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
  275. // disabling is allowed
  276. dialog.$el.find('[name=expirationCheckbox]').click();
  277. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
  278. });
  279. it('enforces default date when enforced date setting is enabled', function() {
  280. configModel.set({
  281. isDefaultExpireDateEnabled: true,
  282. isDefaultExpireDateEnforced: true
  283. });
  284. dialog.render();
  285. dialog.$el.find('.linkCheckbox').click();
  286. // here fetch would be called and the server returns the expiration date
  287. shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
  288. dialog.render();
  289. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
  290. expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
  291. // disabling is not allowed
  292. expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
  293. dialog.$el.find('[name=expirationCheckbox]').click();
  294. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
  295. });
  296. it('enforces default date when enforced date setting is enabled and password is enforced', function() {
  297. configModel.set({
  298. enforcePasswordForPublicLink: true,
  299. isDefaultExpireDateEnabled: true,
  300. isDefaultExpireDateEnforced: true
  301. });
  302. dialog.render();
  303. dialog.$el.find('.linkCheckbox').click();
  304. // here fetch would be called and the server returns the expiration date
  305. shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
  306. dialog.render();
  307. //Enter password
  308. dialog.$el.find('.linkPassText').val('foo');
  309. dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
  310. fakeServer.requests[0].respond(
  311. 200,
  312. { 'Content-Type': 'application/json' },
  313. JSON.stringify({data: {token: 'xyz'}, status: 'success'})
  314. );
  315. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
  316. expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
  317. // disabling is not allowed
  318. expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
  319. dialog.$el.find('[name=expirationCheckbox]').click();
  320. expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
  321. });
  322. it('sets picker minDate to today and no maxDate by default', function() {
  323. dialog.render();
  324. dialog.$el.find('.linkCheckbox').click();
  325. dialog.$el.find('[name=expirationCheckbox]').click();
  326. expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
  327. expect($.datepicker._defaults.maxDate).toEqual(null);
  328. });
  329. it('limits the date range to X days after share time when enforced', function() {
  330. configModel.set({
  331. isDefaultExpireDateEnabled: true,
  332. isDefaultExpireDateEnforced: true
  333. });
  334. dialog.render();
  335. dialog.$el.find('.linkCheckbox').click();
  336. expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
  337. expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
  338. });
  339. it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() {
  340. // item exists, was created two days ago
  341. var shareItem = shareModel.get('linkShare');
  342. shareItem.expiration = '2014-1-27';
  343. // share time has time component but must be stripped later
  344. shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000;
  345. configModel.set({
  346. isDefaultExpireDateEnabled: true,
  347. isDefaultExpireDateEnforced: true
  348. });
  349. dialog.render();
  350. expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
  351. expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
  352. });
  353. });
  354. describe('send link by email', function() {
  355. var sendEmailPrivateLinkStub;
  356. var clock;
  357. beforeEach(function() {
  358. configModel.set({
  359. isMailPublicNotificationEnabled: true
  360. });
  361. shareModel.set('linkShare', {
  362. isLinkShare: true,
  363. token: 'tehtoken',
  364. permissions: OC.PERMISSION_READ,
  365. expiration: null
  366. });
  367. sendEmailPrivateLinkStub = sinon.stub(dialog.model, "sendEmailPrivateLink");
  368. clock = sinon.useFakeTimers();
  369. });
  370. afterEach(function() {
  371. sendEmailPrivateLinkStub.restore();
  372. clock.restore();
  373. });
  374. it('displays form when sending emails is enabled', function() {
  375. $('input[name=mailPublicNotificationEnabled]').val('yes');
  376. dialog.render();
  377. expect(dialog.$('.emailPrivateLinkForm').length).toEqual(1);
  378. });
  379. it('form not rendered when sending emails is disabled', function() {
  380. $('input[name=mailPublicNotificationEnabled]').val('no');
  381. dialog.render();
  382. expect(dialog.$('.emailPrivateLinkForm').length).toEqual(0);
  383. });
  384. it('input cleared on success', function() {
  385. var defer = $.Deferred();
  386. sendEmailPrivateLinkStub.returns(defer.promise());
  387. $('input[name=mailPublicNotificationEnabled]').val('yes');
  388. dialog.render();
  389. dialog.$el.find('.emailPrivateLinkForm .emailField').val('a@b.c');
  390. dialog.$el.find('.emailPrivateLinkForm').trigger('submit');
  391. expect(sendEmailPrivateLinkStub.callCount).toEqual(1);
  392. expect(dialog.$el.find('.emailPrivateLinkForm .emailField').val()).toEqual('Sending ...');
  393. defer.resolve();
  394. expect(dialog.$el.find('.emailPrivateLinkForm .emailField').val()).toEqual('Email sent');
  395. clock.tick(2000);
  396. expect(dialog.$el.find('.emailPrivateLinkForm .emailField').val()).toEqual('');
  397. });
  398. it('input not cleared on failure', function() {
  399. var defer = $.Deferred();
  400. sendEmailPrivateLinkStub.returns(defer.promise());
  401. $('input[name=mailPublicNotificationEnabled]').val('yes');
  402. dialog.render();
  403. dialog.$el.find('.emailPrivateLinkForm .emailField').val('a@b.c');
  404. dialog.$el.find('.emailPrivateLinkForm').trigger('submit');
  405. expect(sendEmailPrivateLinkStub.callCount).toEqual(1);
  406. expect(dialog.$el.find('.emailPrivateLinkForm .emailField').val()).toEqual('Sending ...');
  407. defer.reject();
  408. expect(dialog.$el.find('.emailPrivateLinkForm .emailField').val()).toEqual('a@b.c');
  409. });
  410. });
  411. });
  412. describe('check for avatar', function() {
  413. beforeEach(function() {
  414. shareModel.set({
  415. reshare: {
  416. share_type: OC.Share.SHARE_TYPE_USER,
  417. uid_owner: 'owner',
  418. displayname_owner: 'Owner',
  419. permissions: 31
  420. },
  421. shares: [{
  422. id: 100,
  423. item_source: 123,
  424. permissions: 31,
  425. share_type: OC.Share.SHARE_TYPE_USER,
  426. share_with: 'user1',
  427. share_with_displayname: 'User One'
  428. },{
  429. id: 101,
  430. item_source: 123,
  431. permissions: 31,
  432. share_type: OC.Share.SHARE_TYPE_GROUP,
  433. share_with: 'group',
  434. share_with_displayname: 'group'
  435. },{
  436. id: 102,
  437. item_source: 123,
  438. permissions: 31,
  439. share_type: OC.Share.SHARE_TYPE_REMOTE,
  440. share_with: 'foo@bar.com/baz',
  441. share_with_displayname: 'foo@bar.com/baz'
  442. }]
  443. });
  444. });
  445. describe('avatars enabled', function() {
  446. beforeEach(function() {
  447. oc_config.enable_avatars = true;
  448. avatarStub.reset();
  449. dialog.render();
  450. });
  451. afterEach(function() {
  452. oc_config.enable_avatars = false;
  453. });
  454. it('test correct function calls', function() {
  455. expect(avatarStub.calledTwice).toEqual(true);
  456. expect(placeholderStub.calledTwice).toEqual(true);
  457. expect(dialog.$('.shareWithList').children().length).toEqual(3);
  458. expect(dialog.$('.avatar').length).toEqual(4);
  459. });
  460. it('test avatar owner', function() {
  461. var args = avatarStub.getCall(0).args;
  462. expect(args.length).toEqual(2);
  463. expect(args[0]).toEqual('owner');
  464. });
  465. it('test avatar user', function() {
  466. var args = avatarStub.getCall(1).args;
  467. expect(args.length).toEqual(2);
  468. expect(args[0]).toEqual('user1');
  469. });
  470. it('test avatar for groups', function() {
  471. var args = placeholderStub.getCall(0).args;
  472. expect(args.length).toEqual(1);
  473. expect(args[0]).toEqual('group ' + OC.Share.SHARE_TYPE_GROUP);
  474. });
  475. it('test avatar for remotes', function() {
  476. var args = placeholderStub.getCall(1).args;
  477. expect(args.length).toEqual(1);
  478. expect(args[0]).toEqual('foo@bar.com/baz ' + OC.Share.SHARE_TYPE_REMOTE);
  479. });
  480. });
  481. describe('avatars disabled', function() {
  482. beforeEach(function() {
  483. dialog.render();
  484. });
  485. it('no avatar classes', function() {
  486. expect($('.avatar').length).toEqual(0);
  487. expect(avatarStub.callCount).toEqual(0);
  488. expect(placeholderStub.callCount).toEqual(0);
  489. });
  490. });
  491. });
  492. describe('remote sharing', function() {
  493. it('shows remote share info when allowed', function() {
  494. configModel.set({
  495. isRemoteShareAllowed: true
  496. });
  497. dialog.render();
  498. expect(dialog.$el.find('.shareWithRemoteInfo').length).toEqual(1);
  499. });
  500. it('does not show remote share info when not allowed', function() {
  501. configModel.set({
  502. isRemoteShareAllowed: false
  503. });
  504. dialog.render();
  505. expect(dialog.$el.find('.shareWithRemoteInfo').length).toEqual(0);
  506. });
  507. });
  508. describe('autocompletion of users', function() {
  509. it('triggers autocomplete display and focus with data when ajax search succeeds', function () {
  510. dialog.render();
  511. var response = sinon.stub();
  512. dialog.autocompleteHandler({term: 'bob'}, response);
  513. var jsonData = JSON.stringify({
  514. 'ocs' : {
  515. 'meta' : {
  516. 'status' : 'success',
  517. 'statuscode' : 100,
  518. 'message' : null
  519. },
  520. 'data' : {
  521. 'exact' : {
  522. 'users' : [],
  523. 'groups' : [],
  524. 'remotes': []
  525. },
  526. 'users' : [{'label': 'bob', 'value': {'shareType': 0, 'shareWith': 'test'}}],
  527. 'groups' : [],
  528. 'remotes': []
  529. }
  530. }
  531. });
  532. fakeServer.requests[0].respond(
  533. 200,
  534. {'Content-Type': 'application/json'},
  535. jsonData
  536. );
  537. expect(response.calledWithExactly(JSON.parse(jsonData).ocs.data.users)).toEqual(true);
  538. expect(autocompleteStub.calledWith("option", "autoFocus", true)).toEqual(true);
  539. });
  540. describe('filter out', function() {
  541. it('the current user', function () {
  542. dialog.render();
  543. var response = sinon.stub();
  544. dialog.autocompleteHandler({term: 'bob'}, response);
  545. var jsonData = JSON.stringify({
  546. 'ocs': {
  547. 'meta': {
  548. 'status': 'success',
  549. 'statuscode': 100,
  550. 'message': null
  551. },
  552. 'data': {
  553. 'exact': {
  554. 'users': [],
  555. 'groups': [],
  556. 'remotes': []
  557. },
  558. 'users': [
  559. {
  560. 'label': 'bob',
  561. 'value': {
  562. 'shareType': 0,
  563. 'shareWith': OC.currentUser
  564. }
  565. },
  566. {
  567. 'label': 'bobby',
  568. 'value': {
  569. 'shareType': 0,
  570. 'shareWith': 'imbob'
  571. }
  572. }
  573. ],
  574. 'groups': [],
  575. 'remotes': []
  576. }
  577. }
  578. });
  579. fakeServer.requests[0].respond(
  580. 200,
  581. {'Content-Type': 'application/json'},
  582. jsonData
  583. );
  584. expect(response.calledWithExactly([{
  585. 'label': 'bobby',
  586. 'value': {'shareType': 0, 'shareWith': 'imbob'}
  587. }])).toEqual(true);
  588. expect(autocompleteStub.calledWith("option", "autoFocus", true)).toEqual(true);
  589. });
  590. it('the share owner', function () {
  591. shareModel.set({
  592. reshare: {
  593. uid_owner: 'user1'
  594. },
  595. shares: [],
  596. permissions: OC.PERMISSION_READ
  597. });
  598. dialog.render();
  599. var response = sinon.stub();
  600. dialog.autocompleteHandler({term: 'bob'}, response);
  601. var jsonData = JSON.stringify({
  602. 'ocs': {
  603. 'meta': {
  604. 'status': 'success',
  605. 'statuscode': 100,
  606. 'message': null
  607. },
  608. 'data': {
  609. 'exact': {
  610. 'users': [],
  611. 'groups': [],
  612. 'remotes': []
  613. },
  614. 'users': [
  615. {
  616. 'label': 'bob',
  617. 'value': {
  618. 'shareType': 0,
  619. 'shareWith': 'user1'
  620. }
  621. },
  622. {
  623. 'label': 'bobby',
  624. 'value': {
  625. 'shareType': 0,
  626. 'shareWith': 'imbob'
  627. }
  628. }
  629. ],
  630. 'groups': [],
  631. 'remotes': []
  632. }
  633. }
  634. });
  635. fakeServer.requests[0].respond(
  636. 200,
  637. {'Content-Type': 'application/json'},
  638. jsonData
  639. );
  640. expect(response.calledWithExactly([{
  641. 'label': 'bobby',
  642. 'value': {'shareType': 0, 'shareWith': 'imbob'}
  643. }])).toEqual(true);
  644. expect(autocompleteStub.calledWith("option", "autoFocus", true)).toEqual(true);
  645. });
  646. describe('already shared with', function () {
  647. beforeEach(function() {
  648. shareModel.set({
  649. reshare: {},
  650. shares: [{
  651. id: 100,
  652. item_source: 123,
  653. permissions: 31,
  654. share_type: OC.Share.SHARE_TYPE_USER,
  655. share_with: 'user1',
  656. share_with_displayname: 'User One'
  657. },{
  658. id: 101,
  659. item_source: 123,
  660. permissions: 31,
  661. share_type: OC.Share.SHARE_TYPE_GROUP,
  662. share_with: 'group',
  663. share_with_displayname: 'group'
  664. },{
  665. id: 102,
  666. item_source: 123,
  667. permissions: 31,
  668. share_type: OC.Share.SHARE_TYPE_REMOTE,
  669. share_with: 'foo@bar.com/baz',
  670. share_with_displayname: 'foo@bar.com/baz'
  671. }]
  672. });
  673. });
  674. it('users', function () {
  675. dialog.render();
  676. var response = sinon.stub();
  677. dialog.autocompleteHandler({term: 'bob'}, response);
  678. var jsonData = JSON.stringify({
  679. 'ocs': {
  680. 'meta': {
  681. 'status': 'success',
  682. 'statuscode': 100,
  683. 'message': null
  684. },
  685. 'data': {
  686. 'exact': {
  687. 'users': [],
  688. 'groups': [],
  689. 'remotes': []
  690. },
  691. 'users': [
  692. {
  693. 'label': 'bob',
  694. 'value': {
  695. 'shareType': OC.Share.SHARE_TYPE_USER,
  696. 'shareWith': 'user1'
  697. }
  698. },
  699. {
  700. 'label': 'bobby',
  701. 'value': {
  702. 'shareType': OC.Share.SHARE_TYPE_USER,
  703. 'shareWith': 'imbob'
  704. }
  705. }
  706. ],
  707. 'groups': [],
  708. 'remotes': []
  709. }
  710. }
  711. });
  712. fakeServer.requests[0].respond(
  713. 200,
  714. {'Content-Type': 'application/json'},
  715. jsonData
  716. );
  717. expect(response.calledWithExactly([{
  718. 'label': 'bobby',
  719. 'value': {'shareType': OC.Share.SHARE_TYPE_USER, 'shareWith': 'imbob'}
  720. }])).toEqual(true);
  721. expect(autocompleteStub.calledWith("option", "autoFocus", true)).toEqual(true);
  722. });
  723. it('groups', function () {
  724. dialog.render();
  725. var response = sinon.stub();
  726. dialog.autocompleteHandler({term: 'group'}, response);
  727. var jsonData = JSON.stringify({
  728. 'ocs': {
  729. 'meta': {
  730. 'status': 'success',
  731. 'statuscode': 100,
  732. 'message': null
  733. },
  734. 'data': {
  735. 'exact': {
  736. 'users': [],
  737. 'groups': [],
  738. 'remotes': []
  739. },
  740. 'users': [],
  741. 'groups': [
  742. {
  743. 'label': 'group',
  744. 'value': {
  745. 'shareType': OC.Share.SHARE_TYPE_GROUP,
  746. 'shareWith': 'group'
  747. }
  748. },
  749. {
  750. 'label': 'group2',
  751. 'value': {
  752. 'shareType': OC.Share.SHARE_TYPE_GROUP,
  753. 'shareWith': 'group2'
  754. }
  755. }
  756. ],
  757. 'remotes': []
  758. }
  759. }
  760. });
  761. fakeServer.requests[0].respond(
  762. 200,
  763. {'Content-Type': 'application/json'},
  764. jsonData
  765. );
  766. expect(response.calledWithExactly([{
  767. 'label': 'group2',
  768. 'value': {'shareType': OC.Share.SHARE_TYPE_GROUP, 'shareWith': 'group2'}
  769. }])).toEqual(true);
  770. expect(autocompleteStub.calledWith("option", "autoFocus", true)).toEqual(true);
  771. });
  772. it('remotes', function () {
  773. dialog.render();
  774. var response = sinon.stub();
  775. dialog.autocompleteHandler({term: 'bob'}, response);
  776. var jsonData = JSON.stringify({
  777. 'ocs': {
  778. 'meta': {
  779. 'status': 'success',
  780. 'statuscode': 100,
  781. 'message': null
  782. },
  783. 'data': {
  784. 'exact': {
  785. 'users': [],
  786. 'groups': [],
  787. 'remotes': []
  788. },
  789. 'users': [],
  790. 'groups': [],
  791. 'remotes': [
  792. {
  793. 'label': 'foo@bar.com/baz',
  794. 'value': {
  795. 'shareType': OC.Share.SHARE_TYPE_REMOTE,
  796. 'shareWith': 'foo@bar.com/baz'
  797. }
  798. },
  799. {
  800. 'label': 'foo2@bar.com/baz',
  801. 'value': {
  802. 'shareType': OC.Share.SHARE_TYPE_REMOTE,
  803. 'shareWith': 'foo2@bar.com/baz'
  804. }
  805. }
  806. ]
  807. }
  808. }
  809. });
  810. fakeServer.requests[0].respond(
  811. 200,
  812. {'Content-Type': 'application/json'},
  813. jsonData
  814. );
  815. expect(response.calledWithExactly([{
  816. 'label': 'foo2@bar.com/baz',
  817. 'value': {'shareType': OC.Share.SHARE_TYPE_REMOTE, 'shareWith': 'foo2@bar.com/baz'}
  818. }])).toEqual(true);
  819. expect(autocompleteStub.calledWith("option", "autoFocus", true)).toEqual(true);
  820. });
  821. });
  822. });
  823. it('gracefully handles successful ajax call with failure content', function () {
  824. dialog.render();
  825. var response = sinon.stub();
  826. dialog.autocompleteHandler({term: 'bob'}, response);
  827. var jsonData = JSON.stringify({
  828. 'ocs' : {
  829. 'meta' : {
  830. 'status': 'failure',
  831. 'statuscode': 400
  832. }
  833. }
  834. });
  835. fakeServer.requests[0].respond(
  836. 200,
  837. {'Content-Type': 'application/json'},
  838. jsonData
  839. );
  840. expect(response.calledWithExactly()).toEqual(true);
  841. });
  842. it('throws a notification when the ajax search lookup fails', function () {
  843. notificationStub = sinon.stub(OC.Notification, 'show');
  844. dialog.render();
  845. dialog.autocompleteHandler({term: 'bob'}, sinon.stub());
  846. fakeServer.requests[0].respond(500);
  847. expect(notificationStub.calledOnce).toEqual(true);
  848. notificationStub.restore();
  849. });
  850. describe('renders the autocomplete elements', function() {
  851. it('renders a group element', function() {
  852. dialog.render();
  853. var el = dialog.autocompleteRenderItem(
  854. $("<ul></ul>"),
  855. {label: "1", value: { shareType: OC.Share.SHARE_TYPE_GROUP }}
  856. );
  857. expect(el.is('li')).toEqual(true);
  858. expect(el.hasClass('group')).toEqual(true);
  859. });
  860. it('renders a remote element', function() {
  861. dialog.render();
  862. var el = dialog.autocompleteRenderItem(
  863. $("<ul></ul>"),
  864. {label: "1", value: { shareType: OC.Share.SHARE_TYPE_REMOTE }}
  865. );
  866. expect(el.is('li')).toEqual(true);
  867. expect(el.hasClass('user')).toEqual(true);
  868. });
  869. });
  870. it('calls addShare after selection', function() {
  871. dialog.render();
  872. var shareWith = $('.shareWithField')[0];
  873. var $shareWith = $(shareWith);
  874. var addShareStub = sinon.stub(shareModel, 'addShare');
  875. var autocompleteOptions = autocompleteStub.getCall(0).args[0];
  876. autocompleteOptions.select(new $.Event('select', {target: shareWith}), {
  877. item: {
  878. label: 'User Two',
  879. value: {
  880. shareType: OC.Share.SHARE_TYPE_USER,
  881. shareWith: 'user2'
  882. }
  883. }
  884. });
  885. expect(addShareStub.calledOnce).toEqual(true);
  886. expect(addShareStub.firstCall.args[0]).toEqual({
  887. shareType: OC.Share.SHARE_TYPE_USER,
  888. shareWith: 'user2'
  889. });
  890. //Input is locked
  891. expect($shareWith.val()).toEqual('User Two');
  892. expect($shareWith.attr('disabled')).toEqual('disabled');
  893. //Callback is called
  894. addShareStub.firstCall.args[1].success();
  895. //Input is unlocked
  896. expect($shareWith.val()).toEqual('');
  897. expect($shareWith.attr('disabled')).toEqual(undefined);
  898. addShareStub.restore();
  899. });
  900. it('calls addShare after selection and fail to share', function() {
  901. dialog.render();
  902. var shareWith = $('.shareWithField')[0];
  903. var $shareWith = $(shareWith);
  904. var addShareStub = sinon.stub(shareModel, 'addShare');
  905. var autocompleteOptions = autocompleteStub.getCall(0).args[0];
  906. autocompleteOptions.select(new $.Event('select', {target: shareWith}), {
  907. item: {
  908. label: 'User Two',
  909. value: {
  910. shareType: OC.Share.SHARE_TYPE_USER,
  911. shareWith: 'user2'
  912. }
  913. }
  914. });
  915. expect(addShareStub.calledOnce).toEqual(true);
  916. expect(addShareStub.firstCall.args[0]).toEqual({
  917. shareType: OC.Share.SHARE_TYPE_USER,
  918. shareWith: 'user2'
  919. });
  920. //Input is locked
  921. expect($shareWith.val()).toEqual('User Two');
  922. expect($shareWith.attr('disabled')).toEqual('disabled');
  923. //Callback is called
  924. addShareStub.firstCall.args[1].error();
  925. //Input is unlocked
  926. expect($shareWith.val()).toEqual('User Two');
  927. expect($shareWith.attr('disabled')).toEqual(undefined);
  928. addShareStub.restore();
  929. });
  930. });
  931. describe('reshare permissions', function() {
  932. it('does not show sharing options when sharing not allowed', function() {
  933. shareModel.set({
  934. reshare: {},
  935. shares: [],
  936. permissions: OC.PERMISSION_READ
  937. });
  938. dialog.render();
  939. expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true);
  940. });
  941. it('shows reshare owner', function() {
  942. shareModel.set({
  943. reshare: {
  944. uid_owner: 'user1'
  945. },
  946. shares: [],
  947. permissions: OC.PERMISSION_READ
  948. });
  949. dialog.render();
  950. expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(1);
  951. });
  952. it('does not show reshare owner if owner is current user', function() {
  953. shareModel.set({
  954. reshare: {
  955. uid_owner: OC.currentUser
  956. },
  957. shares: [],
  958. permissions: OC.PERMISSION_READ
  959. });
  960. dialog.render();
  961. expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(0);
  962. });
  963. });
  964. });