Playwright自动化测试实战:从零掌握Cursor技能的高效用法

1次阅读
没有评论

共计 3022 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

为什么需要精细控制光标?

在 Web 自动化测试中,简单的 click()hover()往往难以应对复杂场景。以下是开发者常遇到的典型问题:

Playwright 自动化测试实战:从零掌握 Cursor 技能的高效用法

  • 动态元素遮挡:悬浮菜单展开时可能覆盖目标按钮
  • 坐标计算偏差:CSS transform 导致绝对定位失效
  • 异步加载延迟:元素可见但未达到可交互状态
  • 复合交互需求:需要模拟真实用户的拖拽轨迹

三种光标操作方式对比

方法 适用场景 缺陷
page.click() 稳定可见的普通元素 无法处理动态位移
page.hover() 触发悬浮菜单 不精确控制移动路径
cursor.move() 需要像素级控制的复杂交互 需手动处理坐标系转换

核心实现:从坐标计算到复合操作

1. 坐标系转换基础

Playwright 使用视口相对坐标,需注意:

/**
 * 获取元素中心点坐标(考虑滚动偏移)* @param selector - 目标元素选择器
 */
async function getElementCenter(selector: string) {const box = await page.locator(selector).boundingBox()
  return {
    x: box!.x + box!.width / 2,
    y: box!.y + box!.height / 2
  }
}

2. 带重试机制的复合操作

/**
 * 安全执行拖拽操作(含自动重试)* @param from - 起始元素选择器
 * @param to - 目标元素选择器
 * @param attempts - 最大重试次数
 */
async function reliableDrag(
  from: string, 
  to: string, 
  attempts = 3
) {for (let i = 0; i < attempts; i++) {
    try {const start = await getElementCenter(from)
      const end = await getElementCenter(to)

      await page.mouse.move(start.x, start.y)
      await page.mouse.down()
      await page.mouse.move(end.x, end.y, { steps: 20}) // 模拟真实拖拽
      await page.mouse.up()
      return
    } catch (err) {if (i === attempts - 1) throw err
      await page.waitForTimeout(500)
    }
  }
}

避坑指南:特殊场景处理

iframe 中的光标控制

// 切换到 iframe 上下文
const frame = page.frameLocator('iframe#content')
await frame.locator('.btn').hover()

// 返回主文档
await page.mouse.move(0, 0) // 重置光标位置

调试技巧

  1. 轨迹可视化:
    await page.screenshot({ 
      path: 'drag-path.png',
      clip: {x: 0, y: 0, width: 800, height: 600}
    })
  2. 慢动作模式:
    // 在 playwright.config.ts 中设置
    use: {launchOptions: { slowMo: 500}
    }

性能优化策略

智能等待实现

/**
 * 等待元素可交互状态
 * @param selector - 目标元素
 * @param timeout - 超时时间
 */
async function waitForInteractive(
  selector: string,
  timeout = 5000
) {await page.locator(selector).waitFor({ 
    state: 'visible',
    timeout 
  })

  // 额外检查元素是否启用
  const isDisabled = await page.locator(selector)
    .getAttribute('disabled')
  if (isDisabled) {throw new Error('Element is disabled')
  }
}

移动路径优化

// 不好的写法:产生冗余移动
await page.mouse.move(100, 100)
await page.mouse.move(200, 200)
await page.mouse.click(300, 300)

// 优化写法:直线路径
await page.mouse.move(300, 300)
await page.mouse.click(300, 300)

完整实战案例

场景 1:列表拖拽排序

/**
 * 实现列表项位置交换
 * @param listSelector - 列表容器
 * @param itemAIndex - 第一个项目索引
 * @param itemBIndex - 第二个项目索引
 */
async function swapListItems(
  listSelector: string,
  itemAIndex: number,
  itemBIndex: number
) {const items = await page.locator(`${listSelector} > li`)
  const itemA = items.nth(itemAIndex)
  const itemB = items.nth(itemBIndex)

  const aBox = await itemA.boundingBox()
  const bBox = await itemB.boundingBox()

  await page.mouse.move(
    aBox!.x + aBox!.width / 2,
    aBox!.y + aBox!.height / 2
  )
  await page.mouse.down()
  await page.mouse.move(
    bBox!.x + bBox!.width / 2,
    bBox!.y + bBox!.height / 2,
    {steps: 10}
  )
  await page.mouse.up()}

场景 2:右键菜单触发

/**
 * 安全触发右键上下文菜单
 * @param selector - 目标元素
 */
async function triggerContextMenu(selector: string) {await waitForInteractive(selector)

  const {x, y} = await getElementCenter(selector)

  try {await page.mouse.move(x, y)
    await page.mouse.click(x, y, { button: 'right'})
    await page.waitForSelector('.context-menu', {
      state: 'visible',
      timeout: 2000
    })
  } catch (err) {await page.keyboard.press('Escape') // 确保菜单关闭
    throw err
  }
}

进阶应用方向

  1. 视觉回归测试结合
  2. 在光标操作前后截图比对
  3. 使用expect(screenshot).toMatchSnapshot()

  4. 移动端模拟方案

    // 模拟触摸事件
    await page.touchscreen.tap(100, 200)
    
    // 模拟长按
    await page.touchscreen.touchStart(100, 200)
    await page.waitForTimeout(1000)
    await page.touchscreen.touchEnd()

通过系统性地掌握这些光标控制技术,可以显著提升自动化测试脚本的稳定性和真实用户行为模拟能力。建议在实际项目中从简单场景开始逐步应用,结合具体业务需求不断优化操作策略。

正文完
 0
评论(没有评论)