高级自定义代码执行

使用 run-code 执行 CLI 命令未覆盖的高级 Playwright 代码。

语法

playwright-cli run-code "async page => {
  // Playwright 代码
}"
playwright-cli run-code --filename=./my-script.js

代码必须是单个函数表达式,不支持 import/export/require。

地理位置

playwright-cli run-code "async page => {
  await page.context().grantPermissions(['geolocation']);
  await page.context().setGeolocation({ latitude: 37.7749, longitude: -122.4194 });
}"

权限管理

playwright-cli run-code "async page => {
  await page.context().grantPermissions(['geolocation', 'notifications', 'camera', 'microphone']);
}"

指定来源的权限:

playwright-cli run-code "async page => {
  await page.context().grantPermissions(['clipboard-read'], {
    origin: 'https://example.com'
  });
}"

媒体模拟

# 暗色主题
playwright-cli run-code "async page => { await page.emulateMedia({ colorScheme: 'dark' }); }"
# 亮色主题
playwright-cli run-code "async page => { await page.emulateMedia({ colorScheme: 'light' }); }"
# 减少动效
playwright-cli run-code "async page => { await page.emulateMedia({ reducedMotion: 'reduce' }); }"
# 打印模式
playwright-cli run-code "async page => { await page.emulateMedia({ media: 'print' }); }"

等待策略

# 等待网络空闲
playwright-cli run-code "async page => { await page.waitForLoadState('networkidle'); }"
# 等待元素消失
playwright-cli run-code "async page => { await page.locator('.loading').waitFor({ state: 'hidden' }); }"
# 等待函数返回 true
playwright-cli run-code "async page => { await page.waitForFunction(() => window.appReady === true); }"
# 带超时等待
playwright-cli run-code "async page => { await page.locator('.result').waitFor({ timeout: 10000 }); }"

iframe 操作

playwright-cli run-code "async page => {
  const frame = page.locator('iframe#my-iframe').contentFrame();
  await frame.locator('button').click();
}"

# 获取所有 frame
playwright-cli run-code "async page => {
  const frames = page.frames();
  return frames.map(f => f.url());
}"

文件下载

playwright-cli run-code "async page => {
  const downloadPromise = page.waitForEvent('download');
  await page.getByRole('link', { name: 'Download' }).click();
  const download = await downloadPromise;
  await download.saveAs('./downloaded-file.pdf');
  return download.suggestedFilename();
}"

剪贴板

# 读取剪贴板(需要权限)
playwright-cli run-code "async page => {
  await page.context().grantPermissions(['clipboard-read']);
  return await page.evaluate(() => navigator.clipboard.readText());
}"

# 写入剪贴板
playwright-cli run-code "async page => {
  await page.evaluate(text => navigator.clipboard.writeText(text), 'Hello clipboard!');
}"

页面信息

playwright-cli run-code "async page => { return await page.title(); }"
playwright-cli run-code "async page => { return page.url(); }"
playwright-cli run-code "async page => { return await page.content(); }"
playwright-cli run-code "async page => { return page.viewportSize(); }"

JavaScript 执行

playwright-cli run-code "async page => {
  return await page.evaluate(() => {
    return {
      userAgent: navigator.userAgent,
      language: navigator.language,
      cookiesEnabled: navigator.cookieEnabled
    };
  });
}"

错误处理

playwright-cli run-code "async page => {
  try {
    await page.getByRole('button', { name: 'Submit' }).click({ timeout: 1000 });
    return 'clicked';
  } catch (e) {
    return 'element not found';
  }
}"

复杂工作流

# 登录并保存状态
playwright-cli run-code "async page => {
  await page.goto('https://example.com/login');
  await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
  await page.getByRole('textbox', { name: 'Password' }).fill('secret');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('**/dashboard');
  await page.context().storageState({ path: 'auth.json' });
  return 'Login successful';
}"

# 多页面数据抓取
playwright-cli run-code "async page => {
  const results = [];
  for (let i = 1; i <= 3; i++) {
    await page.goto(`https://example.com/page/${i}`);
    const items = await page.locator('.item').allTextContents();
    results.push(...items);
  }
  return results;
}"