加入线上会议:美国东部时间周四上午 11 点,Storybook 9 发布 & AMA

在 Github 上查看

Eyes-Storybook

适用于 Storybook 的 Applitools Eyes SDK。

目录

安装

安装 npm 包

在你的被测项目中将 Eyes-Storybook 安装为本地开发依赖

npm install --save-dev @applitools/eyes-storybook

Applitools API 密钥

为了通过 Applitools 服务器进行身份验证,你需要向 Eyes-Storybook SDK 提供从 Applitools 获取的 API 密钥。关于如何获取 API 密钥的更多信息,请参阅此处

为此,在运行测试之前,将环境变量 APPLITOOLS_API_KEY 设置为你的 API 密钥。例如,在 Linux/Mac 上

export APPLITOOLS_API_KEY=<your_key>

在 Windows 上

set APPLITOOLS_API_KEY=<your_key>

用法

完成安装并定义 API 密钥后,你就可以从命令行运行 Eyes-Storybook 并让它截取所有 stories 的屏幕截图了。

如果你的项目使用默认的 storybook 配置文件夹(即 <project_folder>/.storybook),则运行以下命令

npx eyes-storybook

配置本地 storybook 服务器

通常,Eyes-Storybook 会在测试期间启动一个 storybook 开发服务器,端口范围在 9000-9010 之间。可以将参数传递给 Eyes-Storybook 来配置本地 storybook 服务器

  • --storybook-port OR -p: 运行 storybook 的端口(传递给 start-storybook-p 参数)。
  • --storybook-host OR -h: 运行 storybook 的主机(传递给 start-storybook-h 参数)。
  • --storybook-config-dir OR -c: 从哪个目录加载 Storybook 配置(传递给 start-storybook-c 参数)
  • --storybook-static-dir OR -s: 从哪个目录加载静态文件,逗号分隔列表(传递给 start-storybook-s 参数)

独立服务器

如上一节所述,Eyes-Storybook 会启动一个 storybook 开发服务器。如果你希望在 Eyes-Storybook 外部启动服务器,或者测试某个 URL 上可用的生产构建版本,只需在命令行中指定 storybook 的 URL(或在配置文件中,参见下面的高级配置)。

例如

npx eyes-storybook -u http://localhost:6006

或者针对生产环境的 storybook

npx eyes-storybook -u http://react.carbondesignsystem.com/

命令行参数

运行 npx eyes-storybook --help 可以查看完整的命令行参数列表

Usage: eyes-storybook.js [options]

Options:
  --help                                            Show help                                                                       [boolean]
  --version, -v                                     Show the version number                                                         [boolean]
  --conf, -f                                        Path to applitools.config.js config file                                         [string]
  --storybook-url, -u                               URL to storybook                                                                 [string]
  --storybookPort, -p, --storybook-port             Port to run Storybook                                                            [number]
  --storybookHost, -h, --storybook-host             Host to run Storybook                                                            [string]
  --storybookConfigDir, -c, --storybook-config-dir  Path to Storybook's config folder (defaults to .storybook)                       [string]
  --storybookStaticDir, --storybook-static-dir      Path to Storybook's static files folder                                          [string]
  --showStorybookOutput, --show-storybook-output    Whether or not you want to see Storybook output                                 [boolean]
  --readStoriesTimeout, --read-stories-timeout      The time to wait until all stories are read, before starting the visual tests    [number]
  --exitcode, -e                                    If tests failed close with non-zero exit code                                   [boolean]

并发

免费账户的默认并发级别为 5。这意味着最多只有 5 个视觉测试可以并行运行,因此执行可能会很慢。如果你的账户支持更高的并发级别,可以通过在 applitools.config.js 文件中(参见下面的高级配置部分)的 testConcurrency 属性中指定不同的值来传递该值。

如果你对加快视觉测试感兴趣,请联系 sdr@applitools.com 获取试用账户,以获得更高的并发性和更快的测试。

高级配置

除了命令行参数,还可以通过使用环境变量或 applitools.config.js 文件来定义以下测试配置参数

属性名 默认值 描述
storybookUrl undefined storybook 的 URL(也可作为命令行参数使用)。
storybookPort 9000 运行 Storybook 的端口(也可作为命令行参数使用)。
storybookHost localhost 运行 Storybook 的主机(也可作为命令行参数使用)。
storybookConfigDir .storybook Storybook 配置文件夹的路径(也可作为命令行参数使用)。
storybookStaticDir undefined Storybook 静态文件文件夹的路径(也可作为命令行参数使用)。
showStorybookOutput undefined 是否希望看到 Storybook 的输出(也可作为命令行参数使用)。
viewportSize { width: 1024, height: 600} Puppeteer 浏览器的窗口大小。这是最初渲染 stories 的浏览器窗口(并以 viewportSize 参数中提供的大小打开),然后将 DOM 快照上传到服务器,服务器在 browser 参数中提供的所有浏览器和大小上渲染此快照。
exitcode true 如果测试失败或存在视觉差异,则使用非零退出码关闭(也可作为命令行参数使用)。
browser { width: 1024, height: 768, name: 'chrome' } 生成的屏幕截图的大小和浏览器。有关更多信息和可能的值,请参阅下面的浏览器部分
showLogs false 是否希望看到 Eyes-Storybook 插件的日志。
batch undefined 描述批次不同方面的一个对象。下表中的以下几行描述了配置批次的各种方法。
batch.id random 提供将测试分组到批次的能力。有关批次的更多信息,请参阅此处
batch.name 批次中第一个测试的名称 为批次提供名称(仅供显示用)。
batch.sequenceName undefined 用于管理批次统计信息的名称。
batch.notifyOnCompletion false 如果为 true,则发送批次完成通知。
batch.properties undefined 整个批次的自定义属性。格式为包含 name/value 属性的对象数组。例如:[{name: 'My prop', value:'My value'}]
baselineEnvName undefined 基线的环境名称。
envName undefined 正在运行被测应用程序的环境名称。
ignoreCaret false 比较图像时是否忽略闪烁的光标。
matchLevel undefined 比较应用程序屏幕截图与预期输出时使用的测试范围匹配级别。可能的值为 StrictExactLayoutContent。有关匹配级别的更多信息,请参阅此处
branchName undefined 分支名称。
baselineBranchName undefined 基线分支的名称。
parentBranchName undefined 设置创建新分支的基础分支。
proxy undefined 设置用于与 Eyes 服务器进行网络请求的代理设置。这可以是指向代理 URI 的字符串,也可以是包含 URI、用户名和密码的对象。例如:{url: 'https://myproxy.com:443', username: 'my_user', password: 'my_password', isHttpOnly: false} 或:"https://username:password@myproxy.com:443"
saveFailedTests false 设置默认情况下是否保存失败的测试(保存为基线)。
saveNewTests true 设置默认情况下是否保存新的测试(保存为基线)。
serverUrl 默认 Eyes 服务器 URL Eyes 服务器的 URL
compareWithParentBranch false
ignoreBaseline false
runInDocker false 如果你在 docker 中运行 SDK 时遇到问题,请将此标志设置为 true。有关更多信息,请参阅下面的部分
puppeteerOptions undefined 发送给 puppeteer.launch 的选项。这是一种低级配置,应非常小心地使用。示例用法 { args: ['--no-sandbox'], headless: false, devtools: true}
jsonFilePath undefined 结果文件的目录路径。如果设置,则在此目录中创建 JSON 文件,文件名为 eyes.json,并包含 Eyes 测试结果。
tapFilePath undefined 结果文件的目录路径。如果设置,则在此目录中创建 TAP 文件,文件名为 eyes.tap,并包含 Eyes 测试结果。
xmlFilePath undefined 结果文件的目录路径。如果设置,则在此目录中创建 XUnit XML 文件,文件名为 eyes.xml,并包含 Eyes 测试结果。
waitBeforeCapture undefined Selector、函数或超时。如果为 number,则该参数被视为在所有屏幕截图之前等待的毫秒数。如果为 string,则该参数被视为在所有屏幕截图之前等待元素的 selector。如果为 function,则该参数被视为在所有屏幕截图之前等待的谓词。对于按组件配置,请参阅waitBeforeCapture。请注意,我们使用 Puppeteer 的 page.waitForTimeout()page.waitForSelector()page.waitForXPath()page.waitForFunction(),请查阅其 API 获取更多详细信息。
include true 一个谓词函数、字符串或正则表达式,用于指定哪些故事应该进行视觉测试。仅为指定的组件创建视觉基线。该函数接收一个包含 namekindstoryTitleparameters 属性的对象。例如(排除所有名称以 [SKIP] 开头的故事):({name, kind, storyTitle, parameters}) => !/^\[SKIP\]/.test(name)。有关更多信息,请参阅按组件配置 - include
variations undefined 为所有或部分故事指定其他变体。例如,RTL。有关更多信息,请参阅按组件配置 - variations
dontCloseBatches false 如果为 true,则不关闭批次以进行 notifyOnCompletion。
testConcurrency 5 可以并发运行的最大测试数。默认值是免费账户允许的数量。对于付费账户,请将此数字设置为你账户的配额。
readStoriesTimeout 60000 Eyes-Storybook 等待 storybook 加载的时间(以毫秒为单位)。对于旧版 Storybook 版本 2 和 3,这也是 Eyes-Storybook 确认它在这些版本上工作所需的时间。因此,在使用 Storybook 版本 2 或 3 时,建议将此值设置得小一些(例如 3000)。
ignoreDisplacements false 设置 Test Manager 是否应初步显示仅发生位移而非真实不匹配的图像特征的不匹配项。这也可以按故事指定,请参阅按组件配置 - ignoreDisplacements
properties undefined 为每个测试添加自定义属性。这些属性显示在 Test Manager 中,并且可以按自定义属性对测试进行分组。默认情况下,Eyes-Storybook 为每个测试添加 2 个自定义属性:每个组件的组件名称状态。通过此配置参数添加更多属性将不会覆盖这两个属性。
ignoreRegions undefined 一个区域数组,在比较检查点屏幕截图和基线屏幕截图时忽略这些区域。有关更多信息,请参阅按组件配置 - ignoreRegions
floatingRegions undefined 一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为浮动区域。有关更多信息,请参阅按组件配置 - floatingRegions
layoutRegions undefined 一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为匹配级别为Layout。有关更多信息,请参阅按组件配置 - layoutRegions
strictRegions undefined 一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为匹配级别为Strict。有关更多信息,请参阅按组件配置 - strictRegions
contentRegions undefined 一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为匹配级别为Content。有关更多信息,请参阅按组件配置 - contentRegions
accessibilityRegions undefined 一个区域数组,在比较检查点屏幕截图和基线屏幕截图时验证这些区域的可访问性。验证是根据配置的 accessibilityValidation 进行的。有关更多信息,请参阅按组件配置 - contentRegions
accessibilityValidation undefined 一个对象,指定用于屏幕截图的可访问性级别和指南版本。level 的可能值为 NoneAAAAAguidelinesVersion 的可能值为 WCAG_2_0WCAG_2_1。例如:{level: 'AA', guidelinesVersion: 'WCAG_2_0'}。有关更多信息,请参阅按组件配置 - accessibilityValidation
layoutBreakpoints undefined 设置为 true 时,将为 browser 配置中的每个浏览器/设备大小获取一次 DOM 快照。出于优化目的,可以传递一个数字数组。DOM 快照将为数组中的每个宽度获取一次。有关更多信息,请参阅按组件配置 - layoutBreakpoints
sendDom true 一个标志,用于指定渲染屏幕截图时是否应该捕获 DOM 和 CSS。默认值为 true。此选项仅用于解决意外行为,不用于正常生产使用。有关更多信息,请参阅按组件配置 - sendDom
visualGridOptions undefined 一个对象,指定在 Ultrafast Grid 上配置渲染的选项。有关更多信息,请参阅按组件配置 - visualGridOptions

有两种方法指定测试配置

  1. 环境变量
  2. applitools.config.js 文件

上述列表也是优先级顺序,这意味着如果你将属性指定为环境变量,它将覆盖在 applitools.config.js 文件中为同一属性定义的值。

方法 1: 环境变量

相应的环境变量名称采用大写,带有 APPLITOOLS_ 前缀,并使用下划线代替驼峰命名法

APPLITOOLS_APP_NAME
APPLITOOLS_SHOW_LOGS
APPLITOOLS_BATCH_ID
APPLITOOLS_BATCH_NAME
APPLITOOLS_BATCH_SEQUENCE_NAME
APPLITOOLS_PROXY
APPLITOOLS_NOTIFY_ON_COMPLETION
...
// all other configuration variables apply as well..

方法 2: applitools.config.js 文件

可以在当前工作目录(运行 eyes-storybook 脚本时的目录)中放置一个名为 applitools.config.js 的文件。在此文件中,以导出的 CommonJS 模块形式指定所需的配置。例如

module.exports = {
  appName: 'My app',
  showLogs: true,
  batchName: 'My batch'
  ...
  // all other configuration variables apply
}

配置浏览器

Eyes-Storybook 将按照 browser 配置参数中指定的方式截取页面屏幕截图。

可能的值为

  • chrome
  • firefox
  • edgechromium
  • edgelegacy
  • ie10
  • ie11
  • safari
  • chrome-one-version-back
  • chrome-two-versions-back
  • firefox-one-version-back
  • firefox-two-versions-back
  • safari-one-version-back
  • safari-two-versions-back
  • edgechromium-one-version-back
  • edgechromium-two-versions-back

以前的浏览器版本

*-one-version-back*-two-versions-back 是相对于同一浏览器版本的。例如,如果 chrome 指的是版本 79,那么 chrome-one-version-back 将是 Chrome 78,chrome-two-versions-back 将是 Chrome 77。

并行获取多个浏览器的屏幕截图

还可以发送一个浏览器数组,例如在 applitools.config.js 文件中

module.exports = {
  browser: [
    {width: 800, height: 600, name: 'firefox'},
    {width: 1024, height: 768, name: 'chrome'},
    {width: 1024, height: 768, name: 'ie11'}
  ]
}

设备模拟

要启用 chrome 的设备模拟,可以发送设备名称和屏幕方向,例如

module.exports = {
  browser: {
    deviceName: 'iPhone X',
    screenOrientation: 'landscape',
    name: 'chrome' // optional, just to make it explicit this is browser emulation and not a real device. Only chrome is supported for device emulation.
  }
}

屏幕方向的可能值为 landscapeportrait,如果未指定值,默认为 portrait

设备名称列表可在 https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/DeviceName.ts 找到。

此外,可以通过除了 widthheight 之外,传递 deviceScaleFactormobile 参数,使用 chrome 的设备模拟和自定义视口大小、像素密度和移动模式。例如

module.exports = {
  browser: {
    width: 800,
    height: 600,
    deviceScaleFactor: 3,
    mobile: true,
    name: 'chrome' // optional, just to make it explicit this is browser emulation and not a real device. Only chrome is supported for device emulation.
  }
}

iOS 设备

module.exports = {
  browser: {
    iosDeviceInfo: {
      deviceName: 'iPhone XR',
      screenOrientation: 'landscape', // optional, default: 'portrait'
      iosVersion: 'latest' // optional, default: undefined (i.e. the default is determined by the Ultrafast grid)
    },
  }
}

设备列表可在 https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-api/src/enums/IosDeviceName.ts 找到

iosVersion 的可能值为

  • 'latest' - UFG 支持的最新 iOS 版本
  • 'latest-1' - 比最新版本早一个版本
  • undefined - UFG 的默认设置

模拟 IE 浏览器

有些页面在 Internet Explorer 上渲染不同,因此在使用 ultrafast grid 在伪 IE 浏览器上运行时获取 dom 快照可能很重要。

使用 fakeIE 标志 - 你可以在模拟 IE 的 chrome 上渲染 stories。

我们通过模拟页面的 userAgentdocumentMode 来实现这一点 - 使页面相信它正在 IE 上渲染。

使用伪 IE 测试时,性能会有轻微影响 - 因为浏览器需要为它渲染的每个故事模拟 IE。

按组件配置

仅支持 Storybook 版本 >= 4

有两种方法为特定的故事或一组故事提供配置。

  1. 作为故事的参数 - 可以将第三个参数传递给 storybook 的 .add 函数来自定义每个故事。可以在 parameters 对象上指定一个 eyes 属性,其中包含配置属性。

  2. 在全局配置文件 applitools.config.js - 如果为以下某个属性指定了一个函数,则该函数将为每个故事调用,并将传递故事的元数据,结构为 {name, kind, parameters},其中 name 是组件的名称,kind 是 storybook 为类别构建的字符串,例如 Forms|Input/Text,而 parameters 是 storybook 的 .add 函数的第三个参数。该函数应返回特定属性+故事的配置值。

在故事中本地指定的值优先于全局配置值。

支持以下属性

include

global

在全局提供时,include 是一个函数,它接收故事的 kindnamestoryTitle 以及 parameters。除了 storyTitle 之外的所有属性都来自 storybook,它们代表故事的层级结构

  • kind - 故事的目录和部分(如果适用)。storybook 中允许嵌套目录结构。这些将以 / 为后缀,而一个部分(可以包含多个目录)将以 | 为后缀。例如
    • Components 目录中名为 Button 的故事 - 其 kind 将是 Components
    • App 部分下 Components 目录中名为 Button 的故事 - 其 kind 将是 App|Components
    • App 部分下 Components 目录的 Radio 子目录中名为 RadioButton 的故事 - 其 kind 将是 App|Components/Radio
  • name - 故事名称。例如
    • Components 目录中名为 Button 的故事 - 其 name 将是 Button
  • parameters - 可以在故事中指定的自定义参数。

storyTitle 由 SDK 生成,并用作测试名称。因此,很容易通过它查找和过滤。

更多信息可以在 Storybook 文档 - 命名组件和层级结构中找到。

你可以按 kindnamestoryTitleparameters、它们的组合或任何其他结果为 boolean 的逻辑进行过滤。例如

// applitools.config.js
module.exports = {
  ...
  // given the example above
  // visually test only the stories in the 'Radio' subdirectory
  include: ({kind}) => {
    return kind === 'App|Components/Radio'
  }
  ...
}

当传递 storyTitle 作为 string 时,只有此故事将被测试。例如

module.exports = {
...
include: "Button: with text",
...
}

当传递 storyTitle 作为 Regex 时,只有匹配的故事将被测试。例如

module.exports = {
...
include: /Button: */,
...
}

注意:你可以使用正则表达式或你喜欢的任何其他方法,只要你从此函数返回一个 boolean

component

当为 false 时,该组件将不会进行视觉测试。例如

// This story will not be tested visually
storiesOf('Some kind', module)
  .add(
    'Some story',
    () => <div>I am visually perfect!</div>,
    {eyes: {include: false}}
  )

variations

一个对象值数组,指定为该故事添加哪些变体。对于每个值,将为该组件执行额外的视觉测试。每个变体可以包含 queryParamsproperties 字段,分别指定自定义查询参数和 eyes 属性。变体测试将具有相同的名称。

这可以适应许多用例,例如 @storybook/addon-contexts。使用此类插件,可以根据 URL 中的查询参数以不同的方式渲染组件。例如,这是一个处理 RTL 变体的 storybook

const isRTL = new URL(window.location).searchParams.get('eyes-variation') === 'RTL';

if (isRTL) {
  document.documentElement.setAttribute('dir', 'rtl')
}

// 2 visual tests will be created - one for LTR and one for RTL
storiesOf('Components that support RTL', module)
  .add(
    'Some story',
    () => <div>
        <span>I am visually perfect!</span>
        <span>{isRTL ? ' and rendered right to left as well :)' : ''}</span>
      </div>,
    {eyes: {variations: [{queryParams: {'eyes-variation': 'RTL'}, properties: {name: 'isRTL', value: 'true'}, {properties: {name: 'isRTL', value: 'false'}}]}}
  )

waitBeforeCapture

Selector 或超时,有关更多详细信息,请参阅高级配置

storiesOf('Components with a waitBeforeCapture', module)
  .add(
    'Some story',
    () => <span id="container" class="loading"></span>,
    {eyes: { waitBeforeCapture: '#container.ready' }}
  );

请注意,目前在按组件配置中,waitBeforeCapture 的谓词选项不可用。

properties

为每个测试添加自定义属性。这些属性显示在 Test Manager 中,并且可以按自定义属性对测试进行分组。默认情况下,Eyes-Storybook 为每个测试添加 2 个自定义属性:每个组件的组件名称状态。通过此配置参数添加更多属性将不会覆盖这两个属性。

例如

storiesOf('Components with custom properties', module)
  .add(
    'Some story',
    () => <span id="container" class="loading"></span>,
    {eyes: { properties: [
      {name: 'some prop #1', value: 'some value #1'},
      {name: 'some prop #2', value: 'some value #2'},
    ] }}
  );

ignoreRegions

一个或多个区域,在检查视觉差异时忽略这些区域。例如

storiesOf('Components with ignored region', module)
  .add(
    'Some story',
    () =>
      <div>
        <span>I am visually perfect!</span>
        <span className="ignore-this">this should be ignored</span>
      </div>,
    {eyes: {
      ignoreRegions: [
        {selector: '.ignore-this'}, // by css selector
        {left: 10, top: 20, width: 200, height: 80} // by absolute coordinates
      ]}
    }
  )

floatingRegions

一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为浮动区域。例如

storiesOf('Components with floating region', module)
  .add(
    'Some story',
    () =>
      <div>
        <span>I am visually perfect!</span>
        <span className="floating-region">this should be floating</span>
      </div>,
    {eyes: {
      floatingRegions: [
        { // by selector
          selector: '.floating-region',
          maxUpOffset: 10,
          maxDownOffset: 20,
          maxLeftOffset: 30,
          maxRightOffset: 40,
        },
        { // by absolute coordinates
          left: 10,
          top: 20,
          width: 200,
          height: 80,
          maxUpOffset: 10,
          maxDownOffset: 20,
          maxLeftOffset: 30,
          maxRightOffset: 40,
        }
      ]}
    }
  )

layoutRegions

一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为匹配级别为Layout。例如

storiesOf('Components with layout region', module)
  .add(
    'Some story',
    () =>
      <div>
        <span>I am visually perfect!</span>
        <span className="layout-region">this should be compared with layout match level</span>
      </div>,
    {eyes: {
      layoutRegions: [
        {selector: '.layout-region'}, // by css selector
        {left: 10, top: 20, width: 200, height: 80} // by absolute coordinates
      ]}
    }
  )

contentRegions

一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为匹配级别为Content。例如

storiesOf('Components with content region', module)
  .add(
    'Some story',
    () =>
      <div>
        <span>I am visually perfect!</span>
        <span className="content-region">this should be compared with content match level</span>
      </div>,
    {eyes: {
      contentRegions: [
        {selector: '.content-region'}, // by css selector
        {left: 10, top: 20, width: 200, height: 80} // by absolute coordinates
      ]}
    }
  )

strictRegions

一个区域数组,在比较检查点屏幕截图和基线屏幕截图时将这些区域视为匹配级别为Strict。例如

storiesOf('Components with strict region', module)
  .add(
    'Some story',
    () =>
      <div>
        <span>I am visually perfect!</span>
        <span className="strict-region">this should be compared with strict match level</span>
      </div>,
    {eyes: {
      strictRegions: [
        {selector: '.strict-region'}, // by css selector
        {left: 10, top: 20, width: 200, height: 80} // by absolute coordinates
      ]}
    }
  )

accessibilityRegions

一个或多个区域,用于可访问性检查。例如

storiesOf('Components with accessibility regions', module)
  .add(
    'Some story',
    () =>
      <div>
        <span>I am visually perfect!</span>
        <span className="check-me">this should be tested for accessibility</span>
      </div>,
    {eyes: {
      accessibilityRegions: [
        {accessibilityType: 'RegularText', selector: '.check-me'}, // by css selector
        {accessibilityType: 'RegularText', left: 10, top: 20, width: 200, height: 80} // by absolute coordinates
      ]
    }}
  )
});

可能的可访问性类型值为:IgnoreContrast,RegularText,LargeText,BoldTextGraphicalObject

accessibilityValidation

验证可访问性区域时应使用的级别和指南版本。例如

storiesOf('Components with accessibility regions', module)
  .add(
    'Some story',
    () => <div>
      <span>I am visually perfect!</span>
      <span className="check-me">this should be tested for accessibility</span>
    </div>,
    {eyes: {
      accessibilityValidation: {
        level: 'AA',
        guidelinesVersion: 'WCAG_2_0'
      }
    }}
  )
});

level 的可能值为:AAAAAguidelinesVersion 的可能值为:WCAG_2_0WCAG_2_1

ignoreDisplacements

设置 Test Manager 是否应初步显示仅发生位移而非真实不匹配的图像特征的不匹配项。例如

storiesOf('Components with ignoreDisplacements', module)
  .add(
    'Some story',
    () => <div>
      <span>I am visually perfect!</span>
    </div>,
    {eyes: {
      ignoreDisplacements: true
    }}
  )
});

sendDom

  storiesOf('Components', module)
    .add(
      'Some story ',
      () =>
        <div>Some Story</div>, { 
          eyes: { 
            sendDom: false
          }
        })

visualGridOptions

一个对象,指定在 Ultrafast grid 上配置渲染的选项。可用选项

  • polyfillAdoptedStyleSheets: 当 DOM 包含 adoptedStyleSheets (参考) 时,为不支持它的浏览器创建一个 polyfill (目前仅在 Chrome 中支持)。当为 true 时,这些浏览器将成功包含 CSS 作为内联样式标签。当为 false 时,CSS 将不包含。当为 undefined 时,将抛出一个错误,指示所需浏览器不支持此功能。
  • ieV2: 在 UFG 中使用 IE 环境 v2。
  storiesOf('Components', module)
    .add(
      'Some story ',
      () =>
        <div>Some Story</div>, { 
          eyes: { 
            visualGridOptions: {
              polyfillAdoptedStyleSheets: true,
              ieV2: true
            }
          }
        })

scriptHooks

在渲染期间由浏览器运行的一组脚本。其目的是在渲染时改变页面的状态和结构。一个包含以下属性的对象

beforeCaptureScreenshot

在页面加载后但在截取屏幕截图之前运行的脚本。例如

storiesOf('Components', module)
  .add(
    'Some story',
    () =>
      <div>Some Story</div>, { 
        eyes: { 
          scriptHooks: {
            beforeCaptureScreenshot: "document.body.style.backgroundColor = 'gold'"
          }
        }
      })

不能设置为高级配置的参数

runBeforerunAfter 函数

runBefore 函数可用于在截取故事快照之前执行任何操作。runAfter 方法应用于清理 runBefore 创建的任何副作用(如果有)。
例如,如果 runBeforebody 元素添加了一个类,则应在 runAfter 中删除该类。这是因为浏览器标签页的窗口在故事之间不会重新加载。

注意:rootEl 也需要清理,因此在 runBefore 中对此元素进行的任何修改都应在 runAfter 中恢复。

runBefore

一个异步函数,它将在截取故事屏幕截图之前执行。这是使用 DOM API 与故事进行任何交互的地方。

为了执行各种 DOM 交互,我们推荐使用 dom-testing-library。它提供了与 DOM 交互、查询和等待条件的工具。

例如,一个渲染弹出框的组件可以触发弹出框的打开并等待内容出现

// these are utilities from dom-testing-library
import {wait, within, fireEvent} from '@testing-library/dom';

// <Popover /> is a component in your UI library.
// The assumption in this example is that it is opened by an element with the text 'Open',
// and then that element's text changes to 'Close':
storiesOf('UI components', module)
  .add('Popover', () => <Popover />, {
    eyes: {
      runBefore({rootEl, story}) {
        fireEvent.click(within(rootEl).getByText('Open'))
        return wait(() => within(rootEl).getByText('Close'))
      }
    },
  })

runAfter

一个异步函数,它在截取故事屏幕截图后执行。这是进行任何可能改变下一个故事渲染方式的清理操作的地方。

例如,恢复到被前一个组件更改过的原始背景颜色

.add('background color', () => (
  <div style={{fontSize: '30px'}}>Component with runBefore hook that modifies the background color</div>
  ), {
  eyes: {
    runBefore({rootEl, story}) {
     window.originalBackgoundColor = document.querySelector("html").style.backgroundColor;
     document.querySelector("html").style.backgroundColor = 'fuchsia';
    },
    runAfter({rootEl, story}){
     document.querySelector("html").style.backgroundColor = window.originalBackgoundColor;
     delete window.originalBackgoundColor;
    }
  }
})

layoutBreakpoints

  storiesOf('Components', module)
    .add(
      'Some story with breakpoints for all browser and device sizes',
      () =>
        <div>Some Story</div>, { 
          eyes: { 
            layoutBreakpoints: true
          }
        })
    .add(
      'Some story with breakpoints for desktop and mobile',
      () =>
        <div>Some Story</div>, { 
          eyes: { 
            layoutBreakpoints: [500, 1200]
          }
        })

在 Docker 中运行 Eyes-Storybook

在 docker 中运行 SDK 时,通过 puppeteer 正确启动内部 chrome 浏览器可能会出现问题。如果你似乎遇到此类问题,请在配置文件中设置 runInDocker: true。这将向内部 chrome 浏览器传递特殊参数,如此处所述。

如果你仍然遇到问题,你可能需要按照说明在 docker 容器中使用你自己的 chromium 浏览器,并将其指向 SDK 的 puppeteer。按照此处的说明进行操作,并通过 puppeteerOptions 设置 executablePath。例如,applitools.config.js

module.exports = {
  puppeteerOptions: {
    executablePath: '/usr/bin/chromium-browser'
  }
}

处理动态数据

有时组件会渲染动态数据,例如日期或随机数据。这在测试这些组件时会带来挑战。我们推荐的解决此问题的方法是在你的 storybook 中插入代码来标准化数据(使用固定日期或特定种子),当它在自动化环境中运行时。

Eyes storybook 使组件能够知道它们正在被测试。故事 iframe 的 URL 上会有一个特定的查询参数:?eyes-storybook=true

这样就可以编写一个像这样的故事

const isBeingTested =
    new URL(window.location).searchParams.get('eyes-storybook')

const SOME_FIXED_DATE = 354060000000

const date = new Date(isBeingTested ? SOME_FIXED_DATE : undefined)

storiesOf('Some kind', module).add('Date', () => <div>{date}</div>)

Storybook interactions Play 功能

自版本 3.28.0 起,支持 Storybook 的 Interactions Play 功能:使用 'Play' 的 Stories,屏幕截图将在 'Play' 流程完成后自动截取。这将取代现有的截取屏幕截图的点,它不应影响任何未使用 'Play' 功能的新故事或现有故事。使用 waitBeforeCapture 属性的测试,等待期将在 'Play' 流程完成后开始。你可以阅读 Storybook interactions 文档了解更多:https://storybook.org.cn/docs/react/essentials/interactions

作者
  • danielputerman
    danielputerman
  • gearm
    gearm
  • amitzur
    amitzur
  • ramapplitools
    ramapplitools
  • amit.rokach
    amit.rokach
  • roy.sela
    roy.sela
标签