forked from jsnbuchanan/crowd-funder-for-time-pwa
Merge branch 'master' into android-safe-area-insets
This commit is contained in:
246
.cursor/rules/app/architectural_examples.mdc
Normal file
246
.cursor/rules/app/architectural_examples.mdc
Normal file
@@ -0,0 +1,246 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user