Applitools 迁移抢先体验

将 Playwright 测试套件从 Applitools Eyes 迁移到 Delta-QA

为 Applitools Eyes 编写的 Playwright 测试套件,只需两处改动即可在 Delta-QA 上运行:import 和环境变量。其中的 eyes.open、check、close、BatchInfo、addBrowser 和区域均保持不变。

安装,并更改 import

@delta-qa/vision 的 Eyes API 沿用 Applitools Eyes for Playwright 的名称和含义。它需要 Node.js 22 或更高版本,以及 @playwright/test 1.62 或更高版本。

Vision SDK 首先以抢先体验方式提供:联系我们,即可在您的测试套件中使用。 申请使用 Vision

ApplitoolsDelta-QA
import { test } from "@applitools/eyes-playwright/fixture";import { test } from "@delta-qa/vision/eyes/fixture";
import type { EyesFixture } from "@applitools/eyes-playwright/fixture";import type { EyesFixture } from "@delta-qa/vision/eyes/fixture";
import { Eyes, Target, … } from "@applitools/eyes-playwright";import { Eyes, Target, … } from "@delta-qa/vision/eyes";

playwright.config 中的 eyesConfig 保持原样。使用 fixture 时,测试只需更改 import:

// import { test } from "@applitools/eyes-playwright/fixture";
import { test } from "@delta-qa/vision/eyes/fixture";

test("checkout", async ({ page, eyes }) => {
  await page.goto("https://shop.example/cart");
  await eyes.check("Cart", { fully: true });
}); // opened before the test, closed by the fixture

设置环境变量

变量用于
DELTAQA_API_KEY您 Delta-QA 工作区的 API 密钥(dqa_…),角色为“Can edit”,在 Settings → Workspace → API keys 中创建。请将其存入 CI 的密钥变量。
APPLITOOLS_API_KEY在未设置 DELTAQA_API_KEY 时读取:保留该变量,并为其填入 Delta-QA 的密钥。
DELTAQA_SERVER_URL您的 Delta-QA 服务器,用于 On-Premise 部署;未设置时为 Delta-QA 云服务。APPLITOOLS_SERVER_URL 从不读取。
APPLITOOLS_BATCH_ID被测的版本,即提交。代码中给出的批次 id 优先。
APPLITOOLS_BRANCH分支。代码中的 setBranchName 优先。
APPLITOOLS_PARENT_BRANCH
APPLITOOLS_BASELINE_BRANCH
基础分支:在分支尚无基准时使用其基准。APPLITOOLS_BASELINE_BRANCH 优先于 APPLITOOLS_PARENT_BRANCH;代码优先于两者。

分支变量的 _NAME 形式(APPLITOOLS_BRANCH_NAME、APPLITOOLS_PARENT_BRANCH_NAME、APPLITOOLS_BASELINE_BRANCH_NAME)同样会被读取。空变量视为未设置。没有密钥时,打开测试会失败,并指出需要设置的变量。

以下 Applitools 变量不会被读取,也不会发出警告: APPLITOOLS_SERVER_URL, APPLITOOLS_BATCH_NAME, APPLITOOLS_DONT_CLOSE_BATCHES, APPLITOOLS_SHOW_LOGS.

配置您的 CI

SDK 既不读取 git,也不读取您 CI 的变量:下面几行根据这些变量设置分支、基础分支和提交,写入三个 Applitools 变量。

GitHub Actions

push 时,github.head_ref 为空:此时分支为 github.ref_name。在 pull request 中,github.sha 是 GitHub 生成的合并提交,而不是推送的提交:head 提交优先。

env:
  APPLITOOLS_BRANCH: ${{ github.head_ref || github.ref_name }}
  APPLITOOLS_PARENT_BRANCH: ${{ github.base_ref || github.event.repository.default_branch }}
  APPLITOOLS_BATCH_ID: ${{ github.event.pull_request.head.sha || github.sha }}

GitLab CI

在合并请求流水线中,CI_MERGE_REQUEST_TARGET_BRANCH_NAME 是目标分支;在分支流水线中,它为空。

script:
  - export APPLITOOLS_BRANCH="${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:-$CI_COMMIT_REF_NAME}"
  - export APPLITOOLS_PARENT_BRANCH="${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-$CI_DEFAULT_BRANCH}"
  - export APPLITOOLS_BATCH_ID="$CI_COMMIT_SHA"
  - npx playwright test

Bitbucket Pipelines

BITBUCKET_PR_DESTINATION_BRANCH 仅在 pull request 流水线中设置。

script:
  - export APPLITOOLS_BRANCH="$BITBUCKET_BRANCH"
  - export APPLITOOLS_PARENT_BRANCH="$BITBUCKET_PR_DESTINATION_BRANCH"
  - export APPLITOOLS_BATCH_ID="$BITBUCKET_COMMIT"
  - npx playwright test

Azure Pipelines

System.PullRequest 变量仅在 pull request 构建中设置,对于 Azure Repos 其形式为 refs/heads/…。Build.SourceBranchName 只保留分支的最后一段(feature/cart 得到 cart),因此分支从完整的 ref 中读取。

- script: |
    branch="${SYSTEM_PULLREQUEST_SOURCEBRANCH:-$BUILD_SOURCEBRANCH}"
    base="${SYSTEM_PULLREQUEST_TARGETBRANCH:-}"
    export APPLITOOLS_BRANCH="${branch#refs/heads/}"
    export APPLITOOLS_PARENT_BRANCH="${base#refs/heads/}"
    export APPLITOOLS_BATCH_ID="${SYSTEM_PULLREQUEST_SOURCECOMMITID:-$BUILD_SOURCEVERSION}"
    npx playwright test

与分支相同的基础分支不会被使用:在默认分支上,这几行不提供基础分支。

在 teardown 中关闭,绝不在测试主体中关闭

使用 fixture 时,eyes 会在测试的 teardown 阶段关闭。不使用 fixture 时,请在 afterEach 中调用 eyes.close(),并在 afterAll 中调用 runner.getAllTestResults()。

绝不要在测试主体中关闭:等待判定结果会占用测试的时间(Playwright Test 中默认 30 秒),而在 close() 之前失败的步骤会让测试保持打开。teardown 有自己的超时时间。

import { test } from "@playwright/test";
import { Eyes, Target, VisualGridRunner } from "@delta-qa/vision/eyes";

const runner = new VisualGridRunner();
let eyes: Eyes;

test.beforeEach(async ({ page }) => {
  eyes = new Eyes(runner);
  await eyes.open(page, "Shop", test.info().title);
});
test("checkout", async ({ page }) => {
  await page.goto("https://shop.example/cart");
  await eyes.check("Cart", Target.window().fully());
});
test.afterEach(async () => {
  await eyes.close(); // throws DiffsFoundError when a difference awaits a judgment
});
test.afterAll(async () => {
  await runner.getAllTestResults();
});

每一项 Applitools 设置及其对应方式

Applitools Eyes 的每一项设置都会被接受:测试套件可以编译并运行。每项设置要么由 Delta-QA 应用,要么被接受但不生效,并发出警告。少数调用不存在。

已应用

设置在 Delta-QA 中
appName测试所属的应用;它与测试名称一起标识测试。在 eyesConfig 中,未设置时为 My App,与 Applitools 的 fixture 相同。
testName测试的名称,即它的键:名称改变即为新测试。在 eyesConfig 中,它也可以是一个以测试为参数的函数;未设置时,为不含文件名的测试标题路径(describe > test),与 Applitools 的 fixture 相同。
apiKey
setApiKey
密钥,替代 DELTAQA_API_KEY 或 APPLITOOLS_API_KEY,须为 Delta-QA 的密钥。它从不出现在任何消息、日志或链接中。
branchName本次运行的分支;未设置时依次读取 APPLITOOLS_BRANCH、APPLITOOLS_BRANCH_NAME。
parentBranchName
baselineBranchName
基础分支,baselineBranchName 优先于 parentBranchName;两者都未设置时,按相同顺序读取对应的变量。
batch其 id 即被测的版本,也就是提交;未设置时读取 APPLITOOLS_BATCH_ID;两者都没有时,每个进程一个版本(见下文“与 Applitools 的不同之处”)。
saveNewTests新测试的页面是否成为其基准;未设置时为 true。
viewportSize测试打开时为页面设定的尺寸。
browsersInfo
addBrowser(width, height)
每个尺寸是一个环境,适用于 Chrome 或未指定浏览器名称的情况。其语言、时区、主题和屏幕像素密度从测试的浏览器中读取。
fully
fully(true)
整个页面,Delta-QA 无论如何都会校验整个页面。
withName
name
check 的名称。
Target.window测试的页面。
Target.region
CheckSettings.region
checkRegion
checkRegionBy
checkRegionByElement
checkElement
checkElementBy
页面中唯一参与比较的部分:locator、选择器或元素句柄所指定的第一个元素,与 Applitools 相同;以最后给出的区域为准。其中包含的忽略区域会被排除。
ignoreRegions
ignoreRegion
ignore
ignores
区域所指定的每个元素(locator、选择器、{ selector, type }、元素句柄)都会被排除在比较之外:locator 的每个元素都排除,与 Applitools 相同。也支持 { region } 形式以及对象形式中的 ignoreRegions 键。
eyesConfig.failTestsOnDiff在 fixture 中,不是 PASSED 的判定结果何时导致失败:'afterEach' 在测试的 teardown 阶段使该测试失败;'afterAll' 在 worker 的测试全部完成后使该 worker 失败;false 不使任何内容失败,但仍会等待判定结果。未设置时,若设置了 CI 变量则为 'afterAll',否则为 'afterEach',与 Applitools fixture 代码的判断方式相同。
eyesConfig.afterAll在 worker 的测试全部完成后调用一次,并传入每个测试的结果。

调用

调用在 Delta-QA 中
open(page, appName, testName, viewportSize?)测试由其应用和名称标识:名称改变即为新测试。视口尺寸会应用到页面上。
check(name?, Target.window())
checkWindow(tag?)
在每个环境中按当前状态检查页面。没有名称的 check 按其页面地址对应;允许两个同名的 check。它返回一个不含判定结果的 MatchResult,与 Applitools 的 check 相同:判定结果在关闭时给出。
close(throwErr = true)等待判定结果:存在待判定的差异时抛出 DiffsFoundError,存在待判定的新测试时抛出 NewTestError(saveNewTests(false)),差异被判定为 Bug 或运行失败时抛出 TestFailedError。错误信息会给出该版本在 Delta-QA 中的链接。
closeAsync()
runner.getAllTestResults(throwErr = true)
closeAsync() 关闭而不等待;runner 会等待每个判定结果,关闭仍处于打开状态的测试,并在测试未通过时抛出错误,closeAsync() 的错误会为其保留。
abort()
abortIfNotClosed()
abortAsync()
关闭而不等待判定结果,且不使任何内容失败:结果被标记为已中止,不含结果;没有打开的测试时返回 null。Delta-QA 仍会比较这些运行,版本会显示其结果。

接受但不生效

Delta-QA 不应用的设置不会使测试失败:测试在没有该设置的情况下运行。它在一个进程中首次使用时,会发出一条指明其名称的 Node.js 警告,代码为 DELTAQA_IGNORED_SETTING;之后的使用不再提示,node --disable-warning=DELTAQA_IGNORED_SETTING 可关闭这些警告。空值(例如在 CI 之外的 setX(process.env.VARIABLE))视为未提供该设置,不会发出警告。下列名称即警告中给出的名称。

比较设置

匹配级别、按匹配级别设定的区域、浮动区域和位移用于调节 Applitools 的比较。Delta-QA 像人眼那样比较页面,没有需要调节的容差级别:每处差异都会展示出来,由人在 Delta-QA 中判定。

  • matchLevel
  • defaultMatchSettings
  • enablePatterns
  • ignoreDisplacements
  • ignoreCaret
  • useDom
  • sendDom
  • CheckSettings.matchLevel
  • CheckSettings.layout
  • CheckSettings.exact
  • CheckSettings.strict
  • CheckSettings.content
  • CheckSettings.ignoreColors
  • CheckSettings.dynamic
  • CheckSettings.enablePatterns
  • CheckSettings.ignoreDisplacements
  • CheckSettings.ignoreCaret
  • CheckSettings.ignoreMismatch
  • CheckSettings.useDom
  • CheckSettings.sendDom
  • CheckSettings.layoutRegion
  • CheckSettings.layoutRegions
  • CheckSettings.strictRegion
  • CheckSettings.strictRegions
  • CheckSettings.contentRegion
  • CheckSettings.contentRegions
  • CheckSettings.floating
  • CheckSettings.floatings
  • CheckSettings.floatingRegion
  • CheckSettings.floatingRegions
  • CheckSettings.dynamicRegion
  • CheckSettings.dynamicRegions

截图设置

拼接、滚动、裁剪、等待和调试截图决定 Applitools 截图的方式。Delta-QA 校验调用 check 时测试所留下的整个页面:如需等待内容或加载懒加载区域,请在测试中、check 之前完成。

  • hideCaret
  • hideScrollbars
  • forceFullPageScreenshot
  • stitchMode
  • stitchOverlap
  • scrollRootElement
  • cut
  • rotation
  • waitBeforeScreenshots
  • waitBeforeCapture
  • matchTimeout
  • saveDebugScreenshots
  • debugScreenshotsPath
  • debugScreenshotsPrefix
  • fully(false)
  • CheckSettings.fully(false)
  • CheckSettings.scrollRootElement
  • CheckSettings.stitchMode
  • CheckSettings.timeout
  • CheckSettings.waitBeforeCapture
  • CheckSettings.lazyLoad
  • CheckSettings.useSystemScreenshot

浏览器、设备和环境

Delta-QA 在 Chromium 中渲染:起作用的是传给 addBrowser 的尺寸,而不是所指定的浏览器,因此尺寸相同的两个浏览器是同一个环境。Applitools 渲染网格中的设备(DeviceName、IosDeviceName、仿真、多设备)不会增加环境。语言、时区、主题和屏幕像素密度从测试的浏览器中读取,环境标签不会改变任何内容。ClassicRunner 和 VisualGridRunner 的行为相同。

  • addBrowser(chrome-one-version-back)
  • addBrowser(chrome-two-versions-back)
  • addBrowser(firefox)
  • addBrowser(firefox-one-version-back)
  • addBrowser(firefox-two-versions-back)
  • addBrowser(safari)
  • addBrowser(safari-one-version-back)
  • addBrowser(safari-two-versions-back)
  • addBrowser(edgechromium)
  • addBrowser(edgechromium-one-version-back)
  • addBrowser(edgechromium-two-versions-back)
  • addBrowser(edgelegacy)
  • addBrowser(ie)
  • addBrowser(ie10)
  • addBrowser(device)
  • addDeviceEmulation
  • addMultiDeviceTarget
  • mobileOptions
  • captureStatusBar
  • layoutBreakpoints
  • CheckSettings.layoutBreakpoints
  • environmentName
  • envName
  • baselineEnvName
  • deviceInfo
  • hostApp
  • hostAppInfo
  • hostOS
  • hostOSInfo
  • scaleRatio
  • CheckSettings.densityMetrics
  • eyesConfig.type

Applitools 的渲染网格

Applitools 渲染网格的渲染选项、钩子、资源获取、缓存和并发都没有对应项:页面按测试所留下的状态进行校验。原本在钩子中运行的脚本,改为在测试中、check 之前运行。

  • visualGridOptions
  • CheckSettings.ufgOption
  • CheckSettings.ufgOptions
  • CheckSettings.visualGridOption
  • CheckSettings.visualGridOptions
  • CheckSettings.variationGroupId
  • CheckSettings.hook
  • CheckSettings.beforeRenderScreenshotHook
  • CheckSettings.webHook
  • disableBrowserFetching
  • CheckSettings.disableBrowserFetching
  • disableNMLUrlCache
  • autProxy
  • bypassCSP
  • skipRootHtmlResource
  • concurrentSessions
  • RunnerOptions.testConcurrency
  • RunnerOptions.fetchConcurrency
  • RunnerOptions.removeDuplicateTests

批次、基准和结果

版本承载的是提交:它没有名称、序列、属性、通知,也无需关闭。只有当有人在 Delta-QA 中判定一个差异时,基准才会改变;没有基准的测试为“新建”。check 按名称对应,否则按其页面地址对应。isDisabled:本应不带 check 运行的测试不会打开 Eyes。

  • BatchInfo.name
  • BatchInfo.sequenceName
  • BatchInfo.startedAt
  • BatchInfo.notifyOnCompletion
  • BatchInfo.properties
  • BatchInfo.buildId
  • dontCloseBatches
  • BatchClose
  • properties
  • removeSession
  • saveDiffs
  • saveFailedTests
  • ignoreBaseline
  • displayName
  • sessionType
  • CheckSettings.pageId
  • isDisabled

Git

SDK 不读取任何 git 信息:请通过 parentBranchName 或 baselineBranchName 提供基础分支,并通过批次 id 或 APPLITOOLS_BATCH_ID 提供提交。

  • compareWithParentBranch
  • ignoreGitMergeBase
  • gitMergeBaseTimestamp
  • latestCommitInfo

服务器、连接和日志

这些设置涉及 Applitools 的服务器、连接和日志。Delta-QA 的服务器从 DELTAQA_SERVER_URL 读取;SDK 不写入任何 Applitools 日志,也不发出任何 Applitools 事件。

  • serverUrl
  • proxy
  • agentId
  • httpVersion
  • connectionTimeout
  • Eyes.on
  • Eyes.setLogHandler
  • Eyes.addSessionEventHandler
  • eyesConfig.logConfig
  • RunnerOptions.maskLog

Delta-QA 之外的功能

Delta-QA 不做无障碍校验,不读取 Figma,也没有自然语言检查;Playwright 页面没有 web view。

  • accessibilityValidation
  • CheckSettings.accessibilityRegion
  • CheckSettings.accessibilityRegions
  • figmaBaselines
  • figmaOptions
  • nlpOptions
  • Target.webview
  • CheckSettings.webview

没有对应元素的区域

Delta-QA 找出不同的元素:以坐标给出的区域({ x, y, width, height }、{ left, top, width, height })没有对应项:页面在不考虑该区域的情况下比较;对于 Target.region,则比较整个页面。区域即元素本身,不含内边距;regionId 用于在 Applitools 的仪表板中命名区域。使用 Target.frame 或 Target.shadow 时,比较整个页面,此时再通过选择器给出的区域会使 check 失败并抛出 TypeError:区域必须是页面本身的元素。

  • CheckSettings.ignoreRegions(coordinates)
  • Target.region(coordinates)
  • CodedRegion.padding
  • CodedRegion.regionId
  • Target.frame
  • Target.shadow
  • CheckSettings.frame
  • CheckSettings.shadow

配置或 check 的对象形式中的未知键会以其名称发出警告(在 check 中为 CheckSettings.<key>)。

不提供:无法编译的部分

这些不是设置:它们返回 Delta-QA 不具备的数据,或校验的是图像而不是页面。使用它们的测试套件会无法编译,而不是得到一个编造的值。

不提供原因
eyes.locate
eyes.extractText
eyes.extractTextRegions
它们返回 Applitools 从其截图中读取的区域或文本。
eyes.checkImage
Target.image
Target.buffer
Target.base64
Target.path
Target.url
Delta-QA 校验的是页面,而不是图像。
eyes.getLogger
eyes.logger
Applitools 的日志。
eyes.runApplitools fixture 中的自然语言检查。

与 Applitools 的不同之处

Delta-QA 有意为之的选择,首次运行前需要了解。

情形Delta-QA
将 check 与其基准对应当每个名称在测试中唯一时,按 check 的名称对应;否则按顺序、按名称,再不行则按页面地址对应:插入或删除一个 check 不会让其他 check 错位。
在某个分支上接受的变更不会自动将一个分支的基准合并到另一个分支。当同样的差异出现在另一个分支上时,Delta-QA 会提示“Already intended on … — apply?”。
接受一个差异“预期行为”作用于您勾选的分支。
没有 APPLITOOLS_BATCH_ID,也没有批次 id每个进程一个版本:Playwright Test 的每个 worker 都是一个进程,因此多个 worker 会产生多个版本,使用 fixture 时也是如此。将 APPLITOOLS_BATCH_ID 设为您 CI 的提交,即可共享同一个版本。
未匹配任何元素的忽略区域该区域被忽略,check 在没有它的情况下发送,与 Applitools 的做法相同;同时会有一条代码为 DELTAQA_IGNORED_REGION 的 Node.js 警告指出该区域,每个区域在每个进程中只提示一次(node --disable-warning=DELTAQA_IGNORED_REGION 可关闭)。未匹配任何内容的待校验区域会使 check 失败并抛出 ZoneNotFoundError,与 Applitools 相同。
runner.getAllTestResults() 与仍处于打开状态的测试测试会被关闭并给出判定结果;Applitools 则会中止它。
Fixture:位于 playwright.config 顶层和项目中的 eyesConfig项目中的配置替换顶层的配置,与 Playwright Test 处理选项的方式相同;test.use 会将其键添加到项目的配置中。Applitools 的 fixture 则逐键合并两者。
Fixture:type: 'classic' 搭配 browsersInfo每个尺寸是一个环境,与在 fixture 之外使用 ClassicRunner 时相同;Applitools 的 fixture 会丢弃这些尺寸。type 会发出警告。
Fixture:expect(page).toHaveScreenshot()使用 Playwright Test 自身的断言:expect 即 @playwright/test 提供的那个。Applitools 的 fixture 会把它变成一个 Eyes check。
30 分钟内没有任何 check 的测试Delta-QA 会将其关闭;之后 close() 读取其判定结果。

您发送的数据

SDK 发送的内容(您的测试所校验的页面及其使用的文件)仅用于比较。使用生产环境中的个人数据进行测试由您负责:请在测试数据上运行测试套件。

Vision SDK:在您的 Playwright 测试中由 Delta-QA 校验

Applitools、Applitools Eyes 和 Eyes 是 Applitools Ltd. 的商标。Delta-QA 与 Applitools 没有关联,也未获得 Applitools 的认可或赞助。

在 Delta-QA 上运行您的 Eyes 测试套件

如需使用 Vision,请联系我们:它首先以抢先体验方式提供。告诉我们您想校验哪些测试,我们会为您开通访问。