Development
Note
This guide is for local development only.
Local Python/Poetry installation is only supported for development purposes. For production deployments, Docker is the only supported method. See Deployment for production setup instructions.
System Requirements
Before setting up the development environment, ensure you have the following system dependencies installed:
Python 3.13+: Required for the Flask backend
Node.js 18+ and npm: Required for frontend development
ffmpeg/ffprobe: Required for automatic video duration detection
Installing ffmpeg:
# ArchLinux
sudo pacman -S ffmpeg
# Ubuntu/Debian
sudo apt install ffmpeg
# Fedora
sudo dnf install ffmpeg
# macOS (with Homebrew)
brew install ffmpeg
Verifying ffprobe installation:
ffprobe -version
# Should output version information
Setting up Development Environment
Clone the repository:
git clone https://github.com/jantman/kiosk-show-replacement.git cd kiosk-show-replacement
Install Poetry (if not already installed):
curl -sSL https://install.python-poetry.org | python3 -
Install dependencies and activate environment:
poetry install eval $(poetry env activate)
Set up the development environment:
nox -s dev-setup
Development Workflow
This project uses nox for development automation.
Available Commands
Important: First activate your Poetry environment in any new terminal session:
eval $(poetry env activate)
Then run the development commands:
# Format code with black and isort
nox -s format
# Run linting (flake8, pycodestyle)
nox -s lint
# Run type checking with mypy
nox -s type_check
# Run unit tests
nox -s test
# Run integration tests
nox -s test-integration
# Run all tests
nox -s test-comprehensive
# Build documentation
nox -s docs
# Serve documentation locally
nox -s docs-serve
# Clean build artifacts
nox -s clean
# Check for security vulnerabilities
nox -s safety
Default Development Session
Running nox without arguments will run the default development session (format, lint, test):
# After activating environment
nox
Code Style
This project uses several tools to maintain code quality:
Black: Code formatting
isort: Import sorting
flake8: Linting and style checking
mypy: Type checking
Configuration files:
.flake8: flake8 configurationpyproject.toml: Black, isort, and mypy configuration
Testing
The project uses pytest for testing with two types of tests:
Unit Tests (
tests/unit/): Test individual functions and classes in isolationIntegration Tests (
tests/integration/): Full-stack browser tests of React frontend + Flask backend
Important: First activate your Poetry environment in any new terminal session:
eval $(poetry env activate)
Unit Tests
Located in tests/unit/, these test individual functions and classes in isolation.
nox -s test
Integration Tests
Located in tests/integration/, these test the complete React frontend + Flask backend integration through a real browser using Playwright.
What Integration Tests Cover:
Full-Stack Integration: React frontend communicating with Flask backend through real HTTP requests
Real Browser Testing: Uses Playwright to control system Chrome/Chromium browser
Complete User Workflows: Full user journeys from login to dashboard interaction
Authentication Integration: Tests session-based authentication across frontend and backend
API Communication: Validates REST API endpoints work correctly with React frontend
Database Integration: Tests data persistence across the full application stack
Key Characteristics:
Use Playwright to automate a real Chrome/Chromium browser
Start both Flask backend (port 5000) and Vite frontend (port 3001) servers
Test actual user interactions (clicking, typing, form submission)
Verify complete authentication workflows and dashboard functionality
Include multi-step user journeys that span multiple application components
Test real-world scenarios that users actually experience
Examples:
Complete login workflow (visit frontend → authenticate → access dashboard → see content)
Frontend-backend authentication integration (session cookies, API calls)
Dashboard data loading from Flask backend APIs
User interface responsiveness and error handling
nox -s test-integration
Integration Test Server Logs:
Integration tests automatically capture comprehensive logs from both Flask and Vite servers during test execution. These logs are saved for debugging purposes and can be found in:
Log File Location:
test-results/integration-server-logs.txt(created in project root)Log Contents:
Flask server startup, requests, and error messages
Vite development server output and build information
Timestamped entries for easy debugging
Complete server lifecycle from startup to shutdown
Automatic Capture: Logs are automatically saved after each integration test session
Debug Access: Server logs are also dumped to console when tests fail for immediate debugging
Example log file structure:
=== INTEGRATION TEST SERVER LOGS ===
=== FLASK SERVER LOGS ===
[14:32:15.123] Flask: Starting development server...
[14:32:15.456] Flask: * Running on http://localhost:5000
[14:32:16.789] Flask: GET /api/v1/auth/login - 200
=== VITE SERVER LOGS ===
[14:32:20.123] Vite: Local: http://localhost:3001/
[14:32:20.456] Vite: ready in 1.2s
=== END OF LOGS ===
System Requirements for Integration Tests:
Integration tests require a system-installed Chrome or Chromium browser and Node.js for the frontend development server. The test framework automatically detects and uses the first available browser from these common locations:
/usr/bin/google-chrome-stable(Google Chrome on most Linux distributions)/usr/bin/chromium-browser(Chromium on Ubuntu/Debian)/usr/bin/google-chrome(Alternative Chrome location)/usr/bin/chromium(Chromium on Arch/Fedora)
Debugging Integration Test Failures:
When integration tests fail, several debugging resources are automatically generated:
Server Logs: Check
test-results/integration-server-logs.txtfor detailed Flask and Vite server outputScreenshots: Failed tests capture screenshots in
test-results/directoryConsole Output: Server logs are also dumped to the terminal on test failures
Real-time Debugging: Run tests with
-sflag to see live server output
Common Integration Test Issues:
Server Startup Failures: Check server logs for port conflicts or missing dependencies
Authentication Issues: Look for API authentication errors in Flask logs
Frontend Build Issues: Check Vite logs for compilation errors or missing assets
Network Timeouts: Increase timeouts or check server health in logs
Example debugging workflow:
# Run integration tests with verbose output
nox -s test-integration -- -s -v
# If tests fail, check the server logs
cat test-results/integration-server-logs.txt
# Run specific test for focused debugging
nox -s test-integration -- -k "test_user_login" -s
Test Configuration
pytest.ini: Pytest configurationtests/conftest.py: Shared test fixtures
Coverage
Code coverage is measured using pytest-cov. Coverage reports are generated in:
Terminal output (with
--cov-report=term-missing)HTML report in
htmlcov/directoryXML report as
coverage.xml
Database Testing
Test Database Isolation:
Tests use completely isolated databases that are separate from your development database:
Unit tests: Use a temporary SQLite database in a pytest temp directory
Integration tests: Use a fresh SQLite database per test session
Your development database: Located at
kiosk_show.db(default) is never touched by tests
This means running tests will never affect your development database or its data.
How it works:
The test fixtures in tests/conftest.py override SQLALCHEMY_DATABASE_URI to point
to a temporary file, ensuring complete isolation from your development environment.
Test Type Summary
When to use each test type:
Unit Tests (
nox -s test): Testing individual functions, classes, or small components in isolation. Fast and focused.Integration Tests (
nox -s test-integration): Testing the complete React + Flask application stack through a real browser. Use for validating user experiences and frontend-backend integration.
Test execution speed: Unit < Integration (Integration tests are slowest due to starting both servers)
Troubleshooting Browser Tests
Playwright Browser Dependencies on Arch Linux
Playwright browser tests (integration tests) may encounter issues on Arch Linux due to browser dependency conflicts. This section documents the successful solution.
Problem: Playwright fails with browser executable errors:
playwright._impl._errors.Error: Executable doesn't exist at
/home/user/.cache/ms-playwright/chromium_headless_shell-1179/chrome-linux/headless_shell
Root Cause: Playwright’s bundled browsers don’t work properly on Arch Linux due to:
Missing system library versions (libicudata.so.66, libicui18n.so.66, etc.)
Arch Linux has newer library versions than Playwright expects
Playwright doesn’t officially support Arch Linux
Failed Solutions to Avoid:
Playwright Version Downgrade: Attempting to downgrade Playwright to 1.30.0 fails due to Python 3.13 incompatibility with older dependency versions (greenlet, etc.)
System Library Symlinks: Creating global symlinks pollutes the system and is not recommended
Playwright Install Commands:
playwright installfails due to missing library versions
✅ Working Solution: System Chrome Integration
Configure Playwright to use the system-installed Google Chrome instead of bundled browsers:
Install Google Chrome:
# Install Google Chrome on Arch Linux yay -S google-chrome # or using AUR helper of choice
Configure Playwright: Update
playwright.config.jsto use system Chrome:projects: [ { name: 'chromium', use: { ...require('@playwright/test').devices['Desktop Chrome'], channel: 'chrome', // Use system Chrome executablePath: '/usr/bin/google-chrome-stable' // Explicit path }, }, ],
Verify Installation:
# Confirm Chrome is installed which google-chrome-stable # Should output: /usr/bin/google-chrome-stable
Run Tests:
eval $(poetry env activate) nox -s test-integration nox -s test-e2e
Alternative Browser Paths:
If google-chrome-stable is not available, try these alternatives:
# Check available browsers
which chromium
which google-chrome
which chromium-browser
Update executablePath in playwright.config.js accordingly:
executablePath: '/usr/bin/chromium' // For Chromium
executablePath: '/usr/bin/google-chrome' // Alternative Chrome path
Testing the Fix:
After configuration, verify browser tests work:
# Test with verbose output
eval $(poetry env activate)
nox -s test-integration -- -v
# Should see browser startup without executable errors
# Look for: "Browser launched successfully" in logs
Success Indicators:
No more “Executable doesn’t exist” errors
Browser tests launch Chrome successfully
Tests can navigate to React frontend and Flask backend
Screenshots and videos are captured properly on test failures
What This Solution Provides:
✅ Browser tests work on Arch Linux
✅ Uses stable, system-managed Chrome
✅ No global system modifications required
✅ Easy to maintain and update
✅ Works with automatic browser updates
Project Structure
kiosk-show-replacement/
├── kiosk_show_replacement/ # Main package
│ ├── __init__.py
│ ├── app.py # Flask application factory
│ ├── api/ # REST API blueprints
│ ├── auth/ # Authentication (future)
│ ├── cli/ # Command-line interface
│ ├── config/ # Configuration management
│ ├── display/ # Display/kiosk blueprints
│ ├── models/ # Database models
│ ├── slideshow/ # Slideshow management
│ ├── static/ # Static files
│ ├── templates/ # Jinja2 templates
│ └── utils/ # Utility functions
├── tests/ # Test suite
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
├── docs/ # Documentation
├── scripts/ # Utility scripts
├── noxfile.py # Development automation
├── pyproject.toml # Poetry configuration
└── README.md
Adding New Features
Create a new branch for your feature
Write tests first (TDD approach)
Implement the feature
Run the full test suite
Update documentation
Submit a pull request
Database Migrations
This project uses Flask-Migrate for database migrations.
Important: First activate your Poetry environment in any new terminal session:
eval $(poetry env activate)
Then run migration commands:
# Create a new migration
flask db migrate -m "Description of changes"
# Apply migrations
flask db upgrade
# Rollback migrations
flask db downgrade
Frontend Development
The kiosk-show-replacement project includes a modern React-based admin interface alongside the Flask backend. This section covers everything you need to know about developing and maintaining the frontend.
Frontend Technology Stack
The frontend uses modern web technologies:
Core Technologies:
React 18: Modern React with hooks and functional components
TypeScript: Type-safe JavaScript for better development experience
Vite: Fast build tool and development server
React Router: Client-side routing for single-page application
UI Framework:
React Bootstrap: Bootstrap 5 components for React
Bootstrap 5: CSS framework for responsive design
React Router Bootstrap: Integration between React Router and Bootstrap
Development Tools:
npm: Package manager for JavaScript dependencies
ESLint: JavaScript/TypeScript linting (future enhancement)
Prettier: Code formatting (future enhancement)
Frontend Project Structure
frontend/ # Frontend application root
├── package.json # npm dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── vite.config.ts # Vite build configuration
├── index.html # HTML entry point
└── src/ # Source code
├── main.tsx # Application entry point
├── App.tsx # Main app component
├── components/ # Reusable UI components
│ ├── common/ # Common components (buttons, forms)
│ ├── layout/ # Layout components (header, sidebar)
│ └── ui/ # Basic UI elements
├── pages/ # Page components
│ ├── Dashboard.tsx # Admin dashboard
│ ├── Login.tsx # Login page
│ ├── Slideshows.tsx # Slideshow management
│ └── Displays.tsx # Display management
├── contexts/ # React contexts
│ └── AuthContext.tsx # Authentication state
├── hooks/ # Custom React hooks
│ ├── useAuth.ts # Authentication hook
│ ├── useApi.ts # API client hook
│ └── useLocalStorage.ts # Local storage hook
└── types/ # TypeScript type definitions
├── api.ts # API response types
├── auth.ts # Authentication types
└── slideshow.ts # Slideshow data types
Running the Application Locally
Important
In development mode, you MUST run BOTH Flask and Vite servers.
Flask redirects /admin/ requests to the Vite dev server on port 3000.
If Vite is not running, the admin interface will not load.
Port Summary:
Port |
Service |
Purpose |
|---|---|---|
5000 |
Flask |
Backend API, redirects /admin/ to Vite in dev |
3000 |
Vite |
React frontend with hot reload (dev only) |
First-time Setup:
Initialize the database:
FLASK_APP=kiosk_show_replacement.app poetry run flask cli init-db # Default credentials: admin / admin
Starting for Development:
Start Flask backend (Terminal 1):
# From project root poetry run python run.py # API available at http://localhost:5000/api/
Start Vite frontend (Terminal 2):
cd frontend npm run dev # Admin interface at http://localhost:3000/admin/
Access the admin interface at http://localhost:3000/admin/ (or http://localhost:5000/admin/ which redirects to port 3000)
Setting Up Frontend Development
Prerequisites:
Node.js 18+ and npm (for frontend development)
Python 3.13+ and Poetry (for backend integration)
Initial Setup:
Navigate to the frontend directory:
cd frontend
Install frontend dependencies:
npm install
The frontend development server runs on http://localhost:3000 and proxies API requests to the Flask backend on http://localhost:5000.
Frontend Development Commands
Development Server:
cd frontend
npm run dev # Start development server with hot reload
Building for Production:
cd frontend
npm run build # Build optimized production assets
npm run preview # Preview production build locally
Package Management:
cd frontend
npm install # Install dependencies
npm install <package> # Add new dependency
npm install --save-dev <package> # Add development dependency
npm update # Update dependencies
npm audit # Check for security vulnerabilities
TypeScript:
cd frontend
npx tsc --noEmit # Type check without building
npx tsc --watch # Watch mode type checking
Frontend-Backend Integration
The frontend integrates with the Flask backend through several mechanisms:
Development Mode: * Vite dev server runs on port 3000 * API requests are proxied to Flask backend on port 5000 * Hot reload for immediate feedback during development
Production Mode:
* Frontend builds to kiosk_show_replacement/static/dist/
* Flask serves the built React app at /admin routes
* All assets served through Flask for single-server deployment
API Communication:
* REST API endpoints at /api/v1/*
* Session-based authentication shared between frontend and backend
* Axios client with automatic authentication handling
Configuration in vite.config.ts:
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:5000',
'/auth': 'http://localhost:5000',
'/uploads': 'http://localhost:5000'
}
},
build: {
outDir: '../kiosk_show_replacement/static/dist'
}
})
Authentication Integration
The frontend uses the same session-based authentication as the Flask backend:
Login Flow:
1. User submits credentials to /api/v1/auth/login
2. Flask creates session and returns user data
3. Frontend stores authentication state in React context
4. Subsequent API requests include session cookies automatically
Protected Routes: * React Router guards routes requiring authentication * Redirects to login page if not authenticated * Preserves intended destination for post-login redirect
API Client (useApi hook):
const api = useApi();
// Authenticated requests automatically include session
const slideshows = await api.get('/api/v1/slideshows');
const newSlideshow = await api.post('/api/v1/slideshows', data);
Adding New Frontend Features
1. Adding a New Page:
// src/pages/NewPage.tsx
import React from 'react';
import { Container } from 'react-bootstrap';
const NewPage: React.FC = () => {
return (
<Container>
<h1>New Page</h1>
{/* Page content */}
</Container>
);
};
export default NewPage;
2. Adding to Navigation:
// src/components/layout/Sidebar.tsx
import { LinkContainer } from 'react-router-bootstrap';
import { Nav } from 'react-bootstrap';
<LinkContainer to="/new-page">
<Nav.Link>New Page</Nav.Link>
</LinkContainer>
3. Adding Route:
// src/App.tsx
import { Routes, Route } from 'react-router-dom';
import NewPage from './pages/NewPage';
<Routes>
<Route path="/new-page" element={<NewPage />} />
</Routes>
4. Adding API Integration:
// Custom hook for API operations
const useNewFeature = () => {
const api = useApi();
const fetchData = async () => {
return await api.get('/api/v1/new-endpoint');
};
const createItem = async (data: NewItemType) => {
return await api.post('/api/v1/new-endpoint', data);
};
return { fetchData, createItem };
};
Common Frontend Development Tasks
Managing State:
Use React Context for global state (authentication, settings)
Use useState for local component state
Use useEffect for side effects and API calls
Handling Forms:
const [formData, setFormData] = useState({ name: '', description: '' });
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await api.post('/api/v1/endpoint', formData);
// Handle success
} catch (error) {
// Handle error
}
};
Error Handling:
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const handleApiCall = async () => {
setLoading(true);
setError(null);
try {
const result = await api.get('/api/v1/data');
// Handle success
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
Responsive Design:
Use Bootstrap classes and React Bootstrap components for responsive layouts:
<Container>
<Row>
<Col xs={12} md={6} lg={4}>
<Card>
<Card.Body>Content</Card.Body>
</Card>
</Col>
</Row>
</Container>
TypeScript Best Practices
Define Types for API Responses:
// src/types/api.ts
export interface ApiResponse<T> {
success: boolean;
data: T;
message?: string;
}
export interface Slideshow {
id: number;
name: string;
description: string;
active: boolean;
items: SlideshowItem[];
}
Use Proper Component Props Types:
interface SlideshowCardProps {
slideshow: Slideshow;
onEdit: (slideshow: Slideshow) => void;
onDelete: (id: number) => void;
}
const SlideshowCard: React.FC<SlideshowCardProps> = ({
slideshow,
onEdit,
onDelete
}) => {
// Component implementation
};
Custom Hook Type Safety:
interface UseApiResult {
get: <T>(url: string) => Promise<T>;
post: <T>(url: string, data: any) => Promise<T>;
put: <T>(url: string, data: any) => Promise<T>;
delete: (url: string) => Promise<void>;
}
const useApi = (): UseApiResult => {
// Hook implementation
};
Troubleshooting Frontend Issues
Common Issues:
Build Errors: - Check TypeScript errors:
npx tsc --noEmit- Verify all dependencies are installed:npm install- Clear node_modules and reinstall:rm -rf node_modules package-lock.json && npm installAPI Connection Issues: - Ensure Flask backend is running on port 5000 - Check Vite proxy configuration in
vite.config.ts- Verify CORS is enabled in Flask appAuthentication Problems: - Check browser cookies and session storage - Verify API endpoints return proper status codes - Test authentication flow in browser dev tools
Hot Reload Not Working: - Restart Vite dev server:
npm run dev- Check file permissions and paths - Clear browser cacheDatabase Not Initialized: - The login page will show a clear error if the database is not initialized - Run:
FLASK_APP=kiosk_show_replacement.app poetry run flask cli init-db- Check the health endpoint:curl http://localhost:5000/health/ready- The/health/readyendpoint returns"reason": "database_not_initialized"if setup is needed
Debugging Tools:
Browser DevTools for inspecting network requests and React components
React Developer Tools browser extension
TypeScript compiler for type checking
Flask debug toolbar for backend API issues
Frontend Testing (Future Enhancement)
When adding frontend tests, consider:
Unit Tests: Jest and React Testing Library for component testing
Integration Tests: Testing API integration and user workflows
E2E Tests: Playwright or Cypress for full application testing
Example test structure:
// src/components/__tests__/SlideshowCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import SlideshowCard from '../SlideshowCard';
test('renders slideshow name', () => {
const slideshow = { id: 1, name: 'Test Slideshow' };
render(<SlideshowCard slideshow={slideshow} />);
expect(screen.getByText('Test Slideshow')).toBeInTheDocument();
});
Deployment Considerations
Production Build:
cd frontend
npm run build
This creates optimized assets in kiosk_show_replacement/static/dist/ that Flask serves in production.
Environment Variables:
Create .env files for different environments:
# frontend/.env.development
VITE_API_BASE_URL=http://localhost:5000
# frontend/.env.production
VITE_API_BASE_URL=
Build Optimization:
Vite automatically optimizes bundle size
Tree shaking removes unused code
Assets are fingerprinted for caching
Source maps available for debugging
Contributing
Fork the repository
Create a feature branch
Make your changes
Add tests for new functionality
Run the test suite
Update documentation
Submit a pull request
Code Review Process
All contributions go through code review:
Automated checks (linting, testing, type checking)
Manual review by maintainers
Discussion and iteration
Approval and merge
Release Process
Update version in
pyproject.tomlUpdate
CHANGELOG.mdCreate a Git tag
Build and publish to PyPI
Create GitHub release