Skip to content

Commit 970fb24

Browse files
Merge pull request #3 from stevegrunwell/feature/assert-has-element-with-attributes
Add assertHasElementWithAttributes() assertion
2 parents e32510a + 1210547 commit 970fb24

File tree

3 files changed

+201
-8
lines changed

3 files changed

+201
-8
lines changed

README.md

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,23 @@ class MyUnitTest extends TestCase
8585

8686
## Available methods
8787

88+
These are the assertions made available to PHPUnit via the `MarkupAssertionsTrait`.
89+
90+
* [`assertContainsSelector()`](#assertcontainsselector)
91+
* [`assertNotContainsSelector()`](#assertnotcontainsselector)
92+
* [`assertHasElementWithAttributes()`](#asserthaselementwithattributes)
93+
* [`assertNotHasElementWithAttributes()`](#assertnothaselementwithattributes)
94+
8895
### assertContainsSelector()
8996

9097
Assert that the given string contains an element matching the given selector.
9198

9299
<dl>
93-
<dt>$selector</dt>
100+
<dt>(string) $selector</dt>
94101
<dd>A query selector for the element to find.</dd>
95-
<dt>$output</dt>
96-
<dd>The output that should contain the $selector.</dd>
97-
<dt>$message</dt>
102+
<dt>(string) $output</dt>
103+
<dd>The output that should contain the <code>$selector</code>.</dd>
104+
<dt>(string) $message</dt>
98105
<dd>A message to display if the assertion fails.</dd>
99106
</dl>
100107

@@ -116,10 +123,55 @@ Assert that the given string does not contain an element matching the given sele
116123
This method is the inverse of [`assertContainsSelector()`](#assertcontainsselector).
117124

118125
<dl>
119-
<dt>$selector</dt>
126+
<dt>(string) $selector</dt>
120127
<dd>A query selector for the element to find.</dd>
121-
<dt>$output</dt>
122-
<dd>The output that should not contain the $selector.</dd>
123-
<dt>$message</dt>
128+
<dt>(string) $output</dt>
129+
<dd>The output that should not contain the <code>$selector</code>.</dd>
130+
<dt>(string) $message</dt>
131+
<dd>A message to display if the assertion fails.</dd>
132+
</dl>
133+
134+
### assertHasElementWithAttributes()
135+
136+
Assert that an element with the given attributes exists in the given markup.
137+
138+
<dl>
139+
<dt>(array) $attributes</dt>
140+
<dd>An array of HTML attributes that should be found on the element.</dd>
141+
<dt>(string) $output</dt>
142+
<dd>The output that should contain an element with the provided <code>$attributes</code>.</dd>
143+
<dt>(string) $message</dt>
144+
<dd>A message to display if the assertion fails.</dd>
145+
</dl>
146+
147+
#### Example
148+
149+
```php
150+
public function testExpectedInputsArePresent()
151+
{
152+
$user = getUser();
153+
$form = getFormMarkup();
154+
155+
$this->assertHasElementWithAttributes(
156+
[
157+
'name' => 'first-name',
158+
'value' => $user->first_name,
159+
],
160+
$form,
161+
'Did not find the expected input for the user first name.'
162+
);
163+
}
164+
```
165+
166+
### assertNotHasElementWithAttributes()
167+
168+
Assert that an element with the given attributes does not exist in the given markup.
169+
170+
<dl>
171+
<dt>(array) $attributes</dt>
172+
<dd>An array of HTML attributes that should not be found on the element.</dd>
173+
<dt>(string) $output</dt>
174+
<dd>The output that should not contain an element with the provided <code>$attributes</code>.</dd>
175+
<dt>(string) $message</dt>
124176
<dd>A message to display if the assertion fails.</dd>
125177
</dl>

src/MarkupAssertionsTrait.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,40 @@ public function assertNotContainsSelector($selector, $output = '', $message = ''
4242
$this->assertEquals(0, count($results), $message);
4343
}
4444

45+
/**
46+
* Assert that an element with the given attributes exists in the given markup.
47+
*
48+
* @param array $attributes An array of HTML attributes that should be found on the element.
49+
* @param string $output The output that should contain an element with the
50+
* provided $attributes.
51+
* @param string $message A message to display if the assertion fails.
52+
*/
53+
public function assertHasElementWithAttributes($attributes = [], $output = '', $message = '')
54+
{
55+
$this->assertContainsSelector(
56+
'*' . $this->flattenAttributeArray($attributes),
57+
$output,
58+
$message
59+
);
60+
}
61+
62+
/**
63+
* Assert that an element with the given attributes does not exist in the given markup.
64+
*
65+
* @param array $attributes An array of HTML attributes that should be found on the element.
66+
* @param string $output The output that should not contain an element with the
67+
* provided $attributes.
68+
* @param string $message A message to display if the assertion fails.
69+
*/
70+
public function assertNotHasElementWithAttributes($attributes = [], $output = '', $message = '')
71+
{
72+
$this->assertNotContainsSelector(
73+
'*' . $this->flattenAttributeArray($attributes),
74+
$output,
75+
$message
76+
);
77+
}
78+
4579
/**
4680
* Build a new DOMDocument from the given markup, then execute a query against it.
4781
*
@@ -56,4 +90,31 @@ protected function executeDomQuery($markup, $query)
5690

5791
return $dom->execute($query);
5892
}
93+
94+
/**
95+
* Given an array of HTML attributes, flatten them into a XPath attribute selector.
96+
*
97+
* @throws RiskyTestError When the $attributes array is empty.
98+
*
99+
* @param array $attributes HTML attributes and their values.
100+
*
101+
* @return string A XPath attribute query selector.
102+
*/
103+
protected function flattenAttributeArray(array $attributes)
104+
{
105+
if (empty($attributes)) {
106+
throw new RiskyTestError('Attributes array is empty.');
107+
}
108+
109+
array_walk($attributes, function (&$value, $key) {
110+
// Boolean attributes.
111+
if (null === $value) {
112+
$value = sprintf('[%s]', $key);
113+
} else {
114+
$value = sprintf('[%s="%s"]', $key, htmlspecialchars($value));
115+
}
116+
});
117+
118+
return implode('', $attributes);
119+
}
59120
}

tests/MarkupAssertionsTraitTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Tests;
44

5+
use PHPUnit\Framework\RiskyTestError;
56
use PHPUnit\Framework\TestCase;
7+
use ReflectionMethod;
68
use Tests\TestFiles\TestCaseWithMarkupAssertions;
79

810
class MarkupAssertionsTraitTest extends TestCase
@@ -49,6 +51,84 @@ public function testAssertNotContainsSelector($selector)
4951
);
5052
}
5153

54+
public function testAssertHasElementWithAttributes()
55+
{
56+
$this->testcase->assertHasElementWithAttributes(
57+
[
58+
'type' => 'email',
59+
'value' => 'test@example.com',
60+
],
61+
'<label>Email</label><br><input type="email" value="test@example.com" />'
62+
);
63+
}
64+
65+
public function testAssertNotHasElementWithAttributes()
66+
{
67+
$this->testcase->assertNotHasElementWithAttributes(
68+
[
69+
'type' => 'email',
70+
'value' => 'test@example.com',
71+
],
72+
'<label>City</label><br><input type="text" value="New York" />'
73+
);
74+
}
75+
76+
/**
77+
* @dataProvider attributeProvider()
78+
*/
79+
public function testFlattenAttributeArray($attributes, $expected)
80+
{
81+
$method = new ReflectionMethod($this->testcase, 'flattenAttributeArray');
82+
$method->setAccessible(true);
83+
84+
$this->assertEquals($expected, $method->invoke($this->testcase, $attributes));
85+
}
86+
87+
/**
88+
* @expectedException PHPUnit\Framework\RiskyTestError
89+
*/
90+
public function testFlattenAttributeArrayThrowsRiskyTestError()
91+
{
92+
$method = new ReflectionMethod($this->testcase, 'flattenAttributeArray');
93+
$method->setAccessible(true);
94+
95+
$method->invoke($this->testcase, []);
96+
}
97+
98+
/**
99+
* Data provider for testFlattenAttributeArray().
100+
*/
101+
public function attributeProvider()
102+
{
103+
return [
104+
'Single attribute' => [
105+
[
106+
'id' => 'first-name',
107+
],
108+
'[id="first-name"]',
109+
],
110+
'Multiple attributes' => [
111+
[
112+
'id' => 'first-name',
113+
'value' => 'Ringo',
114+
],
115+
'[id="first-name"][value="Ringo"]',
116+
],
117+
'Boolean attribute' => [
118+
[
119+
'checked' => null,
120+
],
121+
'[checked]',
122+
],
123+
'Value contains quotes' => [
124+
[
125+
'name' => 'Austin "Danger" Powers',
126+
],
127+
'[name="Austin &quot;Danger&quot; Powers"]',
128+
],
129+
];
130+
}
131+
52132
/**
53133
* Data provider for testAssertContainsSelector().
54134
*/

0 commit comments

Comments
 (0)