Skip to content

Commit 1eb3ea2

Browse files
committed
Some code cleanup, add jest coverage and begin using it for utility functions
1 parent ca39830 commit 1eb3ea2

29 files changed

+2044
-134
lines changed

Diff for: .eslintrc.yml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extends:
2525
- "standard"
2626
- "plugin:react/recommended"
2727
- "plugin:@typescript-eslint/recommended"
28+
- "plugin:jest-dom/recommended"
2829
rules:
2930
quotes:
3031
- warn

Diff for: babel.config.js

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
module.exports = {
2-
presets: [
3-
'@babel/typescript',
4-
['@babel/env', {
5-
modules: false,
6-
useBuiltIns: 'entry',
7-
corejs: 3,
8-
}],
9-
'@babel/react',
10-
],
11-
plugins: [
1+
module.exports = function (api) {
2+
let targets = {};
3+
const plugins = [
124
'babel-plugin-macros',
135
'styled-components',
146
'react-hot-loader/babel',
@@ -19,5 +11,24 @@ module.exports = {
1911
'@babel/proposal-optional-chaining',
2012
'@babel/proposal-nullish-coalescing-operator',
2113
'@babel/syntax-dynamic-import',
22-
],
14+
];
15+
16+
if (api.env('test')) {
17+
targets = { node: 'current' };
18+
plugins.push('@babel/transform-modules-commonjs');
19+
}
20+
21+
return {
22+
plugins,
23+
presets: [
24+
'@babel/typescript',
25+
['@babel/env', {
26+
modules: false,
27+
useBuiltIns: 'entry',
28+
corejs: 3,
29+
targets,
30+
}],
31+
'@babel/react',
32+
]
33+
};
2334
};

Diff for: jest.config.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const { pathsToModuleNameMapper } = require('ts-jest');
2+
const { compilerOptions } = require('./tsconfig');
3+
4+
/** @type {import('ts-jest').InitialOptionsTsJest} */
5+
module.exports = {
6+
preset: 'ts-jest',
7+
globals: {
8+
'ts-jest': {
9+
isolatedModules: true,
10+
},
11+
},
12+
moduleFileExtensions: ['js', 'ts', 'tsx', 'd.ts', 'json', 'node'],
13+
moduleNameMapper: {
14+
'\\.(jpe?g|png|gif|svg)$': '<rootDir>/resources/scripts/__mocks__/file.ts',
15+
'\\.(s?css|less)$': 'identity-obj-proxy',
16+
...pathsToModuleNameMapper(compilerOptions.paths, {
17+
prefix: '<rootDir>/',
18+
}),
19+
},
20+
setupFilesAfterEnv: [
21+
'<rootDir>/resources/scripts/setup-tests.ts',
22+
],
23+
transform: {
24+
'.*\\.[t|j]sx$': 'babel-jest',
25+
'.*\\.ts$': 'ts-jest',
26+
},
27+
testPathIgnorePatterns: ['/node_modules/'],
28+
};

Diff for: package.json

+12
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,22 @@
6060
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
6161
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
6262
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
63+
"@babel/plugin-transform-modules-commonjs": "^7.18.2",
6364
"@babel/plugin-transform-react-jsx": "^7.12.1",
6465
"@babel/plugin-transform-runtime": "^7.12.1",
6566
"@babel/preset-env": "^7.12.1",
6667
"@babel/preset-react": "^7.12.1",
6768
"@babel/preset-typescript": "^7.12.1",
6869
"@babel/runtime": "^7.12.1",
70+
"@testing-library/dom": "^8.14.0",
71+
"@testing-library/jest-dom": "^5.16.4",
72+
"@testing-library/react": "12.1.5",
73+
"@testing-library/user-event": "^14.2.1",
6974
"@types/chart.js": "^2.8.5",
7075
"@types/codemirror": "^0.0.98",
7176
"@types/debounce": "^1.2.0",
7277
"@types/events": "^3.0.0",
78+
"@types/jest": "^28.1.3",
7379
"@types/node": "^14.11.10",
7480
"@types/qrcode.react": "^1.0.1",
7581
"@types/query-string": "^6.3.0",
@@ -88,18 +94,22 @@
8894
"@typescript-eslint/eslint-plugin": "^4.25.0",
8995
"@typescript-eslint/parser": "^4.25.0",
9096
"autoprefixer": "^10.4.7",
97+
"babel-jest": "^28.1.1",
9198
"babel-loader": "^8.2.5",
9299
"babel-plugin-styled-components": "^2.0.7",
93100
"cross-env": "^7.0.2",
94101
"css-loader": "^5.2.7",
95102
"eslint": "^7.27.0",
96103
"eslint-config-standard": "^16.0.3",
97104
"eslint-plugin-import": "^2.23.3",
105+
"eslint-plugin-jest-dom": "^4.0.2",
98106
"eslint-plugin-node": "^11.1.0",
99107
"eslint-plugin-promise": "^5.1.0",
100108
"eslint-plugin-react": "^7.23.2",
101109
"eslint-plugin-react-hooks": "^4.2.0",
102110
"fork-ts-checker-webpack-plugin": "^6.2.10",
111+
"identity-obj-proxy": "^3.0.0",
112+
"jest": "^28.1.1",
103113
"postcss": "^8.4.14",
104114
"postcss-import": "^14.1.0",
105115
"postcss-loader": "^4.0.0",
@@ -111,6 +121,7 @@
111121
"svg-url-loader": "^7.1.1",
112122
"terser-webpack-plugin": "^4.2.3",
113123
"ts-essentials": "^9.1.2",
124+
"ts-jest": "^28.0.5",
114125
"twin.macro": "^2.8.2",
115126
"typescript": "^4.7.3",
116127
"webpack": "^4.43.0",
@@ -122,6 +133,7 @@
122133
},
123134
"scripts": {
124135
"clean": "cd public/assets && find . \\( -name \"*.js\" -o -name \"*.map\" \\) -type f -delete",
136+
"test": "jest",
125137
"lint": "eslint ./resources/scripts/**/*.{ts,tsx} --ext .ts,.tsx",
126138
"watch": "cross-env NODE_ENV=development ./node_modules/.bin/webpack --watch --progress",
127139
"build": "cross-env NODE_ENV=development ./node_modules/.bin/webpack --progress",

Diff for: resources/scripts/__mocks__/file.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = 'test-file-stub';

Diff for: resources/scripts/components/dashboard/ServerRow.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { faEthernet, faHdd, faMemory, faMicrochip, faServer } from '@fortawesome
44
import { Link } from 'react-router-dom';
55
import { Server } from '@/api/server/getServer';
66
import getServerResourceUsage, { ServerPowerState, ServerStats } from '@/api/server/getServerResourceUsage';
7-
import { bytesToHuman, formatIp, megabytesToHuman } from '@/helpers';
7+
import { bytesToString, ip, mbToBytes } from '@/lib/formatters';
88
import tw from 'twin.macro';
99
import GreyRowBox from '@/components/elements/GreyRowBox';
1010
import Spinner from '@/components/elements/Spinner';
@@ -74,8 +74,8 @@ export default ({ server, className }: { server: Server; className?: string }) =
7474
alarms.disk = server.limits.disk === 0 ? false : isAlarmState(stats.diskUsageInBytes, server.limits.disk);
7575
}
7676

77-
const diskLimit = server.limits.disk !== 0 ? megabytesToHuman(server.limits.disk) : 'Unlimited';
78-
const memoryLimit = server.limits.memory !== 0 ? megabytesToHuman(server.limits.memory) : 'Unlimited';
77+
const diskLimit = server.limits.disk !== 0 ? bytesToString(mbToBytes(server.limits.disk)) : 'Unlimited';
78+
const memoryLimit = server.limits.memory !== 0 ? bytesToString(mbToBytes(server.limits.memory)) : 'Unlimited';
7979
const cpuLimit = server.limits.cpu !== 0 ? server.limits.cpu + ' %' : 'Unlimited';
8080

8181
return (
@@ -98,7 +98,7 @@ export default ({ server, className }: { server: Server; className?: string }) =
9898
{
9999
server.allocations.filter(alloc => alloc.isDefault).map(allocation => (
100100
<React.Fragment key={allocation.ip + allocation.port.toString()}>
101-
{allocation.alias || formatIp(allocation.ip)}:{allocation.port}
101+
{allocation.alias || ip(allocation.ip)}:{allocation.port}
102102
</React.Fragment>
103103
))
104104
}
@@ -146,7 +146,7 @@ export default ({ server, className }: { server: Server; className?: string }) =
146146
<div css={tw`flex justify-center`}>
147147
<Icon icon={faMemory} $alarm={alarms.memory}/>
148148
<IconDescription $alarm={alarms.memory}>
149-
{bytesToHuman(stats.memoryUsageInBytes)}
149+
{bytesToString(stats.memoryUsageInBytes)}
150150
</IconDescription>
151151
</div>
152152
<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 }) =
155155
<div css={tw`flex justify-center`}>
156156
<Icon icon={faHdd} $alarm={alarms.disk}/>
157157
<IconDescription $alarm={alarms.disk}>
158-
{bytesToHuman(stats.diskUsageInBytes)}
158+
{bytesToString(stats.diskUsageInBytes)}
159159
</IconDescription>
160160
</div>
161161
<p css={tw`text-xs text-neutral-600 text-center mt-1`}>of {diskLimit}</p>

Diff for: resources/scripts/components/dashboard/search/SearchModal.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { Link } from 'react-router-dom';
1313
import styled from 'styled-components/macro';
1414
import tw from 'twin.macro';
1515
import Input from '@/components/elements/Input';
16-
import { formatIp } from '@/helpers';
16+
import { ip } from '@/lib/formatters';
17+
1718
type Props = RequiredModalProps;
1819

1920
interface Values {
@@ -109,7 +110,7 @@ export default ({ ...props }: Props) => {
109110
<p css={tw`mt-1 text-xs text-neutral-400`}>
110111
{
111112
server.allocations.filter(alloc => alloc.isDefault).map(allocation => (
112-
<span key={allocation.ip + allocation.port.toString()}>{allocation.alias || formatIp(allocation.ip)}:{allocation.port}</span>
113+
<span key={allocation.ip + allocation.port.toString()}>{allocation.alias || ip(allocation.ip)}:{allocation.port}</span>
113114
))
114115
}
115116
</p>

Diff for: resources/scripts/components/elements/InputError.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { FormikErrors, FormikTouched } from 'formik';
33
import tw from 'twin.macro';
4-
import { capitalize } from '@/helpers';
4+
import { capitalize } from '@/lib/strings';
55

66
interface Props {
77
errors: FormikErrors<any>;

Diff for: resources/scripts/components/elements/activity/ActivityLogEntry.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMet
88
import { TerminalIcon } from '@heroicons/react/solid';
99
import classNames from 'classnames';
1010
import style from './style.module.css';
11-
import { isObject } from '@/helpers';
11+
import { isObject } from '@/lib/objects';
1212
import Avatar from '@/components/Avatar';
1313
import useLocationHash from '@/plugins/useLocationHash';
1414

Diff for: resources/scripts/components/server/backups/BackupRow.tsx

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

Diff for: resources/scripts/components/server/console/ServerDetailsBlock.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
faMicrochip,
99
faWifi,
1010
} from '@fortawesome/free-solid-svg-icons';
11-
import { bytesToHuman, formatIp, megabytesToHuman } from '@/helpers';
11+
import { bytesToString, ip, mbToBytes } from '@/lib/formatters';
1212
import { ServerContext } from '@/state/server';
1313
import { SocketEvent, SocketRequest } from '@/components/server/events';
1414
import UptimeDuration from '@/components/server/UptimeDuration';
@@ -41,7 +41,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
4141
const allocation = ServerContext.useStoreState(state => {
4242
const match = state.server.data!.allocations.find(allocation => allocation.isDefault);
4343

44-
return !match ? 'n/a' : `${match.alias || formatIp(match.ip)}:${match.port}`;
44+
return !match ? 'n/a' : `${match.alias || ip(match.ip)}:${match.port}`;
4545
});
4646

4747
useEffect(() => {
@@ -106,26 +106,26 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
106106
title={'Memory'}
107107
color={getBackgroundColor(stats.memory / 1024, limits.memory * 1024)}
108108
description={limits.memory
109-
? `This server is allowed to use up to ${megabytesToHuman(limits.memory)} of memory.`
109+
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.memory))} of memory.`
110110
: 'No memory limit has been configured for this server.'
111111
}
112112
>
113113
{status === 'offline' ?
114114
<span className={'text-gray-400'}>Offline</span>
115115
:
116-
bytesToHuman(stats.memory)
116+
bytesToString(stats.memory)
117117
}
118118
</StatBlock>
119119
<StatBlock
120120
icon={faHdd}
121121
title={'Disk'}
122122
color={getBackgroundColor(stats.disk / 1024, limits.disk * 1024)}
123123
description={limits.disk
124-
? `This server is allowed to use up to ${megabytesToHuman(limits.disk)} of disk space.`
124+
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.disk))} of disk space.`
125125
: 'No disk space limit has been configured for this server.'
126126
}
127127
>
128-
{bytesToHuman(stats.disk)}
128+
{bytesToString(stats.disk)}
129129
</StatBlock>
130130
<StatBlock
131131
icon={faCloudDownloadAlt}
@@ -135,7 +135,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
135135
{status === 'offline' ?
136136
<span className={'text-gray-400'}>Offline</span>
137137
:
138-
bytesToHuman(stats.tx)
138+
bytesToString(stats.tx)
139139
}
140140
</StatBlock>
141141
<StatBlock
@@ -146,7 +146,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
146146
{status === 'offline' ?
147147
<span className={'text-gray-400'}>Offline</span>
148148
:
149-
bytesToHuman(stats.rx)
149+
bytesToString(stats.rx)
150150
}
151151
</StatBlock>
152152
</div>

Diff for: resources/scripts/components/server/console/StatGraphs.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { SocketEvent } from '@/components/server/events';
44
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
55
import { Line } from 'react-chartjs-2';
66
import { useChart, useChartTickLabel } from '@/components/server/console/chart';
7-
import { bytesToHuman, toRGBA } from '@/helpers';
7+
import { hexToRgba } from '@/lib/helpers';
8+
import { bytesToString } from '@/lib/formatters';
89
import { CloudDownloadIcon, CloudUploadIcon } from '@heroicons/react/solid';
910
import { theme } from 'twin.macro';
1011
import ChartBlock from '@/components/server/console/ChartBlock';
@@ -24,7 +25,7 @@ export default () => {
2425
y: {
2526
ticks: {
2627
callback (value) {
27-
return bytesToHuman(typeof value === 'string' ? parseInt(value, 10) : value);
28+
return bytesToString(typeof value === 'string' ? parseInt(value, 10) : value);
2829
},
2930
},
3031
},
@@ -35,7 +36,7 @@ export default () => {
3536
...opts,
3637
label: !index ? 'Network In' : 'Network Out',
3738
borderColor: !index ? theme('colors.cyan.400') : theme('colors.yellow.400'),
38-
backgroundColor: toRGBA(!index ? theme('colors.cyan.700') : theme('colors.yellow.700'), 0.5),
39+
backgroundColor: hexToRgba(!index ? theme('colors.cyan.700') : theme('colors.yellow.700'), 0.5),
3940
};
4041
},
4142
});

Diff for: resources/scripts/components/server/console/chart.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { DeepPartial } from 'ts-essentials';
1212
import { useState } from 'react';
1313
import { deepmerge, deepmergeCustom } from 'deepmerge-ts';
1414
import { theme } from 'twin.macro';
15-
import { toRGBA } from '@/helpers';
15+
import { hexToRgba } from '@/lib/helpers';
1616

1717
ChartJS.register(LineElement, PointElement, Filler, LinearScale);
1818

@@ -86,7 +86,7 @@ function getEmptyData (label: string, sets = 1, callback?: ChartDatasetCallback
8686
label,
8787
data: Array(20).fill(0),
8888
borderColor: theme('colors.cyan.400'),
89-
backgroundColor: toRGBA(theme('colors.cyan.700'), 0.5),
89+
backgroundColor: hexToRgba(theme('colors.cyan.700'), 0.5),
9090
}, index)),
9191
};
9292
}

Diff for: resources/scripts/components/server/features/Features.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useMemo } from 'react';
22
import features from './index';
3-
import { getObjectKeys } from '@/helpers';
3+
import { getObjectKeys } from '@/lib/objects';
44

55
type ListItems = [ string, React.ComponentType ][];
66

Diff for: resources/scripts/components/server/files/FileObjectRow.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
22
import { faFileAlt, faFileArchive, faFileImport, faFolder } from '@fortawesome/free-solid-svg-icons';
3-
import { bytesToHuman, encodePathSegments } from '@/helpers';
3+
import { encodePathSegments } from '@/helpers';
44
import { differenceInHours, format, formatDistanceToNow } from 'date-fns';
55
import React, { memo } from 'react';
66
import { FileObject } from '@/api/server/files/loadDirectory';
@@ -13,6 +13,7 @@ import styled from 'styled-components/macro';
1313
import SelectFileCheckbox from '@/components/server/files/SelectFileCheckbox';
1414
import { usePermissions } from '@/plugins/usePermissions';
1515
import { join } from 'path';
16+
import { bytesToString } from '@/lib/formatters';
1617

1718
const Row = styled.div`
1819
${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 }) => (
6162
</div>
6263
{file.isFile &&
6364
<div css={tw`w-1/6 text-right mr-4 hidden sm:block`}>
64-
{bytesToHuman(file.size)}
65+
{bytesToString(file.size)}
6566
</div>
6667
}
6768
<div

0 commit comments

Comments
 (0)