arda-site-scan
Version:
A standalone CLI tool for comprehensive website analysis including screenshots, SEO, and accessibility testing using Playwright
462 lines (362 loc) • 17.4 kB
Markdown
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
### Build and Run
- `npm run build` - Compile TypeScript to `dist/` directory
- `npm start` - Run the CLI tool (starts interactive walkthrough)
- `npm run dev` - Build and start in one command (development workflow)
- `npm run clean` - Remove the `dist/` directory
### Running the Tool
The CLI defaults to interactive mode when run without arguments:
```bash
npm start
# or
node dist/cli.js
```
Explicit command to start walkthrough:
```bash
node dist/cli.js start
```
## Architecture Overview
This is a TypeScript CLI tool for automated website testing using Playwright. The architecture follows an **orchestrator pattern** with modular test implementations.
### Core Components
**Entry Point** (`src/cli.ts`)
- Commander.js-based CLI that defaults to interactive walkthrough
- Displays ASCII art banner and starts the walkthrough flow
**Test Orchestrator** (`src/orchestrator/test-orchestrator.ts`)
- Central coordinator that manages the entire testing session
- Handles browser lifecycle (Chromium via Playwright)
- Coordinates page discovery, test execution, and result generation
- Manages session directories and progress tracking
**Interactive Walkthrough** (`src/commands/walkthrough.ts`)
- Inquirer.js-based prompts for configuration
- Guides users through URL input, crawling options, and test selection
- Available tests: Screenshots, SEO Scan, Accessibility Scan
- Default viewports: desktop (1920x1080), tablet (768x1024), mobile (375x667)
**Test Libraries** (`src/lib/`)
- `crawlee-site-crawler.ts` - Site discovery using Crawlee with PlaywrightCrawler
- `screenshot-tester.ts` - Multi-viewport screenshot capture
- `seo-tester.ts` - SEO element analysis (meta tags, headings, links, etc.)
- `accessibility-tester.ts` - WCAG compliance testing with axe-core
- `content-scraper.ts` - Extracts page content and images to markdown files
- `sitemap-tester.ts` - Generates XML sitemaps for search engine submission
- `site-summary-tester.ts` - Creates comprehensive site overview reports
**Utilities** (`src/utils/`)
- `session-manager.ts` - File organization and report generation
- `progress-tracker.ts` - Real-time progress display during execution
- `validation.ts` - URL validation for user input
- `ascii-art.ts` - CLI branding and welcome screens
- `parallel-executor.ts` - Task parallelization with concurrency control
- `session-data-store.ts` - Data persistence and retrieval for test sessions
### Key Implementation Details
**Three-Phase Execution Strategy** (`src/types/test-phases.ts`)
The orchestrator organizes tests into three sequential phases for optimal performance:
1. **Phase 1: Data Discovery & Collection**
- Site crawling (discovers all pages)
- Content scraping for all pages
- Sitemap generation from discovered URLs
2. **Phase 2: Page Analysis & Testing**
- Screenshots across all viewports (desktop, tablet, mobile)
- SEO scans (meta tags, headings, links, structured data)
- Accessibility testing with axe-core
3. **Phase 3: Report Generation & Finalization**
- Site summary using scraped content
- Session reports and statistics
**Parallel Execution System**
- Uses `ParallelExecutor` utility for concurrent task execution
- Configurable concurrency limits per phase (Phase 1: 3, Phase 2: 5, Phase 3: 2)
- Progress tracking with real-time updates
- Error handling with graceful degradation
**Session Management**
- Results organized in timestamped directories: `playwright-site-scanner-sessions/MM-DD-YYYY_HH-MM/`
- Each page gets its own subdirectory with screenshots and scan reports
- Session-level summary with statistics and error tracking
**Site Crawling**
- Uses Crawlee's PlaywrightCrawler for page discovery
- Restricts crawling to same domain as starting URL
- Configurable max pages (default: 50) to prevent runaway crawls
- Respects `networkidle` state for consistent page loading
**Browser Management**
- Single Chromium browser instance managed by TestOrchestrator
- Headless mode with sandbox disabled for compatibility
- New page context per URL to isolate tests
- Proper cleanup on session completion or error
**Test Execution Flow**
1. Browser initialization
2. Page discovery (single URL or site crawl)
3. Session directory creation
4. Per-page testing loop with progress tracking
5. Result aggregation and summary generation
6. Browser cleanup
**HTML Reporter Integration**
- Interactive HTML reports generated using custom `HTMLReporter` class
- Cross-platform auto-opening functionality with user-configurable behavior
- Screenshot embedding with click-to-enlarge modal functionality
- Integrated with three-phase orchestrator pattern for comprehensive test result display
**TypeScript Configuration**
- Targets ES2022 with CommonJS modules
- Outputs to `dist/` with source maps and declarations
- Strict mode enabled with comprehensive type checking
## Known Issues & Fixes
### HTML Reporter Duplicate Test Results (Fixed - August 2025)
**Issue**: HTML Reporter was displaying duplicate content-scraping tests under each page, showing all content-scraping results from all pages rather than just the relevant tests for each specific page.
**Root Cause**: Flawed filtering logic in `TestOrchestrator.ts` at two locations (lines 369-373 and 493-497) that included a redundant condition:
```typescript
// Problematic filtering logic
const pageTests = this.allTestResults.filter(result => {
return result.outputPath?.includes(this.sessionManager.getPageName(url)) ||
result.testType === 'content-scraping'; // ← This was the problem
});
```
The `|| result.testType === 'content-scraping'` condition incorrectly included ALL content-scraping tests from all pages for every page's results, causing massive duplication in multi-page crawls.
**Solution**: Removed the redundant `|| result.testType === 'content-scraping'` condition from both filtering locations, leaving only the page-specific outputPath matching:
```typescript
// Corrected filtering logic
const pageTests = this.allTestResults.filter(result => {
return result.outputPath?.includes(this.sessionManager.getPageName(url));
});
```
**Result**: Each page in HTML reports now shows only its own test results, eliminating duplicate display while maintaining all correct functionality.
**Files Modified**: `src/orchestrator/test-orchestrator.ts` (both `generateFinalSessionSummary()` and `generateHTMLReports()` methods)
## Adding New Playwright Tests
This section provides a comprehensive workflow for adding new test capabilities to the Playwright Site Scanner. Follow these steps to ensure proper integration with the orchestrator pattern and three-phase execution system.
### Test Implementation Architecture
The project uses a **modular orchestrator pattern** where:
- **Test Orchestrator** (`src/orchestrator/test-orchestrator.ts`) coordinates all testing
- **Individual Test Classes** (`src/lib/*-tester.ts`) implement specific testing capabilities
- **Phase Classification System** (`src/types/test-phases.ts`) organizes tests into execution phases
- **Walkthrough Interface** (`src/commands/walkthrough.ts`) presents test options to users
### Step-by-Step Implementation Workflow
#### 1. Create the Test Class
Create a new file in `src/lib/` following the naming convention `*-tester.ts`:
```typescript
// src/lib/my-new-tester.ts
import { Page } from 'playwright';
import chalk from 'chalk';
import { TestResult } from '../types/index.js';
import { SessionManager } from '../utils/session-manager.js';
export class MyNewTester {
private sessionManager: SessionManager;
constructor() {
this.sessionManager = new SessionManager();
}
async runMyNewTest(page: Page, pageUrl: string, sessionId: string): Promise<TestResult> {
const startTime = new Date();
const pageName = this.sessionManager.getPageName(pageUrl);
const testResult: TestResult = {
testType: 'my-new-test',
status: 'pending',
startTime
};
try {
console.log(chalk.gray(` 🔍 Running my new test...`));
// Your test implementation here
const testData = await this.performTestLogic(page);
const report = this.generateReport(testData, pageUrl);
// Save results
await this.sessionManager.createPageDirectory(sessionId, pageName);
const outputPath = this.sessionManager.getScanPath(sessionId, pageName, 'my-new-test');
const fs = await import('fs/promises');
await fs.writeFile(outputPath, report, 'utf8');
testResult.status = 'success';
testResult.outputPath = outputPath;
testResult.endTime = new Date();
return testResult;
} catch (error) {
console.error(chalk.red(` ❌ My new test failed: ${error}`));
testResult.status = 'failed';
testResult.error = error instanceof Error ? error.message : 'Unknown error';
testResult.endTime = new Date();
return testResult;
}
}
private async performTestLogic(page: Page): Promise<any> {
// Implement your test logic here
return {};
}
private generateReport(data: any, pageUrl: string): string {
// Generate your report format here
return `# My New Test Report\n\nURL: ${pageUrl}\n\n${JSON.stringify(data, null, 2)}`;
}
}
```
#### 2. Add Test to Phase Classification System
Update `src/types/test-phases.ts` to include your new test in the `TEST_CLASSIFICATIONS` object:
```typescript
// Add to TEST_CLASSIFICATIONS object
'my-new-test': {
testId: 'my-new-test',
phase: 2, // Choose appropriate phase (1, 2, or 3)
scope: 'page', // 'page' or 'session'
executionOrder: 4, // Order within the phase
dependencies: [], // List of test IDs this test depends on
conflictsWith: [], // List of test IDs that conflict with this test
resourceIntensive: false // true if this test uses significant resources
}
```
#### 3. Add Test to Walkthrough Options
Update `src/commands/walkthrough.ts` to include your test in the `AVAILABLE_TESTS` array:
```typescript
// Add to AVAILABLE_TESTS array
{
id: 'my-new-test',
name: 'My New Test',
description: 'Description of what this test does',
enabled: false // Default state in the interactive menu
}
```
#### 4. Integrate with Test Orchestrator
Update `src/orchestrator/test-orchestrator.ts` to integrate your test:
1. **Import your test class:**
```typescript
import { MyNewTester } from '../lib/my-new-tester.js';
```
2. **Add property to the class:**
```typescript
private myNewTester: MyNewTester;
```
3. **Initialize in constructor:**
```typescript
this.myNewTester = new MyNewTester();
```
4. **Add execution logic in the appropriate phase method:**
For Phase 2 tests (most common), add to the test type handling in `executePhase2()`:
```typescript
} else if (testType === 'my-new-test') {
allPageTasks.push({
id: `${testType}-${url}`,
name: `My New Test (${new URL(url).pathname})`,
execute: async () => {
const page = await this.browser!.newPage();
try {
await page.goto(url, { waitUntil: 'networkidle' });
return await this.myNewTester.runMyNewTest(page, url, this.dataManager!.sessionId);
} finally {
await page.close();
}
}
});
}
```
### Phase Classification Guidelines
#### Phase 1: Data Discovery & Collection
- **Use for:** Site crawling, content extraction, data collection that other tests depend on
- **Scope:** Usually 'session' for site-wide operations, 'page' for per-page data collection
- **Examples:** `site-crawling`, `content-scraping`, `sitemap`
- **Concurrency:** Conservative (3 max) due to resource intensity
#### Phase 2: Page Analysis & Testing
- **Use for:** Screenshot capture, SEO analysis, accessibility testing, performance testing
- **Scope:** Typically 'page' for per-page analysis
- **Examples:** `screenshots`, `seo`, `accessibility`
- **Concurrency:** Higher (5 max) for parallel page analysis
#### Phase 3: Report Generation & Finalization
- **Use for:** Summary generation, aggregated reports, final analysis
- **Scope:** Usually 'session' for site-wide summaries
- **Examples:** `site-summary`
- **Concurrency:** Lower (2 max) for resource-intensive report generation
### Technical Requirements
#### File Naming and Structure
- **Location:** `src/lib/`
- **Naming:** `*-tester.ts` (e.g., `performance-tester.ts`)
- **Class naming:** PascalCase ending in `Tester` (e.g., `PerformanceTester`)
#### Required Dependencies
- Import `Page` from `playwright` for browser automation
- Import `TestResult` from `../types/index.js` for return type compliance
- Import `SessionManager` from `../utils/session-manager.js` for file management
- Import `chalk` for consistent console output formatting
#### TestResult Interface Compliance
Your test method must return a `TestResult` object with:
- `testType: string` - Unique identifier for your test
- `status: 'pending' | 'success' | 'failed'` - Current test status
- `startTime: Date` - When the test began
- `endTime?: Date` - When the test completed (if finished)
- `outputPath?: string` - Path to generated report file
- `error?: string` - Error message if test failed
#### Session Management Integration
Use `SessionManager` methods for consistent file organization:
- `getPageName(url)` - Convert URL to safe directory name
- `createPageDirectory(sessionId, pageName)` - Ensure page directory exists
- `getScanPath(sessionId, pageName, testType)` - Get standardized file path
#### Error Handling Patterns
Always wrap test execution in try/catch blocks:
- Set `status: 'failed'` on error
- Capture error message in `error` field
- Always set `endTime` even on failure
- Log errors with `chalk.red()` for consistency
### Resource and Concurrency Considerations
#### Resource Intensive Tests
Mark tests as `resourceIntensive: true` if they:
- Generate large files (screenshots, comprehensive reports)
- Perform heavy DOM analysis
- Make many network requests
- Use significant CPU for processing
#### Conflict Management
Use `conflictsWith` array for tests that:
- Modify viewport settings (screenshots vs accessibility)
- Change page state in incompatible ways
- Compete for same resources
#### Dependencies
Use `dependencies` array when your test:
- Requires data from another test (e.g., needs crawling results)
- Must run after specific setup tests
- Depends on session-level configuration
### Testing and Validation
After implementing your new test:
1. **Build and test:**
```bash
npm run build
npm run dev
```
2. **Verify integration:**
- Your test appears in the walkthrough menu
- Test executes in the correct phase
- Results appear in HTML reports
- No TypeScript compilation errors
3. **Test error scenarios:**
- Verify graceful handling of page load failures
- Test behavior with invalid URLs
- Ensure proper cleanup on errors
### Common Implementation Patterns
#### Page Analysis Pattern
```typescript
async runAnalysis(page: Page, pageUrl: string, sessionId: string): Promise<TestResult> {
// Standard setup
const startTime = new Date();
const pageName = this.sessionManager.getPageName(pageUrl);
const testResult: TestResult = { testType: 'analysis', status: 'pending', startTime };
try {
// Wait for page to be ready
await page.waitForLoadState('networkidle');
// Extract data
const data = await page.evaluate(() => {
// Browser context data extraction
return { /* extracted data */ };
});
// Process and save results
const report = this.formatReport(data);
const outputPath = await this.saveReport(sessionId, pageName, report);
return { ...testResult, status: 'success', outputPath, endTime: new Date() };
} catch (error) {
return { ...testResult, status: 'failed', error: error.message, endTime: new Date() };
}
}
```
#### Session-Level Pattern
```typescript
async generateSessionReport(sessionId: string, urls: string[]): Promise<TestResult> {
const startTime = new Date();
const testResult: TestResult = { testType: 'session-report', status: 'pending', startTime };
try {
// Aggregate data from all pages
const aggregatedData = await this.aggregatePageData(sessionId, urls);
// Generate session-level report
const report = this.createSessionSummary(aggregatedData);
const outputPath = this.sessionManager.getSessionFilePath(sessionId, 'session-summary.html');
await this.saveSessionReport(outputPath, report);
return { ...testResult, status: 'success', outputPath, endTime: new Date() };
} catch (error) {
return { ...testResult, status: 'failed', error: error.message, endTime: new Date() };
}
}
```
This workflow ensures your new test integrates seamlessly with the existing architecture and maintains consistency with the project's patterns and standards.