sharedialogviewSpec.js 29 KB

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