Local vs CI Test Consistency Guide
This guide helps ensure your tests pass both locally and in CI/Docker environments.
Common Issues and Solutions
1. Node Version Mismatch
Issue: Ensure local and Docker use the same Node version (Node 18) Solution:
# Use the project's Node version (defined in .nvmrc)
nvm use
# If not installed:
nvm install 18
nvm use 18
2. TypeScript Strictness
Issue: TypeScript checks ALL files in Docker (including tests) Solution: Always run TypeScript checks before pushing:
npm run typecheck
3. Test Environment Differences
Issue: CI runs tests differently than local Solution: Test in CI mode locally:
CI=true npm run test:run
4. Module Resolution
Issue: Dynamic require() with path aliases fails in Docker
Solution: Don’t use require() with TypeScript path aliases in tests:
// ❌ BAD - Will fail in Docker
const module = require('@/utils/someModule')
// ✅ GOOD - Use imports
import { someFunction } from '@/utils/someModule'
Recommended Workflow
Before Every Commit
# Quick quality check (lint + typecheck)
npm run quality:check:fast
Before Every Push
# Full quality check (matches Docker build)
npm run quality:check
If Tests Fail in CI
- Check Node version:
node --version # Should be v18.x.x nvm use - Run CI mode locally:
CI=true npm run test:run - Check TypeScript errors:
npm run typecheck - Run full Docker check:
npm run docker:check
TypeScript Test Guidelines
Always Type Your Test Data
// ❌ BAD - Missing types
const mockData = {
accessControl: {}, // TypeScript error in strict mode
selectedItems: undefined
}
// ✅ GOOD - Properly typed
import type { AccessControl } from '@/lib/firebase'
import type { SelectedItem } from '@/types/file-browser'
const accessControl: AccessControl = { type: 'private' }
const selectedItems: SelectedItem[] = []
Handle Possibly Undefined Values
// ❌ BAD - Can be undefined
const body = JSON.parse(fetchCall[1].body as string)
// ✅ GOOD - Null-safe
const body = JSON.parse(fetchCall[1]?.body as string)
Don’t Add Properties That Don’t Exist in Types
// ❌ BAD - SelectedItem doesn't have 'size'
const item: SelectedItem = {
path: '/file.txt',
name: 'file.txt',
type: 'file',
size: 1024 // TypeScript error
}
// ✅ GOOD - Only use defined properties
const item: SelectedItem = {
path: '/file.txt',
name: 'file.txt',
type: 'file'
}
Environment Variables
Local Development
- Uses
.env.local - Firebase emulators by default
CI/Docker
- Uses environment variables from Cloud Build
- No Firebase emulators
CI=truechanges test behavior
Quick Reference
| Command | Purpose | When to Use |
|---|---|---|
npm run lint |
Check code style | Before commits |
npm run typecheck |
Check TypeScript types | Before commits |
npm run test:run |
Run tests once | Before commits |
CI=true npm run test:run |
Run tests in CI mode | Before pushes |
npm run quality:check:fast |
Quick lint + typecheck | Before commits |
npm run quality:check |
Full CI simulation | Before pushes |
npm run docker:check |
Complete Docker build test | When tests fail in CI |
Debugging CI Failures
- Read the error carefully - CI errors often show the exact TypeScript issue
- Check the build step - Is it failing at lint, typecheck, test, or build?
- Reproduce locally - Use the exact command that failed
- Fix types, not tests - Don’t modify tests to pass; fix the underlying type issues
Prevention
- Use the correct Node version - Check
.nvmrc - Import types properly - Always import types you use in tests
- Run quality checks - Use
npm run quality:checkbefore pushing - Keep dependencies updated - Run
npm installafter pulling changes