Claude
Skills
Sign in
Back

test-coverage

Included with Lifetime
$97 forever

MUST be used whenever fixing test coverage for a Flows app to meet the 80% line coverage hard gate. This skill finds AND fixes coverage gaps — it configures tooling, writes missing tests, covers untested paths, and refactors code for testability. It does not just report. Triggers: test coverage, fix tests, write tests, add tests, coverage fix, 80% coverage, coverage gate, missing tests, testability, vitest coverage, jest coverage.

Code Review

What this skill does


# Test Coverage Fix

Fix test coverage for **$ARGUMENTS** (or the whole app if no argument is given). This skill enforces the **80% line coverage hard gate** required for Flows app approval by finding AND fixing coverage gaps. Work through every step in order.

---

## Step 1 — Verify test framework and coverage tooling

Check that the project has a working test framework with coverage configured:

```bash
# Check for vitest or jest in package.json
grep -E "(vitest|jest)" package.json

# Check for coverage configuration
cat vitest.config.ts 2>/dev/null || cat vitest.config.js 2>/dev/null || cat jest.config.ts 2>/dev/null || cat jest.config.js 2>/dev/null
```

Verify:
- A test framework (Vitest or Jest) is installed and configured
- The config file has a `coverage` section (e.g. `coverage: { provider: 'v8', ... }` in vitest.config.ts)
- A coverage reporter is configured (at least `text` and `lcov` or `json-summary`)

**If coverage tooling is not configured, fix it now:**

1. Install the coverage provider:
```bash
pnpm add -D @vitest/coverage-v8
```

2. Add the coverage configuration to `vitest.config.ts`. Read the existing config file, then add the `coverage` section inside `test`:

```typescript
// vitest.config.ts — minimum coverage configuration to add
test: {
  coverage: {
    provider: 'v8',
    reporter: ['text', 'text-summary', 'lcov'],
    include: ['src/**/*.{ts,tsx}'],
    exclude: [
      'src/**/*.test.{ts,tsx}',
      'src/**/*.spec.{ts,tsx}',
      'src/**/vite-env.d.ts',
      'src/main.tsx',
    ],
  },
}
```

Write the updated config file. If no vitest.config.ts exists at all, create one with the full `defineConfig` wrapper.

---

## Step 2 — Validate coverage scope

The 80% threshold applies to **all `.ts` and `.tsx` files** under `src/`, excluding only:
- Test files (`*.test.ts`, `*.test.tsx`, `*.spec.ts`, `*.spec.tsx`)
- Type declaration files (`vite-env.d.ts`)
- The entry point (`main.tsx`)

Apps must **not** exclude pages, components, hooks, or other production code from coverage measurement.

```bash
# Check what files are excluded from coverage in the config
grep -A 20 "exclude" vitest.config.ts 2>/dev/null || grep -A 20 "exclude" vitest.config.js 2>/dev/null

# Check for coveragePathIgnorePatterns in jest config
grep -A 10 "coveragePathIgnorePatterns\|collectCoverageFrom" jest.config.ts 2>/dev/null
```

**If the config excludes production files, fix it now:**

Remove any exclusion that hides production code from coverage measurement. Only test files, type declarations, and the entry point should be excluded. Rewrite the `exclude` array to contain only:

```typescript
exclude: [
  'src/**/*.test.{ts,tsx}',
  'src/**/*.spec.{ts,tsx}',
  'src/**/vite-env.d.ts',
  'src/main.tsx',
],
```

Specifically remove any exclusions for:
- `src/pages/` or `src/components/` or `src/hooks/` — **NOT allowed**
- Specific feature files — **NOT allowed** unless they are generated code
- `src/**/*.tsx` (all components) — **NOT allowed**, this hides the majority of the app

Write the corrected config file.

---

## Step 3 — Run tests and collect coverage

```bash
# Try common coverage commands based on project setup
npx vitest run --coverage 2>/dev/null || npx jest --coverage 2>/dev/null || npm test -- --coverage 2>/dev/null
```

Record the coverage summary:
- **Statements:** X%
- **Branches:** X%
- **Functions:** X%
- **Lines:** X%

**Hard gate:** Overall line coverage must be **at least 80%**. Apps below this threshold are listed as **must fix**.

**If tests fail to run, fix them now:**

Common fixes:
- **Missing imports:** Read the failing test file, add the missing import statement, write the fixed file.
- **Broken mocks:** Read the test to understand what is being mocked. Fix the mock to match the current API of the mocked module.
- **Outdated snapshots:** Run `npx vitest run --update` to update snapshots, then review the diff to ensure correctness.
- **Missing dependencies:** Run `pnpm add -D <missing-package>` for any test utilities not yet installed.
- **Config errors:** Read the config file, fix syntax or option errors, write the corrected file.

Re-run tests after each fix until all tests pass. Then record the coverage summary.

---

## Step 4 — Find and write missing test files

For every non-trivial `.ts`/`.tsx` file under `src/`, check whether a corresponding test file exists:

```bash
# List all production files and check for test counterparts
for file in $(find src -name "*.ts" -o -name "*.tsx" | grep -v ".test." | grep -v ".spec." | grep -v "node_modules" | grep -v "vite-env" | sort); do
  base="${file%.*}"
  ext="${file##*.}"
  dir=$(dirname "$file")
  filename=$(basename "$base")

  # Check for test file in same directory or __tests__ directory
  test_exists="false"
  for pattern in "${base}.test.${ext}" "${base}.spec.${ext}" "${base}.test.ts" "${base}.spec.ts" "${dir}/__tests__/${filename}.test.${ext}" "${dir}/__tests__/${filename}.spec.${ext}"; do
    if [ -f "$pattern" ]; then
      test_exists="true"
      break
    fi
  done

  if [ "$test_exists" = "false" ]; then
    echo "NO TEST: $file"
  fi
done
```

Categorize each file without a test:
- **Services, hooks, utils, contexts, ViewModel hooks** — **Write the test file now** (see below)
- **Pure presentational components** with no logic — Mark as **N/A** (no test required)
- **Barrel exports** (`index.ts` that only re-exports) — Mark as **N/A**
- **Type-only files** (`.d.ts`, files with only type/interface exports) — Mark as **N/A**

**For each file missing a test, create a comprehensive test file.** Use context injection for dependency mocking where the production code supports it. If the production code uses hard-coded imports, note this as a testability concern but still write the test using `vi.mock` with a justification comment. Follow this process for each:

1. **Read the source file** to understand its exports, dependencies, and logic.
2. **Create a `.test.ts` or `.test.tsx` file** in the same directory as the source file.
3. **Write tests covering:** happy path, error path, empty state, and edge cases.

Use the right testing pattern for each file type:

**For hooks:**
- Test with `renderHook` from `@testing-library/react`
- Wrap with necessary providers (QueryClientProvider, custom context providers, etc.)
- Test initial state, loading state, success state, and error state
- Example structure:
```typescript
import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
// import the hook

const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('useMyHook', () => {
  it('returns data on success', async () => {
    const { result } = renderHook(() => useMyHook(), { wrapper: createWrapper() });
    await waitFor(() => expect(result.current.data).toBeDefined());
  });

  it('handles errors', async () => {
    // set up error condition
    const { result } = renderHook(() => useMyHook(), { wrapper: createWrapper() });
    await waitFor(() => expect(result.current.error).toBeDefined());
  });
});
```

**For services/utils:**
- Test with direct function calls
- Mock CDF SDK responses where needed
- Test return values, side effects, and thrown errors
- Example structure:
```typescript
import { describe, it, expect, vi } from 'vitest';
// import the service/util functions

describe('myService', () => {
  it('returns expected result for valid input', () => {
    const result = myFunction(validInput);
    expect(result).toEqual(expectedOutput);
  });

  it('throws on invalid input', () => {
    expect(() => myFunction(invalidInput)).toThrow();
  });
});
```

**For components with logic:**
- Test with `render` from `@testing-library/react`
- Verify loading, error, and data states
-

Related in Code Review