From 3fc283d19fbb9ddf5cc87f6518a0fe5153b3a6b6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 27 Nov 2023 20:59:05 +0100 Subject: [PATCH 1/7] Add serve command option to open site in the browser --- .../src/Console/Commands/ServeCommand.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/framework/src/Console/Commands/ServeCommand.php b/packages/framework/src/Console/Commands/ServeCommand.php index a576864477e..57a56059e23 100644 --- a/packages/framework/src/Console/Commands/ServeCommand.php +++ b/packages/framework/src/Console/Commands/ServeCommand.php @@ -12,6 +12,8 @@ use Hyde\Console\Concerns\Command; use Hyde\RealtimeCompiler\ConsoleOutput; use Illuminate\Support\Facades\Process; +use Symfony\Component\Process\Process as SymfonyProcess; +use Symfony\Component\Process\Exception\ProcessFailedException; use function sprintf; use function class_exists; @@ -31,6 +33,7 @@ class ServeCommand extends Command {--dashboard= : Enable the realtime compiler dashboard. (Overrides config setting)} {--pretty-urls= : Enable pretty URLs. (Overrides config setting)} {--play-cdn= : Enable the Tailwind Play CDN. (Overrides config setting)} + {--open : Open the site preview in the browser.} '; /** @var string */ @@ -43,6 +46,10 @@ public function safeHandle(): int $this->configureOutput(); $this->printStartMessage(); + if ($this->option('open')) { + $this->openInBrowser(); + } + $this->runServerProcess(sprintf('php -S %s:%d %s', $this->getHostSelection(), $this->getPortSelection(), @@ -135,4 +142,23 @@ protected function checkArgvForOption(string $name): ?string return null; } + + protected function openInBrowser(): void + { + $command = match (PHP_OS_FAMILY) { + 'Windows' => 'start', + 'Darwin' => 'open', + 'Linux' => 'xdg-open', + default => null + }; + + try { + SymfonyProcess::fromShellCommandline(sprintf('%s http://%s:%d', $command, $this->getHostSelection(), $this->getPortSelection()))->mustRun(); + } catch (ProcessFailedException $exception) { + $this->warn("Unable to open the site preview in the browser on your system.\n"); + if ($this->output->isVerbose()) { + $this->line($exception->getMessage()); + } + } + } } From 5e40f9b2473794996d09c22c1b1ceaa64b50909a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 28 Nov 2023 12:26:22 +0100 Subject: [PATCH 2/7] Refactor to use testable Illuminate process --- .../src/Console/Commands/ServeCommand.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/framework/src/Console/Commands/ServeCommand.php b/packages/framework/src/Console/Commands/ServeCommand.php index 57a56059e23..fa92220cda9 100644 --- a/packages/framework/src/Console/Commands/ServeCommand.php +++ b/packages/framework/src/Console/Commands/ServeCommand.php @@ -12,10 +12,9 @@ use Hyde\Console\Concerns\Command; use Hyde\RealtimeCompiler\ConsoleOutput; use Illuminate\Support\Facades\Process; -use Symfony\Component\Process\Process as SymfonyProcess; -use Symfony\Component\Process\Exception\ProcessFailedException; use function sprintf; +use function str_replace; use function class_exists; /** @@ -152,13 +151,13 @@ protected function openInBrowser(): void default => null }; - try { - SymfonyProcess::fromShellCommandline(sprintf('%s http://%s:%d', $command, $this->getHostSelection(), $this->getPortSelection()))->mustRun(); - } catch (ProcessFailedException $exception) { - $this->warn("Unable to open the site preview in the browser on your system.\n"); - if ($this->output->isVerbose()) { - $this->line($exception->getMessage()); - } + $process = Process::command(sprintf('%s http://%s:%d', $command, $this->getHostSelection(), $this->getPortSelection()))->run(); + + if ($process->failed()) { + $this->warn('Unable to open the site preview in the browser on your system:'); + $this->line(sprintf(' %s', str_replace("\n", "\n ", $process->errorOutput()))); } + + $this->newLine(); } } From ae76d1d6c7311854ed9f2c11c087057590bcab50 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 28 Nov 2023 12:33:02 +0100 Subject: [PATCH 3/7] Show specific error when missing open binary --- packages/framework/src/Console/Commands/ServeCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/framework/src/Console/Commands/ServeCommand.php b/packages/framework/src/Console/Commands/ServeCommand.php index fa92220cda9..7bf82cdb48b 100644 --- a/packages/framework/src/Console/Commands/ServeCommand.php +++ b/packages/framework/src/Console/Commands/ServeCommand.php @@ -151,11 +151,11 @@ protected function openInBrowser(): void default => null }; - $process = Process::command(sprintf('%s http://%s:%d', $command, $this->getHostSelection(), $this->getPortSelection()))->run(); + $process = $command ? Process::command(sprintf('%s http://%s:%d', $command, $this->getHostSelection(), $this->getPortSelection()))->run() : null; - if ($process->failed()) { + if (! $process || $process->failed()) { $this->warn('Unable to open the site preview in the browser on your system:'); - $this->line(sprintf(' %s', str_replace("\n", "\n ", $process->errorOutput()))); + $this->line(sprintf(' %s', str_replace("\n", "\n ", $process ? $process->errorOutput() : "Missing suitable 'open' binary."))); } $this->newLine(); From 8991a3db5c25f827649a5576855a5f592e26bef3 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 28 Nov 2023 13:15:28 +0100 Subject: [PATCH 4/7] Move up newline --- packages/framework/src/Console/Commands/ServeCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/framework/src/Console/Commands/ServeCommand.php b/packages/framework/src/Console/Commands/ServeCommand.php index 7bf82cdb48b..9558fa4f53b 100644 --- a/packages/framework/src/Console/Commands/ServeCommand.php +++ b/packages/framework/src/Console/Commands/ServeCommand.php @@ -156,8 +156,7 @@ protected function openInBrowser(): void if (! $process || $process->failed()) { $this->warn('Unable to open the site preview in the browser on your system:'); $this->line(sprintf(' %s', str_replace("\n", "\n ", $process ? $process->errorOutput() : "Missing suitable 'open' binary."))); + $this->newLine(); } - - $this->newLine(); } } From d1b4e2883d78b58f5e1858c765411e838c6961bb Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 28 Nov 2023 16:24:15 +0100 Subject: [PATCH 5/7] Unit test opener --- .../Unit/ServeCommandOptionsUnitTest.php | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php b/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php index 935d9793198..ae80ed40361 100644 --- a/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php +++ b/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php @@ -4,8 +4,13 @@ namespace Hyde\Framework\Testing\Unit; +use Mockery; use Hyde\Testing\UnitTestCase; +use Illuminate\Console\OutputStyle; use Hyde\Console\Commands\ServeCommand; +use Illuminate\Support\Facades\Process; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; /** * @covers \Hyde\Console\Commands\ServeCommand @@ -196,6 +201,60 @@ public function test_checkArgvForOption() $_SERVER = $serverBackup; } + public function testWithOpenArgument() + { + $output = $this->createMock(OutputStyle::class); + $output->expects($this->never())->method('writeln'); + + $command = $this->getMock(['--open' => true]); + $command->setOutput($output); + + $binary = match (PHP_OS_FAMILY) { + 'Darwin' => 'open', + 'Windows' => 'start', + default => 'xdg-open', + }; + + Process::shouldReceive('command')->once()->with("$binary http://localhost:8080")->andReturnSelf(); + Process::shouldReceive('run')->once()->andReturnSelf(); + Process::shouldReceive('failed')->once()->andReturn(false); + + $command->openInBrowser(); + } + + public function testWithOpenArgumentThatFails() + { + $output = Mockery::mock(OutputStyle::class); + $output->shouldReceive('getFormatter')->andReturn($this->createMock(OutputFormatterInterface::class)); + + $warning = 'Unable to open the site preview in the browser on your system:'; + $context = ' Missing suitable \'open\' binary.'; + + $output->shouldReceive('writeln')->once()->with($warning, OutputInterface::VERBOSITY_NORMAL); + $output->shouldReceive('writeln')->once()->with($context, OutputInterface::VERBOSITY_NORMAL); + $output->shouldReceive('newLine')->once(); + + $command = $this->getMock(['--open' => true]); + $command->setOutput($output); + + $binary = match (PHP_OS_FAMILY) { + 'Darwin' => 'open', + 'Windows' => 'start', + default => 'xdg-open', + }; + + Process::shouldReceive('command')->once()->with("$binary http://localhost:8080")->andReturnSelf(); + Process::shouldReceive('run')->once()->andReturnSelf(); + Process::shouldReceive('failed')->once()->andReturn(true); + Process::shouldReceive('errorOutput')->once()->andReturn("Missing suitable 'open' binary."); + + $command->openInBrowser(); + + Mockery::close(); + + $this->assertTrue(true); + } + protected function getMock(array $options = []): ServeCommandMock { return new ServeCommandMock($options); @@ -208,6 +267,7 @@ protected function getMock(array $options = []): ServeCommandMock * @method getEnvironmentVariables * @method parseEnvironmentOption(string $name) * @method checkArgvForOption(string $name) + * @method openInBrowser() */ class ServeCommandMock extends ServeCommand { From b79704fc42b120b49965248e70ac04312c3cf8c1 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 28 Nov 2023 18:15:25 +0100 Subject: [PATCH 6/7] Unit test argument logic --- .../Unit/ServeCommandOptionsUnitTest.php | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php b/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php index ae80ed40361..9b73aa63aa0 100644 --- a/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php +++ b/packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php @@ -6,6 +6,7 @@ use Mockery; use Hyde\Testing\UnitTestCase; +use Hyde\Foundation\HydeKernel; use Illuminate\Console\OutputStyle; use Hyde\Console\Commands\ServeCommand; use Illuminate\Support\Facades\Process; @@ -202,6 +203,37 @@ public function test_checkArgvForOption() } public function testWithOpenArgument() + { + HydeKernel::setInstance(new HydeKernel()); + + $command = new class(['open' => true]) extends ServeCommandMock + { + public bool $openInBrowserCalled = false; + + // Void unrelated methods + protected function configureOutput(): void + { + } + + protected function printStartMessage(): void + { + } + + protected function runServerProcess(string $command): void + { + } + + protected function openInBrowser(): void + { + $this->openInBrowserCalled = true; + } + }; + + $command->safeHandle(); + $this->assertTrue($command->openInBrowserCalled); + } + + public function test_openInBrowser() { $output = $this->createMock(OutputStyle::class); $output->expects($this->never())->method('writeln'); @@ -222,7 +254,7 @@ public function testWithOpenArgument() $command->openInBrowser(); } - public function testWithOpenArgumentThatFails() + public function test_openInBrowserThatFails() { $output = Mockery::mock(OutputStyle::class); $output->shouldReceive('getFormatter')->andReturn($this->createMock(OutputFormatterInterface::class)); From d533907730b5005173b7e620ea84608dcb3d1882 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 28 Nov 2023 19:10:35 +0100 Subject: [PATCH 7/7] Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7309c20bf19..765af6aec29 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -13,6 +13,7 @@ This serves two purposes: - Adds a new fancy output for the realtime compiler serve command in https://github.com/hydephp/develop/pull/1444 - Added support for dot notation in the Yaml configuration files in https://github.com/hydephp/develop/pull/1478 - Added a config option to customize automatic sidebar navigation group names in https://github.com/hydephp/develop/pull/1481 +- Added a new `hyde serve --open` option to automatically open the site in the browser in https://github.com/hydephp/develop/pull/1483 ### Changed - The `docs.sidebar.footer` config option now accepts a Markdown string to replace the default footer in https://github.com/hydephp/develop/pull/1477