cozy-sharing 33.3.4 → 33.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/SharingProvider.js +176 -84
- package/dist/SharingProvider.spec.js +353 -54
- package/dist/components/Avatar/MemberAvatar.js +21 -5
- package/dist/components/Avatar/MemberAvatar.spec.js +77 -0
- package/dist/components/FederatedFolder/FederatedFolderModal.js +70 -7
- package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +234 -85
- package/dist/components/Recipient/OwnerRecipientDefault.js +8 -1
- package/dist/components/Recipient/OwnerRecipientDefaultLite.js +5 -1
- package/package.json +4 -4
- package/src/SharingProvider.jsx +49 -7
- package/src/SharingProvider.spec.jsx +204 -0
- package/src/components/Avatar/MemberAvatar.jsx +19 -4
- package/src/components/Avatar/MemberAvatar.spec.jsx +80 -0
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +44 -3
- package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +87 -2
- package/src/components/Recipient/OwnerRecipientDefault.jsx +8 -0
- package/src/components/Recipient/OwnerRecipientDefaultLite.jsx +5 -0
|
@@ -411,6 +411,305 @@ describe('updateDocumentPermissions', function () {
|
|
|
411
411
|
}, _callee7);
|
|
412
412
|
})));
|
|
413
413
|
});
|
|
414
|
+
describe('fetchSharedDriveSharingLinks', function () {
|
|
415
|
+
var PERM_DRIVE_FILE = {
|
|
416
|
+
type: 'io.cozy.permissions',
|
|
417
|
+
id: 'perm_drive_file',
|
|
418
|
+
attributes: {
|
|
419
|
+
type: 'share',
|
|
420
|
+
permissions: {
|
|
421
|
+
rule0: {
|
|
422
|
+
type: 'io.cozy.files',
|
|
423
|
+
verbs: ['GET'],
|
|
424
|
+
values: ['file_in_drive']
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
shortcodes: {
|
|
428
|
+
code: 'shortcode123'
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
var driveFile = {
|
|
433
|
+
_id: 'file_in_drive',
|
|
434
|
+
id: 'file_in_drive',
|
|
435
|
+
driveId: 'drive_123'
|
|
436
|
+
};
|
|
437
|
+
it('fetches shared drive sharing links by file id', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8() {
|
|
438
|
+
var mockFindLinksByIds, mockClient, provider, result, permissions;
|
|
439
|
+
return _regeneratorRuntime.wrap(function _callee8$(_context8) {
|
|
440
|
+
while (1) switch (_context8.prev = _context8.next) {
|
|
441
|
+
case 0:
|
|
442
|
+
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
443
|
+
data: [PERM_DRIVE_FILE]
|
|
444
|
+
});
|
|
445
|
+
mockClient = createMockClient({});
|
|
446
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
447
|
+
findLinksByIds: mockFindLinksByIds
|
|
448
|
+
});
|
|
449
|
+
provider = new SharingProvider({
|
|
450
|
+
client: mockClient
|
|
451
|
+
});
|
|
452
|
+
provider.state = reducer();
|
|
453
|
+
provider.dispatch = jest.fn(function (action) {
|
|
454
|
+
provider.state = reducer(provider.state, action);
|
|
455
|
+
});
|
|
456
|
+
_context8.next = 8;
|
|
457
|
+
return provider.fetchSharedDriveSharingLinks(driveFile);
|
|
458
|
+
|
|
459
|
+
case 8:
|
|
460
|
+
result = _context8.sent;
|
|
461
|
+
expect(mockClient.collection).toHaveBeenCalledWith('io.cozy.permissions', {
|
|
462
|
+
driveId: 'drive_123'
|
|
463
|
+
});
|
|
464
|
+
expect(mockFindLinksByIds).toHaveBeenCalledWith(['file_in_drive']);
|
|
465
|
+
expect(provider.dispatch).toHaveBeenCalled();
|
|
466
|
+
permissions = getDocumentPermissions(provider.state, 'file_in_drive');
|
|
467
|
+
expect(permissions).toHaveLength(1);
|
|
468
|
+
expect(permissions[0].id).toBe(PERM_DRIVE_FILE.id);
|
|
469
|
+
expect(result).toEqual([PERM_DRIVE_FILE]);
|
|
470
|
+
|
|
471
|
+
case 16:
|
|
472
|
+
case "end":
|
|
473
|
+
return _context8.stop();
|
|
474
|
+
}
|
|
475
|
+
}, _callee8);
|
|
476
|
+
})));
|
|
477
|
+
it('returns an empty array and skips the API call for non-shared-drive documents', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
|
|
478
|
+
var mockClient, findLinksByIds, provider, result;
|
|
479
|
+
return _regeneratorRuntime.wrap(function _callee9$(_context9) {
|
|
480
|
+
while (1) switch (_context9.prev = _context9.next) {
|
|
481
|
+
case 0:
|
|
482
|
+
mockClient = createMockClient({});
|
|
483
|
+
findLinksByIds = jest.fn();
|
|
484
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
485
|
+
findLinksByIds: findLinksByIds
|
|
486
|
+
});
|
|
487
|
+
provider = new SharingProvider({
|
|
488
|
+
client: mockClient
|
|
489
|
+
});
|
|
490
|
+
provider.dispatch = jest.fn();
|
|
491
|
+
_context9.next = 7;
|
|
492
|
+
return provider.fetchSharedDriveSharingLinks({
|
|
493
|
+
_id: 'regular_file'
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
case 7:
|
|
497
|
+
result = _context9.sent;
|
|
498
|
+
expect(result).toEqual([]);
|
|
499
|
+
expect(findLinksByIds).not.toHaveBeenCalled();
|
|
500
|
+
expect(provider.dispatch).not.toHaveBeenCalled();
|
|
501
|
+
|
|
502
|
+
case 11:
|
|
503
|
+
case "end":
|
|
504
|
+
return _context9.stop();
|
|
505
|
+
}
|
|
506
|
+
}, _callee9);
|
|
507
|
+
})));
|
|
508
|
+
it('uses document.id as a fallback when _id is missing', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
|
|
509
|
+
var mockFindLinksByIds, mockClient, provider;
|
|
510
|
+
return _regeneratorRuntime.wrap(function _callee10$(_context10) {
|
|
511
|
+
while (1) switch (_context10.prev = _context10.next) {
|
|
512
|
+
case 0:
|
|
513
|
+
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
514
|
+
data: [PERM_DRIVE_FILE]
|
|
515
|
+
});
|
|
516
|
+
mockClient = createMockClient({});
|
|
517
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
518
|
+
findLinksByIds: mockFindLinksByIds
|
|
519
|
+
});
|
|
520
|
+
provider = new SharingProvider({
|
|
521
|
+
client: mockClient
|
|
522
|
+
});
|
|
523
|
+
provider.state = reducer();
|
|
524
|
+
provider.dispatch = jest.fn(function (action) {
|
|
525
|
+
provider.state = reducer(provider.state, action);
|
|
526
|
+
});
|
|
527
|
+
_context10.next = 8;
|
|
528
|
+
return provider.fetchSharedDriveSharingLinks({
|
|
529
|
+
id: 'file_in_drive',
|
|
530
|
+
driveId: 'drive_123'
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
case 8:
|
|
534
|
+
expect(mockFindLinksByIds).toHaveBeenCalledWith(['file_in_drive']);
|
|
535
|
+
|
|
536
|
+
case 9:
|
|
537
|
+
case "end":
|
|
538
|
+
return _context10.stop();
|
|
539
|
+
}
|
|
540
|
+
}, _callee10);
|
|
541
|
+
})));
|
|
542
|
+
it('does not dispatch when the response has no data', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
|
|
543
|
+
var mockFindLinksByIds, mockClient, provider, result;
|
|
544
|
+
return _regeneratorRuntime.wrap(function _callee11$(_context11) {
|
|
545
|
+
while (1) switch (_context11.prev = _context11.next) {
|
|
546
|
+
case 0:
|
|
547
|
+
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
548
|
+
data: []
|
|
549
|
+
});
|
|
550
|
+
mockClient = createMockClient({});
|
|
551
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
552
|
+
findLinksByIds: mockFindLinksByIds
|
|
553
|
+
});
|
|
554
|
+
provider = new SharingProvider({
|
|
555
|
+
client: mockClient
|
|
556
|
+
});
|
|
557
|
+
provider.dispatch = jest.fn();
|
|
558
|
+
_context11.next = 7;
|
|
559
|
+
return provider.fetchSharedDriveSharingLinks(driveFile);
|
|
560
|
+
|
|
561
|
+
case 7:
|
|
562
|
+
result = _context11.sent;
|
|
563
|
+
expect(result).toEqual([]);
|
|
564
|
+
expect(provider.dispatch).not.toHaveBeenCalled();
|
|
565
|
+
|
|
566
|
+
case 10:
|
|
567
|
+
case "end":
|
|
568
|
+
return _context11.stop();
|
|
569
|
+
}
|
|
570
|
+
}, _callee11);
|
|
571
|
+
})));
|
|
572
|
+
});
|
|
573
|
+
describe('shareByLink shared drive 409 recovery', function () {
|
|
574
|
+
var PERM_DRIVE_FILE = {
|
|
575
|
+
type: 'io.cozy.permissions',
|
|
576
|
+
id: 'perm_drive_file',
|
|
577
|
+
attributes: {
|
|
578
|
+
type: 'share',
|
|
579
|
+
permissions: {
|
|
580
|
+
rule0: {
|
|
581
|
+
type: 'io.cozy.files',
|
|
582
|
+
verbs: ['GET'],
|
|
583
|
+
values: ['file_in_drive']
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
shortcodes: {
|
|
587
|
+
code: 'shortcode123'
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
var driveFile = {
|
|
592
|
+
_id: 'file_in_drive',
|
|
593
|
+
id: 'file_in_drive',
|
|
594
|
+
driveId: 'drive_123'
|
|
595
|
+
};
|
|
596
|
+
it('falls back to fetching existing links when create returns 409', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
|
|
597
|
+
var conflict, mockCreateSharingLink, mockFindLinksByIds, mockClient, provider, resp;
|
|
598
|
+
return _regeneratorRuntime.wrap(function _callee12$(_context12) {
|
|
599
|
+
while (1) switch (_context12.prev = _context12.next) {
|
|
600
|
+
case 0:
|
|
601
|
+
conflict = Object.assign(new Error('Conflict'), {
|
|
602
|
+
status: 409
|
|
603
|
+
});
|
|
604
|
+
mockCreateSharingLink = jest.fn().mockRejectedValue(conflict);
|
|
605
|
+
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
606
|
+
data: [PERM_DRIVE_FILE]
|
|
607
|
+
});
|
|
608
|
+
mockClient = createMockClient({});
|
|
609
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
610
|
+
createSharingLink: mockCreateSharingLink,
|
|
611
|
+
findLinksByIds: mockFindLinksByIds
|
|
612
|
+
});
|
|
613
|
+
provider = new SharingProvider({
|
|
614
|
+
client: mockClient
|
|
615
|
+
});
|
|
616
|
+
provider.state = reducer();
|
|
617
|
+
provider.dispatch = jest.fn(function (action) {
|
|
618
|
+
provider.state = reducer(provider.state, action);
|
|
619
|
+
});
|
|
620
|
+
_context12.next = 10;
|
|
621
|
+
return provider.shareByLink(driveFile, {
|
|
622
|
+
verbs: ['GET']
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
case 10:
|
|
626
|
+
resp = _context12.sent;
|
|
627
|
+
expect(mockCreateSharingLink).toHaveBeenCalled();
|
|
628
|
+
expect(mockFindLinksByIds).toHaveBeenCalledWith(['file_in_drive']);
|
|
629
|
+
expect(resp.data.id).toBe(PERM_DRIVE_FILE.id);
|
|
630
|
+
|
|
631
|
+
case 14:
|
|
632
|
+
case "end":
|
|
633
|
+
return _context12.stop();
|
|
634
|
+
}
|
|
635
|
+
}, _callee12);
|
|
636
|
+
})));
|
|
637
|
+
it('does not duplicate existing links when 409 recovery refetches them', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
|
|
638
|
+
var conflict, mockCreateSharingLink, mockFindLinksByIds, mockClient, provider, resp, permissions;
|
|
639
|
+
return _regeneratorRuntime.wrap(function _callee13$(_context13) {
|
|
640
|
+
while (1) switch (_context13.prev = _context13.next) {
|
|
641
|
+
case 0:
|
|
642
|
+
conflict = Object.assign(new Error('Conflict'), {
|
|
643
|
+
status: 409
|
|
644
|
+
});
|
|
645
|
+
mockCreateSharingLink = jest.fn().mockRejectedValue(conflict);
|
|
646
|
+
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
647
|
+
data: [PERM_DRIVE_FILE]
|
|
648
|
+
});
|
|
649
|
+
mockClient = createMockClient({});
|
|
650
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
651
|
+
createSharingLink: mockCreateSharingLink,
|
|
652
|
+
findLinksByIds: mockFindLinksByIds
|
|
653
|
+
});
|
|
654
|
+
provider = new SharingProvider({
|
|
655
|
+
client: mockClient
|
|
656
|
+
});
|
|
657
|
+
provider.state = reducer(undefined, addSharingLink(PERM_DRIVE_FILE));
|
|
658
|
+
provider.dispatch = jest.fn(function (action) {
|
|
659
|
+
provider.state = reducer(provider.state, action);
|
|
660
|
+
});
|
|
661
|
+
_context13.next = 10;
|
|
662
|
+
return provider.shareByLink(driveFile, {
|
|
663
|
+
verbs: ['GET']
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
case 10:
|
|
667
|
+
resp = _context13.sent;
|
|
668
|
+
permissions = getDocumentPermissions(provider.state, 'file_in_drive');
|
|
669
|
+
expect(resp.data.id).toBe(PERM_DRIVE_FILE.id);
|
|
670
|
+
expect(provider.dispatch).not.toHaveBeenCalled();
|
|
671
|
+
expect(permissions).toHaveLength(1);
|
|
672
|
+
|
|
673
|
+
case 15:
|
|
674
|
+
case "end":
|
|
675
|
+
return _context13.stop();
|
|
676
|
+
}
|
|
677
|
+
}, _callee13);
|
|
678
|
+
})));
|
|
679
|
+
it('rethrows the original error when 409 happens and no link is found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
|
|
680
|
+
var conflict, mockCreateSharingLink, mockFindLinksByIds, mockClient, provider;
|
|
681
|
+
return _regeneratorRuntime.wrap(function _callee14$(_context14) {
|
|
682
|
+
while (1) switch (_context14.prev = _context14.next) {
|
|
683
|
+
case 0:
|
|
684
|
+
conflict = Object.assign(new Error('Conflict'), {
|
|
685
|
+
status: 409
|
|
686
|
+
});
|
|
687
|
+
mockCreateSharingLink = jest.fn().mockRejectedValue(conflict);
|
|
688
|
+
mockFindLinksByIds = jest.fn().mockResolvedValue({
|
|
689
|
+
data: []
|
|
690
|
+
});
|
|
691
|
+
mockClient = createMockClient({});
|
|
692
|
+
mockClient.collection = jest.fn().mockReturnValue({
|
|
693
|
+
createSharingLink: mockCreateSharingLink,
|
|
694
|
+
findLinksByIds: mockFindLinksByIds
|
|
695
|
+
});
|
|
696
|
+
provider = new SharingProvider({
|
|
697
|
+
client: mockClient
|
|
698
|
+
});
|
|
699
|
+
provider.state = reducer();
|
|
700
|
+
provider.dispatch = jest.fn();
|
|
701
|
+
_context14.next = 10;
|
|
702
|
+
return expect(provider.shareByLink(driveFile, {
|
|
703
|
+
verbs: ['GET']
|
|
704
|
+
})).rejects.toBe(conflict);
|
|
705
|
+
|
|
706
|
+
case 10:
|
|
707
|
+
case "end":
|
|
708
|
+
return _context14.stop();
|
|
709
|
+
}
|
|
710
|
+
}, _callee14);
|
|
711
|
+
})));
|
|
712
|
+
});
|
|
414
713
|
describe('updateSharingMemberType', function () {
|
|
415
714
|
var mockSharing = {
|
|
416
715
|
id: 'sharing-123',
|
|
@@ -447,37 +746,37 @@ describe('updateSharingMemberType', function () {
|
|
|
447
746
|
};
|
|
448
747
|
jest.spyOn(instance, 'setState');
|
|
449
748
|
});
|
|
450
|
-
it('should throw when sharing is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
451
|
-
return _regeneratorRuntime.wrap(function
|
|
452
|
-
while (1) switch (
|
|
749
|
+
it('should throw when sharing is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
|
|
750
|
+
return _regeneratorRuntime.wrap(function _callee15$(_context15) {
|
|
751
|
+
while (1) switch (_context15.prev = _context15.next) {
|
|
453
752
|
case 0:
|
|
454
|
-
|
|
753
|
+
_context15.next = 2;
|
|
455
754
|
return expect(instance.updateSharingMemberType('unknown-id', 1, 'one-way')).rejects.toThrow('Sharing not found');
|
|
456
755
|
|
|
457
756
|
case 2:
|
|
458
757
|
case "end":
|
|
459
|
-
return
|
|
758
|
+
return _context15.stop();
|
|
460
759
|
}
|
|
461
|
-
},
|
|
760
|
+
}, _callee15);
|
|
462
761
|
})));
|
|
463
|
-
it('should throw when member is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
464
|
-
return _regeneratorRuntime.wrap(function
|
|
465
|
-
while (1) switch (
|
|
762
|
+
it('should throw when member is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
|
|
763
|
+
return _regeneratorRuntime.wrap(function _callee16$(_context16) {
|
|
764
|
+
while (1) switch (_context16.prev = _context16.next) {
|
|
466
765
|
case 0:
|
|
467
|
-
|
|
766
|
+
_context16.next = 2;
|
|
468
767
|
return expect(instance.updateSharingMemberType('sharing-123', 99, 'one-way')).rejects.toThrow('Member not found');
|
|
469
768
|
|
|
470
769
|
case 2:
|
|
471
770
|
case "end":
|
|
472
|
-
return
|
|
771
|
+
return _context16.stop();
|
|
473
772
|
}
|
|
474
|
-
},
|
|
773
|
+
}, _callee16);
|
|
475
774
|
})));
|
|
476
|
-
it('should call setReadOnly when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
477
|
-
return _regeneratorRuntime.wrap(function
|
|
478
|
-
while (1) switch (
|
|
775
|
+
it('should call setReadOnly when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17() {
|
|
776
|
+
return _regeneratorRuntime.wrap(function _callee17$(_context17) {
|
|
777
|
+
while (1) switch (_context17.prev = _context17.next) {
|
|
479
778
|
case 0:
|
|
480
|
-
|
|
779
|
+
_context17.next = 2;
|
|
481
780
|
return instance.updateSharingMemberType('sharing-123', 1, 'one-way');
|
|
482
781
|
|
|
483
782
|
case 2:
|
|
@@ -486,15 +785,15 @@ describe('updateSharingMemberType', function () {
|
|
|
486
785
|
|
|
487
786
|
case 4:
|
|
488
787
|
case "end":
|
|
489
|
-
return
|
|
788
|
+
return _context17.stop();
|
|
490
789
|
}
|
|
491
|
-
},
|
|
790
|
+
}, _callee17);
|
|
492
791
|
})));
|
|
493
|
-
it('should call setReadWrite when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
494
|
-
return _regeneratorRuntime.wrap(function
|
|
495
|
-
while (1) switch (
|
|
792
|
+
it('should call setReadWrite when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18() {
|
|
793
|
+
return _regeneratorRuntime.wrap(function _callee18$(_context18) {
|
|
794
|
+
while (1) switch (_context18.prev = _context18.next) {
|
|
496
795
|
case 0:
|
|
497
|
-
|
|
796
|
+
_context18.next = 2;
|
|
498
797
|
return instance.updateSharingMemberType('sharing-123', 1, 'two-way');
|
|
499
798
|
|
|
500
799
|
case 2:
|
|
@@ -503,15 +802,15 @@ describe('updateSharingMemberType', function () {
|
|
|
503
802
|
|
|
504
803
|
case 4:
|
|
505
804
|
case "end":
|
|
506
|
-
return
|
|
805
|
+
return _context18.stop();
|
|
507
806
|
}
|
|
508
|
-
},
|
|
807
|
+
}, _callee18);
|
|
509
808
|
})));
|
|
510
|
-
it('should not dispatch a state update — realtime refreshes the store', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
511
|
-
return _regeneratorRuntime.wrap(function
|
|
512
|
-
while (1) switch (
|
|
809
|
+
it('should not dispatch a state update — realtime refreshes the store', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
|
|
810
|
+
return _regeneratorRuntime.wrap(function _callee19$(_context19) {
|
|
811
|
+
while (1) switch (_context19.prev = _context19.next) {
|
|
513
812
|
case 0:
|
|
514
|
-
|
|
813
|
+
_context19.next = 2;
|
|
515
814
|
return instance.updateSharingMemberType('sharing-123', 1, 'one-way');
|
|
516
815
|
|
|
517
816
|
case 2:
|
|
@@ -519,15 +818,15 @@ describe('updateSharingMemberType', function () {
|
|
|
519
818
|
|
|
520
819
|
case 3:
|
|
521
820
|
case "end":
|
|
522
|
-
return
|
|
821
|
+
return _context19.stop();
|
|
523
822
|
}
|
|
524
|
-
},
|
|
823
|
+
}, _callee19);
|
|
525
824
|
})));
|
|
526
|
-
it('should not dispatch a state update for two-way — realtime refreshes the store', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
527
|
-
return _regeneratorRuntime.wrap(function
|
|
528
|
-
while (1) switch (
|
|
825
|
+
it('should not dispatch a state update for two-way — realtime refreshes the store', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
|
|
826
|
+
return _regeneratorRuntime.wrap(function _callee20$(_context20) {
|
|
827
|
+
while (1) switch (_context20.prev = _context20.next) {
|
|
529
828
|
case 0:
|
|
530
|
-
|
|
829
|
+
_context20.next = 2;
|
|
531
830
|
return instance.updateSharingMemberType('sharing-123', 1, 'two-way');
|
|
532
831
|
|
|
533
832
|
case 2:
|
|
@@ -535,44 +834,44 @@ describe('updateSharingMemberType', function () {
|
|
|
535
834
|
|
|
536
835
|
case 3:
|
|
537
836
|
case "end":
|
|
538
|
-
return
|
|
837
|
+
return _context20.stop();
|
|
539
838
|
}
|
|
540
|
-
},
|
|
839
|
+
}, _callee20);
|
|
541
840
|
})));
|
|
542
|
-
it('should rethrow error if setReadOnly fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
543
|
-
return _regeneratorRuntime.wrap(function
|
|
544
|
-
while (1) switch (
|
|
841
|
+
it('should rethrow error if setReadOnly fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
|
|
842
|
+
return _regeneratorRuntime.wrap(function _callee21$(_context21) {
|
|
843
|
+
while (1) switch (_context21.prev = _context21.next) {
|
|
545
844
|
case 0:
|
|
546
845
|
instance.sharingCol.setReadOnly.mockRejectedValue(new Error('Network error'));
|
|
547
|
-
|
|
846
|
+
_context21.next = 3;
|
|
548
847
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
549
848
|
|
|
550
849
|
case 3:
|
|
551
850
|
case "end":
|
|
552
|
-
return
|
|
851
|
+
return _context21.stop();
|
|
553
852
|
}
|
|
554
|
-
},
|
|
853
|
+
}, _callee21);
|
|
555
854
|
})));
|
|
556
|
-
it('should rethrow error if setReadWrite fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
557
|
-
return _regeneratorRuntime.wrap(function
|
|
558
|
-
while (1) switch (
|
|
855
|
+
it('should rethrow error if setReadWrite fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
|
|
856
|
+
return _regeneratorRuntime.wrap(function _callee22$(_context22) {
|
|
857
|
+
while (1) switch (_context22.prev = _context22.next) {
|
|
559
858
|
case 0:
|
|
560
859
|
instance.sharingCol.setReadWrite.mockRejectedValue(new Error('Network error'));
|
|
561
|
-
|
|
860
|
+
_context22.next = 3;
|
|
562
861
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'two-way')).rejects.toThrow('Network error');
|
|
563
862
|
|
|
564
863
|
case 3:
|
|
565
864
|
case "end":
|
|
566
|
-
return
|
|
865
|
+
return _context22.stop();
|
|
567
866
|
}
|
|
568
|
-
},
|
|
867
|
+
}, _callee22);
|
|
569
868
|
})));
|
|
570
|
-
it('should not dispatch state update if API call fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
571
|
-
return _regeneratorRuntime.wrap(function
|
|
572
|
-
while (1) switch (
|
|
869
|
+
it('should not dispatch state update if API call fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
|
|
870
|
+
return _regeneratorRuntime.wrap(function _callee23$(_context23) {
|
|
871
|
+
while (1) switch (_context23.prev = _context23.next) {
|
|
573
872
|
case 0:
|
|
574
873
|
instance.sharingCol.setReadOnly.mockRejectedValue(new Error('Network error'));
|
|
575
|
-
|
|
874
|
+
_context23.next = 3;
|
|
576
875
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
577
876
|
|
|
578
877
|
case 3:
|
|
@@ -580,9 +879,9 @@ describe('updateSharingMemberType', function () {
|
|
|
580
879
|
|
|
581
880
|
case 4:
|
|
582
881
|
case "end":
|
|
583
|
-
return
|
|
882
|
+
return _context23.stop();
|
|
584
883
|
}
|
|
585
|
-
},
|
|
884
|
+
}, _callee23);
|
|
586
885
|
})));
|
|
587
886
|
}); // TODO Convert with react-testing-library
|
|
588
887
|
// describe('hasWriteAccess', () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
3
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
4
|
var _excluded = ["recipient"];
|
|
4
5
|
|
|
@@ -6,7 +7,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
6
7
|
|
|
7
8
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
8
9
|
|
|
9
|
-
import React from 'react';
|
|
10
|
+
import React, { useState } from 'react';
|
|
10
11
|
import { useClient } from 'cozy-client';
|
|
11
12
|
import Avatar from 'cozy-ui/transpiled/react/Avatar';
|
|
12
13
|
import logger from '../../logger';
|
|
@@ -16,11 +17,19 @@ var MemberAvatar = function MemberAvatar(_ref) {
|
|
|
16
17
|
var recipient = _ref.recipient,
|
|
17
18
|
rest = _objectWithoutProperties(_ref, _excluded);
|
|
18
19
|
|
|
19
|
-
var client = useClient(); //
|
|
20
|
+
var client = useClient(); // Remember which image url failed to load so we can fall back to the
|
|
21
|
+
// initials. Storing the url (instead of a boolean) means a new url is
|
|
22
|
+
// retried automatically, without an effect to reset the error state.
|
|
23
|
+
|
|
24
|
+
var _useState = useState(null),
|
|
25
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
26
|
+
erroredSrc = _useState2[0],
|
|
27
|
+
setErroredSrc = _useState2[1]; // There are cases when due to apparent memory leaks, the recipient does not exist
|
|
20
28
|
// This will trigger an unhanded error in the Avatar component and crash the app
|
|
21
29
|
// At the moment, we are not sure where is the root cause of this cascading undefined props
|
|
22
30
|
// Nevertheless, we can prevent the crash by returning null if the recipient is undefined
|
|
23
31
|
|
|
32
|
+
|
|
24
33
|
if (!recipient) {
|
|
25
34
|
// eslint-disable-next-line
|
|
26
35
|
logger.warn('RecipientAvatar: recipient is missing, see props:', _objectSpread({
|
|
@@ -40,11 +49,18 @@ var MemberAvatar = function MemberAvatar(_ref) {
|
|
|
40
49
|
*/
|
|
41
50
|
|
|
42
51
|
|
|
43
|
-
var image = recipient.avatarPath && recipient.status ? "".concat(client.options.uri).concat(recipient.avatarPath, "
|
|
44
|
-
|
|
52
|
+
var image = recipient.avatarPath && recipient.status ? "".concat(client.options.uri).concat(recipient.avatarPath).concat(recipient.avatarPath.includes('?') ? '&' : '?', "v=").concat(recipient.status) : null; // The avatar can become unreachable (eg. the related sharing is revoked
|
|
53
|
+
// while its members are still rendered), in which case the image request
|
|
54
|
+
// fails. Without a fallback the browser shows a broken-image icon, so we
|
|
55
|
+
// render the initials instead.
|
|
56
|
+
|
|
57
|
+
return /*#__PURE__*/React.createElement(Avatar, rest, image && erroredSrc !== image ? /*#__PURE__*/React.createElement("img", {
|
|
45
58
|
width: "100%",
|
|
46
59
|
height: "100%",
|
|
47
|
-
src: image
|
|
60
|
+
src: image,
|
|
61
|
+
onError: function onError() {
|
|
62
|
+
return setErroredSrc(image);
|
|
63
|
+
}
|
|
48
64
|
}) : getInitials(recipient));
|
|
49
65
|
};
|
|
50
66
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { createMockClient } from 'cozy-client';
|
|
4
|
+
import { MemberAvatar } from './MemberAvatar';
|
|
5
|
+
import AppLike from '../../../test/AppLike';
|
|
6
|
+
describe('MemberAvatar', function () {
|
|
7
|
+
var client = createMockClient({});
|
|
8
|
+
client.options = {
|
|
9
|
+
uri: 'http://cozy.example.com'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
var setup = function setup(recipient) {
|
|
13
|
+
return render( /*#__PURE__*/React.createElement(AppLike, {
|
|
14
|
+
client: client
|
|
15
|
+
}, /*#__PURE__*/React.createElement(MemberAvatar, {
|
|
16
|
+
recipient: recipient
|
|
17
|
+
})));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
it('renders nothing when the recipient is missing', function () {
|
|
21
|
+
var _setup = setup(undefined),
|
|
22
|
+
container = _setup.container; // The component returns null; only the provider wrappers remain, with no
|
|
23
|
+
// avatar rendered inside.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
expect(container.querySelector('[class*="MuiAvatar-root"]')).toBeNull();
|
|
27
|
+
expect(container.querySelector('img')).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it('renders the avatar image when the recipient has an avatarPath and a status', function () {
|
|
30
|
+
var _setup2 = setup({
|
|
31
|
+
public_name: 'Bob',
|
|
32
|
+
status: 'owner',
|
|
33
|
+
avatarPath: '/sharings/123/recipients/0/avatar'
|
|
34
|
+
}),
|
|
35
|
+
container = _setup2.container;
|
|
36
|
+
|
|
37
|
+
var img = container.querySelector('img');
|
|
38
|
+
expect(img).toBeTruthy();
|
|
39
|
+
expect(img.getAttribute('src')).toBe('http://cozy.example.com/sharings/123/recipients/0/avatar?v=owner');
|
|
40
|
+
});
|
|
41
|
+
it('appends the cache-busting param with & when avatarPath already has a query string', function () {
|
|
42
|
+
var _setup3 = setup({
|
|
43
|
+
public_name: 'Bob',
|
|
44
|
+
status: 'owner',
|
|
45
|
+
avatarPath: '/public/avatar?fallback=initials'
|
|
46
|
+
}),
|
|
47
|
+
container = _setup3.container;
|
|
48
|
+
|
|
49
|
+
expect(container.querySelector('img').getAttribute('src')).toBe('http://cozy.example.com/public/avatar?fallback=initials&v=owner');
|
|
50
|
+
});
|
|
51
|
+
it('renders the initials when there is no avatarPath', function () {
|
|
52
|
+
var _setup4 = setup({
|
|
53
|
+
public_name: 'Bob',
|
|
54
|
+
status: 'owner'
|
|
55
|
+
}),
|
|
56
|
+
container = _setup4.container,
|
|
57
|
+
getByText = _setup4.getByText;
|
|
58
|
+
|
|
59
|
+
expect(container.querySelector('img')).toBeNull();
|
|
60
|
+
expect(getByText('B')).toBeTruthy();
|
|
61
|
+
});
|
|
62
|
+
it('falls back to the initials when the avatar image fails to load', function () {
|
|
63
|
+
var _setup5 = setup({
|
|
64
|
+
public_name: 'Bob',
|
|
65
|
+
status: 'owner',
|
|
66
|
+
avatarPath: '/sharings/123/recipients/0/avatar'
|
|
67
|
+
}),
|
|
68
|
+
container = _setup5.container,
|
|
69
|
+
getByText = _setup5.getByText;
|
|
70
|
+
|
|
71
|
+
var img = container.querySelector('img');
|
|
72
|
+
expect(img).toBeTruthy();
|
|
73
|
+
fireEvent.error(img);
|
|
74
|
+
expect(container.querySelector('img')).toBeNull();
|
|
75
|
+
expect(getByText('B')).toBeTruthy();
|
|
76
|
+
});
|
|
77
|
+
});
|