将 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
| Applitools | Delta-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_BRANCHAPPLITOOLS_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 testBitbucket 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 testAzure 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 相同。 |
apiKeysetApiKey | 密钥,替代 DELTAQA_API_KEY 或 APPLITOOLS_API_KEY,须为 Delta-QA 的密钥。它从不出现在任何消息、日志或链接中。 |
branchName | 本次运行的分支;未设置时依次读取 APPLITOOLS_BRANCH、APPLITOOLS_BRANCH_NAME。 |
parentBranchNamebaselineBranchName | 基础分支,baselineBranchName 优先于 parentBranchName;两者都未设置时,按相同顺序读取对应的变量。 |
batch | 其 id 即被测的版本,也就是提交;未设置时读取 APPLITOOLS_BATCH_ID;两者都没有时,每个进程一个版本(见下文“与 Applitools 的不同之处”)。 |
saveNewTests | 新测试的页面是否成为其基准;未设置时为 true。 |
viewportSize | 测试打开时为页面设定的尺寸。 |
browsersInfoaddBrowser(width, height) | 每个尺寸是一个环境,适用于 Chrome 或未指定浏览器名称的情况。其语言、时区、主题和屏幕像素密度从测试的浏览器中读取。 |
fullyfully(true) | 整个页面,Delta-QA 无论如何都会校验整个页面。 |
withNamename | check 的名称。 |
Target.window | 测试的页面。 |
Target.regionCheckSettings.regioncheckRegioncheckRegionBycheckRegionByElementcheckElementcheckElementBy | 页面中唯一参与比较的部分:locator、选择器或元素句柄所指定的第一个元素,与 Applitools 相同;以最后给出的区域为准。其中包含的忽略区域会被排除。 |
ignoreRegionsignoreRegionignoreignores | 区域所指定的每个元素(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 中判定。
matchLeveldefaultMatchSettingsenablePatternsignoreDisplacementsignoreCaretuseDomsendDomCheckSettings.matchLevelCheckSettings.layoutCheckSettings.exactCheckSettings.strictCheckSettings.contentCheckSettings.ignoreColorsCheckSettings.dynamicCheckSettings.enablePatternsCheckSettings.ignoreDisplacementsCheckSettings.ignoreCaretCheckSettings.ignoreMismatchCheckSettings.useDomCheckSettings.sendDomCheckSettings.layoutRegionCheckSettings.layoutRegionsCheckSettings.strictRegionCheckSettings.strictRegionsCheckSettings.contentRegionCheckSettings.contentRegionsCheckSettings.floatingCheckSettings.floatingsCheckSettings.floatingRegionCheckSettings.floatingRegionsCheckSettings.dynamicRegionCheckSettings.dynamicRegions
截图设置
拼接、滚动、裁剪、等待和调试截图决定 Applitools 截图的方式。Delta-QA 校验调用 check 时测试所留下的整个页面:如需等待内容或加载懒加载区域,请在测试中、check 之前完成。
hideCarethideScrollbarsforceFullPageScreenshotstitchModestitchOverlapscrollRootElementcutrotationwaitBeforeScreenshotswaitBeforeCapturematchTimeoutsaveDebugScreenshotsdebugScreenshotsPathdebugScreenshotsPrefixfully(false)CheckSettings.fully(false)CheckSettings.scrollRootElementCheckSettings.stitchModeCheckSettings.timeoutCheckSettings.waitBeforeCaptureCheckSettings.lazyLoadCheckSettings.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)addDeviceEmulationaddMultiDeviceTargetmobileOptionscaptureStatusBarlayoutBreakpointsCheckSettings.layoutBreakpointsenvironmentNameenvNamebaselineEnvNamedeviceInfohostApphostAppInfohostOShostOSInfoscaleRatioCheckSettings.densityMetricseyesConfig.type
Applitools 的渲染网格
Applitools 渲染网格的渲染选项、钩子、资源获取、缓存和并发都没有对应项:页面按测试所留下的状态进行校验。原本在钩子中运行的脚本,改为在测试中、check 之前运行。
visualGridOptionsCheckSettings.ufgOptionCheckSettings.ufgOptionsCheckSettings.visualGridOptionCheckSettings.visualGridOptionsCheckSettings.variationGroupIdCheckSettings.hookCheckSettings.beforeRenderScreenshotHookCheckSettings.webHookdisableBrowserFetchingCheckSettings.disableBrowserFetchingdisableNMLUrlCacheautProxybypassCSPskipRootHtmlResourceconcurrentSessionsRunnerOptions.testConcurrencyRunnerOptions.fetchConcurrencyRunnerOptions.removeDuplicateTests
批次、基准和结果
版本承载的是提交:它没有名称、序列、属性、通知,也无需关闭。只有当有人在 Delta-QA 中判定一个差异时,基准才会改变;没有基准的测试为“新建”。check 按名称对应,否则按其页面地址对应。isDisabled:本应不带 check 运行的测试不会打开 Eyes。
BatchInfo.nameBatchInfo.sequenceNameBatchInfo.startedAtBatchInfo.notifyOnCompletionBatchInfo.propertiesBatchInfo.buildIddontCloseBatchesBatchClosepropertiesremoveSessionsaveDiffssaveFailedTestsignoreBaselinedisplayNamesessionTypeCheckSettings.pageIdisDisabled
Git
SDK 不读取任何 git 信息:请通过 parentBranchName 或 baselineBranchName 提供基础分支,并通过批次 id 或 APPLITOOLS_BATCH_ID 提供提交。
compareWithParentBranchignoreGitMergeBasegitMergeBaseTimestamplatestCommitInfo
服务器、连接和日志
这些设置涉及 Applitools 的服务器、连接和日志。Delta-QA 的服务器从 DELTAQA_SERVER_URL 读取;SDK 不写入任何 Applitools 日志,也不发出任何 Applitools 事件。
serverUrlproxyagentIdhttpVersionconnectionTimeoutEyes.onEyes.setLogHandlerEyes.addSessionEventHandlereyesConfig.logConfigRunnerOptions.maskLog
Delta-QA 之外的功能
Delta-QA 不做无障碍校验,不读取 Figma,也没有自然语言检查;Playwright 页面没有 web view。
accessibilityValidationCheckSettings.accessibilityRegionCheckSettings.accessibilityRegionsfigmaBaselinesfigmaOptionsnlpOptionsTarget.webviewCheckSettings.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.paddingCodedRegion.regionIdTarget.frameTarget.shadowCheckSettings.frameCheckSettings.shadow
配置或 check 的对象形式中的未知键会以其名称发出警告(在 check 中为 CheckSettings.<key>)。
不提供:无法编译的部分
这些不是设置:它们返回 Delta-QA 不具备的数据,或校验的是图像而不是页面。使用它们的测试套件会无法编译,而不是得到一个编造的值。
| 不提供 | 原因 |
|---|---|
eyes.locateeyes.extractTexteyes.extractTextRegions | 它们返回 Applitools 从其截图中读取的区域或文本。 |
eyes.checkImageTarget.imageTarget.bufferTarget.base64Target.pathTarget.url | Delta-QA 校验的是页面,而不是图像。 |
eyes.getLoggereyes.logger | Applitools 的日志。 |
eyes.run | Applitools 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 的认可或赞助。