This document provides comprehensive information about the test suite for Ark Studio, a time tracking and project management application built with Angular 19+ and Firebase.
The test suite achieves 80%+ code coverage across all major components and services:
-
✅ AuthService - 15 test cases
- Authentication flow (login, logout)
- User state management
- User information retrieval
- Observable stream handling
- Error handling
-
✅ ProjectService - 25 test cases
- CRUD operations (Create, Read, Update, Delete)
- Real-time data synchronization
- User-scoped queries
- Error handling
- Edge cases (empty lists, long names, zero duration)
-
✅ ClientService - 22 test cases
- Client management operations
- Rate calculations
- Real-time updates
- Alphabetical sorting
- Edge cases (special characters, unicode, zero rates)
-
✅ TimeTrackingService - 45 test cases
- Clock in/out operations
- Break management
- Time calculations
- Session tracking
- Manual time entry creation
- Duration formatting
- Edge cases (negative time, concurrent operations)
-
✅ FileUploadService - 30 test cases
- Video metadata extraction
- File validation
- Size formatting
- Duration formatting
- Error handling
- Memory management
- ✅ AuthGuard - 12 test cases
- Route protection
- Authentication checks
- Redirection logic
- Multiple route protection
- State changes
- ✅ ProjectsComponent - 15+ test cases
- Component initialization
- File selection and processing
- Project CRUD operations
- Modal management
- Subscription cleanup
- Integration workflows
- ✅ Complete project workflow
- ✅ Time tracking workflow with breaks
- ✅ Multi-client project management
- ✅ Error recovery scenarios
- ✅ Data consistency checks
- ✅ Real-world freelancer scenarios
client/src/app/
├── auth/
│ ├── auth.service.spec.ts (AuthService tests)
│ └── auth-guard.ts.guard.spec.ts (Guard tests)
├── services/
│ ├── project.service.spec.ts (ProjectService tests)
│ ├── client.service.spec.ts (ClientService tests)
│ ├── time-tracking.service.spec.ts (TimeTrackingService tests)
│ └── file-upload.service.spec.ts (FileUploadService tests)
├── components/
│ └── projects/
│ └── projects.component.spec.ts (Component tests)
└── tests/
└── integration.spec.ts (Integration tests)
cd client
ng testng test --code-coverageng test --watch=false --browsers=ChromeHeadlessng test --include='**/auth.service.spec.ts'ng test --include='**/integration.spec.ts'Purpose: Test individual functions and methods in isolation
Examples:
- Service method returns correct data type
- Function handles null/undefined inputs
- Calculations produce expected results
- Error handling throws correct errors
Coverage: All services, utilities, and standalone functions
Purpose: Test component behavior and user interactions
Examples:
- Component initializes correctly
- User interactions trigger expected methods
- Form validation works correctly
- Modal states change appropriately
Coverage: All major components (Projects, Clients, Clocking, etc.)
Purpose: Test multiple components/services working together
Examples:
- Complete user workflows (create client → project → track time)
- Data consistency across multiple entities
- Real-world scenarios (weekly earnings calculation)
- Error recovery in complex workflows
Coverage: Critical user journeys and workflows
Purpose: Test boundary conditions and unusual inputs
Examples:
- Zero values (duration, rate)
- Very large values (file sizes, durations)
- Special characters in names
- Concurrent operations
- Network failures
Coverage: All services and critical components
it('should calculate earnings correctly', () => {
// Arrange
const duration = 3600;
const rate = 0.05;
// Act
const earnings = duration * rate;
// Assert
expect(earnings).toBe(180);
});const authServiceSpy = jasmine.createSpyObj('AuthService', ['getUserId']);
authServiceSpy.getUserId.and.returnValue('user-123');it('should emit user state changes', (done) => {
service.user$.subscribe(user => {
expect(user).toBeTruthy();
done();
});
});it('should create project', async () => {
const result = await service.addProject(projectData);
expect(result).toBeTruthy();
});afterEach(() => {
fixture.destroy();
});- Each test should be independent and not rely on others
- Tests should be able to run in any order
- Use
beforeEachto set up clean state
- Test names should clearly describe what is being tested
- Use descriptive variable names
- Follow "should" pattern:
it('should calculate correctly')
- Aim for 80%+ code coverage
- Test both success and failure paths
- Include edge cases and boundary conditions
- Tests should run quickly
- Mock external dependencies (Firebase, HTTP)
- Avoid real network calls or database operations
- Keep tests simple and focused
- Avoid duplication with helper functions
- Update tests when code changes
- ✅ Google Sign-in success/failure
- ✅ Sign-out operations
- ✅ User state persistence
- ✅ User information retrieval
- ✅ Create project with all fields
- ✅ Create project with minimal fields
- ✅ Update project status
- ✅ Delete project with confirmation
- ✅ Link project to client
- ✅ File upload and metadata extraction
- ✅ Create client with rate
- ✅ Update client rate
- ✅ Delete client
- ✅ List clients alphabetically
- ✅ Handle various rate values
- ✅ Clock in/out workflow
- ✅ Start/end break workflow
- ✅ Calculate session time
- ✅ Calculate daily work time
- ✅ Handle breaks during work
- ✅ Prevent negative time
- ✅ Format duration displays
- ✅ Complete project creation workflow
- ✅ Multi-day time tracking
- ✅ Weekly earnings calculation
- ✅ Multi-client project management
- ✅ Error recovery
- ✅ Data consistency
- name: Run tests
run: |
cd client
npm test -- --watch=false --code-coverage --browsers=ChromeHeadless{
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}fit('should run only this test', () => {
// Test code
});xit('should skip this test', () => {
// Test code
});ng test --browsers=ChromeThen open Chrome DevTools and set breakpoints
ng test --code-coverage
open coverage/index.htmlProblem: Test times out waiting for async operation
Solution: Use done() callback or increase timeout
it('should complete async', (done) => {
asyncOperation().then(() => {
expect(true).toBe(true);
done();
});
}, 10000); // 10 second timeoutProblem: Tests affect each other due to lingering subscriptions
Solution: Unsubscribe in afterEach
afterEach(() => {
subscription?.unsubscribe();
});Problem: Spy method not being invoked Solution: Ensure fixture.detectChanges() is called
fixture.detectChanges();
expect(spy).toHaveBeenCalled();- E2E tests with Cypress/Playwright
- Visual regression tests
- Performance benchmarking tests
- Accessibility testing (axe-core)
- Load testing for statistics service
- Mobile responsiveness tests
- 90%+ coverage for all services
- 85%+ coverage for all components
- 100% coverage for critical paths
- Mutation testing implementation
- Create test file next to source file (
*.spec.ts) - Follow existing test structure and patterns
- Include unit tests for new functions
- Add integration tests for new workflows
- Ensure tests pass before committing
- Tests are independent
- Tests have clear descriptions
- Both success and failure cases covered
- Edge cases included
- Mocks are properly configured
- Cleanup is handled correctly
- Tests run quickly (<100ms each)
For questions about the test suite, contact:
- Maintainer: Robert Oltean
- GitHub: @RobertOltean314
Last Updated: February 2026 Test Coverage: 80%+ Total Test Cases: 150+ Test Execution Time: ~30 seconds