## Phase 8: Enterprise Extractor Improvements ✅ - 14 security extractors (TLS, JWT, SQL injection, XSS, etc.) - 10 framework-specific extractors (Spring, Django, Rails, etc.) - Config file security detection (YAML, TOML) ## Phase 9: Autonomous Extractor Generation ✅ - Shadow mode executor with TP/FP tracking - Graduation pipeline with confidence thresholds - Auto-rollback on regression detection - Cross-project pattern syncing ## UAT Suite Complete (14 scripts, 90 tests) - test-core-detection.sh (6 tests) - test-declarative-extractors.sh (5 tests) - test-domain-frameworks.sh (5 tests) - test-domain-unreal.sh (3 tests) - test-llm-extraction.sh (6 tests) - test-eval-harness.sh (5 tests) - test-cross-language.sh (3 tests) - test-precommit-performance.sh (4 tests) - test-output-formats.sh (8 tests) - test-drift-detection.sh (6 tests) - test-exit-codes.sh (12 tests) + 3 more scripts ## Other Changes - Updated roadmap to mark Phase 8-9 complete - Added .gitignore entries for build artifacts - Updated pre-commit: 800 line limit, exclude tests/data/cmd Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
import { chromium, Browser, Page } from '@playwright/test';
|
|
import * as fs from 'fs/promises';
|
|
|
|
export interface FormAction {
|
|
type: 'fill' | 'click' | 'check';
|
|
selector: string;
|
|
value?: string;
|
|
}
|
|
|
|
export interface ScreenshotConfig {
|
|
url: string;
|
|
name: string;
|
|
waitFor?: string; // CSS selector to wait for
|
|
delay?: number; // ms to wait after page load
|
|
actions?: FormAction[]; // Actions to perform before screenshot
|
|
}
|
|
|
|
export interface CaptureOptions {
|
|
baseUrl: string;
|
|
outputDir: string;
|
|
viewport?: { width: number; height: number };
|
|
}
|
|
|
|
const DEFAULT_OPTIONS: CaptureOptions = {
|
|
baseUrl: 'http://localhost:18188',
|
|
outputDir: 'screenshots',
|
|
viewport: { width: 1920, height: 1080 },
|
|
};
|
|
|
|
export async function captureScreenshots(
|
|
screenshots: ScreenshotConfig[],
|
|
options: Partial<CaptureOptions> = {}
|
|
): Promise<string[]> {
|
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
const browser = await chromium.launch();
|
|
const page = await browser.newPage();
|
|
await page.setViewportSize(opts.viewport!);
|
|
|
|
const captured: string[] = [];
|
|
|
|
for (const config of screenshots) {
|
|
const url = config.url.startsWith('http')
|
|
? config.url
|
|
: `${opts.baseUrl}${config.url}`;
|
|
|
|
await page.goto(url, { waitUntil: 'networkidle' });
|
|
|
|
// Execute any form actions before screenshot
|
|
if (config.actions) {
|
|
for (const action of config.actions) {
|
|
if (action.type === 'fill' && action.value) {
|
|
await page.fill(action.selector, action.value);
|
|
} else if (action.type === 'click') {
|
|
await page.click(action.selector);
|
|
} else if (action.type === 'check') {
|
|
await page.check(action.selector);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (config.waitFor) {
|
|
await page.waitForSelector(config.waitFor, { timeout: 10000 });
|
|
}
|
|
|
|
if (config.delay) {
|
|
await page.waitForTimeout(config.delay);
|
|
}
|
|
|
|
const path = `${opts.outputDir}/${config.name}`;
|
|
await page.screenshot({ path, type: 'png' });
|
|
console.log(`✓ ${config.name}`);
|
|
captured.push(path);
|
|
}
|
|
|
|
await browser.close();
|
|
return captured;
|
|
}
|
|
|
|
export async function captureSingle(
|
|
url: string,
|
|
name: string,
|
|
options: Partial<CaptureOptions> = {}
|
|
): Promise<string> {
|
|
const [path] = await captureScreenshots([{ url, name }], options);
|
|
return path;
|
|
}
|
|
|
|
export { Browser, Page };
|
|
|
|
// CLI runner
|
|
async function main() {
|
|
const configPath = process.argv[2];
|
|
if (!configPath) {
|
|
console.error('Usage: pnpm capture <config.json>');
|
|
console.error('');
|
|
console.error('Config format:');
|
|
console.error(JSON.stringify({
|
|
baseUrl: 'http://localhost:18188',
|
|
outputDir: 'screenshots',
|
|
screenshots: [
|
|
{ url: '/page', name: 'screenshot.png' },
|
|
{ url: '/other', name: 'other.png', waitFor: '.selector', delay: 500 },
|
|
],
|
|
}, null, 2));
|
|
process.exit(1);
|
|
}
|
|
|
|
const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
|
|
|
|
// Ensure output directory exists
|
|
await fs.mkdir(config.outputDir || 'screenshots', { recursive: true });
|
|
|
|
await captureScreenshots(config.screenshots, {
|
|
baseUrl: config.baseUrl,
|
|
outputDir: config.outputDir,
|
|
});
|
|
|
|
console.log(`\nDone! ${config.screenshots.length} screenshots saved to ${config.outputDir || 'screenshots'}/`);
|
|
}
|
|
|
|
main();
|