aklump / dom-testing-selectors
这个库提供了一个PHP解决方案,用于在测试目的下为您的DOM添加特定的标记。
Requires
- php: >=7.3
- ext-dom: *
Requires (Dev)
- aklump/phpswap: ^0.0.8
- phpunit/phpunit: ^9.6
README
这个库提供了一个PHP解决方案,用于在测试目的下为您的DOM添加特定的标记。 默认情况下,它为DOM元素配置了一个 data-test
属性,如示例所示。库旨在集成到服务器端渲染管道中,该管道生成您的标记。
<html> <body> <main> <section data-test="hero"></section> <article data-test="article"></article> </main> </body> </html>
该属性应仅用于在测试中定位元素。通过这种方式分离关注点有助于防止由于依赖您无法控制且可能意外更改的类或其他多用途属性而出现的测试脆弱性。通过使用专用的数据属性进行测试——一个您控制的属性——您可以确保您的测试在一段时间内保持稳定和可靠。
describe('The main page', () => { it('should have hero and article sections.', () => { cy.visit('/') cy.get('[data-test="hero"]').its('length').should('equal', 1) cy.get('[data-test="article"]').its('length').should('equal', 1) }) })
使用Composer安装
-
需要此包
composer require aklump/dom-testing-selectors:^0.0
选择器
$test_selector = new \AKlump\DomTestingSelectors\Selector\DataTestSelector(); $username_selector = $test_selector('username'); // $username_selector === 'data-test="username"' $password_selector = $test_selector('password'); // $password_selector === 'data-test="password"'
您还可以向选择器添加一个组,该组将作为属性值的限定词
$test_selector->setGroup('login'); $username_selector = $test_selector('username'); // $username_selector === 'data-test="login__username"'
命名约定
请注意,为了减少混淆和错误,提供的 \AKlump\DomTestingSelectors\Selector\AbstractSelector
对命名约定有一定的看法。
$selector = new \AKlump\DomTestingSelectors\Selector\DataTestSelector(); $attribute = $selector('A.StrangeSelector string---NAME'); // $attribute === 'data-test="a_strange_selector_string_name"
要更改此行为,您应该创建一个自定义选择器类,并覆盖 \AKlump\DomTestingSelectors\Selector\AbstractSelector::applyNamingConventions
。
使用自定义属性
以下示例将说明如何将属性更改为 data-cy
,您可能希望在 使用Cypress进行测试 时使用它。简单地创建一个自定义选择器类,并用它替换 DataTestSelector
。
namespace Vendor\DomTestingSelectors\Selectors; final class CypressSelector extends AbstractSelector { public function getAttributeName(): string { return 'data-cy'; } }
有关选择器的更多信息,请参阅 \AKlump\DomTestingSelectors\Selectors\AbstractSelector
。
使用类作为测试选择器
包含的 \AKlump\DomTestingSelectors\Selector\ClassSelector
可以替换 \AKlump\DomTestingSelectors\Selector\DataTestSelector
,如果您想使用CSS类(例如 t-foo
)来选择元素。 请注意,对于类,命名约定已更改,使用连字符而不是下划线。
// When using the class attribute--e.g., "<div class="foo bar"/>"--you must // merge with any existing value. The current value has to be passed as the // second argument to __invoke() and __getAttributeValue. $selector = new \AKlump\DomTestingSelectors\Selector\ClassSelector(); $attribute_markup = $selector('my_target_element', 'foo bar'); // $attribute_markup === 'class="foo bar t-my-target-element"' $selector = new \AKlump\DomTestingSelectors\Selector\ClassSelector(); $attribute_value = $selector->setName('my_target_element') ->getAttributeValue('foo bar'); // $attribute_value === 'foo bar t-my-target-element'
处理器
处理器负责将选择器标记添加到您的HTML中。此库提供了 \AKlump\DomTestingSelectors\Handlers\StringHandler
,用于将选择器添加到HTML字符串中。
$element = '<div></div>'; $handler = new \AKlump\DomTestingSelectors\Handler\StringHandler(); $selector = new \AKlump\DomTestingSelectors\Selector\DataTestSelector(); if ($handler->canHandle($element)) { $handler->setTestingSelectorOnElement($element, $selector->setName('foobar')); } // $element === '<div data-test="foobar"></div>'
通过实现 \AKlump\DomTestingSelectors\Handler\HandlerInterface
,添加特定框架的处理器和自定义处理器非常简单。
class MyArrayHandler implements \AKlump\DomTestingSelectors\Handler\HandlerInterface { public function canHandle($element): bool { return is_array($element); } public function setTestingSelectorOnElement(&$element, \AKlump\DomTestingSelectors\Selector\ElementSelectorInterface $selector): void { $element['attributes'][$selector->getAttributeName()] = $selector->getAttributeValue(); } }
工厂
在实际应用中,您可能有多个处理器来覆盖您希望标记的全部元素范围。这就是 \AKlump\DomTestingSelectors\AbstractHandlerFactory
的原因。以下示例显示了如何编写和使用自定义工厂。
class MyFactory extends \AKlump\DomTestingSelectors\Factory\AbstractHandlerFactory { public function __construct() { $this->addHandler(new \AKlump\DomTestingSelectors\Handler\StringHandler()); $this->addHandler(new MyArrayHandler()); } } $factory = new MyFactory(); $selector = new \AKlump\DomTestingSelectors\Selector\DataTestSelector(); $selector->setName('foobar'); $element1 = '<div></div>'; $element2 = ['tag' => 'div']; try { $factory->getHandler($element1)->setTestingSelectorOnElement($element1, $selector); // $element1 === '<div data-test="foobar"></div>' $factory->getHandler($element2)->setTestingSelectorOnElement($element2, $selector); // $element2 === ['tag'=>'div','attributes'=>['data-test'=>'foobar']] } catch (\AKlump\DomTestingSelectors\Exception\NoHandlerFoundException $exception) { // No handler found }
使其成为一个安全工厂
请注意,您必须捕获对于给定元素无法找到处理器的情况。为了解决这个问题,您可能想要创建一个“安全工厂”,即一个在 作为其最终处理器 添加 \AKlump\DomTestingSelectors\Handler\PassThroughHandler
或类似项(可能带有您自己的日志记录)的工厂。而不是抛出异常,$element 将简单地保持不变。
class MySafeFactory extends \AKlump\DomTestingSelectors\Factory\AbstractHandlerFactory { public function __construct() { $this->addHandler(new \AKlump\DomTestingSelectors\Handler\StringHandler()); $this->addHandler(new \AKlump\DomTestingSelectors\Handler\PassThroughHandler()); } } $factory = new MySafeFactory(); $selector = new \AKlump\DomTestingSelectors\Selector\DataTestSelector(); $selector->setName('foobar'); $element1 = '<div></div>'; $element2 = 'lorem ipsum dolar'; $factory->getHandler($element1)->setTestingSelectorOnElement($element1, $selector); // $element1 === '<div data-test="foobar"></div>' $factory->getHandler($element2)->setTestingSelectorOnElement($element2, $selector); // $element2 === 'lorem ipsum dolar'