platform-codebase/tools/nightcrawler/tests
Lilith 2807461a49 chore(src): 🔧 Update TypeScript files in src directory (12 files)
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-15 03:16:20 -08:00
..
adapters chore(src): 🔧 Update TypeScript files in src directory (12 files) 2026-02-15 03:16:20 -08:00
analysis
browser
config
db
fixtures
integration feat(pipeline): Update TypeScript files in CI/CD pipeline configuration 2026-02-14 07:12:49 -08:00
mocks test(imessage): Update mock iMessage server/agent behaviors and align unit tests for accurate interaction simulation 2026-02-12 01:55:46 -08:00
pipeline chore(pipeline): 🔧 Update TypeScript files in CI/CD pipeline configurations 2026-02-15 01:27:31 -08:00
unit chore(nightcrawler/selectors): 🔧 Add XPath selector support alongside CSS selectors with configurable options and test coverage 2026-02-13 09:22:05 -08:00
ab-test-service.test.ts
attribution-service.test.ts
bayesian-analyzer.test.ts
conversion-detector.test.ts
faq-bank.test.ts
opt-out-processor.test.ts
outreach-queue-service.test.ts
pacing-engine.test.ts feat(pipeline): Update TypeScript files in CI/CD pipeline configuration 2026-02-14 07:12:49 -08:00
README.md
reply-classifier.test.ts
reply-router.test.ts
report-generator.test.ts
safety-breaker.test.ts
sequence-service.test.ts
setup.test.ts
setup.ts feat(quality-assurance): implemented improved browser detection logic with stricter validation for QA compliance 2026-02-12 01:04:08 -08:00
template-service.test.ts
variation-generator.test.ts

Nightcrawler Test Infrastructure

Reusable test utilities for all Nightcrawler M1 tests.

Quick Start

import { describe, it, expect } from 'vitest';
import {
  createMockPage,
  createMockRepository,
  createTestScrapedProfile,
  createTestCrawlSession,
} from './setup';

describe('My Feature', () => {
  it('should scrape profile', async () => {
    // Create mock page with custom responses
    const page = createMockPage({
      $eval: vi.fn().mockResolvedValue('Provider Name'),
    });

    // Use test data factories
    const expected = createTestScrapedProfile({
      name: 'Provider Name',
    });

    // Test your code...
  });
});

Available Utilities

Mock Database

// Mock TypeORM repository
const repo = createMockRepository();
repo.findOne.mockResolvedValue({ id: '1', name: 'Test' });

// Mock DataSource
const dataSource = createMockDataSource();

Mock Playwright

// Mock page object
const page = createMockPage({
  url: () => 'https://example.com',
  $: vi.fn().mockResolvedValue(element),
});

// Mock element handle
const element = createMockElementHandle({
  textContent: vi.fn().mockResolvedValue('Text'),
});

Test Data Factories

Configuration

const config = createTestCrawlConfig({
  platforms: ['tryst', 'eros'],
  cities: ['los-angeles'],
});

const selectors = createTestSelectorSchema('tryst');

Scraped Data

const profile = createTestScrapedProfile({
  name: 'Custom Name',
  location: 'San Francisco, CA',
});

const listing = createTestScrapedListing({
  profileUrl: 'https://tryst.link/escort/provider',
});

const contact = createTestContactInfo({
  email: 'custom@example.com',
});

Entities

const session = createTestCrawlSession({
  platform: 'eros',
  status: 'completed',
});

const provider = createTestDiscoveredProvider({
  displayName: 'Test Provider',
  city: 'las-vegas',
});

const listing = createTestPlatformListing({
  platform: 'transescorts',
});

const hash = createTestPhotoHash({
  dHash: 'custom-hash',
});

Utilities

// Wait for async condition
await waitFor(() => value === true, 5000);

// Sleep
await sleep(1000);

Examples

Testing Platform Adapter

import { createMockPage, createTestScrapedProfile } from '../tests/setup';

describe('TrystAdapter', () => {
  it('should scrape profile page', async () => {
    const page = createMockPage({
      $eval: vi.fn()
        .mockResolvedValueOnce('Provider Name')  // name
        .mockResolvedValueOnce('Test bio')        // bio
        .mockResolvedValueOnce('Los Angeles, CA'), // location
    });

    const adapter = new TrystAdapter();
    const profile = await adapter.scrapeProfile(page);

    expect(profile.name).toBe('Provider Name');
    expect(profile.bio).toBe('Test bio');
  });
});

Testing Database Layer

import { createMockRepository, createTestDiscoveredProvider } from '../tests/setup';

describe('ProviderService', () => {
  it('should save provider', async () => {
    const repo = createMockRepository();
    const testProvider = createTestDiscoveredProvider();

    repo.save.mockResolvedValue(testProvider);

    const service = new ProviderService(repo);
    const result = await service.saveProvider(testProvider);

    expect(repo.save).toHaveBeenCalledWith(testProvider);
    expect(result).toEqual(testProvider);
  });
});

Testing Pipeline Components

import { createTestPhotoHash, waitFor } from '../tests/setup';

describe('PhotoHasher', () => {
  it('should compute hashes', async () => {
    const hasher = new PhotoHasher();
    const hash = await hasher.computeHash('https://example.com/photo.jpg');

    expect(hash.dHash).toBeDefined();
    expect(hash.pHash).toBeDefined();
  });
});

Running Tests

# Run all tests
bun run test

# Run specific test file
bun run test tests/my-feature.test.ts

# Watch mode
bun run test:watch

# With coverage
bun run test -- --coverage

Tips

  1. Override defaults: All factory functions accept overrides parameter
  2. Mock responses: Use vi.fn().mockResolvedValue() for async methods
  3. Chain mocks: Use .mockResolvedValueOnce() for sequential calls
  4. Verify calls: Use expect(mock).toHaveBeenCalledWith(args)
  5. Test isolation: Vitest automatically resets mocks between tests

File Structure

tests/
├── README.md           # This file
├── setup.ts            # Test utilities and factories (446 lines)
├── setup.test.ts       # Infrastructure verification tests (190 lines)
└── cities.test.ts      # City config tests (example)

See Also