diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..db840f7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,114 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Test & Coverage + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests with coverage + run: pnpm run test:coverage + + - name: Check coverage threshold + run: | + # Extract coverage percentages from coverage summary + LINES=$(jq '.total.lines.pct' coverage/coverage-summary.json | cut -d. -f1) + FUNCTIONS=$(jq '.total.functions.pct' coverage/coverage-summary.json | cut -d. -f1) + BRANCHES=$(jq '.total.branches.pct' coverage/coverage-summary.json | cut -d. -f1) + STATEMENTS=$(jq '.total.statements.pct' coverage/coverage-summary.json | cut -d. -f1) + + echo "Coverage: Lines=$LINES% Functions=$FUNCTIONS% Branches=$BRANCHES% Statements=$STATEMENTS%" + + if [ "$LINES" -lt 40 ]; then + echo "❌ Lines coverage ($LINES%) is below 40% threshold" + exit 1 + fi + if [ "$FUNCTIONS" -lt 40 ]; then + echo "❌ Functions coverage ($FUNCTIONS%) is below 40% threshold" + exit 1 + fi + if [ "$BRANCHES" -lt 40 ]; then + echo "❌ Branches coverage ($BRANCHES%) is below 40% threshold" + exit 1 + fi + if [ "$STATEMENTS" -lt 40 ]; then + echo "❌ Statements coverage ($STATEMENTS%) is below 40% threshold" + exit 1 + fi + + echo "✅ All coverage thresholds met!" + + - name: Upload coverage reports + uses: codecov/codecov-action@v4 + if: always() + with: + file: ./coverage/lcov.info + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run linter + run: pnpm run lint + + typecheck: + name: Type Check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run type check + run: pnpm exec tsc --noEmit diff --git a/TEST_AUDIT_REPORT.md b/TEST_AUDIT_REPORT.md new file mode 100644 index 0000000..385536e --- /dev/null +++ b/TEST_AUDIT_REPORT.md @@ -0,0 +1,1643 @@ +# 🔍 Comprehensive Test Audit Report +**docs-engine** | Generated: 2025-11-11 + +═══════════════════════════════════════════════════════════════════════════════ + +## 📊 Executive Summary + +**Current Status:** 🔄 Below Coverage Threshold + +``` +Total Test Files: 16 +Total Test Cases: 328 +Test Lines of Code: 3,596 +Source Files: 70 +Coverage: 23.38% ❌ (Target: 40%) +All Tests Passing: ✅ 327/328 passing + +Coverage Breakdown: + Lines: 23.38% ❌ (Target: 40%) + Functions: 29.85% ❌ (Target: 40%) + Branches: 18.11% ❌ (Target: 40%) + Statements: 23.33% ❌ (Target: 40%) +``` + +**Key Findings:** +• 40/70 source files (57%) have NO test coverage +• 96 lines of duplicate test code identified (can be reduced to ~15 lines) +• Tests that exist are generally high quality with good edge case coverage +• No integration tests, E2E tests, or CI/CD pipeline detected +• Security tests present (XSS, sanitization) - strong foundation + +**Overall Grade:** C+ (Good foundation, needs expansion) + +═══════════════════════════════════════════════════════════════════════════════ + +## 🎯 Critical Issues Requiring Immediate Action + +### 1. Coverage Below Threshold ⚠️ +**Impact:** HIGH | **Effort:** HIGH +``` +Current: 23.38% +Target: 40% +Gap: -16.62% +``` +**Recommendation:** Prioritize adding tests for high-risk untested modules + +### 2. Massive Test Duplication 🔴 +**Impact:** MEDIUM | **Effort:** LOW +``` +Location: src/lib/plugins/callouts.test.ts (lines 49-145) +Issue: 9 nearly identical tests (96 lines) +Solution: Parameterize with test.each() (reduce to ~15 lines) +Savings: ~80 lines of code +``` + +### 3. Missing Critical Module Tests ⚠️ +**Impact:** HIGH | **Effort:** HIGH +``` +Untested High-Risk Modules: +• reference.ts (287 lines) - Symbol reference plugin +• screenshot-service.ts (514 lines) - Screenshot capture service +• image-processor.ts (318 lines) - Image processing pipeline +• circuit-breaker.ts (217 lines) - Fault tolerance +• collapse.ts (190 lines) - Collapsible sections +• toc.ts (184 lines) - Table of contents generation +``` + +### 4. No CI/CD Integration ⚠️ +**Impact:** MEDIUM | **Effort:** LOW +``` +Issue: No automated testing on push/PR +Risk: Breaking changes can reach main branch undetected +Action: Add GitHub Actions workflow (see recommendations) +``` + +═══════════════════════════════════════════════════════════════════════════════ + +## 📋 Test Quality Assessment by Module + +### ✅ EXCELLENT QUALITY (Keep as-is) + +**sanitize.test.ts** +``` +Rating: ⭐⭐⭐⭐⭐ (5/5) +Coverage: 100% +Tests: 24 +Strengths: • Comprehensive XSS attack scenarios + • Tests all sanitization contexts (HTML/SVG/Error) + • Good security edge cases (protocol injection, event handlers) + • Clear, focused tests +Gaps: None - exemplary test suite +``` + +**markdown.test.ts** +``` +Rating: ⭐⭐⭐⭐ (4/5) +Coverage: 100% +Tests: 38 +Strengths: • Thorough edge case coverage + • Tests all markdown utility functions + • Good special character handling +Issues: • Tests implementation details (timestamps, blank lines) + • Some redundant tests (empty headers + empty rows) +Action: Remove 5 redundant tests (lines 101-110, 112-115, 196-199, 220-224) +``` + +**frontmatter.test.ts** +``` +Rating: ⭐⭐⭐⭐ (4/5) +Coverage: 100% +Tests: 21 +Strengths: • Good YAML parsing edge cases + • Tests title extraction hierarchy + • Malformed input handling +Issues: • Weak assertions (toBeDefined() instead of exact values) + • Missing tests for multiple delimiters +Action: Strengthen assertions, add 3 edge case tests +``` + +### ⚠️ GOOD QUALITY (Minor improvements needed) + +**symbol-resolver.test.ts** +``` +Rating: ⭐⭐⭐⭐ (4/5) +Coverage: 84.94% +Tests: 21 +Strengths: • Tests all 6 symbol kinds + • Ambiguity detection with suggestions + • Path hint filtering +Gaps: • Missing case sensitivity tests + • Missing glob pattern tests +Action: Add 3-5 tests for missing edge cases +``` + +**rate-limiter.test.ts** +``` +Rating: ⭐⭐⭐ (3/5) +Coverage: 66.66% +Tests: 45 +Strengths: • Good use of fake timers + • Tests sliding windows + • Per-user isolation +Issues: • Redundant tests (concurrent + exceeding limit test same thing) + • Tests implementation details (counting mechanism) + • Unrealistic edge cases (100ms windows, empty identifiers) +Action: Remove 4 redundant tests, add input validation tests +``` + +**image-optimization.test.ts** +``` +Rating: ⭐⭐⭐⭐ (4/5) +Coverage: 100% +Tests: 8 +Strengths: • Tests external URL handling + • Configuration options tested + • Base64 encoding verified +Gaps: • Missing error handling tests (corrupt images) + • Missing performance tests (very large images) +Action: Add 5-7 error handling tests +``` + +### 🔴 NEEDS IMPROVEMENT (Refactoring required) + +**callouts.test.ts** +``` +Rating: ⭐⭐ (2/5) +Coverage: 32.53% +Tests: 13 +Critical: 🔴 MASSIVE CODE DUPLICATION +Issue: Lines 49-145 contain 9 identical tests (96 lines) + Only difference is callout type (NOTE, TIP, WARNING, etc.) + +Before: + test('should transform NOTE callout', () => { /* 10 lines */ }); + test('should transform TIP callout', () => { /* 10 lines */ }); + test('should transform WARNING callout', () => { /* 10 lines */ }); + ... 6 more identical tests ... + +After (recommended): + const types = [ + { type: 'NOTE', class: 'blue', title: 'Note' }, + { type: 'TIP', class: 'green', title: 'Tip' }, + // ... + ]; + test.each(types)('should transform $type callout', ({ type, class, title }) => { + // Single test implementation + }); + +Savings: 96 lines → ~15 lines (81 lines saved) +Gaps: • No tests for nested blockquotes + • No tests for markdown in callout content + • No case sensitivity tests +Action: Refactor to parameterized tests, add 5 edge case tests +``` + +**links.test.ts** +``` +Rating: ⭐⭐ (2/5) +Coverage: 89.79% +Tests: 23 +Critical: 🔴 DUPLICATE TOP-LEVEL FILE TESTS +Issue: Lines 65-133 contain 7 tests that follow identical pattern + (README, LICENSE, CONTRIBUTING, CHANGELOG, etc.) + +Action: Parameterize with test.each(), add query param tests +Savings: ~40 lines of code +Gaps: • No tests for URLs with query parameters + • No tests for .MD vs .md case sensitivity + • No tests for malformed URLs +``` + +**code-highlight.test.ts** +``` +Rating: ⭐⭐ (2/5) +Coverage: 83.33% +Tests: 13 +Issues: • Tests TypeScript interfaces at runtime (useless) + • Tests plugin API shape (TypeScript's job) + • Long justification comments (code smell) + • Type casting everywhere (unsafe) +Action: Remove 2 useless tests, improve assertions, add HTML injection tests +Savings: ~30 lines of code +``` + +**file-io.test.ts** +``` +Rating: ⭐⭐ (2/5) +Coverage: 90% +Tests: 32 +Issues: • Duplicate UTF-8 tests (lines 54-62 and 106-114) + • Circular dependencies (use writeFile to test readFile) + • Over-granular countLines testing (8 tests for simple utility) + • Tests implementation details (JSON indentation spaces) +Gaps: • No permission error tests + • No disk space error tests + • No large file tests + • No concurrent access tests +Action: Remove 6 redundant tests, add 4 error handling tests +Savings: ~80 lines of code +``` + +═══════════════════════════════════════════════════════════════════════════════ + +## 🚫 Tests to Remove or Consolidate + +### High Priority Removals (No Value) + +**1. TypeScript Type Tests (Remove entirely)** +``` +Location: src/lib/plugins/code-highlight.test.ts:256-265 +Reason: Tests TypeScript interface at runtime - TypeScript already validates this +Impact: No loss of test coverage +``` + +**2. API Shape Tests (Remove entirely)** +``` +Location: src/lib/plugins/code-highlight.test.ts:18-31 +Reason: Tests plugin API structure - no runtime value +Impact: No loss of test coverage +``` + +**3. Duplicate UTF-8 Tests (Consolidate to 1)** +``` +Location: src/lib/utils/file-io.test.ts:54-62 and 106-114 +Reason: Both test identical UTF-8 functionality +Action: Keep one, remove the other +Savings: 8 lines +``` + +**4. Circular Dependency Tests (Refactor)** +``` +Location: src/lib/utils/file-io.test.ts (multiple) +Reason: Use writeFile to test readFile and vice versa +Action: Use test fixtures instead +Risk: Current approach could hide bugs in both functions +``` + +### Medium Priority Consolidations + +**5. Callouts Type Tests (Parameterize)** +``` +Location: src/lib/plugins/callouts.test.ts:49-145 +Before: 9 identical tests (96 lines) +After: 1 parameterized test (~15 lines) +Savings: 81 lines (84% reduction) +``` + +**6. Top-Level File Links (Parameterize)** +``` +Location: src/lib/plugins/links.test.ts:65-133 +Before: 7 similar tests (~40 lines) +After: 1 parameterized test (~8 lines) +Savings: 32 lines (80% reduction) +``` + +**7. External Link Protocol Tests (Parameterize)** +``` +Location: src/lib/plugins/links.test.ts (multiple) +Before: 5 tests for different protocols (http, https, ftp, mailto, data) +After: 1 parameterized test +Savings: ~20 lines +``` + +**8. Redundant Rate Limiter Tests (Remove)** +``` +Location: src/lib/server/rate-limiter.test.ts +Remove: • Lines 89-101: "concurrent requests" (duplicate of "block exceeding") + • Lines 117-122: "empty identifier" (unrealistic edge case) + • Lines 124-132: "100ms windows" (unrealistic edge case) +Savings: ~30 lines +``` + +**9. Over-Granular countLines Tests (Consolidate)** +``` +Location: src/lib/utils/file-io.test.ts:266-318 +Before: 8 tests for simple line counting utility +After: 3-4 meaningful tests (empty, single, multiple, edge cases) +Savings: ~30 lines +``` + +**Total Potential Savings: ~273 lines of test code** +**Total Potential Reduction: ~7.6% of test suite size** + +═══════════════════════════════════════════════════════════════════════════════ + +## 🔴 Critical Coverage Gaps (Priority Order) + +### Tier 1: High-Risk Untested Modules (Add immediately) + +**1. reference.ts (287 lines) - 0% coverage** +``` +Risk Level: 🔴 CRITICAL +Functionality: Symbol reference resolution and transformation +Why Critical: • Core feature for documentation linking + • Complex path resolution logic + • Error-prone symbol matching +Tests Needed: ~15-20 tests +Estimated LOC: ~300 lines +Priority: 1 +``` + +**2. screenshot-service.ts (514 lines) - 0% coverage** +``` +Risk Level: 🔴 CRITICAL +Functionality: Screenshot capture with Playwright +Why Critical: • External process management + • File I/O operations + • Resource cleanup critical + • Timeout handling +Tests Needed: ~20-25 tests (mostly integration tests) +Estimated LOC: ~400 lines +Priority: 2 +Note: Consider separating unit tests from integration tests +``` + +**3. circuit-breaker.ts (217 lines) - 0% coverage** +``` +Risk Level: 🔴 CRITICAL +Functionality: Fault tolerance and service protection +Why Critical: • Complex state management + • Time-based logic + • Critical for production resilience +Tests Needed: ~15-18 tests +Estimated LOC: ~250 lines +Priority: 3 +``` + +**4. image-processor.ts (318 lines) - 0% coverage** +``` +Risk Level: 🔴 CRITICAL +Functionality: Image optimization pipeline (Sharp) +Why Critical: • File system operations + • External library integration (Sharp) + • Performance-sensitive + • Error handling for corrupt images +Tests Needed: ~18-22 tests +Estimated LOC: ~350 lines +Priority: 4 +``` + +### Tier 2: Important Feature Gaps (Add within 2 weeks) + +**5. collapse.ts (190 lines) - 0% coverage** +``` +Risk Level: 🟡 HIGH +Functionality: Collapsible section transformation +Tests Needed: ~12-15 tests +Estimated LOC: ~200 lines +Priority: 5 +``` + +**6. toc.ts (184 lines) - 0% coverage** +``` +Risk Level: 🟡 HIGH +Functionality: Table of contents generation +Tests Needed: ~12-15 tests +Estimated LOC: ~180 lines +Priority: 6 +``` + +**7. tabs.ts (135 lines) - 0% coverage** +``` +Risk Level: 🟡 HIGH +Functionality: Tab container transformation +Tests Needed: ~10-12 tests +Estimated LOC: ~150 lines +Priority: 7 +``` + +**8. mermaid.ts (33 lines) - 0% coverage** +``` +Risk Level: 🟡 MEDIUM +Functionality: Mermaid diagram integration +Tests Needed: ~6-8 tests +Estimated LOC: ~80 lines +Priority: 8 +``` + +**9. filetree.ts (45 lines) - 0% coverage** +``` +Risk Level: 🟡 MEDIUM +Functionality: File tree visualization +Tests Needed: ~6-8 tests +Estimated LOC: ~70 lines +Priority: 9 +``` + +### Tier 3: Utility and Support Functions + +**10. symbol-generation.ts (851 lines) - 0% coverage** +``` +Risk Level: 🟡 HIGH (due to size) +Functionality: Symbol generation from source code +Tests Needed: ~25-30 tests +Estimated LOC: ~500 lines +Priority: 10 +``` + +**11. tree-parser.ts (208 lines) - 0% coverage** +``` +Risk Level: 🟢 MEDIUM +Functionality: AST tree parsing utilities +Tests Needed: ~12-15 tests +Estimated LOC: ~180 lines +Priority: 11 +``` + +**12. navigation-builder.ts + navigation-scanner.ts (374 lines combined) - 0% coverage** +``` +Risk Level: 🟢 MEDIUM +Functionality: Navigation structure building +Tests Needed: ~15-18 tests combined +Estimated LOC: ~250 lines +Priority: 12 +``` + +**13. search.ts (163 lines) - 0% coverage** +``` +Risk Level: 🟢 MEDIUM +Functionality: Search implementation +Tests Needed: ~10-12 tests +Estimated LOC: ~150 lines +Priority: 13 +Note: search-index.ts has good coverage, but actual search.ts doesn't +``` + +### Tier 4: Low-Risk Utilities (Add as time permits) + +``` +• cli-executor.ts (80 lines) - 0% coverage +• logger.ts (64 lines) - 0% coverage +• highlighter.ts (74 lines) - 0% coverage +• base64.ts (95 lines) - 12% coverage (needs more) +• openapi-formatter.ts (363 lines) - 0% coverage +• symbol-renderer.ts (277 lines) - 0% coverage +• version.ts (20 lines) - 0% coverage +• html.ts (87 lines) - 42.85% coverage (needs more) +• date.ts - 100% coverage ✅ +``` + +**Estimated Total Effort:** +``` +New Tests Needed: ~220-270 tests +New Test LOC: ~4,000-5,000 lines +Time Estimate: 3-4 weeks (1 developer) +Coverage Increase: 23% → 60-70% +``` + +═══════════════════════════════════════════════════════════════════════════════ + +## 🧪 Missing Test Types + +### 1. Integration Tests ❌ +**Status:** Not present +``` +Current: Only unit tests exist +Need: • Plugin pipeline integration + • File I/O + Markdown processing flow + • Symbol resolution + generation flow + • Image optimization pipeline + • Search indexing + querying flow + +Example Integration Test: + test('full markdown processing pipeline', async () => { + const markdown = '# Title\n\n[!NOTE] Callout\n\n```js\ncode\n```'; + const result = await processMarkdown(markdown, allPlugins); + expect(result.html).toMatchSnapshot(); + expect(result.metadata).toEqual({ ... }); + }); + +Estimated: 15-20 integration tests needed +LOC: ~800-1000 lines +Priority: HIGH +``` + +### 2. End-to-End Tests ❌ +**Status:** Not present +``` +Current: No E2E tests +Need: • Full documentation site generation + • Search functionality E2E + • Navigation generation E2E + • Image optimization E2E + +Tools: Playwright (already in devDependencies ✅) +Example: Generate full site → verify pages → test navigation → test search + +Estimated: 10-15 E2E tests needed +LOC: ~600-800 lines +Priority: MEDIUM +Note: Playwright already installed, just needs test setup +``` + +### 3. Performance Tests ❌ +**Status:** Not present +``` +Current: No performance benchmarks +Need: • Large file processing (10MB+ markdown files) + • Thousands of symbols performance + • Search index with 10k+ documents + • Image batch processing (100+ images) + +Example: + test('should process large markdown file within 5s', async () => { + const largeMarkdown = generateMarkdown(10000); // 10k lines + const start = Date.now(); + await processMarkdown(largeMarkdown); + expect(Date.now() - start).toBeLessThan(5000); + }); + +Estimated: 8-12 performance tests +LOC: ~300-400 lines +Priority: LOW (add after coverage is above 60%) +``` + +### 4. Visual Regression Tests ❌ +**Status:** Not present +``` +Current: No visual testing +Need: • Callout rendering consistency + • Code block styling + • Table rendering + • Symbol reference rendering + +Tools: Percy, Chromatic, or Playwright screenshots +Priority: LOW (nice-to-have) +``` + +### 5. Component Tests ❌ +**Status:** Not present +``` +Current: No component tests (all Svelte components untested) +Files: • All files in lib/components/ have 0% coverage +Need: • Svelte component unit tests + • @testing-library/svelte already installed ✅ + +Priority: MEDIUM (if components contain complex logic) +``` + +═══════════════════════════════════════════════════════════════════════════════ + +## 🎯 Test Best Practices Issues + +### ❌ Current Issues + +**1. Testing Implementation Details** +``` +Location: Multiple files +Examples: • Checking for specific CSS class names + • Testing JSON indentation (2 spaces vs 4) + • Testing exact timestamp formats + • Testing blank lines in output + +Problem: Tests break when implementation changes, even if behavior is correct +Fix: Test behavior and outcomes, not internal implementation +``` + +**2. Weak Assertions** +``` +Location: Multiple files +Examples: • expect(result).toBeDefined() + • expect(result).toContain('shiki') + • expect(count).toBeGreaterThan(0) + +Problem: Assertions are too vague to catch real bugs +Fix: Use specific assertions: + • expect(result).toEqual({ specific: 'value' }) + • expect(html).toMatch(/
/)
+ • expect(count).toBe(expectedCount)
+```
+
+**3. Magic Numbers and Strings**
+```
+Location: All test files
+Examples: • 60000 (milliseconds - what is this?)
+ • 10 (rate limit - why 10?)
+ • '/docs/' (hardcoded path)
+ • 'shiki' (what if class name changes?)
+
+Fix: Extract to named constants:
+ const ONE_MINUTE = 60_000;
+ const RATE_LIMIT = 10;
+ const DOCS_PATH_PREFIX = '/docs/';
+```
+
+**4. Circular Test Dependencies**
+```
+Location: src/lib/utils/file-io.test.ts
+Example: Using writeFile() to test readFile()
+ Using readFile() to test writeFile()
+
+Problem: If both functions have the same bug, tests will pass
+Fix: Use independent test fixtures or known good data
+```
+
+**5. No Test Helpers or Fixtures**
+```
+Current: All mock data is inline in test files
+Problem: • Code duplication
+ • Hard to maintain
+ • Test data scattered everywhere
+
+Fix: Create test utilities:
+ // tests/helpers/fixtures.ts
+ export const mockMarkdownTree = () => ({ /* ... */ });
+ export const mockFrontmatter = () => ({ /* ... */ });
+```
+
+**6. Inconsistent Test Organization**
+```
+Current: Tests co-located with source files (*.test.ts)
+Pros: ✅ Easy to find related tests
+ ✅ Encourages testing
+Cons: ❌ Harder to run only tests
+ ❌ Clutters source directories
+
+Recommendation: Keep current approach (co-location is good for this project size)
+```
+
+**7. No Test Coverage Enforcement on CI**
+```
+Current: Coverage threshold set to 40% in config
+ But no CI to enforce it ❌
+
+Problem: Coverage can drop without anyone noticing
+Fix: Add CI workflow with coverage checks (see recommendations)
+```
+
+### ✅ Things Done Well
+
+**1. Good Error Handling Tests**
+```
+Examples: • Malformed YAML gracefully handled
+ • Invalid language in code blocks
+ • Missing files in file-io
+ • XSS attack prevention in sanitize
+
+Keep: Continue this pattern for all new tests
+```
+
+**2. Security Testing**
+```
+Tests: • XSS prevention (sanitize.test.ts)
+ • HTML injection
+ • Protocol injection (javascript:, data:)
+ • SVG attack vectors
+
+Excellent: This is exemplary - expand to other modules
+```
+
+**3. Good Use of Test Lifecycle Hooks**
+```
+Pattern: beforeEach() for setup
+ afterEach() for cleanup
+ Temporary directories properly cleaned up
+
+Keep: Continue this pattern consistently
+```
+
+**4. Good Test Naming**
+```
+Pattern: "should [expected behavior]" convention
+Examples: • "should transform NOTE callout"
+ • "should handle malformed YAML gracefully"
+ • "should escape special characters"
+
+Keep: Maintain this clear, descriptive naming
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 🚀 CI/CD Recommendations
+
+### Current State: No CI/CD ❌
+
+**Missing:**
+• No GitHub Actions workflows
+• No automated test runs on PR/push
+• No coverage reporting
+• No lint checks
+• No type checking in CI
+
+### Recommended GitHub Actions Workflow
+
+**Create:** `.github/workflows/ci.yml`
+
+```yaml
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ test:
+ name: Test & Coverage
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: pnpm/action-setup@v2
+ with:
+ version: 10
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'pnpm'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Run tests with coverage
+ run: pnpm run test:coverage
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ file: ./coverage/lcov.info
+ fail_ci_if_error: true
+
+ - name: Check coverage threshold
+ run: |
+ if [ $(jq '.total.lines.pct' coverage/coverage-summary.json | cut -d. -f1) -lt 40 ]; then
+ echo "Coverage is below 40% threshold"
+ exit 1
+ fi
+
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v2
+ with:
+ version: 10
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'pnpm'
+
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm run lint
+
+ typecheck:
+ name: Type Check
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v2
+ with:
+ version: 10
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'pnpm'
+
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm exec tsc --noEmit
+```
+
+**Additional Workflows:**
+
+```yaml
+# .github/workflows/nightly.yml
+# Run comprehensive tests nightly (including slow E2E tests)
+
+name: Nightly Tests
+on:
+ schedule:
+ - cron: '0 0 * * *' # Midnight UTC
+ workflow_dispatch:
+
+jobs:
+ e2e:
+ name: E2E Tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v2
+ - uses: actions/setup-node@v4
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm exec playwright install --with-deps
+ - run: pnpm run test:e2e # When E2E tests exist
+
+ performance:
+ name: Performance Tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v2
+ - uses: actions/setup-node@v4
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm run test:perf # When perf tests exist
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 🔧 Test Configuration Optimization
+
+### Current Vitest Config Analysis
+
+**File:** `vitest.config.ts`
+
+**Current Settings:**
+```typescript
+{
+ globals: true,
+ environment: 'happy-dom',
+ include: ['src/**/*.{test,spec}.{js,ts}'],
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html', 'lcov'],
+ include: ['src/lib/**/*.ts'],
+ exclude: [/* standard exclusions */],
+ thresholds: {
+ lines: 40,
+ functions: 40,
+ branches: 40,
+ statements: 40
+ },
+ all: true
+ }
+}
+```
+
+**✅ Good Configurations:**
+• Environment: happy-dom (lightweight, fast) ✅
+• Coverage provider: v8 (fast, accurate) ✅
+• Multiple reporters including lcov for CI ✅
+• Threshold enforcement ✅
+• `all: true` (measures all files, not just tested ones) ✅
+
+**🔧 Recommended Improvements:**
+
+```typescript
+// vitest.config.ts
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ globals: true,
+ environment: 'happy-dom',
+ include: ['src/**/*.{test,spec}.{js,ts}'],
+
+ // NEW: Separate test types
+ exclude: [
+ '**/node_modules/**',
+ '**/dist/**',
+ '**/.{idea,git,cache,output,temp}/**',
+ '**/{e2e,perf}/**' // Exclude from default run
+ ],
+
+ // NEW: Faster test runs
+ pool: 'threads',
+ poolOptions: {
+ threads: {
+ singleThread: false,
+ useAtomics: true
+ }
+ },
+
+ // NEW: Better error messages
+ reporters: ['default', 'html'],
+ outputFile: {
+ html: './coverage/test-report.html'
+ },
+
+ // NEW: Fail fast for CI
+ bail: process.env.CI ? 1 : undefined,
+
+ // NEW: Timeouts
+ testTimeout: 10000,
+ hookTimeout: 10000,
+
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html', 'lcov'],
+ include: ['src/lib/**/*.ts'],
+ exclude: [
+ '**/node_modules/**',
+ '**/dist/**',
+ '**/*.d.ts',
+ '**/*.config.*',
+ '**/types.ts',
+ '**/*.test.ts',
+ '**/*.spec.ts',
+ '**/*.svelte' // Keep for now, may change later
+ ],
+
+ // CHANGED: Increase thresholds gradually
+ thresholds: {
+ lines: 40, // Current: 23.38% → Target: 40%
+ functions: 40, // Current: 29.85% → Target: 40%
+ branches: 40, // Current: 18.11% → Target: 40%
+ statements: 40 // Current: 23.33% → Target: 40%
+
+ // Phase 2 targets (after adding high-risk tests):
+ // lines: 60,
+ // functions: 60,
+ // branches: 50,
+ // statements: 60
+ },
+
+ all: true,
+
+ // NEW: Per-directory thresholds (optional)
+ perFile: true,
+ watermarks: {
+ lines: [50, 80],
+ functions: [50, 80],
+ branches: [50, 80],
+ statements: [50, 80]
+ }
+ }
+ }
+});
+```
+
+**Add New Test Scripts to package.json:**
+
+```json
+{
+ "scripts": {
+ "test": "vitest",
+ "test:ui": "vitest --ui",
+ "test:run": "vitest run",
+ "test:coverage": "vitest run --coverage",
+ "test:watch": "vitest watch",
+
+ // NEW: Separate test types
+ "test:unit": "vitest run --config vitest.config.ts",
+ "test:integration": "vitest run --config vitest.integration.config.ts",
+ "test:e2e": "playwright test",
+ "test:perf": "vitest run --config vitest.perf.config.ts",
+
+ // NEW: CI-specific
+ "test:ci": "vitest run --coverage --reporter=default --reporter=json",
+
+ // NEW: Coverage helpers
+ "coverage": "vitest run --coverage",
+ "coverage:open": "open coverage/index.html",
+
+ // NEW: Watch specific files
+ "test:related": "vitest related"
+ }
+}
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 📊 Actionable Improvement Roadmap
+
+### Phase 1: Quick Wins (1-2 days)
+
+**Goal:** Reduce technical debt, improve maintainability
+
+```
+✅ Tasks:
+ ⬜ Refactor callouts.test.ts to use test.each() (1 hour)
+ Impact: -81 lines, better maintainability
+
+ ⬜ Refactor links.test.ts to parameterize tests (1 hour)
+ Impact: -52 lines
+
+ ⬜ Remove useless tests (TypeScript types, API shapes) (30 min)
+ Impact: -20 lines, cleaner test suite
+
+ ⬜ Add GitHub Actions CI workflow (2 hours)
+ Impact: Automated testing on every PR
+
+ ⬜ Set up Codecov or similar for coverage tracking (1 hour)
+ Impact: Visualize coverage trends
+
+Total Time: 1-2 days
+Coverage Change: 0% (just cleanup)
+Maintainability: ↑↑↑ High improvement
+```
+
+### Phase 2: Critical Coverage (1-2 weeks)
+
+**Goal:** Test high-risk untested modules
+
+```
+✅ Tasks:
+ ⬜ Add tests for reference.ts (2 days)
+ • 15-20 tests for symbol reference resolution
+ • Edge cases for ambiguous references
+ • Error handling tests
+ Impact: +300 LOC, +~5% coverage
+
+ ⬜ Add tests for circuit-breaker.ts (1 day)
+ • State transition tests
+ • Timeout handling
+ • Failure threshold tests
+ Impact: +250 LOC, +~3% coverage
+
+ ⬜ Add tests for collapse.ts + toc.ts + tabs.ts (2 days)
+ • Plugin transformation tests
+ • Edge cases for each
+ Impact: +530 LOC, +~6% coverage
+
+ ⬜ Add tests for mermaid.ts + filetree.ts (1 day)
+ • Basic transformation tests
+ • Error handling
+ Impact: +150 LOC, +~1% coverage
+
+ ⬜ Add integration tests for plugins (2 days)
+ • Full pipeline integration
+ • Plugin interaction tests
+ Impact: +800 LOC, +~2% coverage
+
+Total Time: 1-2 weeks
+Coverage Change: 23% → ~40% ✅
+Risk Reduction: ↑↑↑ High
+```
+
+### Phase 3: Comprehensive Coverage (2-3 weeks)
+
+**Goal:** Reach 60%+ coverage
+
+```
+✅ Tasks:
+ ⬜ Add tests for screenshot-service.ts (3 days)
+ • Integration tests with Playwright
+ • Resource cleanup tests
+ • Timeout handling
+ Impact: +400 LOC, +~6% coverage
+ Note: Requires Playwright setup
+
+ ⬜ Add tests for image-processor.ts (2 days)
+ • Sharp integration tests
+ • Error handling (corrupt images)
+ • Performance tests
+ Impact: +350 LOC, +~4% coverage
+
+ ⬜ Add tests for symbol-generation.ts (3 days)
+ • Source code parsing tests
+ • Symbol extraction tests
+ • Edge cases for complex types
+ Impact: +500 LOC, +~10% coverage
+
+ ⬜ Add tests for navigation modules (2 days)
+ • navigation-builder.ts
+ • navigation-scanner.ts
+ • Integration tests
+ Impact: +250 LOC, +~4% coverage
+
+ ⬜ Add tests for search.ts (2 days)
+ • Search algorithm tests
+ • Ranking tests
+ • Performance tests
+ Impact: +150 LOC, +~2% coverage
+
+Total Time: 2-3 weeks
+Coverage Change: 40% → ~66% ✅
+Risk Reduction: ↑↑↑ Very High
+```
+
+### Phase 4: Advanced Testing (2-3 weeks)
+
+**Goal:** E2E, performance, visual regression
+
+```
+✅ Tasks:
+ ⬜ Set up Playwright E2E tests (3 days)
+ • Full site generation E2E
+ • Navigation E2E
+ • Search E2E
+ Impact: +800 LOC E2E tests
+
+ ⬜ Add performance benchmarks (2 days)
+ • Large file processing
+ • Batch image optimization
+ • Search indexing performance
+ Impact: +400 LOC perf tests
+
+ ⬜ Add visual regression tests (2 days)
+ • Snapshot tests for rendering
+ • CSS regression detection
+ Impact: +300 LOC visual tests
+
+ ⬜ Add component tests (1 week)
+ • Test all Svelte components
+ • Interaction tests
+ Impact: +600 LOC, +~3% coverage
+
+Total Time: 2-3 weeks
+Coverage Change: 66% → ~70%+
+Quality: ↑↑↑ Production-ready
+```
+
+### Total Effort Summary
+
+```
+Phase 1 (Quick Wins): 1-2 days
+Phase 2 (Critical): 1-2 weeks
+Phase 3 (Comprehensive): 2-3 weeks
+Phase 4 (Advanced): 2-3 weeks
+───────────────────────────────────────
+Total: 6-9 weeks (1 developer)
+
+Coverage Progression:
+ Start: 23.38%
+ Phase 1: 23% (cleanup, no change)
+ Phase 2: ~40% (+16.62%) ✅ Threshold met
+ Phase 3: ~66% (+26%)
+ Phase 4: ~70%+ (+4%)
+
+Risk Reduction:
+ Phase 1: ↑ Low (maintainability)
+ Phase 2: ↑↑↑ Very High (critical modules)
+ Phase 3: ↑↑ High (comprehensive coverage)
+ Phase 4: ↑ Medium (quality of life)
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 💡 Specific Code Examples
+
+### Example 1: Refactoring Callouts Tests
+
+**Before (96 lines):**
+```typescript
+test('should transform NOTE callout', () => {
+ const tree = createTree('[!NOTE] Note content');
+ const result = transformCallout(tree);
+ expect(result.html).toContain('callout-blue');
+ expect(result.html).toContain('Note');
+});
+
+test('should transform TIP callout', () => {
+ const tree = createTree('[!TIP] Tip content');
+ const result = transformCallout(tree);
+ expect(result.html).toContain('callout-green');
+ expect(result.html).toContain('Tip');
+});
+
+// ... 7 more identical tests
+```
+
+**After (15 lines):**
+```typescript
+const calloutTypes = [
+ { type: 'NOTE', class: 'blue', title: 'Note', role: 'note' },
+ { type: 'TIP', class: 'green', title: 'Tip', role: 'note' },
+ { type: 'WARNING', class: 'yellow', title: 'Warning', role: 'alert' },
+ { type: 'IMPORTANT', class: 'purple', title: 'Important', role: 'alert' },
+ { type: 'CAUTION', class: 'red', title: 'Caution', role: 'alert' },
+ { type: 'SUCCESS', class: 'success', title: 'Success', role: 'status' },
+ { type: 'DANGER', class: 'danger', title: 'Danger', role: 'alert' },
+ { type: 'INFO', class: 'info', title: 'Info', role: 'note' },
+ { type: 'QUESTION', class: 'question', title: 'Question', role: 'note' }
+];
+
+test.each(calloutTypes)(
+ 'should transform $type callout',
+ ({ type, class: cssClass, title, role }) => {
+ const tree = createTree(`[!${type}] ${type} content`);
+ const result = transformCallout(tree);
+
+ expect(result.html).toContain(`callout-${cssClass}`);
+ expect(result.html).toContain(title);
+ expect(result.html).toContain(`role="${role}"`);
+ expect(result.html).toContain(`aria-label="${title}"`);
+ }
+);
+```
+
+**Benefits:**
+• 81 lines removed (84% reduction)
+• Easier to add new callout types (add one line to array)
+• Single place to update test logic
+• Clear data-driven approach
+
+### Example 2: Adding Error Handling Tests
+
+**Missing (should add):**
+```typescript
+// src/lib/utils/file-io.test.ts
+
+describe('error handling', () => {
+ test('should throw on permission denied', async () => {
+ const readOnlyFile = path.join(tmpDir, 'readonly.txt');
+ await writeFile(readOnlyFile, 'content');
+ fs.chmodSync(readOnlyFile, 0o000); // Remove all permissions
+
+ await expect(readFile(readOnlyFile)).rejects.toThrow(/permission denied/i);
+
+ // Cleanup
+ fs.chmodSync(readOnlyFile, 0o644);
+ });
+
+ test('should throw on disk space exhausted', async () => {
+ // Mock fs.writeFile to simulate ENOSPC error
+ const mockWrite = vi.spyOn(fs.promises, 'writeFile');
+ mockWrite.mockRejectedValueOnce(
+ Object.assign(new Error('ENOSPC: no space left on device'), {
+ code: 'ENOSPC'
+ })
+ );
+
+ const file = path.join(tmpDir, 'test.txt');
+ await expect(writeFile(file, 'content')).rejects.toThrow(/no space left/i);
+
+ mockWrite.mockRestore();
+ });
+
+ test('should handle very large files', async () => {
+ const largeContent = 'x'.repeat(10 * 1024 * 1024); // 10MB
+ const file = path.join(tmpDir, 'large.txt');
+
+ await writeFile(file, largeContent);
+ const result = await readFile(file);
+
+ expect(result.length).toBe(largeContent.length);
+ }, 30000); // 30s timeout for large file
+});
+```
+
+### Example 3: Integration Test Example
+
+**Should add:**
+```typescript
+// src/lib/integration/markdown-pipeline.test.ts
+
+import { describe, it, expect } from 'vitest';
+import { unified } from 'unified';
+import remarkParse from 'remark-parse';
+import remarkDirective from 'remark-directive';
+import { calloutsPlugin } from '../plugins/callouts';
+import { codeHighlightPlugin } from '../plugins/code-highlight';
+import { linksPlugin } from '../plugins/links';
+import { katexPlugin } from '../plugins/katex';
+
+describe('markdown processing pipeline', () => {
+ const processor = unified()
+ .use(remarkParse)
+ .use(remarkDirective)
+ .use(calloutsPlugin)
+ .use(codeHighlightPlugin)
+ .use(linksPlugin)
+ .use(katexPlugin);
+
+ it('should process complex markdown with all plugins', async () => {
+ const markdown = `
+# Documentation
+
+[!NOTE] This is a note callout
+
+Check out [guide.md](./guide.md) for more info.
+
+\`\`\`typescript title="example.ts" showLineNumbers
+function hello() {
+ console.log('Hello World');
+}
+\`\`\`
+
+Inline math: $E = mc^2$
+
+Display math:
+
+$$
+\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}
+$$
+ `.trim();
+
+ const result = await processor.process(markdown);
+ const html = result.toString();
+
+ // Verify all plugins ran
+ expect(html).toContain('callout-blue'); // Callouts
+ expect(html).toContain('/docs/guide'); // Links
+ expect(html).toContain('class="shiki"'); // Code highlight
+ expect(html).toContain('code-block-title'); // Code metadata
+ expect(html).toContain('katex'); // Math rendering
+
+ // Verify no plugin corrupted another's output
+ expect(html).not.toContain('[!NOTE]'); // Callout processed
+ expect(html).not.toContain('./guide.md'); // Link processed
+ expect(html).not.toContain('```typescript'); // Code fence processed
+ expect(html).not.toContain('$E = mc^2$'); // Inline math processed
+ });
+
+ it('should handle plugin errors gracefully', async () => {
+ const markdown = `
+[!NOTE] Valid callout
+
+\`\`\`invalidlang123
+code
+\`\`\`
+
+$\\invalid{latex}$
+ `.trim();
+
+ // Should not throw, but handle errors gracefully
+ await expect(processor.process(markdown)).resolves.toBeDefined();
+ });
+
+ it('should maintain correct order of transformations', async () => {
+ // Links should be processed before callouts
+ // So callouts can contain links
+ const markdown = `
+[!NOTE] Check [guide.md](./guide.md)
+ `.trim();
+
+ const result = await processor.process(markdown);
+ const html = result.toString();
+
+ expect(html).toContain('callout');
+ expect(html).toContain('/docs/guide');
+ });
+});
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 🎓 Testing Best Practices Guide
+
+### Test Naming Convention
+
+**❌ Bad:**
+```typescript
+test('test1', () => { ... });
+test('file-io', () => { ... });
+test('check read', () => { ... });
+```
+
+**✅ Good:**
+```typescript
+test('should read UTF-8 file with special characters', () => { ... });
+test('should throw error when file does not exist', () => { ... });
+test('should handle empty files gracefully', () => { ... });
+```
+
+**Pattern:** `should [expected behavior] [given context]`
+
+### Assertion Quality
+
+**❌ Weak Assertions:**
+```typescript
+expect(result).toBeDefined();
+expect(html).toContain('class');
+expect(count).toBeGreaterThan(0);
+```
+
+**✅ Strong Assertions:**
+```typescript
+expect(result).toEqual({ title: 'Title', content: 'Content' });
+expect(html).toMatch(//);
+expect(count).toBe(5);
+```
+
+### Test Independence
+
+**❌ Dependent Tests:**
+```typescript
+let sharedState;
+
+test('test 1', () => {
+ sharedState = someFunction();
+});
+
+test('test 2', () => {
+ // Depends on test 1 running first
+ expect(sharedState).toBe(expected);
+});
+```
+
+**✅ Independent Tests:**
+```typescript
+test('test 1', () => {
+ const state = someFunction();
+ expect(state).toBe(expected);
+});
+
+test('test 2', () => {
+ const state = someFunction();
+ expect(state).toBe(expected);
+});
+```
+
+### Avoid Magic Values
+
+**❌ Magic Values:**
+```typescript
+test('rate limiter', () => {
+ for (let i = 0; i < 10; i++) {
+ checkRateLimit('user');
+ }
+ vi.advanceTimersByTime(60000);
+});
+```
+
+**✅ Named Constants:**
+```typescript
+const RATE_LIMIT = 10;
+const ONE_MINUTE_MS = 60_000;
+
+test('rate limiter should allow up to limit', () => {
+ for (let i = 0; i < RATE_LIMIT; i++) {
+ checkRateLimit('user');
+ }
+ vi.advanceTimersByTime(ONE_MINUTE_MS);
+});
+```
+
+### Test Helpers and Fixtures
+
+**❌ Inline Mock Data Everywhere:**
+```typescript
+test('test 1', () => {
+ const tree = { type: 'root', children: [{ type: 'paragraph', ... }] };
+ // ...
+});
+
+test('test 2', () => {
+ const tree = { type: 'root', children: [{ type: 'paragraph', ... }] };
+ // ...
+});
+```
+
+**✅ Shared Test Helpers:**
+```typescript
+// tests/helpers/fixtures.ts
+export function createMockTree(content: string) {
+ return { type: 'root', children: [{ type: 'paragraph', ... }] };
+}
+
+// test file
+import { createMockTree } from '../helpers/fixtures';
+
+test('test 1', () => {
+ const tree = createMockTree('content');
+ // ...
+});
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 📈 Coverage Improvement Tracking
+
+### Recommended Coverage Targets
+
+```
+Phase Timeline Lines Functions Branches Statements Status
+─────── ────────── ─────── ───────── ──────── ────────── ──────────
+Current Today 23.38% 29.85% 18.11% 23.33% 🔴 Below
+Phase 1 Week 1 23% 30% 18% 23% 🔄 Cleanup
+Phase 2 Week 3 40% 45% 35% 40% ✅ Threshold
+Phase 3 Week 6 60% 65% 50% 60% ✅ Good
+Phase 4 Week 9 70%+ 75%+ 60%+ 70%+ ✅ Excellent
+```
+
+### Module Priority for Coverage
+
+**Tier 1 - Critical (Add First):**
+```
+Module Current Target Priority
+───────────────────── ─────── ────── ────────
+reference.ts 0% 90% ⭐⭐⭐⭐⭐
+circuit-breaker.ts 0% 85% ⭐⭐⭐⭐⭐
+screenshot-service.ts 0% 60% ⭐⭐⭐⭐⭐
+image-processor.ts 0% 80% ⭐⭐⭐⭐⭐
+```
+
+**Tier 2 - Important (Add Second):**
+```
+Module Current Target Priority
+───────────────────── ─────── ────── ────────
+collapse.ts 0% 85% ⭐⭐⭐⭐
+toc.ts 0% 90% ⭐⭐⭐⭐
+tabs.ts 0% 85% ⭐⭐⭐⭐
+symbol-generation.ts 0% 75% ⭐⭐⭐⭐
+```
+
+**Tier 3 - Utilities (Add Third):**
+```
+Module Current Target Priority
+───────────────────── ─────── ────── ────────
+navigation-builder.ts 0% 80% ⭐⭐⭐
+tree-parser.ts 0% 85% ⭐⭐⭐
+search.ts 0% 85% ⭐⭐⭐
+base64.ts 12% 70% ⭐⭐⭐
+```
+
+### Weekly Progress Tracking Template
+
+```
+Week of: [DATE]
+
+Coverage:
+ Lines: [%] (target: [%]) [↑↓] [delta]
+ Functions: [%] (target: [%]) [↑↓] [delta]
+ Branches: [%] (target: [%]) [↑↓] [delta]
+ Statements: [%] (target: [%]) [↑↓] [delta]
+
+Tests Added: [N] tests ([M] LOC)
+Modules Covered:
+ • [module] - [N] tests - [%] coverage
+ • [module] - [N] tests - [%] coverage
+
+Issues Found:
+ • [issue description]
+ • [issue description]
+
+Next Week Goals:
+ • [goal]
+ • [goal]
+```
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 🚀 Conclusion
+
+### Summary of Findings
+
+**Strengths:**
+✅ Solid test foundation where tests exist
+✅ Good edge case coverage in tested modules
+✅ Excellent security testing (XSS, sanitization)
+✅ Good test organization and naming conventions
+✅ Proper cleanup and lifecycle management
+
+**Critical Issues:**
+❌ Coverage well below 40% threshold (currently 23.38%)
+❌ 57% of source files completely untested
+❌ 96 lines of duplicate test code identified
+❌ No CI/CD pipeline for automated testing
+❌ Missing integration and E2E tests
+
+**Risk Level:** 🟡 MEDIUM-HIGH
+```
+High-risk untested modules exist (reference.ts, screenshot-service.ts,
+circuit-breaker.ts, image-processor.ts) but core functionality has tests.
+```
+
+### Immediate Action Items (This Week)
+
+1. **Refactor duplicate tests** (callouts, links) - 2 hours
+2. **Set up GitHub Actions CI** - 2 hours
+3. **Remove useless tests** (TypeScript types) - 30 minutes
+4. **Start Phase 2: Add reference.ts tests** - Ongoing
+
+### Long-term Roadmap (9 weeks)
+
+```
+Weeks 1-2: Quick wins + cleanup
+Weeks 3-4: Critical coverage (reach 40%)
+Weeks 5-7: Comprehensive coverage (reach 60-70%)
+Weeks 8-9: Advanced testing (E2E, performance)
+```
+
+### Success Metrics
+
+**Phase 2 Complete (Week 4):**
+• Coverage ≥ 40% ✅
+• All Tier 1 modules have tests
+• CI/CD running on all PRs
+
+**Phase 3 Complete (Week 7):**
+• Coverage ≥ 60%
+• All high and medium risk modules tested
+• Integration tests in place
+
+**Phase 4 Complete (Week 9):**
+• Coverage ≥ 70%
+• E2E tests running nightly
+• Performance benchmarks established
+• Visual regression testing (optional)
+
+═══════════════════════════════════════════════════════════════════════════════
+
+## 📚 Additional Resources
+
+**Vitest Documentation:**
+• https://vitest.dev/guide/
+• https://vitest.dev/guide/coverage.html
+• https://vitest.dev/api/
+
+**Testing Best Practices:**
+• Kent C. Dodds - Testing Library principles
+• Martin Fowler - Test Pyramid
+• Google Testing Blog - Effective testing strategies
+
+**Tools to Consider:**
+• Codecov - Coverage tracking and visualization
+• Playwright - E2E testing (already installed ✅)
+• Percy/Chromatic - Visual regression testing
+• Benchmarkjs - Performance benchmarking
+
+═══════════════════════════════════════════════════════════════════════════════
+
+**Report Generated:** 2025-11-11
+**docs-engine version:** 2.0.0
+**Vitest version:** 4.0.7
+
+═══════════════════════════════════════════════════════════════════════════════
diff --git a/src/lib/plugins/callouts.test.ts b/src/lib/plugins/callouts.test.ts
index 75b749a..127ee1c 100644
--- a/src/lib/plugins/callouts.test.ts
+++ b/src/lib/plugins/callouts.test.ts
@@ -46,55 +46,31 @@ describe('callouts plugin', () => {
return tree;
};
- it('should transform NOTE callout', () => {
- const blockquote = createBlockquote('NOTE');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--blue');
- expect(html).toContain('Note');
- });
-
- it('should transform TIP callout', () => {
- const blockquote = createBlockquote('TIP');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--green');
- expect(html).toContain('Tip');
- });
-
- it('should transform WARNING callout', () => {
- const blockquote = createBlockquote('WARNING');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--yellow');
- expect(html).toContain('Warning');
- });
-
- it('should transform IMPORTANT callout', () => {
- const blockquote = createBlockquote('IMPORTANT');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--purple');
- expect(html).toContain('Important');
- });
-
- it('should transform CAUTION callout', () => {
- const blockquote = createBlockquote('CAUTION');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--red');
- expect(html).toContain('Caution');
- });
+ // Test all callout types with parameterized tests
+ const calloutTypes = [
+ { type: 'NOTE', cssClass: 'blue', title: 'Note' },
+ { type: 'TIP', cssClass: 'green', title: 'Tip' },
+ { type: 'WARNING', cssClass: 'yellow', title: 'Warning' },
+ { type: 'IMPORTANT', cssClass: 'purple', title: 'Important' },
+ { type: 'CAUTION', cssClass: 'red', title: 'Caution' },
+ { type: 'SUCCESS', cssClass: 'success', title: 'Success' },
+ { type: 'DANGER', cssClass: 'danger', title: 'Danger' },
+ { type: 'INFO', cssClass: 'info', title: 'Info' },
+ { type: 'QUESTION', cssClass: 'question', title: 'Question' },
+ ];
+
+ it.each(calloutTypes)(
+ 'should transform $type callout with correct class and title',
+ ({ type, cssClass, title }) => {
+ const blockquote = createBlockquote(type);
+ const tree = processTree(blockquote);
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const html = (tree.children[0] as any).value;
+ expect(html).toContain(`md-callout--${cssClass}`);
+ expect(html).toContain(title);
+ }
+ );
it('should not transform regular blockquotes', () => {
const blockquote = createBlockquote();
@@ -104,46 +80,6 @@ describe('callouts plugin', () => {
expect(tree.children[0].type).toBe('blockquote');
});
- it('should handle SUCCESS callout', () => {
- const blockquote = createBlockquote('SUCCESS');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--success');
- expect(html).toContain('Success');
- });
-
- it('should handle DANGER callout', () => {
- const blockquote = createBlockquote('DANGER');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--danger');
- expect(html).toContain('Danger');
- });
-
- it('should handle INFO callout', () => {
- const blockquote = createBlockquote('INFO');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--info');
- expect(html).toContain('Info');
- });
-
- it('should handle QUESTION callout', () => {
- const blockquote = createBlockquote('QUESTION');
- const tree = processTree(blockquote);
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const html = (tree.children[0] as any).value;
- expect(html).toContain('md-callout--question');
- expect(html).toContain('Question');
- });
-
it('should preserve ARIA attributes', () => {
const blockquote = createBlockquote('NOTE');
const tree = processTree(blockquote);
diff --git a/src/lib/plugins/collapse.test.ts b/src/lib/plugins/collapse.test.ts
new file mode 100644
index 0000000..7bc518b
--- /dev/null
+++ b/src/lib/plugins/collapse.test.ts
@@ -0,0 +1,648 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { describe, it, expect } from 'vitest';
+import { collapsePlugin } from './collapse';
+import type { Root } from 'mdast';
+
+describe('collapse plugin', () => {
+ const createCollapseDirective = (content: any[], attributes?: Record): Root => ({
+ type: 'root',
+ children: [
+ {
+ type: 'containerDirective' as any,
+ name: 'collapse',
+ attributes,
+ children: content,
+ },
+ ],
+ });
+
+ describe('basic transformation', () => {
+ it('should transform collapse directive to details element', () => {
+ const tree = createCollapseDirective([
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', value: 'Content here' }],
+ },
+ ]);
+
+ const plugin = collapsePlugin();
+ plugin(tree);
+
+ const htmlNode = tree.children[0] as any;
+ expect(htmlNode.type).toBe('html');
+ expect(htmlNode.value).toContain('');
+ expect(htmlNode.value).toContain('Content here
');
+ expect(htmlNode.children).toBeUndefined();
+ });
+
+ it('should use custom title from attributes', () => {
+ const tree = createCollapseDirective(
+ [
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', value: 'Content' }],
+ },
+ ],
+ { title: 'Custom Title' }
+ );
+
+ const plugin = collapsePlugin();
+ plugin(tree);
+
+ const htmlNode = tree.children[0] as any;
+ expect(htmlNode.value).toContain('Custom Title');
+ });
+
+ it('should use default title when not specified', () => {
+ const tree = createCollapseDirective([
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', value: 'Content' }],
+ },
+ ]);
+
+ const plugin = collapsePlugin();
+ plugin(tree);
+
+ const htmlNode = tree.children[0] as any;
+ expect(htmlNode.value).toContain('Details');
+ });
+
+ it('should be open by default', () => {
+ const tree = createCollapseDirective([
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', value: 'Content' }],
+ },
+ ]);
+
+ const plugin = collapsePlugin();
+ plugin(tree);
+
+ const htmlNode = tree.children[0] as any;
+ expect(htmlNode.value).toContain('');
+ });
+
+ it('should be closed when open=false', () => {
+ const tree = createCollapseDirective(
+ [
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', value: 'Content' }],
+ },
+ ],
+ { open: 'false' }
+ );
+
+ const plugin = collapsePlugin();
+ plugin(tree);
+
+ const htmlNode = tree.children[0] as any;
+ expect(htmlNode.value).toContain('');
+ expect(htmlNode.value).not.toContain('open');
+ });
+
+ it('should include chevron icon', () => {
+ const tree = createCollapseDirective([
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', value: 'Content' }],
+ },
+ ]);
+
+ const plugin = collapsePlugin();
+ plugin(tree);
+
+ const htmlNode = tree.children[0] as any;
+ expect(htmlNode.value).toContain('