社区新闻

开始与Playwright一起编写WordPress端到端测试

你已经看到命名空间和编码标准如何增加自动化单元测试,可以提升你对项目按预期运行的信心。但还有另一层值得补充的因素:端到端(端对端)测试。它们让你能从不同角度测试项目,涵盖单元测试无法涵盖的情况。

在本文中,你将学习如何为WordPress端对端测试设置Playwright,并在使用REST API设置后编写涵盖块变体、块模式和前端的测试。实际案例涵盖了现实场景,为你打下坚实基础,可以根据自己的项目特点进行调整。

什么是端对端测试?

端对端测试可用于性能、无障碍和可视化测试,但最常见的形式是基于界面的函数测试,模拟用户在浏览器中与应用程序(如WordPress)交互时的操作。

单元测试不同,端对端测试是宏观层面的。即使在测试某个特定功能时,他们也会考察应用程序的多个组件和层次如何整体协作。这种更广泛的范围也带来了代价:端对端测试比单元测试更慢,有时也更脆弱。由于它们与整个应用栈交互,任何层、UI、网络或数据库的更改都可能破坏测试。因此,它们最好用于覆盖关键用户流程,而非所有可能的场景。

古腾堡项目从一开始就大量使用端对端测试,因为它们非常适合该项目的性质。此后,它们也被纳入了 WordPress Core,补充了已经相当完善的 PHPUnit 测试套件。

开始之前

正在测试的项目

在继续之前,请确保你熟悉“用块装订构建书评网站”系列,因为本文的测试基于其构建的功能。

这是一个两部分的系列,非常值得从头到尾阅读,因为其中有很多值得学习的地方。不过,略过一下也足以跟上,即使跳过更技术性的部分。

所需工具

如果你想跟着写代码的示例学习,确保你安装了Git、Node.js和Docker。Docker 是要求wp-env,将用于本地的WordPress环境设置。

如果您需要安装这些工具中的任何一项,请参阅块开发环境指南。

项目设置

要设置Justin Tadlock构建的内容,请下载或克隆“用块装订构建书评网站”仓库

从分支开始,但如果你卡住了,可以查看masterfeature/e2e-playwright-tests分支以查看本文中添加的端对端测试最终代码。

一旦你在本地拿到文件,就在项目文件夹里安装依赖,这些依赖已经包含了包括其他内容,通过运行:wp-scripts

npm install

使用wp-env的本地环境设置

要运行端对端测试,你需要一个WordPress环境,所有设备都已安装并配置好,这里是TT4书评子主题,以及Twenty Twenty-Four父主题,都已安装并激活。

对于本地 WordPress 环境,它依然是稳妥的选择,因为它能最小化设置时间,由 WordPress 项目自行维护,并且与其他 WordPress 工具集成良好。不过,这并不是严格要求,而且维护一个专门的测试站点,托管在服务器上而不是本地安装也很常见。wp-envwp-env

安装时,运行:wp-env

npm install @wordpress/env@^10.39.0 --save-dev

接下来,创建一个包含以下内容的配置文件:.wp-env.json

{
	"$schema": "https://schemas.wp.org/trunk/wp-env.json",
	"themes": [ "." ],
	"lifecycleScripts": {
		"afterStart": "THEME_SLUG=$(basename \"$PWD\"); for SERVICE in cli tests-cli; do wp-env run \"$SERVICE\" wp theme activate \"$THEME_SLUG\"; done"
	}
}

这会将当前文件夹映射为主题,生命周期脚本会在开发和测试环境中激活该主题,启动时会启动。wp-env

由于Twenty Twenty-Four主题默认在 中可用,安装无需额外步骤。wp-env

通过运行开始环境:

npx wp-env start

一旦开始,你应该会看到类似的输出:

WordPress development site started at http://localhost:8888
WordPress test site started at http://localhost:8889
MySQL is listening on port 32770
MySQL for automated testing is listening on port 32771

  Done! (in 29s 136ms)

如果出现该端口或不可用的错误,说明其他服务已经在使用该端口。最简单的解决办法是停止该服务,等以后需要时再重新启动。或者,你也可以配置使用不同的端口,但你也需要在下面的Playwright配置中更新设置。88888889wp-envwebServer

许多 WordPress 工具还假设脚本在 中定义,包括下一步将添加的默认 Playwright 配置。通过修改现有文件添加:wp-envpackage.json

{
	"scripts": {
		"start": "...",
		"build": "...",
		"wp-env": "wp-env"
	},
	"devDependencies": {
		// ...
	}
}

如果你是第一次使用,想了解更多,可以使用“用 wp-env 入门指南”或“快速简便的 WordPress 本地开发 wp-env 文章,都是很好的起点。wp-env

剧作家设定

在端对端测试方面,WordPress使用Playwright,这是一种可靠、现代且广泛采用的解决方案。

不过,这并不是端对端测试的唯一选择。WordPress以前用过Puppeteer,现在也有一些相关软件包存在,但对于新项目来说,Playwright的软件包始终是首选。

安装依赖

除了Playwright测试包外,还要安装WordPress库的端到端(E2E)Playwright测试工具。这为WordPress提供了专属便利,使一切更加简单。

要安装两者,请运行:

npm install @playwright/test@^1.58.2 @wordpress/e2e-test-utils-playwright@^1.41.0 --save-dev

Playwright 还需要浏览器二进制文件和系统依赖,与 JavaScript 包分开。下载数据可能有几百兆字节,而且因为它安装了系统级依赖,可能会要求输入你的根密码。

安装方式包括:

npx playwright install --with-deps

添加配置文件

Playwright 有很多配置选项,但你可以先用默认配置来开门。wp-scripts

创建一个文件并添加以下内容:playwright.config.js

export { default } from '@wordpress/scripts/config/playwright.config.js';

这是一个很好的起点。之后,如果你需要自定义任何默认值,比如更改测试文件的位置,你可以这样扩展基础配置:

import { defineConfig } from '@playwright/test';
import baseConfig from '@wordpress/scripts/config/playwright.config';

export default defineConfig( {
	...baseConfig,
	testDir: './tests/e2e/', // Instead of the specs directory which is the default location
} );

需要注意的是,默认配置假设使用,并在端对端测试执行时自动启动。如果你用的是 以外的工具,就需要自定义默认配置wp-envwp-envwebServer配置。

验证设置

此时,所有准备就绪,准备准备参加你的第一次考试。要确认设置是否正常,请运行:

npx wp-scripts test-playwright

你应该会看到一条提示,提示未找到测试文件:

Error: No tests found

这很正常,因为你还没写过任何测试!

你的第一次考验

开始时,先从WordPress Core E2E测试套件中借用一个示例测试,再编写任何项目具体内容。该测试加载WordPress管理仪表盘,并确认显示“欢迎使用WordPress”标题。

创建一个名为 的新文件夹,在里面创建一个名为以下内容的文件:specsmain.spec.js

import { test, expect } from '@wordpress/e2e-test-utils-playwright';

test( 'Loads WordPress dashboard', async ( { admin, page } ) => {
	await admin.visitAdminPage( '/' );

	await expect(
		page.getByRole( 'heading', { name: 'Welcome to WordPress', level: 2 } )
	).toBeVisible();
} );

再运行一次测试套件:

npx wp-scripts test-playwright

这次,你应该看到的不是错误,而是测试通过:

Running 1 test using 1 worker

  ✓  1 [chromium] › specs/main.spec.js:3:5 › Loads WordPress dashboard (831ms)

  1 passed (2.1s)

测试攻略

每个测试都有一个名称,这里是“加载WordPress仪表盘”,其逻辑存储在传递给 的回调中。test()

测试逻辑通常可分为三部分:设定前提条件、执行操作和验证结果。这通常被称为“安排、行动与断言”(AAA)模式。在实际操作中,在端对端测试中,这些步骤可能是交错的,或者具有作用-断言循环,而非单一线性序列。

对于“加载WordPress仪表盘”测试,操作就是访问管理仪表盘。

await admin.visitAdminPage( '/' );

还可以考虑其他检查来验证断言,但如果“欢迎使用WordPress”标题带有2级,且你在URL上,可以合理推断页面已完全加载,你在仪表盘上。/wp-admin/

await expect(
	page.getByRole( 'heading', { name: 'Welcome to WordPress', level: 2 } )
).toBeVisible();

该测试还使用了WordPress专用的辅助工具,由你之前安装的WordPress E2E Test Utils包提供。admin.visitAdminPage()

这些助手可以隐藏大量复杂性,处理边缘情况,使操作更简单。以下是这些功能在幕后实际的作用:

export async function visitAdminPage(
	this: Admin,
	adminPath: string,
	query?: string
) {
	await this.page.goto(
		join( 'wp-admin', adminPath ) + ( query ? `?${ query }` : '' )
	);


	// Handle upgrade required screen
	if ( this.pageUtils.isCurrentURL( 'wp-admin/upgrade.php' ) ) {
		// Click update
		await this.page.click( '.button.button-large.button-primary' );
		// Click continue
		await this.page.click( '.button.button-large' );
	}


	if ( this.pageUtils.isCurrentURL( 'wp-login.php' ) ) {
		throw new Error( 'Not logged in' );
	}


	const error = await this.getPageError();
	if ( error ) {
		throw new Error( 'Unexpected error in page content: ' + error );
	}
}

“加载WordPress仪表盘”中的其他内容都是标准的Playwright API:用于选择元素(称为定位器)和断本身。page.getByRole()expect().toBeVisible()

还有一件事值得指出。测试开始前,你已经被认证为管理员,这也是仪表盘是可见的。使用默认的WordPress工具和配置时,这会自动发生。这意味着所有测试都从登录管理员的视角进行。

这是一个重要的细节,因为有些场景需要从访客的角度进行测试。

在UI模式下运行测试

你还可以在所谓的UI模式下运行测试,这让调试和检查测试行为变得更加简单。用同样的命令执行带有标志的命令:--ui

npx wp-scripts test-playwright --ui

这会打开一个新的浏览器窗口,包含UI模式提供的完整工具套件。

界面相当直观,但如果你想深入了解所有功能,Playwright UI模式的文档里也有视频攻略。

即使你喜欢命令行,也值得一试!

测试书评项目

为了保持文章的合理篇幅,这里只涵盖了“用块装订构建书评网站”的几个关键领域,但希望读完后,你会有动力自己去探索更多情境。

涵盖的领域包括:

  1. 一个测试检查段落块变体(如Book Author)是否已注册,可以插入并产生预期输出。
  2. 一个测试用于验证自定义图案是否注册,可以插入,插入后包含正确的块。
  3. 为了稍微变化一下,还有一个测试,用来验证设置后元值是否正确地在前端渲染。

测试图书作者栏

TT4 书评儿童主题注册了一个称为“书籍作者”的区块变体。

这种段落块的变体采用了块绑定,并以后元键的值作为内容。themeslug_book_author

为了方便参考注册方式如下:

registerBlockVariation( 'core/paragraph', {
	name: 'themeslug/book-author',
	title: __( 'Book Author', 'themeslug' ),
	description: __( 'Displays the book author.', 'themeslug' ),
	category: 'widgets',
	keywords: [ 'book', 'author' ],
	icon: pencil,
	scope: [ 'inserter' ],
	attributes: {
		metadata: {
			bindings: {
				content: {
					source: 'core/post-meta',
					args: {
						key: 'themeslug_book_author',
					},
				},
			},
		},
		placeholder: __( 'Book Author', 'themeslug' ),
	},
	example: {},
	isActive: ( blockAttributes ) =>
		'themeslug_book_author' ===
		blockAttributes?.metadata?.bindings?.content?.args?.key,
} );

考试规划

任何基于UI的功能性端对端测试的良好起点是想象你如何手动测试,然后详细记录每一步,再用代码复制这些步骤。

例如,你可以通过创建新帖子,打开区块插入器,在可用块中找到“书籍作者”,点击插入,来验证“书籍作者”块变体是否已注册并可插入。

如果你能按照这些步骤操作,且该块在编辑器中显示,你就可以相当有信心地说一切正常。然而,仅凭这些还不够。你还应该确认“书籍作者”栏目是否达到了应有的功能,显示它绑定的后元数据值。

有多种方法,但首先要把上面的步骤转化成代码。

插入块

你可以在 下创建一个新的测试文件,但为了简化,先添加一个名为“插入书籍作者块”的新测试。它应该是这样的样子:/specs/main.spec.js

test( 'Inserts Book Author block', async ( { admin, page, editor } ) => {
	await admin.createNewPost();

	await page
		.getByRole( 'button', {
			name: 'Block Inserter',
		} )
		.click();

	await page
		.getByRole( 'region', { name: 'Block Library' } )
		.getByRole( 'listbox', { name: 'Widgets' } )
		.getByRole( 'option', { name: 'Book Author', exact: true } )
		.click();

	// Assertions
} );

如果你在UI模式下运行测试,你应该能看到Playwright执行每一步并得到结果:

npx wp-scripts test-playwright --ui

首先,它使用WordPress E2E Test Utils包中的辅助工具创建新帖子。

await admin.createNewPost();

然后它会在页面上找到并选择元素并点击,就像手动测试时一样。

await page
	.getByRole( 'button', {
		name: 'Block Inserter',
	} )
	.click();

await page
	.getByRole( 'region', { name: 'Block Library' } )
	.getByRole( 'listbox', { name: 'Widgets' } )
	.getByRole( 'option', { name: 'Book Author', exact: true } )
	.click();

选择元素的方法有很多,包括 CSS 选择器和 XPath,但 WordPress 最佳实践指南建议使用无障碍选择器

你的浏览器开发者工具很可能有查看辅助功能树的功能。例如,以下是如何在 Chrome 的开发者工具中切换到无障碍标签页或切换完整无障碍树的方法。这样你就能轻松看到元素的角色和名称。

如果可能,坚持推荐的方法,尽管实际上并不总是可行。

验证封锁

验证属性

验证插入块是否使用块绑定和源码的一种方法是检查其属性是否正确。core/post-meta binding

手动操作时,你可以切换到代码编辑器,确认区块标记是否符合你的预期。

<!-- wp:paragraph {"placeholder":"Book Author","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_author"}}}}} -->
<p></p>
<!-- /wp:paragraph -->

如果这些属性都具备,可以合理假设该块会按预期工作。如果某些功能在属性正确的情况下仍然无法工作,问题很可能出在 WordPress Core 本身。

在Playwright中,你可以通过程序化检索区块结构并将其与预期结构进行比较来实现这一点。这是它的代码:

await expect.poll( editor.getBlocks ).toMatchObject( [
	{
		name: 'core/paragraph',
		attributes: {
			metadata: {
				bindings: {
					content: {
						source: 'core/post-meta',
						args: { key: 'themeslug_book_author' },
					},
				},
			},
			placeholder: 'Book Author',
		},
	},
] );

poll为了安全而添加。块插入是异步的,单纯调用可能在块可用前失败。 反复尝试回调直到通过或超时,从而提高可靠性。expect( editor.getBlocks )poll

验证功能

另一种方法是检查存储在后元数据中的值是否实际上显示为块的内容,而不是直接检查属性。themeslug_book_author

如果你想手动操作,必须打开编辑设置,选择“帖子”标签,找到书评面板,打开它,并在作者字段输入一个数值。

以下是这些步骤如何转化为代码:

await editor.openDocumentSettingsSidebar();
await page.getByRole( 'tab', { name: 'Post' } ).click();

await page
	.getByRole( 'region', { name: 'Editor settings' } )
	.getByRole( 'button', {
		name: 'Book Review',
	} )
	.click();

await page
	.getByRole( 'textbox', {
		name: 'Author',
	} )
	.fill( 'Jane Austen' );

仅仅设定一个数值就要多步了。

由于本次测试的目标不是测试作者控制本身,后元的值可以通过程序方式设置,使用wp.data.dispatch.

以下是选择区块并检查显示内容的代码示例:

await page.evaluate( () =>
	wp.data
		.dispatch( 'core/editor' )
		.editPost( { meta: { themeslug_book_author: 'Jane Austen' } } )
);

const bookAuthorBlock = editor.canvas.getByRole( 'document', {
	name: 'Block: Paragraph',
} );

await expect( bookAuthorBlock ).toHaveText( 'Jane Austen' );

evaluate调用用于确保回调在页面JavaScript上下文中执行,从而访问全局。wp.data

你甚至不必非得选择其中一种,可以两者兼顾。

随着你写更多测试和阅读相关内容,你会形成自己对项目最有效的方案:界限在哪里,什么感觉覆盖过多,什么感觉不够扎实。

供参考,这里是完整的“插入书籍作者模块”测试代码:

test( 'Inserts Book Author block', async ( { admin, page, editor } ) => {
	await admin.createNewPost();

	await page
		.getByRole( 'button', {
			name: 'Block Inserter',
		} )
		.click();

	await page
		.getByRole( 'region', { name: 'Block Library' } )
		.getByRole( 'listbox', { name: 'Widgets' } )
		.getByRole( 'option', { name: 'Book Author', exact: true } )
		.click();

	await expect.poll( editor.getBlocks ).toMatchObject( [
		{
			name: 'core/paragraph',
			attributes: {
				metadata: {
					bindings: {
						content: {
							source: 'core/post-meta',
							args: { key: 'themeslug_book_author' },
						},
					},
				},
				placeholder: 'Book Author',
			},
		},
	] );

	await page.evaluate( () =>
		wp.data
			.dispatch( 'core/editor' )
			.editPost( { meta: { themeslug_book_author: 'Jane Austen' } } )
	);

	const bookAuthorBlock = editor.canvas.getByRole( 'document', {
		name: 'Block: Paragraph',
	} );

	await expect( bookAuthorBlock ).toHaveText( 'Jane Austen' );
} );

如果你在这里运行测试命令,输出中应该会显示有两个测试通过了。

npx wp-scripts test-playwright
Running 2 tests using 1 worker

  ✓  1 [chromium] › specs/main.spec.js:3:5 › Loads WordPress dashboard (869ms)
  ✓  2 [chromium] › specs/main.spec.js:11:5 › Inserts Book Author block (2.6s)

  2 passed (4.8s)

测试书评卡的模式

你也可以用类似的方式测试其他方块变体,但让我们看看如何测试主题中标记的其中一种图案——书评卡

该模式利用所有注册的方块变体,并将它们排列成特定的布局,结合了列、组等其他方块。

书评卡纸样来源

<?php
/**
 * Title: Book Review Card
 * Slug: themeslug/book-review-card
 * Categories: themeslug-book-review
 * Viewport Width: 1376
 */
?>
<!-- wp:columns {"verticalAlignment":"center","align":"wide","style":{"spacing":{"padding":{"top":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30","right":"var:preset|spacing|30"},"blockGap":{"top":"var:preset|spacing|40","left":"var:preset|spacing|30"}}},"backgroundColor":"accent"} -->
<div class="wp-block-columns alignwide are-vertically-aligned-center has-accent-background-color has-background" style="padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)">

	<!-- wp:column {"verticalAlignment":"center","width":"33.33%"} -->
	<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:33.33%">
		<!-- wp:post-featured-image {"aspectRatio":"3/4","style":{"border":{"radius":"0px"}}} /-->
	</div>
	<!-- /wp:column -->

	<!-- wp:column {"verticalAlignment":"center","width":"66.66%","style":{"spacing":{"blockGap":"var:preset|spacing|40"}}} -->
	<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:66.66%">
		<!-- wp:group {"style":{"spacing":{"blockGap":"var:preset|spacing|10"}},"layout":{"type":"flex","orientation":"vertical"}} -->
		<div class="wp-block-group">
			<!-- wp:group {"style":{"spacing":{"blockGap":"0.25em"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
			<div class="wp-block-group">
				<!-- wp:paragraph -->
				<p></p>
				<!-- /wp:paragraph -->

				<!-- wp:paragraph {"placeholder":"<?php esc_attr_e( 'Book Rating', 'themeslug' ); ?>","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_rating"}}}}} -->
				<p></p>
				<!-- /wp:paragraph -->

				<!-- wp:paragraph -->
				<p><?php esc_html_e( '/ 5 Stars', 'themeslug' ); ?></p>
				<!-- /wp:paragraph -->
			</div>
			<!-- /wp:group -->

			<!-- wp:group {"style":{"spacing":{"blockGap":"0.25em"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
			<div class="wp-block-group">
				<!-- wp:paragraph -->
				<p><strong></strong></p>
				<!-- /wp:paragraph -->

				<!-- wp:paragraph {"placeholder":"<?php esc_attr_e( 'Book Length', 'themeslug' ); ?>","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_length"}}}}} -->
				<p></p>
				<!-- /wp:paragraph -->

				<!-- wp:paragraph -->
				<p><?php esc_html_e( 'Pages', 'themeslug' ); ?></p>
				<!-- /wp:paragraph -->
			</div>
			<!-- /wp:group -->

			<!-- wp:group {"style":{"spacing":{"blockGap":"0.25em"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
			<div class="wp-block-group">
				<!-- wp:paragraph -->
				<p><?php esc_html_e( ' Written by', 'themeslug' ); ?></p>
				<!-- /wp:paragraph -->

				<!-- wp:paragraph {"placeholder":"<?php esc_attr_e( 'Book Author', 'themeslug' ); ?>","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_author"}}}}} -->
				<p></p>
				<!-- /wp:paragraph -->
			</div>
			<!-- /wp:group -->

			<!-- wp:buttons -->
			<div class="wp-block-buttons">
				<!-- wp:button {"metadata":{"bindings":{"url":{"source":"core/post-meta","args":{"key":"themeslug_book_goodreads_url"}}}}} -->
				<div class="wp-block-button"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'View on Goodreads →', 'themeslug' ); ?></a></div>
				<!-- /wp:button -->
			</div>
			<!-- /wp:buttons -->
		</div>
		<!-- /wp:group -->

		<!-- wp:pullquote {"textAlign":"left","style":{"typography":{"fontSize":"1.2rem"},"spacing":{"padding":{"top":"0","bottom":"0"}}},"className":"is-style-plain"} -->
		<figure class="wp-block-pullquote has-text-align-left is-style-plain" style="padding-top:0;padding-bottom:0;font-size:1.2rem"><blockquote><p></p></blockquote></figure>
		<!-- /wp:pullquote -->
	</div>
	<!-- /wp:column -->

</div>
<!-- /wp:columns -->

完全可以像测试书籍作者模块那样测试这种模式。

然而,鉴于它包含的块数量,测试代码会非常重复且冗长。幸运的是,在这些情况下还有一种替代方法,马上你就会看到。

插入图样

为了检查图案是否已注册且可插入,你可以应用之前的相同原理。

创建一个名为“插入书评模式”的新测试,代码如下:main.spec.js

test( 'Inserts Book Review Card pattern', async ( { admin, page, editor } ) => {
	await admin.createNewPost();

	await page
		.getByRole( 'button', {
			name: 'Block Inserter',
		} )
		.click();

	await page
		.getByRole( 'tab', {
			name: 'Patterns',
		} )
		.click();

	await page.getByRole( 'tab', { name: 'Book Reviews' } ).click();

	await page
		.getByRole( 'listbox', { name: 'Book Reviews' } )
		.getByRole( 'option', { name: 'Book Review Card' } )
		.click();

	// Assertions
} );

和块测试一样,这会创建一个新帖子重新开始,然后在页面上找到元素,点击图案插入。

验证模式

由于图案由多个方块组成,单独检查会很繁琐。为了节省时间,建议使用快照测试一次性验证整个模式,而不是逐块检查。

快照是对状态、元素或某一特定时刻捕获的数据的表示,保存后用于比较。

你只需选择图案最外层的元素,即包裹其余部分的元素,并使用断言:toMatchAriaSnapshot

const bookReviewCardPattern = editor.canvas.getByRole( 'document', {
	name: 'Block: Columns',
} );

await expect( bookReviewCardPattern ).toMatchAriaSnapshot();

如果你在这里运行,会报错,因为必须先生成快照:test-playwright

Running 3 tests using 1 worker

  ✓  1 [chromium] › specs/main.spec.js:3:5 › Loads WordPress dashboard (896ms)
  ✓  2 [chromium] › specs/main.spec.js:11:5 › Inserts Book Author block (2.6s)
  ✘  3 [chromium] › specs/main.spec.js:56:5 › Inserts Book Review Card pattern (8.0s)


  1) [chromium] › specs/main.spec.js:56:5 › Inserts Book Review Card pattern ───────────────────────

    Error: A snapshot doesn't exist at specs/__snapshots__/Inserts-Book-Review-Card-pattern-1-chromium.aria.yml, writing actual.

      80 |      } );
      81 |
    > 82 |      await expect( bookReviewCardPattern ).toMatchAriaSnapshot();
         |      ^
      83 | } );
      84 |

  1 failed
    [chromium] › specs/main.spec.js:56:5 › Inserts Book Review Card pattern ────────────────────────
  2 passed (13.0s)

如果你正在使用UI模式,应该会在错误面板下看到:

确认图案正确后,你可以用带标志的常规命令保存快照:--update-snapshots

npx wp-scripts test-playwright --update-snapshots

运行后,应会出现一个包含YAML格式的无障碍树表示的新文件:/specs/__snapshots__/Inserts-Book-Review-Card-pattern-1-chromium.yml

- 'document "Block: Columns"':
  - 'document "Block: Column (1 of 2)"':
    - 'document "Block: Featured Image"':
      - button "Add a featured image"
  - 'document "Block: Column (2 of 2)"':
    - 'document "Block: Stack"':
      - 'document "Block: Row"':
        - 'document "Block: Paragraph"'
        - document "Empty themeslug_book_rating; start writing to edit its value"
        - 'document "Block: Paragraph"'
      - 'document "Block: Row"':
        - 'document "Block: Paragraph"':
          - strong: 
        - document "Empty themeslug_book_length; start writing to edit its value"
        - 'document "Block: Paragraph"'
      - 'document "Block: Row"':
        - 'document "Block: Paragraph"'
        - document "Empty themeslug_book_author; start writing to edit its value"
      - 'document "Block: Buttons"':
        - 'document "Block: Book Goodreads Button"':
          - textbox "Button text"
    - 'document "Block: Pullquote"':
      - blockquote:
        - textbox "Pullquote text"

现在,运行没有标志的测试命令,将模式输出与快照进行比较:

npx wp-scripts test-playwright

只有在你有意更新快照时才使用!--update-snapshots

供参考,这里有完整的“插入书评模式”测试:

test( 'Inserts Book Review Card pattern', async ( { admin, page, editor } ) => {
	await admin.createNewPost();

	await page
		.getByRole( 'button', {
			name: 'Block Inserter',
		} )
		.click();

	await page
		.getByRole( 'tab', {
			name: 'Patterns',
		} )
		.click();

	await page.getByRole( 'tab', { name: 'Book Reviews' } ).click();

	await page
		.getByRole( 'listbox', { name: 'Book Reviews' } )
		.getByRole( 'option', { name: 'Book Review Card' } )
		.click();

	const bookReviewCardPattern = editor.canvas.getByRole( 'document', {
		name: 'Block: Columns',
	} );

	await expect( bookReviewCardPattern ).toMatchAriaSnapshot();
} );

此时你有三场考试,全部应当通过。

npx wp-scripts test-playwright
Running 3 tests using 1 worker

  ✓  1 [chromium] › specs/main.spec.js:3:5 › Loads WordPress dashboard (848ms)
  ✓  2 [chromium] › specs/main.spec.js:11:5 › Inserts Book Author block (2.8s)
  ✓  3 [chromium] › specs/main.spec.js:56:5 › Inserts Book Review Card pattern (3.1s)

  3 passed (7.7s)

测试前端和元值

要测试积木在前端是否显示了后元值,你需要同时插入积木或图案并设置元值。

通过编辑器一步步操作会导致大量代码。如果帖子的创建方式和设置对测试本身不重要,那么值得考虑一个捷径。

通过REST API创建帖子

一个好的快捷方式是使用 REST API 创建帖子,添加图案所需的元数据(作者、评分、长度和 Goodreads URL)。这种方法完全绕过了编辑器界面。

要创建新帖子,有一个专门的助手叫做requestUtils.createPost()但你也可以直接与帖子或任何端点互动。

再次,在 中创建一个新测试,并添加以下设置部分:main.specs.js

test( 'Displays book review meta on the frontend', async ( {
	page,
	requestUtils,
} ) => {
	const newPost = await requestUtils.createPost( {
		status: 'publish',
		title: 'Emma',
		content: '<!-- wp:pattern {"slug":"themeslug/book-review-card"} /-->',
		meta: {
			themeslug_book_author: 'Jane Austen',
			themeslug_book_rating: '5',
			themeslug_book_length: '477',
			themeslug_book_goodreads_url:
				'https://www.goodreads.com/book/show/6969.Emma',
		},
	} );

	await page.goto( `?p=${ newPost.id }` );

	// Assertions
} );

验证前端输出

你可能已经猜到断言部分会发生什么。关键在于选择元素并验证其状态。

以下是断言部分的代码:

await expect( page.getByText( '5 / 5 Stars' ) ).toBeVisible();
await expect( page.getByText( '477 Pages' ) ).toBeVisible();
await expect( page.getByText( 'Written by Jane Austen' ) ).toBeVisible();
await expect(
	page.getByRole( 'link', { name: 'View on Goodreads' } )
).toHaveAttribute(
	'href',
	'https://www.goodreads.com/book/show/6969.Emma'
);

这是不可能使用诸如getByRole之类的可访问性定位器的情况之一,例如,“5/5星”部分的HTML看起来像这样:

<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-10 wp-block-group-is-layout-flex">
	<p></p>
	<p>5</p>
	<p>/ 5 Stars</p>
</div>

包含是一个非语义元素,文本本身分布在多个标记中。
综上所述,以下是完整的“在前端显示书评元”测试:

test( 'Displays book review meta on the frontend', async ( {
	page,
	requestUtils,
} ) => {
	const newPost = await requestUtils.createPost( {
		status: 'publish',
		title: 'Emma',
		content: '<!-- wp:pattern {"slug":"themeslug/book-review-card"} /-->',
		meta: {
			themeslug_book_author: 'Jane Austen',
			themeslug_book_rating: '5',
			themeslug_book_length: '477',
			themeslug_book_goodreads_url:
				'https://www.goodreads.com/book/show/6969.Emma',
		},
	} );

	await page.goto( `?p=${ newPost.id }` );

	await expect( page.getByText( '5 / 5 Stars' ) ).toBeVisible();
	await expect( page.getByText( '477 Pages' ) ).toBeVisible();
	await expect( page.getByText( 'Written by Jane Austen' ) ).toBeVisible();
	await expect(
		page.getByRole( 'link', { name: 'View on Goodreads' } )
	).toHaveAttribute(
		'href',
		'https://www.goodreads.com/book/show/6969.Emma'
	);
} );

如果你最后一次运行测试,你应该看到所有测试都通过了。

npx wp-scripts test-playwright
Running 4 tests using 1 worker

  ✓  1 [chromium] › specs/main.spec.js:3:5 › Loads WordPress dashboard (848ms)
  ✓  2 [chromium] › specs/main.spec.js:11:5 › Inserts Book Author block (2.8s)
  ✓  3 [chromium] › specs/main.spec.js:56:5 › Inserts Book Review Card pattern (3.1s)
  ✓  4 [chromium] › specs/main.spec.js:85:5 › Displays book review meta on the frontend (413ms)

  4 passed (8.4s)

延伸阅读

希望这篇文章能说明,一旦基础设置完成,写基础的端对端测试比看起来更容易上手,几个核心概念也能带你走出乎意料的远。

话虽如此,还有很多值得探讨的内容,而本文仅仅触及了表面。

作家的资料全面,但对各个层面都易于理解。一个不错的起点是写作测试页面,那里涵盖了这里讨论的更多行动和断言。

接下来,值得学习如何在多个文件和多个组间组织测试,以及如何通过测试钩子简化操作,测试钩子在每次测试前后执行。Playwright最佳实践指南和WordPress Core自己的推荐都值得一读,随着你的测试套件不断壮大。

以现实世界为例,古腾堡为核心模块提供的端对端测试是极大的灵感来源。WordPress的E2EPlaywright测试工具也值得浏览,以了解所有可用的助手。

一旦测试数量增加,夹具提供了一种干净利落的方式,从单个测试中提取可重复使用的逻辑。如果你想对每个提交或 PR 运行测试,还有专门的配置指南。

最后,如果你想知道未来趋势,关注测试代理,或者你更喜欢低代码方案,Playwright也可以为你生成测试