Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Twig] add test helper #821

Merged
merged 1 commit into from
Jun 26, 2023
Merged

[Twig] add test helper #821

merged 1 commit into from
Jun 26, 2023

Conversation

kbond
Copy link
Member

@kbond kbond commented Apr 28, 2023

Q A
Bug fix? no
New feature? yes
Tickets Fix #818
License MIT

This works with Live components out of the box (not with slots though as they aren't supported).

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\UX\TwigComponent\Test\InteractsWithTwigComponents;

class MyComponentTest extends KernelTestCase
{
    use InteractsWithTwigComponents;

    public function testComponentMount(): void
    {
        $component = $this->mountTwigComponent(
            name: 'MyComponent', // can also use FQCN (MyComponent::class)
            data: ['foo' => 'bar'],
        );

        $this->assertInstanceOf(MyComponent::class, $component);
        $this->assertSame('bar', $component->foo);
    }

    public function testComponentRenders(): void
    {
        $rendered = $this->renderTwigComponent(
            name: 'MyComponent', // can also use FQCN (MyComponent::class)
            data: ['foo' => 'bar'],
        );

        $this->assertStringContainsString('bar', $rendered);
    }

    public function testEmbeddedComponentRenders(): void
    {
        $rendered = $this->renderTwigComponent(
            name: 'MyComponent', // can also use FQCN (MyComponent::class)
            data: ['foo' => 'bar'],
            content: '<div>My content</div>', // "content" (default) block
            blocks: [
                'header' => '<div>My header</div>',
                'menu' => $this->renderTwigComponent('Menu'), // can embed other components
            ],
        );

        $this->assertStringContainsString('bar', $rendered);
    }
}

@kbond kbond force-pushed the twig-test-helpers branch from ce5f2db to 7251aea Compare April 28, 2023 19:42
/**
* @author Kevin Bond <[email protected]>
*/
final class RenderedComponent implements \Stringable
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this stringable object to wrap the rendered html so we can possibly add something like described in #818 (comment) in the future.

I wasn't sure if changing the type-hint from string -> stringable object as the return type of InteractsWithTwigComponent::renderTwigComponent() would be considered a BC break. If not, I can change to just return string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's start with this - easier to remove this later if we end up not needing it or it's a pain for some reason than to go in the other direction.

/**
* @author Kevin Bond <[email protected]>
*/
trait InteractsWithTwigComponents
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my preferred name (:wink:) but I have no problem adding the Trait suffix or naming something else that jives with Symfony standards.

@kbond kbond changed the title feat(twig): add test helper [Twig] add test helper Apr 28, 2023
@kbond
Copy link
Member Author

kbond commented Apr 28, 2023

My plan is to eventually add an InteractsWithLiveComponents trait (that'll probably use/extend this one) that adds live-specific testing helpers.

@kbond kbond force-pushed the twig-test-helpers branch 2 times, most recently from bba954a to b4f7084 Compare April 28, 2023 20:52
@kbond
Copy link
Member Author

kbond commented Apr 28, 2023

I've added the ability to use the component class name with these helpers (ie $this->renderTwigComponent(MyComponent::class)). Not sure if we should though as we aren't testing that the name (which will be used in twig) renders the expected component. For instance, if you use your IDE to rename a component, the tests will still pass but the template might not (if not manually configuring a component name).

Copy link
Contributor

@WebMamba WebMamba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kbond that's super handy! 😁

/**
* @author Kevin Bond <[email protected]>
*/
trait InteractsWithTwigComponents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just wondering why do we use trait here? In my use case, I gonna create a dedicated test class for my component, and I not gonna mixed my components tests with other tests

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a pattern I've found useful with my other testing libraries. I like using traits to avoid forcing a single type of test.

class MyComponentTest extends KernelTestCase
{
    use InteractsWithTwigComponents, ResetDatabase, Factories;

    // ...
}

I get the above would still work if we made this trait into and abstract TwigComponentTestCase. I'm just trying to be consistent: Make a standard kernel test, and inject the test features you need via traits.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ho yes using this trait with foundry just rocks! Thanks for all of this 😁

@WebMamba
Copy link
Contributor

WebMamba commented Jun 25, 2023

@kbond friendly ping here! What is needed in order to merge it? I feel like it's super close. I already start to use this trait on my project and it's super handy! 😁

@kbond
Copy link
Member Author

kbond commented Jun 25, 2023

I feel this is basically ready. I'll rebase this week to ensure all's still good. I'll add a test for an embedded component using html syntax too.

I guess I was looking for opinions on the following:

/cc @weaverryan

@weaverryan
Copy link
Member

I've added the ability to use the component class name with these helpers (ie $this->renderTwigComponent(MyComponent::class))

I like this. I see what you're saying... but we can't do any better. To me, it feels like you've added a way to unit test a class... but sure, ultimately, someone could call it with invalid arguments or forget to call it at all on page. A higher-level test would be needed for that.

'name' => $name,
'data' => $data,
])
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is pretty nuts 👍

@weaverryan
Copy link
Member

Thanks Kevin - this looks awesome!

@weaverryan weaverryan merged commit 21d16ea into symfony:2.x Jun 26, 2023
@kbond kbond deleted the twig-test-helpers branch June 26, 2023 20:12
weaverryan added a commit that referenced this pull request Aug 17, 2023
This PR was squashed before being merged into the 2.x branch.

Discussion
----------

[Live] add test helper

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Tickets       | Continuation of #821
| License       | MIT

```php
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\UX\LiveComponent\Test\InteractsWithLiveComponents;

class MyComponentTest extends KernelTestCase
{
    use InteractsWithLiveComponents;

    public function testThisComponent(): void
    {
        $testComponent = $this->createLiveComponent('my_component');
        $testComponent = $this->createLiveComponent(Component::class); // or use the FQCN

        (string) $testComponent->render(); // string - initial state html
        $testComponent->component(); // object - component instance at initial state

        $testComponent
            ->call('increase') // call a live action
            ->call('increase', ['amount' => 2]) // call a live action with arguments
            ->set('count', 5) // set a live prop
            ->emit('inceaseEvent') // emit a live event
            ->emit('inceaseEvent', ['amount' => 2]) // emit a live event with arguments
            ->refresh() // simply refresh the component
        ;

        (string) $testComponent->render(); // string - updated state html
        $testComponent->component(); // object - component instance at updated state

        /** `@var` Symfony\Component\HttpFoundation\Response $response */
        $response = $testComponent->call('actionThatRedirects')->response();
    }
}
```

TODO:
- [x] handle action responses
- [x] `->refresh()` method?
- [x] Docs
- [x] Changelog

Commits
-------

faa1518 [Live] add test helper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[TwigComponent] Provide class/trait for testing component
3 participants