Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@
"@babel/preset-typescript": "^7.24.7",
"@babel/template": "^7.24.7",
"@babel/types": "^7.24.7",
"@box/blueprint-web": "^12.104.1",
"@box/blueprint-web-assets": "4.88.2",
"@box/blueprint-web": "^12.112.3",
"@box/blueprint-web-assets": "4.91.3",
"@box/box-ai-agent-selector": "^0.53.0",
"@box/box-ai-content-answers": "^0.139.0",
"@box/box-item-type-selector": "^0.73.1",
"@box/cldr-data": "^34.2.0",
"@box/combobox-with-api": "^1.18.0",
"@box/copy-input": "^1.5.3",
"@box/copy-input": "^1.22.3",
"@box/frontend": "^11.0.1",
"@box/item-icon": "^0.27.1",
"@box/languages": "^1.0.0",
Expand All @@ -140,8 +140,8 @@
"@box/metadata-view": "^1.10.0",
"@box/react-virtualized": "^9.22.3-rc-box.10",
"@box/types": "^0.2.1",
"@box/unified-share-modal": "^1.7.1",
"@box/user-selector": "^1.23.25",
"@box/unified-share-modal": "^1.37.1",
"@box/user-selector": "^1.55.3",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@chromatic-com/storybook": "^4.0.1",
"@commitlint/cli": "^19.8.0",
Expand Down Expand Up @@ -271,8 +271,8 @@
"regenerator-runtime": "^0.14.1",
"remarkable": "^2.0.1",
"sanitize-html": "^2.14.0",
"sass": "1.45.0",
"sass-loader": "^8.0.2",
"sass": "1.56.0",
"sass-loader": "^16.0.6",
"scroll-into-view-if-needed": "^2.2.20",
"semantic-release": "^24.2.3",
"sinon": "^2.3.7",
Expand All @@ -296,22 +296,22 @@
"webpack-dev-server": "^5.2.1"
},
"peerDependencies": {
"@box/blueprint-web": "^12.104.1",
"@box/blueprint-web-assets": "4.88.2",
"@box/blueprint-web": "^12.112.3",
"@box/blueprint-web-assets": "4.91.3",
"@box/box-ai-agent-selector": "^0.53.0",
"@box/box-ai-content-answers": "^0.139.0",
"@box/box-item-type-selector": "^0.73.1",
"@box/cldr-data": ">=34.2.0",
"@box/combobox-with-api": "^1.18.0",
"@box/copy-input": "^1.5.3",
"@box/copy-input": "^1.22.3",
"@box/item-icon": "^0.27.1",
"@box/metadata-editor": "^1.18.0",
"@box/metadata-filter": "^1.41.3",
"@box/metadata-view": "^1.10.0",
"@box/react-virtualized": "^9.22.3-rc-box.10",
"@box/types": "^0.2.1",
"@box/unified-share-modal": "^1.7.1",
"@box/user-selector": "^1.23.25",
"@box/unified-share-modal": "^1.37.1",
"@box/user-selector": "^1.55.3",
"@hapi/address": "^2.1.4",
"@tanstack/react-virtual": "^3.13.12",
"axios": "^0.30.0",
Expand Down Expand Up @@ -352,7 +352,7 @@
"regenerator-runtime": "^0.13.2",
"remarkable": "^2.0.1",
"sanitize-html": "^2.14.0",
"sass": "1.45.0",
"sass": "1.56.0",
"scroll-into-view-if-needed": "^2.2.20",
"tabbable": "^1.1.2",
"uuid": "^8.3.2"
Expand Down
2 changes: 0 additions & 2 deletions src/elements/content-sharing/SharingNotification.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ function SharingNotification({
const sendInvitesFn = useInvites(api, itemID, itemType, {
handleSuccess: response => {
createNotification(TYPE_INFO, contentSharingMessages.sendInvitesSuccess);
setIsLoading(false);
setCollaboratorsList((prevList: collaboratorsListType | null) => {
const newList = prevList ? { ...prevList } : { collaborators: [] };
const newCollab = convertCollab({
Expand All @@ -251,7 +250,6 @@ function SharingNotification({
},
handleError: () => {
createNotification(TYPE_ERROR, contentSharingMessages.sendInvitesError);
setIsLoading(false);
closeComponent();
},
setIsLoading,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1021,8 +1021,13 @@ describe('elements/content-sharing/SharingModal', () => {
wrapper.update();

await act(async () => {
wrapper.find(UnifiedShareModal).invoke(`${usmFn}`)();
try {
await wrapper.find(UnifiedShareModal).invoke(`${usmFn}`)();
} catch (error) {
expect(error).toEqual(new Error({ status: '400' }));
}
});

wrapper.update();
expect(setIsVisibleMock).toHaveBeenCalledWith(false);
const notification = wrapper.find(Notification);
Expand Down
79 changes: 71 additions & 8 deletions src/elements/content-sharing/__tests__/sharingService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,18 @@ describe('elements/content-sharing/sharingService', () => {
});

test('should call share with correct parameters when createSharedLink is called', async () => {
mockItemApiInstance.share.mockImplementation((_options, _access, successCallback) => {
successCallback({ id: '123', shared_link: null });
});

const service = createSharingServiceWrapper();
await service.createSharedLink();

expect(mockItemApiInstance.share).toHaveBeenCalledWith(
options,
undefined,
mockOnUpdateSharedLink,
{},
expect.any(Function),
expect.any(Function),
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
);
});
Expand All @@ -118,14 +122,18 @@ describe('elements/content-sharing/sharingService', () => {
});

test('should call share with ACCESS_NONE and onRemoveSharedLink when deleteSharedLink is called', async () => {
mockItemApiInstance.share.mockImplementation((_options, _access, successCallback) => {
successCallback({ id: '123', shared_link: null });
});

const service = createSharingServiceWrapper();
await service.deleteSharedLink();

expect(mockItemApiInstance.share).toHaveBeenCalledWith(
options,
ACCESS_NONE,
mockOnRemoveSharedLink,
{},
expect.any(Function),
expect.any(Function),
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
);
});
Expand All @@ -140,6 +148,10 @@ describe('elements/content-sharing/sharingService', () => {
});

test('should call updateSharedLink with basic shared link settings', async () => {
mockItemApiInstance.updateSharedLink.mockImplementation((_options, _access, successCallback) => {
successCallback({ id: '123', shared_link: null });
});

const service = createSharingServiceWrapper();

const sharedLinkSettings = {
Expand Down Expand Up @@ -167,13 +179,17 @@ describe('elements/content-sharing/sharingService', () => {
expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith(
options,
expectedConvertedSettings,
mockOnUpdateSharedLink,
{},
expect.any(Function),
expect.any(Function),
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
);
});

test('should call updateSharedLink with options including access, isDownloadAvailable, and serverUrl', async () => {
mockItemApiInstance.updateSharedLink.mockImplementation((_options, _access, successCallback) => {
successCallback({ id: '123', shared_link: null });
});

const mockConvertedSharedLinkSettings = {
password: 'test-password',
permissions: { can_download: false, can_preview: true },
Expand Down Expand Up @@ -215,13 +231,17 @@ describe('elements/content-sharing/sharingService', () => {
expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith(
options,
mockConvertedSharedLinkSettings,
mockOnUpdateSharedLink,
{},
expect.any(Function),
expect.any(Function),
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
);
});

test('should handle shared link settings correctly', async () => {
mockItemApiInstance.updateSharedLink.mockImplementation((_options, _access, successCallback) => {
successCallback({ id: '123', shared_link: null });
});

const service = createSharingServiceWrapper();

const expirationDate = new Date('2024-12-31T23:59:59Z');
Expand All @@ -239,4 +259,47 @@ describe('elements/content-sharing/sharingService', () => {
expect(convertSharedLinkSettings).toHaveBeenCalledWith(sharedLinkSettings, undefined, undefined, undefined);
});
});

describe('error handling', () => {
test('should reject when createSharedLink fails', async () => {
const mockError = new Error('Failed to create shared link');
mockItemApiInstance.share.mockImplementation((_options, _access, _successCallback, errorCallback) => {
errorCallback(mockError);
});

const service = createSharingServiceWrapper();
await expect(service.createSharedLink()).rejects.toEqual(mockError);
});

test('should reject when deleteSharedLink fails', async () => {
const mockError = new Error('Failed to delete shared link');
mockItemApiInstance.share.mockImplementation((_options, _access, _successCallback, errorCallback) => {
errorCallback(mockError);
});

const service = createSharingServiceWrapper();
await expect(service.deleteSharedLink()).rejects.toEqual(mockError);
});

test('should reject when updateSharedLink fails', async () => {
const mockError = new Error('Failed to update shared link');
mockItemApiInstance.updateSharedLink.mockImplementation(
(_options, _settings, _successCallback, errorCallback) => {
errorCallback(mockError);
},
);

const service = createSharingServiceWrapper();
const sharedLinkSettings = {
expiration: null,
isDownloadEnabled: true,
isExpirationEnabled: false,
isPasswordEnabled: false,
password: '',
vanityName: 'vanity-name',
};

await expect(service.updateSharedLink(sharedLinkSettings)).rejects.toEqual(mockError);
});
});
});
27 changes: 8 additions & 19 deletions src/elements/content-sharing/__tests__/useInvites.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { renderHook, act } from '@testing-library/react';
import { renderHook } from '@testing-library/react';
import useInvites from '../hooks/useInvites';
import API from '../../../api';

Expand Down Expand Up @@ -51,9 +51,7 @@ describe('useInvites hook', () => {
}),
);

act(() => {
result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
});
await result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });

expect(mockHandleSuccess).toHaveBeenCalledWith({ id: 'collab123', role: 'editor' });
expect(mockTransformResponse).toHaveBeenCalledWith({ id: 'collab123', role: 'editor' });
Expand All @@ -69,10 +67,7 @@ describe('useInvites hook', () => {
}),
);

act(() => {
result.current({ users: [{ email: 'fail@example.com', role: 'editor' }] });
});

await result.current({ users: [{ email: 'fail@example.com', role: 'editor' }] }).catch(() => {});
expect(mockHandleError).toHaveBeenCalled();
});

Expand All @@ -85,12 +80,8 @@ describe('useInvites hook', () => {
}),
);

let actionResult;
act(() => {
actionResult = result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
});

expect(actionResult).toEqual(Promise.resolve());
const actionResult = await result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
expect(actionResult).toEqual(null);
expect(mockHandleSuccess).not.toHaveBeenCalled();
expect(mockHandleError).not.toHaveBeenCalled();
});
Expand All @@ -108,11 +99,9 @@ describe('useInvites hook', () => {
}),
);

act(() => {
result.current({
users: [{ email: 'user@example.com', role: 'editor' }],
groups: [{ id: 'group123', role: 'viewer' }],
});
await result.current({
users: [{ email: 'user@example.com', role: 'editor' }],
groups: [{ id: 'group123', role: 'viewer' }],
});

expect(mockHandleSuccess).toHaveBeenCalledTimes(2);
Expand Down
38 changes: 24 additions & 14 deletions src/elements/content-sharing/hooks/useInvites.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,38 @@ function useInvites(api: API, itemID: string, itemType: ItemType, options: UseIn
id: itemID,
type: itemType,
};

const sendCollabRequest = collab => {
setIsLoading(true);
return api.getCollaborationsAPI(false).addCollaboration(
itemData,
collab,
response => {
handleSuccess(response);
return transformResponse(response);
},
handleError,
);
return new Promise((resolve, reject) => {
api.getCollaborationsAPI(false).addCollaboration(
itemData,
collab,
response => {
handleSuccess(response);
resolve(transformResponse(response));
},
error => {
handleError(error);
reject(error);
},
);
});
};

const createPostCollaborationFn: SendInvitesFnType =
() => async (collabRequest: InviteCollaboratorsRequest) => {
if (!transformRequest) return Promise.resolve(null);

const { users, groups } = transformRequest(collabRequest);
return Promise.all([
...users.map(user => sendCollabRequest(user)),
...groups.map(group => sendCollabRequest(group)),
]);
setIsLoading(true);
try {
return await Promise.all([
...users.map(user => sendCollabRequest(user)),
...groups.map(group => sendCollabRequest(group)),
]);
} finally {
setIsLoading(false);
}
};

if (!sendInvites) {
Expand Down
Loading