mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
✅ Add vitest and initial tests
This commit is contained in:
11
package.json
11
package.json
@@ -19,7 +19,9 @@
|
|||||||
"jest:watch": "jest --watch",
|
"jest:watch": "jest --watch",
|
||||||
"prettier:check": "prettier --check \"**/*.{ts,tsx}\"",
|
"prettier:check": "prettier --check \"**/*.{ts,tsx}\"",
|
||||||
"prettier:write": "prettier --write \"**/*.{ts,tsx}\"",
|
"prettier:write": "prettier --write \"**/*.{ts,tsx}\"",
|
||||||
"test": "npm run prettier:check && npm run lint && npm run typecheck && npm run jest",
|
"test": "vitest",
|
||||||
|
"test:ui": "vitest --ui",
|
||||||
|
"test:run": "vitest run",
|
||||||
"ci": "yarn test && yarn lint --fix && yarn typecheck && yarn prettier:write"
|
"ci": "yarn test && yarn lint --fix && yarn typecheck && yarn prettier:write"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
"@tabler/icons": "^1.106.0",
|
"@tabler/icons": "^1.106.0",
|
||||||
"@tanstack/react-query": "^4.2.1",
|
"@tanstack/react-query": "^4.2.1",
|
||||||
"@tanstack/react-query-devtools": "^4.24.4",
|
"@tanstack/react-query-devtools": "^4.24.4",
|
||||||
|
"@vitejs/plugin-react": "^3.1.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"consola": "^2.15.3",
|
"consola": "^2.15.3",
|
||||||
"cookies-next": "^2.1.1",
|
"cookies-next": "^2.1.1",
|
||||||
@@ -70,6 +73,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^12.1.4",
|
"@next/bundle-analyzer": "^12.1.4",
|
||||||
"@next/eslint-plugin-next": "^12.1.4",
|
"@next/eslint-plugin-next": "^12.1.4",
|
||||||
|
"@testing-library/react": "^14.0.0",
|
||||||
"@types/dockerode": "^3.3.9",
|
"@types/dockerode": "^3.3.9",
|
||||||
"@types/node": "17.0.1",
|
"@types/node": "17.0.1",
|
||||||
"@types/prismjs": "^1.26.0",
|
"@types/prismjs": "^1.26.0",
|
||||||
@@ -78,6 +82,7 @@
|
|||||||
"@types/video.js": "^7.3.51",
|
"@types/video.js": "^7.3.51",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||||
"@typescript-eslint/parser": "^5.30.7",
|
"@typescript-eslint/parser": "^5.30.7",
|
||||||
|
"@vitest/ui": "^0.29.3",
|
||||||
"eslint": "^8.20.0",
|
"eslint": "^8.20.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||||
@@ -90,11 +95,13 @@
|
|||||||
"eslint-plugin-testing-library": "^5.5.1",
|
"eslint-plugin-testing-library": "^5.5.1",
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"jest": "^28.1.3",
|
"jest": "^28.1.3",
|
||||||
|
"jsdom": "^21.1.1",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"sass": "^1.56.1",
|
"sass": "^1.56.1",
|
||||||
"turbo": "^1.7.4",
|
"turbo": "^1.7.4",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"video.js": "^8.0.3"
|
"video.js": "^8.0.3",
|
||||||
|
"vitest": "^0.29.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "17.0.2",
|
"@types/react": "17.0.2",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type {
|
|||||||
} from '../../../pages/api/modules/usenet/history';
|
} from '../../../pages/api/modules/usenet/history';
|
||||||
import { UsenetInfoRequestParams, UsenetInfoResponse } from '../../../pages/api/modules/usenet';
|
import { UsenetInfoRequestParams, UsenetInfoResponse } from '../../../pages/api/modules/usenet';
|
||||||
import { UsenetPauseRequestParams } from '../../../pages/api/modules/usenet/pause';
|
import { UsenetPauseRequestParams } from '../../../pages/api/modules/usenet/pause';
|
||||||
import { queryClient } from '../../../tools/queryClient';
|
import { queryClient } from '../../../tools/server/configurations/tanstack/queryClient.tool';
|
||||||
import { UsenetResumeRequestParams } from '../../../pages/api/modules/usenet/resume';
|
import { UsenetResumeRequestParams } from '../../../pages/api/modules/usenet/resume';
|
||||||
|
|
||||||
const POLLING_INTERVAL = 2000;
|
const POLLING_INTERVAL = 2000;
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import { CategoryEditModal } from '../components/Dashboard/Wrappers/Category/Cat
|
|||||||
import { ConfigProvider } from '../config/provider';
|
import { ConfigProvider } from '../config/provider';
|
||||||
import { usePackageAttributesStore } from '../tools/client/zustands/usePackageAttributesStore';
|
import { usePackageAttributesStore } from '../tools/client/zustands/usePackageAttributesStore';
|
||||||
import { ColorTheme } from '../tools/color';
|
import { ColorTheme } from '../tools/color';
|
||||||
import { queryClient } from '../tools/queryClient';
|
import { queryClient } from '../tools/server/configurations/tanstack/queryClient.tool';
|
||||||
import {
|
import {
|
||||||
getServiceSidePackageAttributes,
|
getServiceSidePackageAttributes,
|
||||||
ServerSidePackageAttributesType,
|
ServerSidePackageAttributesType,
|
||||||
} from '../tools/server/getPackageVersion';
|
} from '../tools/server/getPackageVersion';
|
||||||
import { theme } from '../tools/theme';
|
import { theme } from '../tools/server/theme/theme';
|
||||||
|
|
||||||
import { useEditModeInformationStore } from '../hooks/useEditModeInformation';
|
import { useEditModeInformationStore } from '../hooks/useEditModeInformation';
|
||||||
import '../styles/global.scss';
|
import '../styles/global.scss';
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import Parser from 'rss-parser';
|
import Parser from 'rss-parser';
|
||||||
|
|
||||||
import { getConfig } from '../../../../tools/config/getConfig';
|
import { getConfig } from '../../../../tools/config/getConfig';
|
||||||
import { Stopwatch } from '../../../../tools/shared/stopwatch';
|
|
||||||
import { IRssWidget } from '../../../../widgets/rss/RssWidgetTile';
|
import { IRssWidget } from '../../../../widgets/rss/RssWidgetTile';
|
||||||
|
import { Stopwatch } from '../../../../tools/shared/time/stopwatch.tool';
|
||||||
|
|
||||||
type CustomItem = {
|
type CustomItem = {
|
||||||
'media:content': string;
|
'media:content': string;
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import Dockerode from 'dockerode';
|
|
||||||
|
|
||||||
import { MatchingImages, ServiceType, tryMatchPort } from './types';
|
|
||||||
|
|
||||||
function tryMatchType(imageName: string): ServiceType {
|
|
||||||
// Try to find imageName inside MatchingImages
|
|
||||||
|
|
||||||
const match = MatchingImages.find(({ image }) => imageName.includes(image));
|
|
||||||
if (match) {
|
|
||||||
return match.type;
|
|
||||||
}
|
|
||||||
return 'Other';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tryMatchService(container: Dockerode.ContainerInfo | undefined) {
|
|
||||||
if (container === undefined) return {};
|
|
||||||
const name = container.Names[0].substring(1);
|
|
||||||
const type = tryMatchType(container.Image);
|
|
||||||
const port = tryMatchPort(type.toLowerCase())?.value ?? container.Ports[0]?.PublicPort;
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
id: container.Id,
|
|
||||||
type: tryMatchType(container.Image),
|
|
||||||
url: `localhost${port ? `:${port}` : ''}`,
|
|
||||||
icon: `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${name
|
|
||||||
.replace(/\s+/g, '-')
|
|
||||||
.toLowerCase()}.png`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
27
src/tools/shared/math/percentage.tool.test.ts
Normal file
27
src/tools/shared/math/percentage.tool.test.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { percentage } from './percentage.tool';
|
||||||
|
|
||||||
|
describe('percentage', () => {
|
||||||
|
it.concurrent('be fixed value', () => {
|
||||||
|
// arrange
|
||||||
|
const value = 62;
|
||||||
|
|
||||||
|
// act
|
||||||
|
const fixedPercentage = percentage(value, 100);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(fixedPercentage).toBe('62.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('be fixed value when decimal places', () => {
|
||||||
|
// arrange
|
||||||
|
const value = 42.69696969;
|
||||||
|
|
||||||
|
// act
|
||||||
|
const fixedPercentage = percentage(value, 100);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(fixedPercentage).toBe('42.7');
|
||||||
|
});
|
||||||
|
});
|
||||||
47
src/tools/shared/time/date.tool.test.ts
Normal file
47
src/tools/shared/time/date.tool.test.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { isToday } from './date.tool';
|
||||||
|
|
||||||
|
describe('isToday', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('should return true if date is today', () => {
|
||||||
|
// arrange
|
||||||
|
const date = new Date(2022, 3, 17);
|
||||||
|
vi.setSystemTime(date);
|
||||||
|
|
||||||
|
// act
|
||||||
|
const today = isToday(date);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(today).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent("should return true if date is today and time doesn't match", () => {
|
||||||
|
// arrange
|
||||||
|
vi.setSystemTime(new Date(2022, 3, 17, 16, 25, 11));
|
||||||
|
|
||||||
|
// act
|
||||||
|
const today = isToday(new Date(2022, 3, 17));
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(today).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent("should be false if date doesn't match", () => {
|
||||||
|
// arrange
|
||||||
|
vi.setSystemTime(new Date(2022, 3, 17, 16));
|
||||||
|
|
||||||
|
// act
|
||||||
|
const today = isToday(new Date(2022, 3, 15));
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(today).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
26
src/tools/shared/time/stopwatch.tool.test.ts
Normal file
26
src/tools/shared/time/stopwatch.tool.test.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { Stopwatch } from './stopwatch.tool';
|
||||||
|
|
||||||
|
describe('stopwatch', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('should be elapsed time between start and current', () => {
|
||||||
|
// arrange
|
||||||
|
vi.setSystemTime(new Date(2023, 2, 26, 0, 0, 0));
|
||||||
|
const stopwatch = new Stopwatch();
|
||||||
|
|
||||||
|
// act
|
||||||
|
vi.setSystemTime(new Date(2023, 2, 26, 0, 0, 2));
|
||||||
|
const milliseconds = stopwatch.getEllapsedMilliseconds();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(milliseconds).toBe(2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,7 +6,7 @@ import { i18n } from 'next-i18next';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useConfigContext } from '../../config/provider';
|
import { useConfigContext } from '../../config/provider';
|
||||||
import { useColorTheme } from '../../tools/color';
|
import { useColorTheme } from '../../tools/color';
|
||||||
import { isToday } from '../../tools/isToday';
|
import { isToday } from '../../tools/shared/time/date.tool';
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
import { CalendarDay } from './CalendarDay';
|
import { CalendarDay } from './CalendarDay';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import axios from 'axios';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useConfigContext } from '../../config/provider';
|
import { useConfigContext } from '../../config/provider';
|
||||||
import { bytes } from '../../tools/bytesHelper';
|
import { bytes } from '../../tools/bytesHelper';
|
||||||
import { percentage } from '../../tools/percentage';
|
import { percentage } from '../../tools/shared/math/percentage.tool';
|
||||||
import { DashDotInfo } from './DashDotCompactNetwork';
|
import { DashDotInfo } from './DashDotCompactNetwork';
|
||||||
|
|
||||||
interface DashDotCompactStorageProps {
|
interface DashDotCompactStorageProps {
|
||||||
|
|||||||
11
vitest.config.ts
Normal file
11
vitest.config.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user