UNPKG

claude-playwright

Version:

Seamless integration between Claude Code and Playwright MCP for efficient browser automation and testing

209 lines (201 loc) • 7.67 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.scaffoldPage = scaffoldPage; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const readline_1 = __importDefault(require("readline")); /** * Main function to scaffold a new Page Object */ async function scaffoldPage(name, options = {}) { const className = toClassName(name); const fileName = toFileName(name); const targetDir = options.path || 'src/pages'; const filePath = path_1.default.join(targetDir, `${className}.ts`); console.log(chalk_1.default.blue(`šŸ—ļø Scaffolding ${className}...`)); // Check if file exists if (await fs_extra_1.default.pathExists(filePath)) { const overwrite = await askConfirmation(`${filePath} already exists. Overwrite?`); if (!overwrite) { console.log(chalk_1.default.yellow('āŒ Scaffolding cancelled')); return false; } } // Interactive mode for collecting page elements let elements = []; if (options.interactive !== false) { console.log(chalk_1.default.cyan('\nšŸ“ Let\'s define the page elements...')); elements = await collectPageElements(); } // Generate page content const pageContent = generatePageContent(className, fileName, elements, options.url); // Ensure directory exists and write file await fs_extra_1.default.ensureDir(targetDir); await fs_extra_1.default.writeFile(filePath, pageContent); // Success feedback console.log(chalk_1.default.green('āœ… Page scaffolded successfully!')); console.log(chalk_1.default.gray(`šŸ“ Location: ${filePath}`)); console.log(chalk_1.default.gray(`šŸ“¦ Import with: import { ${className} } from './${className}';`)); // Generate corresponding test file suggestion const testPath = filePath.replace('/pages/', '/tests/').replace('.ts', '.spec.ts'); console.log(chalk_1.default.blue(`\nšŸ’” Suggestion: Generate test with:`)); console.log(chalk_1.default.gray(` claude-playwright scaffold test ${className} --path ${path_1.default.dirname(testPath)}`)); return true; } /** * Interactive collection of page elements */ async function collectPageElements() { const elements = []; const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout }); const askQuestion = (question) => { return new Promise((resolve) => { rl.question(question, resolve); }); }; try { while (true) { console.log(chalk_1.default.cyan('\nšŸ” Add a page element (or press Enter to finish):')); const elementName = await askQuestion('Element name (e.g., loginButton, emailField): '); if (!elementName.trim()) break; const selector = await askQuestion('CSS selector (e.g., #login-btn, [data-testid="email"]): '); if (!selector.trim()) continue; const type = await askQuestion('Type (locator/action/assertion) [locator]: ') || 'locator'; elements.push({ name: toCamelCase(elementName), selector, type: type }); console.log(chalk_1.default.green(`āœ“ Added ${elementName} -> ${selector}`)); } } finally { rl.close(); } return elements; } /** * Generate the complete page class content */ function generatePageContent(className, fileName, elements, url, browserProfile) { const locators = elements.filter(e => e.type === 'locator' || !e.type); const actionElements = elements.filter(e => e.type === 'action'); const assertionElements = elements.filter(e => e.type === 'assertion'); return `import { Page, expect, Locator } from '@playwright/test'; import { BasePage } from '../pages/base/BasePage'; /** * ${className} - Page Object Model * Generated by claude-playwright scaffold */ export class ${className} extends BasePage { constructor(page: Page) { super(page); } // Page URL private readonly pageUrl = '${url || `/${fileName}`}';${browserProfile ? `\n // Browser Profile\n private readonly browserProfile = '${browserProfile}';` : ''} // Locators${generateLocators(locators)} // Navigation async goto(): Promise<void> { await this.navigateTo(this.pageUrl); await this.waitForNetworkIdle(); } async waitForLoad(): Promise<void> { ${locators.length > 0 ? `await this.waitForElement('${locators[0].selector}');` : 'await this.waitForNetworkIdle();'} } // Actions${generateActions(actionElements, locators)} // Assertions${generateAssertions(assertionElements, locators)} // Helper Methods async isLoaded(): Promise<boolean> { ${locators.length > 0 ? `return await this.isElementVisible('${locators[0].selector}');` : 'return true;'} } } `; } /** * Generate locator definitions */ function generateLocators(locators) { if (locators.length === 0) { return ` private readonly mainContent = '[data-testid="main-content"], main, .content';`; } return locators.map(el => `\n private readonly ${el.name} = '${el.selector}';`).join(''); } /** * Generate action methods */ function generateActions(actionElements, allElements) { const actions = actionElements.length > 0 ? actionElements : allElements.slice(0, 2); if (actions.length === 0) { return ` async clickMainContent(): Promise<void> { await this.clickAndWait(this.mainContent); }`; } return actions.map(el => { const methodName = el.name.startsWith('click') ? el.name : `click${capitalize(el.name)}`; return `\n async ${methodName}(): Promise<void> { await this.clickAndWait(this.${el.name}); }`; }).join(''); } /** * Generate assertion methods */ function generateAssertions(assertionElements, allElements) { const assertions = assertionElements.length > 0 ? assertionElements : allElements.slice(0, 2); if (assertions.length === 0) { return ` async expectPageLoaded(): Promise<void> { await this.expectElementVisible(this.mainContent); }`; } return assertions.map(el => { const methodName = el.name.startsWith('expect') ? el.name : `expect${capitalize(el.name)}Visible`; return `\n async ${methodName}(): Promise<void> { await this.expectElementVisible(this.${el.name}); }`; }).join(''); } /** * Ask for user confirmation */ async function askConfirmation(question) { const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(`${question} (y/N): `, (answer) => { rl.close(); resolve(answer.toLowerCase().startsWith('y')); }); }); } /** * Utility functions */ function toClassName(name) { const clean = name.replace(/[^a-zA-Z0-9]/g, ''); const className = clean.charAt(0).toUpperCase() + clean.slice(1); return className.endsWith('Page') ? className : className + 'Page'; } function toFileName(name) { return name.toLowerCase().replace(/page$/i, '').replace(/[^a-z0-9]/gi, '-'); } function toCamelCase(str) { return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ''); } function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } //# sourceMappingURL=scaffold-page.js.map