使用 YAML 格式的自动化脚本

在大多数情况下,开发者编写自动化脚本只是为了执行一些简单流程,比如检查某些内容是否出现,或者验证某个关键用户路径是否可用。此时维护一个大型测试项目会显得毫无必要。

⁠Midscene 提供了一种基于 .yaml 文件的自动化测试方法,这有助于你专注于编写流程,而不是测试框架。

这里有一个示例,通过阅读它的内容,你应该已经理解了它的工作原理。

web:
  url: https://www.bing.com

tasks:
  - name: 搜索天气
    flow:
      - ai: 搜索 "今日天气"
      - sleep: 3000

  - name: 检查结果
    flow:
      - aiAssert: 结果中展示了天气信息
样例项目

你可以在这里找到使用 YAML 脚本做自动化的样例项目

配置 AI 模型服务

将你的模型配置写入环境变量。更多信息请查看 选择 AI 模型

# 替换为你的 API Key
export OPENAI_API_KEY="sk-abcdefghijklmnopqrstuvwxyz"

# 可能需要更多配置,如模型名称、接入点等,请参考 《选择 AI 模型》文档
export OPENAI_BASE_URL="..."

或使用当前命令运行目录下的 .env 文件存储配置,Midscene 命令行工具在运行 yaml 脚本时会自动加载它

OPENAI_API_KEY="sk-abcdefghijklmnopqrstuvwxyz"

使用命令行工具

全局安装 @midscene/cli

npm i -g @midscene/cli
# 或在项目中安装
npm i @midscene/cli --save-dev

编写一个名为 bing-search.yaml 的文件来驱动 web 浏览器的自动化任务

web:
  url: https://www.bing.com

tasks:
  - name: 搜索天气
    flow:
      - ai: 搜索 "今日天气"
      - sleep: 3000
      - aiAssert: 结果显示天气信息

或者驱动安卓设备的自动化任务(需要使用 adb 连接安卓设备)

android:
  # launch: https://www.bing.com
  deviceId: s4ey59

tasks:
  - name: 搜索天气
    flow:
      - ai: 打开浏览器并导航到 bing.com
      - ai: 搜索 "今日天气"
      - sleep: 3000
      - aiAssert: 结果显示天气信息

或者驱动 iOS 设备的自动化任务(需要配置 WebDriverAgent)

ios:
  # launch: com.apple.mobilesafari
  wdaPort: 8100

tasks:
  - name: 搜索天气
    flow:
      - ai: 打开浏览器并导航到 bing.com
      - ai: 搜索 "今日天气"
      - sleep: 3000
      - aiAssert: 结果显示天气信息

运行脚本

midscene ./bing-search.yaml
# 或者如果你在项目中安装了 midscene
npx midscene ./bing-search.yaml

你将会看到脚本的执行进度和可视化运行报告文件。

脚本文件结构

脚本文件使用 YAML 格式来描述自动化任务。它定义了要操作的目标(如网页或安卓应用)以及一系列要执行的步骤。

一个标准的 .yaml 脚本文件包含 webandroidios 部分配置环境,以及一个 tasks 部分来定义自动化任务。

web:
  url: https://www.bing.com

# tasks 部分定义了要执行的一系列步骤
tasks:
  - name: 搜索天气
    flow:
      - ai: 搜索 "今日天气"
      - sleep: 3000
      - aiAssert: 结果显示天气信息

web 部分

web:
  # 访问的 URL,必填。如果提供了 `serve` 参数,则提供相对路径
  url: <url>

  # 在本地路径下启动一个静态服务,可选
  serve: <root-directory>

  # 浏览器 UA,可选
  userAgent: <ua>

  # 浏览器视口宽度,可选,默认 1280
  viewportWidth: <width>

  # 浏览器视口高度,可选,默认 960
  viewportHeight: <height>

  # 浏览器设备像素比,可选,默认 1
  deviceScaleFactor: <scale>

  # JSON 格式的浏览器 Cookie 文件路径,可选
  cookie: <path-to-cookie-file>

  # 等待网络空闲的策略,可选
  waitForNetworkIdle:
    # 等待超时时间,可选,默认 2000ms
    timeout: <ms>
    # 是否在等待超时后继续,可选,默认 true
    continueOnNetworkIdleError: <boolean>

  # 输出 aiQuery/aiAssert 结果的 JSON 文件路径,可选
  output: <path-to-output-file>

  # 是否保存日志内容到 JSON 文件,可选,默认 `false`。如果为 true,保存到 `unstableLogContent.json` 文件中。如果为字符串,则保存到该字符串指定的路径中。日志内容的结构可能会在未来发生变化。
  unstableLogContent: <boolean | path-to-unstable-log-file>

  # 是否限制页面在当前 tab 打开,可选,默认 true
  forceSameTabNavigation: <boolean>

  # 桥接模式,可选,默认 false,可以为 'newTabWithUrl' 或 'currentTab'。更多详情请参阅后文
  bridgeMode: false | 'newTabWithUrl' | 'currentTab'

  # 是否在桥接断开时关闭新创建的标签页,可选,默认 false
  closeNewTabsAfterDisconnect: <boolean>

  # 是否忽略 HTTPS 证书错误,可选,默认 false
  acceptInsecureCerts: <boolean>

全局 agent 配置

如需使用 aiActionContext 参数,可以通过全局的 agent 配置来设置:

# 全局 AI agent 配置
agent:
  # 在调用 aiAction 时发送给 AI 模型的背景知识,可选
  aiActionContext: <string>
aiActionContext 配置说明
  • 适用环境:Web、iOS 和 Android 环境都可以通过全局的 agent 配置来设置 aiActionContext
  • 作用:为 AI 模型提供背景知识,例如处理弹窗、业务介绍等常见场景

使用示例

# 全局 agent 配置,适用于所有环境
agent:
  aiActionContext: "如果出现弹窗,点击同意。如果出现登录页面,跳过它。"

# iOS 环境配置
ios:
  launch: https://www.bing.com
  wdaPort: 8100

# 或 Android 环境配置
android:
  deviceId: s4ey59
  launch: https://www.bing.com

tasks:
  - name: 搜索天气
    flow:
      - ai: 搜索 "今日天气"
      - aiAssert: 结果显示天气信息

android 部分

android:
  # 设备 ID,可选,默认使用第一个连接的设备
  deviceId: <device-id>

  # 启动 URL,可选,默认使用设备当前页面
  launch: <url>

  # 输出 aiQuery/aiAssert 结果的 JSON 文件路径,可选
  output: <path-to-output-file>

ios 部分

ios:
  # WebDriverAgent 端口,可选,默认 8100
  wdaPort: <port>

  # WebDriverAgent 主机地址,可选,默认 localhost
  wdaHost: <host>

  # 是否自动关闭键盘,可选,默认 false
  autoDismissKeyboard: <boolean>

  # 启动 URL 或应用包名,可选,默认使用设备当前页面
  launch: <url-or-bundle-id>

  # 输出 aiQuery/aiAssert 结果的 JSON 文件路径,可选
  output: <path-to-output-file>

  # 是否保存日志内容到 JSON 文件,可选,默认 `false`。如果为 true,保存到 `unstableLogContent.json` 文件中。如果为字符串,则保存到该字符串指定的路径中。日志内容的结构可能会在未来发生变化。
  unstableLogContent: <boolean | path-to-unstable-log-file>

tasks 部分

tasks 部分是一个数组,定义了脚本执行的步骤。记得在每个步骤前添加 - 符号,表明这些步骤是个数组。

flow 部分的接口与 API 几乎相同,除了一些参数的嵌套层级。

tasks:
  - name: <name>
    continueOnError: <boolean> # 可选,错误时是否继续执行下一个任务,默认 false
    flow:
      # 自动规划(Auto Planning, .ai)
      # ----------------

      # 执行一个交互,`ai` 是 `aiAction` 的简写方式
      - ai: <prompt>
        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 这种用法与 `ai` 相同
      - aiAction: <prompt>
        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 即时操作(Instant Action, .aiTap, .aiHover, .aiInput, .aiKeyboardPress, .aiScroll)
      # ----------------

      # 点击一个元素,用 prompt 描述元素位置
      - aiTap: <prompt>
        deepThink: <boolean> # 可选,是否使用深度思考(deepThink)来精确定位元素。默认值为 False
        xpath: <xpath> # 可选,目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 鼠标悬停一个元素,用 prompt 描述元素位置
      - aiHover: <prompt>
        deepThink: <boolean> # 可选,是否使用深度思考(deepThink)来精确定位元素。默认值为 False
        xpath: <xpath> # 可选,目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空

        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 输入文本到一个元素,用 prompt 描述元素位置
      - aiInput: <输入框的最终文本内容>
        locate: <prompt>
        deepThink: <boolean> # 可选,是否使用深度思考(deepThink)来精确定位元素。默认值为 False
        xpath: <xpath> # 可选,目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 在元素上按下某个按键(如 Enter,Tab,Escape 等),用 prompt 描述元素位置
      - aiKeyboardPress: <按键>
        locate: <prompt>
        deepThink: <boolean> # 可选,是否使用深度思考(deepThink)来精确定位元素。默认值为 False
        xpath: <xpath> # 可选,目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空

        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 全局滚动,或滚动 prompt 描述的元素
      - aiScroll:
        direction: 'up' # 或 'down' | 'left' | 'right'
        scrollType: 'once' # 或 'untilTop' | 'untilBottom' | 'untilLeft' | 'untilRight'
        distance: <number> # 可选,滚动距离,单位为像素
        locate: <prompt> # 可选,执行滚动的元素
        deepThink: <boolean> # 可选,是否使用深度思考(deepThink)来精确定位元素。默认值为 False
        xpath: <xpath> # 可选,目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空

        cacheable: <boolean> # 可选,当启用 [缓存功能](./caching.mdx) 时,是否允许缓存当前 API 调用结果。默认值为 True

      # 在报告文件中记录当前截图,并添加描述
      - logScreenshot: <title> # 可选,截图的标题,如果未提供,则标题为 'untitled'
        content: <content> # 可选,截图的描述

      # 数据提取
      # ----------------

      # 执行一个查询,返回一个 JSON 对象
      - aiQuery: <prompt> # 记得在提示词中描述输出结果的格式
        name: <name> # 查询结果在 JSON 输出中的 key

      # 更多 API
      # ----------------

      # 等待某个条件满足,并设置超时时间(ms,可选,默认 30000)
      - aiWaitFor: <prompt>
        timeout: <ms>

      # 执行一个断言
      - aiAssert: <prompt>
        errorMessage: <error-message> # 可选,当断言失败时打印的错误信息。
        name: <name> # 可选,给断言一个名称,会在 JSON 输出中作为 key 使用

      # 等待一定时间
      - sleep: <ms>

      # 在 web 页面上下文中执行一段 JavaScript 代码
      - javascript: <javascript>
        name: <name> # 可选,给返回值一个名称,会在 JSON 输出中作为 key 使用

  - name: <name>
    flow:
      # ...

使用图像提示

对于支持在提示词中附带图像的步骤(参见 API 参考),可以把提示词改写为对象,并通过设置 images 字段(一个包含 nameurl 的对象数组)来附加图像。该对象包含以下字段:

  • prompt:发送给模型的文本描述。
  • images(可选):提示词引用的参考图像,每一项需要提供 nameurl
  • convertHttpImage2Base64(可选):在图片链接无法公开访问时,将 HTTP 链接转换为 Base64 再发送给模型。

图片 URL 可以是本地路径、Base64 字符串或远程链接。如果图片链接无法被模型访问,请设置 convertHttpImage2Base64: true,Midscene 会将图像下载后以 Base64 字符串的形式发送给模型。

对于 aiTapaiHoveraiDoubleClickaiRightClick 等交互操作,请把文本和图像配置写在 locate 字段中。

tasks:
  - name: 校验品牌一致性
    flow:
      - aiHover:
          locate:
            prompt: 将鼠标移动到包含 GitHub 标志的区域。
            images:
              - name: GitHub 标志
                url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
            convertHttpImage2Base64: true

      - aiTap:
          locate:
            prompt: 点击包含 GitHub 标志的区域。
            images:
              - name: GitHub 标志
                url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
            convertHttpImage2Base64: true

对于视觉问答类步骤,例如 aiAskaiQueryaiBooleanaiNumberaiStringaiAssert,可以直接设置 promptimages 字段。

tasks:
  - name: 校验品牌一致性
    flow:
      - aiAssert:
          prompt: 判断页面上是否出现该图像。
          images:
            - name: 目标标志
              url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
          convertHttpImage2Base64: true

命令行工具的高级用法

@midscene/cli 提供了灵活的方式来运行你的自动化脚本。

运行一个或多个脚本

你可以直接向 midscene 命令传递一个 .yaml 脚本文件或使用通配符模式来匹配多个 .yaml。这是 --files 参数的简写方式。

# 运行单个脚本
midscene ./bing-search.yaml

# 使用通配符模式运行所有匹配的脚本
midscene './scripts/**/*.yaml'

命令行选项

命令行工具提供了一些选项来控制脚本的执行行为。

  • --files <file1> <file2> ...: 指定要执行的脚本文件列表,文件将按顺序执行。支持通配符模式,遵循 glob 支持的语法。
  • --concurrent <number>: 设置并发执行的数量。默认为 1
  • --continue-on-error: 如果设置了此选项,即使有脚本文件执行失败,也会继续运行余下的脚本文件。默认关闭。
  • --share-browser-context: 在所有脚本之间共享同一个浏览器上下文(例如 Cookies 和 localStorage)。这对于需要登录状态的连续测试非常有用,默认关闭。
  • --summary <filename>: 指定生成的 JSON 格式汇总报告文件的路径。
  • --headed: 在有图形界面的浏览器中运行脚本,而不是在无头模式下。
  • --keep-window: 脚本执行结束后保持浏览器窗口打开。此选项会自动启用 --headed 参数。
  • --config <filename>: 指定配置文件,配置文件中的参数将作为命令行参数的默认值。
  • --web.userAgent <ua>: 设置浏览器 UA,这将覆盖所有脚本文件中的 web.userAgent 参数。
  • --web.viewportWidth <width>: 设置浏览器视口宽度,这将覆盖所有脚本文件中的 web.viewportWidth 参数。
  • --web.viewportHeight <height>: 设置浏览器视口高度,这将覆盖所有脚本文件中的 web.viewportHeight 参数。
  • --android.deviceId <device-id>: 设置安卓设备 ID,这将覆盖所有脚本文件中的 android.deviceId 参数。
  • --ios.wdaPort <port>: 设置 WebDriverAgent 端口,这将覆盖所有脚本文件中的 ios.wdaPort 参数。
  • --ios.wdaHost <host>: 设置 WebDriverAgent 主机地址,这将覆盖所有脚本文件中的 ios.wdaHost 参数。
  • --dotenv-debug: 设置 dotenv 的 debug 日志,默认关闭。
  • --dotenv-override: 设置 dotenv 是否覆盖同名的全局环境变量,默认关闭。

举例:

使用 --files 参数来指定文件顺序,并行执行

midscene --files ./login.yaml ./buy/*.yaml ./checkout.yaml

以 4 个并发数运行所有脚本,并在任一文件出错时继续

midscene --files './scripts/**/*.yaml' --concurrent 4 --continue-on-error

以文件形式编写命令行参数

你可以编写 YAML 格式的配置文件,然后通过 --config 来引用它。调用命令行工具时,命令行参数的优先级高于配置文件。

files:
  - './scripts/login.yaml'
  - './scripts/search.yaml'
  - './scripts/**/*.yaml'

concurrent: 4
continueOnError: true
shareBrowserContext: true

使用方法:

midscene --config ./config.yaml

更多特性

.yaml 文件中使用环境变量

你可以在 .yaml 文件中使用环境变量,通过 ${variable-name} 的方式。

例如,如果你有一个 .env 文件,内容如下:

topic=weather today

你可以在 .yaml 文件中使用环境变量,如下所示:

#...
- ai: type ${topic} in input box
#...

运行在有界面(Headed)模式下

web 场景下支持

'headed' 模式意味着浏览器窗口是可见的。默认情况下,脚本会在无界面模式下运行。

如果你想运行在有界面模式下,你可以使用 --headed 选项。此外,如果你想在脚本运行结束后保持浏览器窗口打开,你可以使用 --keep-window 选项。--keep-window 选项会自动开启 --headed 模式。

headed 模式会消耗更多资源,所以建议你仅在本地使用。

# 运行在有界面模式下
midscene /path/to/yaml --headed

# 运行在有界面模式下,并在结束后保持浏览器窗口打开
midscene /path/to/yaml --keep-window

使用桥接模式

web 场景下支持

通过使用桥接模式,你可以利用 YAML 脚本在已有的桌面浏览器上执行自动化。这对于需要复用 Cookies、插件和页面状态,或者需要人工与自动化脚本交互的情况非常有用。

使用桥接模式,你需要先安装 Chrome 扩展,然后在 target 部分使用以下配置:

web:
  url: https://www.bing.com
+ bridgeMode: newTabWithUrl

请参阅 通过 Chrome 扩展桥接模式 了解更多详细信息。

使用 JavaScript 运行 YAML 脚本

你也可以使用 JavaScript 运行 YAML 脚本,调用 Agent 上的 runYaml 方法即可。注意,这种方法只会执行 YAML 脚本中的 tasks 部分。

分析命令行工具的运行结果

执行完成后,会在输出目录中生成以下文件:

  • --summary 选项指定的文件路径(默认是 index.json),包含所有文件的执行状态和统计信息
  • 各个 YAML 文件的独立执行结果(JSON 格式)
  • 各个文件的可视化报告(HTML 格式)

配置 dotenv 的默认行为

Midscene 使用 dotenv 加载 .env 文件中的环境变量。

关闭 dotenv 的 debug 日志

默认情况下,Midscene 会打印 dotenv 的 debug 信息,如果你不想看到这些信息,你可以使用 --dotenv-debug 选项关闭。

midscene /path/to/yaml --dotenv-debug=false

使用 .env 中的环境变量覆盖同名的全局环境变量

默认情况下,dotenv 不会覆盖.env 文件中同名的全局环境变量。如果希望覆盖,你可以使用 --dotenv-override 选项。

midscene /path/to/yaml --dotenv-override=true

FAQ

如何从 Chrome 中获取 JSON 格式的 Cookies?

你可以使用这个 Chrome 扩展 来导出 Cookies 为 JSON 格式。

如何打开 dotenv 的 debug 日志?

Midscene 使用 dotenv 加载 .env 文件中的环境变量。你可以使用 --dotenv-debug 选项来打开 dotenv 的 debug 日志。

midscene /path/to/yaml --dotenv-debug=true

更多

你可能还想了解 提示词技巧