Passed
Push — 1.x ( db95a4...d555ce )
by Kevin
02:20 queued 14s
created

BrowserTests   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 621
Duplicated Lines 0 %

Importance

Changes 17
Bugs 1 Features 6
Metric Value
eloc 312
c 17
b 1
f 6
dl 0
loc 621
rs 9.68
wmc 34

34 Methods

Rating   Name   Duplication   Size   Complexity  
A can_use_response() 0 9 1
A invalid_use_callback_parameter_throws_type_error() 0 5 1
A html_assertions() 0 11 1
A with_can_accept_multiple_browsers_and_components() 0 12 1
A html_head_assertions() 0 8 1
A can_use_crawler() 0 6 1
A encodedUrlProvider() 0 10 1
A assert_on() 0 13 1
A assert_on_encoded() 0 5 1
A can_dump_response() 0 12 1
A redirects_are_followed_by_default() 0 5 1
A can_save_source() 0 12 1
A content_assertions() 0 6 1
A form_assertions() 0 39 1
A link_action() 0 9 1
A can_use_components() 0 7 1
A multiple_browsers() 0 14 1
A component_pre_assertions_and_actions_are_called() 0 6 1
A can_use_current_browser() 0 13 1
A can_submit_form_with_different_submit_buttons() 0 26 1
A cannot_attach_file_that_does_not_exist() 0 7 1
A select_field() 0 14 1
A can_attach_multiple_files() 0 7 1
A cannot_attach_multiple_files_to_a_non_multiple_input() 0 7 1
A can_access_the_html_crawler() 0 11 1
A catchFileContents() 0 9 1
A form_actions_by_field_id() 0 20 1
A can_dump_html_element() 0 11 1
A form_actions_by_field_name() 0 20 1
A form_actions_by_field_label() 0 20 1
A catchVarDumperOutput() 0 15 1
A click_on_element() 0 6 1
A can_submit_filled_form_with_different_submit_buttons() 0 34 1
A if_dump_selector_matches_multiple_elements_all_are_dumped() 0 12 1
1
<?php
2
3
namespace Zenstruck\Browser\Tests;
4
5
use Symfony\Component\DomCrawler\Crawler;
6
use Symfony\Component\Filesystem\Filesystem;
7
use Symfony\Component\VarDumper\VarDumper;
8
use Zenstruck\Browser;
9
use Zenstruck\Browser\Response;
10
use Zenstruck\Browser\Response\HtmlResponse;
11
use Zenstruck\Browser\Test\HasBrowser;
12
use Zenstruck\Browser\Tests\Fixture\TestComponent1;
13
use Zenstruck\Browser\Tests\Fixture\TestComponent2;
14
use Zenstruck\Callback\Exception\UnresolveableArgument;
15
16
/**
17
 * @author Kevin Bond <[email protected]>
18
 */
19
trait BrowserTests
20
{
21
    use HasBrowser {
22
        browser as kernelBrowser;
23
    }
24
25
    /**
26
     * @test
27
     */
28
    public function multiple_browsers(): void
29
    {
30
        $browser1 = $this->browser()
31
            ->visit('/page1')
32
            ->assertOn('/page1')
33
        ;
34
35
        $browser2 = $this->browser()
0 ignored issues
show
Unused Code introduced by
The assignment to $browser2 is dead and can be removed.
Loading history...
36
            ->visit('/page2')
37
            ->assertOn('/page2')
38
        ;
39
40
        // this ensures a different browser is actually used
41
        $browser1->assertOn('/page1');
42
    }
43
44
    /**
45
     * @test
46
     */
47
    public function assert_on(): void
48
    {
49
        $this->browser()
50
            ->visit('/page1')
51
            ->assertOn('/page1')
52
            ->assertOn('http://www.example.com/page1')
53
            ->assertNotOn('/page2')
54
            ->assertNotOn('http://www.example.com/page1', ['path', 'host'])
55
            ->visit('/page1?foo=bar')
56
            ->assertOn('/page1?foo=bar')
57
            ->assertOn('/page1', ['path'])
58
            ->assertOn('/page1', ['path', 'fragment'])
59
            ->assertNotOn('/page1?foo=baz')
60
        ;
61
    }
62
63
    /**
64
     * @test
65
     * @dataProvider encodedUrlProvider
66
     */
67
    public function assert_on_encoded($url, $expected): void
68
    {
69
        $this->browser()
70
            ->visit($url)
71
            ->assertOn($expected)
72
        ;
73
    }
74
75
    public static function encodedUrlProvider(): iterable
76
    {
77
        yield ['/page1?filter[q]=value', '/page1?filter[q]=value'];
78
        yield ['/page1?filter%5Bq%5D=value', '/page1?filter[q]=value'];
79
        yield ['/page1?filter[q]=value', '/page1?filter%5Bq%5D=value'];
80
        yield ['/page1#foo bar', '/page1#foo bar'];
81
        yield ['/page1#foo%20bar', '/page1#foo bar'];
82
        yield ['/page1#foo bar', '/page1#foo%20bar'];
83
        yield ['/page1#foo+bar', '/page1#foo bar'];
84
        yield ['/page1#foo bar', '/page1#foo+bar'];
85
    }
86
87
    /**
88
     * @test
89
     */
90
    public function can_use_current_browser(): void
91
    {
92
        $browser = $this->browser();
93
94
        $browser
95
            ->use(function(Browser $b) use ($browser) {
96
                $this->assertSame($b, $browser);
0 ignored issues
show
Bug introduced by
The method assertSame() does not exist on Zenstruck\Browser\Tests\BrowserTests. Did you maybe mean assert_on()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

96
                $this->/** @scrutinizer ignore-call */ 
97
                       assertSame($b, $browser);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
97
98
                $browser->visit('/redirect1');
99
            })
100
            ->assertOn('/page1')
101
            ->use(function() {
102
                $this->assertTrue(true);
0 ignored issues
show
Bug introduced by
The method assertTrue() does not exist on Zenstruck\Browser\Tests\BrowserTests. Did you maybe mean assert_on()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

102
                $this->/** @scrutinizer ignore-call */ 
103
                       assertTrue(true);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
103
            })
104
        ;
105
    }
106
107
    /**
108
     * @test
109
     */
110
    public function can_use_components(): void
111
    {
112
        $this->browser()
113
            ->use(function(TestComponent1 $component) {
114
                $component->assertTitle('h1 title');
115
            })
116
            ->assertOn('/page1')
117
        ;
118
    }
119
120
    /**
121
     * @test
122
     */
123
    public function component_pre_assertions_and_actions_are_called(): void
124
    {
125
        $this->browser()
126
            ->use(function(TestComponent2 $component) {
127
                $this->assertTrue($component->preActionsCalled);
128
                $this->assertTrue($component->preAssertionsCalled);
129
            })
130
        ;
131
    }
132
133
    /**
134
     * @test
135
     */
136
    public function can_use_response(): void
137
    {
138
        $this->browser()
139
            ->visit('/page1')
140
            ->use(function(Response $response) {
141
                $this->assertStringContainsString('<h1>h1 title</h1>', $response->body());
0 ignored issues
show
Bug introduced by
It seems like assertStringContainsString() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
                $this->/** @scrutinizer ignore-call */ 
142
                       assertStringContainsString('<h1>h1 title</h1>', $response->body());
Loading history...
142
            })
143
            ->use(function(HtmlResponse $response) {
144
                $this->assertCount(2, $response->crawler()->filter('ul li'));
0 ignored issues
show
Bug introduced by
The method assertCount() does not exist on Zenstruck\Browser\Tests\BrowserTests. Did you maybe mean assert_on()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

144
                $this->/** @scrutinizer ignore-call */ 
145
                       assertCount(2, $response->crawler()->filter('ul li'));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
145
            })
146
        ;
147
    }
148
149
    /**
150
     * @test
151
     */
152
    public function can_use_crawler(): void
153
    {
154
        $this->browser()
155
            ->visit('/page1')
156
            ->use(function(Crawler $crawler) {
157
                $this->assertSame('h1 title', $crawler->filter('h1')->text());
158
            })
159
        ;
160
    }
161
162
    /**
163
     * @test
164
     */
165
    public function with_can_accept_multiple_browsers_and_components(): void
166
    {
167
        $browser = $this->browser();
168
169
        $browser
170
            ->use(function(Browser $browser1, $browser2, TestComponent1 $component1, TestComponent2 $component2, Crawler $crawler) use ($browser) {
0 ignored issues
show
Unused Code introduced by
The parameter $crawler is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

170
            ->use(function(Browser $browser1, $browser2, TestComponent1 $component1, TestComponent2 $component2, /** @scrutinizer ignore-unused */ Crawler $crawler) use ($browser) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
171
                $this->assertInstanceOf(Browser::class, $browser1);
0 ignored issues
show
Bug introduced by
It seems like assertInstanceOf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

171
                $this->/** @scrutinizer ignore-call */ 
172
                       assertInstanceOf(Browser::class, $browser1);
Loading history...
172
                $this->assertInstanceOf(Browser::class, $browser2);
173
                $this->assertInstanceOf(\get_class($browser), $browser1);
174
                $this->assertInstanceOf(\get_class($browser), $browser2);
175
                $this->assertInstanceOf(TestComponent1::class, $component1);
176
                $this->assertInstanceOf(TestComponent2::class, $component2);
177
            })
178
        ;
179
    }
180
181
    /**
182
     * @test
183
     */
184
    public function invalid_use_callback_parameter_throws_type_error(): void
185
    {
186
        $this->expectException(UnresolveableArgument::class);
0 ignored issues
show
Bug introduced by
It seems like expectException() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

186
        $this->/** @scrutinizer ignore-call */ 
187
               expectException(UnresolveableArgument::class);
Loading history...
187
188
        $this->browser()->use(function(string $invalidType) {});
0 ignored issues
show
Unused Code introduced by
The parameter $invalidType is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

188
        $this->browser()->use(function(/** @scrutinizer ignore-unused */ string $invalidType) {});

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
189
    }
190
191
    /**
192
     * @test
193
     */
194
    public function redirects_are_followed_by_default(): void
195
    {
196
        $this->browser()
197
            ->visit('/redirect1')
198
            ->assertOn('/page1')
199
        ;
200
    }
201
202
    /**
203
     * @test
204
     */
205
    public function content_assertions(): void
206
    {
207
        $this->browser()
208
            ->visit('/page1')
209
            ->assertContains('h1 title')
210
            ->assertNotContains('invalid text')
211
        ;
212
    }
213
214
    /**
215
     * @test
216
     */
217
    public function can_dump_response(): void
218
    {
219
        $output = self::catchVarDumperOutput(function() {
220
            $this->browser()
221
                ->visit('/page1')
222
                ->dump()
223
            ;
224
        });
225
226
        $this->assertStringContainsString('/page1', $output[0]);
227
        $this->assertStringContainsString('<html', $output[0]);
228
        $this->assertStringContainsString('<h1>h1 title</h1>', $output[0]);
229
    }
230
231
    /**
232
     * @test
233
     */
234
    public function can_save_source(): void
235
    {
236
        $contents = self::catchFileContents(__DIR__.'/../var/browser/source/source.txt', function() {
237
            $this->browser()
238
                ->visit('/page1')
239
                ->saveSource('source.txt')
240
            ;
241
        });
242
243
        $this->assertStringContainsString('/page1', $contents);
244
        $this->assertStringContainsString('<html', $contents);
245
        $this->assertStringContainsString('<h1>h1 title</h1>', $contents);
246
    }
247
248
    /**
249
     * @test
250
     */
251
    public function html_assertions(): void
252
    {
253
        $this->browser()
254
            ->visit('/page1')
255
            ->assertSee('h1 title')
256
            ->assertNotSee('invalid text')
257
            ->assertSeeIn('h1', 'title')
258
            ->assertNotSeeIn('h1', 'invalid text')
259
            ->assertSeeElement('h1')
260
            ->assertNotSeeElement('h2')
261
            ->assertElementCount('ul li', 2)
262
        ;
263
    }
264
265
    /**
266
     * @test
267
     */
268
    public function html_head_assertions(): void
269
    {
270
        $this->browser()
271
            ->visit('/page1')
272
            ->assertSeeIn('title', 'meta title')
273
            ->assertElementAttributeContains('meta[name="description"]', 'content', 'meta')
274
            ->assertElementAttributeNotContains('meta[name="description"]', 'content', 'invalid')
275
            ->assertElementAttributeContains('html', 'lang', 'en')
276
        ;
277
    }
278
279
    /**
280
     * @test
281
     */
282
    public function form_assertions(): void
283
    {
284
        $this->browser()
285
            ->visit('/page1')
286
            ->assertFieldEquals('Input 1', 'input 1')
287
            ->assertFieldEquals('input1', 'input 1')
288
            ->assertFieldEquals('input_1', 'input 1')
289
            ->assertFieldNotEquals('Input 1', 'invalid')
290
            ->assertFieldNotEquals('input1', 'invalid')
291
            ->assertFieldNotEquals('input_1', 'invalid')
292
            ->assertChecked('Input 3')
293
            ->assertChecked('input3')
294
            ->assertChecked('input_3')
295
            ->assertNotChecked('Input 2')
296
            ->assertNotChecked('input2')
297
            ->assertNotChecked('input_2')
298
            ->assertSelected('Input 4', 'option 1')
299
            ->assertSelected('input4', 'option 1')
300
            ->assertSelected('input_4', 'option 1')
301
            ->assertSelected('Input 7', 'option 1')
302
            ->assertSelected('input7', 'option 1')
303
            ->assertSelected('input_7[]', 'option 1')
304
            ->assertSelected('Input 7', 'option 3')
305
            ->assertSelected('input7', 'option 3')
306
            ->assertSelected('input_7[]', 'option 3')
307
            ->assertNotSelected('Input 4', 'option 2')
308
            ->assertNotSelected('input4', 'option 2')
309
            ->assertNotSelected('input_4', 'option 2')
310
            ->assertNotSelected('Input 7', 'option 2')
311
            ->assertNotSelected('input7', 'option 2')
312
            ->assertNotSelected('input_7[]', 'option 2')
313
            ->assertNotSelected('input_8', 'option 1')
314
            ->assertSelected('input_8', 'option 2')
315
            ->assertNotChecked('Radio 1')
316
            ->assertNotChecked('radio1')
317
            ->assertNotChecked('Radio 3')
318
            ->assertNotChecked('radio3')
319
            ->assertChecked('Radio 2')
320
            ->assertChecked('radio2')
321
        ;
322
    }
323
324
    /**
325
     * @test
326
     */
327
    public function link_action(): void
328
    {
329
        $this->browser()
330
            ->visit('/page1')
331
            ->follow('a link')
332
            ->assertOn('/page2')
333
            ->visit('/page1')
334
            ->click('a link')
335
            ->assertOn('/page2')
336
        ;
337
    }
338
339
    /**
340
     * @test
341
     */
342
    public function click_on_element(): void
343
    {
344
        $this->browser()
345
            ->visit('/page1')
346
            ->click('#link a')
347
            ->assertOn('/page2')
348
        ;
349
    }
350
351
    /**
352
     * @test
353
     */
354
    public function form_actions_by_field_label(): void
355
    {
356
        $this->browser()
357
            ->visit('/page1')
358
            ->fillField('Input 1', 'Kevin')
359
            ->checkField('Input 2')
360
            ->uncheckField('Input 3')
361
            ->selectFieldOption('Input 4', 'option 2')
362
            ->attachFile('Input 5', __FILE__)
363
            ->selectFieldOptions('Input 6', ['option 1', 'option 3'])
364
            ->checkField('Radio 3')
365
            ->click('Submit')
366
            ->assertOn('/submit-form')
367
            ->assertContains('"input_1":"Kevin"')
368
            ->assertContains('"input_2":"on"')
369
            ->assertNotContains('"input_3')
370
            ->assertContains('"input_4":"option 2"')
371
            ->assertContains(\sprintf('"input_5":"%s"', \pathinfo(__FILE__, \PATHINFO_BASENAME)))
0 ignored issues
show
Bug introduced by
It seems like pathinfo(__FILE__, PATHINFO_BASENAME) can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

371
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
372
            ->assertContains('"input_6":["option 1","option 3"]')
373
            ->assertContains('"input_8":"option 3"')
374
        ;
375
    }
376
377
    /**
378
     * @test
379
     */
380
    public function form_actions_by_field_id(): void
381
    {
382
        $this->browser()
383
            ->visit('/page1')
384
            ->fillField('input1', 'Kevin')
385
            ->checkField('input2')
386
            ->uncheckField('input3')
387
            ->selectFieldOption('input4', 'option 2')
388
            ->attachFile('input5', __FILE__)
389
            ->selectFieldOptions('input6', ['option 1', 'option 3'])
390
            ->checkField('radio3')
391
            ->click('Submit')
392
            ->assertOn('/submit-form')
393
            ->assertContains('"input_1":"Kevin"')
394
            ->assertContains('"input_2":"on"')
395
            ->assertNotContains('"input_3')
396
            ->assertContains('"input_4":"option 2"')
397
            ->assertContains(\sprintf('"input_5":"%s"', \pathinfo(__FILE__, \PATHINFO_BASENAME)))
0 ignored issues
show
Bug introduced by
It seems like pathinfo(__FILE__, PATHINFO_BASENAME) can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

397
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
398
            ->assertContains('"input_6":["option 1","option 3"]')
399
            ->assertContains('"input_8":"option 3"')
400
        ;
401
    }
402
403
    /**
404
     * @test
405
     */
406
    public function form_actions_by_field_name(): void
407
    {
408
        $this->browser()
409
            ->visit('/page1')
410
            ->fillField('input_1', 'Kevin')
411
            ->checkField('input_2')
412
            ->uncheckField('input_3')
413
            ->selectFieldOption('input_4', 'option 2')
414
            ->attachFile('input_5', __FILE__)
415
            ->selectFieldOptions('input_6[]', ['option 1', 'option 3'])
416
            ->selectFieldOption('input_8', 'option 3')
417
            ->click('Submit')
418
            ->assertOn('/submit-form')
419
            ->assertContains('"input_1":"Kevin"')
420
            ->assertContains('"input_2":"on"')
421
            ->assertNotContains('"input_3')
422
            ->assertContains('"input_4":"option 2"')
423
            ->assertContains(\sprintf('"input_5":"%s"', \pathinfo(__FILE__, \PATHINFO_BASENAME)))
0 ignored issues
show
Bug introduced by
It seems like pathinfo(__FILE__, PATHINFO_BASENAME) can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

423
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
424
            ->assertContains('"input_6":["option 1","option 3"]')
425
            ->assertContains('"input_8":"option 3"')
426
        ;
427
    }
428
429
    /**
430
     * @test
431
     */
432
    public function select_field(): void
433
    {
434
        $this->browser()
435
            ->visit('/page1')
436
            ->selectField('Input 2')
437
            ->selectField('Input 4', 'option 2')
438
            ->selectField('Input 6', ['option 1', 'option 3'])
439
            ->selectField('Radio 3')
440
            ->click('Submit')
441
            ->assertOn('/submit-form')
442
            ->assertContains('"input_2":"on"')
443
            ->assertContains('"input_4":"option 2"')
444
            ->assertContains('"input_6":["option 1","option 3"]')
445
            ->assertContains('"input_8":"option 3"')
446
        ;
447
    }
448
449
    /**
450
     * @test
451
     */
452
    public function can_submit_form_with_different_submit_buttons(): void
453
    {
454
        // Submit and Submit B, have the same field name but different values
455
        // Submit C has a different field name (and value)
456
457
        $this->browser()
458
            ->visit('/page1')
459
            ->click('Submit')
460
            ->assertOn('/submit-form')
461
            ->assertContains('"submit_1":"a"')
462
            ->assertNotContains('submit_2')
463
            ->visit('/page1')
464
            ->click('Submit B')
465
            ->assertOn('/submit-form')
466
            ->assertContains('"submit_1":"b"')
467
            ->assertNotContains('submit_2')
468
            ->visit('/page1')
469
            ->click('Submit C')
470
            ->assertOn('/submit-form')
471
            ->assertContains('"submit_2":"c"')
472
            ->assertNotContains('submit_1')
473
            ->visit('/page1')
474
            ->click('Submit D')
475
            ->assertOn('/submit-form')
476
            ->assertContains('"submit_2":"d"')
477
            ->assertNotContains('submit_1')
478
        ;
479
    }
480
481
    /**
482
     * @see https://github.com/zenstruck/browser/issues/55
483
     *
484
     * @test
485
     */
486
    public function can_submit_filled_form_with_different_submit_buttons(): void
487
    {
488
        // Submit and Submit B, have the same field name but different values
489
        // Submit C has a different field name (and value)
490
491
        $this->browser()
492
            ->visit('/page1')
493
            ->fillField('input_1', 'Kevin')
494
            ->click('Submit')
495
            ->assertOn('/submit-form')
496
            ->assertContains('"input_1":"Kevin"')
497
            ->assertContains('"submit_1":"a"')
498
            ->assertNotContains('submit_2')
499
            ->visit('/page1')
500
            ->fillField('input_1', 'Kevin')
501
            ->click('Submit B')
502
            ->assertOn('/submit-form')
503
            ->assertContains('"input_1":"Kevin"')
504
            ->assertContains('"submit_1":"b"')
505
            ->assertNotContains('submit_2')
506
            ->visit('/page1')
507
            ->fillField('input_1', 'Kevin')
508
            ->click('Submit C')
509
            ->assertOn('/submit-form')
510
            ->assertContains('"input_1":"Kevin"')
511
            ->assertContains('"submit_2":"c"')
512
            ->assertNotContains('submit_1')
513
            ->visit('/page1')
514
            ->fillField('input_1', 'Kevin')
515
            ->click('Submit D')
516
            ->assertOn('/submit-form')
517
            ->assertContains('"input_1":"Kevin"')
518
            ->assertContains('"submit_2":"d"')
519
            ->assertNotContains('submit_1')
520
        ;
521
    }
522
523
    /**
524
     * @test
525
     */
526
    public function cannot_attach_file_that_does_not_exist(): void
527
    {
528
        $this->expectException(\InvalidArgumentException::class);
529
530
        $this->browser()
531
            ->visit('/page1')
532
            ->attachFile('Input 5', '/invalid/file')
533
        ;
534
    }
535
536
    /**
537
     * @test
538
     */
539
    public function can_attach_multiple_files(): void
540
    {
541
        $this->browser()
542
            ->visit('/page1')
543
            ->attachFile('Input 9', [__DIR__.'/Fixture/files/attachment.txt', __DIR__.'/Fixture/files/xml.xml'])
544
            ->click('Submit')
545
            ->assertContains('"input_9":["attachment.txt","xml.xml"]')
546
        ;
547
    }
548
549
    /**
550
     * @test
551
     */
552
    public function cannot_attach_multiple_files_to_a_non_multiple_input(): void
553
    {
554
        $this->expectException(\InvalidArgumentException::class);
555
556
        $this->browser()
557
            ->visit('/page1')
558
            ->attachFile('Input 5', [__DIR__.'/Fixture/files/attachment.txt', __DIR__.'/Fixture/files/xml.xml'])
559
        ;
560
    }
561
562
    /**
563
     * @test
564
     */
565
    public function can_dump_html_element(): void
566
    {
567
        $output = self::catchVarDumperOutput(function() {
568
            $this->browser()
569
                ->visit('/page1')
570
                ->dump('p#link')
571
            ;
572
        });
573
574
        $this->assertCount(1, $output);
575
        $this->assertSame('<p id="link"><a href="/page2">a link</a> not a link</p>', $output[0]);
576
    }
577
578
    /**
579
     * @test
580
     */
581
    public function if_dump_selector_matches_multiple_elements_all_are_dumped(): void
582
    {
583
        $output = self::catchVarDumperOutput(function() {
584
            $this->browser()
585
                ->visit('/page1')
586
                ->dump('li')
587
            ;
588
        });
589
590
        $this->assertCount(2, $output);
591
        $this->assertSame('<li>list 1</li>', $output[0]);
592
        $this->assertSame('<li>list 2</li>', $output[1]);
593
    }
594
595
    /**
596
     * @test
597
     */
598
    public function can_access_the_html_crawler(): void
599
    {
600
        $crawler = $this->browser()
601
            ->visit('/page1')
602
            ->response()
603
            ->assertHtml()
604
            ->crawler()
605
            ->filter('ul li')
606
        ;
607
608
        $this->assertCount(2, $crawler);
609
    }
610
611
    protected static function catchFileContents(string $expectedFile, callable $callback): string
612
    {
613
        (new Filesystem())->remove($expectedFile);
614
615
        $callback();
616
617
        self::assertFileExists($expectedFile);
618
619
        return \file_get_contents($expectedFile);
620
    }
621
622
    protected static function catchVarDumperOutput(callable $callback): array
623
    {
624
        $output[] = null;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$output was never initialized. Although not strictly required by PHP, it is generally a good practice to add $output = array(); before regardless.
Loading history...
625
626
        VarDumper::setHandler(function($var) use (&$output) {
627
            $output[] = $var;
628
        });
629
630
        $callback();
631
632
        // reset to default handler
633
        VarDumper::setHandler();
634
635
        // a null value is added to the beginning
636
        return \array_values(\array_filter($output));
637
    }
638
639
    abstract protected function browser(): Browser;
640
}
641