This document outlines the testing strategy, current test coverage, and future testing goals for the gh-manager-cli project.
- Test Infrastructure
- Running Tests
- Current Test Coverage
- Testing Strategies
- Future Testing Goals
- Known Limitations
- Test Runner: Vitest v2.1.9
- Testing Library: ink-testing-library (for React/Ink components)
- Coverage Tool: @vitest/coverage-v8
- Assertion Library: Vitest's built-in expect API
tests/
├── config.test.ts # Configuration management tests
├── github.test.ts # GitHub API helper tests
├── utils.test.ts # Utility function tests
├── apolloMeta.test.ts # Apollo cache metadata tests
└── ui/
├── RepoRow.test.tsx # Repository row component (with balanced spacing)
├── RepoListHeader.test.tsx # Repository list header
├── RepoListHeaderVisibility.test.tsx # Visibility filter display tests
├── FilterInput.test.tsx # Filter input component
├── SlowSpinner.test.tsx # Loading spinner component
├── DeleteModal.test.tsx # Delete confirmation modal
├── ArchiveModal.test.tsx # Archive/unarchive modal
├── LogoutModal.test.tsx # Logout confirmation modal
├── SortModal.test.tsx # Sort selection modal (to be implemented)
└── VisibilityModal.test.tsx # Visibility filter modal (to be implemented)
# Run all tests
pnpm test
# Run tests with coverage report
pnpm test:coverage
# Run tests in watch mode (if configured)
pnpm vitest
# Run specific test file
pnpm vitest tests/config.test.ts
# Run tests matching a pattern
pnpm vitest --grep "config"After running pnpm test:coverage, you'll see:
- Statement coverage
- Branch coverage
- Function coverage
- Line coverage
- Uncovered line numbers for each file
- Total Test Files: 12
- Total Tests: 82+
- Overall Coverage: ~7.62% (low due to untested main components)
- Utilities Coverage: 100% (4/4 utilities tested)
- Component Coverage: 62% (8/13 components tested, 2 new modals pending tests)
Coverage: 100% | Tests: 23
Testing:
getConfigPath()- Returns correct config file pathreadConfig()- Reads and parses configuration fileswriteConfig()- Writes configuration with proper permissionsgetTokenFromEnv()- Retrieves tokens from environment variablesgetStoredToken()- Gets stored authentication tokensstoreToken()- Persists tokens securelyclearStoredToken()- Removes tokens while preserving other settingsgetUIPrefs()- Retrieves UI preferencesstoreUIPrefs()- Persists UI preferences with merging
Coverage: 100% | Tests: 13
Testing:
truncate()function:- Strings shorter than max length
- Strings longer than max length with ellipsis
- Exact length strings
- Default max value (80 chars)
- Empty strings
- Very small max values
formatDate()function:- "today" formatting
- "yesterday" formatting
- "X days ago" (within a week)
- "X weeks ago" (within a month)
- "X months ago" (within a year)
- "X years ago"
- Future date handling
Coverage: 4.91% (mostly untested) | Tests: 8
Testing:
makeClient()- Creates GraphQL client with authenticationgetViewerLogin()- Fetches authenticated user's loginfetchViewerOrganizations()- Retrieves user's organizations- Handling of null values in organization names
- Empty organization lists
Not Tested: Most GraphQL query functions (fetchViewerReposPage, etc.)
Coverage: 100% | Tests: 2
Testing:
- Cache key generation with TTL suffix
- TTL checking for stale cache entries
Coverage: 74.5% | Tests: 1
- Renders repository name and metadata correctly
- Balanced spacing (1 line above, 1 line below) implemented
Coverage: 100% | Tests: 8
- Personal account context display
- Organization context display (with/without name)
- Sort indicator display (field and direction)
- Fork tracking status display (renamed to "Fork Status - Commits Behind")
- Filter display when not searching
- Search mode display
- All sort keys and directions
Coverage: New | Tests: Various
- Visibility filter display in header
- All visibility states (All, Public, Private, Internal)
- Enterprise vs standard account detection
Coverage: 100% | Tests: 6
- Filter label rendering
- Current filter value display
- Placeholder text when empty
- onChange callback invocation
- onSubmit callback invocation
- Works without optional debug prop
Coverage: 100% | Tests: 5
- Initial spinner frame rendering
- Frame cycling over time
- Looping back to first frame
- 500ms update interval
- Cleanup on unmount
Coverage: Partial | Tests: 4
- Verification code generation logic
- Code validation
- Error states
Coverage: 64.42% | Tests: 6
- Archive confirmation for non-archived repos
- Unarchive confirmation for archived repos
- Button display
- Cancel action on Escape key
- Cancel action on 'C' key
- Null repository handling
Coverage: 66.66% | Tests: 6
- Logout confirmation message
- Button display
- Keyboard shortcuts help text
- Cancel on Escape key
- Cancel on 'C' key
- Default focus state
Components using Ink's useInput hook require special handling:
// Mock the useInput hook to avoid stdin.ref issues
vi.mock('ink', async () => {
const actual = await vi.importActual('ink');
return {
...actual,
useInput: vi.fn()
};
});
// In tests, configure mock behavior
beforeEach(async () => {
const ink = await import('ink');
mockUseInput = (ink as any).useInput;
mockUseInput.mockReset();
});For components using ink-text-input:
vi.mock('ink-text-input', async () => {
const React = await import('react');
const { Text } = await import('ink');
return {
default: vi.fn(({ value, placeholder, onChange, onSubmit }: any) => {
// Mock implementation
return React.createElement(Text, {}, value || placeholder);
})
};
});Focus on testing:
- UI rendering and display
- Conditional rendering
- Props handling
- Basic state changes
-
SortModal.tsx - Sort selection modal
- Modal rendering with all sort options
- Description display for each sort option
- Keyboard navigation (arrow keys, Enter, Esc)
- Selection callback
-
VisibilityModal.tsx - Visibility filter modal
- Modal rendering with visibility options
- Enterprise detection (showing Internal option)
- Keyboard navigation
- Selection callback
- Current filter highlighting
-
RepoList.tsx - Main component state management and logic
- Pagination and infinite scroll
- Sorting functionality with modal interface
- Visibility filtering (server-side)
- Rate limit handling
- Error states
- Footer reorganization (3 lines)
- Updated keyboard shortcuts (Del/Backspace for delete)
-
App.tsx - Application initialization and routing
- Token bootstrap flow
- Initial data loading
-
Visibility Filtering Flow
- Opening modal with
Vkey - Selecting filter option
- Server-side API calls with privacy parameter
- Pagination with filtered results
- Config persistence
- Opening modal with
-
Sort Modal Flow
- Opening modal with
Skey - Selecting sort option
- Server refresh with new sort
- Config persistence
- Opening modal with
-
Footer Navigation
- Testing 3-line footer layout
- Keyboard shortcut verification
- Updated Delete key (Del/Backspace without Ctrl)
- Error boundary behavior
-
OrgSwitcher.tsx - Organization switching
- Organization list display
- Context switching
- Persistence of selected org
- InfoModal.tsx - Repository information display
- SyncModal.tsx - Fork synchronization flow
- DeleteModal.tsx - Expand to test full deletion flow
Expand github.test.ts to cover:
fetchViewerReposPage()with various parameters- Organization repository queries
- Search functionality
- Rate limit information parsing
- Error handling for API failures
-
Modal Keyboard Navigation
- Arrow key navigation between buttons
- Enter key submission
- Escape key cancellation
-
Search Flow
- Triggering server-side search (3+ characters)
- Debouncing behavior
- Result display and navigation
-
Repository Actions
- Archive/unarchive flow
- Delete confirmation flow
- Fork sync flow
- Reducer logic in RepoList
- Action dispatching
- State updates and side effects
- Apollo cache persistence
- Cache invalidation
- Stale-while-revalidate behavior
- Group related tests into describe blocks
- Add more descriptive test names
- Create shared test utilities and fixtures
- Create centralized mock data factories
- Standardize mock repository objects
- Create reusable GraphQL response mocks
- Aim for 80%+ coverage on critical paths
- 100% coverage on utility functions
- 70%+ coverage on UI components
- Focus on business logic over UI details
- Test virtualization with large datasets
- Memory leak detection
- Render performance metrics
- Keyboard navigation
- Screen reader compatibility
- Focus management
- Cannot fully test keyboard interactions due to stdin.ref incompatibility
- Limited to testing component rendering and basic callbacks
- Recommend integration/e2e tests for full keyboard flow
- React state updates in tests may not reflect immediately
- Need careful handling of async operations
- May require
waitForutilities or timers
- Limited ability to test terminal-specific features
- Box dimensions and wrapping behavior
- Color output verification
- Complex to mock entire GraphQL responses
- Need to maintain mock data in sync with schema
- Pagination and cursor-based queries are challenging
- Write tests alongside new features - Don't let test debt accumulate
- Test behavior, not implementation - Focus on what the component does
- Use descriptive test names - Should read like documentation
- Keep tests isolated - Each test should be independent
- Mock external dependencies - Don't make real API calls
- Test edge cases - Empty states, errors, loading states
- Maintain test fixtures - Keep mock data organized and reusable
Consider adding GitHub Actions workflow for automated testing:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'pnpm'
- run: pnpm install
- run: pnpm test:coverage
- uses: codecov/codecov-action@v3 # Optional: Upload coverageLast Updated: December 2024 Total Tests: 82 | Test Files: 11 | Coverage: 7.62% overall (100% for tested utilities)