Passed
Pull Request — 1.x (#31)
by Kevin
01:54
created

BrowserTests::can_access_cookies()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

88
                $this->/** @scrutinizer ignore-call */ 
89
                       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...
89
90
                $browser->visit('/redirect1');
91
            })
92
            ->assertOn('/page1')
93
            ->use(function() {
94
                $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

94
                $this->/** @scrutinizer ignore-call */ 
95
                       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...
95
            })
96
        ;
97
    }
98
99
    /**
100
     * @test
101
     */
102
    public function can_use_components(): void
103
    {
104
        $this->browser()
105
            ->use(function(TestComponent1 $component) {
106
                $component->assertTitle('h1 title');
107
            })
108
            ->assertOn('/page1')
109
        ;
110
    }
111
112
    /**
113
     * @test
114
     */
115
    public function component_pre_assertions_and_actions_are_called(): void
116
    {
117
        $this->browser()
118
            ->use(function(TestComponent2 $component) {
119
                $this->assertTrue($component->preActionsCalled);
120
                $this->assertTrue($component->preAssertionsCalled);
121
            })
122
        ;
123
    }
124
125
    /**
126
     * @test
127
     */
128
    public function with_can_accept_multiple_browsers_and_components(): void
129
    {
130
        $browser = $this->browser();
131
132
        $browser
133
            ->use(function(Browser $browser1, $browser2, TestComponent1 $component1, TestComponent2 $component2) use ($browser) {
134
                $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

134
                $this->/** @scrutinizer ignore-call */ 
135
                       assertInstanceOf(Browser::class, $browser1);
Loading history...
135
                $this->assertInstanceOf(Browser::class, $browser2);
136
                $this->assertInstanceOf(\get_class($browser), $browser1);
137
                $this->assertInstanceOf(\get_class($browser), $browser2);
138
                $this->assertInstanceOf(TestComponent1::class, $component1);
139
                $this->assertInstanceOf(TestComponent2::class, $component2);
140
            })
141
        ;
142
    }
143
144
    /**
145
     * @test
146
     */
147
    public function invalid_use_callback_parameter_throws_type_error(): void
148
    {
149
        $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

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

151
        $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...
152
    }
153
154
    /**
155
     * @test
156
     */
157
    public function redirects_are_followed_by_default(): void
158
    {
159
        $this->browser()
160
            ->visit('/redirect1')
161
            ->assertOn('/page1')
162
        ;
163
    }
164
165
    /**
166
     * @test
167
     */
168
    public function content_assertions(): void
169
    {
170
        $this->browser()
171
            ->visit('/page1')
172
            ->assertContains('h1 title')
173
            ->assertNotContains('invalid text')
174
        ;
175
    }
176
177
    /**
178
     * @test
179
     */
180
    public function can_dump_response(): void
181
    {
182
        $output = self::catchVarDumperOutput(function() {
183
            $this->browser()
184
                ->visit('/page1')
185
                ->dump()
186
            ;
187
        });
188
189
        $this->assertStringContainsString('/page1', $output[0]);
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

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

313
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
314
            ->assertContains('"input_6":["option 1","option 3"]')
315
        ;
316
    }
317
318
    /**
319
     * @test
320
     */
321
    public function form_actions_by_field_id(): void
322
    {
323
        $this->browser()
324
            ->visit('/page1')
325
            ->fillField('input1', 'Kevin')
326
            ->checkField('input2')
327
            ->uncheckField('input3')
328
            ->selectFieldOption('input4', 'option 2')
329
            ->attachFile('input5', __FILE__)
330
            ->selectFieldOptions('input6', ['option 1', 'option 3'])
331
            ->click('Submit')
332
            ->assertOn('/submit-form')
333
            ->assertContains('"input_1":"Kevin"')
334
            ->assertContains('"input_2":"on"')
335
            ->assertNotContains('"input_3')
336
            ->assertContains('"input_4":"option 2"')
337
            ->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

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

361
            ->assertContains(\sprintf('"input_5":"%s"', /** @scrutinizer ignore-type */ \pathinfo(__FILE__, \PATHINFO_BASENAME)))
Loading history...
362
            ->assertContains('"input_6":["option 1","option 3"]')
363
        ;
364
    }
365
366
    /**
367
     * @test
368
     */
369
    public function can_dump_html_element(): void
370
    {
371
        $output = self::catchVarDumperOutput(function() {
372
            $this->browser()
373
                ->visit('/page1')
374
                ->dump('p#link')
375
            ;
376
        });
377
378
        $this->assertCount(1, $output);
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

378
        $this->/** @scrutinizer ignore-call */ 
379
               assertCount(1, $output);

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...
379
        $this->assertSame('<a href="/page2">a link</a> not a link', $output[0]);
380
    }
381
382
    /**
383
     * @test
384
     */
385
    public function if_dump_selector_matches_multiple_elements_all_are_dumped(): void
386
    {
387
        $output = self::catchVarDumperOutput(function() {
388
            $this->browser()
389
                ->visit('/page1')
390
                ->dump('li')
391
            ;
392
        });
393
394
        $this->assertCount(2, $output);
395
        $this->assertSame('list 1', $output[0]);
396
        $this->assertSame('list 2', $output[1]);
397
    }
398
399
    /**
400
     * @test
401
     */
402
    public function can_access_cookies(): void
403
    {
404
        $cookies = $this->browser()->visit('/page1')->cookies();
405
406
        $this->assertIsArray($cookies->all());
0 ignored issues
show
Bug introduced by
It seems like assertIsArray() 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

406
        $this->/** @scrutinizer ignore-call */ 
407
               assertIsArray($cookies->all());
Loading history...
407
    }
408
409
    protected static function catchFileContents(string $expectedFile, callable $callback): string
410
    {
411
        (new Filesystem())->remove($expectedFile);
412
413
        $callback();
414
415
        self::assertFileExists($expectedFile);
416
417
        return \file_get_contents($expectedFile);
418
    }
419
420
    protected static function catchVarDumperOutput(callable $callback): array
421
    {
422
        $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...
423
424
        VarDumper::setHandler(function($var) use (&$output) {
425
            $output[] = $var;
426
        });
427
428
        $callback();
429
430
        // reset to default handler
431
        VarDumper::setHandler();
432
433
        // a null value is added to the beginning
434
        return \array_values(\array_filter($output));
435
    }
436
437
    abstract protected function browser(): Browser;
438
}
439