platform-codebase/tools/nightcrawler/tests
Lilith cd2ad88ddb chore(cli): 🔧 Update CLI JSON configuration files across 10 modules
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-12 00:46:18 -08:00
..
adapters
analysis test(nightcrawler): Add/update tests to fix failing/flaky components across nightcrawler 2026-02-07 19:51:06 -08:00
browser chore(nightcrawler): 🔧 Update configuration files (crawl-config.example.yaml, crawl-config.yaml) with new settings 2026-02-08 17:58:43 -08:00
config test(nightcrawler): Add/update tests to fix failing/flaky components across nightcrawler 2026-02-07 19:51:06 -08:00
db test(nightcrawler): Add/update tests to fix failing/flaky components across nightcrawler 2026-02-07 19:51:06 -08:00
fixtures chore(src): 🔧 Update 9 Python files in src directory for maintenance consistency 2026-02-08 19:36:34 -08:00
integration chore(components): 🔧 Update 17 PNG assets in component directory 2026-02-08 20:25:00 -08:00
pipeline chore(src): 🔧 Update TypeScript files in src directory (31 files) 2026-02-12 00:28:21 -08:00
unit chore(cli): 🔧 Update CLI JSON configuration files across 10 modules 2026-02-12 00:46:18 -08:00
ab-test-service.test.ts chore(src): 🔧 Update TypeScript files in src directory (7 files) 2026-02-07 23:47:36 -08:00
attribution-service.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
bayesian-analyzer.test.ts chore(src): 🔧 Update TypeScript files in src directory (7 files) 2026-02-07 23:47:36 -08:00
conversion-detector.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
faq-bank.test.ts chore(src): 🔧 Update TypeScript files in src directory (15 files) 2026-02-07 23:41:17 -08:00
opt-out-processor.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
outreach-queue-service.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
pacing-engine.test.ts chore(src): 🔧 Update TypeScript files in src directory (7 files) 2026-02-07 23:47:36 -08:00
README.md
reply-classifier.test.ts chore(src): 🔧 Update TypeScript files in src directory (15 files) 2026-02-07 23:41:17 -08:00
reply-router.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
report-generator.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
safety-breaker.test.ts chore(src): 🔧 Update TypeScript files in src directory (7 files) 2026-02-07 23:47:36 -08:00
sequence-service.test.ts feat(outreach): Add CRNN-based captcha solving, update message queue processing, and refine reply routing/reporting workflows 2026-02-07 23:53:25 -08:00
setup.test.ts
setup.ts chore(src): 🔧 Update TypeScript files in src directory (31 files) 2026-02-12 00:28:21 -08:00
template-service.test.ts chore(src): 🔧 Update TypeScript files in src directory (15 files) 2026-02-07 23:41:17 -08:00
variation-generator.test.ts chore(src): 🔧 Update TypeScript files in src directory (15 files) 2026-02-07 23:41:17 -08:00

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