mirror of
https://github.com/Xevion/banner.git
synced 2025-12-06 01:14:22 -06:00
feat: add ESLint configuration and testing infrastructure
Add comprehensive ESLint setup with React and TypeScript support, create basic integration tests for the shutdown utilities, and enhance the Justfile with a new check command that runs all validation steps (cargo check, clippy, tests, and linting).
This commit is contained in:
9
Justfile
9
Justfile
@@ -1,5 +1,14 @@
|
||||
default_services := "bot,web,scraper"
|
||||
|
||||
default:
|
||||
just --list
|
||||
|
||||
check:
|
||||
cargo check
|
||||
cargo clippy
|
||||
cargo nextest run
|
||||
pnpm run -C web lint
|
||||
|
||||
# Auto-reloading frontend server
|
||||
frontend:
|
||||
pnpm run -C web dev
|
||||
|
||||
33
tests/basic_test.rs
Normal file
33
tests/basic_test.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use banner::utils::shutdown::join_tasks;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_join_tasks_success() {
|
||||
// Create some tasks that complete successfully
|
||||
let handles: Vec<JoinHandle<()>> = vec![
|
||||
tokio::spawn(async { tokio::time::sleep(tokio::time::Duration::from_millis(10)).await }),
|
||||
tokio::spawn(async { tokio::time::sleep(tokio::time::Duration::from_millis(20)).await }),
|
||||
tokio::spawn(async { /* immediate completion */ }),
|
||||
];
|
||||
|
||||
// All tasks should complete successfully
|
||||
let result = join_tasks(handles).await;
|
||||
assert!(result.is_ok(), "Expected all tasks to complete successfully");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_join_tasks_with_panic() {
|
||||
// Create some tasks, including one that panics
|
||||
let handles: Vec<JoinHandle<()>> = vec![
|
||||
tokio::spawn(async { tokio::time::sleep(tokio::time::Duration::from_millis(10)).await }),
|
||||
tokio::spawn(async { panic!("intentional test panic") }),
|
||||
tokio::spawn(async { /* immediate completion */ }),
|
||||
];
|
||||
|
||||
// Should return an error because one task panicked
|
||||
let result = join_tasks(handles).await;
|
||||
assert!(result.is_err(), "Expected an error when a task panics");
|
||||
|
||||
let error_msg = result.unwrap_err().to_string();
|
||||
assert!(error_msg.contains("1 task(s) panicked"), "Error message should mention panicked tasks");
|
||||
}
|
||||
63
web/eslint.config.js
Normal file
63
web/eslint.config.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import js from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import react from 'eslint-plugin-react';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
|
||||
export default tseslint.config(
|
||||
// Ignore generated files and build outputs
|
||||
{
|
||||
ignores: ['dist', 'node_modules', 'src/routeTree.gen.ts', '*.config.js'],
|
||||
},
|
||||
// Base configs
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
// React plugin configuration
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
plugins: {
|
||||
react,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: '19.0',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
// React rules
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
|
||||
// React Refresh
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
|
||||
// TypeScript overrides
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
|
||||
// Disable prop-types since we're using TypeScript
|
||||
'react/prop-types': 'off',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -7,7 +7,8 @@
|
||||
"start": "vite --port 3000",
|
||||
"build": "vite build && tsc",
|
||||
"serve": "vite preview",
|
||||
"test": "vitest run"
|
||||
"test": "vitest run",
|
||||
"lint": "tsc && eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/themes": "^3.2.1",
|
||||
@@ -23,14 +24,20 @@
|
||||
"recharts": "^3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@types/node": "^24.3.3",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.39.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.24",
|
||||
"jsdom": "^26.0.0",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"vite": "^6.3.5",
|
||||
"vitest": "^3.0.5",
|
||||
"web-vitals": "^4.2.4"
|
||||
|
||||
2129
web/pnpm-lock.yaml
generated
2129
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ export class BannerApiClient {
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
return (await response.json()) as T;
|
||||
}
|
||||
|
||||
async getHealth(): Promise<HealthResponse> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const reportWebVitals = (onPerfEntry?: () => void) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
|
||||
void import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
|
||||
onCLS(onPerfEntry)
|
||||
onINP(onPerfEntry)
|
||||
onFCP(onPerfEntry)
|
||||
|
||||
@@ -237,11 +237,11 @@ function App() {
|
||||
}
|
||||
|
||||
// Schedule the next request after the current one completes
|
||||
timeoutId = setTimeout(fetchData, REFRESH_INTERVAL);
|
||||
timeoutId = setTimeout(() => void fetchData(), REFRESH_INTERVAL);
|
||||
};
|
||||
|
||||
// Start the first request immediately
|
||||
fetchData();
|
||||
void fetchData();
|
||||
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
|
||||
Reference in New Issue
Block a user