You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

246 lines
5.8 KiB

# Time Safari Architecture — Examples and Testing
> **Agent role**: Reference this file for architectural examples and
testing patterns when working with TimeSafari architecture.
## Error Handling Patterns
### Global Error Handler
```typescript
// main.ts
app.config.errorHandler = (err, instance, info) => {
const componentName = instance?.$options?.name || 'Unknown';
logger.error(`[${componentName}] Vue error`, err, info);
};
window.addEventListener('unhandledrejection', (event) => {
logger.error('[Global] Unhandled promise rejection', event.reason);
});
```
### Platform-Specific Error Wrapping
```typescript
// services/platforms/CapacitorPlatformService.ts
export class CapacitorPlatformService {
async getFileContents(path: string): Promise<string> {
try {
const result = await Filesystem.readFile({
path: path,
encoding: 'utf8'
});
return result.data;
} catch (error) {
logger.error('[Capacitor API Error] Failed to read file', error, path);
throw new Error(`Failed to read file: ${path}`);
}
}
}
```
## Testing Patterns
### Platform-Specific Test Skipping
```typescript
// tests/QRScanner.test.ts
describe('QRScanner Service', () => {
test('should start scanning on web', async () => {
test.skip(process.env.VITE_PLATFORM !== 'web', 'Web-only test');
const scanner = new WebInlineQRScanner();
await scanner.startScanning();
// Assert scanning started
});
test('should start scanning on mobile', async () => {
test.skip(process.env.VITE_PLATFORM !== 'capacitor', 'Mobile-only test');
const scanner = new CapacitorQRScanner();
await scanner.startScanning();
// Assert scanning started
});
});
```
### Mock Service Testing
```typescript
// tests/mocks/QRScannerMock.ts
export class QRScannerMock implements QRScannerService {
private isScanning = false;
private listeners: Map<string, Function[]> = new Map();
async startScanning(): Promise<void> {
this.isScanning = true;
this.emit('scanningStarted');
}
async stopScanning(): Promise<void> {
this.isScanning = false;
this.emit('scanningStopped');
}
addListener(event: string, callback: Function): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(callback);
}
removeListener(event: string, callback: Function): void {
const callbacks = this.listeners.get(event);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
private emit(event: string, ...args: any[]): void {
const callbacks = this.listeners.get(event);
if (callbacks) {
callbacks.forEach(callback => callback(...args));
}
}
getScanningState(): boolean {
return this.isScanning;
}
}
```
## Integration Examples
### Service Composition
```typescript
// services/QRScannerService.ts
export class QRScannerService {
constructor(
private platformService: PlatformService,
private notificationService: NotificationService
) {}
async startScanning(): Promise<void> {
try {
await this.platformService.startCamera();
this.notificationService.show('Camera started');
} catch (error) {
this.notificationService.showError('Failed to start camera');
throw error;
}
}
}
```
### Component Integration
```typescript
// components/QRScannerDialog.vue
export default class QRScannerDialog extends Vue {
@Inject() private qrScannerService!: QRScannerService;
async mounted() {
try {
await this.qrScannerService.startScanning();
} catch (error) {
this.$notify.error('Failed to start scanner');
}
}
beforeDestroy() {
this.qrScannerService.stopScanning();
}
}
```
## Best Practices
### Service Design
- Keep services focused and single-purpose
- Use dependency injection for service composition
- Implement proper error handling and logging
- Provide clear interfaces and contracts
### Testing Strategy
- Test platform-specific behavior separately
- Use mocks for external dependencies
- Test error conditions and edge cases
- Validate service contracts and interfaces
### Error Handling
- Log errors with appropriate context
- Provide user-friendly error messages
- Implement graceful degradation
- Handle platform-specific error scenarios
---
**See also**:
- `.cursor/rules/app/architectural_decision_record.mdc` for
core architecture principles
- `.cursor/rules/app/architectural_implementation.mdc` for
implementation details
- `.cursor/rules/app/architectural_patterns.mdc` for core patterns
**Status**: Active examples and testing guide
**Priority**: Medium
**Estimated Effort**: Ongoing reference
**Dependencies**: architectural_patterns.mdc
**Stakeholders**: Development team, Testing team
## Model Implementation Checklist
### Before Architectural Examples
- [ ] **Pattern Selection**: Choose appropriate architectural pattern for the use
case
- [ ] **Service Design**: Plan service structure and dependencies
- [ ] **Testing Strategy**: Plan testing approach for the example
- [ ] **Error Handling**: Plan error handling and logging strategy
### During Architectural Examples
- [ ] **Service Implementation**: Implement focused, single-purpose services
- [ ] **Dependency Injection**: Use proper dependency injection patterns
- [ ] **Error Handling**: Implement proper error handling and logging
- [ ] **Interface Design**: Provide clear interfaces and contracts
### After Architectural Examples
- [ ] **Testing Execution**: Test platform-specific behavior separately
- [ ] **Service Validation**: Validate service contracts and interfaces
- [ ] **Error Testing**: Test error conditions and edge cases
- [ ] **Documentation**: Update architectural examples documentation