cozy-sharing 40.1.2 → 40.1.3
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 +6 -0
- package/dist/SharingProvider.js +4 -4
- package/dist/SharingProvider.spec.js +272 -154
- package/dist/components/FederatedFolder/FederatedFolderModal.revokeSelf.spec.js +294 -0
- package/package.json +3 -3
- package/src/SharingProvider.jsx +2 -4
- package/src/SharingProvider.spec.jsx +77 -0
- package/src/components/FederatedFolder/FederatedFolderModal.revokeSelf.spec.jsx +133 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
3
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
4
|
+
|
|
5
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
6
|
+
|
|
7
|
+
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
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
10
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
11
|
+
import React, { useState } from 'react';
|
|
12
|
+
import { createMockClient } from 'cozy-client';
|
|
13
|
+
import { FederatedFolderModal } from './FederatedFolderModal';
|
|
14
|
+
import AppLike from '../../../test/AppLike';
|
|
15
|
+
import { SharingProvider } from '../../SharingProvider';
|
|
16
|
+
import { useSharingContext } from '../../hooks/useSharingContext';
|
|
17
|
+
jest.mock('../ShareByEmail', function () {
|
|
18
|
+
return function () {
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
jest.mock('../ShareByLink', function () {
|
|
23
|
+
return function () {
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
var document = {
|
|
28
|
+
id: 'received-folder',
|
|
29
|
+
_id: 'received-folder',
|
|
30
|
+
name: 'Received drive',
|
|
31
|
+
type: 'directory',
|
|
32
|
+
driveId: 'received-sharing'
|
|
33
|
+
};
|
|
34
|
+
var instance = 'https://me.mycozy.cloud';
|
|
35
|
+
var members = [{
|
|
36
|
+
status: 'owner',
|
|
37
|
+
instance: 'https://owner.mycozy.cloud',
|
|
38
|
+
name: 'Owner',
|
|
39
|
+
email: 'owner@test.com'
|
|
40
|
+
}, {
|
|
41
|
+
status: 'ready',
|
|
42
|
+
instance: instance,
|
|
43
|
+
name: 'Me',
|
|
44
|
+
email: 'me@test.com'
|
|
45
|
+
}];
|
|
46
|
+
var sharing = {
|
|
47
|
+
id: document.driveId,
|
|
48
|
+
_id: document.driveId,
|
|
49
|
+
type: 'io.cozy.sharings',
|
|
50
|
+
attributes: {
|
|
51
|
+
owner: false,
|
|
52
|
+
drive: true,
|
|
53
|
+
active: true,
|
|
54
|
+
rules: [{
|
|
55
|
+
values: [document.id]
|
|
56
|
+
}],
|
|
57
|
+
members: members
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function RevocationView() {
|
|
62
|
+
var _useState = useState(false),
|
|
63
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
64
|
+
hasLeft = _useState2[0],
|
|
65
|
+
setHasLeft = _useState2[1];
|
|
66
|
+
|
|
67
|
+
var _useSharingContext = useSharingContext(),
|
|
68
|
+
allLoaded = _useSharingContext.allLoaded,
|
|
69
|
+
getSharingForSelf = _useSharingContext.getSharingForSelf;
|
|
70
|
+
|
|
71
|
+
if (!allLoaded) return null;
|
|
72
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("output", null, getSharingForSelf(document.id) ? 'Member of drive' : 'Not a member'), hasLeft ? /*#__PURE__*/React.createElement("h1", null, "Shared drives") : /*#__PURE__*/React.createElement(FederatedFolderModal, {
|
|
73
|
+
document: document,
|
|
74
|
+
onClose: function onClose() {
|
|
75
|
+
return setHasLeft(true);
|
|
76
|
+
},
|
|
77
|
+
onRevokeSuccess: function onRevokeSuccess() {
|
|
78
|
+
return setHasLeft(true);
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe('FederatedFolderModal self-revocation with SharingProvider', function () {
|
|
84
|
+
it('closes and runs the success transition after leaving, without refreshing forbidden members', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7() {
|
|
85
|
+
var hasRevoked, client, sharingCol;
|
|
86
|
+
return _regeneratorRuntime.wrap(function _callee7$(_context7) {
|
|
87
|
+
while (1) switch (_context7.prev = _context7.next) {
|
|
88
|
+
case 0:
|
|
89
|
+
hasRevoked = false;
|
|
90
|
+
client = createMockClient({});
|
|
91
|
+
client.options.uri = instance;
|
|
92
|
+
client.getStackClient().uri = instance;
|
|
93
|
+
client.isLogged = true;
|
|
94
|
+
client.plugins.realtime = {
|
|
95
|
+
subscribe: jest.fn(),
|
|
96
|
+
unsubscribe: jest.fn()
|
|
97
|
+
};
|
|
98
|
+
sharingCol = {
|
|
99
|
+
findByDoctype: function () {
|
|
100
|
+
var _findByDoctype = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
101
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
102
|
+
while (1) switch (_context.prev = _context.next) {
|
|
103
|
+
case 0:
|
|
104
|
+
return _context.abrupt("return", {
|
|
105
|
+
data: [sharing]
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
case 1:
|
|
109
|
+
case "end":
|
|
110
|
+
return _context.stop();
|
|
111
|
+
}
|
|
112
|
+
}, _callee);
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
function findByDoctype() {
|
|
116
|
+
return _findByDoctype.apply(this, arguments);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return findByDoctype;
|
|
120
|
+
}(),
|
|
121
|
+
revokeSelf: function () {
|
|
122
|
+
var _revokeSelf = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
123
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
124
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
125
|
+
case 0:
|
|
126
|
+
hasRevoked = true;
|
|
127
|
+
|
|
128
|
+
case 1:
|
|
129
|
+
case "end":
|
|
130
|
+
return _context2.stop();
|
|
131
|
+
}
|
|
132
|
+
}, _callee2);
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
function revokeSelf() {
|
|
136
|
+
return _revokeSelf.apply(this, arguments);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return revokeSelf;
|
|
140
|
+
}(),
|
|
141
|
+
fetchEffectiveRecipients: function () {
|
|
142
|
+
var _fetchEffectiveRecipients = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
|
|
143
|
+
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
144
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
145
|
+
case 0:
|
|
146
|
+
if (!hasRevoked) {
|
|
147
|
+
_context3.next = 2;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
throw Object.assign(new Error('Forbidden'), {
|
|
152
|
+
status: 403
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
case 2:
|
|
156
|
+
return _context3.abrupt("return", {
|
|
157
|
+
data: members.map(function (member, memberIndex) {
|
|
158
|
+
return _objectSpread(_objectSpread({}, member), {}, {
|
|
159
|
+
sources: [{
|
|
160
|
+
kind: 'self',
|
|
161
|
+
sharing_id: sharing.id,
|
|
162
|
+
member_index: memberIndex
|
|
163
|
+
}]
|
|
164
|
+
});
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
case 3:
|
|
169
|
+
case "end":
|
|
170
|
+
return _context3.stop();
|
|
171
|
+
}
|
|
172
|
+
}, _callee3);
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
function fetchEffectiveRecipients() {
|
|
176
|
+
return _fetchEffectiveRecipients.apply(this, arguments);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return fetchEffectiveRecipients;
|
|
180
|
+
}()
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
client.collection = function (doctype) {
|
|
184
|
+
if (doctype === 'io.cozy.sharings') return sharingCol;
|
|
185
|
+
|
|
186
|
+
if (doctype === 'io.cozy.permissions') {
|
|
187
|
+
return {
|
|
188
|
+
findLinksByDoctype: function () {
|
|
189
|
+
var _findLinksByDoctype = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
|
|
190
|
+
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
|
|
191
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
192
|
+
case 0:
|
|
193
|
+
return _context4.abrupt("return", {
|
|
194
|
+
data: []
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
case 1:
|
|
198
|
+
case "end":
|
|
199
|
+
return _context4.stop();
|
|
200
|
+
}
|
|
201
|
+
}, _callee4);
|
|
202
|
+
}));
|
|
203
|
+
|
|
204
|
+
function findLinksByDoctype() {
|
|
205
|
+
return _findLinksByDoctype.apply(this, arguments);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return findLinksByDoctype;
|
|
209
|
+
}(),
|
|
210
|
+
findLinksByIds: function () {
|
|
211
|
+
var _findLinksByIds = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
|
|
212
|
+
return _regeneratorRuntime.wrap(function _callee5$(_context5) {
|
|
213
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
214
|
+
case 0:
|
|
215
|
+
return _context5.abrupt("return", {
|
|
216
|
+
data: []
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
case 1:
|
|
220
|
+
case "end":
|
|
221
|
+
return _context5.stop();
|
|
222
|
+
}
|
|
223
|
+
}, _callee5);
|
|
224
|
+
}));
|
|
225
|
+
|
|
226
|
+
function findLinksByIds() {
|
|
227
|
+
return _findLinksByIds.apply(this, arguments);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return findLinksByIds;
|
|
231
|
+
}()
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
all: function () {
|
|
237
|
+
var _all = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
|
|
238
|
+
return _regeneratorRuntime.wrap(function _callee6$(_context6) {
|
|
239
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
240
|
+
case 0:
|
|
241
|
+
return _context6.abrupt("return", {
|
|
242
|
+
data: []
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
case 1:
|
|
246
|
+
case "end":
|
|
247
|
+
return _context6.stop();
|
|
248
|
+
}
|
|
249
|
+
}, _callee6);
|
|
250
|
+
}));
|
|
251
|
+
|
|
252
|
+
function all() {
|
|
253
|
+
return _all.apply(this, arguments);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return all;
|
|
257
|
+
}()
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
render( /*#__PURE__*/React.createElement(AppLike, {
|
|
262
|
+
client: client
|
|
263
|
+
}, /*#__PURE__*/React.createElement(SharingProvider, {
|
|
264
|
+
client: client,
|
|
265
|
+
doctype: "io.cozy.files"
|
|
266
|
+
}, /*#__PURE__*/React.createElement(RevocationView, null))));
|
|
267
|
+
_context7.t0 = fireEvent;
|
|
268
|
+
_context7.next = 12;
|
|
269
|
+
return screen.findByLabelText('Remove from sharing');
|
|
270
|
+
|
|
271
|
+
case 12:
|
|
272
|
+
_context7.t1 = _context7.sent;
|
|
273
|
+
|
|
274
|
+
_context7.t0.click.call(_context7.t0, _context7.t1);
|
|
275
|
+
|
|
276
|
+
_context7.next = 16;
|
|
277
|
+
return waitFor(function () {
|
|
278
|
+
expect(screen.queryByRole('dialog')).toBe(null);
|
|
279
|
+
expect(screen.queryByRole('heading', {
|
|
280
|
+
name: 'Shared drives'
|
|
281
|
+
})).toBeInTheDocument();
|
|
282
|
+
expect(screen.queryByText('Not a member')).toBeInTheDocument();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
case 16:
|
|
286
|
+
expect(hasRevoked).toBe(true);
|
|
287
|
+
|
|
288
|
+
case 17:
|
|
289
|
+
case "end":
|
|
290
|
+
return _context7.stop();
|
|
291
|
+
}
|
|
292
|
+
}, _callee7);
|
|
293
|
+
})));
|
|
294
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "40.1.
|
|
3
|
+
"version": "40.1.3",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"cozy-intent": "^2.31.1",
|
|
59
59
|
"cozy-minilog": "^3.10.1",
|
|
60
60
|
"cozy-ui": "^140.5.0",
|
|
61
|
-
"cozy-ui-plus": "^14.0.
|
|
61
|
+
"cozy-ui-plus": "^14.0.8",
|
|
62
62
|
"jest": "30.3.0",
|
|
63
63
|
"jest-environment-jsdom": "30.3.0",
|
|
64
64
|
"react": "16.12.0",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"sideEffects": [
|
|
85
85
|
"*.css"
|
|
86
86
|
],
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "958de80707ab26c724df5384bac265e676d80295"
|
|
88
88
|
}
|
package/src/SharingProvider.jsx
CHANGED
|
@@ -381,10 +381,8 @@ export class SharingProvider extends Component {
|
|
|
381
381
|
const sharing = getSharingForSelf(this.state, document.id)
|
|
382
382
|
await this.sharingCol.revokeSelf(sharing)
|
|
383
383
|
this.dispatch(revokeSelf(sharing))
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
document.driveId
|
|
387
|
-
)
|
|
384
|
+
// Leaving removes our access to the sharing's effective recipients.
|
|
385
|
+
this.dispatch(receiveEffectiveRecipients(getDocumentId(document), []))
|
|
388
386
|
}
|
|
389
387
|
|
|
390
388
|
/**
|
|
@@ -632,6 +632,83 @@ describe('shared drive recipient revocation', () => {
|
|
|
632
632
|
})
|
|
633
633
|
})
|
|
634
634
|
|
|
635
|
+
describe('revokeSelf', () => {
|
|
636
|
+
const document = { id: 'received-folder', _id: 'received-folder' }
|
|
637
|
+
const sharing = {
|
|
638
|
+
id: 'received-sharing',
|
|
639
|
+
type: 'io.cozy.sharings',
|
|
640
|
+
attributes: {
|
|
641
|
+
owner: false,
|
|
642
|
+
drive: true,
|
|
643
|
+
rules: [{ values: [document.id] }],
|
|
644
|
+
members: []
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const recipients = [{ sharingId: sharing.id, memberIndex: 1 }]
|
|
648
|
+
const forbidden = Object.assign(new Error('Forbidden'), { status: 403 })
|
|
649
|
+
let provider
|
|
650
|
+
let sharingCol
|
|
651
|
+
|
|
652
|
+
beforeEach(() => {
|
|
653
|
+
const client = createMockClient({})
|
|
654
|
+
sharingCol = {
|
|
655
|
+
revokeSelf: jest.fn().mockResolvedValue({}),
|
|
656
|
+
fetchEffectiveRecipients: jest.fn().mockRejectedValue(forbidden)
|
|
657
|
+
}
|
|
658
|
+
client.collection = () => sharingCol
|
|
659
|
+
provider = setupProvider(
|
|
660
|
+
client,
|
|
661
|
+
{},
|
|
662
|
+
reducer(undefined, receiveSharings({ sharings: [sharing] }))
|
|
663
|
+
)
|
|
664
|
+
provider.dispatch(receiveEffectiveRecipients(document.id, recipients))
|
|
665
|
+
provider.dispatch(receiveEffectiveRecipients('other-folder', recipients))
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
it.each([true, false])(
|
|
669
|
+
'forgets the sharing and clears its recipients without reloading lost access (drive: %s)',
|
|
670
|
+
async drive => {
|
|
671
|
+
provider.state.sharings = [
|
|
672
|
+
{ ...sharing, attributes: { ...sharing.attributes, drive } }
|
|
673
|
+
]
|
|
674
|
+
|
|
675
|
+
await provider.revokeSelf(document)
|
|
676
|
+
|
|
677
|
+
expect(getDocumentSharing(provider.state, document.id)).toBeNull()
|
|
678
|
+
expect(getEffectiveRecipients(provider.state, document.id)).toEqual([])
|
|
679
|
+
expect(getEffectiveRecipients(provider.state, 'other-folder')).toEqual(
|
|
680
|
+
recipients
|
|
681
|
+
)
|
|
682
|
+
expect(sharingCol.fetchEffectiveRecipients).not.toHaveBeenCalled()
|
|
683
|
+
}
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
it.each([403, 500])(
|
|
687
|
+
'preserves sharing state and rejects when self-revocation fails (%s)',
|
|
688
|
+
async status => {
|
|
689
|
+
const error = Object.assign(new Error('Revocation failed'), { status })
|
|
690
|
+
sharingCol.revokeSelf.mockRejectedValue(error)
|
|
691
|
+
|
|
692
|
+
await expect(provider.revokeSelf(document)).rejects.toBe(error)
|
|
693
|
+
|
|
694
|
+
expect(getDocumentSharing(provider.state, document.id)).toEqual(sharing)
|
|
695
|
+
expect(getEffectiveRecipients(provider.state, document.id)).toEqual(
|
|
696
|
+
recipients
|
|
697
|
+
)
|
|
698
|
+
}
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
it('still rejects forbidden recipient refreshes outside self-revocation', async () => {
|
|
702
|
+
await expect(provider.fetchEffectiveRecipients(document.id)).rejects.toBe(
|
|
703
|
+
forbidden
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
expect(getEffectiveRecipients(provider.state, document.id)).toEqual(
|
|
707
|
+
recipients
|
|
708
|
+
)
|
|
709
|
+
})
|
|
710
|
+
})
|
|
711
|
+
|
|
635
712
|
describe('share flow reused by PermissionTypeMenu upgrade', () => {
|
|
636
713
|
const PARENT_SHARING = {
|
|
637
714
|
_id: 'parent_sharing',
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import { createMockClient } from 'cozy-client'
|
|
5
|
+
|
|
6
|
+
import { FederatedFolderModal } from './FederatedFolderModal'
|
|
7
|
+
import AppLike from '../../../test/AppLike'
|
|
8
|
+
import { SharingProvider } from '../../SharingProvider'
|
|
9
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
10
|
+
|
|
11
|
+
jest.mock('../ShareByEmail', () => () => null)
|
|
12
|
+
jest.mock('../ShareByLink', () => () => null)
|
|
13
|
+
|
|
14
|
+
const document = {
|
|
15
|
+
id: 'received-folder',
|
|
16
|
+
_id: 'received-folder',
|
|
17
|
+
name: 'Received drive',
|
|
18
|
+
type: 'directory',
|
|
19
|
+
driveId: 'received-sharing'
|
|
20
|
+
}
|
|
21
|
+
const instance = 'https://me.mycozy.cloud'
|
|
22
|
+
const members = [
|
|
23
|
+
{
|
|
24
|
+
status: 'owner',
|
|
25
|
+
instance: 'https://owner.mycozy.cloud',
|
|
26
|
+
name: 'Owner',
|
|
27
|
+
email: 'owner@test.com'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
status: 'ready',
|
|
31
|
+
instance,
|
|
32
|
+
name: 'Me',
|
|
33
|
+
email: 'me@test.com'
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
const sharing = {
|
|
37
|
+
id: document.driveId,
|
|
38
|
+
_id: document.driveId,
|
|
39
|
+
type: 'io.cozy.sharings',
|
|
40
|
+
attributes: {
|
|
41
|
+
owner: false,
|
|
42
|
+
drive: true,
|
|
43
|
+
active: true,
|
|
44
|
+
rules: [{ values: [document.id] }],
|
|
45
|
+
members
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function RevocationView() {
|
|
50
|
+
const [hasLeft, setHasLeft] = useState(false)
|
|
51
|
+
const { allLoaded, getSharingForSelf } = useSharingContext()
|
|
52
|
+
if (!allLoaded) return null
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<output>
|
|
57
|
+
{getSharingForSelf(document.id) ? 'Member of drive' : 'Not a member'}
|
|
58
|
+
</output>
|
|
59
|
+
{hasLeft ? (
|
|
60
|
+
<h1>Shared drives</h1>
|
|
61
|
+
) : (
|
|
62
|
+
<FederatedFolderModal
|
|
63
|
+
document={document}
|
|
64
|
+
onClose={() => setHasLeft(true)}
|
|
65
|
+
onRevokeSuccess={() => setHasLeft(true)}
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
</>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('FederatedFolderModal self-revocation with SharingProvider', () => {
|
|
73
|
+
it('closes and runs the success transition after leaving, without refreshing forbidden members', async () => {
|
|
74
|
+
let hasRevoked = false
|
|
75
|
+
const client = createMockClient({})
|
|
76
|
+
client.options.uri = instance
|
|
77
|
+
client.getStackClient().uri = instance
|
|
78
|
+
client.isLogged = true
|
|
79
|
+
client.plugins.realtime = { subscribe: jest.fn(), unsubscribe: jest.fn() }
|
|
80
|
+
const sharingCol = {
|
|
81
|
+
findByDoctype: async () => ({ data: [sharing] }),
|
|
82
|
+
revokeSelf: async () => {
|
|
83
|
+
hasRevoked = true
|
|
84
|
+
},
|
|
85
|
+
fetchEffectiveRecipients: async () => {
|
|
86
|
+
if (hasRevoked) {
|
|
87
|
+
throw Object.assign(new Error('Forbidden'), { status: 403 })
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
data: members.map((member, memberIndex) => ({
|
|
91
|
+
...member,
|
|
92
|
+
sources: [
|
|
93
|
+
{
|
|
94
|
+
kind: 'self',
|
|
95
|
+
sharing_id: sharing.id,
|
|
96
|
+
member_index: memberIndex
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
client.collection = doctype => {
|
|
104
|
+
if (doctype === 'io.cozy.sharings') return sharingCol
|
|
105
|
+
if (doctype === 'io.cozy.permissions') {
|
|
106
|
+
return {
|
|
107
|
+
findLinksByDoctype: async () => ({ data: [] }),
|
|
108
|
+
findLinksByIds: async () => ({ data: [] })
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { all: async () => ({ data: [] }) }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
render(
|
|
115
|
+
<AppLike client={client}>
|
|
116
|
+
<SharingProvider client={client} doctype="io.cozy.files">
|
|
117
|
+
<RevocationView />
|
|
118
|
+
</SharingProvider>
|
|
119
|
+
</AppLike>
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
fireEvent.click(await screen.findByLabelText('Remove from sharing'))
|
|
123
|
+
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
expect(screen.queryByRole('dialog')).toBe(null)
|
|
126
|
+
expect(
|
|
127
|
+
screen.queryByRole('heading', { name: 'Shared drives' })
|
|
128
|
+
).toBeInTheDocument()
|
|
129
|
+
expect(screen.queryByText('Not a member')).toBeInTheDocument()
|
|
130
|
+
})
|
|
131
|
+
expect(hasRevoked).toBe(true)
|
|
132
|
+
})
|
|
133
|
+
})
|