diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 7b98811..86f74ad 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -23,6 +23,9 @@ jobs: - name: Install Dependencies run: yarn install --frozen-lockfile + - name: TSC + run: yarn run tsc + - name: Test run: yarn run coverage diff --git a/package.json b/package.json index 7c3e994..26afdf7 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "build": "vite build", "dev": "vite", "preview": "vite preview", + "tsc": "tsc", "test": "vitest", "coverage": "vitest run --coverage", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" diff --git a/src/components/message/MessageAttachments.tsx b/src/components/message/MessageAttachments.tsx index b76ca5a..e8c8e36 100644 --- a/src/components/message/MessageAttachments.tsx +++ b/src/components/message/MessageAttachments.tsx @@ -1,8 +1,8 @@ import { ImageList, ImageListItem } from '@mui/material' import { FC, useCallback, useState } from 'react' import Lightbox from 'react-image-lightbox' -import { Message } from '../../api/Message' import 'react-image-lightbox/style.css' +import { Message } from '../../api/Message' interface Props { message: Message @@ -11,7 +11,7 @@ interface Props { const MessageAttachements: FC = ({ message }) => { const [imageLightboxOpen, setImageLightboxOpen] = useState(false) const [imageIndex, setImageIndex] = useState(0) - const attachments = message.attachments + const attachments = message.attachments || [] const openImageLightbox = useCallback(() => setImageLightboxOpen(true), []) const closeImageLightbox = useCallback(() => setImageLightboxOpen(false), []) diff --git a/src/components/message/MessageFormCounter.test.tsx b/src/components/message/MessageFormCounter.test.tsx index 4f25165..b8b6782 100644 --- a/src/components/message/MessageFormCounter.test.tsx +++ b/src/components/message/MessageFormCounter.test.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { render, screen } from '@testing-library/react' import MessageFormCounter from './MessageFormCounter' diff --git a/src/components/ticker/LocationSearch.tsx b/src/components/ticker/LocationSearch.tsx index 828bd78..a6abeb3 100644 --- a/src/components/ticker/LocationSearch.tsx +++ b/src/components/ticker/LocationSearch.tsx @@ -1,5 +1,5 @@ -import React, { FC, useRef, useState } from 'react' import { Autocomplete, TextField } from '@mui/material' +import React, { FC, useRef, useState } from 'react' interface SearchResult { place_id: number @@ -28,7 +28,7 @@ const LocationSearch: FC = ({ callback }) => { const [options, setOptions] = useState([]) const previousController = useRef() - const handleInputChange = (event: React.SyntheticEvent, value: string) => { + const handleInputChange = (_: React.SyntheticEvent, value: string) => { if (previousController.current) { previousController.current.abort() } @@ -44,7 +44,7 @@ const LocationSearch: FC = ({ callback }) => { }) } - const handleChange = (event: React.SyntheticEvent, value: SearchResult | null) => { + const handleChange = (_: React.SyntheticEvent, value: SearchResult | null) => { if (value) { callback({ title: value?.display_name, lat: value?.lat, lon: value?.lon }) } diff --git a/src/components/ticker/TickerModalForm.tsx b/src/components/ticker/TickerModalForm.tsx index 8e635c0..02fb3c2 100644 --- a/src/components/ticker/TickerModalForm.tsx +++ b/src/components/ticker/TickerModalForm.tsx @@ -1,10 +1,10 @@ import { Tab, Tabs } from '@mui/material' import React, { FC, useState } from 'react' import { Ticker } from '../../api/Ticker' +import Modal from '../common/Modal' import TabPanel from '../common/TabPanel' -import TickerForm from './form/TickerForm' import TickerSocialConnections from './TickerSocialConnections' -import Modal from '../common/Modal' +import TickerForm from './form/TickerForm' interface Props { onClose: () => void @@ -15,7 +15,7 @@ interface Props { const TickerModalForm: FC = ({ onClose, open, ticker }) => { const [tabValue, setTabValue] = useState(0) - const handleTabChange = (e: React.SyntheticEvent, value: number) => { + const handleTabChange = (_: React.SyntheticEvent, value: number) => { setTabValue(value) } diff --git a/src/theme/ThemeProvider.tsx b/src/theme/ThemeProvider.tsx index 77e10a1..b8348d3 100644 --- a/src/theme/ThemeProvider.tsx +++ b/src/theme/ThemeProvider.tsx @@ -1,10 +1,10 @@ -import React, { FC, ReactNode } from 'react' -import { ThemeProvider as MUIThemeProvider, createTheme, StyledEngineProvider, CssBaseline, alpha } from '@mui/material' +import { CssBaseline, ThemeProvider as MUIThemeProvider, StyledEngineProvider, alpha, createTheme } from '@mui/material' +import { FC, ReactNode } from 'react' import GlobalStyles from './GlobalStyles' +import customShadows from './customShadows' import palette from './palette' import shadows from './shadows' import typography from './typography' -import customShadows from './customShadows' const theme = createTheme({ palette: palette, diff --git a/src/views/SettingsView.test.tsx b/src/views/SettingsView.test.tsx index 29f45ea..5aa8c86 100644 --- a/src/views/SettingsView.test.tsx +++ b/src/views/SettingsView.test.tsx @@ -1,11 +1,11 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import sign from 'jwt-encode' import { MemoryRouter } from 'react-router' +import { vi } from 'vitest' import { AuthProvider } from '../contexts/AuthContext' -import sign from 'jwt-encode' import SettingsView from './SettingsView' -import userEvent from '@testing-library/user-event' -import { vi } from 'vitest' describe('SettingsView', function () { const jwt = sign({ id: 1, email: 'louis@systemli.org', roles: ['admin', 'user'] }, 'secret') @@ -38,7 +38,7 @@ describe('SettingsView', function () { beforeEach(() => { vi.spyOn(window.localStorage.__proto__, 'getItem').mockReturnValue(jwt) - fetch.resetMocks() + fetchMock.resetMocks() }) function setup() { @@ -61,7 +61,7 @@ describe('SettingsView', function () { } test('renders settings and open dialogs', async function () { - fetch.mockIf(/^http:\/\/localhost:8080\/.*$/, (request: Request) => { + fetchMock.mockIf(/^http:\/\/localhost:8080\/.*$/, (request: Request) => { if (request.url.endsWith('/admin/settings/inactive_settings')) { return Promise.resolve(inactiveSettingsResponse) } @@ -114,7 +114,7 @@ describe('SettingsView', function () { }) test('settings could not fetched', async function () { - fetch.mockReject(new Error('network error')) + fetchMock.mockReject(new Error('network error')) setup() const loaders = screen.getAllByText(/loading/i) diff --git a/src/views/TickerView.tsx b/src/views/TickerView.tsx index 21ae7f6..89ff6d7 100644 --- a/src/views/TickerView.tsx +++ b/src/views/TickerView.tsx @@ -26,7 +26,7 @@ const TickerView: FC = () => { ) } - const ticker = data?.data.ticker + const ticker = data?.data?.ticker return ( diff --git a/src/views/UsersView.test.tsx b/src/views/UsersView.test.tsx index 8a9d12f..926a032 100644 --- a/src/views/UsersView.test.tsx +++ b/src/views/UsersView.test.tsx @@ -1,18 +1,18 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import sign from 'jwt-encode' import { MemoryRouter } from 'react-router' +import { vi } from 'vitest' import { AuthProvider } from '../contexts/AuthContext' import UsersView from './UsersView' -import sign from 'jwt-encode' -import userEvent from '@testing-library/user-event' -import { vi } from 'vitest' describe('UsersView', function () { const jwt = sign({ id: 1, email: 'louis@systemli.org', roles: ['admin', 'user'] }, 'secret') beforeEach(() => { vi.spyOn(window.localStorage.__proto__, 'getItem').mockReturnValue(jwt) - fetch.resetMocks() + fetchMock.resetMocks() }) function setup() { @@ -35,7 +35,7 @@ describe('UsersView', function () { } test('renders list', async function () { - fetch.mockResponseOnce( + fetchMock.mockResponseOnce( JSON.stringify({ data: { users: [ @@ -57,7 +57,7 @@ describe('UsersView', function () { test('open new users dialog', async function () { setup() - fetch.mockIf( + fetchMock.mockIf( /\/v1\/admin\/users/i, JSON.stringify({ data: { @@ -65,7 +65,7 @@ describe('UsersView', function () { }, }) ) - fetch.mockIf( + fetchMock.mockIf( /\/v1\/admin\/tickers/i, JSON.stringify({ data: { @@ -91,7 +91,7 @@ describe('UsersView', function () { }) test('open dialog for existing user', async function () { - fetch.mockIf( + fetchMock.mockIf( /\/v1\/admin\/users/i, JSON.stringify({ data: { @@ -154,7 +154,7 @@ describe('UsersView', function () { }) test('user list could not fetched', async function () { - fetch.mockReject(new Error('network error')) + fetchMock.mockReject(new Error('network error')) setup() expect(screen.getByText(/loading/i)).toBeInTheDocument()