Contributing to zmNinjaNg
This chapter covers the workflow for contributing code to the zmNinjaNg project.
Before You Start
Read the documentation
This developer guide (chapters 1-8)
AGENTS.md- Development guidelines and requirementstests/README.md- Testing documentation
Set up your development environment
# Clone the repository git clone https://github.com/your-org/zmNinjaNg.git cd zmNinjaNg/app # Install dependencies npm install # Set up test server credentials cp .env.example .env # Edit .env with your ZoneMinder server details # Run the app npm run dev # Run tests to ensure setup is correct npm test npx tsc --noEmit npm run build
Understand the codebase
Review the architecture (chapters 4-5)
Look at existing code for patterns
Run the app and explore features
Development Workflow
1. Pick or Create an Issue
Check existing issues on GitHub
If no issue exists for your change, create one
Discuss the approach before writing code (for large changes)
2. Create a Branch
# Create branch from main
git checkout main
git pull origin main
git checkout -b feature/your-feature-name
# Or for bug fixes:
git checkout -b fix/issue-123-bug-description
Branch naming:
feature/description- New featuresfix/description- Bug fixesrefactor/description- Code improvementsdocs/description- Documentation updatestest/description- Test additions/fixes
3. Write Code
Follow the patterns in this guide and AGENTS.md:
Code quality:
Keep files small and focused
Follow DRY principles
Don’t over-engineer
Write clear comments for “why”, not “what”
Testing (MANDATORY):
Write tests BEFORE or DURING implementation
Unit tests for all new logic/components
E2E tests for UI changes and user journeys
Internationalization:
No hardcoded user-facing text
Update ALL language files (en, de, es, fr, zh)
Logging:
Use component-specific log helpers
Always specify LogLevel
Never use console.*
Data attributes:
Add
data-testidto all interactive elementsUse kebab-case naming
4. Run Tests
All tests must pass before committing.
# Unit tests
npm test
# Type checking
npx tsc --noEmit
# Build
npm run build
# E2E tests (if UI changed)
npm run test:e2e -- <relevant-feature>.feature
If tests fail:
Fix the code AND/OR fix the tests
Never commit failing tests
Never skip tests
5. Commit Changes
Commit message format:
<type>: <description>
[optional body]
[optional footer]
Types:
feat:- New featurefix:- Bug fixdocs:- Documentation changestest:- Test changesrefactor:- Code restructuringchore:- Maintenance tasks
Examples:
# Good commits
git commit -m "feat: add PTZ preset buttons to monitor detail page
- Implemented preset selection UI
- Added API integration for preset recall
- Updated MonitorDetail component tests
Tests verified: npm test ✓, npm run test:e2e -- monitors.feature ✓"
git commit -m "fix: resolve infinite loop in DashboardLayout
Used refs for currentProfile and updateSettings to prevent
callback recreation on every render.
Fixes #42
Tests verified: npm test ✓"
git commit -m "docs: update testing strategy chapter with pagination examples"
# Bad commits
git commit -m "fixed bug" # ❌ Too vague
git commit -m "wip" # ❌ Not complete
git commit -m "test" # ❌ Not descriptive
Split unrelated changes:
# ❌ Don't do this:
git commit -m "fix login bug and add dark mode and update docs"
# ✅ Do this:
git commit -m "fix: resolve login token refresh race condition"
git commit -m "feat: implement dark mode theme toggle"
git commit -m "docs: document theme customization"
Reference issues:
# Reference an issue (doesn't close it)
git commit -m "feat: add monitor sorting options
Adds dropdown to sort monitors by name, ID, or status.
refs #123"
# Close an issue with the commit
git commit -m "fix: prevent duplicate profiles in list
Fixes #45"
6. Push and Create Pull Request
# Push your branch
git push origin feature/your-feature-name
# Create PR on GitHub
# - Use a clear title
# - Describe what changed and why
# - Reference related issues
# - Include test verification statement
Pull Request Template:
## Description
Brief description of changes and motivation.
Fixes #<issue-number>
## Changes Made
- Added X feature
- Refactored Y component
- Updated Z tests
## Testing
- [ ] Unit tests added/updated
- [ ] E2E tests added/updated (if UI changed)
- [ ] All tests passing
- [ ] Type checking passes
- [ ] Build succeeds
Tests verified:
- npm test ✓
- npx tsc --noEmit ✓
- npm run build ✓
- npm run test:e2e -- monitors.feature ✓
## Screenshots (if UI changed)
[Add screenshots]
## Checklist
- [ ] Followed AGENTS.md guidelines
- [ ] Updated all language files (en, de, es, fr, zh)
- [ ] Added data-testid to new interactive elements
- [ ] Used structured logging (no console.*)
- [ ] No sensitive data in logs
- [ ] Code is DRY and modular
7. Code Review
Address reviewer feedback promptly
Make requested changes in new commits (don’t force-push)
Re-run tests after changes
Update PR description if scope changes
Common review feedback:
Missing tests
Hardcoded text (needs i18n)
Missing data-testid attributes
Using console.* instead of log helpers
Not updating all language files
Complex code needs comments
Performance issues (missing memo/useMemo)
8. Merge
Once approved:
Squash commits if requested
Ensure CI passes
Maintainer will merge
Code Review Guidelines
When reviewing others’ PRs:
Functional Requirements
☐ Does it solve the stated problem?
☐ Are there edge cases not handled?
☐ Does it introduce new bugs?
Code Quality
☐ Is the code clear and readable?
☐ Are functions/components reasonably sized?
☐ Is it DRY (not duplicating existing code)?
☐ Are variable/function names descriptive?
Testing
☐ Are there unit tests?
☐ Are there E2E tests (if UI changed)?
☐ Do tests actually test the feature?
☐ Are tests clear and maintainable?
Guidelines Compliance
☐ Follows AGENTS.md requirements?
☐ All language files updated?
☐ Has data-testid attributes?
☐ Uses structured logging?
☐ No console.* statements?
Performance
☐ Are expensive operations memoized?
☐ Are list components memoized?
☐ Any unnecessary re-renders?
Security
☐ No sensitive data in logs?
☐ Credentials stored encrypted?
☐ No XSS/injection vulnerabilities?
Common Contribution Scenarios
Adding a New Feature
Create issue describing the feature
Discuss approach with maintainers
Create branch:
feature/feature-nameImplement feature with tests
Update relevant documentation
Create PR with screenshots/videos
Example: Adding a “Favorites” feature
# 1. Create branch
git checkout -b feature/monitor-favorites
# 2. Implement (in order):
# - Add favorites array to ProfileSettings type
# - Update useProfileStore with addFavorite/removeFavorite actions
# - Add star icon to MonitorCard
# - Filter monitors page to show favorites first
# - Add i18n keys to all language files
# - Add data-testid to star button
# 3. Write tests
# - Unit test: ProfileStore favorites actions
# - Unit test: MonitorCard renders star button
# - E2E test: User can favorite/unfavorite monitors
# 4. Run all tests
npm test
npm run test:e2e -- monitors.feature
# 5. Commit
git commit -m "feat: add monitor favorites feature
Users can now star monitors to mark them as favorites.
Favorites appear first in the monitors list.
- Added favorites array to ProfileSettings
- Implemented favorite toggle in MonitorCard
- Updated monitors list to sort favorites first
- Added unit and E2E tests
Tests verified: npm test ✓, npm run test:e2e -- monitors.feature ✓
refs #78"
# 6. Push and create PR
git push origin feature/monitor-favorites
Fixing a Bug
Create/find issue describing the bug
Write a failing test that reproduces the bug
Fix the bug
Verify test now passes
Create PR
Example: Fixing a stream connection bug
# 1. Create branch
git checkout -b fix/stream-reconnect-loop
# 2. Write failing test
# - test/features/monitors.feature: Add scenario that triggers bug
# - Verify test fails
# 3. Fix the bug
# - Identify root cause (connkey regeneration infinite loop)
# - Apply ref pattern to fix
# 4. Verify
npm test
npm run test:e2e -- monitors.feature # Test now passes
# 5. Commit
git commit -m "fix: prevent infinite connkey regeneration loop
Used refs for currentProfile and regenerateConnection to avoid
creating new callback instances on every render.
The bug caused ResizeObserver to repeatedly trigger connection
regeneration, flooding the server with requests.
Fixes #92
Tests verified: npm test ✓, npm run test:e2e -- monitors.feature ✓"
Updating Documentation
Create branch:
docs/descriptionMake changes
Verify documentation renders correctly
Create PR
git checkout -b docs/improve-testing-guide
# Make changes to docs/developer-guide/06-testing-strategy.md
git commit -m "docs: add pagination testing examples to testing strategy
Added section on testing infinite scroll and useInfiniteQuery patterns"
git push origin docs/improve-testing-guide
Style Guide
TypeScript
// ✅ Use interfaces for props
interface MonitorCardProps {
monitor: Monitor;
onPress?: () => void;
}
// ✅ Use type for unions/intersections
type Status = 'loading' | 'success' | 'error';
// ✅ Explicit return types for public functions
export function calculateMaxCols(width: number): number {
return Math.floor(width / MIN_CARD_WIDTH);
}
// ✅ Avoid any - use unknown or specific type
function processData(data: unknown) {
if (typeof data === 'string') {
// ...
}
}
React Components
// ✅ Function declaration for named exports
export function MonitorCard({ monitor }: MonitorCardProps) {
// ...
}
// ✅ Arrow function for inline components
const Item = ({ name }: { name: string }) => <div>{name}</div>;
// ✅ Destructure props in parameter
export function MonitorCard({ monitor, onPress }: MonitorCardProps) {
// Not: function MonitorCard(props) { const { monitor } = props; }
}
// ✅ Early returns for guards
export function MonitorCard({ monitor }: MonitorCardProps) {
if (!monitor) return null;
if (monitor.deleted) return <DeletedCard />;
return <Card>...</Card>;
}
Naming Conventions
// Components: PascalCase
function MonitorCard() {}
// Hooks: camelCase with 'use' prefix
function useMonitorStream() {}
// Constants: UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
const API_BASE_URL = 'https://api.example.com';
// Variables/functions: camelCase
const monitorList = [];
function fetchMonitors() {}
// Files:
// - Components: PascalCase (MonitorCard.tsx)
// - Hooks: camelCase (useMonitorStream.ts)
// - Utils: camelCase (formatDate.ts)
// - Tests: Same as source (MonitorCard.test.tsx)
Mobile Development
zmNinjaNg is a cross-platform app built with Capacitor.
Prerequisites
Node.js: 18+
Android Studio: For Android development (installs Android SDK/JDK)
Xcode: For iOS development (macOS only)
Running on Device/Emulator
The project includes helper scripts in package.json to streamline
the mobile workflow.
Android:
# Sync web assets to Android project and open Android Studio
npm run android
# Just sync (if you already have Android Studio open)
npm run android:sync
# View logs from connected Android device
npm run android:logs
iOS:
# Sync web assets to iOS project and open Xcode
npm run ios
# Just sync
npm run ios:sync
Workflow
Make changes to the web code (
src/).Run
npm run buildto compile the web assets.Run
npm run android:syncornpm run ios:syncto copy the built assets to the native projects.Run/Debug via Android Studio or Xcode.
[!TIP] Live Reload: For faster development, you can configure Capacitor to load the dev server URL instead of the built bundle. Edit
capacitor.config.ts:server: { url: 'http://YOUR_LOCAL_IP:5173', cleartext: true }Remember to remove this before building for release!
Getting Help
Questions about the codebase: Create a GitHub discussion
Bug reports: Create a GitHub issue
Feature requests: Create a GitHub issue
Security issues: Email maintainers (don’t create public issue)
License
By contributing, you agree that your contributions will be licensed under the same license as the project.
Recognition
Contributors are listed in:
CONTRIBUTORS.mdRelease notes for features and fixes
Git commit history