1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00

F #5422: Add data cy master (#1634)

This commit is contained in:
Jorge Miguel Lobo Escalona 2021-11-30 17:06:20 +01:00 committed by GitHub
parent 5ff147b27c
commit 6625e09592
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 238 additions and 83 deletions

View File

@ -42,9 +42,12 @@ const useStyles = makeStyles((theme) => ({
}))
const ButtonComponent = forwardRef(
({ icon, endicon, children, size, variant = 'contained', ...props }, ref) =>
(
{ icon, endicon, children, size, variant = 'contained', value, ...props },
ref
) =>
icon && !endicon ? (
<IconButton ref={ref} {...props}>
<IconButton ref={ref} {...props} value={value}>
{children}
</IconButton>
) : (
@ -122,6 +125,7 @@ export const SubmitButtonPropTypes = {
color: PropTypes.string,
size: PropTypes.string,
variant: PropTypes.string,
value: PropTypes.string,
}
TooltipComponent.propTypes = SubmitButtonPropTypes

View File

@ -50,7 +50,7 @@ const Content = () => {
<FormWithSchema
key={id}
className={classes[id]}
cy={`instantiate-vm-template-configuration.${id}`}
cy={id}
fields={fields}
legend={legend}
id={STEP_ID}

View File

@ -38,7 +38,10 @@ const InternalLayout = ({ title, children }) => {
}, [title])
return (
<Box className={clsx(classes.root, { [classes.isDrawerFixed]: isFixMenu })}>
<Box
data-cy="main-layout"
className={clsx(classes.root, { [classes.isDrawerFixed]: isFixMenu })}
>
<Header scrollContainer={container.current} />
<Box component="main" className={classes.main}>
<CSSTransition

View File

@ -27,11 +27,18 @@ const MultipleTags = ({ tags, limitTags = 1, clipboard }) => {
const more = tags.length - limitTags
const Tags = tags
.splice(0, limitTags)
.map((tag, idx) => (
<StatusChip key={`${idx}-${tag}`} text={tag} clipboard={clipboard} />
))
const Tags = tags.splice(0, limitTags).map((tag, idx) => {
const text = tag.text ?? tag
return (
<StatusChip
key={`${idx}-${text}`}
text={text}
clipboard={clipboard}
dataCy={tag.dataCy ?? ''}
/>
)
})
return (
<>
@ -39,15 +46,20 @@ const MultipleTags = ({ tags, limitTags = 1, clipboard }) => {
{more > 0 && (
<Tooltip
arrow
title={tags.map((tag, idx) => (
<Typography
key={`${idx}-${tag}`}
variant="subtitle2"
sx={{ height: 'max-content' }}
>
{tag}
</Typography>
))}
title={tags.map((tag, idx) => {
const text = tag.text ?? tag
return (
<Typography
key={`${idx}-${text}`}
variant="subtitle2"
sx={{ height: 'max-content' }}
{...(tag.dataCy && { dataCy: tag.dataCy })}
>
{text}
</Typography>
)
})}
>
<Typography component="span" variant="subtitle2" sx={{ ml: 1 }}>
{`+${more} more`}

View File

@ -68,7 +68,14 @@ const callAll =
fns.forEach((fn) => fn && fn?.(...args))
const StatusChip = memo(
({ stateColor, text = '', clipboard = false, onClick, ...props }) => {
({
stateColor,
text = '',
dataCy = '',
clipboard = false,
onClick,
...props
}) => {
const { copy, isCopied } = useClipboard()
const textToCopy = typeof clipboard === 'string' ? clipboard : text
const classes = useStyles({ stateColor, clipboard })
@ -93,6 +100,7 @@ const StatusChip = memo(
component="span"
className={classes.text}
onClick={callAll(onClick, clipboard && handleCopy)}
data-cy={dataCy}
{...props}
>
{text}
@ -113,6 +121,7 @@ StatusChip.propTypes = {
stateColor: PropTypes.string,
text: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
clipboard: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
dataCy: PropTypes.string,
onClick: PropTypes.func,
}

View File

@ -62,6 +62,7 @@ const ActionItem = memo(
({ item, selectedRows }) => {
const {
accessor,
dataCy,
tooltip,
label,
color,
@ -75,7 +76,8 @@ const ActionItem = memo(
const buttonProps = {
color,
variant,
'data-cy': accessor && `action.${accessor}`,
'data-cy':
(dataCy && `action-${dataCy}`) ?? (accessor && `action-${accessor}`),
disabled:
typeof disabled === 'function' ? disabled(selectedRows) : disabled,
icon: Icon && <Icon />,
@ -141,6 +143,7 @@ const ActionItem = memo(
export const ActionPropTypes = PropTypes.shape({
accessor: PropTypes.string,
dataCy: PropTypes.string,
variant: PropTypes.string,
color: PropTypes.string,
size: PropTypes.string,

View File

@ -58,6 +58,7 @@ const EnhancedTable = ({
showPageCount,
singleSelect = false,
classes = {},
rootProps = {},
}) => {
const styles = EnhancedTableStyles()
@ -140,7 +141,11 @@ const EnhancedTable = ({
}
return (
<Box {...getTableProps()} className={clsx(styles.root, classes.root)}>
<Box
{...getTableProps()}
className={clsx(styles.root, classes.root)}
{...rootProps}
>
<div className={styles.toolbar}>
{/* TOOLBAR */}
{!isFetching && (
@ -236,6 +241,9 @@ export const EnhancedTableProps = {
root: PropTypes.string,
body: PropTypes.string,
}),
rootProps: PropTypes.shape({
'data-cy': PropTypes.string,
}),
isLoading: PropTypes.bool,
onlyGlobalSearch: PropTypes.bool,
onlyGlobalSelectedRows: PropTypes.bool,

View File

@ -44,7 +44,7 @@ const Row = ({ original, value, ...props }) => {
const timeAgo = `registered ${time.toRelative()}`
return (
<div {...props}>
<div {...props} data-cy={`template-${ID}`}>
<div className={classes.figure}>
<Image src={logoSource} imgProps={{ className: classes.image }} />
</div>

View File

@ -135,6 +135,7 @@ const Actions = () => {
},
{
accessor: VM_ACTIONS.CREATE_DIALOG,
dataCy: `vm_${VM_ACTIONS.CREATE_DIALOG}`,
tooltip: T.Create,
icon: AddSquare,
action: () => {

View File

@ -47,7 +47,7 @@ const Row = ({ original, value, ...props }) => {
VirtualMachineModel.getState(original)
return (
<div {...props}>
<div {...props} data-cy={`vm-${ID}`}>
<div>
<StatusCircle color={stateColor} tooltip={stateName} />
</div>

View File

@ -53,6 +53,7 @@ const Attribute = memo(
showActionsOnHover = false,
value,
valueInOptionList,
dataCy,
}) => {
const numberOfParents = useMemo(() => path.split('.').length - 1, [path])
@ -131,6 +132,7 @@ const Attribute = memo(
variant="body2"
flexGrow={1}
title={typeof value === 'string' ? value : undefined}
data-cy={dataCy}
>
{link ? (
<Link color="secondary" component={RouterLink} to={link}>
@ -183,6 +185,7 @@ export const AttributePropTypes = {
showActionsOnHover: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
valueInOptionList: PropTypes.string,
dataCy: PropTypes.string,
}
Attribute.propTypes = AttributePropTypes

View File

@ -53,6 +53,7 @@ const Ownership = memo(
canEdit: actions?.includes?.(ACTIONS.CHANGE_OWNER),
handleGetOptionList: getUserOptions,
handleEdit: (_, user) => handleEdit?.({ user }),
dataCy: 'owner',
},
{
name: T.Group,
@ -62,6 +63,7 @@ const Ownership = memo(
canEdit: actions?.includes?.(ACTIONS.CHANGE_GROUP),
handleGetOptionList: getGroupOptions,
handleEdit: (_, group) => handleEdit?.({ group }),
dataCy: 'group',
},
]

View File

@ -83,6 +83,7 @@ const Permissions = memo(({ handleEdit, actions, ...permissions }) => {
cy={`permission-${key}`}
disabled={permission === undefined}
icon={getIcon(permission)}
value={permission}
handleClick={() => handleChange(key, permission)}
/>
) : (

View File

@ -36,10 +36,12 @@ const InformationPanel = ({ actions, vm = {}, handleResizeCapacity }) => {
{
name: T.PhysicalCpu,
value: TEMPLATE?.CPU,
dataCy: 'cpu',
},
{
name: T.VirtualCpu,
value: TEMPLATE?.VCPU ?? '-',
dataCy: 'virtualcpu',
},
isVCenter && {
name: T.VirtualCores,
@ -49,18 +51,22 @@ const InformationPanel = ({ actions, vm = {}, handleResizeCapacity }) => {
${Tr(T.Sockets)} ${TEMPLATE?.TOPOLOGY?.SOCKETS || '-'}`}
</>
),
dataCy: 'virtualcores',
},
{
name: T.Memory,
value: prettyBytes(+TEMPLATE?.MEMORY, 'MB'),
dataCy: 'memory',
},
{
name: T.CostCpu,
value: TEMPLATE?.CPU_COST || 0,
dataCy: 'cpucost',
},
{
name: T.CostMByte,
value: TEMPLATE?.MEMORY_COST || 0,
dataCy: 'memorycost',
},
].filter(Boolean)
@ -85,12 +91,12 @@ const InformationPanel = ({ actions, vm = {}, handleResizeCapacity }) => {
/>
)}
</div>
{capacity.map(({ name, value }) => (
{capacity.map(({ name, value, dataCy }) => (
<div key={name} className={classes.item}>
<Typography className={classes.title} noWrap title={name}>
{name}
</Typography>
<Typography variant="body2" noWrap title={value}>
<Typography variant="body2" noWrap title={value} data-cy={dataCy}>
{value}
</Typography>
</div>

View File

@ -57,36 +57,48 @@ const InformationPanel = ({ vm = {}, handleRename, actions }) => {
}, [])
const info = [
{ name: T.ID, value: ID },
{
name: T.ID,
value: ID,
dataCy: 'id',
},
{
name: T.Name,
value: NAME,
canEdit: actions?.includes?.(VM_ACTIONS.RENAME),
handleEdit: handleRename,
dataCy: 'name',
},
{
name: T.State,
value: <StatusChip text={stateName} stateColor={stateColor} />,
value: (
<StatusChip dataCy={'state'} text={stateName} stateColor={stateColor} />
),
},
{
name: T.Reschedule,
value: Helper.booleanToString(+RESCHED),
dataCy: 'reschedule',
},
{
name: T.Locked,
value: Helper.levelLockToString(LOCK?.LOCKED),
dataCy: 'locked',
},
{
name: T.IP,
value: ips?.length ? <MultipleTags tags={ips} /> : '--',
dataCy: 'ips',
},
{
name: T.StartTime,
value: Helper.timeToString(STIME),
dataCy: 'starttime',
},
{
name: T.EndTime,
value: Helper.timeToString(ETIME),
dataCy: 'endtime',
},
hostId && {
name: T.Host,
@ -94,6 +106,7 @@ const InformationPanel = ({ vm = {}, handleRename, actions }) => {
link:
!Number.isNaN(+hostId) &&
generatePath(PATH.INFRASTRUCTURE.HOSTS.DETAIL, { id: hostId }),
dataCy: 'hostid',
},
clusterId && {
name: T.Cluster,
@ -101,10 +114,12 @@ const InformationPanel = ({ vm = {}, handleRename, actions }) => {
link:
!Number.isNaN(+clusterId) &&
generatePath(PATH.INFRASTRUCTURE.CLUSTERS.DETAIL, { id: clusterId }),
dataCy: 'clusterid',
},
{
name: T.DeployID,
value: DEPLOY_ID,
dataCy: 'deployid',
},
].filter(Boolean)

View File

@ -37,6 +37,10 @@ import MultipleTags from 'client/components/MultipleTags'
import { Translate } from 'client/components/HOC'
import { T, VM_ACTIONS } from 'client/constants'
const DATACYSECURITY = 'securitygroup-'
const DATACYNETWORK = 'network-'
const DATACYALIAS = 'alias-'
const Accordion = styled((props) => (
<MAccordion disableGutters elevation={0} square {...props} />
))(({ theme }) => ({
@ -126,17 +130,34 @@ const NetworkItem = ({ nic = {}, actions }) => {
/>
)
const tags = [
{
text: IP,
dataCy: `${DATACYNETWORK}ip`,
},
{
text: MAC,
dataCy: `${DATACYNETWORK}mac`,
},
{
text: ADDRESS,
dataCy: `${DATACYNETWORK}address`,
},
].filter(({ text } = {}) => Boolean(text))
return (
<>
<Accordion>
<Accordion data-cy={`${DATACYNETWORK}${NIC_ID}`}>
<Summary {...(hasDetails && { expandIcon: <ExpandIcon /> })}>
<Row>
<Typography noWrap>{`${NIC_ID} | ${NETWORK}`}</Typography>
<Typography noWrap data-cy={`${DATACYNETWORK}name`}>
{`${NIC_ID} | ${NETWORK}`}
</Typography>
<Labels>
<MultipleTags
clipboard
limitTags={isMobile ? 1 : 3}
tags={[IP, MAC, ADDRESS].filter(Boolean)}
tags={tags}
/>
</Labels>
{!isMobile && !isPciDevice && detachAction(NIC_ID)}
@ -144,24 +165,43 @@ const NetworkItem = ({ nic = {}, actions }) => {
</Summary>
{hasDetails && (
<Details>
{ALIAS?.map(({ NIC_ID, NETWORK = '-', BRIDGE, IP, MAC }) => (
<Row key={NIC_ID}>
<Typography noWrap variant="body2">
<Translate word={T.Alias} />
{`${NIC_ID} | ${NETWORK}`}
</Typography>
<Labels>
<MultipleTags
clipboard
limitTags={isMobile ? 1 : 3}
tags={[IP, MAC, BRIDGE && `BRIDGE - ${BRIDGE}`].filter(
Boolean
)}
/>
</Labels>
{!isMobile && !isPciDevice && detachAction(NIC_ID, true)}
</Row>
))}
{ALIAS?.map(({ NIC_ID, NETWORK = '-', BRIDGE, IP, MAC }) => {
const tags = [
{
text: IP,
dataCy: `${DATACYALIAS}ip`,
},
{
text: MAC,
dataCy: `${DATACYALIAS}mac`,
},
{
text: BRIDGE && `BRIDGE - ${BRIDGE}`,
dataCy: `${DATACYALIAS}bridge`,
},
].filter(({ text } = {}) => Boolean(text))
return (
<Row key={NIC_ID} data-cy={`${DATACYALIAS}${NIC_ID}`}>
<Typography
noWrap
variant="body2"
data-cy={`${DATACYALIAS}name`}
>
<Translate word={T.Alias} />
{`${NIC_ID} | ${NETWORK}`}
</Typography>
<Labels>
<MultipleTags
clipboard
limitTags={isMobile ? 1 : 3}
tags={tags}
/>
</Labels>
{!isMobile && !isPciDevice && detachAction(NIC_ID, true)}
</Row>
)
})}
{!!SECURITY_GROUPS?.length && (
<SecGroups variant="outlined">
<Typography variant="body1">
@ -172,6 +212,7 @@ const NetworkItem = ({ nic = {}, actions }) => {
(
{
ID,
SECURITY_GROUP_ID,
NAME,
PROTOCOL,
RULE_TYPE,
@ -180,25 +221,51 @@ const NetworkItem = ({ nic = {}, actions }) => {
NETWORK_ID,
},
idx
) => (
<Row key={`${idx}-${NAME}`}>
<Typography noWrap variant="body2">
{`${ID} | ${NAME}`}
</Typography>
<Labels>
<MultipleTags
limitTags={isMobile ? 2 : 5}
tags={[
PROTOCOL,
RULE_TYPE,
RANGE,
NETWORK_ID,
ICMP_TYPE,
].filter(Boolean)}
/>
</Labels>
</Row>
)
) => {
const tags = [
{
text: PROTOCOL,
dataCy: `${DATACYSECURITY}protocol`,
},
{
text: RULE_TYPE,
dataCy: `${DATACYSECURITY}ruletype`,
},
{
text: RANGE,
dataCy: `${DATACYSECURITY}range`,
},
{
text: NETWORK_ID,
dataCy: `${DATACYSECURITY}networkid`,
},
{
text: ICMP_TYPE,
dataCy: `${DATACYSECURITY}icmp_type`,
},
].filter(({ text } = {}) => Boolean(text))
return (
<Row
key={`${idx}-${NAME}`}
data-cy={`${DATACYSECURITY}${idx}`}
>
<Typography
noWrap
variant="body2"
data-cy={`${DATACYSECURITY}name`}
>
{`${ID} | ${NAME}`}
</Typography>
<Labels>
<MultipleTags
limitTags={isMobile ? 2 : 5}
tags={tags}
/>
</Labels>
</Row>
)
}
)}
</SecGroups>
)}

View File

@ -63,23 +63,42 @@ const StorageItem = ({ disk, actions = [] }) => {
}[type]
const labels = [
...new Set([
TYPE,
Helper.stringToBoolean(PERSISTENT) && 'PERSISTENT',
Helper.stringToBoolean(READONLY) && 'READONLY',
Helper.stringToBoolean(SAVE) && 'SAVE',
Helper.stringToBoolean(CLONE) && 'CLONE',
]),
].filter(Boolean)
{
label: TYPE,
dataCy: 'type',
},
{
label: Helper.stringToBoolean(PERSISTENT) && 'PERSISTENT',
dataCy: 'persistent',
},
{
label: Helper.stringToBoolean(READONLY) && 'READONLY',
dataCy: 'readonly',
},
{
label: Helper.stringToBoolean(SAVE) && 'SAVE',
dataCy: 'save',
},
{
label: Helper.stringToBoolean(CLONE) && 'CLONE',
dataCy: 'clone',
},
].filter(({ label } = {}) => Boolean(label))
return (
<Paper variant="outlined" className={classes.root}>
<Paper
variant="outlined"
className={classes.root}
data-cy={`disk-${DISK_ID}`}
>
<div className={classes.main}>
<div className={classes.title}>
<Typography component="span">{image}</Typography>
<Typography component="span" data-cy={'name'}>
{image}
</Typography>
<span className={classes.labels}>
{labels.map((label) => (
<StatusChip key={label} text={label} />
{labels.map(({ label, dataCy }) => (
<StatusChip key={label} text={label} dataCy={dataCy} />
))}
</span>
</div>
@ -88,18 +107,18 @@ const StorageItem = ({ disk, actions = [] }) => {
{TARGET && (
<span title={`Target: ${TARGET}`}>
<DatabaseSettings />
<span>{` ${TARGET}`}</span>
<span data-cy={'target'}>{` ${TARGET}`}</span>
</span>
)}
{DATASTORE && (
<span title={`Datastore Name: ${DATASTORE}`}>
<Folder />
<span>{` ${DATASTORE}`}</span>
<span data-cy={'datastore'}>{` ${DATASTORE}`}</span>
</span>
)}
<span title={`Monitor Size / Disk Size: ${monitorSize}/${size}`}>
<ModernTv />
<span>{` ${monitorSize}/${size}`}</span>
<span data-cy={'monitorsize'}>{` ${monitorSize}/${size}`}</span>
</span>
</div>
</div>

View File

@ -81,6 +81,7 @@ const Tabs = ({
icon={error ? <WarningIcon /> : Icon && <Icon />}
value={value ?? idx}
label={label ?? name}
data-cy={`tab-${name}`}
/>
)
})}

View File

@ -33,10 +33,11 @@ function VirtualMachines() {
<VmsTable
onSelectedRowsChange={onSelectedRowsChange}
globalActions={actions}
rootProps={{ 'data-cy': 'vms' }}
/>
{selectedRows?.length > 0 && (
<Stack overflow="auto">
<Stack overflow="auto" data-cy={'detail'}>
{selectedRows?.length === 1 ? (
<VmTabs id={selectedRows[0]?.values.ID} />
) : (