🎨 [element-identifier]: Update test id convention

This commit is contained in:
lethemanh
2024-12-12 13:33:58 +07:00
parent 6d3be1fa28
commit c59545aff1
52 changed files with 220 additions and 180 deletions
@@ -40,7 +40,8 @@ export default function Avatar(props: AvatarProps) {
const fontSize = fontSizes[props.size || 'md']; const fontSize = fontSizes[props.size || 'md'];
const addedClassName = props.className || ''; const addedClassName = props.className || '';
const avatarTitle = props.title || ''; const avatarTitle = props.title || '';
const restProps = _.omit(props, 'size', 'type', 'avatar', 'title', 'className', 'icon'); const testClassId = props.testClassId;
const restProps = _.omit(props, 'size', 'type', 'avatar', 'title', 'className', 'icon', 'testClassId');
let className = `w-${avatarSize} h-${avatarSize} ${ let className = `w-${avatarSize} h-${avatarSize} ${
avatarType === 'circle' ? 'rounded-full' : 'rounded-sm' avatarType === 'circle' ? 'rounded-full' : 'rounded-sm'
@@ -65,17 +66,19 @@ export default function Avatar(props: AvatarProps) {
? {} ? {}
: { backgroundImage: `url('${getGradient(props.title || '')}')` }; : { backgroundImage: `url('${getGradient(props.title || '')}')` };
const testId = testClassId ? `testid:${testClassId}` : ''
if (props.icon) { if (props.icon) {
className += ' '; className += ' ';
return ( return (
<div <div
{..._.omit(restProps, 'testClassId')} {...restProps}
style={style} style={style}
className={ className={
className + className +
' text-white overflow-hidden flex items-center justify-center ' + ' text-white overflow-hidden flex items-center justify-center ' +
addedClassName + ' ' + addedClassName + ' ' +
(props.testClassId ? `testid:${props.testClassId}` : '') testId
} }
> >
{props.icon} {props.icon}
@@ -85,18 +88,18 @@ export default function Avatar(props: AvatarProps) {
if (props.avatar) { if (props.avatar) {
return ( return (
<div {..._.omit(restProps, 'testClassId')}> <div {...restProps}>
<img <img
alt={props.title} alt={props.title}
src={props.avatar} src={props.avatar}
className={className + ' object-cover ' + addedClassName + ' ' + (props.testClassId ? `testid:${props.testClassId}` : '')} className={className + ' object-cover ' + addedClassName + ' ' + testId}
/> />
</div> </div>
); );
} }
return ( return (
<div className={className + ' ' + addedClassName + ' ' + (props.testClassId ? `testid:${props.testClassId}` : '')} {..._.omit(restProps, 'testClassId')} style={style}> <div className={className + ' ' + addedClassName + ' ' + testId} {...restProps} style={style}>
<div className={lettersClass}>{letters.toUpperCase()}</div> <div className={lettersClass}>{letters.toUpperCase()}</div>
</div> </div>
); );
@@ -8,10 +8,13 @@ export const Checkbox = (props: {
value?: boolean; value?: boolean;
className?: string; className?: string;
disabled?: boolean; disabled?: boolean;
testClassId?: string;
}) => { }) => {
const renderSwitch = () => { const renderSwitch = () => {
const className = props.className || ''; const className = props.className || '';
const testId = props.testClassId ? `testid:${props.testClassId}` : '';
return ( return (
<div <div
className={ className={
@@ -22,7 +25,8 @@ export const Checkbox = (props: {
' ' + ' ' +
(props.disabled ? 'opacity-50' : 'cursor-pointer') + (props.disabled ? 'opacity-50' : 'cursor-pointer') +
' ' + ' ' +
(className || '') (className || '') + ' ' +
testId
} }
onClick={() => onClick={() =>
!props.label && !props.disabled && props.onChange && props.onChange(!props.value) !props.label && !props.disabled && props.onChange && props.onChange(!props.value)
@@ -1,7 +1,7 @@
export const Loader = (props: { className?: string }) => { export const Loader = (props: { className?: string }) => {
return ( return (
<svg <svg
className={'animate-spin text-gray-400 inline ' + (props.className || '')} className={'animate-spin text-gray-400 inline ' + (props.className || '') + ' testid:loader'}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -30,7 +30,7 @@ export const ConfirmModal = (props: ConfirmModalProps) => {
}; };
} }
return <> return <>
<Modal open={props.open} onClose={dialogCloseHandler()}> <Modal className="testid:confirm-modal" open={props.open} onClose={dialogCloseHandler()}>
<ModalContent <ModalContent
title={props.title} title={props.title}
text={props.text} text={props.text}
@@ -42,14 +42,14 @@ export const ConfirmModal = (props: ConfirmModalProps) => {
className="ml-2" className="ml-2"
theme={props.buttonOkTheme || "danger"} theme={props.buttonOkTheme || "danger"}
onClick={dialogCloseHandler(true)} onClick={dialogCloseHandler(true)}
testClassId="confirm-modal-button-confirm" testClassId="button-confirm"
> >
{Languages.t(props.buttonOkLabel)} {Languages.t(props.buttonOkLabel)}
</Button> </Button>
<Button <Button
theme='default' theme='default'
onClick={dialogCloseHandler(false)} onClick={dialogCloseHandler(false)}
testClassId="confirm-modal-button-cancel" testClassId="button-cancel"
> >
{Languages.t(props.buttonCancelLabel || "general.cancel")} {Languages.t(props.buttonCancelLabel || "general.cancel")}
</Button> </Button>
@@ -15,6 +15,7 @@ type PropsType = {
contentColumnStyle?: CSSProperties; contentColumnStyle?: CSSProperties;
className?: string; className?: string;
children?: JSX.Element | JSX.Element[]; children?: JSX.Element | JSX.Element[];
testClassId?: string;
}; };
const Banner: FC<PropsType> = ({ const Banner: FC<PropsType> = ({
@@ -27,6 +28,7 @@ const Banner: FC<PropsType> = ({
style, style,
contentColumnStyle, contentColumnStyle,
className, className,
testClassId,
}) => { }) => {
const headerStyle = { const headerStyle = {
height: height ? height : 68, height: height ? height : 68,
@@ -34,13 +36,15 @@ const Banner: FC<PropsType> = ({
...style, ...style,
}; };
const testId = testClassId ? `testid:${testClassId}` : ''
return ( return (
<Layout.Header className={`banner-container ${type} ${className || ''}`} style={headerStyle}> <Layout.Header className={`banner-container ${type} ${className || ''} ${testId}`} style={headerStyle}>
<Row align="middle" justify="space-between" gutter={[0, 0]} style={headerStyle}> <Row align="middle" justify="space-between" gutter={[0, 0]} style={headerStyle}>
<Col /* ghost column */></Col> <Col /* ghost column */></Col>
<Col style={contentColumnStyle}>{content || children || ''}</Col> <Col style={contentColumnStyle}>{content || children || ''}</Col>
<Col className="banner-col-icon"> <Col className="banner-col-icon">
{closable && <X size={16} className="icon" onClick={onClose} />} {closable && <X size={16} className="icon testid:button-close" onClick={onClose} />}
</Col> </Col>
</Row> </Row>
</Layout.Header> </Layout.Header>
@@ -12,19 +12,19 @@ export default () => {
const [{ connected, reconnecting }] = useRecoilState(ConnectedState); const [{ connected, reconnecting }] = useRecoilState(ConnectedState);
return ( return (
<div className={'connection_indicator ' + (connected === false ? 'visible' : '')}> <div className={'connection_indicator ' + (connected === false ? 'visible' : '') + ' testid:connection-indicator'}>
{connected === false && reconnecting !== true && ( {connected === false && reconnecting !== true && (
<div> <div className="testid:disconnected-status">
<ErrorOutlinedIcon /> <span>{Languages.t('general.connexion_status.disconnected')}</span> <ErrorOutlinedIcon /> <span>{Languages.t('general.connexion_status.disconnected')}</span>
</div> </div>
)} )}
{connected === false && reconnecting === true && ( {connected === false && reconnecting === true && (
<div> <div className="testid:reconnect-status">
<HourglassEmpty /> <span>{Languages.t('general.connexion_status.connecting')}</span> <HourglassEmpty /> <span>{Languages.t('general.connexion_status.connecting')}</span>
</div> </div>
)} )}
{connected === true && ( {connected === true && (
<div> <div className="testid:connected-status">
<CheckCircleIcon /> <span>{Languages.t('general.connexion_status.connected')}</span> <CheckCircleIcon /> <span>{Languages.t('general.connexion_status.connected')}</span>
</div> </div>
)} )}
@@ -76,7 +76,7 @@ export default ({ pendingFileState, pendingFile }: PropsType) => {
maxWidth: isPendingFileStatusPause(pendingFile.status) ? 130 : 160, maxWidth: isPendingFileStatusPause(pendingFile.status) ? 130 : 160,
verticalAlign: 'middle', verticalAlign: 'middle',
}} }}
className="testid:pending-file-name" className="testid:file-name"
> >
{capitalize(pendingFile?.originalFile.name)} {capitalize(pendingFile?.originalFile.name)}
</Text> </Text>
@@ -100,6 +100,7 @@ export default ({ pendingFileState, pendingFile }: PropsType) => {
{pendingFile?.label ? ( {pendingFile?.label ? (
<Row justify="start" align="middle" wrap={false}> <Row justify="start" align="middle" wrap={false}>
<Text <Text
className="testid:file-label"
ellipsis ellipsis
style={{ style={{
maxWidth: isPendingFileStatusPause(pendingFile.status) ? 130 : 160, maxWidth: isPendingFileStatusPause(pendingFile.status) ? 130 : 160,
@@ -140,6 +141,7 @@ export default ({ pendingFileState, pendingFile }: PropsType) => {
? Languages.t('general.resume') ? Languages.t('general.resume')
: Languages.t('general.pause') : Languages.t('general.pause')
} }
className="pending-file-row-tooltip-file-status"
> >
<Button <Button
type="link" type="link"
@@ -158,7 +160,7 @@ export default ({ pendingFileState, pendingFile }: PropsType) => {
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}} }}
className="testid:pending-file-button-status" className="testid:button-toggle-tooltip-status"
/> />
</Tooltip> </Tooltip>
) : ( ) : (
@@ -187,14 +189,14 @@ export default ({ pendingFileState, pendingFile }: PropsType) => {
> >
{!isPendingFileStatusSuccess(pendingFileState.status) && {!isPendingFileStatusSuccess(pendingFileState.status) &&
!isPendingFileStatusError(pendingFileState.status) ? ( !isPendingFileStatusError(pendingFileState.status) ? (
<Tooltip title={Languages.t('general.cancel')} placement="top"> <Tooltip title={Languages.t('general.cancel')} placement="top" className="testid:pending-file-row-tooltip-cancel">
<Button <Button
type="link" type="link"
shape="circle" shape="circle"
icon={<Trash2 size={16} color={'var(--black)'} />} icon={<Trash2 size={16} color={'var(--black)'} />}
onClick={() => cancelUpload(pendingFileState.id)} onClick={() => cancelUpload(pendingFileState.id)}
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
className="testid:pending-file-button-cancel" className="testid:button-toggle-tooltip-cancel"
/> />
</Tooltip> </Tooltip>
) : ( ) : (
@@ -202,7 +204,7 @@ export default ({ pendingFileState, pendingFile }: PropsType) => {
)} )}
</Col> </Col>
</Row> </Row>
<div className="file-progress-bar-container testid:pending-file-progress-bar"> <div className="file-progress-bar-container testid:progress-bar">
<Progress <Progress
type="line" type="line"
className="file-progress-bar" className="file-progress-bar"
@@ -49,7 +49,7 @@ export default ({ pendingFilesState, visible }: PropsType) => {
}, [getOnePendingFile, pendingFilesState]); }, [getOnePendingFile, pendingFilesState]);
return pendingFilesState.length > 0 ? ( return pendingFilesState.length > 0 ? (
<Layout className={'pending-files-list-layout ' + (visible ? 'visible' : '')}> <Layout className={'pending-files-list-layout ' + (visible ? 'visible' : '') + ' testid:pending-file-list'}>
<Header <Header
className={classNames('pending-files-list-header')} className={classNames('pending-files-list-header')}
onClick={() => setHiddenPendingFiles(!hiddenPendingFiles)} onClick={() => setHiddenPendingFiles(!hiddenPendingFiles)}
@@ -44,6 +44,7 @@ export default () => {
footer={null} footer={null}
destroyOnClose={true} destroyOnClose={true}
width={ModalManager.getPosition()?.size?.width || '700px'} width={ModalManager.getPosition()?.size?.width || '700px'}
className={`testid:${ModalManager.getModalTestClassId()}`}
> >
{component} {component}
</Modal> </Modal>
@@ -20,7 +20,7 @@ class ModalManagerService extends Observable {
this.notify(); this.notify();
} }
open(component: any, position: any, canClose?: boolean, clearState?: any) { open(component: any, position: any, canClose?: boolean, clearState?: any, modalTestClassId?: string) {
this.close(); this.close();
this.mountedComponent = null; this.mountedComponent = null;
@@ -33,7 +33,7 @@ class ModalManagerService extends Observable {
if (clearState) { if (clearState) {
delete this.popupStates[clearState]; delete this.popupStates[clearState];
} }
this.component.push({ component: component, canClose: canClose !== false }); this.component.push({ component: component, canClose: canClose !== false, modalTestClassId });
this.notify(); this.notify();
}, 100); }, 100);
} }
@@ -76,6 +76,9 @@ class ModalManagerService extends Observable {
getComponent() { getComponent() {
return this.component[this.component.length - 1]?.component || ''; return this.component[this.component.length - 1]?.component || '';
} }
getModalTestClassId() {
return this.component[this.component.length - 1]?.modalTestClassId || '';
}
} }
const ModalManager = ModalManagerService.service; const ModalManager = ModalManagerService.service;
@@ -57,6 +57,8 @@ const NewVersionComponent = (): JSX.Element => {
size: { width: '600px' }, size: { width: '600px' },
}, },
false, false,
undefined,
'modal-new-version'
); );
} }
@@ -80,9 +82,10 @@ const NewVersionComponent = (): JSX.Element => {
<Banner <Banner
height={32} height={32}
type="primary" type="primary"
testClassId="new-version-banner"
content={ content={
<Row align="middle" gutter={[8, 0]}> <Row align="middle" gutter={[8, 0]}>
<Col> <Col className="testid:new-version-text">
<b>{Languages.t('components.newversion.new_version_component.row.part_1')}</b>,{' '} <b>{Languages.t('components.newversion.new_version_component.row.part_1')}</b>,{' '}
{Languages.t('components.newversion.new_version_component.row.part_2')}{' '} {Languages.t('components.newversion.new_version_component.row.part_2')}{' '}
<Emojione type="rocket" /> <Emojione type="rocket" />
@@ -92,6 +95,7 @@ const NewVersionComponent = (): JSX.Element => {
style={{ color: 'var(--white)' }} style={{ color: 'var(--white)' }}
underline underline
onClick={() => window.location.reload()} onClick={() => window.location.reload()}
className="testid:button-reload"
> >
{Languages.t('components.newversion.new_version_component.link')} {Languages.t('components.newversion.new_version_component.link')}
</Typography.Link> </Typography.Link>
@@ -17,7 +17,7 @@ const NewVersionModal = () => {
</> </>
} }
footer={ footer={
<Button type="primary" onClick={onClickBtn}> <Button type="primary" onClick={onClickBtn} className="testid:button-reload">
{Languages.t('scenes.app.header.disconnected.reload')} {Languages.t('scenes.app.header.disconnected.reload')}
</Button> </Button>
} }
@@ -38,7 +38,7 @@ const ObjectModal: FC<PropsType> = (props: PropsType) => {
hasSider={false} hasSider={false}
style={props.style || {}} style={props.style || {}}
> >
<Header className="modal-layout-header" style={props.headerStyle || {}}> <Header className="modal-layout-header testid:modal-header" style={props.headerStyle || {}}>
<Row justify="space-between" align="middle" className="modal-layout-row" wrap={false}> <Row justify="space-between" align="middle" className="modal-layout-row" wrap={false}>
{props.titleCenter && <Col style={{ width: 32 }}></Col>} {props.titleCenter && <Col style={{ width: 32 }}></Col>}
{props.title && ( {props.title && (
@@ -62,7 +62,7 @@ const ObjectModal: FC<PropsType> = (props: PropsType) => {
type="times" type="times"
className={`m-icon-small square-button ${ className={`m-icon-small square-button ${
props.headerStyle?.backgroundColor ? 'red' : '' props.headerStyle?.backgroundColor ? 'red' : ''
}`} } testid:button-close-modal`}
onClick={() => (props?.onClose ? props.onClose() : ModalManager.closeAll())} onClick={() => (props?.onClose ? props.onClose() : ModalManager.closeAll())}
/> />
</Col> </Col>
@@ -71,7 +71,7 @@ const ObjectModal: FC<PropsType> = (props: PropsType) => {
</Header> </Header>
{props.children && <Content style={props.contentStyle}>{props.children}</Content>} {props.children && <Content style={props.contentStyle}>{props.children}</Content>}
{props.footer && ( {props.footer && (
<Footer className="modal-layout-footer"> <Footer className="modal-layout-footer testid:modal-footer">
{!props.hideFooterDivider && ( {!props.hideFooterDivider && (
<Divider className="y-margin" style={props.footerDividerStyle} /> <Divider className="y-margin" style={props.footerDividerStyle} />
)} )}
@@ -69,7 +69,7 @@ export default (props: { driveItem: DriveItem & { user?: UserType }}) => {
theme="outline" theme="outline"
className="w-9 !p-0 flex items-center justify-center ml-2 rounded-full border-none" className="w-9 !p-0 flex items-center justify-center ml-2 rounded-full border-none"
onClick={() => onDriveItemDownloadClick(file)} onClick={() => onDriveItemDownloadClick(file)}
testClassId="drive-item-button-download" testClassId="button-download"
> >
<DownloadIcon className="text-blue-500 w-6 h-6" /> <DownloadIcon className="text-blue-500 w-6 h-6" />
</Button> </Button>
@@ -96,21 +96,21 @@ export const FileResultMedia = (props: {
if (file.is_directory) { if (file.is_directory) {
return ( return (
<div className={'relative flex bg-blue-100 rounded-md ' + (props.className || '')}> <div className={'relative flex bg-blue-100 rounded-md ' + (props.className || '') + ' testid:folder-result-media'}>
<FolderIcon className="w-10 h-10 m-auto text-blue-500" /> <FolderIcon className="w-10 h-10 m-auto text-blue-500 testid:folder-icon" />
</div> </div>
); );
} }
return ( return (
<div className={'relative flex bg-zinc-200 rounded-md ' + (props.className || '')}> <div className={'relative flex bg-zinc-200 rounded-md ' + (props.className || '') + ' testid:file-result-media'}>
<Media <Media
size={props.size || 'md'} size={props.size || 'md'}
url={url} url={url}
duration={type === 'video' ? extension : undefined} duration={type === 'video' ? extension : undefined}
/> />
{(!['image', 'video'].includes(type) || !url) && ( {(!['image', 'video'].includes(type) || !url) && (
<DocumentIcon item={file} fileType={type} className={iconClassName} /> <DocumentIcon item={file} fileType={type} className={iconClassName} />
)} )}
</div> </div>
); );
@@ -38,7 +38,7 @@ export const SearchInput = () => {
value={input.query} value={input.query}
className={className} className={className}
placeholder={Languages.t('scenes.app.mainview.quick_search_placeholder')} placeholder={Languages.t('scenes.app.mainview.quick_search_placeholder')}
testClassId="search-input-quick-search" testClassId="input-quick-search"
/> />
)} )}
/> />
@@ -19,7 +19,7 @@ interface UserBlockProps {
export default function UserBlock(props: UserBlockProps) { export default function UserBlock(props: UserBlockProps) {
return <BaseBlock return <BaseBlock
className={props.className} className={props.className + ' testid:user-block'}
suffix={props.suffix} suffix={props.suffix}
title_suffix={props.title_suffix} title_suffix={props.title_suffix}
subtitle_suffix={props.subtitle_suffix} subtitle_suffix={props.subtitle_suffix}
@@ -29,7 +29,8 @@ export default function UserBlock(props: UserBlockProps) {
avatar={props.user?.thumbnail || ''} avatar={props.user?.thumbnail || ''}
title={props.user ? getFullName(props.user) : '-'} title={props.user ? getFullName(props.user) : '-'}
size="sm" size="sm"
/> testClassId="avatar"
/>
} }
title={ title={
<> <>
@@ -40,5 +41,5 @@ export default function UserBlock(props: UserBlockProps) {
subtitle={ subtitle={
<div className='text-sm text-slate-500'>{props.user?.email || ""}</div> <div className='text-sm text-slate-500'>{props.user?.email || ""}</div>
} }
/> />
} }
@@ -10,7 +10,7 @@ import { useDriveUpload } from '@features/drive/hooks/use-drive-upload';
import { DriveItemSelectedList, DriveItemSort } from '@features/drive/state/store'; import { DriveItemSelectedList, DriveItemSort } from '@features/drive/state/store';
import { formatBytes } from '@features/drive/utils'; import { formatBytes } from '@features/drive/utils';
import useRouterCompany from '@features/router/hooks/use-router-company'; import useRouterCompany from '@features/router/hooks/use-router-company';
import _, { get, set } from 'lodash'; import _ from 'lodash';
import { memo, Suspense, useCallback, useEffect, useRef, useState } from 'react'; import { memo, Suspense, useCallback, useEffect, useRef, useState } from 'react';
import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil'; import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil';
import { DrivePreview } from '../../viewer/drive-preview'; import { DrivePreview } from '../../viewer/drive-preview';
@@ -232,10 +232,10 @@ export default memo(
inPublicSharing, inPublicSharing,
}; };
return isMobile ? ( return isMobile ? (
<DocumentRow {...commonProps} testClassId="browser-document-row" /> <DocumentRow {...commonProps} />
) : ( ) : (
<Draggable id={index} key={index}> <Draggable id={index} key={index}>
<DocumentRow {...commonProps} testClassId="browser-document-row" /> <DocumentRow {...commonProps} />
</Draggable> </Draggable>
); );
} }
@@ -379,7 +379,7 @@ export default memo(
"browser-share-with-me-menu-file-type" "browser-share-with-me-menu-file-type"
); );
}} }}
testClassId="browser-share-with-me-button-file-type" testClassId="button-open-menu-file-type"
> >
<span> <span>
{filter.mimeType.key && filter.mimeType.key != 'All' {filter.mimeType.key && filter.mimeType.key != 'All'
@@ -402,7 +402,7 @@ export default memo(
"browser-share-with-me-menu-people" "browser-share-with-me-menu-people"
); );
}} }}
testClassId="browser-share-with-me-button-people" testClassId="button-open-menu-people"
> >
<span>{Languages.t('scenes.app.shared_with_me.people')}</span> <span>{Languages.t('scenes.app.shared_with_me.people')}</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" /> <ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
@@ -422,7 +422,7 @@ export default memo(
"browser-share-with-me-menu-last-modified" "browser-share-with-me-menu-last-modified"
); );
}} }}
testClassId="browser-share-with-me-button-last-modified" testClassId="button-open-menu-last-modified"
> >
<span> <span>
{filter.date.key && filter.date.key != 'All' {filter.date.key && filter.date.key != 'All'
@@ -452,7 +452,7 @@ export default memo(
<Menu menu={() => onBuildSortContextMenu()} sortData={sortLabel} testClassId="browser-menu-sorting"> <Menu menu={() => onBuildSortContextMenu()} sortData={sortLabel} testClassId="browser-menu-sorting">
{' '} {' '}
<Button theme="outline" className="ml-4 flex flex-row items-center" testClassId="browser-button-sorting"> <Button theme="outline" className="ml-4 flex flex-row items-center" testClassId="button-sorting">
<SortIcon <SortIcon
className={`h-4 w-4 mr-2 -ml-1 ${ className={`h-4 w-4 mr-2 -ml-1 ${
sortLabel.order === 'asc' ? 'transform rotate-180' : '' sortLabel.order === 'asc' ? 'transform rotate-180' : ''
@@ -467,7 +467,7 @@ export default memo(
{viewId !== 'shared_with_me' && ( {viewId !== 'shared_with_me' && (
<Menu menu={() => onBuildContextMenu(details)} testClassId="browser-menu-more"> <Menu menu={() => onBuildContextMenu(details)} testClassId="browser-menu-more">
{' '} {' '}
<Button theme="secondary" className="ml-4 flex flex-row items-center" testClassId="browser-button-more"> <Button theme="secondary" className="ml-4 flex flex-row items-center" testClassId="button-more">
<span> <span>
{selectedCount > 1 {selectedCount > 1
? `${selectedCount} items` ? `${selectedCount} items`
@@ -495,7 +495,7 @@ export default memo(
onClick={() => uploadItemModal()} onClick={() => uploadItemModal()}
theme="primary" theme="primary"
className="mt-4" className="mt-4"
testClassId="browser-button-add-doc" testClassId="button-add-doc"
> >
{Languages.t('scenes.app.drive.add_doc')} {Languages.t('scenes.app.drive.add_doc')}
</Button> </Button>
@@ -525,7 +525,6 @@ export default memo(
setChecked(_.pickBy({ ...checked, [child.id]: v }, _.identity)) setChecked(_.pickBy({ ...checked, [child.id]: v }, _.identity))
} }
onBuildContextMenu={() => onBuildContextMenu(details, child)} onBuildContextMenu={() => onBuildContextMenu(details, child)}
testClassId="browser-folder-row"
/> />
</Droppable> </Droppable>
) : ( ) : (
@@ -9,7 +9,6 @@ export type DriveItemProps = {
checked: boolean; checked: boolean;
onClick?: () => void; onClick?: () => void;
onBuildContextMenu: () => Promise<any[]>; onBuildContextMenu: () => Promise<any[]>;
testClassId?: string;
}; };
export type DriveItemOverlayProps = { export type DriveItemOverlayProps = {
@@ -45,9 +45,10 @@ export const DocumentIcon = (props: {
size="xs" size="xs"
type="square" type="square"
title={metadata.name} title={metadata.name}
testClassId="avatar"
/> />
: props.item?.is_directory : props.item?.is_directory
? <FolderIcon className={className + (props.blueiffyFolders || props.blueiffyFolders == null ? ' text-blue-500' : '')} /> ? <FolderIcon className={className + (props.blueiffyFolders || props.blueiffyFolders == null ? ' text-blue-500' : '') + ' testid:folder-icon'} />
: <SpecificFileTypeIcon className={className} /> : <SpecificFileTypeIcon className={className + ' testid:specific-file-type-icon'} />
); );
}; };
@@ -31,7 +31,6 @@ export const DocumentRow = ({
checked, checked,
onClick, onClick,
onBuildContextMenu, onBuildContextMenu,
testClassId,
}: DriveItemProps) => { }: DriveItemProps) => {
const history = useHistory(); const history = useHistory();
const [hover, setHover] = useState(false); const [hover, setHover] = useState(false);
@@ -45,8 +44,6 @@ export const DocumentRow = ({
// history.push(RouterServices.generateRouteFromState({ companyId: company, itemId: item.id })); // history.push(RouterServices.generateRouteFromState({ companyId: company, itemId: item.id }));
}; };
const testId = testClassId ? `testid:${testClassId}` : '';
return ( return (
<div <div
className={ className={
@@ -55,7 +52,7 @@ export const DocumentRow = ({
? (notSafe ? 'bg-rose-500' : 'bg-blue-500') + ' bg-opacity-10 hover:bg-opacity-25' ? (notSafe ? 'bg-rose-500' : 'bg-blue-500') + ' bg-opacity-10 hover:bg-opacity-25'
: 'hover:bg-zinc-500 hover:bg-opacity-10 ') + : 'hover:bg-zinc-500 hover:bg-opacity-10 ') +
(className || '') + ' ' + (className || '') + ' ' +
testId 'testid:document-row'
} }
id={`DR-${item.id}`} id={`DR-${item.id}`}
onMouseEnter={() => setHover(true)} onMouseEnter={() => setHover(true)}
@@ -17,12 +17,9 @@ export const FolderRow = ({
checked, checked,
onClick, onClick,
onBuildContextMenu, onBuildContextMenu,
testClassId,
}: DriveItemProps) => { }: DriveItemProps) => {
const [hover, setHover] = useState(false); const [hover, setHover] = useState(false);
const testId = testClassId ? ` testid:${testClassId}` : '';
return ( return (
<div <div
className={ className={
@@ -32,7 +29,7 @@ export const FolderRow = ({
? 'bg-blue-500 bg-opacity-10 hover:bg-opacity-25 ' ? 'bg-blue-500 bg-opacity-10 hover:bg-opacity-25 '
: 'hover:bg-zinc-500 hover:bg-opacity-10 ') + : 'hover:bg-zinc-500 hover:bg-opacity-10 ') +
(className || '') + ' ' + (className || '') + ' ' +
testId 'testid:folder-row'
} }
onMouseEnter={() => setHover(true)} onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)} onMouseLeave={() => setHover(false)}
@@ -133,20 +133,22 @@ const PathItem = ({
const { access: trashAccess } = useDriveItem('trash'); const { access: trashAccess } = useDriveItem('trash');
const isInSharedWithMe = viewId === 'shared_with_me'; const isInSharedWithMe = viewId === 'shared_with_me';
return ( return (
<div className={`flex items-center ${!first ? 'overflow-hidden' : ''}`}> <div className={`flex items-center ${!first ? 'overflow-hidden' : ''} testid:header-path-item`}>
<a <a
href="#" href="#"
className={`text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white ${!first ? 'overflow-hidden' : ''} testid:header-path-item`} className={`text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white ${!first ? 'overflow-hidden' : ''} testid:item-link`}
onClick={evt => { onClick={evt => {
evt.preventDefault(); evt.preventDefault();
const trashMenuItems = [ const trashMenuItems = [
{ {
testClassId: 'my-trash',
type: 'menu', type: 'menu',
text: Languages.t('components.header_path.my_trash'), text: Languages.t('components.header_path.my_trash'),
onClick: () => onClick('trash_' + user?.id, ''), onClick: () => onClick('trash_' + user?.id, ''),
}, },
{ {
testClassId: 'shared-trash',
type: 'menu', type: 'menu',
text: Languages.t('components.header_path.shared_trash'), text: Languages.t('components.header_path.shared_trash'),
onClick: () => onClick('trash', ''), onClick: () => onClick('trash', ''),
@@ -160,7 +162,13 @@ const PathItem = ({
if (first && user?.id) { if (first && user?.id) {
if (viewId?.includes('trash')) { if (viewId?.includes('trash')) {
MenusManager.openMenu(trashMenuItems, { x: evt.clientX, y: evt.clientY }, 'center'); MenusManager.openMenu(
trashMenuItems,
{ x: evt.clientX, y: evt.clientY },
'center',
undefined,
'menu-trash'
);
} else { } else {
if (viewId === 'root') { if (viewId === 'root') {
onClick('root', ''); onClick('root', '');
@@ -71,7 +71,7 @@ const ConfirmDeleteModalContent = ({ items }: { items: DriveItem[] }) => {
setSelected({}); setSelected({});
setState({ ...state, open: false }); setState({ ...state, open: false });
}} }}
testClassId="confirm-delete-modal-button-delete" testClassId="button-delete"
> >
Delete Delete
</Button> </Button>
@@ -52,7 +52,7 @@ const ConfirmModalContent = () => {
theme="primary" theme="primary"
className="float-right" className="float-right"
onClick={handleClose} onClick={handleClose}
testClassId="confirm-modal-button-move" testClassId="button-move"
> >
<>{Languages.t('components.SelectorModalContent_move_to')} '{selected[0]?.name}'</> <>{Languages.t('components.SelectorModalContent_move_to')} '{selected[0]?.name}'</>
</Button> </Button>
@@ -68,7 +68,7 @@ export const ConfirmModal = () => {
}; };
return ( return (
<Modal className="testid:confirm-modal" open={state.open} onClose={handleClose}> <Modal className="testid:confirm-move-modal" open={state.open} onClose={handleClose}>
<ConfirmModalContent key={state.parent_id} /> <ConfirmModalContent key={state.parent_id} />
</Modal> </Modal>
); );
@@ -74,7 +74,7 @@ const ConfirmTrashModalContent = ({ items }: { items: DriveItem[] }) => {
await refresh("trash"); await refresh("trash");
setState({ ...state, open: false }); setState({ ...state, open: false });
}} }}
testClassId="cofirm-trash-modal-button-remove" testClassId="button-remove"
> >
{Languages.t('components.ConfirmTrashModalContent_move_to_trash')} {Languages.t('components.ConfirmTrashModalContent_move_to_trash')}
</Button> </Button>
@@ -19,7 +19,8 @@ export const AccessLevel = ({
className={ className={
className + className +
' w-auto ' + ' w-auto ' +
(level === 'none' ? '!text-rose-500 !bg-rose-100 dark-bg-rose-800' : '') (level === 'none' ? '!text-rose-500 !bg-rose-100 dark-bg-rose-800' : '') +
' testid:select-access-level'
} }
value={level || 'read'} value={level || 'read'}
onChange={e => onChange(e.target.value as DriveFileAccessLevel & 'remove')} onChange={e => onChange(e.target.value as DriveFileAccessLevel & 'remove')}
@@ -27,7 +27,7 @@ export const UsersModal = () => {
const [state, setState] = useRecoilState(UsersModalAtom); const [state, setState] = useRecoilState(UsersModalAtom);
const userList = useUserCompanyList(); const userList = useUserCompanyList();
return ( return (
<Modal open={state.open} onClose={() => setState({ open: false })}> <Modal className="testid:manage-users-modal" open={state.open} onClose={() => setState({ open: false })}>
<Base className="block mt-4 mb-1"> <Base className="block mt-4 mb-1">
{Languages.t('components.internal-manage_root_users')} {Languages.t('components.internal-manage_root_users')}
</Base> </Base>
@@ -59,15 +59,16 @@ const UserAccessLevel = ({
//const level = role == "admin" ? "manage" : "read"; //const level = role == "admin" ? "manage" : "read";
return ( return (
<div className="p-4 border-t flex flex-row items-center justify-center"> <div className="p-4 border-t flex flex-row items-center justify-center testid:user-access-level">
<div className="shrink-0"> <div className="shrink-0">
<Avatar <Avatar
avatar={user?.thumbnail || ''} avatar={user?.thumbnail || ''}
title={!user ? '-' : currentUserService.getFullName(user)} title={!user ? '-' : currentUserService.getFullName(user)}
size="sm" size="sm"
testClassId="avatar"
/> />
</div> </div>
<div className="grow ml-2"> <div className="grow ml-2 testid:username">
<Base>{username} </Base>{' '} <Base>{username} </Base>{' '}
{user?.id === currentUser?.id && ( {user?.id === currentUser?.id && (
<Info>{Languages.t('components.internal-access_specific_rules_you')}</Info> <Info>{Languages.t('components.internal-access_specific_rules_you')}</Info>
@@ -105,7 +105,7 @@ const PropertiesModalContent = ({ id, onClose, inPublicSharing }: { id: string;
} }
}} }}
placeholder={Languages.t('components.PropertiesModalContent_place_holder')} placeholder={Languages.t('components.PropertiesModalContent_place_holder')}
testClassId="properties-modal-input-update-name" testClassId="input-update-name"
/> />
} }
/> />
@@ -116,7 +116,7 @@ const PropertiesModalContent = ({ id, onClose, inPublicSharing }: { id: string;
theme="primary" theme="primary"
loading={loading} loading={loading}
onClick={doSave} onClick={doSave}
testClassId="properties-modal-button-update-name" testClassId="button-update-name"
> >
{Languages.t('components.PropertiesModalContent_update_button')} {Languages.t('components.PropertiesModalContent_update_button')}
</Button> </Button>
@@ -81,7 +81,7 @@ export const ExpiryEditorRow = (props: {
return <> return <>
<BaseBlock <BaseBlock
className={"m-4" + (disabled ? Styles.Disabled.Yes : "")} className={"m-4" + (disabled ? Styles.Disabled.Yes : "") + ' testid:expiry-editor-row'}
disabled={disabled} disabled={disabled}
avatar={<CalendarIcon className={Styles.SmallIconClasses} />} avatar={<CalendarIcon className={Styles.SmallIconClasses} />}
title={ title={
@@ -90,7 +90,7 @@ export const ExpiryEditorRow = (props: {
{!!props.value && !isEditing && {!!props.value && !isEditing &&
<> <>
<a <a
className={disabled ? Styles.Disabled.Yes : ""} className={(disabled ? Styles.Disabled.Yes : "") + ' testid:button-edit'}
onClick={() => { onClick={() => {
if (!disabled) { if (!disabled) {
setCurrentEditedValue(props.value); setCurrentEditedValue(props.value);
@@ -130,7 +130,7 @@ export const ExpiryEditorRow = (props: {
else if (e.key == 'Enter') else if (e.key == 'Enter')
confirmSaveExpiry(currentEditedValue); confirmSaveExpiry(currentEditedValue);
}} }}
testClassId="expiry-editor-row-expired-date" testClassId="input-expired-date"
/> />
<Button <Button
disabled={disabled} disabled={disabled}
@@ -138,7 +138,7 @@ export const ExpiryEditorRow = (props: {
size="sm" size="sm"
className="max-w-xs mr-2 mt-1" className="max-w-xs mr-2 mt-1"
onClick={() => confirmSaveExpiry(currentEditedValue)} onClick={() => confirmSaveExpiry(currentEditedValue)}
testClassId="expiry-editor-row-button-confirm" testClassId="button-confirm"
> >
{Languages.t('components.public-link-security_field_confirm_edit')} {Languages.t('components.public-link-security_field_confirm_edit')}
</Button> </Button>
@@ -155,7 +155,7 @@ export const ExpiryEditorRow = (props: {
} else } else
confirmSaveExpiry(0); confirmSaveExpiry(0);
}} }}
testClassId="expiry-editor-row-checkbox" testClassId="checkbox"
/> />
} }
/> />
@@ -79,7 +79,7 @@ const ChangePublicLinkAccessLevelRow = (props: {
level={props.level} level={props.level}
hiddenLevels={['remove']} hiddenLevels={['remove']}
onChange={props.onChange} onChange={props.onChange}
testClassId="public-link-access-level-dropdown" testClassId="level-dropdown"
/> />
} }
/> />
@@ -103,7 +103,7 @@ const SwitchToAdvancedSettingsRow = (props: {
if (!props.disabled) if (!props.disabled)
props.onShowAdvancedScreen(true); props.onShowAdvancedScreen(true);
}} }}
testClassId="public-link-access-advance-switcher" testClassId="advance-switcher"
> >
{Languages.t("components.public-link-security-change")} {Languages.t("components.public-link-security-change")}
</A> </A>
@@ -75,7 +75,7 @@ export const PasswordEditorRow = (props: {
return <> return <>
<BaseBlock <BaseBlock
className={"m-4" + (disabled ? Styles.Disabled.Yes : "")} className={"m-4" + (disabled ? Styles.Disabled.Yes : "") + ' testid:password-editor-row'}
disabled={disabled} disabled={disabled}
avatar={<ShieldCheckIcon className={Styles.SmallIconClasses} />} avatar={<ShieldCheckIcon className={Styles.SmallIconClasses} />}
title={ title={
@@ -83,7 +83,7 @@ return <>
<label htmlFor={chkPasswordId}>{Languages.t('components.public-link-security_password')}</label> <label htmlFor={chkPasswordId}>{Languages.t('components.public-link-security_password')}</label>
{!!props.password?.length && !isEditingPassword && {!!props.password?.length && !isEditingPassword &&
<a <a
className={disabled ? Styles.Disabled.Yes : "!text-zinc-800"} className={(disabled ? Styles.Disabled.Yes : "!text-zinc-800") + ' testid:button-edit'}
onClick={() => { onClick={() => {
if (!disabled) { if (!disabled) {
setCurrentEditedPassword(props.password!); setCurrentEditedPassword(props.password!);
@@ -112,7 +112,7 @@ return <>
else if (e.key == 'Enter') else if (e.key == 'Enter')
confirmSavePassword(currentEditedPassword); confirmSavePassword(currentEditedPassword);
}} }}
testClassId="pasword-editor-row-editting-password" testClassId="input-edit-password"
/> />
<Button <Button
disabled={disabled} disabled={disabled}
@@ -120,7 +120,7 @@ return <>
size="sm" size="sm"
className="max-w-xs mr-2 mt-1" className="max-w-xs mr-2 mt-1"
onClick={() => confirmSavePassword(currentEditedPassword)} onClick={() => confirmSavePassword(currentEditedPassword)}
testClassId="password-editor-row-button-confirm" testClassId="button-confirm"
> >
{Languages.t('components.public-link-security_field_confirm_edit')} {Languages.t('components.public-link-security_field_confirm_edit')}
</Button> </Button>
@@ -137,7 +137,7 @@ return <>
} else } else
confirmSavePassword(""); confirmSavePassword("");
}} }}
testClassId="password-editor-row-checkbox" testClassId="checkbox"
/> />
} }
/> />
@@ -32,7 +32,7 @@ export const SelectorModal = () => {
const [state, setState] = useRecoilState(SelectorModalAtom); const [state, setState] = useRecoilState(SelectorModalAtom);
return ( return (
<Modal open={state.open} onClose={() => setState({ ...state, open: false })}> <Modal className="testid:modal-selector" open={state.open} onClose={() => setState({ ...state, open: false })}>
<SelectorModalContent key={state.parent_id} showfiles={false}/> <SelectorModalContent key={state.parent_id} showfiles={false}/>
</Modal> </Modal>
); );
@@ -78,7 +78,8 @@ const SelectorModalContent = (key: any) => {
key={folder.id} key={folder.id}
className={ className={
'flex flex-row items-center border-t -mt-px px-4 py-2 cursor-pointer ' + 'flex flex-row items-center border-t -mt-px px-4 py-2 cursor-pointer ' +
'hover:bg-zinc-500 hover:bg-opacity-10 ' 'hover:bg-zinc-500 hover:bg-opacity-10 ' +
'testid:folder'
} }
onClick={() => { onClick={() => {
setParentId(folder.id); setParentId(folder.id);
@@ -93,35 +94,36 @@ const SelectorModalContent = (key: any) => {
{key.showfiles && ( {key.showfiles && (
<> <>
{files.map(file => ( {files.map(file => (
<div <div
key={file.id} key={file.id}
className={ className={
'flex flex-row items-center border-t -mt-px px-4 py-2 cursor-pointer ' + 'flex flex-row items-center border-t -mt-px px-4 py-2 cursor-pointer ' +
'hover:bg-zinc-500 hover:bg-opacity-10 ' 'hover:bg-zinc-500 hover:bg-opacity-10 ' +
} 'testid:file'
onClick={() => {
if (state.mode === 'select-files') {
if (!selected.some(i => i.id === file.id)) {
setSelected([...selected, file]);
} else {
setSelected(selected.filter(i => i.id !== file.id));
} }
} else if (state.mode === 'select-file') { onClick={() => {
setSelected([file]); if (state.mode === 'select-files') {
} if (!selected.some(i => i.id === file.id)) {
}} setSelected([...selected, file]);
> } else {
<div className="grow flex flex-row items-center dark:text-white"> setSelected(selected.filter(i => i.id !== file.id));
<DocumentIcon className="h-5 w-5 shrink-0 text-gray-400 mr-2" /> }
{file.name} } else if (state.mode === 'select-file') {
</div> setSelected([file]);
{(state.mode === 'select-file' || state.mode === 'select-files') && ( }
<div className="shrink-0" onClick={e => e.stopPropagation()}> }}
<Checkbox value={selected.some(i => i.id === file.id)} /> >
<div className="grow flex flex-row items-center dark:text-white">
<DocumentIcon className="h-5 w-5 shrink-0 text-gray-400 mr-2" />
{file.name}
</div>
{(state.mode === 'select-file' || state.mode === 'select-files') && (
<div className="shrink-0" onClick={e => e.stopPropagation()}>
<Checkbox value={selected.some(i => i.id === file.id)} testClassId="checkbox-select-file" />
</div>
)}
</div> </div>
)}
</div>
))} ))}
</> </>
)} )}
@@ -138,7 +140,7 @@ const SelectorModalContent = (key: any) => {
setState({ ...state, open: false }); setState({ ...state, open: false });
setLoading(false); setLoading(false);
}} }}
testClassId="selector-modal-button" testClassId="button-select"
> >
{selected.length === 0 ? ( {selected.length === 0 ? (
<>{Languages.t('components.SelectorModalContent_no_items')}</> <>{Languages.t('components.SelectorModalContent_no_items')}</>
@@ -145,7 +145,7 @@ export const InternalUsersAccessManager = ({
onAddSearchResult={(userId) => { onAddSearchResult={(userId) => {
item && update(changeUserAccess(item, userId, level)); item && update(changeUserAccess(item, userId, level));
}} }}
/> />
)} )}
{!loading && resultFooterText && <> {!loading && resultFooterText && <>
<div className={(result.length == 0 ? 'rounded-md' : 'rounded-b-md') + ' grow text-center text-zinc-700 dark:text-white dark:opacity-75 py-2 dark:border-zinc-700 border-t bg-zinc-100 dark:bg-zinc-900'}> <div className={(result.length == 0 ? 'rounded-md' : 'rounded-b-md') + ' grow text-center text-zinc-700 dark:text-white dark:opacity-75 py-2 dark:border-zinc-700 border-t bg-zinc-100 dark:bg-zinc-900'}>
@@ -163,8 +163,8 @@ export const InternalUsersAccessManager = ({
userId={user?.id} userId={user?.id}
disabled={disabled} disabled={disabled}
className={(showResults ? '!rounded-none' : '') + (index === usersWithAccess.length - 1 ? ' !rounded-b-md border-y' : ' border-t')} className={(showResults ? '!rounded-none' : '') + (index === usersWithAccess.length - 1 ? ' !rounded-b-md border-y' : ' border-t')}
/> />
)} )}
{(usersWithAccess?.length ?? 0) < minUserHeight && {(usersWithAccess?.length ?? 0) < minUserHeight &&
[...Array(minUserHeight - (usersWithAccess?.length ?? 0))].map((_, i) => [...Array(minUserHeight - (usersWithAccess?.length ?? 0))].map((_, i) =>
<div key={i} className="p-4 flex flex-row"> <div key={i} className="p-4 flex flex-row">
@@ -181,7 +181,7 @@ export const InternalUsersAccessManager = ({
size="sm" size="sm"
className='text-center' className='text-center'
onClick={onCloseModal} onClick={onCloseModal}
testClassId="public-link-button-confirm-edit" testClassId="button-confirm-edit"
> >
{Languages.t('components.public-link-security_field_confirm_edit')} {Languages.t('components.public-link-security_field_confirm_edit')}
</Button> </Button>
@@ -212,7 +212,7 @@ const UserAccessLevel = (props: {
? <Button ? <Button
disabled={loading || props.disabled || user?.id === currentUser?.id} disabled={loading || props.disabled || user?.id === currentUser?.id}
size="sm" size="sm"
testClassId={`user-access-button-${props.isSearchResultAdd}`} testClassId="button-grant-access"
> >
{Languages.t('components.user_picker.modal.result_add.' + props.isSearchResultAdd)} {Languages.t('components.user_picker.modal.result_add.' + props.isSearchResultAdd)}
</Button> </Button>
@@ -69,20 +69,20 @@ export const UploadModal = ({
as="div" as="div"
{...(!state.type ? slideXTransitionReverted : slideXTransition)} {...(!state.type ? slideXTransitionReverted : slideXTransition)}
> >
<div className="-m-2" > <div className="-m-2" >
<CreateModalOption <CreateModalOption
icon={<DocumentDownloadIcon className="w-5 h-5" />} icon={<DocumentDownloadIcon className="w-5 h-5" />}
text={Languages.t('components.create_modal.upload_files')} text={Languages.t('components.create_modal.upload_files')}
onClick={() => selectFromDevice()} onClick={() => selectFromDevice()}
testClassId="upload-file-from-device" testClassId="upload-file-from-device"
/> />
<CreateModalOption <CreateModalOption
icon={<FolderDownloadIcon className="w-5 h-5" />} icon={<FolderDownloadIcon className="w-5 h-5" />}
text={Languages.t('components.create_modal.upload_folders')} text={Languages.t('components.create_modal.upload_folders')}
onClick={() => selectFolderFromDevice()} onClick={() => selectFolderFromDevice()}
testClassId="upload-folder-from-device" testClassId="upload-folder-from-device"
/> />
</div> </div>
</Transition> </Transition>
</div> </div>
</ModalContent> </ModalContent>
@@ -64,7 +64,7 @@ const VersionModalContent = ({ id }: { id: string }) => {
await uploadVersion(file); await uploadVersion(file);
await refresh(id); await refresh(id);
}} }}
testClassId="version-modal-upload-zone" testClassId="upload-zone"
> >
{access !== 'read' && ( {access !== 'read' && (
<div <div
@@ -29,7 +29,7 @@ export const SharedFilesTable = () => {
return formattedDate; return formattedDate;
}; };
return ( return (
<div className="testid:shared-file-table"> <div className="testid:shared-file">
<Title className="mb-4 block">{Languages.t('scenes.app.shared_with_me.shared_with_me')}</Title> <Title className="mb-4 block">{Languages.t('scenes.app.shared_with_me.shared_with_me')}</Title>
{/* Filters */} {/* Filters */}
<div className="flex items-center space-x-4 mb-6"> <div className="flex items-center space-x-4 mb-6">
@@ -42,8 +42,11 @@ export const SharedFilesTable = () => {
buildFileTypeContextMenu(), buildFileTypeContextMenu(),
{ x: evt.clientX, y: evt.clientY }, { x: evt.clientX, y: evt.clientY },
'center', 'center',
undefined,
'shared-file-menu-file-types'
); );
}} }}
testClassId="button-open-menu-file-types"
> >
<span>{filter.mimeType ? filter.mimeType : Languages.t('scenes.app.shared_with_me.file_type')}</span> <span>{filter.mimeType ? filter.mimeType : Languages.t('scenes.app.shared_with_me.file_type')}</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" /> <ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
@@ -58,8 +61,11 @@ export const SharedFilesTable = () => {
buildPeopleContextMen(), buildPeopleContextMen(),
{ x: evt.clientX, y: evt.clientY }, { x: evt.clientX, y: evt.clientY },
'center', 'center',
undefined,
'shared-file-menu-people'
); );
}} }}
testClassId="button-open-menu-people"
> >
<span>{Languages.t('scenes.app.shared_with_me.people')}</span> <span>{Languages.t('scenes.app.shared_with_me.people')}</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" /> <ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
@@ -75,8 +81,11 @@ export const SharedFilesTable = () => {
buildDateContextMenu(), buildDateContextMenu(),
{ x: evt.clientX, y: evt.clientY }, { x: evt.clientX, y: evt.clientY },
'center', 'center',
undefined,
'shared-file-menu-last-modified'
); );
}} }}
testClassId="button-open-menu-last-modified"
> >
<span>{Languages.t('scenes.app.shared_with_me.last_modified')}</span> <span>{Languages.t('scenes.app.shared_with_me.last_modified')}</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" /> <ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
@@ -85,7 +94,7 @@ export const SharedFilesTable = () => {
</div> </div>
<Title className="mb-4 block">{Languages.t('scenes.app.drive.documents')}:</Title> <Title className="mb-4 block">{Languages.t('scenes.app.drive.documents')}:</Title>
<div className="relative overflow-x-auto shadow-md sm:rounded-lg"> <div className="relative overflow-x-auto shadow-md sm:rounded-lg">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400"> <table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 testid:shared-file-table">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-blue-500 dark:text-white"> <thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-blue-500 dark:text-white">
<tr> <tr>
<th scope="col" className="px-6 py-3"> <th scope="col" className="px-6 py-3">
@@ -107,7 +116,7 @@ export const SharedFilesTable = () => {
driveItems.map((file: any, index: any) => ( driveItems.map((file: any, index: any) => (
<tr <tr
key={index} key={index}
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600" className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 testid:table-row"
> >
<th <th
scope="row" scope="row"
@@ -65,18 +65,18 @@ export default () => {
return ( return (
<FeatureToggles features={activeFeatureNames}> <FeatureToggles features={activeFeatureNames}>
<> <>
<div className="flex flex-col h-full w-full dark:bg-zinc-900"> <div className="flex flex-col h-full w-full dark:bg-zinc-900 testid:shared-view">
<div className="flex flex-row items-center justify-center bg-blue-500 px-4 py-2"> <div className="flex flex-row items-center justify-center bg-blue-500 px-4 py-2">
<div className="grow flex flex-row items-center"> <div className="grow flex flex-row items-center">
{group.logo && ( {group.logo && (
<Avatar avatar={group.logo} className="inline-block mr-3" size="sm" type="square" testClassId="shared-view-avatar" /> <Avatar avatar={group.logo} className="inline-block mr-3" size="sm" type="square" testClassId="avatar" />
)} )}
<span className="text-white font-semibold" style={{ lineHeight: '32px' }}> <span className="text-white font-semibold" style={{ lineHeight: '32px' }}>
Twake Drive Twake Drive
</span> </span>
</div> </div>
<div className="shrink-0"> <div className="shrink-0">
<a href="https://twake.app" target="_BLANK" rel="noreferrer" className="!text-white"> <a href="https://twake.app" target="_BLANK" rel="noreferrer" className="!text-white testid:create-account-link">
<span className="nomobile text-white"> <span className="nomobile text-white">
{Languages.t('scenes.app.mainview.create_account')} {Languages.t('scenes.app.mainview.create_account')}
</span> </span>
@@ -145,7 +145,7 @@ const AccessChecker = ({
if ((!details?.item?.id && !loading) || accessGranted === false) { if ((!details?.item?.id && !loading) || accessGranted === false) {
return ( return (
<div className="text-center"> <div className="text-center testid:access-checker">
<div style={{ height: '20vh' }} /> <div style={{ height: '20vh' }} />
<div className="inline-block text-left max-w-sm margin-auto bg-zinc-50 dark:bg-zinc-900 rounded-md p-4"> <div className="inline-block text-left max-w-sm margin-auto bg-zinc-50 dark:bg-zinc-900 rounded-md p-4">
<Title>You don't have access to this document or folder.</Title> <Title>You don't have access to this document or folder.</Title>
@@ -162,7 +162,7 @@ const AccessChecker = ({
className="-mr-px rounded-r-none border-r-none" className="-mr-px rounded-r-none border-r-none"
value={password} value={password}
onChange={e => setPassword(e.target.value)} onChange={e => setPassword(e.target.value)}
testClassId="access-checker-input-password" testClassId="input-password"
/> />
<Button <Button
className="rounded-l-none" className="rounded-l-none"
@@ -171,7 +171,7 @@ const AccessChecker = ({
await setPublicToken(token || '', password); await setPublicToken(token || '', password);
await refresh(folderId); await refresh(folderId);
}} }}
testClassId="access-checker-button-submit" testClassId="button-submit"
> >
Submit Submit
</Button> </Button>
@@ -59,6 +59,7 @@ export default (): JSX.Element => {
}, },
}, },
]} ]}
testClassId="menu-account"
> >
<Avatar <Avatar
size="md" size="md"
@@ -41,6 +41,7 @@ export default ({ className }: { className?: string }): JSX.Element => {
), ),
}, },
]} ]}
testClassId="menu-apps"
> >
<Button <Button
theme="default" theme="default"
@@ -25,16 +25,16 @@ export default (): JSX.Element => {
return ( return (
<> <>
<div className="w-full max-w-lg"> <div className="w-full max-w-lg testid:header-search">
<InputDecorationIcon <InputDecorationIcon
prefix={() => ( prefix={() => (
<SearchIcon className={'h-5 w-5 absolute m-auto top-0 bottom-0 left-3 text-blue-500'} /> <SearchIcon className={'h-5 w-5 absolute m-auto top-0 bottom-0 left-3 text-blue-500'} />
)} )}
suffix={() => suffix={() =>
<XIcon <XIcon
className={'h-4 w-4 absolute m-auto top-0 bottom-0 right-3 cursor-pointer text-zinc-500 ' + (searchState.query?.length ? '' : 'hidden')} className={'h-4 w-4 absolute m-auto top-0 bottom-0 right-3 cursor-pointer text-zinc-500 ' + (searchState.query?.length ? '' : 'hidden') + ' testid:button-clear'}
onClick={() => setSearchState({ ...searchState, query: '' }) } onClick={() => setSearchState({ ...searchState, query: '' }) }
/> />
} }
input={({ className }) => ( input={({ className }) => (
<Input <Input
@@ -44,7 +44,7 @@ export default (): JSX.Element => {
readOnly readOnly
placeholder={Languages.t('scenes.client.main_view.main_header.search_input')} placeholder={Languages.t('scenes.client.main_view.main_header.search_input')}
onClick={() => setOpen()} onClick={() => setOpen()}
testClassId="header-search-input" testClassId="input-search"
/> />
)} )}
/> />
@@ -8,7 +8,7 @@ import version from '../../../environment/version';
export default ({ openSideMenu }: { openSideMenu: () => void }) => { export default ({ openSideMenu }: { openSideMenu: () => void }) => {
return ( return (
<div <div
className="bg-white dark:bg-zinc-900 h-16 sm:h-20 p-4 sm:p-6 flex space-between items-center" className="bg-white dark:bg-zinc-900 h-16 sm:h-20 p-4 sm:p-6 flex space-between items-center testid:header"
style={{ style={{
paddingLeft: '1.875rem', paddingLeft: '1.875rem',
paddingRight: '1.875rem', paddingRight: '1.875rem',
@@ -28,14 +28,14 @@ export default ({ openSideMenu }: { openSideMenu: () => void }) => {
/> />
</div> </div>
<div className="sm:inline-grid pl-3"> <div className="sm:inline-grid pl-3">
<Info className="font-bold overflow-hidden text-ellipsis whitespace-nowrap w-full block -mb-1"> <Info className="font-bold overflow-hidden text-ellipsis whitespace-nowrap w-full block -mb-1 testid:version">
&nbsp;v{version.version} &nbsp;v{version.version}
</Info> </Info>
</div> </div>
</div> </div>
<div <div
onClick={() => openSideMenu()} onClick={() => openSideMenu()}
className="sm:hidden block shrink-0 w-10 hover:text-zinc-600 text-zinc-500 cursor-pointer -mx-2 px-2" className="sm:hidden block shrink-0 w-10 hover:text-zinc-600 text-zinc-500 cursor-pointer -mx-2 px-2 testid:button-open-side-menu"
> >
<MenuIcon className="w-6 h-6" /> <MenuIcon className="w-6 h-6" />
</div> </div>
@@ -38,7 +38,7 @@ export default React.memo((): JSX.Element => {
if (user?.id) { if (user?.id) {
page = ( page = (
<DesktopRedirect> <DesktopRedirect>
<div className="fade_in bg-zinc-100 dark:bg-black flex flex-col gap-2 h-full"> <div className="fade_in bg-zinc-100 dark:bg-black flex flex-col gap-2 h-full testid:header">
<DownloadAppBanner/> <DownloadAppBanner/>
<NewVersionComponent /> <NewVersionComponent />
@@ -79,18 +79,18 @@ export const CreateModalWithUploadZones = ({ initialParentId }: { initialParentI
parentId, parentId,
}); });
}} }}
/> />
<UploadModal <UploadModal
selectFolderFromDevice={() => uploadFolderZoneRef.current?.open()} selectFolderFromDevice={() => uploadFolderZoneRef.current?.open()}
selectFromDevice={() => uploadZoneRef.current?.open()} selectFromDevice={() => uploadZoneRef.current?.open()}
addFromUrl={(url, name) => { addFromUrl={(url, name) => {
setUploadModalState({ parent_id: '', open: false }); setUploadModalState({ parent_id: '', open: false });
uploadFromUrl(url, name, { uploadFromUrl(url, name, {
companyId, companyId,
parentId, parentId,
}); });
}} }}
/> />
</> </>
); );
}; };
@@ -119,7 +119,7 @@ export default () => {
}, [item?.id, setUploadModalState]); }, [item?.id, setUploadModalState]);
return ( return (
<div className="-m-4 overflow-hidden"> <div className="-m-4 overflow-hidden testid:sidebar-actions">
<AnimatedHeight> <AnimatedHeight>
<div className="p-4"> <div className="p-4">
<CreateModalWithUploadZones initialParentId={parentId} /> <CreateModalWithUploadZones initialParentId={parentId} />
@@ -170,7 +170,7 @@ export default () => {
theme="primary" theme="primary"
className="w-full mb-2 justify-center" className="w-full mb-2 justify-center"
style={{ boxShadow: '0 0 10px 0 rgba(0, 122, 255, 0.5)' }} style={{ boxShadow: '0 0 10px 0 rgba(0, 122, 255, 0.5)' }}
testClassId="sidebar-action-button-upload" testClassId="button-upload"
> >
<UploadIcon className="w-5 h-5 mr-2" /> {Languages.t('components.side_menu.buttons.upload')} <UploadIcon className="w-5 h-5 mr-2" /> {Languages.t('components.side_menu.buttons.upload')}
</Button> </Button>
@@ -179,7 +179,7 @@ export default () => {
size="lg" size="lg"
theme="secondary" theme="secondary"
className="w-full mb-2 justify-center" className="w-full mb-2 justify-center"
testClassId="sidebar-action-button-open-create-modal" testClassId="button-open-create-modal"
> >
<PlusIcon className="w-5 h-5 mr-2" /> {Languages.t('components.side_menu.buttons.create')} <PlusIcon className="w-5 h-5 mr-2" /> {Languages.t('components.side_menu.buttons.create')}
</Button> </Button>
@@ -48,7 +48,7 @@ export default () => {
dirId && viewId && setParentId(dirId); dirId && viewId && setParentId(dirId);
}, [viewId, itemId, dirId]); }, [viewId, itemId, dirId]);
return ( return (
<div className="grow flex flex-col overflow-auto -m-4 p-4 relative"> <div className="grow flex flex-col overflow-auto -m-4 p-4 relative testid:sidebar">
<div className="grow"> <div className="grow">
<div className="sm:hidden block mb-2"> <div className="sm:hidden block mb-2">
<div className="flex flex-row space-between w-full"> <div className="flex flex-row space-between w-full">
@@ -4,12 +4,12 @@ import Languages from '@features/global/services/languages-service';
export default (props: { download: string; name: string }) => { export default (props: { download: string; name: string }) => {
return ( return (
<> <>
<div className="text-white m-auto w-full text-center block h-full flex items-center"> <div className="text-white m-auto w-full text-center block h-full flex items-center testid:archive-display">
<span className="block w-full text-center mt-16"> <span className="block w-full text-center mt-16">
{props.name} {props.name}
<br /> <br />
<br /> <br />
<Button theme="dark">{Languages.t('scenes.apps.drive.download_button')}</Button> <Button theme="dark" testClassId="button-download">{Languages.t('scenes.apps.drive.download_button')}</Button>
</span> </span>
</div> </div>
</> </>
@@ -9,7 +9,7 @@ export default (props: { download: string; name: string }) => {
return ( return (
<> <>
<iframe <iframe
className="w-full h-full left-0 right-0 absolute bottom-0 top-0" className="w-full h-full left-0 right-0 absolute bottom-0 top-0 testid:code-display"
title={props.name} title={props.name}
src={url} src={url}
/> />
@@ -45,7 +45,7 @@ export default () => {
} }
return ( return (
<div className="text-white m-auto w-full text-center block h-full flex items-center"> <div className="text-white m-auto w-full text-center block h-full flex items-center testid:cannot-display">
<span className="block w-full text-center">We can't display this document.</span> <span className="block w-full text-center">We can't display this document.</span>
</div> </div>
); );
@@ -35,7 +35,7 @@ export default ({ download, loading, setLoading}: PropsType) => {
return ( return (
<TransformComponent wrapperClass="absolute w-full h-full top-0 left-0 right-0 bottom-0"> <TransformComponent wrapperClass="absolute w-full h-full top-0 left-0 right-0 bottom-0">
<img <img
className="object-contain" className="object-contain testid:image-display"
style={{ style={{
transform: `rotate(${rotated}deg)`, transform: `rotate(${rotated}deg)`,
transition: 'transform 200ms', transition: 'transform 200ms',
@@ -13,7 +13,7 @@ export default (props: { download: string; name: string; id: string }) => {
if (!(props.id && previewUrl)) { if (!(props.id && previewUrl)) {
return ( return (
<> <>
<div className="text-white m-auto w-full text-center block h-full flex items-center"> <div className="text-white m-auto w-full text-center block h-full flex items-center testid:cannot-display">
<span className="block w-full text-center">We can't display this document.</span> <span className="block w-full text-center">We can't display this document.</span>
</div> </div>
</> </>
@@ -24,7 +24,7 @@ export default (props: { download: string; name: string; id: string }) => {
<> <>
{props.id && previewUrl && ( {props.id && previewUrl && (
<iframe <iframe
className="w-full h-full left-0 right-0 absolute bottom-0 top-0" className="w-full h-full left-0 right-0 absolute bottom-0 top-0 testid:preview-url"
title={props.name} title={props.name}
src={previewUrl} src={previewUrl}
/> />
@@ -4,7 +4,7 @@ export default (props: { download: string; name: string }) => {
return ( return (
<> <>
<iframe <iframe
className="w-full h-full left-0 right-0 absolute bottom-0 top-0" className="w-full h-full left-0 right-0 absolute bottom-0 top-0 testid:pdf-display"
title={props.name} title={props.name}
src={url} src={url}
/> />
@@ -4,7 +4,7 @@ export default (props: { download: string }) => {
return ( return (
<ReactPlayer <ReactPlayer
url={props.download} url={props.download}
className="max-w-full max-h-full m-auto absolute left-0 top-0 bottom-0 right-0" className="max-w-full max-h-full m-auto absolute left-0 top-0 bottom-0 right-0 testid:video-display"
controls controls
/> />
); );
@@ -42,7 +42,7 @@ export default () => {
<Modal <Modal
open={isOpen} open={isOpen}
closable={false} closable={false}
className="bg-black bg-opacity-50 !sm:max-w-none !w-full !rounded-none !p-0" className="bg-black bg-opacity-50 !sm:max-w-none !w-full !rounded-none !p-0 testid:modal-viewer"
style={{ maxWidth: 'none', margin: 0, left: 0, top: 0, height: '100vh' }} style={{ maxWidth: 'none', margin: 0, left: 0, top: 0, height: '100vh' }}
positioned={false} positioned={false}
> >
@@ -92,7 +92,7 @@ const Navigation = () => {
return ( return (
<> <>
<XIcon <XIcon
className="z-10 cursor-pointer absolute right-5 top-5 w-12 h-12 text-zinc-300 hover:text-white rounded-full p-1 hover:bg-black hover:bg-opacity-25" className="z-10 cursor-pointer absolute right-5 top-5 w-12 h-12 text-zinc-300 hover:text-white rounded-full p-1 hover:bg-black hover:bg-opacity-25 testid:button-close"
onClick={() => close()} onClick={() => close()}
/> />
@@ -103,7 +103,7 @@ const Navigation = () => {
{...fadeZoomTransition} {...fadeZoomTransition}
> >
<ChevronLeftIcon <ChevronLeftIcon
className="cursor-pointer w-10 h-10 text-zinc-300 hover:text-white" className="cursor-pointer w-10 h-10 text-zinc-300 hover:text-white testid:button-previous"
onClick={() => previous()} onClick={() => previous()}
/> />
</Transition> </Transition>
@@ -115,7 +115,7 @@ const Navigation = () => {
{...fadeZoomTransition} {...fadeZoomTransition}
> >
<ChevronRightIcon <ChevronRightIcon
className="cursor-pointer w-10 h-10 text-zinc-300 hover:text-white" className="cursor-pointer w-10 h-10 text-zinc-300 hover:text-white testid:button-next"
onClick={() => next()} onClick={() => next()}
/> />
</Transition> </Transition>
@@ -133,18 +133,19 @@ const Footer = () => {
return ( return (
<> <>
<div className="z-10 p-5 bg-black w-full flex text-white"> <div className="z-10 p-5 bg-black w-full flex text-white testid:footer">
<div className="mr-4 w-12"> <div className="mr-4 w-12">
<Avatar <Avatar
title={currentUserService.getFullName(user)} title={currentUserService.getFullName(user)}
src={currentUserService.getThumbnail(user)} src={currentUserService.getThumbnail(user)}
testClassId="avatar"
/> />
</div> </div>
<div className="grow overflow-hidden text-ellipsis"> <div className="grow overflow-hidden text-ellipsis">
<Text.Base noColor className="w-full block text-white whitespace-nowrap"> <Text.Base noColor className="w-full block text-white whitespace-nowrap testid:text-name">
{name} {name}
</Text.Base> </Text.Base>
<Text.Info className="whitespace-nowrap"> <Text.Info className="whitespace-nowrap testid:file-info">
{currentUserService.getFullName(user)} {extension?.toLocaleUpperCase()},{' '} {currentUserService.getFullName(user)} {extension?.toLocaleUpperCase()},{' '}
{formatSize(status.details?.metadata?.size)} {formatSize(status.details?.metadata?.size)}
</Text.Info> </Text.Info>
@@ -162,6 +163,7 @@ const Footer = () => {
onClick={() => { onClick={() => {
download && (window.location.href = download); download && (window.location.href = download);
}} }}
testClassId="button-download"
/> />
</div> </div>
</div> </div>