Passed
Push — 1.x ( 91d62d...d76502 )
by Kevin
01:50
created

cannot_attach_file_that_does_not_exist()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
namespace Zenstruck\Browser\Tests;
4
5
use Symfony\Component\Filesystem\Filesystem;
6
use Symfony\Component\VarDumper\VarDumper;
7
use Zenstruck\Browser;
8
use Zenstruck\Browser\Response;
9
use Zenstruck\Browser\Response\HtmlResponse;
10
use Zenstruck\Browser\Test\HasBrowser;
11
use Zenstruck\Browser\Tests\Fixture\TestComponent1;
12
use Zenstruck\Browser\Tests\Fixture\TestComponent2;
13
use Zenstruck\Callback\Exception\UnresolveableArgument;
14
15
/**
16
 * @author Kevin Bond <[email protected]>
17
 */
18
trait BrowserTests
19
{
20
    use HasBrowser {
21
        browser as kernelBrowser;
22
    }
23
24
    /**
25
     * @test
26
     */
27
    public function multiple_browsers(): void
28
    {
29
        $browser1 = $this->browser()
30
            ->visit('/page1')
31
            ->assertOn('/page1')
32
        ;
33
34
        $browser2 = $this->browser()
0 ignored issues
show
Unused Code introduced by
The assignment to $browser2 is dead and can be removed.
Loading history...
35
            ->visit('/page2')
36
            ->assertOn('/page2')
37
        ;
38
39
        // this ensures a different browser is actually used
40
        $browser1->assertOn('/page1');
41
    }
42
43
    /**
44
     * @test
45
     */
46
    public function assert_on(): void
47
    {
48
        $this->browser()
49
            ->visit('/page1')
50
            ->assertOn('/page1')
51
            ->assertOn('http://www.example.com/page1')
52
            ->assertNotOn('/page2')
53
            ->assertNotOn('http://www.example.com/page1', ['path', 'host'])
54
            ->visit('/page1?foo=bar')
55
            ->assertOn('/page1?foo=bar')
56
            ->assertOn('/page1', ['path'])
57
            ->assertOn('/page1', ['path', 'fragment'])
58
            ->assertNotOn('/page1?foo=baz')
59
        ;
60
    }
61
62
    /**
63
     * @test
64
     * @dataProvider encodedUrlProvider
65
     */
66
    public function assert_on_encoded($url, $expected): void
67
    {
68
        $this->browser()
69
            ->visit($url)
70
            ->assertOn($expected)
71
        ;
72
    }
73
74
    public static function encodedUrlProvider(): iterable
75
    {
76
        yield ['/page1?filter[q]=value', '/page1?filter[q]=value'];
77
        yield ['/page1?filter%5Bq%5D=value', '/page1?filter[q]=value'];
78
        yield ['/page1?filter[q]=value', '/page1?filter%5Bq%5D=value'];
79
        yield ['/page1#foo bar', '/page1#foo bar'];
80
        yield ['/page1#foo%20bar', '/page1#foo bar'];
81
        yield ['/page1#foo bar', '/page1#foo%20bar'];
82
        yield ['/page1#foo+bar', '/page1#foo bar'];
83
        yield ['/page1#foo bar', '/page1#foo+bar'];
84
    }
85
86
    /**
87
     * @test
88
     */
89
    public function can_use_current_browser(): void
90
    {
91
        $browser = $this->browser();
92
93
        $browser
94
            ->use(function(Browser $b) use ($browser) {
95
                $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

95
                $this->/** @scrutinizer ignore-call */ 
96
                       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...
96
97
                $browser->visit('/redirect1');
98
            })
99
            ->assertOn('/page1')
100
            ->use(function() {
101
                $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

101
                $this->/** @scrutinizer ignore-call */ 
102
                       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...
102
            })
103
        ;
104
    }
105
106
    /**
107
     * @test
108
     */
109
    public function can_use_components(): void
110
    {
111
        $this->browser()
112
            ->use(function(TestComponent1 $component) {
113
                $component->assertTitle('h1 title');
114
            })
115
            ->assertOn('/page1')
116
        ;
117
    }
118
119
    /**
120
     * @test
121
     */
122
    public function component_pre_assertions_and_actions_are_called(): void
123
    {
124
        $this->browser()
125
            ->use(function(TestComponent2 $component) {
126
                $this->assertTrue($component->preActionsCalled);
127
                $this->assertTrue($component->preAssertionsCalled);
128
            })
129
        ;
130
    }
131
132
    /**
133
     * @test
134
     */
135
    public function can_use_response(): void
136
    {
137
        $this->browser()
138
            ->visit('/page1')
139
            ->use(function(Response $response) {
140
                $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

140
                $this->/** @scrutinizer ignore-call */ 
141
                       assertStringContainsString('<h1>h1 title</h1>', $response->body());
Loading history...
141
            })
142
            ->use(function(HtmlResponse $response) {
143
                $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

143
                $this->/** @scrutinizer ignore-call */ 
144
                       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...
144
            })
145
        ;
146
    }
147
148
    /**
149
     * @test
150
     */
151
    public function with_can_accept_multiple_browsers_and_components(): void
152
    {
153
        $browser = $this->browser();
154
155
        $browser
156
            ->use(function(Browser $browser1, $browser2, TestComponent1 $component1, TestComponent2 $component2) use ($browser) {
157
                $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

157
                $this->/** @scrutinizer ignore-call */ 
158
                       assertInstanceOf(Browser::class, $browser1);
Loading history...
158
                $this->assertInstanceOf(Browser::class, $browser2);
159
                $this->assertInstanceOf(\get_class($browser), $browser1);
160
                $this->assertInstanceOf(\get_class($browser), $browser2);
161
                $this->assertInstanceOf(TestComponent1::class, $component1);
162
                $this->assertInstanceOf(TestComponent2::class, $component2);
163
            })
164
        ;
165
    }
166
167
    /**
168
     * @test
169
     */
170
    public function invalid_use_callback_parameter_throws_type_error(): void
171
    {
172
        $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

172
        $this->/** @scrutinizer ignore-call */ 
173
               expectException(UnresolveableArgument::class);
Loading history...
173
174
        $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

174
        $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...
175
    }
176
177
    /**
178
     * @test
179
     */
180
    public function redirects_are_followed_by_default(): void
181
    {
182
        $this->browser()
183
            ->visit('/redirect1')
184
            ->assertOn('/page1')
185
        ;
186
    }
187
188
    /**
189
     * @test
190
     */
191
    public function content_assertions(): void
192
    {
193
        $this->browser()
194
            ->visit('/page1')
195
            ->assertContains('h1 title')
196
            ->assertNotContains('invalid text')
197
        ;
198
    }
199
200
    /**
201
     * @test
202
     */
203
    public function can_dump_response(): void
204
    {
205
        $output = self::catchVarDumperOutput(function() {
206
            $this->browser()
207
                ->visit('/page1')
208
                ->dump()
209
            ;
210
        });
211
212
        $this->assertStringContainsString('/page1', $output[0]);
213
        $this->assertStringContainsString('<html', $output[0]);
214
        $this->assertStringContainsString('<h1>h1 title</h1>', $output[0]);
215
    }
216
217
    /**
218
     * @test
219
     */
220
    public function can_save_source(): void
221
    {
222
        $contents = self::catchFileContents(__DIR__.'/../var/browser/source/source.txt', function() {
223
            $this->browser()
224
                ->visit('/page1')
225
                ->saveSource('source.txt')
226
            ;
227
        });
228
229
        $this->assertStringContainsString('/page1', $contents);
230
        $this->assertStringContainsString('<html', $contents);
231
        $this->assertStringContainsString('<h1>h1 title</h1>', $contents);
232
    }
233
234
    /**
235
     * @test
236
     */
237
    public function html_assertions(): void
238
    {
239
        $this->browser()
240
            ->visit('/page1')
241
            ->assertSee('h1 title')
242
            ->assertNotSee('invalid text')
243
            ->assertSeeIn('h1', 'title')
244
            ->assertNotSeeIn('h1', 'invalid text')
245
            ->assertSeeElement('h1')
246
            ->assertNotSeeElement('h2')
247
            ->assertElementCount('ul li', 2)
248
        ;
249
    }
250
251
    /**
252
     * @test
253
     */
254
    public function html_head_assertions(): void
255
    {
256
        $this->browser()
257
            ->visit('/page1')
258
            ->assertSeeIn('title', 'meta title')
259
            ->assertElementAttributeContains('meta[name="description"]', 'content', 'meta')
260
            ->assertElementAttributeNotContains('meta[name="description"]', 'content', 'invalid')
261
            ->assertElementAttributeContains('html', 'lang', 'en')
262
        ;
263
    }
264
265
    /**
266
     * @test
267
     */
268
    public function form_assertions(): void
269
    {
270
        $this->browser()
271
            ->visit('/page1')
272
            ->assertFieldEquals('Input 1', 'input 1')
273
            ->assertFieldEquals('input1', 'input 1')
274
            ->assertFieldEquals('input_1', 'input 1')
275
            ->assertFieldNotEquals('Input 1', 'invalid')
276
            ->assertFieldNotEquals('input1', 'invalid')
277
            ->assertFieldNotEquals('input_1', 'invalid')
278
            ->assertChecked('Input 3')
279
            ->assertChecked('input3')
280
            ->assertChecked('input_3')
281
            ->assertNotChecked('Input 2')
282
            ->assertNotChecked('input2')
283
            ->assertNotChecked('input_2')
284
            ->assertSelected('Input 4', 'option 1')
285
            ->assertSelected('input4', 'option 1')
286
            ->assertSelected('input_4', 'option 1')
287
            ->assertSelected('Input 7', 'option 1')
288
            ->assertSelected('input7', 'option 1')
289
            ->assertSelected('input_7[]', 'option 1')
290
            ->assertSelected('Input 7', 'option 3')
291
            ->assertSelected('input7', 'option 3')
292
            ->assertSelected('input_7[]', 'option 3')
293
            ->assertNotSelected('Input 4', 'option 2')
294
            ->assertNotSelected('input4', 'option 2')
295
            ->assertNotSelected('input_4', 'option 2')
296
            ->assertNotSelected('Input 7', 'option 2')
297
            ->assertNotSelected('input7', 'option 2')
298
            ->assertNotSelected('input_7[]', 'option 2')
299
        ;
300
    }
301
302
    /**
303
     * @test
304
     */
305
    public function link_action(): void
306
    {
307
        $this->browser()
308
            ->visit('/page1')
309
            ->follow('a link')
310
            ->assertOn('/page2')
311
            ->visit('/page1')
312
            ->click('a link')
313
            ->assertOn('/page2')
314
        ;
315
    }
316
317
    /**
318
     * @test
319
     */
320
    public function form_actions_by_field_label(): void
321
    {
322
        $this->browser()
323
            ->visit('/page1')
324
            ->fillField('Input 1', 'Kevin')
325
            ->checkField('Input 2')
326
            ->uncheckField('Input 3')
327
            ->selectFieldOption('Input 4', 'option 2')
328
            ->attachFile('Input 5', __FILE__)
329
            ->selectFieldOptions('Input 6', ['option 1', 'option 3'])
330
            ->click('Submit')
331
            ->assertOn('/submit-form')
332
            ->assertContains('"input_1":"Kevin"')
333
            ->assertContains('"input_2":"on"')
334
            ->assertNotContains('"input_3')
335
            ->assertContains('"input_4":"option 2"')
336
            ->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

336
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
337
            ->assertContains('"input_6":["option 1","option 3"]')
338
        ;
339
    }
340
341
    /**
342
     * @test
343
     */
344
    public function form_actions_by_field_id(): void
345
    {
346
        $this->browser()
347
            ->visit('/page1')
348
            ->fillField('input1', 'Kevin')
349
            ->checkField('input2')
350
            ->uncheckField('input3')
351
            ->selectFieldOption('input4', 'option 2')
352
            ->attachFile('input5', __FILE__)
353
            ->selectFieldOptions('input6', ['option 1', 'option 3'])
354
            ->click('Submit')
355
            ->assertOn('/submit-form')
356
            ->assertContains('"input_1":"Kevin"')
357
            ->assertContains('"input_2":"on"')
358
            ->assertNotContains('"input_3')
359
            ->assertContains('"input_4":"option 2"')
360
            ->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

360
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
361
            ->assertContains('"input_6":["option 1","option 3"]')
362
        ;
363
    }
364
365
    /**
366
     * @test
367
     */
368
    public function form_actions_by_field_name(): void
369
    {
370
        $this->browser()
371
            ->visit('/page1')
372
            ->fillField('input_1', 'Kevin')
373
            ->checkField('input_2')
374
            ->uncheckField('input_3')
375
            ->selectFieldOption('input_4', 'option 2')
376
            ->attachFile('input_5', __FILE__)
377
            ->selectFieldOptions('input_6[]', ['option 1', 'option 3'])
378
            ->click('Submit')
379
            ->assertOn('/submit-form')
380
            ->assertContains('"input_1":"Kevin"')
381
            ->assertContains('"input_2":"on"')
382
            ->assertNotContains('"input_3')
383
            ->assertContains('"input_4":"option 2"')
384
            ->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

384
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
385
            ->assertContains('"input_6":["option 1","option 3"]')
386
        ;
387
    }
388
389
    /**
390
     * @test
391
     */
392
    public function cannot_attach_file_that_does_not_exist(): void
393
    {
394
        $this->expectException(\InvalidArgumentException::class);
395
396
        $this->browser()
397
            ->visit('/page1')
398
            ->attachFile('Input 5', '/invalid/file')
399
        ;
400
    }
401
402
    /**
403
     * @test
404
     */
405
    public function can_dump_html_element(): void
406
    {
407
        $output = self::catchVarDumperOutput(function() {
408
            $this->browser()
409
                ->visit('/page1')
410
                ->dump('p#link')
411
            ;
412
        });
413
414
        $this->assertCount(1, $output);
415
        $this->assertSame('<p id="link"><a href="/page2">a link</a> not a link</p>', $output[0]);
416
    }
417
418
    /**
419
     * @test
420
     */
421
    public function if_dump_selector_matches_multiple_elements_all_are_dumped(): void
422
    {
423
        $output = self::catchVarDumperOutput(function() {
424
            $this->browser()
425
                ->visit('/page1')
426
                ->dump('li')
427
            ;
428
        });
429
430
        $this->assertCount(2, $output);
431
        $this->assertSame('<li>list 1</li>', $output[0]);
432
        $this->assertSame('<li>list 2</li>', $output[1]);
433
    }
434
435
    /**
436
     * @test
437
     */
438
    public function can_access_the_html_crawler(): void
439
    {
440
        $crawler = $this->browser()
441
            ->visit('/page1')
442
            ->response()
443
            ->assertHtml()
444
            ->crawler()
445
            ->filter('ul li')
446
        ;
447
448
        $this->assertCount(2, $crawler);
449
    }
450
451
    protected static function catchFileContents(string $expectedFile, callable $callback): string
452
    {
453
        (new Filesystem())->remove($expectedFile);
454
455
        $callback();
456
457
        self::assertFileExists($expectedFile);
458
459
        return \file_get_contents($expectedFile);
460
    }
461
462
    protected static function catchVarDumperOutput(callable $callback): array
463
    {
464
        $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...
465
466
        VarDumper::setHandler(function($var) use (&$output) {
467
            $output[] = $var;
468
        });
469
470
        $callback();
471
472
        // reset to default handler
473
        VarDumper::setHandler();
474
475
        // a null value is added to the beginning
476
        return \array_values(\array_filter($output));
477
    }
478
479
    abstract protected function browser(): Browser;
480
}
481