• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stevegrunwell / phpunit-markup-assertions / 16208120791

10 Jul 2025 11:25PM UTC coverage: 98.592%. First build
16208120791

Pull #55

github

web-flow
Merge f466be06b into 525823c23
Pull Request #55: Version 2.0.0

12 of 13 new or added lines in 1 file covered. (92.31%)

70 of 71 relevant lines covered (98.59%)

9.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.59
/src/MarkupAssertionsTrait.php
1
<?php
2

3
/**
4
 * Markup assertions for PHPUnit.
5
 *
6
 * @package PHPUnit_Markup_Assertions
7
 * @author  Steve Grunwell
8
 */
9

10
namespace SteveGrunwell\PHPUnit_Markup_Assertions;
11

12
use PHPUnit\Framework\RiskyTestError;
13
use Symfony\Component\DomCrawler\Crawler;
14

15
trait MarkupAssertionsTrait
16
{
17
    /**
18
     * Assert that the given string contains an element matching the given selector.
19
     *
20
     * @since 1.0.0
21
     *
22
     * @param string $selector A query selector for the element to find.
23
     * @param string $markup   The output that should contain the $selector.
24
     * @param string $message  A message to display if the assertion fails.
25
     *
26
     * @return void
27
     */
28
    public function assertContainsSelector($selector, $markup = '', $message = '')
10✔
29
    {
30
        $results = $this->executeDomQuery($markup, $selector);
10✔
31

32
        $this->assertGreaterThan(0, count($results), $message);
10✔
33
    }
34

35
    /**
36
     * Assert that the given string does not contain an element matching the given selector.
37
     *
38
     * @since 1.0.0
39
     *
40
     * @param string $selector A query selector for the element to find.
41
     * @param string $markup   The output that should not contain the $selector.
42
     * @param string $message  A message to display if the assertion fails.
43
     *
44
     * @return void
45
     */
46
    public function assertNotContainsSelector($selector, $markup = '', $message = '')
8✔
47
    {
48
        $results = $this->executeDomQuery($markup, $selector);
8✔
49

50
        $this->assertEquals(0, count($results), $message);
8✔
51
    }
52

53
    /**
54
     * Assert the number of times an element matching the given selector is found.
55
     *
56
     * @since 1.0.0
57
     *
58
     * @param int    $count    The number of matching elements expected.
59
     * @param string $selector A query selector for the element to find.
60
     * @param string $markup   The markup to run the assertion against.
61
     * @param string $message  A message to display if the assertion fails.
62
     *
63
     * @return void
64
     */
65
    public function assertSelectorCount($count, $selector, $markup = '', $message = '')
1✔
66
    {
67
        $results = $this->executeDomQuery($markup, $selector);
1✔
68

69
        $this->assertCount($count, $results, $message);
1✔
70
    }
71

72
    /**
73
     * Assert that an element with the given attributes exists in the given markup.
74
     *
75
     * @since 1.0.0
76
     *
77
     * @param array<string, scalar> $attributes An array of HTML attributes that should be found
78
     *                                          on the element.
79
     * @param string                $markup     The output that should contain an element with the
80
     *                                          provided $attributes.
81
     * @param string                $message    A message to display if the assertion fails.
82
     *
83
     * @return void
84
     */
85
    public function assertHasElementWithAttributes($attributes = [], $markup = '', $message = '')
2✔
86
    {
87
        $this->assertContainsSelector(
2✔
88
            '*' . $this->flattenAttributeArray($attributes),
2✔
89
            $markup,
2✔
90
            $message
2✔
91
        );
2✔
92
    }
93

94
    /**
95
     * Assert that an element with the given attributes does not exist in the given markup.
96
     *
97
     * @since 1.0.0
98
     *
99
     * @param array<string, scalar> $attributes An array of HTML attributes that should be found
100
     *                                          on the element.
101
     * @param string                $markup     The output that should not contain an element with
102
     *                                          the provided $attributes.
103
     * @param string                $message    A message to display if the assertion fails.
104
     *
105
     * @return void
106
     */
107
    public function assertNotHasElementWithAttributes($attributes = [], $markup = '', $message = '')
1✔
108
    {
109
        $this->assertNotContainsSelector(
1✔
110
            '*' . $this->flattenAttributeArray($attributes),
1✔
111
            $markup,
1✔
112
            $message
1✔
113
        );
1✔
114
    }
115

116
    /**
117
     * Assert an element's contents contain the given string.
118
     *
119
     * @since 1.1.0
120
     *
121
     * @param string $contents The string to look for within the DOM node's contents.
122
     * @param string $selector A query selector for the element to find.
123
     * @param string $markup   The output that should contain the $selector.
124
     * @param string $message  A message to display if the assertion fails.
125
     *
126
     * @return void
127
     */
128
    public function assertElementContains($contents, $selector = '', $markup = '', $message = '')
11✔
129
    {
130
        $this->assertStringContainsString(
11✔
131
            $contents,
11✔
132
            $this->getInnerHtmlOfMatchedElements($markup, $selector),
11✔
133
            $message
11✔
134
        );
11✔
135
    }
136

137
    /**
138
     * Assert an element's contents do not contain the given string.
139
     *
140
     * @since 1.1.0
141
     *
142
     * @param string $contents The string to look for within the DOM node's contents.
143
     * @param string $selector A query selector for the element to find.
144
     * @param string $markup   The output that should not contain the $selector.
145
     * @param string $message  A message to display if the assertion fails.
146
     *
147
     * @return void
148
     */
149
    public function assertElementNotContains($contents, $selector = '', $markup = '', $message = '')
9✔
150
    {
151
        $this->assertStringNotContainsString(
9✔
152
            $contents,
9✔
153
            $this->getInnerHtmlOfMatchedElements($markup, $selector),
9✔
154
            $message
9✔
155
        );
9✔
156
    }
157

158
    /**
159
     * Assert an element's contents contain the given regular expression pattern.
160
     *
161
     * @since 1.1.0
162
     *
163
     * @param string $regexp   The regular expression pattern to look for within the DOM node.
164
     * @param string $selector A query selector for the element to find.
165
     * @param string $markup   The output that should contain the $selector.
166
     * @param string $message  A message to display if the assertion fails.
167
     *
168
     * @return void
169
     */
170
    public function assertElementRegExp($regexp, $selector = '', $markup = '', $message = '')
2✔
171
    {
172
        // @phpstan-ignore function.alreadyNarrowedType (Introduced in PHPUnit 9.x, PHP 7.3+)
173
        $method = method_exists($this, 'assertMatchesRegularExpression')
2✔
174
            ? 'assertMatchesRegularExpression'
2✔
175
            : 'assertRegExp'; // @codeCoverageIgnore
176

177
        $this->$method(
2✔
178
            $regexp,
2✔
179
            $this->getInnerHtmlOfMatchedElements($markup, $selector),
2✔
180
            $message
2✔
181
        );
2✔
182
    }
183

184
    /**
185
     * Assert an element's contents do not contain the given regular expression pattern.
186
     *
187
     * @since 1.1.0
188
     *
189
     * @param string $regexp   The regular expression pattern to look for within the DOM node.
190
     * @param string $selector A query selector for the element to find.
191
     * @param string $markup   The output that should not contain the $selector.
192
     * @param string $message  A message to display if the assertion fails.
193
     *
194
     * @return void
195
     */
196
    public function assertElementNotRegExp($regexp, $selector = '', $markup = '', $message = '')
1✔
197
    {
198
        // @phpstan-ignore function.alreadyNarrowedType (Introduced in PHPUnit 9.x, PHP 7.3+)
199
        $method = method_exists($this, 'assertDoesNotMatchRegularExpression')
1✔
200
            ? 'assertDoesNotMatchRegularExpression'
1✔
201
            : 'assertNotRegExp'; // @codeCoverageIgnore
202

203
        $this->$method(
1✔
204
            $regexp,
1✔
205
            $this->getInnerHtmlOfMatchedElements($markup, $selector),
1✔
206
            $message
1✔
207
        );
1✔
208
    }
209

210
    /**
211
     * Build a new DOMDocument from the given markup, then execute a query against it.
212
     *
213
     * @since 1.0.0
214
     *
215
     * @param string $markup The HTML for the DOMDocument.
216
     * @param string $query  The DOM selector query.
217
     *
218
     * @return Crawler
219
     */
220
    private function executeDomQuery($markup, $query)
45✔
221
    {
222
        $dom = new Crawler($markup);
45✔
223

224
        return $dom->filter($query);
45✔
225
    }
226

227
    /**
228
     * Given an array of HTML attributes, flatten them into a XPath attribute selector.
229
     *
230
     * @since 1.0.0
231
     *
232
     * @throws RiskyTestError When the $attributes array is empty.
233
     *
234
     * @param array<string, scalar> $attributes HTML attributes and their values.
235
     *
236
     * @return string A XPath attribute query selector.
237
     */
238
    private function flattenAttributeArray(array $attributes)
13✔
239
    {
240
        if (empty($attributes)) {
13✔
241
            throw new RiskyTestError('Attributes array is empty.');
5✔
242
        }
243

244
        array_walk($attributes, function (&$value, $key) {
8✔
245
            // Boolean attributes.
246
            if (empty($value)) {
8✔
247
                $value = sprintf('[%s]', $key);
1✔
248
            } else {
249
                $value = sprintf('[%s="%s"]', $key, htmlspecialchars((string) $value));
7✔
250
            }
251
        });
8✔
252

253
        return implode('', $attributes);
8✔
254
    }
255

256
    /**
257
     * Given HTML markup and a DOM selector query, collect the innerHTML of the matched selectors.
258
     *
259
     * @since 1.1.0
260
     *
261
     * @param string $markup The HTML for the DOMDocument.
262
     * @param string $query  The DOM selector query.
263
     *
264
     * @return string The concatenated innerHTML of any matched selectors.
265
     */
266
    private function getInnerHtmlOfMatchedElements($markup, $query)
26✔
267
    {
268
        $results  = $this->executeDomQuery($markup, $query);
26✔
269
        $contents = [];
26✔
270

271
        // Loop through results and collect their innerHTML values.
272
        foreach ($results as $result) {
26✔
273
            if (!isset($result->firstChild)) {
26✔
NEW
274
                continue;
×
275
            }
276

277
            $document = new \DOMDocument();
26✔
278
            $document->appendChild($document->importNode($result->firstChild, true));
26✔
279

280
            $contents[] = trim(html_entity_decode((string) $document->saveHTML()));
26✔
281
        }
282

283
        return implode(PHP_EOL, $contents);
26✔
284
    }
285
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc