Passed
Pull Request — master (#50)
by Wilmer
02:02
created

testSourcesPublishedBySymlinkIssue9333()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 10
rs 10
cc 1
nc 1
nop 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace Yiisoft\Asset\Tests;
5
6
use \RuntimeException;
7
use Yiisoft\Asset\AssetBundle;
8
use Yiisoft\Asset\AssetManager;
9
use Yiisoft\Asset\Tests\Stubs\TestAssetLevel3;
10
use Yiisoft\Asset\Tests\Stubs\TestAssetCircleA;
11
use Yiisoft\Asset\Tests\Stubs\TestPosBeginAsset;
12
use Yiisoft\Asset\Tests\Stubs\TestPosBeginConflictAsset;
13
use Yiisoft\Asset\Tests\Stubs\TestPosEndAsset;
14
use Yiisoft\Asset\Tests\Stubs\TestPosHeadAsset;
15
use Yiisoft\Asset\Tests\Stubs\TestJqueryAsset;
16
use Yiisoft\Asset\Tests\Stubs\TestJqueryConflictAsset;
17
use Yiisoft\Asset\Tests\Stubs\TestAssetPerFileOptions;
18
use Yiisoft\Asset\Tests\Stubs\TestSimpleAsset;
19
use Yiisoft\Asset\Tests\Stubs\TestSourceAsset;
20
use Yiisoft\Files\FileHelper;
21
use Yiisoft\Tests\TestCase;
22
use Yiisoft\View\WebView;
23
24
/**
25
 * AssetBundleTest.
26
 */
27
final class AssetBundleTest extends TestCase
28
{
29
    public function testCircularDependency(): void
30
    {
31
        $this->expectException(\RuntimeException::class);
32
        TestAssetCircleA::register($this->webView);
33
    }
34
35
    public function testDuplicateAssetFile(): void
36
    {
37
        $view = $this->webView;
38
39
        $this->assertEmpty($view->getAssetBundles());
40
41
        TestSimpleAsset::register($view);
42
43
        $this->assertCount(1, $view->getAssetBundles());
44
        $this->assertArrayHasKey(TestSimpleAsset::class, $view->getAssetBundles());
45
        $this->assertInstanceOf(AssetBundle::class, $view->getAssetBundles()[TestSimpleAsset::class]);
46
        // register TestJqueryAsset which also has the jquery.js
47
48
        TestJqueryAsset::register($view);
49
50
        $expected = <<<'EOF'
51
123<script src="/baseUrl/js/jquery.js"></script>4
52
EOF;
53
        $this->assertEquals($expected, $view->renderFile($this->aliases->get('@view/rawlayout.php')));
0 ignored issues
show
Bug introduced by
It seems like $this->aliases->get('@view/rawlayout.php') can also be of type boolean; however, parameter $viewFile of Yiisoft\View\View::renderFile() does only seem to accept 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

53
        $this->assertEquals($expected, $view->renderFile(/** @scrutinizer ignore-type */ $this->aliases->get('@view/rawlayout.php')));
Loading history...
54
    }
55
56
    public function testPerFileOptions(): void
57
    {
58
        $view = $this->webView;
59
60
        $this->assertEmpty($view->getAssetBundles());
61
62
        TestAssetPerFileOptions::register($view);
63
64
        $expected = <<<'EOF'
65
1<link href="/baseUrl/default_options.css" rel="stylesheet" media="screen" hreflang="en">
66
<link href="/baseUrl/tv.css" rel="stylesheet" media="tv" hreflang="en">
67
<link href="/baseUrl/screen_and_print.css" rel="stylesheet" media="screen, print" hreflang="en">23<script src="/baseUrl/normal.js" charset="utf-8"></script>
68
<script src="/baseUrl/defered.js" charset="utf-8" defer></script>4
69
EOF;
70
        $this->assertEqualsWithoutLE($expected, $view->renderFile($this->aliases->get('@view/rawlayout.php')));
0 ignored issues
show
Bug introduced by
It seems like $this->aliases->get('@view/rawlayout.php') can also be of type boolean; however, parameter $viewFile of Yiisoft\View\View::renderFile() does only seem to accept 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

70
        $this->assertEqualsWithoutLE($expected, $view->renderFile(/** @scrutinizer ignore-type */ $this->aliases->get('@view/rawlayout.php')));
Loading history...
71
    }
72
73
    public function positionProvider(): array
74
    {
75
        return [
76
            [TestPosHeadAsset::class, WebView::POS_HEAD, true],
77
            [TestPosHeadAsset::class, WebView::POS_HEAD, false],
78
            [TestPosBeginAsset::class, WebView::POS_BEGIN, true],
79
            [TestPosBeginAsset::class, WebView::POS_BEGIN, false],
80
            [TestPosEndAsset::class, WebView::POS_END, true],
81
            [TestPosEndAsset::class, WebView::POS_END, false],
82
        ];
83
    }
84
85
    /**
86
     * @dataProvider positionProvider
87
     *
88
     * @param string $assetBundle
89
     * @param int $pos
90
     * @param bool $jqAlreadyRegistered
91
     */
92
    public function testPositionDependencyPos(string $assetBundle, int $pos, bool $jqAlreadyRegistered): void
93
    {
94
        $view = $this->webView;
95
96
        $this->assertEmpty($view->getAssetBundles());
97
98
        if ($jqAlreadyRegistered) {
99
            TestJqueryAsset::register($view);
100
        }
101
102
        $assetBundle::register($view);
103
104
        $this->assertCount(3, $view->getAssetBundles());
105
106
        $this->assertArrayHasKey($assetBundle, $view->getAssetBundles());
107
        $this->assertArrayHasKey(TestJqueryAsset::class, $view->getAssetBundles());
108
        $this->assertArrayHasKey(TestAssetLevel3::class, $view->getAssetBundles());
109
110
        $this->assertInstanceOf(AssetBundle::class, $view->getAssetBundles()[$assetBundle]);
111
        $this->assertInstanceOf(AssetBundle::class, $view->getAssetBundles()[TestJqueryAsset::class]);
112
        $this->assertInstanceOf(AssetBundle::class, $view->getAssetBundles()[TestAssetLevel3::class]);
113
114
        $this->assertArrayHasKey('position', $view->getAssetBundles()[$assetBundle]->jsOptions);
115
        $this->assertEquals($pos, $view->getAssetBundles()[$assetBundle]->jsOptions['position']);
116
        $this->assertArrayHasKey('position', $view->getAssetBundles()[TestJqueryAsset::class]->jsOptions);
117
        $this->assertEquals($pos, $view->getAssetBundles()[TestJqueryAsset::class]->jsOptions['position']);
118
        $this->assertArrayHasKey('position', $view->getAssetBundles()[TestAssetLevel3::class]->jsOptions);
119
        $this->assertEquals($pos, $view->getAssetBundles()[TestAssetLevel3::class]->jsOptions['position']);
120
121
        switch ($pos) {
122
            case WebView::POS_HEAD:
123
                $expected = <<<'EOF'
124
1<link href="/baseUrl/files/cssFile.css" rel="stylesheet">
125
<script src="/baseUrl/js/jquery.js"></script>
126
<script src="/baseUrl/files/jsFile.js"></script>234
127
EOF;
128
                break;
129
            case WebView::POS_BEGIN:
130
                $expected = <<<'EOF'
131
1<link href="/baseUrl/files/cssFile.css" rel="stylesheet">2<script src="/baseUrl/js/jquery.js"></script>
132
<script src="/baseUrl/files/jsFile.js"></script>34
133
EOF;
134
                break;
135
            default:
136
            case WebView::POS_END:
137
                $expected = <<<'EOF'
138
1<link href="/baseUrl/files/cssFile.css" rel="stylesheet">23<script src="/baseUrl/js/jquery.js"></script>
139
<script src="/baseUrl/files/jsFile.js"></script>4
140
EOF;
141
                break;
142
        }
143
        $this->assertEqualsWithoutLE($expected, $view->renderFile($this->aliases->get('@view/rawlayout.php')));
0 ignored issues
show
Bug introduced by
It seems like $this->aliases->get('@view/rawlayout.php') can also be of type boolean; however, parameter $viewFile of Yiisoft\View\View::renderFile() does only seem to accept 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

143
        $this->assertEqualsWithoutLE($expected, $view->renderFile(/** @scrutinizer ignore-type */ $this->aliases->get('@view/rawlayout.php')));
Loading history...
144
    }
145
146
    public function positionProvider2(): array
147
    {
148
        return [
149
            [TestPosBeginConflictAsset::class, WebView::POS_BEGIN, true],
150
            [TestPosBeginConflictAsset::class, WebView::POS_BEGIN, false],
151
        ];
152
    }
153
154
    /**
155
     * @dataProvider positionProvider2
156
     *
157
     * @param string $assetBundle
158
     * @param int  $pos
159
     * @param bool $jqAlreadyRegistered
160
     */
161
    public function testPositionDependencyConflict(string $assetBundle, int $pos, bool $jqAlreadyRegistered): void
162
    {
163
        $view = $this->webView;
164
165
        $this->assertEmpty($view->getAssetBundles());
166
167
        if ($jqAlreadyRegistered) {
168
            TestJqueryConflictAsset::register($view);
169
        }
170
171
        $this->expectException(\RuntimeException::class);
172
        $assetBundle::register($this->webView);
173
    }
174
175
    public function testSourcesPublishedBySymlinkIssue9333(): void
176
    {
177
        $this->assetManager->setLinkAssets(true);
178
        $this->assetManager->setHashCallback(
179
            function ($path) {
180
                return sprintf('%x/%x', crc32($path), crc32('3.0-dev'));
181
            }
182
        );
183
        $bundle = $this->verifySourcesPublishedBySymlink($this->webView);
184
        $this->assertTrue(is_dir(dirname($bundle->basePath)));
185
    }
186
187
    public function testSourcesPublishOptionsOnly(): void
188
    {
189
        $am = $this->webView->getAssetManager();
190
        $am->setLinkAssets(false);
191
192
        $bundle = new TestSourceAsset();
193
194
        $bundle->publishOptions = [
195
            'only' => [
196
                'js/*'
197
            ],
198
        ];
199
200
        $bundle->publish($am);
201
202
        $notNeededFilesDir = dirname($bundle->basePath . DIRECTORY_SEPARATOR . $bundle->css[0]);
203
204
        $this->assertFileNotExists($notNeededFilesDir);
205
206
        foreach ($bundle->js as $filename) {
207
            $publishedFile = $bundle->basePath . DIRECTORY_SEPARATOR . $filename;
208
            $this->assertFileExists($publishedFile);
209
        }
210
211
        $this->assertTrue(is_dir(dirname($bundle->basePath . DIRECTORY_SEPARATOR . $bundle->js[0])));
212
        $this->assertTrue(is_dir($bundle->basePath));
213
    }
214
215
    public function registerFileDataProvider(): array
216
    {
217
        return [
218
            // Custom alias repeats in the asset URL
219
            [
220
                'css', '@web/assetSources/repeat/css/stub.css', false,
221
                '1<link href="/repeat/assetSources/repeat/css/stub.css" rel="stylesheet">234',
222
                '/repeat',
223
            ],
224
            [
225
                'js', '@web/assetSources/repeat/js/jquery.js', false,
226
                '123<script src="/repeat/assetSources/repeat/js/jquery.js"></script>4',
227
                '/repeat',
228
            ],
229
230
            // JS files registration
231
            [
232
                'js', '@web/assetSources/js/missing-file.js', true,
233
                '123<script src="/baseUrl/assetSources/js/missing-file.js"></script>4',
234
            ],
235
            [
236
                'js', '@web/assetSources/js/jquery.js', false,
237
                '123<script src="/baseUrl/assetSources/js/jquery.js"></script>4',
238
            ],
239
            [
240
                'js', 'http://example.com/assetSources/js/jquery.js', false,
241
                '123<script src="http://example.com/assetSources/js/jquery.js"></script>4',
242
            ],
243
            [
244
                'js', '//example.com/assetSources/js/jquery.js', false,
245
                '123<script src="//example.com/assetSources/js/jquery.js"></script>4',
246
            ],
247
            [
248
                'js', 'assetSources/js/jquery.js', false,
249
                '123<script src="assetSources/js/jquery.js"></script>4',
250
            ],
251
            [
252
                'js', '/assetSources/js/jquery.js', false,
253
                '123<script src="/assetSources/js/jquery.js"></script>4',
254
            ],
255
256
            // CSS file registration
257
            [
258
                'css', '@web/assetSources/css/missing-file.css', true,
259
                '1<link href="/baseUrl/assetSources/css/missing-file.css" rel="stylesheet">234',
260
            ],
261
            [
262
                'css', '@web/assetSources/css/stub.css', false,
263
                '1<link href="/baseUrl/assetSources/css/stub.css" rel="stylesheet">234',
264
            ],
265
            [
266
                'css', 'http://example.com/assetSources/css/stub.css', false,
267
                '1<link href="http://example.com/assetSources/css/stub.css" rel="stylesheet">234',
268
            ],
269
            [
270
                'css', '//example.com/assetSources/css/stub.css', false,
271
                '1<link href="//example.com/assetSources/css/stub.css" rel="stylesheet">234',
272
            ],
273
            [
274
                'css', 'assetSources/css/stub.css', false,
275
                '1<link href="assetSources/css/stub.css" rel="stylesheet">234',
276
            ],
277
            [
278
                'css', '/assetSources/css/stub.css', false,
279
                '1<link href="/assetSources/css/stub.css" rel="stylesheet">234',
280
            ],
281
282
            // Custom `@web` aliases
283
            [
284
                'js', '@web/assetSources/js/missing-file1.js', true,
285
                '123<script src="/backend/assetSources/js/missing-file1.js"></script>4',
286
                '/backend',
287
            ],
288
            [
289
                'js', 'http://full-url.example.com/backend/assetSources/js/missing-file.js', true,
290
                '123<script src="http://full-url.example.com/backend/assetSources/js/missing-file.js"></script>4',
291
                '/backend',
292
            ],
293
            [
294
                'css', '//backend/backend/assetSources/js/missing-file.js', true,
295
                '1<link href="//backend/backend/assetSources/js/missing-file.js" rel="stylesheet">234',
296
                '/backend',
297
            ],
298
            [
299
                'css', '@web/assetSources/css/stub.css', false,
300
                '1<link href="/en/blog/backend/assetSources/css/stub.css" rel="stylesheet">234',
301
                '/en/blog/backend',
302
            ],
303
304
            // UTF-8 chars
305
            [
306
                'css', '@web/assetSources/css/stub.css', false,
307
                '1<link href="/рус/сайт/assetSources/css/stub.css" rel="stylesheet">234',
308
                '/рус/сайт',
309
            ],
310
            [
311
                'js', '@web/assetSources/js/jquery.js', false,
312
                '123<script src="/汉语/漢語/assetSources/js/jquery.js"></script>4',
313
                '/汉语/漢語',
314
            ],
315
        ];
316
    }
317
318
    /**
319
     * @dataProvider registerFileDataProvider
320
     *
321
     * @param string      $type            either `js` or `css`
322
     * @param string      $path
323
     * @param string|bool $appendTimestamp
324
     * @param string      $expected
325
     * @param string|null $webAlias
326
     */
327
    public function testRegisterFileAppendTimestamp($type, $path, $appendTimestamp, $expected, $webAlias = null): void
328
    {
329
        $originalAlias = $this->aliases->get('@web');
330
331
        if ($webAlias === null) {
332
            $webAlias = $originalAlias;
333
        }
334
335
        $this->aliases->set('@web', $webAlias);
0 ignored issues
show
Bug introduced by
It seems like $webAlias can also be of type boolean; however, parameter $path of Yiisoft\Aliases\Aliases::set() does only seem to accept null|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

335
        $this->aliases->set('@web', /** @scrutinizer ignore-type */ $webAlias);
Loading history...
336
337
        $path = $this->aliases->get($path);
338
        $view = $this->webView;
339
        $am = $this->webView->getAssetManager();
340
        $am->setAppendTimestamp($appendTimestamp);
0 ignored issues
show
Bug introduced by
It seems like $appendTimestamp can also be of type string; however, parameter $value of Yiisoft\Asset\AssetManager::setAppendTimestamp() does only seem to accept boolean, 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

340
        $am->setAppendTimestamp(/** @scrutinizer ignore-type */ $appendTimestamp);
Loading history...
341
342
        $method = 'register' . ucfirst($type) . 'File';
343
344
        $view->$method($path);
345
346
        $this->assertEquals($expected, $view->renderFile($this->aliases->get('@view/rawlayout.php')));
0 ignored issues
show
Bug introduced by
It seems like $this->aliases->get('@view/rawlayout.php') can also be of type boolean; however, parameter $viewFile of Yiisoft\View\View::renderFile() does only seem to accept 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

346
        $this->assertEquals($expected, $view->renderFile(/** @scrutinizer ignore-type */ $this->aliases->get('@view/rawlayout.php')));
Loading history...
347
    }
348
349
    /**
350
     * @param WebView $view
351
     *
352
     * @return AssetBundle
353
     */
354
    public function verifySourcesPublishedBySymlink($view): AssetBundle
355
    {
356
        $am = $view->getAssetManager();
357
358
        $bundle = TestSourceAsset::register($view);
359
        $bundle->publish($am);
360
361
        $this->assertTrue(is_dir($bundle->basePath));
362
363
        foreach ($bundle->js as $filename) {
364
            $publishedFile = $bundle->basePath . DIRECTORY_SEPARATOR . $filename;
365
            $sourceFile = $bundle->basePath . DIRECTORY_SEPARATOR . $filename;
366
367
            $this->assertTrue(is_link($bundle->basePath));
368
            $this->assertFileExists($publishedFile);
369
            $this->assertFileEquals($publishedFile, $sourceFile);
370
        }
371
372
        $this->assertTrue(FileHelper::unlink($bundle->basePath));
373
374
        return $bundle;
375
    }
376
}
377