mirror of
https://github.com/symfony/ux-live-component.git
synced 2026-03-24 00:42:12 +01:00
[LiveComponent] Add debug:live-component command
This commit is contained in:
committed by
Hugo Alliaume
parent
1d06dbc4d6
commit
8d1df497b6
@@ -23,6 +23,7 @@
|
||||
```
|
||||
|
||||
- Add support for dynamic template resolution with `AsLiveComponent(template: FromMethod('customFunction'))`
|
||||
- Add `debug:live-component` command
|
||||
|
||||
## 2.31
|
||||
|
||||
|
||||
254
src/Command/LiveComponentDebugCommand.php
Normal file
254
src/Command/LiveComponentDebugCommand.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\UX\LiveComponent\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
||||
use Symfony\UX\LiveComponent\Attribute\LiveArg;
|
||||
use Symfony\UX\LiveComponent\Attribute\LiveProp;
|
||||
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadata;
|
||||
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
|
||||
|
||||
#[AsCommand(name: 'debug:live-component', description: 'Display live components and their usage for an application')]
|
||||
class LiveComponentDebugCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly LiveComponentMetadataFactory $metadataFactory,
|
||||
protected readonly array $liveComponentList,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument(
|
||||
'name',
|
||||
InputArgument::OPTIONAL,
|
||||
'A LiveComponent name or part of the name'
|
||||
),
|
||||
new InputOption(
|
||||
name: 'listening',
|
||||
mode: InputOption::VALUE_REQUIRED,
|
||||
description: 'Filter list to display only those listening to the given event'
|
||||
),
|
||||
])
|
||||
->setHelp(
|
||||
<<<'EOF'
|
||||
The <info>%command.name%</info> display all the live components in your application.
|
||||
|
||||
To list all live components:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
To get specific information about a component, specify its name (or a part of it):
|
||||
|
||||
<info>php %command.full_name% ProductSearch</info>
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$name = $input->getArgument('name');
|
||||
|
||||
if (\is_string($name)) {
|
||||
$componentName = $this->findComponentName($io, $name, $input->isInteractive());
|
||||
if (null === $componentName) {
|
||||
$io->error(\sprintf('Unknown component "%s".', $name));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$this->displayComponentDetails($io, $componentName);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$components = $this->findComponents($input->getOption('listening'));
|
||||
|
||||
$this->displayComponentsTable($components, $io);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function findComponentName(SymfonyStyle $io, string $name, bool $interactive): ?string
|
||||
{
|
||||
$components = [];
|
||||
foreach ($this->liveComponentList as $className) {
|
||||
$metadata = $this->metadataFactory->getMetadata($className);
|
||||
$componentName = $metadata->getComponentMetadata()->getName();
|
||||
if ($name === $componentName) {
|
||||
return $name;
|
||||
}
|
||||
if (str_contains($componentName, $name)) {
|
||||
$components[$componentName] = $componentName;
|
||||
}
|
||||
}
|
||||
|
||||
if ($interactive && \count($components)) {
|
||||
return $io->choice('Select one of the following component to display its information', array_values($components), 0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, LiveComponentMetadata>
|
||||
*/
|
||||
private function findComponents(?string $eventFilter = null): array
|
||||
{
|
||||
$components = [];
|
||||
if (null === $eventFilter) {
|
||||
foreach ($this->liveComponentList as $className) {
|
||||
$components[$className] ??= $this->metadataFactory->getMetadata($className);
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
foreach ($this->liveComponentList as $className) {
|
||||
foreach (AsLiveComponent::liveListeners($className) as $listener) {
|
||||
if ($listener['event'] === $eventFilter) {
|
||||
$components[$className] ??= $this->metadataFactory->getMetadata($className);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
private function displayComponentDetails(SymfonyStyle $io, string $name): void
|
||||
{
|
||||
$metadata = $this->metadataFactory->getMetadata($name);
|
||||
|
||||
$table = $io->createTable();
|
||||
$table->setHeaderTitle('Component');
|
||||
$table->setHeaders(['Property', 'Value']);
|
||||
$table->addRows([
|
||||
['Name', $metadata->getComponentMetadata()->getName()],
|
||||
['Class', $metadata->getComponentMetadata()->getClass()],
|
||||
]);
|
||||
|
||||
$table->addRows([
|
||||
['LiveProps', implode("\n", $this->getComponentLiveProps($metadata))],
|
||||
['LiveListeners', implode("\n", $this->getComponentLiveListeners($metadata->getComponentMetadata()->getClass()))],
|
||||
]);
|
||||
|
||||
$table->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, LiveComponentMetadata> $components
|
||||
*/
|
||||
private function displayComponentsTable(array $components, SymfonyStyle $io): void
|
||||
{
|
||||
$table = $io->createTable();
|
||||
$table->setStyle('default');
|
||||
$table->setHeaderTitle('Components');
|
||||
$table->setHeaders(['Name', 'Class']);
|
||||
foreach ($components as $component) {
|
||||
$table->addRow([
|
||||
$component->getComponentMetadata()->getName(),
|
||||
$component->getComponentMetadata()->getClass() ?? '',
|
||||
]);
|
||||
}
|
||||
$table->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function getComponentLiveProps(LiveComponentMetadata $component): array
|
||||
{
|
||||
$liveProps = [];
|
||||
foreach ($component->getAllLivePropsMetadata(null) as $liveProp) {
|
||||
$reflection = new \ReflectionProperty($component->getComponentMetadata()->getClass(), $liveProp->getName());
|
||||
$type = $this->displayType($liveProp->getType());
|
||||
$propertyName = '$'.$liveProp->getName();
|
||||
$defaultValueDisplay = $reflection->hasDefaultValue() ?
|
||||
$this->displayDefaultValue($reflection->getDefaultValue()) :
|
||||
'';
|
||||
$arguments = $reflection->getAttributes(LiveProp::class)[0]->getArguments();
|
||||
$argumentsDisplay = empty($arguments) ?
|
||||
'' :
|
||||
' ('.implode(', ', array_map(
|
||||
static fn ($key, $value) => $key.': '.json_encode($value),
|
||||
array_keys($arguments),
|
||||
$arguments
|
||||
)).')';
|
||||
|
||||
$propertyDisplay = $type.$propertyName.$defaultValueDisplay.$argumentsDisplay;
|
||||
$liveProps[$liveProp->getName()] = $propertyDisplay;
|
||||
}
|
||||
|
||||
return $liveProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function getComponentLiveListeners(string $class): array
|
||||
{
|
||||
$events = [];
|
||||
foreach (AsLiveComponent::liveListeners($class) as $liveListener) {
|
||||
$name = $liveListener['event'];
|
||||
$methodName = $liveListener['action'];
|
||||
$method = new \ReflectionMethod($class, $methodName);
|
||||
$parameters = array_map(
|
||||
fn (\ReflectionParameter $parameter) => $this->displayType($parameter->getType()).'$'.$parameter->getName().$this->displayDefaultValue($parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null),
|
||||
array_filter(
|
||||
$method->getParameters(),
|
||||
static fn (\ReflectionParameter $parameter) => !empty($parameter->getAttributes(LiveArg::class))
|
||||
)
|
||||
);
|
||||
$parametersDisplay = empty($parameters) ?
|
||||
'' :
|
||||
' ('.implode(', ', $parameters).')';
|
||||
|
||||
$display = $name.' => '.$methodName.$parametersDisplay;
|
||||
$events[] = $display;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
private function displayType(Type|string|null $type): string
|
||||
{
|
||||
$display = (string) $type;
|
||||
if ($type instanceof Type && $type->isNullable() && !str_contains($display, 'null')) {
|
||||
$display = '?'.$display;
|
||||
}
|
||||
if ('' !== $display) {
|
||||
$display .= ' ';
|
||||
}
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
private function displayDefaultValue(mixed $defaultValue): string
|
||||
{
|
||||
return (null !== $defaultValue) ?
|
||||
' = '.json_encode($defaultValue) :
|
||||
'';
|
||||
}
|
||||
}
|
||||
37
src/DependencyInjection/Compiler/LiveComponentPass.php
Normal file
37
src/DependencyInjection/Compiler/LiveComponentPass.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\UX\LiveComponent\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class LiveComponentPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$liveComponentList = [];
|
||||
foreach ($container->findTaggedServiceIds('twig.component') as $id => $tags) {
|
||||
if (!($tags[0]['live'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$className = $container->findDefinition($id)->getClass();
|
||||
$liveComponentList[$className] = $className;
|
||||
}
|
||||
|
||||
$debugCommandDefinition = $container->findDefinition('ux.live_component.command.debug');
|
||||
$debugCommandDefinition->setArgument(1, $liveComponentList);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use Symfony\Component\AssetMapper\AssetMapperInterface;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@@ -26,9 +27,11 @@ use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
||||
use Symfony\UX\LiveComponent\Command\LiveComponentDebugCommand;
|
||||
use Symfony\UX\LiveComponent\ComponentValidator;
|
||||
use Symfony\UX\LiveComponent\ComponentValidatorInterface;
|
||||
use Symfony\UX\LiveComponent\Controller\BatchActionController;
|
||||
use Symfony\UX\LiveComponent\DependencyInjection\Compiler\LiveComponentPass;
|
||||
use Symfony\UX\LiveComponent\EventListener\AddLiveAttributesSubscriber;
|
||||
use Symfony\UX\LiveComponent\EventListener\DataModelPropsSubscriber;
|
||||
use Symfony\UX\LiveComponent\EventListener\DeferLiveComponentSubscriber;
|
||||
@@ -275,6 +278,13 @@ final class LiveComponentExtension extends Extension implements PrependExtension
|
||||
new Parameter('container.build_hash'),
|
||||
])
|
||||
->addTag('kernel.cache_warmer');
|
||||
|
||||
$container->register('ux.live_component.command.debug', LiveComponentDebugCommand::class)
|
||||
->setArguments([
|
||||
new Reference('ux.live_component.metadata_factory'),
|
||||
new AbstractArgument(\sprintf('Added in %s.', LiveComponentPass::class)),
|
||||
])
|
||||
->addTag('console.command');
|
||||
}
|
||||
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
|
||||
@@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\UX\LiveComponent\DependencyInjection\Compiler\ComponentDefaultActionPass;
|
||||
use Symfony\UX\LiveComponent\DependencyInjection\Compiler\LiveComponentPass;
|
||||
use Symfony\UX\LiveComponent\DependencyInjection\Compiler\OptionalDependencyPass;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,7 @@ final class LiveComponentBundle extends Bundle
|
||||
// must run before Symfony\Component\Serializer\DependencyInjection\SerializerPass
|
||||
$container->addCompilerPass(new OptionalDependencyPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100);
|
||||
$container->addCompilerPass(new ComponentDefaultActionPass());
|
||||
$container->addCompilerPass(new LiveComponentPass());
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
|
||||
@@ -125,8 +125,11 @@ final class LegacyLivePropMetadata
|
||||
* If a modifier is specified, a modified clone is returned.
|
||||
* Otherwise, the metadata is returned as it is.
|
||||
*/
|
||||
public function withModifier(object $component): self
|
||||
public function withModifier(?object $component): self
|
||||
{
|
||||
if (null === $component) {
|
||||
return $this;
|
||||
}
|
||||
if (null === ($modifier = $this->liveProp->modifier())) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class LiveComponentMetadata
|
||||
/**
|
||||
* @return list<LivePropMetadata|LegacyLivePropMetadata>
|
||||
*/
|
||||
public function getAllLivePropsMetadata(object $component): iterable
|
||||
public function getAllLivePropsMetadata(?object $component): iterable
|
||||
{
|
||||
foreach ($this->livePropsMetadata as $livePropMetadata) {
|
||||
yield $livePropMetadata->withModifier($component);
|
||||
|
||||
@@ -107,8 +107,11 @@ final class LivePropMetadata
|
||||
* If a modifier is specified, a modified clone is returned.
|
||||
* Otherwise, the metadata is returned as it is.
|
||||
*/
|
||||
public function withModifier(object $component): self
|
||||
public function withModifier(?object $component): self
|
||||
{
|
||||
if (null === $component) {
|
||||
return $this;
|
||||
}
|
||||
if (null === ($modifier = $this->liveProp->modifier())) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
188
tests/Integration/Command/LiveComponentDebugCommandTest.php
Normal file
188
tests/Integration/Command/LiveComponentDebugCommandTest.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\UX\LiveComponent\Tests\Integration\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class LiveComponentDebugCommandTest extends KernelTestCase
|
||||
{
|
||||
public function testList()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute([]);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Name', $display);
|
||||
$this->assertStringContainsString('Class', $display);
|
||||
$this->assertStringContainsString('component1', $display);
|
||||
$this->assertStringContainsString('component2', $display);
|
||||
}
|
||||
|
||||
public function testListListeningToEvent()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute(['--listening' => 'the_event_name']);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Name', $display);
|
||||
$this->assertStringContainsString('Class', $display);
|
||||
$this->assertStringNotContainsString('component1', $display);
|
||||
$this->assertStringContainsString('component5', $display);
|
||||
}
|
||||
|
||||
public function testEmptyListListeningToEvent()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute(['--listening' => 'event_not_defined']);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Name', $display);
|
||||
$this->assertStringContainsString('Class', $display);
|
||||
$this->assertStringNotContainsString('component1', $display);
|
||||
$this->assertStringNotContainsString('component5', $display);
|
||||
}
|
||||
|
||||
public function testWithNoMatchComponent()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$result = $commandTester->execute(['name' => 'NoMatchComponent']);
|
||||
|
||||
$this->assertEquals(1, $result);
|
||||
$this->assertStringContainsString('Unknown component "NoMatchComponent".', $commandTester->getDisplay());
|
||||
}
|
||||
|
||||
public function testNotLiveComponentsIsNotListed()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$result = $commandTester->execute(['name' => 'SimpleTwigComponent']);
|
||||
|
||||
$this->assertEquals(1, $result);
|
||||
$this->assertStringContainsString('Unknown component "SimpleTwigComponent".', $commandTester->getDisplay());
|
||||
}
|
||||
|
||||
public function testWithOnePartialMatchComponent()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->setInputs([]);
|
||||
$result = $commandTester->execute(['name' => 'todo_list_']);
|
||||
|
||||
$this->assertEquals(0, $result);
|
||||
// Choices
|
||||
$this->assertStringNotContainsString('] todo_list\n', $commandTester->getDisplay());
|
||||
$this->assertStringContainsString('] todo_list_with_keys', $commandTester->getDisplay());
|
||||
// Component table
|
||||
$this->assertStringContainsString('Component\\TodoListWithKeysComponent', $commandTester->getDisplay());
|
||||
}
|
||||
|
||||
public function testWithMultiplePartialMatchComponent()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->setInputs(['todo_list']);
|
||||
$result = $commandTester->execute(['name' => 'todo_']);
|
||||
|
||||
$this->assertEquals(0, $result);
|
||||
// Choices
|
||||
$this->assertStringContainsString('Select one of the following component to display its information', $commandTester->getDisplay());
|
||||
$this->assertStringContainsString('] todo_item', $commandTester->getDisplay());
|
||||
$this->assertStringContainsString('] todo_list', $commandTester->getDisplay());
|
||||
$this->assertStringContainsString('] todo_list_with_keys', $commandTester->getDisplay());
|
||||
// Component table
|
||||
$this->assertStringNotContainsString('Component\\TodoItemComponent', $commandTester->getDisplay());
|
||||
$this->assertStringContainsString('Component\\TodoListComponent', $commandTester->getDisplay());
|
||||
$this->assertStringNotContainsString('Component\\TodoListWithKeysComponent', $commandTester->getDisplay());
|
||||
}
|
||||
|
||||
public function testComponent()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute(['name' => 'component1']);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
|
||||
$this->tableDisplayCheck($display);
|
||||
$this->assertStringContainsString('component1', $display);
|
||||
$this->assertStringContainsString('Component\\Component1', $display);
|
||||
}
|
||||
|
||||
public function testLivePropsWithFieldName()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute(['name' => 'component3']);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
|
||||
$this->tableDisplayCheck($display);
|
||||
$this->assertStringContainsString('component3', $display);
|
||||
$this->assertStringContainsString('LiveProps', $display);
|
||||
$this->assertStringContainsString('$prop1 (fieldName: "myProp1")', $display);
|
||||
$this->assertStringContainsString('$prop2 (fieldName: "getProp2Name()"', $display);
|
||||
}
|
||||
|
||||
public function testLivePropsWithUrlMapping()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute(['name' => 'component_with_url_bound_props']);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
|
||||
$this->tableDisplayCheck($display);
|
||||
$this->assertStringContainsString('component_with_url_bound_props', $display);
|
||||
$this->assertStringContainsString('LiveProps', $display);
|
||||
$this->assertStringContainsString('$pathPropWithAlias (writable: true, url: {"as":"pathAlias","mapPath":true})', $display);
|
||||
}
|
||||
|
||||
public function testWithLiveListeners()
|
||||
{
|
||||
$commandTester = $this->createCommandTester();
|
||||
$commandTester->execute(['name' => 'component2']);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$display = $commandTester->getDisplay();
|
||||
|
||||
$this->tableDisplayCheck($display);
|
||||
$this->assertStringContainsString('component2', $display);
|
||||
$this->assertStringContainsString('LiveListeners', $display);
|
||||
$this->assertStringContainsString('triggerIncrease => increaseEvent1 (int $amount = 1)', $display);
|
||||
$this->assertStringContainsString('triggerIncrease => increaseEvent2 (int $amount = 1)', $display);
|
||||
}
|
||||
|
||||
private function createCommandTester(): CommandTester
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$application = new Application($kernel);
|
||||
|
||||
return new CommandTester($application->find('debug:live-component'));
|
||||
}
|
||||
|
||||
private function tableDisplayCheck(string $display): void
|
||||
{
|
||||
$this->assertStringContainsString('Component', $display);
|
||||
$this->assertStringContainsString('Class', $display);
|
||||
$this->assertStringContainsString('LiveProps', $display);
|
||||
$this->assertStringContainsString('LiveListeners', $display);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user