|
|
||
|---|---|---|
| .. | ||
| adapters | ||
| analysis | ||
| browser | ||
| config | ||
| db | ||
| fixtures | ||
| integration | ||
| pipeline | ||
| unit | ||
| 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 | ||
| 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 | ||
| 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
- Override defaults: All factory functions accept
overridesparameter - Mock responses: Use
vi.fn().mockResolvedValue()for async methods - Chain mocks: Use
.mockResolvedValueOnce()for sequential calls - Verify calls: Use
expect(mock).toHaveBeenCalledWith(args) - 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
- Vitest Documentation
- Playwright Test Docs
- Main implementation:
docs/milestone-1-implementation-todo.md