|
8 | 8 | beforeEach(function () { |
9 | 9 | $this->compiler = new Compiler(); |
10 | 10 |
|
11 | | - $this->reflection = new ReflectionClass(Compiler::class); |
12 | | - $this->reflection->setStaticPropertyValue('cachedProcess', null); |
13 | | - $this->reflection->setStaticPropertyValue('cachedCommand', null); |
| 11 | + $reflection = new ReflectionClass(Compiler::class); |
| 12 | + $reflection->setStaticPropertyValue('cachedProcess', null); |
| 13 | + $reflection->setStaticPropertyValue('cachedCommand', null); |
14 | 14 | }); |
15 | 15 |
|
16 | 16 | it('compiles a simple SCSS string', function () { |
|
160 | 160 | $mockProcess2->shouldReceive('isRunning')->andReturn(true); |
161 | 161 |
|
162 | 162 | $mockCompiler->shouldReceive('readFile')->with($inputFile)->andReturn($scss1, $scss2); |
163 | | - $mockCompiler->shouldReceive('createProcess')->andReturn($mockProcess1, $mockProcess2); |
| 163 | + $mockCompiler->shouldReceive('getOrCreateProcess')->andReturn($mockProcess1, $mockProcess2); |
164 | 164 | $mockCompiler->shouldReceive('checkEnvironment')->andReturnNull(); |
165 | 165 | $mockCompiler->shouldReceive('findNode')->andReturn('node'); |
166 | 166 |
|
|
179 | 179 | file_put_contents($inputFile, $scss2); |
180 | 180 | touch($inputFile, 1001); |
181 | 181 |
|
182 | | - $this->reflection->setStaticPropertyValue('cachedProcess', null); |
183 | | - $this->reflection->setStaticPropertyValue('cachedCommand', null); |
184 | | - |
185 | 182 | $changed = $mockCompiler->compileFileAndSave($inputFile, $outputFile); |
186 | 183 | expect($changed)->toBeTrue(); |
187 | 184 |
|
|
523 | 520 | $compiler = mockCompiler(); |
524 | 521 | $compiler->shouldReceive('checkEnvironment')->andReturnNull(); |
525 | 522 | $compiler->shouldReceive('findNode')->andReturn('node'); |
526 | | - $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
527 | | - $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
528 | | - |
529 | | - $this->reflection->setStaticPropertyValue('cachedProcess', $mockProcess); |
530 | | - $this->reflection->setStaticPropertyValue('cachedCommand', ['node', 'bridge.js', '--stdin']); |
| 523 | + $compiler->shouldReceive('getOrCreateProcess')->andReturn($mockProcess); |
531 | 524 |
|
532 | 525 | $result1 = $compiler->compileString($scss); |
533 | | - expect($result1)->toBe($expectedCss) |
534 | | - ->and($this->reflection->getStaticPropertyValue('cachedProcess'))->toBe($mockProcess); |
| 526 | + expect($result1)->toBe($expectedCss); |
535 | 527 |
|
536 | 528 | $result2 = $compiler->compileString($scss); |
537 | 529 | expect($result2)->toBe($expectedCss); |
|
562 | 554 | expect($css)->toBe($expectedCss . "\n/*# sourceMappingURL=output.map */") |
563 | 555 | ->and(file_exists($expectedMapFile))->toBeTrue(); |
564 | 556 | }); |
| 557 | + |
| 558 | +it('compiles multiple requests using persistent mode', function () { |
| 559 | + $scss1 = '$color: red; body { color: $color; }'; |
| 560 | + $scss2 = '$color: blue; .test { color: $color; }'; |
| 561 | + $expectedCss1 = 'body{color:red}'; |
| 562 | + $expectedCss2 = '.test{color:blue}'; |
| 563 | + |
| 564 | + $mockProcess = Mockery::mock(Process::class); |
| 565 | + $mockProcess->shouldReceive('start')->once(); |
| 566 | + $mockProcess->shouldReceive('isRunning')->andReturn(true); |
| 567 | + $mockProcess->shouldReceive('setInput')->twice()->andReturnSelf(); |
| 568 | + $mockProcess->shouldReceive('run')->twice()->andReturn(0); |
| 569 | + $mockProcess->shouldReceive('getOutput')->andReturn( |
| 570 | + json_encode(['css' => $expectedCss1]) . "\n", |
| 571 | + json_encode(['css' => $expectedCss2]) . "\n" |
| 572 | + ); |
| 573 | + $mockProcess->shouldReceive('getErrorOutput')->andReturn(''); |
| 574 | + |
| 575 | + $compiler = mockCompiler(); |
| 576 | + $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
| 577 | + $compiler->shouldReceive('checkEnvironment')->andReturnNull(); |
| 578 | + $compiler->shouldReceive('findNode')->andReturn('node'); |
| 579 | + |
| 580 | + $compiler->enablePersistentMode(); |
| 581 | + |
| 582 | + $result1 = $compiler->compileInPersistentMode($scss1); |
| 583 | + $result2 = $compiler->compileInPersistentMode($scss2); |
| 584 | + |
| 585 | + expect($result1)->toBe($expectedCss1) |
| 586 | + ->and($result2)->toBe($expectedCss2); |
| 587 | +}); |
| 588 | + |
| 589 | +it('compiles in persistent mode with options', function () { |
| 590 | + $scss = '$color: green; .box { color: $color; }'; |
| 591 | + $expectedCss = '.box{color:green}'; |
| 592 | + |
| 593 | + $mockProcess = mockPersistentProcess($expectedCss); |
| 594 | + $compiler = mockPersistentCompiler($mockProcess); |
| 595 | + $compiler->enablePersistentMode(); |
| 596 | + |
| 597 | + $result = $compiler->compileInPersistentMode($scss, ['compressed' => true]); |
| 598 | + expect($result)->toBe($expectedCss); |
| 599 | +}); |
| 600 | + |
| 601 | +it('exits persistent mode properly', function () { |
| 602 | + $scss = '$color: red; body { color: $color; }'; |
| 603 | + $expectedCss = 'body{color:red}'; |
| 604 | + |
| 605 | + $mockProcess = mockPersistentProcess($expectedCss, true); |
| 606 | + $compiler = mockPersistentCompiler($mockProcess); |
| 607 | + $compiler->enablePersistentMode(); |
| 608 | + |
| 609 | + $result = $compiler->compileInPersistentMode($scss); |
| 610 | + expect($result)->toBe($expectedCss); |
| 611 | + |
| 612 | + $compiler->exitPersistentMode(); |
| 613 | +}); |
| 614 | + |
| 615 | +it('throws exception on error in persistent mode', function () { |
| 616 | + $scss = '$color: red; invalid syntax }'; |
| 617 | + $errorMessage = 'Sass parsing error: Invalid syntax'; |
| 618 | + |
| 619 | + $mockProcess = Mockery::mock(Process::class); |
| 620 | + $mockProcess->shouldReceive('start')->once(); |
| 621 | + $mockProcess->shouldReceive('isRunning')->andReturn(true); |
| 622 | + $mockProcess->shouldReceive('setInput')->once()->andReturnSelf(); |
| 623 | + $mockProcess->shouldReceive('run')->once()->andReturn(0); |
| 624 | + $mockProcess->shouldReceive('getOutput')->once()->andReturn( |
| 625 | + json_encode(['error' => $errorMessage]) . "\n" |
| 626 | + ); |
| 627 | + $mockProcess->shouldReceive('getErrorOutput')->andReturn(''); |
| 628 | + |
| 629 | + $compiler = mockCompiler(); |
| 630 | + $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
| 631 | + $compiler->shouldReceive('checkEnvironment')->andReturnNull(); |
| 632 | + $compiler->shouldReceive('findNode')->andReturn('node'); |
| 633 | + $compiler->enablePersistentMode(); |
| 634 | + |
| 635 | + expect(fn() => $compiler->compileInPersistentMode($scss)) |
| 636 | + ->toThrow(Exception::class, $errorMessage); |
| 637 | +}); |
| 638 | + |
| 639 | +it('returns empty css for empty string in persistent mode', function () { |
| 640 | + $compiler = new Compiler(); |
| 641 | + $compiler->enablePersistentMode(); |
| 642 | + |
| 643 | + $result = $compiler->compileInPersistentMode(''); |
| 644 | + expect($result)->toBe(''); |
| 645 | +}); |
| 646 | + |
| 647 | +it('compiles in persistent mode with sourceMap', function () { |
| 648 | + $scss = '$color: purple; .box { color: $color; }'; |
| 649 | + $expectedCss = '.box{color:purple}'; |
| 650 | + $expectedSourceMap = ['version' => 3, 'mappings' => '...']; |
| 651 | + |
| 652 | + $mockProcess = Mockery::mock(Process::class); |
| 653 | + $mockProcess->shouldReceive('start')->once(); |
| 654 | + $mockProcess->shouldReceive('isRunning')->andReturn(true); |
| 655 | + $mockProcess->shouldReceive('setInput')->once()->andReturnSelf(); |
| 656 | + $mockProcess->shouldReceive('run')->once()->andReturn(0); |
| 657 | + $mockProcess->shouldReceive('getOutput')->once()->andReturn( |
| 658 | + json_encode(['css' => $expectedCss, 'sourceMap' => $expectedSourceMap]) . "\n" |
| 659 | + ); |
| 660 | + $mockProcess->shouldReceive('getErrorOutput')->andReturn(''); |
| 661 | + |
| 662 | + $compiler = mockCompiler(); |
| 663 | + $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
| 664 | + $compiler->shouldReceive('checkEnvironment')->andReturnNull(); |
| 665 | + $compiler->shouldReceive('findNode')->andReturn('node'); |
| 666 | + $compiler->shouldReceive('processSourceMap')->once()->with($expectedSourceMap, [])->andReturn("\n/*# sourceMappingURL=data:application/json;base64,encoded */"); |
| 667 | + $compiler->enablePersistentMode(); |
| 668 | + |
| 669 | + $result = $compiler->compileInPersistentMode($scss); |
| 670 | + expect($result)->toBe($expectedCss . "\n/*# sourceMappingURL=data:application/json;base64,encoded */"); |
| 671 | +}); |
| 672 | + |
| 673 | +it('throws exception when persistent process fails with error output', function () { |
| 674 | + $scss = '$color: red; body { color: $color; }'; |
| 675 | + |
| 676 | + $mockProcess = Mockery::mock(Process::class); |
| 677 | + $mockProcess->shouldReceive('start')->once(); |
| 678 | + $mockProcess->shouldReceive('isRunning')->andReturn(true); |
| 679 | + $mockProcess->shouldReceive('setInput')->once()->andReturnSelf(); |
| 680 | + $mockProcess->shouldReceive('run')->once()->andReturn(0); |
| 681 | + $mockProcess->shouldReceive('getOutput')->once()->andReturn(''); |
| 682 | + $mockProcess->shouldReceive('getErrorOutput')->once()->andReturn('Compilation failed: syntax error'); |
| 683 | + |
| 684 | + $compiler = mockCompiler(); |
| 685 | + $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
| 686 | + $compiler->shouldReceive('checkEnvironment')->andReturnNull(); |
| 687 | + $compiler->shouldReceive('findNode')->andReturn('node'); |
| 688 | + $compiler->enablePersistentMode(); |
| 689 | + |
| 690 | + expect(fn() => $compiler->compileInPersistentMode($scss)) |
| 691 | + ->toThrow(Exception::class, 'Sass persistent process failed: Compilation failed: syntax error'); |
| 692 | +}); |
| 693 | + |
| 694 | +it('throws exception when persistent process returns invalid json', function () { |
| 695 | + $scss = '$color: red; body { color: $color; }'; |
| 696 | + |
| 697 | + $mockProcess = Mockery::mock(Process::class); |
| 698 | + $mockProcess->shouldReceive('start')->once(); |
| 699 | + $mockProcess->shouldReceive('isRunning')->andReturn(true); |
| 700 | + $mockProcess->shouldReceive('setInput')->once()->andReturnSelf(); |
| 701 | + $mockProcess->shouldReceive('run')->once()->andReturn(0); |
| 702 | + $mockProcess->shouldReceive('getOutput')->once()->andReturn('invalid json'); |
| 703 | + $mockProcess->shouldReceive('getErrorOutput')->andReturn(''); |
| 704 | + |
| 705 | + $compiler = mockCompiler(); |
| 706 | + $compiler->shouldReceive('createProcess')->once()->andReturn($mockProcess); |
| 707 | + $compiler->shouldReceive('checkEnvironment')->andReturnNull(); |
| 708 | + $compiler->shouldReceive('findNode')->andReturn('node'); |
| 709 | + $compiler->enablePersistentMode(); |
| 710 | + |
| 711 | + expect(fn() => $compiler->compileInPersistentMode($scss)) |
| 712 | + ->toThrow(Exception::class, 'Invalid response from sass persistent bridge'); |
| 713 | +}); |
| 714 | + |
| 715 | +it('resets cached process and command when process is not running', function () { |
| 716 | + $cmd = ['node', '--version']; |
| 717 | + |
| 718 | + $mockProcess = Mockery::mock(Process::class); |
| 719 | + $mockProcess->shouldReceive('isRunning')->andReturn(false); |
| 720 | + |
| 721 | + $reflection = new ReflectionClass(Compiler::class); |
| 722 | + $reflection->setStaticPropertyValue('cachedProcess', $mockProcess); |
| 723 | + $reflection->setStaticPropertyValue('cachedCommand', $cmd); |
| 724 | + |
| 725 | + $newMockProcess = Mockery::mock(Process::class); |
| 726 | + $newMockProcess->shouldReceive('isRunning')->andReturn(true); |
| 727 | + |
| 728 | + $compiler = Mockery::mock(Compiler::class)->makePartial(); |
| 729 | + $compiler->shouldAllowMockingProtectedMethods(); |
| 730 | + $compiler->shouldReceive('createProcess')->once()->with($cmd)->andReturn($newMockProcess); |
| 731 | + |
| 732 | + $result = $compiler->mockery_callSubjectMethod('getOrCreateProcess', [$cmd]); |
| 733 | + |
| 734 | + expect($result)->toBe($newMockProcess) |
| 735 | + ->and($reflection->getStaticPropertyValue('cachedProcess'))->toBe($newMockProcess) |
| 736 | + ->and($reflection->getStaticPropertyValue('cachedCommand'))->toBe($cmd); |
| 737 | +}); |
0 commit comments