📚 Add state management and testing guidelines documentation

Introduced new documentation files for state management and testing guidelines to enhance developer understanding and best practices.

- 📚 **Docs**: Added state management guidelines with local and global state recommendations
- 📚 **Docs**: Created testing guidelines including unit testing requirements and best practices
- 📚 **Docs**: Updated project structure documentation for clarity
This commit is contained in:
Lorenz Hilpert
2025-03-31 11:52:17 +02:00
parent 1cbabd2d7a
commit 3c110efdfa
12 changed files with 619 additions and 368 deletions

View File

@@ -0,0 +1,218 @@
# Code Style Guidelines
## General Principles
- **Readability First**: Write code that is easy to read and understand.
- **Consistency**: Follow the same patterns and conventions throughout the codebase.
- **Clean Code**: Avoid unnecessary complexity and keep functions small and focused.
## Extended Guidelines for Angular and TypeScript
This section extends the core code style principles with Angular-specific and advanced TypeScript best practices to ensure consistency and maintainability in our projects.
### Angular Enhancements
- **Change Detection**: Use the OnPush strategy by default for better performance.
- **Lifecycle Hooks**: Explicitly implement Angular lifecycle interfaces.
- **Template Management**: Keep templates concise and use the async pipe to handle observables.
- **Component Structure**: Follow best practices for component modularization to enhance readability and testability.
### TypeScript Enhancements
- **Strict Type Checking**: Enable strict mode (`strict: true`) and avoid excessive use of `any`.
- **Interfaces vs. Types**: Prefer interfaces for object definitions and use type aliases for unions and intersections.
- **Generics**: Use meaningful type parameter names and constrain generics when applicable.
- **Documentation**: Employ JSDoc comments for complex functions and generic parameters to improve code clarity.
## TypeScript Guidelines
- **Strict Typing**:
- Enable `strict: true` in tsconfig.json
- Avoid `any` unless absolutely necessary
- Use `unknown` instead of `any` when type is truly unknown
- Always specify return types for functions
- Use type inference for variable declarations where types are obvious
```typescript
// Good
const user: User = getUserById('123');
const items = ['apple', 'banana']; // Type inference is fine here
// Bad
const user: any = getUserById('123');
const items: string[] = ['apple', 'banana']; // Unnecessary type annotation
```
- **Interfaces and Types**:
- Prefer `interface` over `type` for object definitions
- Use `type` for unions, intersections, and mapped types
- Follow Angular's naming convention: `IComponentProps` for props interfaces
- Extend interfaces instead of repeating properties
- Use readonly modifiers where appropriate
```typescript
// Good
interface IBaseProps {
readonly id: string;
name: string;
}
interface IUserProps extends IBaseProps {
email: string;
}
type ValidationResult = 'success' | 'error' | 'pending';
// Bad
type UserProps = {
id: string;
name: string;
email: string;
};
```
- **Enums and Constants**:
- Use `const enum` for better performance
- Only use regular `enum` when runtime access is required
- Prefer union types for simple string literals
- **Functions and Methods**:
- Use arrow functions for callbacks and class methods
- Explicitly type parameters and return values
- Keep functions pure when possible
- Use function overloads for complex type scenarios
- Document complex functions with JSDoc
```typescript
// Good
/**
* Fetches a user by ID and transforms it to the required format
* @param id - The user's unique identifier
* @param includeDetails - Whether to include additional user details
*/
const getUser = (id: string, includeDetails = false): Promise<IUser> => {
// ...implementation
};
// Bad
function getUser(id) {
// ...implementation
}
```
- **Generics**:
- Use meaningful type parameter names (e.g., `T` for type, `K` for key)
- Constrain generic types when possible using `extends`
- Document generic parameters using JSDoc
Example:
```typescript
// Good
interface IUserProps {
id: string;
name: string;
}
interface IAdminProps extends IUserProps {
permissions: string[];
}
const enum UserRole {
Admin = 'ADMIN',
User = 'USER',
}
const getUser = <T extends IUserProps>(id: string): Promise<T> => {
// ...implementation
};
// Bad
type User = {
id: any;
name: any;
};
function getUser(id) {
// ...implementation
}
```
## Angular-Specific Guidelines
- **Components**:
- Use OnPush change detection strategy by default
- Implement lifecycle hooks interfaces explicitly
- Keep templates small and focused
- Use async pipe instead of manual subscription management
```typescript
// Good
@Component({
selector: 'app-user-list',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserListComponent implements OnInit, OnDestroy {
users$ = this.userService.getUsers().pipe(shareReplay(1));
}
// Bad
@Component({
selector: 'app-user-list',
})
export class UserListComponent {
users: User[] = [];
subscription: Subscription;
ngOnInit() {
this.subscription = this.userService.getUsers().subscribe((users) => (this.users = users));
}
}
```
## Project-Specific Preferences
- **Frameworks**: Follow best practices for Nx, Hono, and Zod.
- **Testing**: Use Jest with Spectator for unit tests and follow the Arrange-Act-Assert pattern.
- **File Naming**: Use kebab-case for filenames (e.g., `my-component.ts`).
- **Comments**: Use JSDoc for documenting functions, classes, and modules.
## Formatting
- **Indentation**: Use 2 spaces for indentation.
- **Line Length**: Limit lines to 80 characters where possible.
- **Semicolons**: Always use semicolons.
- **Quotes**: Use single quotes for strings, except when using template literals.
## Linting and Tools
- Use ESLint with the recommended TypeScript and Nx configurations.
- Prettier should be used for consistent formatting.
## Example
```typescript
// Good Example
interface User {
id: string;
name: string;
}
const getUser = (id: string): User => {
// ...function logic...
};
// Bad Example
function getUser(id) {
// ...function logic...
}
```
## References
- [Angular Style Guide](https://angular.dev/style-guide#)

View File

@@ -0,0 +1,44 @@
## Project Structure
### Directory Organization
```
ISA-Frontend/
├── apps/ # Main applications
│ └── isa-app/ # Primary application
├── libs/ # Shared libraries
│ ├── feature/ # Feature libraries with business logic
│ ├── ui/ # Reusable UI components
│ ├── core/ # Core functionality
│ └── shared/ # Cross-cutting concerns
├── generated/ # Generated API clients
└── docs/ # Documentation
```
### Library Types
- **Feature Libraries** (`libs/feature/*`)
- Smart components
- Business logic
- Route configurations
- Feature-specific services
- **UI Libraries** (`libs/ui/*`)
- Presentational components
- Pure functions
- No dependencies on feature libraries
- **Core Libraries** (`libs/core/*`)
- Authentication
- Guards
- Interceptors
- Core services
- **Shared Libraries** (`libs/shared/*`)
- Common utilities
- Shared interfaces
- Common pipes and directives

View File

@@ -0,0 +1,13 @@
# State Management
## Local State
- Use Signals for component-level state
- Keep state close to where it's used
- Document state management decisions
## Global State (NgRx)
- Use for complex application state
- Follow feature-based store organization
- Implement proper error handling

View File

@@ -0,0 +1,35 @@
# Testing Guidelines
## Unit Testing Requirements
- Test files should end with `.spec.ts`.
- Use Spectator for Component, Directive and Service tests.
- Use Jest as the test runner.
- Follow the Arrange-Act-Assert (AAA) pattern in tests.
- Mock external dependencies to isolate the unit under test.
## Example Test Structure
```typescript
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { MyComponent } from './my-component.component';
describe('MyComponent', () => {
let spectator: Spectator<MyComponent>;
const createComponent = createComponentFactory(MyComponent);
beforeEach(() => {
spectator = createComponent();
});
it('should create', () => {
expect(spectator.component).toBeTruthy();
});
it('should handle action correctly', () => {
spectator.setInput('inputProp', 'testValue');
spectator.click('button');
expect(spectator.component.outputProp).toBe('expectedValue');
});
});
```