mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 08:23:55 +00:00
Refactor/vite (#209)
* refactor(vite): add dependencies * refactor(vite): necessary code migrations * refactor(vite): convert file to tsx * refactor(vite): config * refactor(vite): doctype casing
This commit is contained in:
+14
-6
@@ -1,8 +1,18 @@
|
||||
{
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest",
|
||||
"plugin:react/recommended"
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"eslint-config-prettier"
|
||||
],
|
||||
"plugins": [
|
||||
"react",
|
||||
@@ -10,7 +20,6 @@
|
||||
"simple-import-sort"
|
||||
],
|
||||
"rules": {
|
||||
"jest/no-mocks-import": "warn",
|
||||
"no-useless-concat": "warn",
|
||||
"prefer-template": "warn",
|
||||
"react/jsx-no-bind": [
|
||||
@@ -81,8 +90,7 @@
|
||||
}
|
||||
],
|
||||
"simple-import-sort/imports": "warn",
|
||||
"simple-import-sort/exports": "warn",
|
||||
"import/no-duplicates": "error"
|
||||
"simple-import-sort/exports": "warn"
|
||||
},
|
||||
"overrides": [
|
||||
// override "simple-import-sort" config
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<link rel='icon' href='/favicon.ico' />
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<meta name='theme-color' content='#121212' />
|
||||
<meta name='ontime' content='ontime - event timer manager' />
|
||||
<link rel='apple-touch-icon' href='/logo192.png' />
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='32x32'
|
||||
href='/favicon-32x32.png'
|
||||
/>
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='16x16'
|
||||
href='/favicon-16x16.png'
|
||||
/>
|
||||
<link rel='manifest' href='/site.webmanifest' />
|
||||
<link rel='manifest' href='/manifest.json' />
|
||||
<title>ontime</title>
|
||||
<style>
|
||||
body,
|
||||
html {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id='root'></div>
|
||||
<script type='module' src='/src/index.jsx'></script>
|
||||
</body>
|
||||
</html>
|
||||
+18
-10
@@ -27,17 +27,15 @@
|
||||
"react-table": "^7.7.0",
|
||||
"socket.io-client": "^4.5.1",
|
||||
"typeface-open-sans": "^1.1.13",
|
||||
"use-fit-text": "^2.4.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "set BROWSER=none&&react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"start": "set BROWSER=none&&vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"stylelint": "npx stylelint \"**/*.scss\"\n",
|
||||
"test": "react-scripts test",
|
||||
"test:pipeline": "react-scripts test --watchAll=false --runInBand --detectOpenHandles --forceExit ",
|
||||
"eject": "react-scripts eject"
|
||||
"test": "vitest ",
|
||||
"test:pipeline": "vitest run"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -56,20 +54,30 @@
|
||||
"@testing-library/react": "^13.1.1",
|
||||
"@testing-library/react-hooks": "^8.0.0",
|
||||
"@testing-library/user-event": "^14.1.1",
|
||||
"@types/jest": "^29.0.1",
|
||||
"@types/node": "^18.7.16",
|
||||
"@types/react": "^18.0.19",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||
"@typescript-eslint/parser": "^5.37.0",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"eslint-plugin-react": "^7.31.8",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"eslint-plugin-testing-library": "^5.6.4",
|
||||
"jsdom": "^20.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-test-renderer": "^18.1.0",
|
||||
"sass": "^1.51.0",
|
||||
"stylelint": "^14.9.1",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-standard-scss": "^4.0.0",
|
||||
"typescript": "^4.8.3"
|
||||
"typescript": "^4.8.3",
|
||||
"vite": "^3.1.1",
|
||||
"vite-plugin-svgr": "^2.2.1",
|
||||
"vite-tsconfig-paths": "^3.5.0",
|
||||
"vitest": "^0.23.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#121212" />
|
||||
<meta name="ontime" content="ontime - event timer manager" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="%PUBLIC_URL%/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="%PUBLIC_URL%/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>ontime</title>
|
||||
<style>
|
||||
body,
|
||||
html {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
/// <reference types="react-scripts" />
|
||||
+1
-3
@@ -11,7 +11,7 @@ import theme from './theme/theme';
|
||||
import AppRouter from './AppRouter';
|
||||
|
||||
// Load Open Sans typeface
|
||||
require('typeface-open-sans');
|
||||
import('typeface-open-sans');
|
||||
export const ontimeQueryClient = new QueryClient();
|
||||
|
||||
function App() {
|
||||
@@ -42,8 +42,6 @@ function App() {
|
||||
};
|
||||
}, [handleKeyPress]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<ChakraProvider resetCSS theme={theme}>
|
||||
<SocketProvider>
|
||||
|
||||
@@ -19,12 +19,8 @@ export const TIMER = ['ontime-timer'];
|
||||
* @description finds server path given the current location, it
|
||||
* @return {*}
|
||||
*/
|
||||
export const calculateServer = () => {
|
||||
if (process.env?.NODE_ENV === 'development') {
|
||||
return `http://localhost:${STATIC_PORT}`;
|
||||
}
|
||||
return window.location.origin;
|
||||
};
|
||||
export const calculateServer = () =>
|
||||
import.meta.env.DEV ? `http://localhost:${STATIC_PORT}` : window.location.origin;
|
||||
|
||||
export const serverURL = calculateServer();
|
||||
export const eventURL = `${serverURL}/${EVENT_TABLE}`;
|
||||
|
||||
@@ -2,8 +2,6 @@ import { IconButton } from '@chakra-ui/button';
|
||||
import { IconButtonProps } from '@chakra-ui/react';
|
||||
import { Tooltip } from '@chakra-ui/tooltip';
|
||||
|
||||
export type Sizes = 'xs' | 'sm' | 'md' | 'lg';
|
||||
|
||||
interface TooltipActionBtnProps extends IconButtonProps {
|
||||
clickHandler: () => void;
|
||||
tooltip: string;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable react/destructuring-assignment */
|
||||
import React from 'react';
|
||||
|
||||
import { version as appVersion } from '../../../../package.json';
|
||||
import { LoggingContext } from '../../context/LoggingContext';
|
||||
|
||||
import style from './ErrorBoundary.module.scss';
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const appVersion = require('../../../../package.json').version;
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
static contextType = LoggingContext;
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import EditableTimer from '../EditableTimer';
|
||||
|
||||
const renderEditableTimer = (
|
||||
name = 'test',
|
||||
actionHandler = () => undefined,
|
||||
validate = () => undefined
|
||||
) => render(<EditableTimer name={name} actionHandler={actionHandler} validate={validate} />);
|
||||
|
||||
describe('test EditableTimer component', () => {
|
||||
const testName = "test";
|
||||
const actionHandler = jest.fn();
|
||||
const validate = jest.fn();
|
||||
|
||||
renderEditableTimer(testName, actionHandler, validate);
|
||||
const editableTimer = screen.getByTestId('editable-timer');
|
||||
const editableInput = screen.getByTestId('editable-timer-input');
|
||||
|
||||
// skipping for now as error seems to come from beta library
|
||||
it.skip('renders correctly', () => {
|
||||
expect(editableTimer).toBeInTheDocument();
|
||||
expect(editableInput).toBeInTheDocument();
|
||||
|
||||
const myTypedString = 'verylongandcool'
|
||||
userEvent.type(editableInput, myTypedString);
|
||||
expect(editableInput).toHaveValue(myTypedString);
|
||||
|
||||
userEvent.type(editableInput, '{enter}');
|
||||
|
||||
// no previous is given, defaults to 0
|
||||
expect(validate).toHaveBeenCalledWith(testName, 0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
export type TLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
|
||||
|
||||
export type TOptions = {
|
||||
logLevel?: TLogLevel;
|
||||
maxFontSize?: number;
|
||||
minFontSize?: number;
|
||||
onFinish?: (fontSize: number) => void;
|
||||
onStart?: () => void;
|
||||
resolution?: number;
|
||||
};
|
||||
|
||||
const LOG_LEVEL: Record<TLogLevel, number> = {
|
||||
debug: 10,
|
||||
info: 20,
|
||||
warn: 30,
|
||||
error: 40,
|
||||
none: 100,
|
||||
};
|
||||
|
||||
const useFitText = ({
|
||||
logLevel: logLevelOption = 'info',
|
||||
maxFontSize = 100,
|
||||
minFontSize = 20,
|
||||
onFinish,
|
||||
onStart,
|
||||
resolution = 5,
|
||||
}: TOptions = {}) => {
|
||||
const logLevel = LOG_LEVEL[logLevelOption];
|
||||
|
||||
const initState = useCallback(() => {
|
||||
return {
|
||||
calcKey: 0,
|
||||
fontSize: maxFontSize,
|
||||
fontSizePrev: minFontSize,
|
||||
fontSizeMax: maxFontSize,
|
||||
fontSizeMin: minFontSize,
|
||||
};
|
||||
}, [maxFontSize, minFontSize]);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const innerHtmlPrevRef = useRef<string | null>();
|
||||
const isCalculatingRef = useRef(false);
|
||||
const [state, setState] = useState(initState);
|
||||
const { calcKey, fontSize, fontSizeMax, fontSizeMin, fontSizePrev } = state;
|
||||
|
||||
// Monitor div size changes and recalculate on resize
|
||||
let animationFrameId: number | null = null;
|
||||
const [ro] = useState(
|
||||
() =>
|
||||
new ResizeObserver(() => {
|
||||
animationFrameId = window.requestAnimationFrame(() => {
|
||||
if (isCalculatingRef.current) {
|
||||
return;
|
||||
}
|
||||
onStart && onStart();
|
||||
isCalculatingRef.current = true;
|
||||
// `calcKey` is used in the dependencies array of
|
||||
// `useIsoLayoutEffect` below. It is incremented so that the font size
|
||||
// will be recalculated even if the previous state didn't change (e.g.
|
||||
// when the text fit initially).
|
||||
setState({
|
||||
...initState(),
|
||||
calcKey: calcKey + 1,
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ro.observe(ref.current);
|
||||
}
|
||||
return () => {
|
||||
animationFrameId && window.cancelAnimationFrame(animationFrameId);
|
||||
ro.disconnect();
|
||||
};
|
||||
}, [animationFrameId, ro]);
|
||||
|
||||
// Recalculate when the div contents change
|
||||
const innerHtml = ref.current && ref.current.innerHTML;
|
||||
useEffect(() => {
|
||||
if (calcKey === 0 || isCalculatingRef.current) {
|
||||
return;
|
||||
}
|
||||
if (innerHtml !== innerHtmlPrevRef.current) {
|
||||
onStart && onStart();
|
||||
setState({
|
||||
...initState(),
|
||||
calcKey: calcKey + 1,
|
||||
});
|
||||
}
|
||||
innerHtmlPrevRef.current = innerHtml;
|
||||
}, [calcKey, initState, innerHtml, onStart]);
|
||||
|
||||
// Check overflow and resize font
|
||||
useLayoutEffect(() => {
|
||||
// Don't start calculating font size until the `resizeKey` is incremented
|
||||
// above in the `ResizeObserver` callback. This avoids an extra resize
|
||||
// on initialization.
|
||||
if (calcKey === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isWithinResolution = Math.abs(fontSize - fontSizePrev) <= resolution;
|
||||
const isOverflow =
|
||||
!!ref.current &&
|
||||
(ref.current.scrollHeight > ref.current.offsetHeight ||
|
||||
ref.current.scrollWidth > ref.current.offsetWidth);
|
||||
const isFailed = isOverflow && fontSize === fontSizePrev;
|
||||
const isAsc = fontSize > fontSizePrev;
|
||||
|
||||
// Return if the font size has been adjusted "enough" (change within `resolution`)
|
||||
// reduce font size by one increment if it's overflowing.
|
||||
if (isWithinResolution) {
|
||||
if (isFailed) {
|
||||
isCalculatingRef.current = false;
|
||||
if (logLevel <= LOG_LEVEL.info) {
|
||||
console.info(
|
||||
`[use-fit-text] reached \`minFontSize = ${minFontSize}\` without fitting text`,
|
||||
);
|
||||
}
|
||||
} else if (isOverflow) {
|
||||
setState({
|
||||
fontSize: isAsc ? fontSizePrev : fontSizeMin,
|
||||
fontSizeMax,
|
||||
fontSizeMin,
|
||||
fontSizePrev,
|
||||
calcKey,
|
||||
});
|
||||
} else {
|
||||
isCalculatingRef.current = false;
|
||||
onFinish && onFinish(fontSize);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Binary search to adjust font size
|
||||
let delta: number;
|
||||
let newMax = fontSizeMax;
|
||||
let newMin = fontSizeMin;
|
||||
if (isOverflow) {
|
||||
delta = isAsc ? fontSizePrev - fontSize : fontSizeMin - fontSize;
|
||||
newMax = Math.min(fontSizeMax, fontSize);
|
||||
} else {
|
||||
delta = isAsc ? fontSizeMax - fontSize : fontSizePrev - fontSize;
|
||||
newMin = Math.max(fontSizeMin, fontSize);
|
||||
}
|
||||
setState({
|
||||
calcKey,
|
||||
fontSize: fontSize + delta / 2,
|
||||
fontSizeMax: newMax,
|
||||
fontSizeMin: newMin,
|
||||
fontSizePrev: fontSize,
|
||||
});
|
||||
}, [
|
||||
calcKey,
|
||||
fontSize,
|
||||
fontSizeMax,
|
||||
fontSizeMin,
|
||||
fontSizePrev,
|
||||
onFinish,
|
||||
ref,
|
||||
resolution,
|
||||
]);
|
||||
|
||||
return { fontSize: `${fontSize}%`, ref };
|
||||
};
|
||||
|
||||
export default useFitText;
|
||||
@@ -16,7 +16,7 @@ test('generate 100 with less than 110 attempts', () => {
|
||||
expect(attempts).toBeLessThan(105);
|
||||
});
|
||||
|
||||
describe('generate 1000 with less than 1020 attempts', () => {
|
||||
test('generate 1000 with less than 1020 attempts', () => {
|
||||
const ids = new Set();
|
||||
let attempts = 1;
|
||||
while (ids.size < 1000) {
|
||||
|
||||
+14
-15
@@ -3,17 +3,25 @@ import { IconButton } from '@chakra-ui/button';
|
||||
import { Editable, EditableInput, EditablePreview } from '@chakra-ui/editable';
|
||||
import { Tooltip } from '@chakra-ui/tooltip';
|
||||
import { IoSunny } from '@react-icons/all-files/io5/IoSunny';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import style from './MessageControl.module.scss';
|
||||
|
||||
export default function InputRow(props) {
|
||||
const { label, placeholder, text, visible, actionHandler, changeHandler } = props;
|
||||
const [inputText, setInputText] = useState(text || '');
|
||||
interface InputRowProps {
|
||||
label: string;
|
||||
placeholder: string;
|
||||
text: string;
|
||||
visible: boolean;
|
||||
actionHandler: (action: string, payload: object) => void;
|
||||
changeHandler: (newValue: string) => void;
|
||||
}
|
||||
|
||||
const handleInputChange = (newValue) => {
|
||||
export default function InputRow(props: InputRowProps) {
|
||||
const { label, placeholder, text, visible, actionHandler, changeHandler } = props;
|
||||
const [inputText, setInputText] = useState<string>(text || '');
|
||||
|
||||
const handleInputChange = (newValue: string) => {
|
||||
setInputText(newValue);
|
||||
changeHandler(newValue);
|
||||
};
|
||||
@@ -26,7 +34,7 @@ export default function InputRow(props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className={`${visible ? style.inputRowActive: ''}`}>
|
||||
<div className={`${visible ? style.inputRowActive : ''}`}>
|
||||
<span className={style.label}>{label}</span>
|
||||
<div className={style.inputItems}>
|
||||
<Editable
|
||||
@@ -53,12 +61,3 @@ export default function InputRow(props) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
InputRow.propTypes = {
|
||||
label: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
visible: PropTypes.bool,
|
||||
actionHandler: PropTypes.func.isRequired,
|
||||
changeHandler: PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
||||
import MenuBar from '../MenuBar';
|
||||
|
||||
const onOpenHandler = jest.fn();
|
||||
const onCloseHandler = jest.fn();
|
||||
|
||||
const renderInMock = () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClientMock}>
|
||||
<MenuBar onOpen={onOpenHandler} onClose={onCloseHandler} />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
test('check that menu bar renders correctly', () => {
|
||||
// need to inject the react query provider
|
||||
renderInMock();
|
||||
|
||||
const nButtons = screen.getAllByRole('button').length;
|
||||
expect(nButtons).toBe(7);
|
||||
});
|
||||
+7
-6
@@ -1,10 +1,11 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import MenuActionButtons from "../MenuActionButtons";
|
||||
import MenuActionButtons from '../MenuActionButtons';
|
||||
|
||||
const actionHandler = jest.fn();
|
||||
const actionHandler = vi.fn();
|
||||
const renderInMock = () => {
|
||||
render(<MenuActionButtons actionHandler={actionHandler} />)
|
||||
render(<MenuActionButtons actionHandler={actionHandler} />);
|
||||
};
|
||||
|
||||
test('check that menu bar renders correctly', () => {
|
||||
@@ -12,8 +13,8 @@ test('check that menu bar renders correctly', () => {
|
||||
renderInMock();
|
||||
|
||||
const b = screen.getByRole('button', {
|
||||
name: /create menu/i
|
||||
})
|
||||
name: /create menu/i,
|
||||
});
|
||||
|
||||
expect(b).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,19 +1,22 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
||||
import MenuBar from '../MenuBar';
|
||||
|
||||
const onOpenHandler = jest.fn();
|
||||
const onCloseHandler = jest.fn();
|
||||
const onOpenHandler = vi.fn();
|
||||
const onCloseHandler = vi.fn();
|
||||
const isOpen = false;
|
||||
const onUploadOpenHandler = jest.fn();
|
||||
const onUploadOpenHandler = vi.fn();
|
||||
|
||||
const renderInMock = () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClientMock}>
|
||||
<MenuBar onOpen={onOpenHandler} onClose={onCloseHandler} isOpen={isOpen} onUploadOpen={onUploadOpenHandler} />
|
||||
</QueryClientProvider>
|
||||
<MenuBar isSettingsOpen={isOpen} onSettingsOpen={onOpenHandler}
|
||||
onSettingsClose={onCloseHandler} isUploadOpen={isOpen}
|
||||
onUploadOpen={onUploadOpenHandler} />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { getSettings, ontimePlaceholderSettings, postSettings } from 'common/api
|
||||
import { useFetch } from 'common/hooks/useFetch';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
import { version } from '../../../package.json';
|
||||
import { eventSettingsAtom } from '../../common/atoms/LocalEventSettings';
|
||||
import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn';
|
||||
import { LoggingContext } from '../../common/context/LoggingContext';
|
||||
@@ -26,9 +27,6 @@ import SubmitContainer from './SubmitContainer';
|
||||
|
||||
import style from './Modals.module.scss';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const version = require('../../../package.json').version;
|
||||
|
||||
export default function AppSettingsModal() {
|
||||
const { data, status, refetch } = useFetch(APP_SETTINGS, getSettings);
|
||||
const { emitError, emitWarning } = useContext(LoggingContext);
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`makeTable() > returns array of arrays with given fields 1`] = `
|
||||
[
|
||||
[
|
||||
"Ontime · Schedule Template",
|
||||
],
|
||||
[
|
||||
"Event Name",
|
||||
"",
|
||||
],
|
||||
[
|
||||
"Event URL",
|
||||
"",
|
||||
],
|
||||
[],
|
||||
[
|
||||
"Time Start",
|
||||
"Time End",
|
||||
"Event Title",
|
||||
"Presenter Name",
|
||||
"Event Subtitle",
|
||||
"Is Public? (x)",
|
||||
"Notes",
|
||||
"Colour",
|
||||
"user0:test",
|
||||
],
|
||||
[
|
||||
"00:00:00",
|
||||
"00:00:00",
|
||||
"test title 1",
|
||||
"",
|
||||
"",
|
||||
"x",
|
||||
"",
|
||||
"",
|
||||
"test",
|
||||
"test",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`makeTable() returns array of arrays with given fields 1`] = `
|
||||
Array [
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import useFitText from 'use-fit-text';
|
||||
|
||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||
import NavLogo from '../../../common/components/nav/NavLogo';
|
||||
import useFitText from '../../../common/hooks/useFitText';
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { formatDisplay } from '../../../common/utils/dateConfig';
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import matchers from "@testing-library/jest-dom/matchers";
|
||||
import { expect } from "vitest";
|
||||
|
||||
expect.extend(matchers);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"target": "ESNext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"types": ["vite/client", "vite-plugin-svgr/client", "vitest/globals"],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
@@ -31,6 +33,7 @@
|
||||
"./src/declarations/declaration.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
"node_modules",
|
||||
"build"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,19 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
import svgrPlugin from 'vite-plugin-svgr';
|
||||
import viteTsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), viteTsconfigPaths(), svgrPlugin()],
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
setupFiles: './src/setupTests.js',
|
||||
environment: 'jsdom',
|
||||
},
|
||||
build: {
|
||||
outDir: './build'
|
||||
}
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
stats: {
|
||||
logging: 'errors',
|
||||
errors: false,
|
||||
},
|
||||
};
|
||||
+1104
-6740
File diff suppressed because it is too large
Load Diff
@@ -1,95 +1,74 @@
|
||||
// go trough routes and make sure things render as expected
|
||||
// go through routes and make sure things render as expected
|
||||
describe('validate routes', () => {
|
||||
it('viewer routes', () => {
|
||||
cy.visit('http://localhost:4001/');
|
||||
cy.contains('Time Now');
|
||||
describe('viewer routes', () => {
|
||||
it('default to stage timer', () => {
|
||||
cy.visit('http://localhost:4001/');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/timer');
|
||||
cy.contains('Time Now');
|
||||
it('renders stage timer', () => {
|
||||
cy.visit('http://localhost:4001/timer');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/presenter');
|
||||
cy.contains('Time Now');
|
||||
it('renders presenter', () => {
|
||||
cy.visit('http://localhost:4001/presenter');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/speaker');
|
||||
cy.contains('Time Now');
|
||||
it('renders speaker', () => {
|
||||
cy.visit('http://localhost:4001/speaker');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/stage');
|
||||
cy.contains('Time Now');
|
||||
it('renders backstage view /stage', () => {
|
||||
cy.visit('http://localhost:4001/stage');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/backstage');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
it('renders backstage view /backstage', () => {
|
||||
cy.visit('http://localhost:4001/backstage');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/sm');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
it('renders backstage view /sm', () => {
|
||||
cy.visit('http://localhost:4001/sm');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/public');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
it('renders public view', () => {
|
||||
cy.visit('http://localhost:4001/public');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/pip');
|
||||
cy.contains('Today');
|
||||
cy.contains('Info');
|
||||
it('renders pip view', () => {
|
||||
cy.visit('http://localhost:4001/pip');
|
||||
cy.contains('Today');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/studio');
|
||||
cy.contains('ON AIR');
|
||||
it('renders studio clock', () => {
|
||||
cy.visit('http://localhost:4001/studio');
|
||||
cy.contains('ON AIR');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/countdown');
|
||||
cy.contains('Select an event to follow');
|
||||
it('renders countdown selection', () => {
|
||||
cy.visit('http://localhost:4001/countdown');
|
||||
cy.contains('Select an event to follow');
|
||||
});
|
||||
|
||||
cy.visit('http://localhost:4001/countdown?event=1');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Start Time');
|
||||
cy.contains('End Time');
|
||||
});
|
||||
|
||||
it('editor routes', () => {
|
||||
cy.visit('http://localhost:4001/editor');
|
||||
cy.contains('Event List');
|
||||
cy.contains('Timer Control');
|
||||
cy.contains('Messages Control');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
it('self contained editor routes', () => {
|
||||
cy.visit('http://localhost:4001/eventlist');
|
||||
cy.get('.App').should('contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
|
||||
cy.visit('http://localhost:4001/timercontrol');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
|
||||
cy.visit('http://localhost:4001/messagecontrol');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
|
||||
cy.visit('http://localhost:4001/info');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('contain', 'Info');
|
||||
});
|
||||
|
||||
it('table routes', () => {
|
||||
cy.visit('http://localhost:4001/table');
|
||||
cy.contains('Running Timer');
|
||||
|
||||
cy.visit('http://localhost:4001/cuesheet');
|
||||
cy.contains('Running Timer');
|
||||
|
||||
cy.visit('http://localhost:4001/cuelist');
|
||||
cy.contains('Running Timer');
|
||||
it('renders countdown with event', () => {
|
||||
cy.visit('http://localhost:4001/countdown?event=1');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Start Time');
|
||||
cy.contains('End Time');
|
||||
});
|
||||
});
|
||||
|
||||
describe('clock view timer', () => {
|
||||
@@ -130,4 +109,53 @@ describe('validate routes', () => {
|
||||
cy.get('[data-testid="nav-logo"]').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('editor routes', () => {
|
||||
it('app editor', () => {
|
||||
cy.visit('http://localhost:4001/editor');
|
||||
cy.contains('Event List');
|
||||
cy.contains('Timer Control');
|
||||
cy.contains('Messages Control');
|
||||
cy.contains('Info');
|
||||
});
|
||||
it('self contained eventlist', () => {
|
||||
cy.visit('http://localhost:4001/eventlist');
|
||||
cy.get('.App').should('contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
});
|
||||
it('self contained timercontrol', () => {
|
||||
cy.visit('http://localhost:4001/timercontrol');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
});
|
||||
it('self contained messagecontrol', () => {
|
||||
cy.visit('http://localhost:4001/messagecontrol');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
});
|
||||
it('self contained info', () => {
|
||||
cy.visit('http://localhost:4001/info');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('contain', 'Info');
|
||||
});
|
||||
});
|
||||
|
||||
it('table routes', () => {
|
||||
cy.visit('http://localhost:4001/table');
|
||||
cy.contains('Running Timer');
|
||||
|
||||
cy.visit('http://localhost:4001/cuesheet');
|
||||
cy.contains('Running Timer');
|
||||
|
||||
cy.visit('http://localhost:4001/cuelist');
|
||||
cy.contains('Running Timer');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user