Passed
Push — 1.x ( bfdb67...14afbb )
by Kevin
08:20
created

Browser::assertElementCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Zenstruck;
4
5
use Behat\Mink\Driver\DriverInterface;
6
use Behat\Mink\Element\DocumentElement;
7
use Behat\Mink\Exception\ElementNotFoundException;
8
use Behat\Mink\Exception\ExpectationException;
9
use Behat\Mink\Mink;
10
use Behat\Mink\Session;
11
use Behat\Mink\WebAssert;
12
use PHPUnit\Framework\Assert as PHPUnit;
0 ignored issues
show
Bug introduced by
The type PHPUnit\Framework\Assert was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Symfony\Component\Filesystem\Filesystem;
14
use Zenstruck\Browser\Component;
15
use Zenstruck\Browser\Response;
16
use Zenstruck\Browser\Test\Constraint\UrlMatches;
17
use Zenstruck\Browser\Util\FunctionExecutor;
18
19
/**
20
 * @author Kevin Bond <[email protected]>
21
 */
22
class Browser
23
{
24
    private const SESSION = 'app';
25
26
    private Mink $mink;
27
    private ?string $sourceDir = null;
28
    private array $savedSources = [];
29
30
    public function __construct(DriverInterface $driver)
31
    {
32
        $this->mink = new Mink([self::SESSION => new Session($driver)]);
33
    }
34
35
    /**
36
     * @return static
37
     */
38
    public static function create(callable $factory): self
39
    {
40
        $browser = $factory();
41
42
        if (!$browser instanceof self) {
43
            throw new \RuntimeException(\sprintf('The factory callable must return an instance of "%s".', self::class));
44
        }
45
46
        return $browser;
47
    }
48
49
    /**
50
     * @return static
51
     */
52
    final public function setSourceDir(string $dir): self
53
    {
54
        $this->sourceDir = $dir;
55
56
        return $this;
57
    }
58
59
    final public function minkSession(): Session
60
    {
61
        return $this->mink->getSession(self::SESSION);
62
    }
63
64
    final public function webAssert(): WebAssert
65
    {
66
        return $this->mink->assertSession(self::SESSION);
67
    }
68
69
    final public function documentElement(): DocumentElement
70
    {
71
        return $this->minkSession()->getPage();
72
    }
73
74
    /**
75
     * @return static
76
     */
77
    final public function visit(string $uri): self
78
    {
79
        $this->minkSession()->visit($uri);
80
81
        return $this;
82
    }
83
84
    /**
85
     * @param array $parts The url parts to check (@see parse_url)
86
     *
87
     * @return static
88
     */
89
    final public function assertOn(string $expected, array $parts = ['path', 'query', 'fragment']): self
90
    {
91
        PHPUnit::assertThat($expected, new UrlMatches($this->minkSession()->getCurrentUrl(), $parts));
92
93
        return $this;
94
    }
95
96
    /**
97
     * @param array $parts The url parts to check (@see parse_url)
98
     *
99
     * @return static
100
     */
101
    final public function assertNotOn(string $expected, array $parts = ['path', 'query', 'fragment']): self
102
    {
103
        PHPUnit::assertThat(
104
            $expected,
105
            PHPUnit::logicalNot(new UrlMatches($this->minkSession()->getCurrentUrl(), $parts))
106
        );
107
108
        return $this;
109
    }
110
111
    /**
112
     * @return static
113
     */
114
    final public function assertContains(string $expected): self
115
    {
116
        return $this->wrapMinkExpectation(
117
            fn() => $this->webAssert()->responseContains($expected)
118
        );
119
    }
120
121
    /**
122
     * @return static
123
     */
124
    final public function assertNotContains(string $expected): self
125
    {
126
        return $this->wrapMinkExpectation(
127
            fn() => $this->webAssert()->responseNotContains($expected)
128
        );
129
    }
130
131
    /**
132
     * @return static
133
     */
134
    final public function use(callable $callback): self
135
    {
136
        FunctionExecutor::createFor($callback)
137
            ->replaceUntypedArgument($this)
138
            ->replaceTypedArgument(self::class, $this)
139
            ->replaceTypedArgument(Component::class, fn(string $class) => new $class($this))
140
            ->execute()
141
        ;
142
143
        return $this;
144
    }
145
146
    /**
147
     * @return static
148
     */
149
    final public function saveSource(string $filename): self
150
    {
151
        if ($this->sourceDir) {
152
            $filename = \sprintf('%s/%s', \rtrim($this->sourceDir, '/'), \ltrim($filename, '/'));
153
        }
154
155
        (new Filesystem())->dumpFile($this->savedSources[] = $filename, $this->response()->raw());
156
157
        return $this;
158
    }
159
160
    /**
161
     * @return static
162
     */
163
    final public function dump(?string $selector = null): self
164
    {
165
        $this->response()->dump($selector);
166
167
        return $this;
168
    }
169
170
    final public function dd(?string $selector = null): void
171
    {
172
        $this->dump($selector);
173
        $this->die();
174
    }
175
176
    /**
177
     * @return static
178
     */
179
    public function follow(string $link): self
180
    {
181
        $this->documentElement()->clickLink($link);
182
183
        return $this;
184
    }
185
186
    /**
187
     * @return static
188
     */
189
    final public function fillField(string $selector, string $value): self
190
    {
191
        $this->documentElement()->fillField($selector, $value);
192
193
        return $this;
194
    }
195
196
    /**
197
     * @return static
198
     */
199
    public function checkField(string $selector): self
200
    {
201
        $this->documentElement()->checkField($selector);
202
203
        return $this;
204
    }
205
206
    /**
207
     * @return static
208
     */
209
    public function uncheckField(string $selector): self
210
    {
211
        $this->documentElement()->uncheckField($selector);
212
213
        return $this;
214
    }
215
216
    /**
217
     * @return static
218
     */
219
    public function selectFieldOption(string $selector, string $value): self
220
    {
221
        $this->documentElement()->selectFieldOption($selector, $value);
222
223
        return $this;
224
    }
225
226
    /**
227
     * @return static
228
     */
229
    public function selectFieldOptions(string $selector, array $values): self
230
    {
231
        foreach ($values as $value) {
232
            $this->documentElement()->selectFieldOption($selector, $value, true);
233
        }
234
235
        return $this;
236
    }
237
238
    /**
239
     * @return static
240
     */
241
    final public function attachFile(string $selector, string $path): self
242
    {
243
        $this->documentElement()->attachFileToField($selector, $path);
244
245
        return $this;
246
    }
247
248
    /**
249
     * @return static
250
     */
251
    final public function click(string $selector): self
252
    {
253
        try {
254
            $this->documentElement()->pressButton($selector);
255
        } catch (ElementNotFoundException $e) {
256
            // try link
257
            $this->documentElement()->clickLink($selector);
258
        }
259
260
        return $this;
261
    }
262
263
    /**
264
     * @return static
265
     */
266
    final public function assertSee(string $expected): self
267
    {
268
        return $this->wrapMinkExpectation(
269
            fn() => $this->webAssert()->pageTextContains($expected)
270
        );
271
    }
272
273
    /**
274
     * @return static
275
     */
276
    final public function assertNotSee(string $expected): self
277
    {
278
        return $this->wrapMinkExpectation(
279
            fn() => $this->webAssert()->pageTextNotContains($expected)
280
        );
281
    }
282
283
    /**
284
     * @return static
285
     */
286
    final public function assertSeeIn(string $selector, string $expected): self
287
    {
288
        return $this->wrapMinkExpectation(
289
            fn() => $this->webAssert()->elementTextContains('css', $selector, $expected)
290
        );
291
    }
292
293
    /**
294
     * @return static
295
     */
296
    final public function assertNotSeeIn(string $selector, string $expected): self
297
    {
298
        return $this->wrapMinkExpectation(
299
            fn() => $this->webAssert()->elementTextNotContains('css', $selector, $expected)
300
        );
301
    }
302
303
    /**
304
     * @return static
305
     */
306
    final public function assertSeeElement(string $selector): self
307
    {
308
        return $this->wrapMinkExpectation(
309
            fn() => $this->webAssert()->elementExists('css', $selector)
310
        );
311
    }
312
313
    /**
314
     * @return static
315
     */
316
    final public function assertNotSeeElement(string $selector): self
317
    {
318
        return $this->wrapMinkExpectation(
319
            fn() => $this->webAssert()->elementNotExists('css', $selector)
320
        );
321
    }
322
323
    /**
324
     * @return static
325
     */
326
    final public function assertElementCount(string $selector, int $count): self
327
    {
328
        return $this->wrapMinkExpectation(
329
            fn() => $this->webAssert()->elementsCount('css', $selector, $count)
330
        );
331
    }
332
333
    /**
334
     * @return static
335
     */
336
    final public function assertFieldEquals(string $selector, string $expected): self
337
    {
338
        return $this->wrapMinkExpectation(
339
            fn() => $this->webAssert()->fieldValueEquals($selector, $expected)
340
        );
341
    }
342
343
    /**
344
     * @return static
345
     */
346
    final public function assertFieldNotEquals(string $selector, string $expected): self
347
    {
348
        return $this->wrapMinkExpectation(
349
            fn() => $this->webAssert()->fieldValueNotEquals($selector, $expected)
350
        );
351
    }
352
353
    /**
354
     * @return static
355
     */
356
    final public function assertSelected(string $selector, string $expected): self
357
    {
358
        try {
359
            $field = $this->webAssert()->fieldExists($selector);
360
            PHPUnit::assertTrue(true);
361
        } catch (ExpectationException $e) {
362
            PHPUnit::fail($e->getMessage());
363
        }
364
365
        PHPUnit::assertContains($expected, (array) $field->getValue());
366
367
        return $this;
368
    }
369
370
    /**
371
     * @return static
372
     */
373
    final public function assertNotSelected(string $selector, string $expected): self
374
    {
375
        try {
376
            $field = $this->webAssert()->fieldExists($selector);
377
            PHPUnit::assertTrue(true);
378
        } catch (ExpectationException $e) {
379
            PHPUnit::fail($e->getMessage());
380
        }
381
382
        PHPUnit::assertNotContains($expected, (array) $field->getValue());
383
384
        return $this;
385
    }
386
387
    /**
388
     * @return static
389
     */
390
    final public function assertChecked(string $selector): self
391
    {
392
        return $this->wrapMinkExpectation(
393
            fn() => $this->webAssert()->checkboxChecked($selector)
394
        );
395
    }
396
397
    /**
398
     * @return static
399
     */
400
    final public function assertNotChecked(string $selector): self
401
    {
402
        return $this->wrapMinkExpectation(
403
            fn() => $this->webAssert()->checkboxNotChecked($selector)
404
        );
405
    }
406
407
    /**
408
     * @return static
409
     */
410
    final public function assertElementAttributeContains(string $selector, string $attribute, string $expected): self
411
    {
412
        return $this->wrapMinkExpectation(
413
            fn() => $this->webAssert()->elementAttributeContains('css', $selector, $attribute, $expected)
414
        );
415
    }
416
417
    /**
418
     * @return static
419
     */
420
    final public function assertElementAttributeNotContains(string $selector, string $attribute, string $expected): self
421
    {
422
        return $this->wrapMinkExpectation(
423
            fn() => $this->webAssert()->elementAttributeNotContains('css', $selector, $attribute, $expected)
424
        );
425
    }
426
427
    /**
428
     * @internal
429
     */
430
    public function dumpCurrentState(string $filename): void
431
    {
432
        $this->saveSource("{$filename}.txt");
433
    }
434
435
    /**
436
     * @internal
437
     */
438
    public function savedArtifacts(): array
439
    {
440
        return ['Saved Source Files' => $this->savedSources];
441
    }
442
443
    protected function response(): Response
444
    {
445
        return Response::createFor($this->minkSession());
446
    }
447
448
    /**
449
     * @return static
450
     */
451
    final protected function wrapMinkExpectation(callable $callback): self
452
    {
453
        try {
454
            $callback();
455
            PHPUnit::assertTrue(true);
456
        } catch (ExpectationException $e) {
457
            PHPUnit::fail($e->getMessage());
458
        }
459
460
        return $this;
461
    }
462
463
    protected function die(): void
464
    {
465
        exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
466
    }
467
}
468