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:
Carlos Valente
2022-09-28 21:13:20 +02:00
committed by GitHub
parent d86afee477
commit 562cb769c5
30 changed files with 1572 additions and 7013 deletions
+14 -6
View File
@@ -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
+37
View File
@@ -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
View File
@@ -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"
}
}
}
-59
View File
@@ -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>
-1
View File
@@ -1 +0,0 @@
/// <reference types="react-scripts" />
+1 -3
View File
@@ -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>
+2 -6
View File
@@ -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);
});
});
+177
View File
@@ -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) {
@@ -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);
});
@@ -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 {
+4 -5
View File
@@ -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);
+5 -2
View File
@@ -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"
]
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+19
View File
@@ -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'
}
});
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
stats: {
logging: 'errors',
errors: false,
},
};
+1104 -6740
View File
File diff suppressed because it is too large Load Diff
+108 -80
View File
@@ -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');
});
});