Some code cleanup, add jest coverage and begin using it for utility functions

This commit is contained in:
DaneEveritt 2022-06-26 14:34:09 -04:00
parent ca39830333
commit 1eb3ea2ee4
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
29 changed files with 2044 additions and 134 deletions

View file

@ -4,7 +4,7 @@ import { faEthernet, faHdd, faMemory, faMicrochip, faServer } from '@fortawesome
import { Link } from 'react-router-dom';
import { Server } from '@/api/server/getServer';
import getServerResourceUsage, { ServerPowerState, ServerStats } from '@/api/server/getServerResourceUsage';
import { bytesToHuman, formatIp, megabytesToHuman } from '@/helpers';
import { bytesToString, ip, mbToBytes } from '@/lib/formatters';
import tw from 'twin.macro';
import GreyRowBox from '@/components/elements/GreyRowBox';
import Spinner from '@/components/elements/Spinner';
@ -74,8 +74,8 @@ export default ({ server, className }: { server: Server; className?: string }) =
alarms.disk = server.limits.disk === 0 ? false : isAlarmState(stats.diskUsageInBytes, server.limits.disk);
}
const diskLimit = server.limits.disk !== 0 ? megabytesToHuman(server.limits.disk) : 'Unlimited';
const memoryLimit = server.limits.memory !== 0 ? megabytesToHuman(server.limits.memory) : 'Unlimited';
const diskLimit = server.limits.disk !== 0 ? bytesToString(mbToBytes(server.limits.disk)) : 'Unlimited';
const memoryLimit = server.limits.memory !== 0 ? bytesToString(mbToBytes(server.limits.memory)) : 'Unlimited';
const cpuLimit = server.limits.cpu !== 0 ? server.limits.cpu + ' %' : 'Unlimited';
return (
@ -98,7 +98,7 @@ export default ({ server, className }: { server: Server; className?: string }) =
{
server.allocations.filter(alloc => alloc.isDefault).map(allocation => (
<React.Fragment key={allocation.ip + allocation.port.toString()}>
{allocation.alias || formatIp(allocation.ip)}:{allocation.port}
{allocation.alias || ip(allocation.ip)}:{allocation.port}
</React.Fragment>
))
}
@ -146,7 +146,7 @@ export default ({ server, className }: { server: Server; className?: string }) =
<div css={tw`flex justify-center`}>
<Icon icon={faMemory} $alarm={alarms.memory}/>
<IconDescription $alarm={alarms.memory}>
{bytesToHuman(stats.memoryUsageInBytes)}
{bytesToString(stats.memoryUsageInBytes)}
</IconDescription>
</div>
<p css={tw`text-xs text-neutral-600 text-center mt-1`}>of {memoryLimit}</p>
@ -155,7 +155,7 @@ export default ({ server, className }: { server: Server; className?: string }) =
<div css={tw`flex justify-center`}>
<Icon icon={faHdd} $alarm={alarms.disk}/>
<IconDescription $alarm={alarms.disk}>
{bytesToHuman(stats.diskUsageInBytes)}
{bytesToString(stats.diskUsageInBytes)}
</IconDescription>
</div>
<p css={tw`text-xs text-neutral-600 text-center mt-1`}>of {diskLimit}</p>

View file

@ -13,7 +13,8 @@ import { Link } from 'react-router-dom';
import styled from 'styled-components/macro';
import tw from 'twin.macro';
import Input from '@/components/elements/Input';
import { formatIp } from '@/helpers';
import { ip } from '@/lib/formatters';
type Props = RequiredModalProps;
interface Values {
@ -109,7 +110,7 @@ export default ({ ...props }: Props) => {
<p css={tw`mt-1 text-xs text-neutral-400`}>
{
server.allocations.filter(alloc => alloc.isDefault).map(allocation => (
<span key={allocation.ip + allocation.port.toString()}>{allocation.alias || formatIp(allocation.ip)}:{allocation.port}</span>
<span key={allocation.ip + allocation.port.toString()}>{allocation.alias || ip(allocation.ip)}:{allocation.port}</span>
))
}
</p>

View file

@ -1,7 +1,7 @@
import React from 'react';
import { FormikErrors, FormikTouched } from 'formik';
import tw from 'twin.macro';
import { capitalize } from '@/helpers';
import { capitalize } from '@/lib/strings';
interface Props {
errors: FormikErrors<any>;

View file

@ -8,7 +8,7 @@ import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMet
import { TerminalIcon } from '@heroicons/react/solid';
import classNames from 'classnames';
import style from './style.module.css';
import { isObject } from '@/helpers';
import { isObject } from '@/lib/objects';
import Avatar from '@/components/Avatar';
import useLocationHash from '@/plugins/useLocationHash';

View file

@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArchive, faEllipsisH, faLock } from '@fortawesome/free-solid-svg-icons';
import { format, formatDistanceToNow } from 'date-fns';
import Spinner from '@/components/elements/Spinner';
import { bytesToHuman } from '@/helpers';
import { bytesToString } from '@/lib/formatters';
import Can from '@/components/elements/Can';
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
import BackupContextMenu from '@/components/server/backups/BackupContextMenu';
@ -64,7 +64,7 @@ export default ({ backup, className }: Props) => {
{backup.name}
</p>
{(backup.completedAt !== null && backup.isSuccessful) &&
<span css={tw`ml-3 text-neutral-300 text-xs font-extralight hidden sm:inline`}>{bytesToHuman(backup.bytes)}</span>
<span css={tw`ml-3 text-neutral-300 text-xs font-extralight hidden sm:inline`}>{bytesToString(backup.bytes)}</span>
}
</div>
<p css={tw`mt-1 md:mt-0 text-xs text-neutral-400 font-mono truncate`}>

View file

@ -8,7 +8,7 @@ import {
faMicrochip,
faWifi,
} from '@fortawesome/free-solid-svg-icons';
import { bytesToHuman, formatIp, megabytesToHuman } from '@/helpers';
import { bytesToString, ip, mbToBytes } from '@/lib/formatters';
import { ServerContext } from '@/state/server';
import { SocketEvent, SocketRequest } from '@/components/server/events';
import UptimeDuration from '@/components/server/UptimeDuration';
@ -41,7 +41,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
const allocation = ServerContext.useStoreState(state => {
const match = state.server.data!.allocations.find(allocation => allocation.isDefault);
return !match ? 'n/a' : `${match.alias || formatIp(match.ip)}:${match.port}`;
return !match ? 'n/a' : `${match.alias || ip(match.ip)}:${match.port}`;
});
useEffect(() => {
@ -106,14 +106,14 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
title={'Memory'}
color={getBackgroundColor(stats.memory / 1024, limits.memory * 1024)}
description={limits.memory
? `This server is allowed to use up to ${megabytesToHuman(limits.memory)} of memory.`
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.memory))} of memory.`
: 'No memory limit has been configured for this server.'
}
>
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
bytesToHuman(stats.memory)
bytesToString(stats.memory)
}
</StatBlock>
<StatBlock
@ -121,11 +121,11 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
title={'Disk'}
color={getBackgroundColor(stats.disk / 1024, limits.disk * 1024)}
description={limits.disk
? `This server is allowed to use up to ${megabytesToHuman(limits.disk)} of disk space.`
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.disk))} of disk space.`
: 'No disk space limit has been configured for this server.'
}
>
{bytesToHuman(stats.disk)}
{bytesToString(stats.disk)}
</StatBlock>
<StatBlock
icon={faCloudDownloadAlt}
@ -135,7 +135,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
bytesToHuman(stats.tx)
bytesToString(stats.tx)
}
</StatBlock>
<StatBlock
@ -146,7 +146,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
bytesToHuman(stats.rx)
bytesToString(stats.rx)
}
</StatBlock>
</div>

View file

@ -4,7 +4,8 @@ import { SocketEvent } from '@/components/server/events';
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
import { Line } from 'react-chartjs-2';
import { useChart, useChartTickLabel } from '@/components/server/console/chart';
import { bytesToHuman, toRGBA } from '@/helpers';
import { hexToRgba } from '@/lib/helpers';
import { bytesToString } from '@/lib/formatters';
import { CloudDownloadIcon, CloudUploadIcon } from '@heroicons/react/solid';
import { theme } from 'twin.macro';
import ChartBlock from '@/components/server/console/ChartBlock';
@ -24,7 +25,7 @@ export default () => {
y: {
ticks: {
callback (value) {
return bytesToHuman(typeof value === 'string' ? parseInt(value, 10) : value);
return bytesToString(typeof value === 'string' ? parseInt(value, 10) : value);
},
},
},
@ -35,7 +36,7 @@ export default () => {
...opts,
label: !index ? 'Network In' : 'Network Out',
borderColor: !index ? theme('colors.cyan.400') : theme('colors.yellow.400'),
backgroundColor: toRGBA(!index ? theme('colors.cyan.700') : theme('colors.yellow.700'), 0.5),
backgroundColor: hexToRgba(!index ? theme('colors.cyan.700') : theme('colors.yellow.700'), 0.5),
};
},
});

View file

@ -12,7 +12,7 @@ import { DeepPartial } from 'ts-essentials';
import { useState } from 'react';
import { deepmerge, deepmergeCustom } from 'deepmerge-ts';
import { theme } from 'twin.macro';
import { toRGBA } from '@/helpers';
import { hexToRgba } from '@/lib/helpers';
ChartJS.register(LineElement, PointElement, Filler, LinearScale);
@ -86,7 +86,7 @@ function getEmptyData (label: string, sets = 1, callback?: ChartDatasetCallback
label,
data: Array(20).fill(0),
borderColor: theme('colors.cyan.400'),
backgroundColor: toRGBA(theme('colors.cyan.700'), 0.5),
backgroundColor: hexToRgba(theme('colors.cyan.700'), 0.5),
}, index)),
};
}

View file

@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import features from './index';
import { getObjectKeys } from '@/helpers';
import { getObjectKeys } from '@/lib/objects';
type ListItems = [ string, React.ComponentType ][];

View file

@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFileAlt, faFileArchive, faFileImport, faFolder } from '@fortawesome/free-solid-svg-icons';
import { bytesToHuman, encodePathSegments } from '@/helpers';
import { encodePathSegments } from '@/helpers';
import { differenceInHours, format, formatDistanceToNow } from 'date-fns';
import React, { memo } from 'react';
import { FileObject } from '@/api/server/files/loadDirectory';
@ -13,6 +13,7 @@ import styled from 'styled-components/macro';
import SelectFileCheckbox from '@/components/server/files/SelectFileCheckbox';
import { usePermissions } from '@/plugins/usePermissions';
import { join } from 'path';
import { bytesToString } from '@/lib/formatters';
const Row = styled.div`
${tw`flex bg-neutral-700 rounded-sm mb-px text-sm hover:text-neutral-100 cursor-pointer items-center no-underline hover:bg-neutral-600`};
@ -61,7 +62,7 @@ const FileObjectRow = ({ file }: { file: FileObject }) => (
</div>
{file.isFile &&
<div css={tw`w-1/6 text-right mr-4 hidden sm:block`}>
{bytesToHuman(file.size)}
{bytesToString(file.size)}
</div>
}
<div

View file

@ -18,7 +18,7 @@ import CopyOnClick from '@/components/elements/CopyOnClick';
import DeleteAllocationButton from '@/components/server/network/DeleteAllocationButton';
import setPrimaryServerAllocation from '@/api/server/network/setPrimaryServerAllocation';
import getServerAllocations from '@/api/swr/getServerAllocations';
import { formatIp } from '@/helpers';
import { ip } from '@/lib/formatters';
import Code from '@/components/elements/Code';
const Label = styled.label`${tw`uppercase text-xs mt-1 text-neutral-400 block px-1 select-none transition-colors duration-150`}`;
@ -67,7 +67,7 @@ const AllocationRow = ({ allocation }: Props) => {
<div className={'mr-4 flex-1 md:w-40'}>
{allocation.alias ?
<CopyOnClick text={allocation.alias}><Code dark className={'w-40 truncate'}>{allocation.alias}</Code></CopyOnClick> :
<CopyOnClick text={formatIp(allocation.ip)}><Code dark>{formatIp(allocation.ip)}</Code></CopyOnClick>}
<CopyOnClick text={ip(allocation.ip)}><Code dark>{ip(allocation.ip)}</Code></CopyOnClick>}
<Label>{allocation.alias ? 'Hostname' : 'IP Address'}</Label>
</div>
<div className={'w-16 md:w-24 overflow-hidden'}>

View file

@ -12,7 +12,7 @@ import Label from '@/components/elements/Label';
import ServerContentBlock from '@/components/elements/ServerContentBlock';
import isEqual from 'react-fast-compare';
import CopyOnClick from '@/components/elements/CopyOnClick';
import { formatIp } from '@/helpers';
import { ip } from '@/lib/formatters';
import { Button } from '@/components/elements/button/index';
export default () => {
@ -31,10 +31,10 @@ export default () => {
<TitledGreyBox title={'SFTP Details'} css={tw`mb-6 md:mb-10`}>
<div>
<Label>Server Address</Label>
<CopyOnClick text={`sftp://${formatIp(sftp.ip)}:${sftp.port}`}>
<CopyOnClick text={`sftp://${ip(sftp.ip)}:${sftp.port}`}>
<Input
type={'text'}
value={`sftp://${formatIp(sftp.ip)}:${sftp.port}`}
value={`sftp://${ip(sftp.ip)}:${sftp.port}`}
readOnly
/>
</CopyOnClick>
@ -58,7 +58,7 @@ export default () => {
</div>
</div>
<div css={tw`ml-4`}>
<a href={`sftp://${username}.${id}@${formatIp(sftp.ip)}:${sftp.port}`}>
<a href={`sftp://${username}.${id}@${ip(sftp.ip)}:${sftp.port}`}>
<Button.Text variant={Button.Variants.Secondary}>Launch SFTP</Button.Text>
</a>
</div>