Skip to content

Integration Tests

Overview

Integration tests verify that multiple components work correctly together. They test the complete processing pipeline using JSDOM to simulate a browser environment without the overhead of a real browser.

Location

Integration tests are in test/integration/:

test/integration/
├── pattern-processing.test.ts  # All patterns working together
└── element-positioning.test.ts # SPAN vs SUP/SUB positioning

Running Integration Tests

bash
# Run all integration tests
npx vitest run test/integration

# Watch mode
npx vitest watch test/integration

# With debugging
DEBUG=true npx vitest run test/integration

What We Test

Pattern Processing Pipeline

Tests the complete flow from text input to DOM output:

typescript
describe('Pattern processing pipeline', () => {
  it('should process multiple patterns in same text', () => {
    const element = createTestElement('H2O and CO2 at 25°C')
    engine.processElement(element)
    
    // Verify all patterns processed correctly
    const subs = element.querySelectorAll('sub')
    expect(subs).toHaveLength(2)
    expect(subs[0].textContent).toBe('2')
    expect(subs[1].textContent).toBe('2')
    
    const sup = element.querySelector('sup')
    expect(sup?.textContent).toBe('°')
  })
})

Element Positioning System

Tests the hybrid SPAN/SUP approach:

typescript
describe('Element positioning', () => {
  it('should use SPAN for positioned elements', () => {
    const element = createTestElement('Product(TM)')
    engine.processElement(element)
    
    const tm = element.querySelector('.ss-tm')
    expect(tm?.tagName).toBe('SPAN')
    
    // Verify CSS positioning works
    const styles = getComputedStyle(tm)
    expect(styles.position).toBe('relative')
  })

  it('should use SUP for semantic elements', () => {
    const element = createTestElement('x^2')
    engine.processElement(element)
    
    const sup = element.querySelector('sup')
    expect(sup?.tagName).toBe('SUP')
  })
})

Configuration Flow

Tests config propagation through the system:

typescript
describe('Configuration integration', () => {
  it('should apply custom CSS variables', () => {
    const config = createTestConfig({
      cssVariables: { 'tm-top': '-0.5em' }
    })
    
    const engine = new SmartScriptEngine(config)
    // Process and verify CSS variables applied
  })

  it('should respect disabled patterns', () => {
    const config = createTestConfig({
      symbols: { 
        trademark: { enabled: false }
      }
    })
    
    const element = createTestElement('(TM)')
    engine.processElement(element, config)
    
    // Should not transform
    expect(element.textContent).toBe('(TM)')
  })
})

Test Scenarios

Complex Document Processing

typescript
describe('Complex documents', () => {
  it('should handle nested elements', () => {
    const html = `
      <div>
        <p>H2O in <strong>bold</strong></p>
        <span>CO2 in <em>italic</em></span>
      </div>
    `
    const element = createTestElement(html)
    engine.processElement(element)
    
    // Verify processing doesn't break nesting
    expect(element.querySelector('strong')).toBeTruthy()
    expect(element.querySelector('em')).toBeTruthy()
  })
})

Performance Characteristics

typescript
describe('Batch processing', () => {
  it('should process in batches', async () => {
    const config = createTestConfig({
      performance: { batchSize: 5 }
    })
    
    // Create element with many text nodes
    const element = createLargeElement(100)
    
    const processSpy = vi.spyOn(engine, 'processBatch')
    await engine.processElement(element, config)
    
    // Should have processed in batches of 5
    expect(processSpy).toHaveBeenCalledTimes(20)
  })
})

Error Handling

typescript
describe('Error recovery', () => {
  it('should continue processing after error', () => {
    const element = createTestElement('Valid (TM) and invalid')
    
    // Inject error for testing
    vi.spyOn(processor, 'processMatch')
      .mockImplementationOnce(() => { throw new Error('Test error') })
      .mockImplementation(originalImplementation)
    
    engine.processElement(element)
    
    // Should still process other patterns
    expect(element.querySelector('.ss-tm')).toBeTruthy()
  })
})

Test Helpers

Integration tests use comprehensive helpers:

typescript
import { 
  setupDOM, 
  cleanupDOM, 
  createTestElement,
  createTestConfig,
  countTransformedElements 
} from '../helpers/setup'

beforeEach(() => {
  setupDOM()
})

afterEach(() => {
  cleanupDOM()
})

Best Practices

1. Test Real Workflows

Integration tests should mirror actual usage:

typescript
// Good ✅ - Tests real workflow
it('should process document like Nuxt would', () => {
  const doc = createRealDocument()
  const config = loadNuxtConfig()
  engine.processDocument(doc, config)
  // Verify results
})

// Bad ❌ - Too isolated
it('should call processNode', () => {
  const spy = vi.spyOn(engine, 'processNode')
  engine.process()
  expect(spy).toHaveBeenCalled()
})

2. Use Realistic Data

typescript
const testDocuments = {
  scientific: 'The reaction of H2O with CO2 produces H2CO3',
  math: 'The equation x^2 + y^2 = r^2 represents a circle',
  business: 'Our Product(TM) is ISO(R) certified',
  mixed: 'At 25°C, H2O has a pH of 7.0'
}

3. Test State Management

typescript
describe('State management', () => {
  it('should handle multiple processing calls', () => {
    const element = createTestElement('(TM)')
    
    engine.processElement(element)
    const firstResult = element.innerHTML
    
    engine.processElement(element)
    const secondResult = element.innerHTML
    
    // Should not double-process
    expect(secondResult).toBe(firstResult)
  })
})

Common Integration Patterns

Testing MutationObserver

typescript
describe('Dynamic content', () => {
  it('should process dynamically added content', async () => {
    const container = createTestElement('')
    engine.observe(container)
    
    // Add content dynamically
    container.innerHTML = 'New content with (TM)'
    
    // Wait for processing
    await waitForProcessing()
    
    expect(container.querySelector('.ss-tm')).toBeTruthy()
  })
})

Testing Exclusions

typescript
describe('Exclusion rules', () => {
  it('should respect no-superscript class', () => {
    const element = createTestElement(
      '<div class="no-superscript">H2O</div>'
    )
    engine.processElement(element)
    
    expect(element.querySelector('sub')).toBeFalsy()
  })

  it('should respect data-no-superscript', () => {
    const element = createTestElement(
      '<div data-no-superscript>x^2</div>'
    )
    engine.processElement(element)
    
    expect(element.querySelector('sup')).toBeFalsy()
  })
})

Debugging Integration Tests

bash
# Enable debug logging
DEBUG=true npx vitest run test/integration

# Run specific scenario
npx vitest run test/integration -t "complex document"

# Step debugging
node --inspect-brk ./node_modules/.bin/vitest run test/integration

Performance Considerations

Integration tests are slower than unit tests but faster than E2E:

  • Average runtime: 50-200ms per test
  • Use JSDOM instead of real browser
  • Mock external dependencies when possible
  • Run in parallel when appropriate

Apache 2.0 License