From d51337691ddfcacac87fd9d57456006f1f5a306e Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 6 Nov 2023 18:50:04 +0000 Subject: [PATCH 01/23] Merge pull request #1431 from hydephp/navigation-discovery-improvements Improve navigation priority discovery to allow both sidebars and main navigation to support both list and keyed config syntax https://github.com/hydephp/develop/commit/ed14ee3dcf561320db9621f751e478aec1da4c13 --- .../Factories/NavigationDataFactory.php | 41 +++-- tests/Unit/NavigationDataFactoryUnitTest.php | 148 ++++++++++++++++++ 2 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 tests/Unit/NavigationDataFactoryUnitTest.php diff --git a/src/Framework/Factories/NavigationDataFactory.php b/src/Framework/Factories/NavigationDataFactory.php index dd6d9a8f..44dcf51b 100644 --- a/src/Framework/Factories/NavigationDataFactory.php +++ b/src/Framework/Factories/NavigationDataFactory.php @@ -12,9 +12,11 @@ use Hyde\Framework\Factories\Concerns\CoreDataObject; use Hyde\Markdown\Contracts\FrontMatter\SubSchemas\NavigationSchema; +use function basename; use function array_flip; use function in_array; use function is_a; +use function array_key_exists; /** * Discover data used for navigation menus and the documentation sidebar. @@ -157,31 +159,44 @@ private function searchForPriorityInConfigs(): ?int private function searchForPriorityInSidebarConfig(): ?int { - // Sidebars uses a special syntax where the keys are just the page identifiers in a flat array. - // TODO: In the future we could normalize this with the standard navigation config so both strategies can be auto-detected and used. - - // Adding an offset makes so that pages with a front matter priority that is lower can be shown first. - // This is all to make it easier to mix ways of adding priorities. - - /** @var array $config */ + /** @var array|array $config */ $config = Config::getArray('docs.sidebar_order', []); - return $this->offset( - array_flip($config)[$this->identifier] ?? null, - self::CONFIG_OFFSET - ); + return $this->parseNavigationPriorityConfig($config); } private function searchForPriorityInNavigationConfig(): ?int { - /** @var array $config */ + /** @var array|array $config */ $config = Config::getArray('hyde.navigation.order', [ 'index' => 0, 'posts' => 10, 'docs/index' => 100, ]); - return $config[$this->routeKey] ?? null; + return $this->parseNavigationPriorityConfig($config); + } + + /** @param array|array $config */ + private function parseNavigationPriorityConfig(array $config): ?int + { + $pageKey = $this->isInstanceOf(DocumentationPage::class) + ? $this->identifier // Required for backwards compatibility. + : $this->routeKey; + + // Check if the config entry is a flat array or a keyed array. + if (! array_key_exists($pageKey, $config)) { + // Adding an offset makes so that pages with a front matter priority, or + // explicit keyed priority selection that is lower can be shown first. + // This is all to make it easier to mix ways of adding priorities. + + return $this->offset( + array_flip($config)[$pageKey] ?? null, + self::CONFIG_OFFSET + ); + } + + return $config[$pageKey] ?? null; } private function canUseSubdirectoryForGroups(): bool diff --git a/tests/Unit/NavigationDataFactoryUnitTest.php b/tests/Unit/NavigationDataFactoryUnitTest.php new file mode 100644 index 00000000..539d080f --- /dev/null +++ b/tests/Unit/NavigationDataFactoryUnitTest.php @@ -0,0 +1,148 @@ + [ + 'foo' => 15, + 'bar' => 10, + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'foo')); + $this->assertSame(15, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'bar')); + $this->assertSame(10, $factory->makePriority()); + } + + public function testSearchForPriorityInNavigationConfigForMarkdownPageWithListConfig() + { + self::mockConfig(['hyde.navigation.order' => [ + 'foo', + 'bar', + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'foo')); + $this->assertSame(500, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'bar')); + $this->assertSame(501, $factory->makePriority()); + } + + public function testSearchForPriorityInNavigationConfigForMarkdownPageSupportsMixingKeyedAndListConfig() + { + self::mockConfig(['hyde.navigation.order' => [ + 'foo', + 'bar' => 10, + 'baz', + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'foo')); + $this->assertSame(500, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'bar')); + $this->assertSame(10, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'baz')); + $this->assertSame(501, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject(routeKey: 'qux')); + $this->assertSame(999, $factory->makePriority()); + } + + public function testSearchForPriorityInNavigationConfigForDocumentationPageWithListConfig() + { + self::mockConfig(['docs.sidebar_order' => [ + 'foo' => 15, + 'bar' => 10, + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('foo', pageClass: DocumentationPage::class)); + $this->assertSame(15, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('bar', pageClass: DocumentationPage::class)); + $this->assertSame(10, $factory->makePriority()); + } + + public function testSearchForPriorityInNavigationConfigForDocumentationPageWithKeyedConfig() + { + self::mockConfig(['docs.sidebar_order' => [ + 'foo', + 'bar' => 10, + 'baz', + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('foo', pageClass: DocumentationPage::class)); + $this->assertSame(500, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('bar', pageClass: DocumentationPage::class)); + $this->assertSame(10, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('baz', pageClass: DocumentationPage::class)); + $this->assertSame(501, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('qux', pageClass: DocumentationPage::class)); + $this->assertSame(999, $factory->makePriority()); + } + + public function testSearchForPriorityInNavigationConfigForDocumentationPageSupportsMixingKeyedAndListConfig() + { + self::mockConfig(['docs.sidebar_order' => [ + 'foo', + 'bar' => 10, + 'baz', + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('foo', pageClass: DocumentationPage::class)); + $this->assertSame(500, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('bar', pageClass: DocumentationPage::class)); + $this->assertSame(10, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('baz', pageClass: DocumentationPage::class)); + $this->assertSame(501, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('qux', pageClass: DocumentationPage::class)); + $this->assertSame(999, $factory->makePriority()); + } + + protected function makeCoreDataObject(string $identifier = '', string $routeKey = '', string $pageClass = MarkdownPage::class): CoreDataObject + { + return new CoreDataObject(new FrontMatter(), new Markdown(), $pageClass, $identifier, '', '', $routeKey); + } +} + +class NavigationConfigTestClass extends NavigationDataFactory +{ + public function __construct(CoreDataObject $pageData) + { + parent::__construct($pageData, ''); + } + + public function makePriority(): int + { + return parent::makePriority(); + } +} From 3692696ceb87dcd0e6fdeba774fcb8e97536e2eb Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 7 Nov 2023 10:13:34 +0000 Subject: [PATCH 02/23] Merge pull request #1432 from hydephp/normalize-navigation-menu-configuration-options Support both route keys and identifiers for specifying sidebar order https://github.com/hydephp/develop/commit/cf88506039c01ddeda9b74848f16292df36c854e --- config/docs.php | 7 ++++--- config/hyde.php | 12 ++++++++---- .../Factories/NavigationDataFactory.php | 15 +++++++++------ tests/Unit/NavigationDataFactoryUnitTest.php | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/config/docs.php b/config/docs.php index c26d702e..fe9cf452 100644 --- a/config/docs.php +++ b/config/docs.php @@ -43,10 +43,11 @@ | default to sort alphabetically. You can reorder the page identifiers | in the list below, and the links will get sorted in that order. | - | Internally, the items listed will get a position priority of 500 + the order its found in the list. - | Link items without an entry here will have fall back to the default priority of 999, putting them last. + | The items will get a priority of 500 plus the order its found in the list. + | Pages without a priority will fall back to the default priority of 999. | - | You can also set explicit priorities in front matter. + | You can also set explicit priorities in front matter or by specifying + | a value to the array key in the list to override the inferred value. | */ diff --git a/config/hyde.php b/config/hyde.php index f9126cb3..0f83b803 100644 --- a/config/hyde.php +++ b/config/hyde.php @@ -300,13 +300,17 @@ | | If you are looking to customize the main navigation menu, this is the place! | + | All these settings uses Route Keys to identify the page you want to configure. + | A route key is simply the URL path to the page, without the file extension. + | So `_site/posts/hello-world.html` has the route key 'posts/hello-world'. + | */ 'navigation' => [ // This configuration sets the priorities used to determine the order of the menu. // The default values have been added below for reference and easy editing. - // The array key should match the page's route key (slug). - // Lower values show up first in the menu. + // The array key is the page's route key, the value is the priority. + // Lower values show up first in the menu. The default is 999. 'order' => [ 'index' => 0, 'posts' => 10, @@ -314,13 +318,13 @@ ], // In case you want to customize the labels for the menu items, you can do so here. - // Simply add the route key (slug) as the key, and the label as the value. + // Simply add the route key as the array key, and the label as the value. 'labels' => [ 'index' => 'Home', 'docs/index' => 'Docs', ], - // These are the pages that should not show up in the navigation menu. + // These are the route keys of pages that should not show up in the navigation menu. 'exclude' => [ '404', ], diff --git a/src/Framework/Factories/NavigationDataFactory.php b/src/Framework/Factories/NavigationDataFactory.php index 44dcf51b..d9406d6f 100644 --- a/src/Framework/Factories/NavigationDataFactory.php +++ b/src/Framework/Factories/NavigationDataFactory.php @@ -162,7 +162,12 @@ private function searchForPriorityInSidebarConfig(): ?int /** @var array|array $config */ $config = Config::getArray('docs.sidebar_order', []); - return $this->parseNavigationPriorityConfig($config); + return + // For consistency with the navigation config. + $this->parseNavigationPriorityConfig($config, 'routeKey') + // For backwards compatibility, and ease of use, as the route key prefix + // is redundant due to it being the same for all documentation pages + ?? $this->parseNavigationPriorityConfig($config, 'identifier'); } private function searchForPriorityInNavigationConfig(): ?int @@ -174,15 +179,13 @@ private function searchForPriorityInNavigationConfig(): ?int 'docs/index' => 100, ]); - return $this->parseNavigationPriorityConfig($config); + return $this->parseNavigationPriorityConfig($config, 'routeKey'); } /** @param array|array $config */ - private function parseNavigationPriorityConfig(array $config): ?int + private function parseNavigationPriorityConfig(array $config, string $pageKeyName): ?int { - $pageKey = $this->isInstanceOf(DocumentationPage::class) - ? $this->identifier // Required for backwards compatibility. - : $this->routeKey; + $pageKey = $this->{$pageKeyName}; // Check if the config entry is a flat array or a keyed array. if (! array_key_exists($pageKey, $config)) { diff --git a/tests/Unit/NavigationDataFactoryUnitTest.php b/tests/Unit/NavigationDataFactoryUnitTest.php index 539d080f..a118c695 100644 --- a/tests/Unit/NavigationDataFactoryUnitTest.php +++ b/tests/Unit/NavigationDataFactoryUnitTest.php @@ -128,6 +128,24 @@ public function testSearchForPriorityInNavigationConfigForDocumentationPageSuppo $this->assertSame(999, $factory->makePriority()); } + public function testRouteKeysCanBeUsedForDocumentationSidebarPriorities() + { + self::mockConfig(['docs.sidebar_order' => [ + 'key/foo', + 'key/bar', + 'baz', + ]]); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('foo', routeKey: 'key/foo', pageClass: DocumentationPage::class)); + $this->assertSame(500, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('bar', routeKey: 'key/bar', pageClass: DocumentationPage::class)); + $this->assertSame(501, $factory->makePriority()); + + $factory = new NavigationConfigTestClass($this->makeCoreDataObject('baz', routeKey: 'key', pageClass: DocumentationPage::class)); + $this->assertSame(502, $factory->makePriority()); + } + protected function makeCoreDataObject(string $identifier = '', string $routeKey = '', string $pageClass = MarkdownPage::class): CoreDataObject { return new CoreDataObject(new FrontMatter(), new Markdown(), $pageClass, $identifier, '', '', $routeKey); From c1852df5d71cbf01207ebfe699c885cf96c227ad Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 8 Nov 2023 13:01:48 +0000 Subject: [PATCH 03/23] Merge pull request #1435 from hydephp/update-internal-testing-scripts Update internal testing scripts https://github.com/hydephp/develop/commit/1478d04ca48bcb25e2eca03659c0635f4e4d38ac --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index db63d350..b8c54efe 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: shivammathur/setup-php@a36e1e52ff4a1c9e9c9be31551ee4712a6cb6bd0 + - uses: shivammathur/setup-php@16011a795d747d5f45038f96371c3b98aec5669d with: php-version: ${{ matrix.php }} extensions: fileinfo, zip From 0a6c94431425972ad4167726e5401b839b1c65eb Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 8 Nov 2023 13:04:50 +0000 Subject: [PATCH 04/23] Merge pull request #1437 from hydephp/sync-downstream-repositories Sync back with downstream repositories https://github.com/hydephp/develop/commit/1e5c2d3d59dac788b51ceed05f44c74186414de8 --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b8c54efe..db63d350 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: shivammathur/setup-php@16011a795d747d5f45038f96371c3b98aec5669d + - uses: shivammathur/setup-php@a36e1e52ff4a1c9e9c9be31551ee4712a6cb6bd0 with: php-version: ${{ matrix.php }} extensions: fileinfo, zip From b177e63418f40c9b3626f81a75a9f6da34e307af Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 8 Nov 2023 13:11:51 +0000 Subject: [PATCH 05/23] Merge pull request #1438 from hydephp/dynamic-setup-php-action-version-for-test-runners Use dynamic PHP setup action version for test runners https://github.com/hydephp/develop/commit/82a166faaacdcd66160da8a58a74448b49aaece1 --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index db63d350..5b24a1c7 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: shivammathur/setup-php@a36e1e52ff4a1c9e9c9be31551ee4712a6cb6bd0 + - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} extensions: fileinfo, zip From 3d60d27186039a915e23e0474e54795dc420ee05 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 9 Nov 2023 18:07:20 +0000 Subject: [PATCH 06/23] Merge pull request #1445 from hydephp/serve-command-tests Implement process output test https://github.com/hydephp/develop/commit/a60c42d941e1fa345e00ca445f3974e895998aa5 --- src/Console/Commands/ServeCommand.php | 1 - tests/Feature/Commands/ServeCommandTest.php | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Console/Commands/ServeCommand.php b/src/Console/Commands/ServeCommand.php index fe5ce803..78170117 100644 --- a/src/Console/Commands/ServeCommand.php +++ b/src/Console/Commands/ServeCommand.php @@ -52,7 +52,6 @@ protected function getExecutablePath(): string return Hyde::path('vendor/hyde/realtime-compiler/bin/server.php'); } - /** @codeCoverageIgnore Until output is testable */ protected function runServerProcess(string $command): void { Process::forever()->run($command, function (string $type, string $line): void { diff --git a/tests/Feature/Commands/ServeCommandTest.php b/tests/Feature/Commands/ServeCommandTest.php index db0f4ec0..1dec744c 100644 --- a/tests/Feature/Commands/ServeCommandTest.php +++ b/tests/Feature/Commands/ServeCommandTest.php @@ -4,6 +4,7 @@ namespace Hyde\Framework\Testing\Feature\Commands; +use Closure; use Hyde\Hyde; use Hyde\Testing\TestCase; use Illuminate\Support\Facades\Process; @@ -135,16 +136,24 @@ public function test_hyde_serve_command_with_invalid_config_value() public function test_hyde_serve_command_passes_through_process_output() { - $this->markTestSkipped('Unable to access the output of the process. Assuming vendor bug for now.'); + Process::shouldReceive('forever') + ->once() + ->withNoArgs() + ->andReturnSelf(); - Process::fake(['php -S localhost:8080 {$this->binaryPath()}' => 'foo']); + Process::shouldReceive('run') + ->once() + ->withArgs(function (string $command, Closure $handle) { + $handle('type', 'foo'); + + return $command === "php -S localhost:8080 {$this->binaryPath()}"; + }) + ->andReturnSelf(); $this->artisan('serve') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->expectsOutput('foo') ->assertExitCode(0); - - Process::assertRan("php -S localhost:8080 {$this->binaryPath()}"); } protected function binaryPath(): string From c23d073710a7771679418ddf79f1d70bf3e633c0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 10 Nov 2023 20:16:16 +0000 Subject: [PATCH 07/23] Merge pull request #1444 from hydephp/fancy-serve-command Fancy serve command https://github.com/hydephp/develop/commit/1fafeb501de441364f8c37f7d12a7ae59567b581 --- src/Console/Commands/ServeCommand.php | 33 +++++++++++++++-- tests/Feature/Commands/ServeCommandTest.php | 40 ++++++++++++++------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/Console/Commands/ServeCommand.php b/src/Console/Commands/ServeCommand.php index 78170117..04813e17 100644 --- a/src/Console/Commands/ServeCommand.php +++ b/src/Console/Commands/ServeCommand.php @@ -4,12 +4,15 @@ namespace Hyde\Console\Commands; +use Closure; use Hyde\Hyde; use Hyde\Facades\Config; +use Hyde\RealtimeCompiler\ConsoleOutput; use Illuminate\Support\Facades\Process; use LaravelZero\Framework\Commands\Command; use function sprintf; +use function class_exists; /** * Start the realtime compiler server. @@ -26,7 +29,7 @@ class ServeCommand extends Command public function handle(): int { - $this->line('Starting the HydeRC server... Press Ctrl+C to stop'); + $this->printStartMessage(); $this->runServerProcess(sprintf('php -S %s:%d %s', $this->getHostSelection(), @@ -54,8 +57,32 @@ protected function getExecutablePath(): string protected function runServerProcess(string $command): void { - Process::forever()->run($command, function (string $type, string $line): void { + Process::forever()->env($this->getEnvironmentVariables())->run($command, $this->getOutputHandler()); + } + + protected function getEnvironmentVariables(): array + { + return [ + 'HYDE_RC_REQUEST_OUTPUT' => ! $this->option('no-ansi'), + ]; + } + + protected function printStartMessage(): void + { + $this->useBasicOutput() + ? $this->line('Starting the HydeRC server... Press Ctrl+C to stop') + : ConsoleOutput::printStartMessage($this->getHostSelection(), $this->getPortSelection()); + } + + protected function getOutputHandler(): Closure + { + return $this->useBasicOutput() ? function (string $type, string $line): void { $this->output->write($line); - }); + } : ConsoleOutput::getFormatter($this->output->isVerbose()); + } + + protected function useBasicOutput(): bool + { + return $this->option('no-ansi') || ! class_exists(ConsoleOutput::class); } } diff --git a/tests/Feature/Commands/ServeCommandTest.php b/tests/Feature/Commands/ServeCommandTest.php index 1dec744c..e42bce53 100644 --- a/tests/Feature/Commands/ServeCommandTest.php +++ b/tests/Feature/Commands/ServeCommandTest.php @@ -24,7 +24,7 @@ protected function setUp(): void public function test_hyde_serve_command() { - $this->artisan('serve') + $this->artisan('serve --no-ansi') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -33,7 +33,7 @@ public function test_hyde_serve_command() public function test_hyde_serve_command_with_port_option() { - $this->artisan('serve --port=8081') + $this->artisan('serve --no-ansi --port=8081') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -42,7 +42,7 @@ public function test_hyde_serve_command_with_port_option() public function test_hyde_serve_command_with_host_option() { - $this->artisan('serve --host=foo') + $this->artisan('serve --no-ansi --host=foo') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -51,7 +51,7 @@ public function test_hyde_serve_command_with_host_option() public function test_hyde_serve_command_with_port_and_host_option() { - $this->artisan('serve --port=8081 --host=foo') + $this->artisan('serve --no-ansi --port=8081 --host=foo') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -62,7 +62,7 @@ public function test_hyde_serve_command_with_port_defined_in_config() { config(['hyde.server.port' => 8081]); - $this->artisan('serve') + $this->artisan('serve --no-ansi') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -73,7 +73,7 @@ public function test_hyde_serve_command_with_port_defined_in_config_and_port_opt { config(['hyde.server.port' => 8081]); - $this->artisan('serve --port=8082') + $this->artisan('serve --no-ansi --port=8082') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -84,7 +84,7 @@ public function test_hyde_serve_command_with_port_missing_in_config_and_port_opt { config(['hyde.server.port' => null]); - $this->artisan('serve --port=8081') + $this->artisan('serve --no-ansi --port=8081') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -95,7 +95,7 @@ public function test_hyde_serve_command_with_host_defined_in_config() { config(['hyde.server.host' => 'foo']); - $this->artisan('serve') + $this->artisan('serve --no-ansi') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -106,7 +106,7 @@ public function test_hyde_serve_command_with_host_defined_in_config_and_host_opt { config(['hyde.server.host' => 'foo']); - $this->artisan('serve --host=bar') + $this->artisan('serve --no-ansi --host=bar') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -117,7 +117,7 @@ public function test_hyde_serve_command_with_host_missing_in_config_and_host_opt { config(['hyde.server.host' => null]); - $this->artisan('serve --host=foo') + $this->artisan('serve --no-ansi --host=foo') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); @@ -129,7 +129,7 @@ public function test_hyde_serve_command_with_invalid_config_value() $this->expectException(TypeError::class); config(['hyde.server.port' => 'foo']); - $this->artisan('serve') + $this->artisan('serve --no-ansi') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->assertExitCode(0); } @@ -141,6 +141,11 @@ public function test_hyde_serve_command_passes_through_process_output() ->withNoArgs() ->andReturnSelf(); + Process::shouldReceive('env') + ->once() + ->with(['HYDE_RC_REQUEST_OUTPUT' => false]) + ->andReturnSelf(); + Process::shouldReceive('run') ->once() ->withArgs(function (string $command, Closure $handle) { @@ -150,12 +155,23 @@ public function test_hyde_serve_command_passes_through_process_output() }) ->andReturnSelf(); - $this->artisan('serve') + $this->artisan('serve --no-ansi') ->expectsOutput('Starting the HydeRC server... Press Ctrl+C to stop') ->expectsOutput('foo') ->assertExitCode(0); } + public function testWithFancyOutput() + { + Process::fake(['php -S localhost:8080 {$this->binaryPath()}' => 'foo']); + + $this->artisan('serve') + ->expectsOutputToContain('HydePHP Realtime Compiler') + ->assertExitCode(0); + + Process::assertRan("php -S localhost:8080 {$this->binaryPath()}"); + } + protected function binaryPath(): string { return Hyde::path('vendor/hyde/realtime-compiler/bin/server.php'); From a29a52f6b5e8612fd4da5269a867653fbb0f9393 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 10 Nov 2023 20:24:58 +0000 Subject: [PATCH 08/23] Skip test when dependent class does not exist https://github.com/hydephp/develop/commit/0efe927ec4f1ed84e577749780abe6e160ba0f60 --- tests/Feature/Commands/ServeCommandTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Feature/Commands/ServeCommandTest.php b/tests/Feature/Commands/ServeCommandTest.php index e42bce53..7112b461 100644 --- a/tests/Feature/Commands/ServeCommandTest.php +++ b/tests/Feature/Commands/ServeCommandTest.php @@ -9,6 +9,7 @@ use Hyde\Testing\TestCase; use Illuminate\Support\Facades\Process; use TypeError; +use Hyde\RealtimeCompiler\ConsoleOutput; /** * @covers \Hyde\Console\Commands\ServeCommand @@ -163,6 +164,10 @@ public function test_hyde_serve_command_passes_through_process_output() public function testWithFancyOutput() { + if (! class_exists(ConsoleOutput::class)) { + $this->markTestSkipped('ConsoleOutput class not found.'); + } + Process::fake(['php -S localhost:8080 {$this->binaryPath()}' => 'foo']); $this->artisan('serve') From 0268b187be82e7ec7cdeb0f8ebc59f89c9a7d9fa Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 11 Nov 2023 10:43:31 +0000 Subject: [PATCH 09/23] Merge pull request #1447 from hydephp/fancy-serve-command Refactor console output internals https://github.com/hydephp/develop/commit/cd9add2467035d433d98c00a045b02125fd14fa0 --- src/Console/Commands/ServeCommand.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Console/Commands/ServeCommand.php b/src/Console/Commands/ServeCommand.php index 04813e17..75240db7 100644 --- a/src/Console/Commands/ServeCommand.php +++ b/src/Console/Commands/ServeCommand.php @@ -27,8 +27,11 @@ class ServeCommand extends Command /** @var string */ protected $description = 'Start the realtime compiler server.'; + protected ConsoleOutput $console; + public function handle(): int { + $this->configureOutput(); $this->printStartMessage(); $this->runServerProcess(sprintf('php -S %s:%d %s', @@ -67,18 +70,25 @@ protected function getEnvironmentVariables(): array ]; } + protected function configureOutput(): void + { + if (! $this->useBasicOutput()) { + $this->console = new ConsoleOutput($this->output->isVerbose()); + } + } + protected function printStartMessage(): void { $this->useBasicOutput() - ? $this->line('Starting the HydeRC server... Press Ctrl+C to stop') - : ConsoleOutput::printStartMessage($this->getHostSelection(), $this->getPortSelection()); + ? $this->output->writeln('Starting the HydeRC server... Press Ctrl+C to stop') + : $this->console->printStartMessage($this->getHostSelection(), $this->getPortSelection()); } protected function getOutputHandler(): Closure { return $this->useBasicOutput() ? function (string $type, string $line): void { $this->output->write($line); - } : ConsoleOutput::getFormatter($this->output->isVerbose()); + } : $this->console->getFormatter(); } protected function useBasicOutput(): bool From 8dfa059913b22006b75ba3ee31f31b8c76ca5851 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 11 Nov 2023 12:39:01 +0000 Subject: [PATCH 10/23] Merge pull request #1449 from hydephp/fancy-serve-command Fancy serve command cleanup https://github.com/hydephp/develop/commit/e13e2155659aadc57a2f7ed0c36783eb8164e624 --- src/Console/Commands/ServeCommand.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Console/Commands/ServeCommand.php b/src/Console/Commands/ServeCommand.php index 75240db7..bc738dac 100644 --- a/src/Console/Commands/ServeCommand.php +++ b/src/Console/Commands/ServeCommand.php @@ -22,7 +22,10 @@ class ServeCommand extends Command { /** @var string */ - protected $signature = 'serve {--host= : [default: "localhost"]}} {--port= : [default: 8080]}'; + protected $signature = 'serve + {--host= : [default: "localhost"]}} + {--port= : [default: 8080]} + '; /** @var string */ protected $description = 'Start the realtime compiler server.'; From 5d162ac18106ac3da89bbfb8cb1e26790a6920bc Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 12 Nov 2023 14:30:54 +0000 Subject: [PATCH 11/23] Merge pull request #1450 from hydephp/pass-through-settings-to-realtime-compiler Pass through settings to realtime compiler https://github.com/hydephp/develop/commit/0594ddc759e3dce364524f3803dd885640074f81 --- src/Console/Commands/ServeCommand.php | 58 ++++- src/Foundation/Internal/LoadConfiguration.php | 51 +++- tests/Feature/Commands/ServeCommandTest.php | 4 +- tests/Unit/LoadConfigurationTest.php | 51 +++- tests/Unit/ServeCommandOptionsUnitTest.php | 245 ++++++++++++++++++ 5 files changed, 377 insertions(+), 32 deletions(-) create mode 100644 tests/Unit/ServeCommandOptionsUnitTest.php diff --git a/src/Console/Commands/ServeCommand.php b/src/Console/Commands/ServeCommand.php index bc738dac..804151fd 100644 --- a/src/Console/Commands/ServeCommand.php +++ b/src/Console/Commands/ServeCommand.php @@ -7,9 +7,12 @@ use Closure; use Hyde\Hyde; use Hyde\Facades\Config; +use Illuminate\Support\Arr; +use InvalidArgumentException; use Hyde\RealtimeCompiler\ConsoleOutput; use Illuminate\Support\Facades\Process; use LaravelZero\Framework\Commands\Command; +use Hyde\Publications\Commands\ValidatingCommand; use function sprintf; use function class_exists; @@ -19,12 +22,16 @@ * * @see https://github.com/hydephp/realtime-compiler */ -class ServeCommand extends Command +class ServeCommand extends ValidatingCommand { /** @var string */ protected $signature = 'serve {--host= : [default: "localhost"]}} {--port= : [default: 8080]} + {--save-preview= : Should the served page be saved to disk? (Overrides config setting)} + {--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)} '; /** @var string */ @@ -32,7 +39,7 @@ class ServeCommand extends Command protected ConsoleOutput $console; - public function handle(): int + public function safeHandle(): int { $this->configureOutput(); $this->printStartMessage(); @@ -46,14 +53,14 @@ public function handle(): int return Command::SUCCESS; } - protected function getPortSelection(): int + protected function getHostSelection(): string { - return (int) ($this->option('port') ?: Config::getInt('hyde.server.port', 8080)); + return (string) $this->option('host') ?: Config::getString('hyde.server.host', 'localhost'); } - protected function getHostSelection(): string + protected function getPortSelection(): int { - return (string) $this->option('host') ?: Config::getString('hyde.server.host', 'localhost'); + return (int) ($this->option('port') ?: Config::getInt('hyde.server.port', 8080)); } protected function getExecutablePath(): string @@ -68,9 +75,13 @@ protected function runServerProcess(string $command): void protected function getEnvironmentVariables(): array { - return [ - 'HYDE_RC_REQUEST_OUTPUT' => ! $this->option('no-ansi'), - ]; + return Arr::whereNotNull([ + 'HYDE_SERVER_REQUEST_OUTPUT' => ! $this->option('no-ansi'), + 'HYDE_SERVER_SAVE_PREVIEW' => $this->parseEnvironmentOption('save-preview'), + 'HYDE_SERVER_DASHBOARD' => $this->parseEnvironmentOption('dashboard'), + 'HYDE_PRETTY_URLS' => $this->parseEnvironmentOption('pretty-urls'), + 'HYDE_PLAY_CDN' => $this->parseEnvironmentOption('play-cdn'), + ]); } protected function configureOutput(): void @@ -84,7 +95,7 @@ protected function printStartMessage(): void { $this->useBasicOutput() ? $this->output->writeln('Starting the HydeRC server... Press Ctrl+C to stop') - : $this->console->printStartMessage($this->getHostSelection(), $this->getPortSelection()); + : $this->console->printStartMessage($this->getHostSelection(), $this->getPortSelection(), $this->getEnvironmentVariables()); } protected function getOutputHandler(): Closure @@ -98,4 +109,31 @@ protected function useBasicOutput(): bool { return $this->option('no-ansi') || ! class_exists(ConsoleOutput::class); } + + protected function parseEnvironmentOption(string $name): ?string + { + $value = $this->option($name) ?? $this->checkArgvForOption($name); + + if ($value !== null) { + return match ($value) { + 'true', '' => 'enabled', + 'false' => 'disabled', + default => throw new InvalidArgumentException(sprintf('Invalid boolean value for --%s option.', $name)) + }; + } + + return null; + } + + /** Fallback check so that an environment option without a value is acknowledged as true. */ + protected function checkArgvForOption(string $name): ?string + { + if (isset($_SERVER['argv'])) { + if (in_array("--$name", $_SERVER['argv'], true)) { + return 'true'; + } + } + + return null; + } } diff --git a/src/Foundation/Internal/LoadConfiguration.php b/src/Foundation/Internal/LoadConfiguration.php index b4b51bf4..10bf1418 100644 --- a/src/Foundation/Internal/LoadConfiguration.php +++ b/src/Foundation/Internal/LoadConfiguration.php @@ -6,9 +6,10 @@ use Phar; use Illuminate\Contracts\Foundation\Application; -use Illuminate\Contracts\Config\Repository as RepositoryContract; +use Illuminate\Contracts\Config\Repository; use Illuminate\Foundation\Bootstrap\LoadConfiguration as BaseLoadConfiguration; +use function getenv; use function array_merge; use function dirname; use function in_array; @@ -30,7 +31,7 @@ protected function getConfigurationFiles(Application $app): array } /** Load the configuration items from all the files. */ - protected function loadConfigurationFiles(Application $app, RepositoryContract $repository): void + protected function loadConfigurationFiles(Application $app, Repository $repository): void { parent::loadConfigurationFiles($app, $repository); @@ -39,7 +40,7 @@ protected function loadConfigurationFiles(Application $app, RepositoryContract $ $this->loadRuntimeConfiguration($app, $repository); } - private function mergeConfigurationFiles(RepositoryContract $repository): void + private function mergeConfigurationFiles(Repository $repository): void { // These files do commonly not need to be customized by the user, so to get them out of the way, // we don't include them in the default project install. @@ -49,7 +50,7 @@ private function mergeConfigurationFiles(RepositoryContract $repository): void } } - private function mergeConfigurationFile(RepositoryContract $repository, string $file): void + private function mergeConfigurationFile(Repository $repository, string $file): void { // We of course want the user to be able to customize the config files, // if they're present, so we'll merge their changes here. @@ -78,18 +79,42 @@ private static function providePharSupportIfNeeded(array &$files): void } } - private function loadRuntimeConfiguration(Application $app, RepositoryContract $repository): void + private function loadRuntimeConfiguration(Application $app, Repository $repository): void { - if ($app->runningInConsole() && isset($_SERVER['argv'])) { - // Check if the `--pretty-urls` CLI argument is set, and if so, set the config value accordingly. - if (in_array('--pretty-urls', $_SERVER['argv'], true)) { - $repository->set('hyde.pretty_urls', true); + if ($app->runningInConsole()) { + if ($this->getArgv() !== null) { + $this->mergeCommandLineArguments($repository, '--pretty-urls', 'hyde.pretty_urls', true); + $this->mergeCommandLineArguments($repository, '--no-api', 'hyde.api_calls', false); } - // Check if the `--no-api` CLI argument is set, and if so, set the config value accordingly. - if (in_array('--no-api', $_SERVER['argv'], true)) { - $repository->set('hyde.api_calls', false); - } + $this->mergeRealtimeCompilerEnvironment($repository, 'HYDE_SERVER_SAVE_PREVIEW', 'hyde.server.save_preview'); + $this->mergeRealtimeCompilerEnvironment($repository, 'HYDE_SERVER_DASHBOARD', 'hyde.server.dashboard.enabled'); + $this->mergeRealtimeCompilerEnvironment($repository, 'HYDE_PRETTY_URLS', 'hyde.pretty_urls'); + $this->mergeRealtimeCompilerEnvironment($repository, 'HYDE_PLAY_CDN', 'hyde.use_play_cdn'); + } + } + + private function mergeCommandLineArguments(Repository $repository, string $argumentName, string $configKey, bool $value): void + { + if (in_array($argumentName, $this->getArgv(), true)) { + $repository->set($configKey, $value); } } + + private function mergeRealtimeCompilerEnvironment(Repository $repository, string $environmentKey, string $configKey): void + { + if ($this->getEnv($environmentKey) !== false) { + $repository->set($configKey, $this->getEnv($environmentKey) === 'enabled'); + } + } + + protected function getArgv(): ?array + { + return $_SERVER['argv'] ?? null; + } + + protected function getEnv(string $name): string|false|null + { + return getenv($name); + } } diff --git a/tests/Feature/Commands/ServeCommandTest.php b/tests/Feature/Commands/ServeCommandTest.php index 7112b461..7aa1cd8d 100644 --- a/tests/Feature/Commands/ServeCommandTest.php +++ b/tests/Feature/Commands/ServeCommandTest.php @@ -13,6 +13,8 @@ /** * @covers \Hyde\Console\Commands\ServeCommand + * + * @see \Hyde\Framework\Testing\Unit\ServeCommandOptionsUnitTest */ class ServeCommandTest extends TestCase { @@ -144,7 +146,7 @@ public function test_hyde_serve_command_passes_through_process_output() Process::shouldReceive('env') ->once() - ->with(['HYDE_RC_REQUEST_OUTPUT' => false]) + ->with(['HYDE_SERVER_REQUEST_OUTPUT' => false]) ->andReturnSelf(); Process::shouldReceive('run') diff --git a/tests/Unit/LoadConfigurationTest.php b/tests/Unit/LoadConfigurationTest.php index d1125608..4358e8bf 100644 --- a/tests/Unit/LoadConfigurationTest.php +++ b/tests/Unit/LoadConfigurationTest.php @@ -15,22 +15,57 @@ class LoadConfigurationTest extends UnitTestCase { public function testItLoadsRuntimeConfiguration() { - $serverBackup = $_SERVER; + $app = new Application(getcwd()); - $_SERVER['argv'] = ['--pretty-urls', '--no-api']; + $loader = new LoadConfigurationTestClass([]); + $loader->bootstrap($app); - $app = new Application(getcwd()); + $this->assertFalse(config('hyde.pretty_urls')); + $this->assertNull(config('hyde.api_calls')); - $loader = new LoadConfiguration(); + $loader = new LoadConfigurationTestClass(['--pretty-urls', '--no-api']); $loader->bootstrap($app); $this->assertTrue(config('hyde.pretty_urls')); $this->assertFalse(config('hyde.api_calls')); + } + + public function testItLoadsRealtimeCompilerEnvironmentConfiguration() + { + (new LoadConfigurationEnvironmentTestClass(['HYDE_SERVER_DASHBOARD' => 'enabled']))->bootstrap(new Application(getcwd())); + $this->assertTrue(config('hyde.server.dashboard.enabled')); - $_SERVER = $serverBackup; + (new LoadConfigurationEnvironmentTestClass(['HYDE_SERVER_DASHBOARD' => 'disabled']))->bootstrap(new Application(getcwd())); + $this->assertFalse(config('hyde.server.dashboard.enabled')); + } +} - $loader->bootstrap($app); - $this->assertFalse(config('hyde.pretty_urls')); - $this->assertNull(config('hyde.api_calls')); +class LoadConfigurationTestClass extends LoadConfiguration +{ + protected array $argv; + + public function __construct(array $argv) + { + $this->argv = $argv; + } + + protected function getArgv(): ?array + { + return $this->argv; + } +} + +class LoadConfigurationEnvironmentTestClass extends LoadConfiguration +{ + protected array $env; + + public function __construct(array $env) + { + $this->env = $env; + } + + protected function getEnv(string $name): string|false|null + { + return $this->env[$name]; } } diff --git a/tests/Unit/ServeCommandOptionsUnitTest.php b/tests/Unit/ServeCommandOptionsUnitTest.php new file mode 100644 index 00000000..935d9793 --- /dev/null +++ b/tests/Unit/ServeCommandOptionsUnitTest.php @@ -0,0 +1,245 @@ + 'localhost', + 'hyde.server.port' => 8080, + ]); + } + + public function test_getHostSelection() + { + $this->assertSame('localhost', $this->getMock()->getHostSelection()); + } + + public function test_getHostSelection_withHostOption() + { + $this->assertSame('foo', $this->getMock(['host' => 'foo'])->getHostSelection()); + } + + public function test_getHostSelection_withConfigOption() + { + self::mockConfig(['hyde.server.host' => 'foo']); + $this->assertSame('foo', $this->getMock()->getHostSelection()); + } + + public function test_getHostSelection_withHostOptionAndConfigOption() + { + self::mockConfig(['hyde.server.host' => 'foo']); + $this->assertSame('bar', $this->getMock(['host' => 'bar'])->getHostSelection()); + } + + public function test_getPortSelection() + { + $this->assertSame(8080, $this->getMock()->getPortSelection()); + } + + public function test_getPortSelection_withPortOption() + { + $this->assertSame(8081, $this->getMock(['port' => 8081])->getPortSelection()); + } + + public function test_getPortSelection_withConfigOption() + { + self::mockConfig(['hyde.server.port' => 8082]); + $this->assertSame(8082, $this->getMock()->getPortSelection()); + } + + public function test_getPortSelection_withPortOptionAndConfigOption() + { + self::mockConfig(['hyde.server.port' => 8082]); + $this->assertSame(8081, $this->getMock(['port' => 8081])->getPortSelection()); + } + + public function test_getEnvironmentVariables() + { + $this->assertSame([ + 'HYDE_SERVER_REQUEST_OUTPUT' => true, + ], $this->getMock()->getEnvironmentVariables()); + } + + public function test_getEnvironmentVariables_withNoAnsiOption() + { + $this->assertSame([ + 'HYDE_SERVER_REQUEST_OUTPUT' => false, + ], $this->getMock(['no-ansi' => true])->getEnvironmentVariables()); + } + + public function testSavePreviewOptionPropagatesToEnvironmentVariables() + { + $command = $this->getMock(['save-preview' => 'false']); + $this->assertSame('disabled', $command->getEnvironmentVariables()['HYDE_SERVER_SAVE_PREVIEW']); + + $command = $this->getMock(['save-preview' => 'true']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_SERVER_SAVE_PREVIEW']); + + $command = $this->getMock(['save-preview' => '']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_SERVER_SAVE_PREVIEW']); + + $command = $this->getMock(['save-preview' => null]); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_SERVER_SAVE_PREVIEW'])); + + $command = $this->getMock(); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_SERVER_SAVE_PREVIEW'])); + } + + public function testDashboardOptionPropagatesToEnvironmentVariables() + { + $command = $this->getMock(['dashboard' => 'false']); + $this->assertSame('disabled', $command->getEnvironmentVariables()['HYDE_SERVER_DASHBOARD']); + + $command = $this->getMock(['dashboard' => 'true']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_SERVER_DASHBOARD']); + + $command = $this->getMock(['dashboard' => '']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_SERVER_DASHBOARD']); + + $command = $this->getMock(['dashboard' => null]); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_SERVER_DASHBOARD'])); + + $command = $this->getMock(); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_SERVER_DASHBOARD'])); + } + + public function testPrettyUrlsOptionPropagatesToEnvironmentVariables() + { + $command = $this->getMock(['pretty-urls' => 'false']); + $this->assertSame('disabled', $command->getEnvironmentVariables()['HYDE_PRETTY_URLS']); + + $command = $this->getMock(['pretty-urls' => 'true']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_PRETTY_URLS']); + + $command = $this->getMock(['pretty-urls' => '']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_PRETTY_URLS']); + + $command = $this->getMock(['pretty-urls' => null]); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_PRETTY_URLS'])); + + $command = $this->getMock(); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_PRETTY_URLS'])); + } + + public function testPlayCdnOptionPropagatesToEnvironmentVariables() + { + $command = $this->getMock(['play-cdn' => 'false']); + $this->assertSame('disabled', $command->getEnvironmentVariables()['HYDE_PLAY_CDN']); + + $command = $this->getMock(['play-cdn' => 'true']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_PLAY_CDN']); + + $command = $this->getMock(['play-cdn' => '']); + $this->assertSame('enabled', $command->getEnvironmentVariables()['HYDE_PLAY_CDN']); + + $command = $this->getMock(['play-cdn' => null]); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_PLAY_CDN'])); + + $command = $this->getMock(); + $this->assertFalse(isset($command->getEnvironmentVariables()['HYDE_PLAY_CDN'])); + } + + public function test_parseEnvironmentOption() + { + $command = $this->getMock(['foo' => 'true']); + $this->assertSame('enabled', $command->parseEnvironmentOption('foo')); + + $command = $this->getMock(['foo' => 'false']); + $this->assertSame('disabled', $command->parseEnvironmentOption('foo')); + } + + public function test_parseEnvironmentOption_withEmptyString() + { + $command = $this->getMock(['foo' => '']); + $this->assertSame('enabled', $command->parseEnvironmentOption('foo')); + } + + public function test_parseEnvironmentOption_withNull() + { + $command = $this->getMock(['foo' => null]); + $this->assertNull($command->parseEnvironmentOption('foo')); + } + + public function test_parseEnvironmentOption_withInvalidValue() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid boolean value for --foo option.'); + + $command = $this->getMock(['foo' => 'bar']); + $command->parseEnvironmentOption('foo'); + } + + public function test_checkArgvForOption() + { + $serverBackup = $_SERVER; + + $_SERVER['argv'] = ['--pretty-urls']; + + $command = $this->getMock(); + + $this->assertSame('true', $command->checkArgvForOption('pretty-urls')); + $this->assertSame(null, $command->checkArgvForOption('dashboard')); + + $_SERVER = $serverBackup; + } + + protected function getMock(array $options = []): ServeCommandMock + { + return new ServeCommandMock($options); + } +} + +/** + * @method getHostSelection + * @method getPortSelection + * @method getEnvironmentVariables + * @method parseEnvironmentOption(string $name) + * @method checkArgvForOption(string $name) + */ +class ServeCommandMock extends ServeCommand +{ + public function __construct(array $options = []) + { + parent::__construct(); + + $this->input = new InputMock($options); + } + + public function __call($method, $parameters) + { + return call_user_func_array([$this, $method], $parameters); + } + + public function option($key = null) + { + return $this->input->getOption($key); + } +} + +class InputMock +{ + protected array $options; + + public function __construct(array $options = []) + { + $this->options = $options; + } + + public function getOption(string $key) + { + return $this->options[$key] ?? null; + } +} From 72f51b12c1729d93ea434f3eb952ed6a76f5c6f7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 12 Nov 2023 15:21:12 +0000 Subject: [PATCH 12/23] Merge pull request #1453 from hydephp/pass-through-settings-to-realtime-compiler Fix command extending wrong base class https://github.com/hydephp/develop/commit/ddd0c75b758d26e1ee0b44db7b72733a9d8b742a --- src/Console/Commands/ServeCommand.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Console/Commands/ServeCommand.php b/src/Console/Commands/ServeCommand.php index 804151fd..a5768644 100644 --- a/src/Console/Commands/ServeCommand.php +++ b/src/Console/Commands/ServeCommand.php @@ -9,10 +9,9 @@ use Hyde\Facades\Config; use Illuminate\Support\Arr; use InvalidArgumentException; +use Hyde\Console\Concerns\Command; use Hyde\RealtimeCompiler\ConsoleOutput; use Illuminate\Support\Facades\Process; -use LaravelZero\Framework\Commands\Command; -use Hyde\Publications\Commands\ValidatingCommand; use function sprintf; use function class_exists; @@ -22,7 +21,7 @@ * * @see https://github.com/hydephp/realtime-compiler */ -class ServeCommand extends ValidatingCommand +class ServeCommand extends Command { /** @var string */ protected $signature = 'serve From e288c53932ad9a5cb95baf5d077424aa78e28035 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 14 Nov 2023 20:02:19 +0000 Subject: [PATCH 13/23] Merge pull request #1458 from hydephp/realtime-compiler-live-edit Add a live edit feature to the realtime compiler https://github.com/hydephp/develop/commit/e1eba70af08d8e8846fdd6203f92681cbbe2be6c --- config/hyde.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/hyde.php b/config/hyde.php index 0f83b803..b05f8683 100644 --- a/config/hyde.php +++ b/config/hyde.php @@ -418,6 +418,9 @@ // Should preview pages be saved to the output directory? 'save_preview' => true, + // Should the live edit feature be enabled? + 'live_edit' => env('SERVER_LIVE_EDIT', true), + // Configure the realtime compiler dashboard 'dashboard' => [ // Should the realtime compiler dashboard be enabled? From f729bd7eb4fe55d50885e88f363cf394a0878251 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 26 Nov 2023 16:32:37 +0000 Subject: [PATCH 14/23] Merge pull request #1472 from hydephp/add-documentation-index-page-to-sidebar-when-it-is-the-only-page Add documentation index page to the sidebar when it's the only page https://github.com/hydephp/develop/commit/a524c176bf569fdc6949dd5f43c3ca94ca564797 --- .../Navigation/DocumentationSidebar.php | 5 ++++ .../Services/DocumentationSidebarTest.php | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/Framework/Features/Navigation/DocumentationSidebar.php b/src/Framework/Features/Navigation/DocumentationSidebar.php index ebbe5112..6cf239a9 100644 --- a/src/Framework/Features/Navigation/DocumentationSidebar.php +++ b/src/Framework/Features/Navigation/DocumentationSidebar.php @@ -22,6 +22,11 @@ protected function generate(): void $this->items->put($route->getRouteKey(), NavItem::fromRoute($route)); } }); + + // If there are no pages other than the index page, we add it to the sidebar so that it's not empty + if ($this->items->count() === 0 && DocumentationPage::home() !== null) { + $this->items->push(NavItem::fromRoute(DocumentationPage::home(), group: 'other')); + } } public function hasGroups(): bool diff --git a/tests/Feature/Services/DocumentationSidebarTest.php b/tests/Feature/Services/DocumentationSidebarTest.php index b1cadcbd..9ac37522 100644 --- a/tests/Feature/Services/DocumentationSidebarTest.php +++ b/tests/Feature/Services/DocumentationSidebarTest.php @@ -376,6 +376,31 @@ public function test_is_group_active_for_index_page_with_no_groups() $this->assertFalse(DocumentationSidebar::create()->isGroupActive('foo')); } + public function test_index_page_added_to_sidebar_when_it_is_the_only_page() + { + Filesystem::touch('_docs/index.md'); + $sidebar = DocumentationSidebar::create(); + + $this->assertCount(1, $sidebar->items); + $this->assertEquals( + collect([NavItem::fromRoute(Routes::get('docs/index'))]), + $sidebar->items + ); + } + + public function test_index_page_not_added_to_sidebar_when_other_pages_exist() + { + $this->createTestFiles(1); + Filesystem::touch('_docs/index.md'); + $sidebar = DocumentationSidebar::create(); + + $this->assertCount(1, $sidebar->items); + $this->assertEquals( + collect([NavItem::fromRoute(Routes::get('docs/test-0'))]), + $sidebar->items + ); + } + protected function createTestFiles(int $count = 5): void { for ($i = 0; $i < $count; $i++) { From c03d972512d81845c0f7f58275b6e31074181b5e Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 26 Nov 2023 19:46:56 +0000 Subject: [PATCH 15/23] Merge pull request #1477 from hydephp/documentation-sidebar-footer-configuration-option Update configuration option for the documentation sidebar footer to allow custom Markdown to be specified https://github.com/hydephp/develop/commit/b4e6831175a4e312431cdaf936c7c3a26403fc68 --- config/docs.php | 3 ++- .../components/docs/sidebar-footer-text.blade.php | 6 +++++- resources/views/components/docs/sidebar.blade.php | 2 +- tests/Feature/SidebarViewTest.php | 11 +++++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/config/docs.php b/config/docs.php index fe9cf452..fab400e9 100644 --- a/config/docs.php +++ b/config/docs.php @@ -30,7 +30,8 @@ // When using a grouped sidebar, should the groups be collapsible? 'collapsible' => true, - // Should the sidebar footer be shown? + // Should the sidebar footer be shown? You can also set this to a string + // of Markdown to show in the footer. Set to `false` to disable. 'footer' => true, ], diff --git a/resources/views/components/docs/sidebar-footer-text.blade.php b/resources/views/components/docs/sidebar-footer-text.blade.php index 4bafb848..6b49ca39 100644 --- a/resources/views/components/docs/sidebar-footer-text.blade.php +++ b/resources/views/components/docs/sidebar-footer-text.blade.php @@ -1,3 +1,7 @@

- Back to home page + @if(is_bool(config('docs.sidebar.footer', true))) + Back to home page + @else + {{ Hyde::markdown(config('docs.sidebar.footer')) }} + @endif

\ No newline at end of file diff --git a/resources/views/components/docs/sidebar.blade.php b/resources/views/components/docs/sidebar.blade.php index 9d8872bc..04b9857a 100644 --- a/resources/views/components/docs/sidebar.blade.php +++ b/resources/views/components/docs/sidebar.blade.php @@ -7,7 +7,7 @@ 'sidebar' => \Hyde\Framework\Features\Navigation\DocumentationSidebar::create(), ]) - @if(config('docs.sidebar.footer', true)) + @if(config('docs.sidebar.footer', true) !== false)
@include('hyde::components.docs.sidebar-footer-text')
diff --git a/tests/Feature/SidebarViewTest.php b/tests/Feature/SidebarViewTest.php index 53843c6c..44840e64 100644 --- a/tests/Feature/SidebarViewTest.php +++ b/tests/Feature/SidebarViewTest.php @@ -65,6 +65,17 @@ public function testBaseSidebarWithoutFooter() $this->assertViewWasNotRendered(view('hyde::components.docs.sidebar-footer-text')); } + public function testBaseSidebarWithCustomFooterText() + { + config(['docs.sidebar.footer' => 'My **Markdown** Footer Text']); + + $this->renderComponent(view('hyde::components.docs.sidebar')) + ->assertSeeHtml('