feat: add playwright-pro plugin — production-grade Playwright testing toolkit (#254)
Complete Claude Code plugin with: - 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report) - 3 specialized agents (test-architect, test-debugger, migration-planner) - 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility) - TestRail MCP server (TypeScript) — 8 tools for bidirectional sync - BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing - Smart hooks (auto-validate tests, auto-detect Playwright projects) - 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests) - Leverages Claude Code built-ins (/batch, /debug, Explore subagent) - Zero-config for core features; TestRail/BrowserStack via env vars - Both TypeScript and JavaScript support throughout Co-authored-by: Leo <leo@openclaw.ai>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import type {
|
||||
BrowserStackConfig,
|
||||
BrowserStackPlan,
|
||||
BrowserStackBrowser,
|
||||
BrowserStackBuild,
|
||||
BrowserStackSession,
|
||||
BrowserStackSessionUpdate,
|
||||
} from './types.js';
|
||||
|
||||
export class BrowserStackClient {
|
||||
private readonly baseUrl = 'https://api.browserstack.com';
|
||||
private readonly headers: Record<string, string>;
|
||||
|
||||
constructor(config: BrowserStackConfig) {
|
||||
const auth = Buffer.from(`${config.username}:${config.accessKey}`).toString('base64');
|
||||
this.headers = {
|
||||
Authorization: `Basic ${auth}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body?: unknown,
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
const options: RequestInit = {
|
||||
method,
|
||||
headers: this.headers,
|
||||
};
|
||||
if (body) {
|
||||
options.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(
|
||||
`BrowserStack API error ${response.status}: ${errorText}`,
|
||||
);
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
async getPlan(): Promise<BrowserStackPlan> {
|
||||
return this.request<BrowserStackPlan>('GET', '/automate/plan.json');
|
||||
}
|
||||
|
||||
async getBrowsers(): Promise<BrowserStackBrowser[]> {
|
||||
return this.request<BrowserStackBrowser[]>('GET', '/automate/browsers.json');
|
||||
}
|
||||
|
||||
async getBuilds(limit?: number, status?: string): Promise<BrowserStackBuild[]> {
|
||||
let endpoint = '/automate/builds.json';
|
||||
const params: string[] = [];
|
||||
if (limit) params.push(`limit=${limit}`);
|
||||
if (status) params.push(`status=${status}`);
|
||||
if (params.length > 0) endpoint += `?${params.join('&')}`;
|
||||
return this.request<BrowserStackBuild[]>('GET', endpoint);
|
||||
}
|
||||
|
||||
async getSessions(buildId: string, limit?: number): Promise<BrowserStackSession[]> {
|
||||
let endpoint = `/automate/builds/${buildId}/sessions.json`;
|
||||
if (limit) endpoint += `?limit=${limit}`;
|
||||
return this.request<BrowserStackSession[]>('GET', endpoint);
|
||||
}
|
||||
|
||||
async getSession(sessionId: string): Promise<BrowserStackSession> {
|
||||
return this.request<BrowserStackSession>(
|
||||
'GET',
|
||||
`/automate/sessions/${sessionId}.json`,
|
||||
);
|
||||
}
|
||||
|
||||
async updateSession(
|
||||
sessionId: string,
|
||||
update: BrowserStackSessionUpdate,
|
||||
): Promise<BrowserStackSession> {
|
||||
return this.request<BrowserStackSession>(
|
||||
'PUT',
|
||||
`/automate/sessions/${sessionId}.json`,
|
||||
update,
|
||||
);
|
||||
}
|
||||
|
||||
async getSessionLogs(sessionId: string): Promise<string> {
|
||||
const url = `${this.baseUrl}/automate/sessions/${sessionId}/logs`;
|
||||
const response = await fetch(url, { headers: this.headers });
|
||||
if (!response.ok) {
|
||||
throw new Error(`BrowserStack logs error ${response.status}`);
|
||||
}
|
||||
return response.text();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user