|
129 | 129 | expect($css)->toBe($expected); |
130 | 130 | }); |
131 | 131 |
|
| 132 | +it('compiles and saves file only if source has changed', function () { |
| 133 | + $inputFile = __DIR__ . '/test_input.scss'; |
| 134 | + $outputFile = __DIR__ . '/test_output.css'; |
| 135 | + |
| 136 | + file_put_contents($inputFile, '$color: red; body { color: $color; }'); |
| 137 | + |
| 138 | + $changed = $this->compiler->compileFileAndSave($inputFile, $outputFile); |
| 139 | + expect($changed)->toBeTrue() |
| 140 | + ->and(file_exists($outputFile))->toBeTrue(); |
| 141 | + |
| 142 | + $css = file_get_contents($outputFile); |
| 143 | + expect($css)->toContain('color: red'); |
| 144 | + |
| 145 | + sleep(1); |
| 146 | + $changed = $this->compiler->compileFileAndSave($inputFile, $outputFile); |
| 147 | + expect($changed)->toBeFalse(); |
| 148 | + |
| 149 | + file_put_contents($inputFile, '$color: blue; body { color: $color; }'); |
| 150 | + $changed = $this->compiler->compileFileAndSave($inputFile, $outputFile); |
| 151 | + expect($changed)->toBeTrue(); |
| 152 | + |
| 153 | + $css = file_get_contents($outputFile); |
| 154 | + expect($css)->toContain('color: blue'); |
| 155 | + |
| 156 | + unlink($inputFile); |
| 157 | + unlink($outputFile); |
| 158 | +}); |
| 159 | + |
132 | 160 | it('returns empty css for empty string input in compileString', function () { |
133 | 161 | $css = $this->compiler->compileString(''); |
134 | 162 |
|
|
290 | 318 | $compiler->__construct(); |
291 | 319 | }); |
292 | 320 |
|
293 | | -it('compiles and saves file only if source has changed', function () { |
294 | | - $inputFile = __DIR__ . '/test_input.scss'; |
295 | | - $outputFile = __DIR__ . '/test_output.css'; |
| 321 | +it('throws Exception when input file does not exist in compileFileAndSave', function () { |
| 322 | + $this->compiler->compileFileAndSave( |
| 323 | + __DIR__ . '/nonexistent.scss', |
| 324 | + __DIR__ . '/output.css' |
| 325 | + ); |
| 326 | +})->throws(Exception::class); |
296 | 327 |
|
297 | | - file_put_contents($inputFile, '$color: red; body { color: $color; }'); |
| 328 | +it('processes source map with URL path', function () { |
| 329 | + $scss = '$color: blue; .box { color: $color; }'; |
| 330 | + $mockProcess = Mockery::mock(Process::class); |
| 331 | + $mockProcess->shouldReceive('setInput')->once()->andReturnSelf(); |
| 332 | + $mockProcess->shouldReceive('run')->once()->andReturn(0); |
| 333 | + $mockProcess->shouldReceive('getOutput')->once()->andReturn(json_encode([ |
| 334 | + 'css' => 'body{color:blue}', |
| 335 | + 'sourceMap' => ['version' => 3, 'mappings' => '...', 'sources' => ['style.scss']] |
| 336 | + ])); |
| 337 | + $mockProcess->shouldReceive('getErrorOutput')->andReturn(''); |
298 | 338 |
|
299 | | - $changed = $this->compiler->compileFileAndSave($inputFile, $outputFile); |
300 | | - expect($changed)->toBeTrue() |
301 | | - ->and(file_exists($outputFile))->toBeTrue(); |
| 339 | + $compiler = Mockery::mock(Compiler::class)->makePartial(); |
| 340 | + $compiler->shouldAllowMockingProtectedMethods(); |
| 341 | + $reflection = new ReflectionClass($compiler); |
| 342 | + $reflection->getProperty('nodePath')->setValue($compiler, 'node'); |
| 343 | + $reflection->getProperty('bridgePath')->setValue($compiler, __DIR__ . '/../bin/bridge.js'); |
| 344 | + $compiler->shouldReceive('createProcess')->andReturn($mockProcess); |
302 | 345 |
|
303 | | - $css = file_get_contents($outputFile); |
304 | | - expect($css)->toContain('color: red'); |
| 346 | + $css = $compiler->compileString($scss, ['sourceMap' => true, 'sourceMapPath' => 'https://example.com/style.map']); |
305 | 347 |
|
306 | | - sleep(1); |
307 | | - $changed = $this->compiler->compileFileAndSave($inputFile, $outputFile); |
308 | | - expect($changed)->toBeFalse(); |
| 348 | + expect($css)->toContain('/*# sourceMappingURL=https://example.com/style.map */'); |
| 349 | +}); |
309 | 350 |
|
310 | | - file_put_contents($inputFile, '$color: blue; body { color: $color; }'); |
311 | | - $changed = $this->compiler->compileFileAndSave($inputFile, $outputFile); |
312 | | - expect($changed)->toBeTrue(); |
| 351 | +it('processes source map with directory path', function () { |
| 352 | + $testDir = __DIR__ . '/test_maps'; |
| 353 | + if (! is_dir($testDir)) { |
| 354 | + mkdir($testDir); |
| 355 | + } |
313 | 356 |
|
314 | | - $css = file_get_contents($outputFile); |
315 | | - expect($css)->toContain('color: blue'); |
| 357 | + $scss = '$color: blue; .box { color: $color; }'; |
| 358 | + file_put_contents(__DIR__ . '/test.scss', $scss); |
| 359 | + $expectedMapFile = $testDir . DIRECTORY_SEPARATOR . 'style.map'; |
316 | 360 |
|
317 | | - unlink($inputFile); |
318 | | - unlink($outputFile); |
319 | | -}); |
| 361 | + $mockProcess = Mockery::mock(Process::class); |
| 362 | + $mockProcess->shouldReceive('setInput')->once()->andReturnSelf(); |
| 363 | + $mockProcess->shouldReceive('run')->once()->andReturn(0); |
| 364 | + $mockProcess->shouldReceive('getOutput')->once()->andReturn(json_encode([ |
| 365 | + 'css' => 'body{color:blue}', |
| 366 | + 'sourceMap' => ['version' => 3, 'mappings' => '...', 'sources' => ['https://example.com/style.scss']] |
| 367 | + ])); |
| 368 | + $mockProcess->shouldReceive('getErrorOutput')->andReturn(''); |
320 | 369 |
|
321 | | -it('throws Exception when input file does not exist in compileFileAndSave', function () { |
322 | | - $this->compiler->compileFileAndSave( |
323 | | - __DIR__ . '/nonexistent.scss', |
324 | | - __DIR__ . '/output.css' |
| 370 | + $compiler = Mockery::mock(Compiler::class)->makePartial(); |
| 371 | + $compiler->shouldAllowMockingProtectedMethods(); |
| 372 | + $reflection = new ReflectionClass($compiler); |
| 373 | + $reflection->getProperty('nodePath')->setValue($compiler, 'node'); |
| 374 | + $reflection->getProperty('bridgePath')->setValue($compiler, __DIR__ . '/../bin/bridge.js'); |
| 375 | + $compiler->shouldReceive('createProcess')->andReturn($mockProcess); |
| 376 | + |
| 377 | + $css = $compiler->compileFile( |
| 378 | + __DIR__ . '/test.scss', |
| 379 | + [ |
| 380 | + 'sourceMap' => true, |
| 381 | + 'sourceMapPath' => $testDir, |
| 382 | + 'url' => 'https://example.com/style.scss', |
| 383 | + ] |
325 | 384 | ); |
326 | | -})->throws(Exception::class, 'Source file not found:'); |
| 385 | + |
| 386 | + expect(file_exists($expectedMapFile))->toBeTrue() |
| 387 | + ->and($css)->toContain('/*# sourceMappingURL=style.map */'); |
| 388 | + |
| 389 | + if (file_exists($expectedMapFile)) { |
| 390 | + unlink($expectedMapFile); |
| 391 | + rmdir($testDir); |
| 392 | + } |
| 393 | + |
| 394 | + unlink(__DIR__ . '/test.scss'); |
| 395 | +}); |
| 396 | + |
| 397 | +it('extracts filename from URL in getSourceFilenameFromUrl', function () { |
| 398 | + $compiler = Mockery::mock(Compiler::class)->makePartial(); |
| 399 | + $compiler->shouldAllowMockingProtectedMethods(); |
| 400 | + |
| 401 | + expect($compiler->getSourceFilenameFromUrl(''))->toBe('style') |
| 402 | + ->and($compiler->getSourceFilenameFromUrl('https://example.com/css/style.scss'))->toBe('style') |
| 403 | + ->and($compiler->getSourceFilenameFromUrl('https://example.com/'))->toBe('style') |
| 404 | + ->and($compiler->getSourceFilenameFromUrl('file:///home/user/style.scss'))->toBe('style'); |
| 405 | +}); |
0 commit comments