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
39 changes: 1 addition & 38 deletions src/app/drive/components/ShareDialog/ShareDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { SharingMeta } from '@internxt/sdk/dist/drive/share/types';
import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings';
import { UserPlus } from '@phosphor-icons/react';
import { useTranslationContext } from 'app/i18n/provider/TranslationProvider';
import { MAX_SHARED_NAME_LENGTH } from '../../../../views/Shared/SharedView';
import { Button, Modal } from '@internxt/ui';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/store/hooks';
Expand All @@ -11,14 +10,12 @@ import { uiActions } from 'app/store/slices/ui';
import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react';
import { connect } from 'react-redux';
import errorService from 'services/error.service';
import localStorageService from 'services/local-storage.service';
import notificationsService, { ToastType } from 'app/notifications/services/notifications.service';
import shareService, { copyTextToClipboard, getSharingRoles } from 'app/share/services/share.service';
import { AdvancedSharedItem } from 'app/share/types';
import { isUserItemOwner } from 'views/Shared/utils/sharedViewUtils';
import { sharedThunks } from 'app/store/slices/sharedLinks';
import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors';
import { DriveItemData } from 'app/drive/types';
import ShareInviteDialog from '../ShareInviteDialog/ShareInviteDialog';
import './ShareDialog.scss';
import envService from 'services/env.service';
Expand All @@ -33,14 +30,7 @@ import { ProtectWithPassword } from './components/GeneralView/ProtectWithPasswor
import { UserRoleSelection } from './components/GeneralView/UserRoleSelection';
import { InvitedUsersList } from './components/GeneralView/InvitedUsersList';
import { Header } from './components/Header';

const cropSharedName = (name: string) => {
if (name.length > MAX_SHARED_NAME_LENGTH) {
return name.substring(0, 32).concat('...');
} else {
return name;
}
};
import { cropSharedName, filterEditorAndReader, getLocalUserData, isAdvancedShareItem } from './utils';

type ShareDialogProps = {
user: UserSettings;
Expand All @@ -50,33 +40,6 @@ type ShareDialogProps = {
onCloseDialog?: () => void;
};

const isAdvancedShareItem = (item: DriveItemData | AdvancedSharedItem): item is AdvancedSharedItem => {
return item['encryptionKey'];
};

const getLocalUserData = () => {
const user = localStorageService.getUser() as UserSettings;
const ownerData = {
name: user.name,
lastname: user.lastname,
email: user.email,
sharingId: '',
avatar: user.avatar,
uuid: user.uuid,
role: {
id: 'NONE',
name: 'OWNER',
createdAt: '',
updatedAt: '',
},
};
return ownerData;
};

const filterEditorAndReader = (users: Role[]): Role[] => {
return users.filter((user) => user.name === 'EDITOR' || user.name === 'READER');
};

const ShareDialog = (props: ShareDialogProps): JSX.Element => {
const { onCloseDialog } = props;

Expand Down
210 changes: 210 additions & 0 deletions src/app/drive/components/ShareDialog/utils/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
import { isRequestPending, cropSharedName, isAdvancedShareItem, getLocalUserData, filterEditorAndReader } from '.';
import { REQUEST_STATUS } from '../types';
import { localStorageService } from 'services';
import { DriveItemData } from 'app/drive/types';
import { AdvancedSharedItem } from 'app/share/types';
import { Role } from '@internxt/sdk/dist/drive/share/types';

describe('Check if the request is pending', () => {
test('When request status is pending, then returns true', () => {
const status = REQUEST_STATUS.PENDING;

const result = isRequestPending(status);

expect(result).toBeTruthy();
});

test('When request status is accepted, then returns false', () => {
const status = REQUEST_STATUS.ACCEPTED;

const result = isRequestPending(status);

expect(result).toBeFalsy();
});

test('When request status is denied, then returns false', () => {
const status = REQUEST_STATUS.DENIED;

const result = isRequestPending(status);

expect(result).toBeFalsy();
});
});

describe('Cropping the name', () => {
test('When name length is less than max length, then returns original name', () => {
const name = 'Short Name';

const result = cropSharedName(name);

expect(result).toBe('Short Name');
});

test('When name length equals max length (32), then returns original name', () => {
const name = 'a'.repeat(32);

const result = cropSharedName(name);

expect(result).toBe(name);
});

test('When name length exceeds max length, then returns cropped name with ellipsis', () => {
const name = 'This is a very long name that exceeds the maximum allowed length';

const result = cropSharedName(name);

expect(result).not.toBe(name);
expect(result.endsWith('...')).toBe(true);
expect(result.length).toBeLessThan(name.length);
expect(result.startsWith(name.substring(0, 10))).toBe(true);
});

test('When name is exactly 33 characters, then crops to 32 characters plus ellipsis', () => {
const name = 'a'.repeat(33);

const result = cropSharedName(name);

expect(result).not.toBe(name);
expect(result.endsWith('...')).toBe(true);
expect(result.length).toBe(35);
expect(result.startsWith('a'.repeat(32))).toBe(true);
});
});

describe('Check if it is an advanced share item', () => {
test('When item has an encryption key property, then returns truthy value', () => {
const item = {
id: '123',
encryptionKey: 'some-key',
name: 'Test Item',
} as unknown as AdvancedSharedItem;

const result = isAdvancedShareItem(item);

expect(result).toBeTruthy();
});

test('When item does not have an encryption key property, then returns falsy value', () => {
const item = {
id: '123',
name: 'Test Item',
} as unknown as DriveItemData;

const result = isAdvancedShareItem(item);

expect(result).toBeFalsy();
});

test('When item has an encryption key with undefined value, then returns falsy value', () => {
const item = {
id: '123',
encryptionKey: undefined,
name: 'Test Item',
} as any;

const result = isAdvancedShareItem(item);

expect(result).toBeFalsy();
});
});

describe('Get the local user data', () => {
beforeEach(() => {
vi.clearAllMocks();
});

afterEach(() => {
vi.clearAllMocks();
});

test('When user data exists in local storage, then returns formatted owner data', () => {
const mockUser = {
name: 'John',
lastname: 'Doe',
email: 'john@example.com',
avatar: 'avatar-url',
uuid: 'user-uuid-123',
} as any;
vi.spyOn(localStorageService, 'getUser').mockReturnValue(mockUser);

const result = getLocalUserData();

expect(result).toEqual({
name: 'John',
lastname: 'Doe',
email: 'john@example.com',
sharingId: '',
avatar: 'avatar-url',
uuid: 'user-uuid-123',
role: {
id: 'NONE',
name: 'OWNER',
createdAt: '',
updatedAt: '',
},
});
});

test('When user has no avatar, then returns owner data with null avatar', () => {
const mockUser = {
name: 'Bob',
lastname: 'Johnson',
email: 'bob@example.com',
avatar: null,
uuid: 'user-uuid-789',
} as any;
vi.spyOn(localStorageService, 'getUser').mockReturnValue(mockUser);

const result = getLocalUserData();

expect(result.avatar).toBeNull();
});
});

describe('Filtering roles by EDITOR and READER', () => {
test('When roles array contains only EDITOR and READER, then returns all roles', () => {
const roles: Role[] = [
{ id: '1', name: 'EDITOR', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
{ id: '2', name: 'READER', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
];

const result = filterEditorAndReader(roles);

expect(result).toHaveLength(2);
expect(result).toEqual(roles);
});

test('When roles array contains OWNER, then filters it out', () => {
const roles: Role[] = [
{ id: '1', name: 'OWNER', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
{ id: '2', name: 'EDITOR', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
{ id: '3', name: 'READER', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
];

const result = filterEditorAndReader(roles);

expect(result).toHaveLength(2);
expect(result.every((role) => role.name === 'EDITOR' || role.name === 'READER')).toBe(true);
});

test('When roles array is empty, then returns empty array', () => {
const roles: Role[] = [];

const result = filterEditorAndReader(roles);

expect(result).toHaveLength(0);
expect(result).toEqual([]);
});

test('When roles array contains no EDITOR or READER, then returns empty array', () => {
const roles: Role[] = [
{ id: '1', name: 'OWNER', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
{ id: '2', name: 'ADMIN', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01') },
];

const result = filterEditorAndReader(roles);

expect(result).toHaveLength(0);
});
});
45 changes: 45 additions & 0 deletions src/app/drive/components/ShareDialog/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { DriveItemData } from 'app/drive/types';
import { REQUEST_STATUS, RequestStatus } from '../types';
import { AdvancedSharedItem } from 'app/share/types';
import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings';
import { Role } from '@internxt/sdk/dist/drive/share/types';
import { MAX_SHARED_NAME_LENGTH } from 'views/Shared/SharedView';
import localStorageService from 'services/local-storage.service';

export const isRequestPending = (status: RequestStatus): boolean =>
status !== REQUEST_STATUS.DENIED && status !== REQUEST_STATUS.ACCEPTED;

export const cropSharedName = (name: string) => {
if (name.length > MAX_SHARED_NAME_LENGTH) {
return name.substring(0, 32).concat('...');
} else {
return name;
}
};

export const isAdvancedShareItem = (item: DriveItemData | AdvancedSharedItem): item is AdvancedSharedItem => {
return item['encryptionKey'];
};

export const getLocalUserData = () => {
const user = localStorageService.getUser() as UserSettings;
const ownerData = {
name: user.name,
lastname: user.lastname,
email: user.email,
sharingId: '',
avatar: user.avatar,
uuid: user.uuid,
role: {
id: 'NONE',
name: 'OWNER',
createdAt: '',
updatedAt: '',
},
};
return ownerData;
};

export const filterEditorAndReader = (users: Role[]): Role[] => {
return users.filter((user) => user.name === 'EDITOR' || user.name === 'READER');
};
Loading