Claude Code Skill for browser automation with Playwright. Model-invoked - Claude autonomously writes and executes custom automation for testing and validation.
SaferSkills independently audited playwright-skill (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Write and execute focused Playwright scripts for the user's request. Prefer the skill's executor and helpers, but use the full Playwright API when needed.
This skill can be installed in several locations, so resolve its directory first. Set SKILL_DIR to the directory containing this SKILL.md file, then run the commands below as written:
export SKILL_DIR=<absolute path of the directory containing this SKILL.md>
export TMP_DIR="$(node -p 'require("node:os").tmpdir()')"If shell state does not persist between commands, substitute the literal paths for $SKILL_DIR and $TMP_DIR in each command instead.
Common installation paths:
~/.claude/plugins/marketplaces/playwright-skill/skills/playwright-skill~/.claude/skills/playwright-skill<project>/.claude/skills/playwright-skill node -e "require('$SKILL_DIR/lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"Use the only result automatically. Ask which URL to use when there are multiple results. Ask for a URL or offer to start a server when none exist.
$TMP_DIR/playwright-test-*.js unless the userasks to save them in the project. Use PW_SCRIPT_DIR to preserve scripts.
headless: true only when requestedor when the environment has no display.
node "$SKILL_DIR/run.js" <script.js>.checking the resulting page.
Run once:
cd "$SKILL_DIR" && npm run setupThis installs Playwright and Chromium. Use cd "$SKILL_DIR" && npm run install-all-browsers when Firefox or WebKit is required.
const os = require('node:os');
const path = require('node:path');
const { chromium } = require('playwright');
const targetUrl = process.env.TARGET_URL || 'http://localhost:3000';
const artifactDir = process.env.PW_ARTIFACT_DIR || os.tmpdir();
(async () => {
const browser = await chromium.launch({ headless: false });
try {
const page = await browser.newPage();
await page.goto(targetUrl);
console.log('Page loaded:', await page.title());
await page.screenshot({ path: path.join(artifactDir, 'page.png'), fullPage: true });
} finally {
await browser.close();
}
})();Run it:
node "$SKILL_DIR/run.js" "$TMP_DIR/playwright-test-page.js"For short one-off tasks, use inline execution:
node "$SKILL_DIR/run.js" -e "const browser = await chromium.launch({headless: false}); try { const page = await browser.newPage(); await page.goto('https://example.com'); console.log(await page.title()); } finally { await browser.close(); }"The -e process exits as soon as the snippet settles, so close the browser inside the snippet.
Prefer locators that describe what a user sees, in this order:
page.getByRole() with an accessible namepage.getByLabel() for form controlspage.getByText() for visible contentpage.getByTestId() when the application provides a test contractActions auto-wait for actionability. Use web-first assertions or a locator's waitFor() instead of waitForSelector(), fixed sleeps, or networkidle.
await page.getByLabel('Email').fill('[email protected]');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('**/dashboard');
await page.getByRole('heading', { name: 'Dashboard' }).waitFor();{
const os = require('node:os');
const path = require('node:path');
const artifactDir = process.env.PW_ARTIFACT_DIR || os.tmpdir();
const viewports = [
{ name: 'desktop', width: 1440, height: 900 },
{ name: 'mobile', width: 390, height: 844 },
];
for (const viewport of viewports) {
await page.setViewportSize(viewport);
await page.goto(targetUrl);
await page.screenshot({ path: path.join(artifactDir, `${viewport.name}.png`), fullPage: true });
}
}Use test credentials supplied by the user. Never invent or expose real credentials. Verify both the navigation and a post-login element.
await page.goto(`${targetUrl}/login`);
await page.getByLabel('Email').fill(process.env.TEST_EMAIL);
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD);
await page.getByRole('button', { name: /sign in|log in/i }).click();
await page.waitForURL('**/dashboard');
await page.getByRole('heading', { name: /dashboard/i }).waitFor();PW_SCRIPT_DIR=./playwright-tests node "$SKILL_DIR/run.js" "$TMP_DIR/playwright-test-login.js"
PW_ARTIFACT_DIR=./playwright-artifacts node "$SKILL_DIR/run.js" "$TMP_DIR/playwright-test-page.js"PW_SCRIPT_DIR copies file-based scripts before execution and adds a timestamp when a filename already exists. PW_ARTIFACT_DIR controls helper screenshot output; the default is the operating system temporary directory.
Start Chrome with remote debugging enabled, then connect with Playwright:
const browser = await chromium.connectOverCDP('http://127.0.0.1:9222');
const page = browser.contexts()[0].pages()[0];This reuses cookies and extensions in that session. Do not use it for secrets unless the user explicitly asks; a connected browser has the user's access.
const helpers = require(`${process.env.PW_SKILL_DIR}/lib/helpers`);
const servers = await helpers.detectDevServers();
const browser = await helpers.launchBrowser('chromium');
const context = await helpers.createContext(browser);
const page = await context.newPage();
await helpers.handleCookieBanner(page);
await helpers.takeScreenshot(page, 'result');Available helpers are detectDevServers, getExtraHeadersFromEnv, launchBrowser, createContext, handleCookieBanner, and takeScreenshot. Use Playwright locators and assertions directly for actions, waits, extraction, authentication, tables, and retries.
PW_BROWSER: chromium, firefox, or webkit for launchBrowser().PW_CHANNEL: installed browser channel such as chrome or msedge.PW_EXECUTABLE_PATH: explicit browser executable path.PW_HEADLESS: true or false; visible mode is the default.SLOW_MO: action delay in milliseconds.PW_HEADER_NAME and PW_HEADER_VALUE: one extra HTTP header.PW_EXTRA_HEADERS: JSON object of extra HTTP headers.PW_SCRIPT_DIR: directory for preserving file-based scripts.PW_ARTIFACT_DIR: directory for helper-generated screenshots.See API_REFERENCE.md for network interception, API mocking, authentication state, video, visual checks, device emulation, and CI patterns.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.