Passed
Push — master ( 59ab14...8278b6 )
by Alexander
06:48
created

AssetPublisherTest::testSourceSetHashCallback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 21
nc 1
nop 0
dl 0
loc 39
c 1
b 0
f 0
cc 1
rs 9.584
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Assets\Tests;
6
7
use Yiisoft\Assets\Exception\InvalidConfigException;
8
use Yiisoft\Assets\Tests\stubs\BaseAsset;
9
use Yiisoft\Assets\Tests\stubs\JqueryAsset;
10
use Yiisoft\Assets\Tests\stubs\SourceAsset;
11
use Yiisoft\Files\FileHelper;
12
13
/**
14
 * AssetPublisherTest.
15
 */
16
final class AssetPublisherTest extends TestCase
17
{
18
    protected function tearDown(): void
19
    {
20
        parent::tearDown();
21
22
        $this->removeAssets('@basePath');
23
    }
24
25
    public function testBaseAppendtimestamp(): void
26
    {
27
        $bundle = new BaseAsset();
28
29
        $timestampCss = @filemtime($this->aliases->get($bundle->basePath) . '/' . $bundle->css[0]);
30
        $urlCss = "/baseUrl/css/basePath.css?v=$timestampCss";
31
32
        $timestampJs = @filemtime($this->aliases->get($bundle->basePath) . '/' . $bundle->js[0]);
33
        $urlJs = "/baseUrl/js/basePath.js?v=$timestampJs";
34
35
        $this->assertEmpty($this->assetManager->getAssetBundles());
36
37
        $this->publisher->setAppendTimestamp(true);
38
39
        $this->assetManager->register([BaseAsset::class]);
40
41
        $this->assertStringContainsString(
42
            $urlCss,
43
            $this->assetManager->getCssFiles()[$urlCss]['url']
44
        );
45
        $this->assertEquals(
46
            [
47
                'integrity' => 'integrity-hash',
48
                'crossorigin' => 'anonymous'
49
            ],
50
            $this->assetManager->getCssFiles()[$urlCss]['attributes']
51
        );
52
53
        $this->assertStringContainsString(
54
            $urlJs,
55
            $this->assetManager->getJsFiles()[$urlJs]['url']
56
        );
57
        $this->assertEquals(
58
            [
59
                'integrity' => 'integrity-hash',
60
                'crossorigin' => 'anonymous',
61
                'position' => 3
62
            ],
63
            $this->assetManager->getJsFiles()[$urlJs]['attributes']
64
        );
65
    }
66
67
    public function testPublisherSetAssetMap(): void
68
    {
69
        $urlJs = '//testme.css';
70
71
        $this->publisher->setAssetMap(
72
            [
73
                'jquery.js' => $urlJs,
74
            ]
75
        );
76
77
        $this->assertEmpty($this->assetManager->getAssetBundles());
78
79
        $this->assetManager->register([JqueryAsset::class]);
80
81
        $this->assertStringContainsString(
82
            $urlJs,
83
            $this->assetManager->getJsFiles()[$urlJs]['url']
84
        );
85
        $this->assertEquals(
86
            [
87
                'position' => 3
88
            ],
89
            $this->assetManager->getJsFiles()[$urlJs]['attributes']
90
        );
91
    }
92
93
    /**
94
     * @return array
95
     */
96
    public function registerFileDataProvider(): array
97
    {
98
        return [
99
            // Custom alias repeats in the asset URL
100
            [
101
                'css', '@web/assetSources/repeat/css/stub.css', false,
102
                '/repeat/assetSources/repeat/css/stub.css',
103
                '/repeat',
104
            ],
105
            [
106
                'js', '@web/assetSources/repeat/js/jquery.js', false,
107
                '/repeat/assetSources/repeat/js/jquery.js',
108
                '/repeat',
109
            ],
110
            // JS files registration
111
            [
112
                'js', '@web/assetSources/js/missing-file.js', true,
113
                '/baseUrl/assetSources/js/missing-file.js'
114
            ],
115
            [
116
                'js', '@web/assetSources/js/jquery.js', false,
117
                '/baseUrl/assetSources/js/jquery.js',
118
            ],
119
            [
120
                'js', 'http://example.com/assetSources/js/jquery.js', false,
121
                'http://example.com/assetSources/js/jquery.js',
122
            ],
123
            [
124
                'js', '//example.com/assetSources/js/jquery.js', false,
125
                '//example.com/assetSources/js/jquery.js',
126
            ],
127
            [
128
                'js', 'assetSources/js/jquery.js', false,
129
                'assetSources/js/jquery.js',
130
            ],
131
            [
132
                'js', '/assetSources/js/jquery.js', false,
133
                '/assetSources/js/jquery.js',
134
            ],
135
            // CSS file registration
136
            [
137
                'css', '@web/assetSources/css/missing-file.css', true,
138
                '/baseUrl/assetSources/css/missing-file.css',
139
            ],
140
            [
141
                'css', '@web/assetSources/css/stub.css', false,
142
                '/baseUrl/assetSources/css/stub.css',
143
            ],
144
            [
145
                'css', 'http://example.com/assetSources/css/stub.css', false,
146
                'http://example.com/assetSources/css/stub.css',
147
            ],
148
            [
149
                'css', '//example.com/assetSources/css/stub.css', false,
150
                '//example.com/assetSources/css/stub.css',
151
            ],
152
            [
153
                'css', 'assetSources/css/stub.css', false,
154
                'assetSources/css/stub.css',
155
            ],
156
            [
157
                'css', '/assetSources/css/stub.css', false,
158
                '/assetSources/css/stub.css',
159
            ],
160
            // Custom `@web` aliases
161
            [
162
                'js', '@web/assetSources/js/missing-file1.js', true,
163
                '/backend/assetSources/js/missing-file1.js',
164
                '/backend',
165
            ],
166
            [
167
                'js', 'http://full-url.example.com/backend/assetSources/js/missing-file.js', true,
168
                'http://full-url.example.com/backend/assetSources/js/missing-file.js',
169
                '/backend',
170
            ],
171
            [
172
                'css', '//backend/backend/assetSources/js/missing-file.js', true,
173
                '//backend/backend/assetSources/js/missing-file.js',
174
                '/backend',
175
            ],
176
            [
177
                'css', '@web/assetSources/css/stub.css', false,
178
                '/en/blog/backend/assetSources/css/stub.css',
179
                '/en/blog/backend',
180
            ],
181
            // UTF-8 chars
182
            [
183
                'css', '@web/assetSources/css/stub.css', false,
184
                '/рус/сайт/assetSources/css/stub.css',
185
                '/рус/сайт',
186
            ],
187
            [
188
                'js', '@web/assetSources/js/jquery.js', false,
189
                '/汉语/漢語/assetSources/js/jquery.js',
190
                '/汉语/漢語',
191
            ],
192
        ];
193
    }
194
195
    /**
196
     * @dataProvider registerFileDataProvider
197
     *
198
     * @param string $type either `js` or `css`
199
     * @param string $path
200
     * @param bool $appendTimestamp
201
     * @param string $expected
202
     * @param string|null $webAlias
203
     */
204
    public function testRegisterFileAppendTimestamp(
205
        string $type,
206
        string $path,
207
        bool $appendTimestamp,
208
        string $expected,
209
        ?string $webAlias = null
210
    ): void {
211
        $originalAlias = $this->aliases->get('@web');
212
213
        if ($webAlias === null) {
214
            $webAlias = $originalAlias;
215
        }
216
217
        $this->aliases->set('@web', $webAlias);
218
219
        $path = $this->aliases->get($path);
220
221
        $this->publisher->setAppendTimestamp($appendTimestamp);
222
223
        $method = 'register' . ucfirst($type) . 'File';
224
225
        $this->assetManager->$method($path, [], null);
226
227
        $this->assertStringContainsString(
228
            $expected,
229
            $type === 'css' ? $this->assetManager->getCssFiles()[$expected]['url']
230
                : $this->assetManager->getJsFiles()[$expected]['url']
231
        );
232
    }
233
234
    public function testSourcesCssJsDefaultOptions(): void
235
    {
236
        $bundle = new SourceAsset();
237
238
        $sourcePath = $this->aliases->get($bundle->sourcePath);
239
        $path = (is_file($sourcePath) ? \dirname($sourcePath) : $sourcePath) . @filemtime($sourcePath);
240
        $hash = sprintf('%x', crc32($path . '|' . $this->publisher->getLinkAssets()));
241
242
        $this->publisher->setCssDefaultOptions([
243
            'media' => 'none'
244
        ]);
245
246
        $this->publisher->setJsDefaultOptions([
247
            'position' => 2
248
        ]);
249
250
        $this->assertEmpty($this->assetManager->getAssetBundles());
251
252
        $this->assetManager->register([SourceAsset::class]);
253
254
        $this->assertEquals(
255
            [
256
                'media' => 'none'
257
            ],
258
            $this->assetManager->getCssFiles()["/baseUrl/$hash/css/stub.css"]['attributes']
259
        );
260
        $this->assertEquals(
261
            [
262
                'position' => 2
263
            ],
264
            $this->assetManager->getJsFiles()['/js/jquery.js']['attributes']
265
        );
266
        $this->assertEquals(
267
            [
268
                'position' => 2
269
            ],
270
            $this->assetManager->getJsFiles()["/baseUrl/$hash/js/stub.js"]['attributes']
271
        );
272
    }
273
274
    public function testSourceSetHashCallback(): void
275
    {
276
        $this->publisher->setHashCallback(function () {
277
            return 'HashCallback';
278
        });
279
280
        $this->assertEmpty($this->assetManager->getAssetBundles());
281
282
        $this->assetManager->register([SourceAsset::class]);
283
284
        $this->assertStringContainsString(
285
            '/baseUrl/HashCallback/css/stub.css',
286
            $this->assetManager->getCssFiles()['/baseUrl/HashCallback/css/stub.css']['url']
287
        );
288
        $this->assertEquals(
289
            [],
290
            $this->assetManager->getCssFiles()['/baseUrl/HashCallback/css/stub.css']['attributes']
291
        );
292
293
        $this->assertStringContainsString(
294
            '/js/jquery.js',
295
            $this->assetManager->getJsFiles()['/js/jquery.js']['url']
296
        );
297
        $this->assertEquals(
298
            [
299
                'position' => 3
300
            ],
301
            $this->assetManager->getJsFiles()['/js/jquery.js']['attributes']
302
        );
303
304
        $this->assertStringContainsString(
305
            '/baseUrl/HashCallback/js/stub.js',
306
            $this->assetManager->getJsFiles()['/baseUrl/HashCallback/js/stub.js']['url']
307
        );
308
        $this->assertEquals(
309
            [
310
                'position' => 3
311
            ],
312
            $this->assetManager->getJsFiles()['/baseUrl/HashCallback/js/stub.js']['attributes']
313
        );
314
    }
315
316
    public function testSourcesPublishOptionsOnlyRegex(): void
317
    {
318
        $bundle = new SourceAsset();
319
320
        $bundle->publishOptions = [
321
            'only' => [
322
                'js/*'
323
            ],
324
        ];
325
326
        [$bundle->basePath, $bundle->baseUrl] = $this->publisher->publish($bundle);
327
328
        $notNeededFilesDir = dirname($bundle->basePath . DIRECTORY_SEPARATOR . $bundle->css[0]);
329
330
        $this->assertFileNotExists($notNeededFilesDir);
331
332
        foreach ($bundle->js as $filename) {
333
            $publishedFile = $bundle->basePath . DIRECTORY_SEPARATOR . $filename;
334
335
            $this->assertFileExists($publishedFile);
336
        }
337
338
        $this->assertDirectoryExists(dirname($bundle->basePath . DIRECTORY_SEPARATOR . $bundle->js[0]));
339
        $this->assertDirectoryExists($bundle->basePath);
340
    }
341
342
    public function testSourcesPathException(): void
343
    {
344
        $bundle = new SourceAsset();
345
346
        $bundle->sourcePath = '/wrong';
347
348
        $message = "The sourcePath to be published does not exist: $bundle->sourcePath";
349
350
        $this->expectException(InvalidConfigException::class);
351
        $this->expectExceptionMessage($message);
352
353
        $this->publisher->publish($bundle);
354
    }
355
356
    public function testSourcesPublishedBySymlinkIssue9333(): void
357
    {
358
        $this->publisher->setLinkAssets(true);
359
360
        $this->publisher->setHashCallback(
361
            static function ($path) {
362
                return sprintf('%x/%x', crc32($path), crc32('3.0-dev'));
363
            }
364
        );
365
366
        $bundle = $this->verifySourcesPublishedBySymlink();
367
368
        $this->assertDirectoryExists(dirname($bundle->basePath));
369
    }
370
371
    private function verifySourcesPublishedBySymlink(): SourceAsset
372
    {
373
        $bundle = new SourceAsset();
374
375
        [$bundle->basePath, $bundle->baseUrl] = $this->publisher->publish($bundle);
376
377
        $this->assertDirectoryExists($bundle->basePath);
378
379
        foreach ($bundle->js as $filename) {
380
            $publishedFile = $bundle->basePath . DIRECTORY_SEPARATOR . $filename;
381
            $sourceFile = $bundle->basePath . DIRECTORY_SEPARATOR . $filename;
382
383
            $this->assertTrue(is_link($bundle->basePath));
384
            $this->assertFileExists($publishedFile);
385
            $this->assertFileEquals($publishedFile, $sourceFile);
386
        }
387
388
        $this->assertTrue(FileHelper::unlink($bundle->basePath));
389
390
        return $bundle;
391
    }
392
}
393